From ed7b9c6d433806f368d672603a7b6c84cbbd6f38 Mon Sep 17 00:00:00 2001 From: liuchen <23082234@qq.com> Date: Thu, 25 Jan 2024 17:24:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .asf.yaml | 11 + .gitignore | 1 + .idea/.gitignore | 3 + .idea/compiler.xml | 18 + .idea/encodings.xml | 7 + .idea/jarRepositories.xml | 25 + .idea/misc.xml | 13 + .idea/uiDesigner.xml | 124 + .idea/vcs.xml | 6 + .travis.yml | 22 + DEPENDENCIES | 221 + LICENSE | 549 + NOTICE | 5 + README.md | 70 + docs/1_0_0/Milestone.md | 102 + docs/1_0_0/UserGuide_CN.md | 152 + docs/1_0_0/UserGuide_EN.md | 158 + pom.xml | 374 + src/main/docker/Dockerfile | 23 + .../rocketmq/dashboard/Application.java | 36 + .../dashboard/admin/MQAdminFactory.java | 57 + .../admin/MQAdminPooledObjectFactory.java | 79 + .../dashboard/admin/MqAdminExtObjectPool.java | 47 + .../dashboard/aspect/admin/MQAdminAspect.java | 59 + .../OriginalControllerReturnValue.java | 30 + .../config/AuthWebMVCConfigurerAdapter.java | 90 + .../dashboard/config/RMQConfigure.java | 161 + .../controller/ClusterController.java | 48 + .../controller/ConfigController.java | 62 + .../controller/ConsumerController.java | 120 + .../controller/DashboardController.java | 60 + .../dashboard/controller/LoginController.java | 95 + .../controller/MessageController.java | 90 + .../controller/MessageTraceController.java | 71 + .../controller/MonitorController.java | 64 + .../controller/NamesvrController.java | 41 + .../dashboard/controller/OpsController.java | 70 + .../controller/ProducerController.java | 45 + .../dashboard/controller/TestController.java | 101 + .../dashboard/controller/TopicController.java | 120 + .../dashboard/exception/ServiceException.java | 31 + .../filter/HttpBasicAuthorizedFilter.java | 51 + .../interceptor/AuthInterceptor.java | 38 + .../dashboard/model/ConnectionInfo.java | 53 + .../model/ConsumerGroupRollBackStat.java | 61 + .../model/ConsumerMonitorConfig.java | 46 + .../dashboard/model/GroupConsumeInfo.java | 94 + .../rocketmq/dashboard/model/LoginInfo.java | 39 + .../rocketmq/dashboard/model/LoginResult.java | 36 + .../rocketmq/dashboard/model/MessagePage.java | 54 + .../dashboard/model/MessagePageTask.java | 55 + .../dashboard/model/MessageQueryByPage.java | 107 + .../dashboard/model/MessageTraceView.java | 237 + .../rocketmq/dashboard/model/MessageView.java | 196 + .../dashboard/model/QueueOffsetInfo.java | 104 + .../dashboard/model/QueueStatInfo.java | 85 + .../dashboard/model/TopicConsumerInfo.java | 62 + .../apache/rocketmq/dashboard/model/User.java | 83 + .../rocketmq/dashboard/model/UserInfo.java | 67 + .../model/request/ConsumerConfigInfo.java | 61 + .../model/request/DeleteSubGroupRequest.java | 40 + .../dashboard/model/request/MessageQuery.java | 82 + .../model/request/ResetOffsetRequest.java | 58 + .../request/SendTopicMessageRequest.java | 28 + .../model/request/TopicConfigInfo.java | 114 + .../model/trace/MessageTraceGraph.java | 31 + .../model/trace/MessageTraceStatusEnum.java | 32 + .../dashboard/model/trace/ProducerNode.java | 33 + .../model/trace/SubscriptionNode.java | 27 + .../dashboard/model/trace/TraceNode.java | 36 + .../dashboard/permisssion/Permission.java | 27 + .../permisssion/PermissionAspect.java | 69 + .../dashboard/permisssion/UserRoleEnum.java | 38 + .../service/AbstractCommonService.java | 49 + .../dashboard/service/ClusterService.java | 27 + .../dashboard/service/ConfigService.java | 32 + .../dashboard/service/ConsumerService.java | 58 + .../service/DashboardCollectService.java | 36 + .../dashboard/service/DashboardService.java | 42 + .../dashboard/service/LoginService.java | 25 + .../dashboard/service/MessageService.java | 59 + .../service/MessageTraceService.java | 31 + .../dashboard/service/MonitorService.java | 30 + .../dashboard/service/OpsService.java | 34 + .../dashboard/service/PermissionService.java | 24 + .../dashboard/service/ProducerService.java | 24 + .../dashboard/service/TopicService.java | 54 + .../dashboard/service/UserService.java | 25 + .../service/checker/CheckerType.java | 23 + .../service/checker/RocketMqChecker.java | 24 + .../impl/ClusterHealthCheckerImpl.java | 34 + .../impl/TopicOnlyOneBrokerCheckerImpl.java | 39 + .../service/client/MQAdminExtImpl.java | 551 + .../service/client/MQAdminInstance.java | 75 + .../service/impl/AbstractFileStore.java | 83 + .../service/impl/ClusterServiceImpl.java | 76 + .../service/impl/ConfigServiceImpl.java | 115 + .../service/impl/ConsumerServiceImpl.java | 332 + .../impl/DashboardCollectServiceImpl.java | 157 + .../service/impl/DashboardServiceImpl.java | 73 + .../service/impl/LoginServiceImpl.java | 71 + .../service/impl/MessageServiceImpl.java | 544 + .../service/impl/MessageTraceServiceImpl.java | 243 + .../service/impl/MonitorServiceImpl.java | 101 + .../service/impl/OpsServiceImpl.java | 88 + .../service/impl/PermissionServiceImpl.java | 98 + .../service/impl/ProducerServiceImpl.java | 41 + .../service/impl/TopicServiceImpl.java | 287 + .../service/impl/UserServiceImpl.java | 118 + .../support/GlobalExceptionHandler.java | 48 + .../GlobalRestfulResponseBodyAdvice.java | 62 + .../dashboard/support/JsonResult.java | 63 + .../dashboard/task/DashboardCollectTask.java | 337 + .../rocketmq/dashboard/task/MonitorTask.java | 50 + .../rocketmq/dashboard/util/JsonUtil.java | 156 + .../rocketmq/dashboard/util/MatcherUtil.java | 58 + .../dashboard/util/MsgTraceDecodeUtil.java | 172 + .../rocketmq/dashboard/util/WebUtil.java | 140 + src/main/resources/application.properties | 52 + src/main/resources/logback.xml | 50 + src/main/resources/rmqcngkeystore.jks | Bin 0 -> 2589 bytes src/main/resources/role-permission.yml | 35 + src/main/resources/static/index.html | 117 + src/main/resources/static/src/app.js | 264 + src/main/resources/static/src/cluster.js | 72 + src/main/resources/static/src/config.js | 236 + src/main/resources/static/src/consumer.js | 380 + src/main/resources/static/src/controller.js | 567 + src/main/resources/static/src/data/dict.json | 4 + src/main/resources/static/src/i18n/en.js | 115 + src/main/resources/static/src/i18n/zh.js | 116 + src/main/resources/static/src/login.js | 45 + src/main/resources/static/src/message.js | 262 + src/main/resources/static/src/messageTrace.js | 398 + src/main/resources/static/src/ops.js | 76 + src/main/resources/static/src/producer.js | 52 + .../static/src/remoteApi/remoteApi.js | 75 + src/main/resources/static/src/tools/tools.js | 85 + src/main/resources/static/src/topic.js | 501 + src/main/resources/static/style/animate.css | 49 + src/main/resources/static/style/app.css | 291 + src/main/resources/static/style/btn.css | 769 + .../resources/static/style/img/alipay.png | Bin 0 -> 2156 bytes .../static/style/img/alipay_active.png | Bin 0 -> 2275 bytes .../static/style/img/icon_user_center.png | Bin 0 -> 31449 bytes .../resources/static/style/img/weipay.png | Bin 0 -> 1801 bytes .../static/style/img/weipay_active.png | Bin 0 -> 1920 bytes src/main/resources/static/style/login.css | 29 + .../static/style/preLoading/main.css | 459 + .../static/style/preLoading/normalize.css | 544 + src/main/resources/static/style/theme.css | 97 + src/main/resources/static/style/timeline.css | 143 + .../angular-material/angular-animate.min.js | 56 + .../angular-material/angular-aria.min.js | 14 + .../angular-material/angular-material.min.css | 16862 ++++++ .../angular-material/angular-material.min.js | 15 + .../static/vendor/angular/angular-animate.js | 4148 ++ .../vendor/angular/angular-animate.min.js | 56 + .../vendor/angular/angular-animate.min.js.map | 8 + .../static/vendor/angular/angular-aria.js | 405 + .../static/vendor/angular/angular-aria.min.js | 14 + .../vendor/angular/angular-aria.min.js.map | 8 + .../static/vendor/angular/angular-cookies.js | 322 + .../vendor/angular/angular-cookies.min.js | 9 + .../vendor/angular/angular-cookies.min.js.map | 8 + .../static/vendor/angular/angular-csp.css | 21 + .../static/vendor/angular/angular-loader.js | 484 + .../vendor/angular/angular-loader.min.js | 10 + .../vendor/angular/angular-loader.min.js.map | 8 + .../vendor/angular/angular-message-format.js | 980 + .../angular/angular-message-format.min.js | 26 + .../angular/angular-message-format.min.js.map | 8 + .../static/vendor/angular/angular-messages.js | 724 + .../vendor/angular/angular-messages.min.js | 12 + .../angular/angular-messages.min.js.map | 8 + .../static/vendor/angular/angular-mocks.js | 3112 ++ .../vendor/angular/angular-parse-ext.js | 1271 + .../vendor/angular/angular-parse-ext.min.js | 49 + .../angular/angular-parse-ext.min.js.map | 8 + .../static/vendor/angular/angular-resource.js | 768 + .../vendor/angular/angular-resource.min.js | 15 + .../angular/angular-resource.min.js.map | 8 + .../static/vendor/angular/angular-route.js | 1032 + .../vendor/angular/angular-route.min.js | 15 + .../vendor/angular/angular-route.min.js.map | 8 + .../static/vendor/angular/angular-sanitize.js | 717 + .../vendor/angular/angular-sanitize.min.js | 15 + .../angular/angular-sanitize.min.js.map | 8 + .../static/vendor/angular/angular-scenario.js | 43097 ++++++++++++++++ .../static/vendor/angular/angular-touch.js | 729 + .../vendor/angular/angular-touch.min.js | 14 + .../vendor/angular/angular-touch.min.js.map | 8 + .../angular-translate-storage-cookie.js | 97 + .../angular-translate-storage-cookie.min.js | 6 + .../vendor/angular/angular-translate.js | 3691 ++ .../vendor/angular/angular-translate.min.js | 6 + .../vendor/angular/angular-ui-router.min.js | 8 + .../vendor/angular/angular-websocket.min.js | 2 + .../static/vendor/angular/angular.js | 31023 +++++++++++ .../static/vendor/angular/angular.min.js | 316 + .../static/vendor/angular/angular.min.js.map | 8 + .../static/vendor/angular/errors.json | 1 + .../angular/i18n/angular-locale_aa-dj.js | 128 + .../angular/i18n/angular-locale_aa-er.js | 128 + .../angular/i18n/angular-locale_aa-et.js | 128 + .../vendor/angular/i18n/angular-locale_aa.js | 128 + .../angular/i18n/angular-locale_af-na.js | 125 + .../angular/i18n/angular-locale_af-za.js | 125 + .../vendor/angular/i18n/angular-locale_af.js | 125 + .../angular/i18n/angular-locale_agq-cm.js | 143 + .../vendor/angular/i18n/angular-locale_agq.js | 143 + .../angular/i18n/angular-locale_ak-gh.js | 143 + .../vendor/angular/i18n/angular-locale_ak.js | 143 + .../angular/i18n/angular-locale_am-et.js | 125 + .../vendor/angular/i18n/angular-locale_am.js | 125 + .../angular/i18n/angular-locale_ar-001.js | 125 + .../angular/i18n/angular-locale_ar-ae.js | 125 + .../angular/i18n/angular-locale_ar-bh.js | 125 + .../angular/i18n/angular-locale_ar-dj.js | 125 + .../angular/i18n/angular-locale_ar-dz.js | 125 + .../angular/i18n/angular-locale_ar-eg.js | 125 + .../angular/i18n/angular-locale_ar-eh.js | 125 + .../angular/i18n/angular-locale_ar-er.js | 125 + .../angular/i18n/angular-locale_ar-il.js | 125 + .../angular/i18n/angular-locale_ar-iq.js | 125 + .../angular/i18n/angular-locale_ar-jo.js | 125 + .../angular/i18n/angular-locale_ar-km.js | 125 + .../angular/i18n/angular-locale_ar-kw.js | 125 + .../angular/i18n/angular-locale_ar-lb.js | 125 + .../angular/i18n/angular-locale_ar-ly.js | 125 + .../angular/i18n/angular-locale_ar-ma.js | 125 + .../angular/i18n/angular-locale_ar-mr.js | 125 + .../angular/i18n/angular-locale_ar-om.js | 125 + .../angular/i18n/angular-locale_ar-ps.js | 125 + .../angular/i18n/angular-locale_ar-qa.js | 125 + .../angular/i18n/angular-locale_ar-sa.js | 125 + .../angular/i18n/angular-locale_ar-sd.js | 125 + .../angular/i18n/angular-locale_ar-so.js | 125 + .../angular/i18n/angular-locale_ar-ss.js | 125 + .../angular/i18n/angular-locale_ar-sy.js | 125 + .../angular/i18n/angular-locale_ar-td.js | 125 + .../angular/i18n/angular-locale_ar-tn.js | 125 + .../angular/i18n/angular-locale_ar-ye.js | 125 + .../vendor/angular/i18n/angular-locale_ar.js | 125 + .../angular/i18n/angular-locale_as-in.js | 143 + .../vendor/angular/i18n/angular-locale_as.js | 143 + .../angular/i18n/angular-locale_asa-tz.js | 143 + .../vendor/angular/i18n/angular-locale_asa.js | 143 + .../angular/i18n/angular-locale_ast-es.js | 143 + .../vendor/angular/i18n/angular-locale_ast.js | 143 + .../angular/i18n/angular-locale_az-cyrl-az.js | 125 + .../angular/i18n/angular-locale_az-cyrl.js | 125 + .../angular/i18n/angular-locale_az-latn-az.js | 125 + .../angular/i18n/angular-locale_az-latn.js | 125 + .../vendor/angular/i18n/angular-locale_az.js | 125 + .../angular/i18n/angular-locale_bas-cm.js | 143 + .../vendor/angular/i18n/angular-locale_bas.js | 143 + .../angular/i18n/angular-locale_be-by.js | 125 + .../vendor/angular/i18n/angular-locale_be.js | 125 + .../angular/i18n/angular-locale_bem-zm.js | 143 + .../vendor/angular/i18n/angular-locale_bem.js | 143 + .../angular/i18n/angular-locale_bez-tz.js | 143 + .../vendor/angular/i18n/angular-locale_bez.js | 143 + .../angular/i18n/angular-locale_bg-bg.js | 125 + .../vendor/angular/i18n/angular-locale_bg.js | 125 + .../angular/i18n/angular-locale_bm-latn-ml.js | 143 + .../angular/i18n/angular-locale_bm-latn.js | 143 + .../angular/i18n/angular-locale_bm-ml.js | 115 + .../vendor/angular/i18n/angular-locale_bm.js | 143 + .../angular/i18n/angular-locale_bn-bd.js | 125 + .../angular/i18n/angular-locale_bn-in.js | 125 + .../vendor/angular/i18n/angular-locale_bn.js | 125 + .../angular/i18n/angular-locale_bo-cn.js | 143 + .../angular/i18n/angular-locale_bo-in.js | 143 + .../vendor/angular/i18n/angular-locale_bo.js | 143 + .../angular/i18n/angular-locale_br-fr.js | 125 + .../vendor/angular/i18n/angular-locale_br.js | 125 + .../angular/i18n/angular-locale_brx-in.js | 143 + .../vendor/angular/i18n/angular-locale_brx.js | 143 + .../angular/i18n/angular-locale_bs-cyrl-ba.js | 143 + .../angular/i18n/angular-locale_bs-cyrl.js | 143 + .../angular/i18n/angular-locale_bs-latn-ba.js | 143 + .../angular/i18n/angular-locale_bs-latn.js | 143 + .../vendor/angular/i18n/angular-locale_bs.js | 143 + .../angular/i18n/angular-locale_byn-er.js | 115 + .../vendor/angular/i18n/angular-locale_byn.js | 115 + .../angular/i18n/angular-locale_ca-ad.js | 143 + .../i18n/angular-locale_ca-es-valencia.js | 143 + .../angular/i18n/angular-locale_ca-es.js | 143 + .../angular/i18n/angular-locale_ca-fr.js | 143 + .../angular/i18n/angular-locale_ca-it.js | 143 + .../vendor/angular/i18n/angular-locale_ca.js | 143 + .../angular/i18n/angular-locale_cgg-ug.js | 143 + .../vendor/angular/i18n/angular-locale_cgg.js | 143 + .../angular/i18n/angular-locale_chr-us.js | 125 + .../vendor/angular/i18n/angular-locale_chr.js | 125 + .../i18n/angular-locale_ckb-arab-iq.js | 143 + .../i18n/angular-locale_ckb-arab-ir.js | 143 + .../angular/i18n/angular-locale_ckb-arab.js | 143 + .../angular/i18n/angular-locale_ckb-iq.js | 143 + .../angular/i18n/angular-locale_ckb-ir.js | 143 + .../i18n/angular-locale_ckb-latn-iq.js | 143 + .../angular/i18n/angular-locale_ckb-latn.js | 143 + .../vendor/angular/i18n/angular-locale_ckb.js | 143 + .../angular/i18n/angular-locale_cs-cz.js | 143 + .../vendor/angular/i18n/angular-locale_cs.js | 143 + .../angular/i18n/angular-locale_cy-gb.js | 125 + .../vendor/angular/i18n/angular-locale_cy.js | 125 + .../angular/i18n/angular-locale_da-dk.js | 156 + .../angular/i18n/angular-locale_da-gl.js | 156 + .../vendor/angular/i18n/angular-locale_da.js | 156 + .../angular/i18n/angular-locale_dav-ke.js | 143 + .../vendor/angular/i18n/angular-locale_dav.js | 143 + .../angular/i18n/angular-locale_de-at.js | 143 + .../angular/i18n/angular-locale_de-be.js | 143 + .../angular/i18n/angular-locale_de-ch.js | 143 + .../angular/i18n/angular-locale_de-de.js | 143 + .../angular/i18n/angular-locale_de-li.js | 143 + .../angular/i18n/angular-locale_de-lu.js | 143 + .../vendor/angular/i18n/angular-locale_de.js | 143 + .../angular/i18n/angular-locale_dje-ne.js | 143 + .../vendor/angular/i18n/angular-locale_dje.js | 143 + .../angular/i18n/angular-locale_dsb-de.js | 143 + .../vendor/angular/i18n/angular-locale_dsb.js | 143 + .../angular/i18n/angular-locale_dua-cm.js | 143 + .../vendor/angular/i18n/angular-locale_dua.js | 143 + .../angular/i18n/angular-locale_dyo-sn.js | 143 + .../vendor/angular/i18n/angular-locale_dyo.js | 143 + .../angular/i18n/angular-locale_dz-bt.js | 143 + .../vendor/angular/i18n/angular-locale_dz.js | 143 + .../angular/i18n/angular-locale_ebu-ke.js | 143 + .../vendor/angular/i18n/angular-locale_ebu.js | 143 + .../angular/i18n/angular-locale_ee-gh.js | 143 + .../angular/i18n/angular-locale_ee-tg.js | 143 + .../vendor/angular/i18n/angular-locale_ee.js | 143 + .../angular/i18n/angular-locale_el-cy.js | 125 + .../angular/i18n/angular-locale_el-gr.js | 125 + .../vendor/angular/i18n/angular-locale_el.js | 125 + .../angular/i18n/angular-locale_en-001.js | 143 + .../angular/i18n/angular-locale_en-150.js | 143 + .../angular/i18n/angular-locale_en-ag.js | 143 + .../angular/i18n/angular-locale_en-ai.js | 143 + .../angular/i18n/angular-locale_en-as.js | 143 + .../angular/i18n/angular-locale_en-au.js | 143 + .../angular/i18n/angular-locale_en-bb.js | 143 + .../angular/i18n/angular-locale_en-be.js | 143 + .../angular/i18n/angular-locale_en-bm.js | 143 + .../angular/i18n/angular-locale_en-bs.js | 143 + .../angular/i18n/angular-locale_en-bw.js | 143 + .../angular/i18n/angular-locale_en-bz.js | 143 + .../angular/i18n/angular-locale_en-ca.js | 143 + .../angular/i18n/angular-locale_en-cc.js | 143 + .../angular/i18n/angular-locale_en-ck.js | 143 + .../angular/i18n/angular-locale_en-cm.js | 143 + .../angular/i18n/angular-locale_en-cx.js | 143 + .../angular/i18n/angular-locale_en-dg.js | 143 + .../angular/i18n/angular-locale_en-dm.js | 143 + .../angular/i18n/angular-locale_en-er.js | 143 + .../angular/i18n/angular-locale_en-fj.js | 143 + .../angular/i18n/angular-locale_en-fk.js | 143 + .../angular/i18n/angular-locale_en-fm.js | 143 + .../angular/i18n/angular-locale_en-gb.js | 143 + .../angular/i18n/angular-locale_en-gd.js | 143 + .../angular/i18n/angular-locale_en-gg.js | 143 + .../angular/i18n/angular-locale_en-gh.js | 143 + .../angular/i18n/angular-locale_en-gi.js | 143 + .../angular/i18n/angular-locale_en-gm.js | 143 + .../angular/i18n/angular-locale_en-gu.js | 143 + .../angular/i18n/angular-locale_en-gy.js | 143 + .../angular/i18n/angular-locale_en-hk.js | 143 + .../angular/i18n/angular-locale_en-ie.js | 143 + .../angular/i18n/angular-locale_en-im.js | 143 + .../angular/i18n/angular-locale_en-in.js | 143 + .../angular/i18n/angular-locale_en-io.js | 143 + .../angular/i18n/angular-locale_en-iso.js | 143 + .../angular/i18n/angular-locale_en-je.js | 143 + .../angular/i18n/angular-locale_en-jm.js | 143 + .../angular/i18n/angular-locale_en-ke.js | 143 + .../angular/i18n/angular-locale_en-ki.js | 143 + .../angular/i18n/angular-locale_en-kn.js | 143 + .../angular/i18n/angular-locale_en-ky.js | 143 + .../angular/i18n/angular-locale_en-lc.js | 143 + .../angular/i18n/angular-locale_en-lr.js | 143 + .../angular/i18n/angular-locale_en-ls.js | 143 + .../angular/i18n/angular-locale_en-mg.js | 143 + .../angular/i18n/angular-locale_en-mh.js | 143 + .../angular/i18n/angular-locale_en-mo.js | 143 + .../angular/i18n/angular-locale_en-mp.js | 143 + .../angular/i18n/angular-locale_en-ms.js | 143 + .../angular/i18n/angular-locale_en-mt.js | 143 + .../angular/i18n/angular-locale_en-mu.js | 143 + .../angular/i18n/angular-locale_en-mw.js | 143 + .../angular/i18n/angular-locale_en-my.js | 143 + .../angular/i18n/angular-locale_en-na.js | 143 + .../angular/i18n/angular-locale_en-nf.js | 143 + .../angular/i18n/angular-locale_en-ng.js | 143 + .../angular/i18n/angular-locale_en-nr.js | 143 + .../angular/i18n/angular-locale_en-nu.js | 143 + .../angular/i18n/angular-locale_en-nz.js | 143 + .../angular/i18n/angular-locale_en-pg.js | 143 + .../angular/i18n/angular-locale_en-ph.js | 143 + .../angular/i18n/angular-locale_en-pk.js | 143 + .../angular/i18n/angular-locale_en-pn.js | 143 + .../angular/i18n/angular-locale_en-pr.js | 143 + .../angular/i18n/angular-locale_en-pw.js | 143 + .../angular/i18n/angular-locale_en-rw.js | 143 + .../angular/i18n/angular-locale_en-sb.js | 143 + .../angular/i18n/angular-locale_en-sc.js | 143 + .../angular/i18n/angular-locale_en-sd.js | 143 + .../angular/i18n/angular-locale_en-sg.js | 143 + .../angular/i18n/angular-locale_en-sh.js | 143 + .../angular/i18n/angular-locale_en-sl.js | 143 + .../angular/i18n/angular-locale_en-ss.js | 143 + .../angular/i18n/angular-locale_en-sx.js | 143 + .../angular/i18n/angular-locale_en-sz.js | 143 + .../angular/i18n/angular-locale_en-tc.js | 143 + .../angular/i18n/angular-locale_en-tk.js | 143 + .../angular/i18n/angular-locale_en-to.js | 143 + .../angular/i18n/angular-locale_en-tt.js | 143 + .../angular/i18n/angular-locale_en-tv.js | 143 + .../angular/i18n/angular-locale_en-tz.js | 143 + .../angular/i18n/angular-locale_en-ug.js | 143 + .../angular/i18n/angular-locale_en-um.js | 143 + .../angular/i18n/angular-locale_en-us.js | 143 + .../angular/i18n/angular-locale_en-vc.js | 143 + .../angular/i18n/angular-locale_en-vg.js | 143 + .../angular/i18n/angular-locale_en-vi.js | 143 + .../angular/i18n/angular-locale_en-vu.js | 143 + .../angular/i18n/angular-locale_en-ws.js | 143 + .../angular/i18n/angular-locale_en-za.js | 143 + .../angular/i18n/angular-locale_en-zm.js | 143 + .../angular/i18n/angular-locale_en-zw.js | 143 + .../vendor/angular/i18n/angular-locale_en.js | 143 + .../angular/i18n/angular-locale_eo-001.js | 143 + .../vendor/angular/i18n/angular-locale_eo.js | 143 + .../angular/i18n/angular-locale_es-419.js | 125 + .../angular/i18n/angular-locale_es-ar.js | 125 + .../angular/i18n/angular-locale_es-bo.js | 125 + .../angular/i18n/angular-locale_es-cl.js | 125 + .../angular/i18n/angular-locale_es-co.js | 125 + .../angular/i18n/angular-locale_es-cr.js | 125 + .../angular/i18n/angular-locale_es-cu.js | 125 + .../angular/i18n/angular-locale_es-do.js | 125 + .../angular/i18n/angular-locale_es-ea.js | 125 + .../angular/i18n/angular-locale_es-ec.js | 125 + .../angular/i18n/angular-locale_es-es.js | 125 + .../angular/i18n/angular-locale_es-gq.js | 125 + .../angular/i18n/angular-locale_es-gt.js | 125 + .../angular/i18n/angular-locale_es-hn.js | 125 + .../angular/i18n/angular-locale_es-ic.js | 125 + .../angular/i18n/angular-locale_es-mx.js | 125 + .../angular/i18n/angular-locale_es-ni.js | 125 + .../angular/i18n/angular-locale_es-pa.js | 125 + .../angular/i18n/angular-locale_es-pe.js | 125 + .../angular/i18n/angular-locale_es-ph.js | 125 + .../angular/i18n/angular-locale_es-pr.js | 125 + .../angular/i18n/angular-locale_es-py.js | 125 + .../angular/i18n/angular-locale_es-sv.js | 125 + .../angular/i18n/angular-locale_es-us.js | 125 + .../angular/i18n/angular-locale_es-uy.js | 125 + .../angular/i18n/angular-locale_es-ve.js | 125 + .../vendor/angular/i18n/angular-locale_es.js | 125 + .../angular/i18n/angular-locale_et-ee.js | 143 + .../vendor/angular/i18n/angular-locale_et.js | 143 + .../angular/i18n/angular-locale_eu-es.js | 125 + .../vendor/angular/i18n/angular-locale_eu.js | 125 + .../angular/i18n/angular-locale_ewo-cm.js | 143 + .../vendor/angular/i18n/angular-locale_ewo.js | 143 + .../angular/i18n/angular-locale_fa-af.js | 125 + .../angular/i18n/angular-locale_fa-ir.js | 125 + .../vendor/angular/i18n/angular-locale_fa.js | 125 + .../angular/i18n/angular-locale_ff-cm.js | 143 + .../angular/i18n/angular-locale_ff-gn.js | 143 + .../angular/i18n/angular-locale_ff-mr.js | 143 + .../angular/i18n/angular-locale_ff-sn.js | 143 + .../vendor/angular/i18n/angular-locale_ff.js | 143 + .../angular/i18n/angular-locale_fi-fi.js | 143 + .../vendor/angular/i18n/angular-locale_fi.js | 143 + .../angular/i18n/angular-locale_fil-ph.js | 143 + .../vendor/angular/i18n/angular-locale_fil.js | 143 + .../angular/i18n/angular-locale_fo-fo.js | 143 + .../vendor/angular/i18n/angular-locale_fo.js | 143 + .../angular/i18n/angular-locale_fr-be.js | 125 + .../angular/i18n/angular-locale_fr-bf.js | 125 + .../angular/i18n/angular-locale_fr-bi.js | 125 + .../angular/i18n/angular-locale_fr-bj.js | 125 + .../angular/i18n/angular-locale_fr-bl.js | 125 + .../angular/i18n/angular-locale_fr-ca.js | 125 + .../angular/i18n/angular-locale_fr-cd.js | 125 + .../angular/i18n/angular-locale_fr-cf.js | 125 + .../angular/i18n/angular-locale_fr-cg.js | 125 + .../angular/i18n/angular-locale_fr-ch.js | 125 + .../angular/i18n/angular-locale_fr-ci.js | 125 + .../angular/i18n/angular-locale_fr-cm.js | 125 + .../angular/i18n/angular-locale_fr-dj.js | 125 + .../angular/i18n/angular-locale_fr-dz.js | 125 + .../angular/i18n/angular-locale_fr-fr.js | 125 + .../angular/i18n/angular-locale_fr-ga.js | 125 + .../angular/i18n/angular-locale_fr-gf.js | 125 + .../angular/i18n/angular-locale_fr-gn.js | 125 + .../angular/i18n/angular-locale_fr-gp.js | 125 + .../angular/i18n/angular-locale_fr-gq.js | 125 + .../angular/i18n/angular-locale_fr-ht.js | 125 + .../angular/i18n/angular-locale_fr-km.js | 125 + .../angular/i18n/angular-locale_fr-lu.js | 125 + .../angular/i18n/angular-locale_fr-ma.js | 125 + .../angular/i18n/angular-locale_fr-mc.js | 125 + .../angular/i18n/angular-locale_fr-mf.js | 125 + .../angular/i18n/angular-locale_fr-mg.js | 125 + .../angular/i18n/angular-locale_fr-ml.js | 125 + .../angular/i18n/angular-locale_fr-mq.js | 125 + .../angular/i18n/angular-locale_fr-mr.js | 125 + .../angular/i18n/angular-locale_fr-mu.js | 125 + .../angular/i18n/angular-locale_fr-nc.js | 125 + .../angular/i18n/angular-locale_fr-ne.js | 125 + .../angular/i18n/angular-locale_fr-pf.js | 125 + .../angular/i18n/angular-locale_fr-pm.js | 125 + .../angular/i18n/angular-locale_fr-re.js | 125 + .../angular/i18n/angular-locale_fr-rw.js | 125 + .../angular/i18n/angular-locale_fr-sc.js | 125 + .../angular/i18n/angular-locale_fr-sn.js | 125 + .../angular/i18n/angular-locale_fr-sy.js | 125 + .../angular/i18n/angular-locale_fr-td.js | 125 + .../angular/i18n/angular-locale_fr-tg.js | 125 + .../angular/i18n/angular-locale_fr-tn.js | 125 + .../angular/i18n/angular-locale_fr-vu.js | 125 + .../angular/i18n/angular-locale_fr-wf.js | 125 + .../angular/i18n/angular-locale_fr-yt.js | 125 + .../vendor/angular/i18n/angular-locale_fr.js | 125 + .../angular/i18n/angular-locale_fur-it.js | 143 + .../vendor/angular/i18n/angular-locale_fur.js | 143 + .../angular/i18n/angular-locale_fy-nl.js | 143 + .../vendor/angular/i18n/angular-locale_fy.js | 143 + .../angular/i18n/angular-locale_ga-ie.js | 125 + .../vendor/angular/i18n/angular-locale_ga.js | 125 + .../angular/i18n/angular-locale_gd-gb.js | 143 + .../vendor/angular/i18n/angular-locale_gd.js | 143 + .../angular/i18n/angular-locale_gl-es.js | 143 + .../vendor/angular/i18n/angular-locale_gl.js | 143 + .../angular/i18n/angular-locale_gsw-ch.js | 125 + .../angular/i18n/angular-locale_gsw-fr.js | 125 + .../angular/i18n/angular-locale_gsw-li.js | 125 + .../vendor/angular/i18n/angular-locale_gsw.js | 125 + .../angular/i18n/angular-locale_gu-in.js | 125 + .../vendor/angular/i18n/angular-locale_gu.js | 125 + .../angular/i18n/angular-locale_guz-ke.js | 143 + .../vendor/angular/i18n/angular-locale_guz.js | 143 + .../angular/i18n/angular-locale_gv-im.js | 143 + .../vendor/angular/i18n/angular-locale_gv.js | 143 + .../angular/i18n/angular-locale_ha-latn-gh.js | 143 + .../angular/i18n/angular-locale_ha-latn-ne.js | 143 + .../angular/i18n/angular-locale_ha-latn-ng.js | 143 + .../angular/i18n/angular-locale_ha-latn.js | 143 + .../vendor/angular/i18n/angular-locale_ha.js | 143 + .../angular/i18n/angular-locale_haw-us.js | 125 + .../vendor/angular/i18n/angular-locale_haw.js | 125 + .../angular/i18n/angular-locale_he-il.js | 143 + .../vendor/angular/i18n/angular-locale_he.js | 143 + .../angular/i18n/angular-locale_hi-in.js | 125 + .../vendor/angular/i18n/angular-locale_hi.js | 125 + .../angular/i18n/angular-locale_hr-ba.js | 143 + .../angular/i18n/angular-locale_hr-hr.js | 143 + .../vendor/angular/i18n/angular-locale_hr.js | 143 + .../angular/i18n/angular-locale_hsb-de.js | 143 + .../vendor/angular/i18n/angular-locale_hsb.js | 143 + .../angular/i18n/angular-locale_hu-hu.js | 125 + .../vendor/angular/i18n/angular-locale_hu.js | 125 + .../angular/i18n/angular-locale_hy-am.js | 125 + .../vendor/angular/i18n/angular-locale_hy.js | 125 + .../angular/i18n/angular-locale_ia-fr.js | 128 + .../vendor/angular/i18n/angular-locale_ia.js | 128 + .../angular/i18n/angular-locale_id-id.js | 125 + .../vendor/angular/i18n/angular-locale_id.js | 125 + .../angular/i18n/angular-locale_ig-ng.js | 143 + .../vendor/angular/i18n/angular-locale_ig.js | 143 + .../angular/i18n/angular-locale_ii-cn.js | 143 + .../vendor/angular/i18n/angular-locale_ii.js | 143 + .../vendor/angular/i18n/angular-locale_in.js | 125 + .../angular/i18n/angular-locale_is-is.js | 156 + .../vendor/angular/i18n/angular-locale_is.js | 156 + .../angular/i18n/angular-locale_it-ch.js | 143 + .../angular/i18n/angular-locale_it-it.js | 143 + .../angular/i18n/angular-locale_it-sm.js | 143 + .../vendor/angular/i18n/angular-locale_it.js | 143 + .../vendor/angular/i18n/angular-locale_iw.js | 143 + .../angular/i18n/angular-locale_ja-jp.js | 125 + .../vendor/angular/i18n/angular-locale_ja.js | 125 + .../angular/i18n/angular-locale_jgo-cm.js | 143 + .../vendor/angular/i18n/angular-locale_jgo.js | 143 + .../angular/i18n/angular-locale_jmc-tz.js | 143 + .../vendor/angular/i18n/angular-locale_jmc.js | 143 + .../angular/i18n/angular-locale_ka-ge.js | 125 + .../vendor/angular/i18n/angular-locale_ka.js | 125 + .../angular/i18n/angular-locale_kab-dz.js | 143 + .../vendor/angular/i18n/angular-locale_kab.js | 143 + .../angular/i18n/angular-locale_kam-ke.js | 143 + .../vendor/angular/i18n/angular-locale_kam.js | 143 + .../angular/i18n/angular-locale_kde-tz.js | 143 + .../vendor/angular/i18n/angular-locale_kde.js | 143 + .../angular/i18n/angular-locale_kea-cv.js | 143 + .../vendor/angular/i18n/angular-locale_kea.js | 143 + .../angular/i18n/angular-locale_khq-ml.js | 143 + .../vendor/angular/i18n/angular-locale_khq.js | 143 + .../angular/i18n/angular-locale_ki-ke.js | 143 + .../vendor/angular/i18n/angular-locale_ki.js | 143 + .../angular/i18n/angular-locale_kk-cyrl-kz.js | 125 + .../angular/i18n/angular-locale_kk-cyrl.js | 125 + .../vendor/angular/i18n/angular-locale_kk.js | 125 + .../angular/i18n/angular-locale_kkj-cm.js | 143 + .../vendor/angular/i18n/angular-locale_kkj.js | 143 + .../angular/i18n/angular-locale_kl-gl.js | 143 + .../vendor/angular/i18n/angular-locale_kl.js | 143 + .../angular/i18n/angular-locale_kln-ke.js | 143 + .../vendor/angular/i18n/angular-locale_kln.js | 143 + .../angular/i18n/angular-locale_km-kh.js | 125 + .../vendor/angular/i18n/angular-locale_km.js | 125 + .../angular/i18n/angular-locale_kn-in.js | 125 + .../vendor/angular/i18n/angular-locale_kn.js | 125 + .../angular/i18n/angular-locale_ko-kp.js | 125 + .../angular/i18n/angular-locale_ko-kr.js | 125 + .../vendor/angular/i18n/angular-locale_ko.js | 125 + .../angular/i18n/angular-locale_kok-in.js | 143 + .../vendor/angular/i18n/angular-locale_kok.js | 143 + .../angular/i18n/angular-locale_ks-arab-in.js | 143 + .../angular/i18n/angular-locale_ks-arab.js | 143 + .../vendor/angular/i18n/angular-locale_ks.js | 143 + .../angular/i18n/angular-locale_ksb-tz.js | 143 + .../vendor/angular/i18n/angular-locale_ksb.js | 143 + .../angular/i18n/angular-locale_ksf-cm.js | 143 + .../vendor/angular/i18n/angular-locale_ksf.js | 143 + .../angular/i18n/angular-locale_ksh-de.js | 143 + .../vendor/angular/i18n/angular-locale_ksh.js | 143 + .../angular/i18n/angular-locale_kw-gb.js | 143 + .../vendor/angular/i18n/angular-locale_kw.js | 143 + .../angular/i18n/angular-locale_ky-cyrl-kg.js | 125 + .../angular/i18n/angular-locale_ky-cyrl.js | 125 + .../vendor/angular/i18n/angular-locale_ky.js | 125 + .../angular/i18n/angular-locale_lag-tz.js | 143 + .../vendor/angular/i18n/angular-locale_lag.js | 143 + .../angular/i18n/angular-locale_lb-lu.js | 143 + .../vendor/angular/i18n/angular-locale_lb.js | 143 + .../angular/i18n/angular-locale_lg-ug.js | 143 + .../vendor/angular/i18n/angular-locale_lg.js | 143 + .../angular/i18n/angular-locale_lkt-us.js | 143 + .../vendor/angular/i18n/angular-locale_lkt.js | 143 + .../angular/i18n/angular-locale_ln-ao.js | 125 + .../angular/i18n/angular-locale_ln-cd.js | 125 + .../angular/i18n/angular-locale_ln-cf.js | 125 + .../angular/i18n/angular-locale_ln-cg.js | 125 + .../vendor/angular/i18n/angular-locale_ln.js | 125 + .../angular/i18n/angular-locale_lo-la.js | 125 + .../vendor/angular/i18n/angular-locale_lo.js | 125 + .../angular/i18n/angular-locale_lt-lt.js | 143 + .../vendor/angular/i18n/angular-locale_lt.js | 143 + .../angular/i18n/angular-locale_lu-cd.js | 143 + .../vendor/angular/i18n/angular-locale_lu.js | 143 + .../angular/i18n/angular-locale_luo-ke.js | 143 + .../vendor/angular/i18n/angular-locale_luo.js | 143 + .../angular/i18n/angular-locale_luy-ke.js | 143 + .../vendor/angular/i18n/angular-locale_luy.js | 143 + .../angular/i18n/angular-locale_lv-lv.js | 143 + .../vendor/angular/i18n/angular-locale_lv.js | 143 + .../angular/i18n/angular-locale_mas-ke.js | 143 + .../angular/i18n/angular-locale_mas-tz.js | 143 + .../vendor/angular/i18n/angular-locale_mas.js | 143 + .../angular/i18n/angular-locale_mer-ke.js | 143 + .../vendor/angular/i18n/angular-locale_mer.js | 143 + .../angular/i18n/angular-locale_mfe-mu.js | 143 + .../vendor/angular/i18n/angular-locale_mfe.js | 143 + .../angular/i18n/angular-locale_mg-mg.js | 143 + .../vendor/angular/i18n/angular-locale_mg.js | 143 + .../angular/i18n/angular-locale_mgh-mz.js | 143 + .../vendor/angular/i18n/angular-locale_mgh.js | 143 + .../angular/i18n/angular-locale_mgo-cm.js | 143 + .../vendor/angular/i18n/angular-locale_mgo.js | 143 + .../angular/i18n/angular-locale_mk-mk.js | 143 + .../vendor/angular/i18n/angular-locale_mk.js | 143 + .../angular/i18n/angular-locale_ml-in.js | 125 + .../vendor/angular/i18n/angular-locale_ml.js | 125 + .../angular/i18n/angular-locale_mn-cyrl-mn.js | 125 + .../angular/i18n/angular-locale_mn-cyrl.js | 125 + .../vendor/angular/i18n/angular-locale_mn.js | 125 + .../angular/i18n/angular-locale_mr-in.js | 125 + .../vendor/angular/i18n/angular-locale_mr.js | 125 + .../angular/i18n/angular-locale_ms-latn-bn.js | 125 + .../angular/i18n/angular-locale_ms-latn-my.js | 125 + .../angular/i18n/angular-locale_ms-latn-sg.js | 125 + .../angular/i18n/angular-locale_ms-latn.js | 125 + .../vendor/angular/i18n/angular-locale_ms.js | 125 + .../angular/i18n/angular-locale_mt-mt.js | 125 + .../vendor/angular/i18n/angular-locale_mt.js | 125 + .../angular/i18n/angular-locale_mua-cm.js | 143 + .../vendor/angular/i18n/angular-locale_mua.js | 143 + .../angular/i18n/angular-locale_my-mm.js | 125 + .../vendor/angular/i18n/angular-locale_my.js | 125 + .../angular/i18n/angular-locale_naq-na.js | 143 + .../vendor/angular/i18n/angular-locale_naq.js | 143 + .../angular/i18n/angular-locale_nb-no.js | 125 + .../angular/i18n/angular-locale_nb-sj.js | 125 + .../vendor/angular/i18n/angular-locale_nb.js | 125 + .../angular/i18n/angular-locale_nd-zw.js | 143 + .../vendor/angular/i18n/angular-locale_nd.js | 143 + .../angular/i18n/angular-locale_ne-in.js | 125 + .../angular/i18n/angular-locale_ne-np.js | 125 + .../vendor/angular/i18n/angular-locale_ne.js | 125 + .../angular/i18n/angular-locale_nl-aw.js | 143 + .../angular/i18n/angular-locale_nl-be.js | 143 + .../angular/i18n/angular-locale_nl-bq.js | 143 + .../angular/i18n/angular-locale_nl-cw.js | 143 + .../angular/i18n/angular-locale_nl-nl.js | 143 + .../angular/i18n/angular-locale_nl-sr.js | 143 + .../angular/i18n/angular-locale_nl-sx.js | 143 + .../vendor/angular/i18n/angular-locale_nl.js | 143 + .../angular/i18n/angular-locale_nmg-cm.js | 143 + .../vendor/angular/i18n/angular-locale_nmg.js | 143 + .../angular/i18n/angular-locale_nn-no.js | 143 + .../vendor/angular/i18n/angular-locale_nn.js | 143 + .../angular/i18n/angular-locale_nnh-cm.js | 143 + .../vendor/angular/i18n/angular-locale_nnh.js | 143 + .../angular/i18n/angular-locale_no-no.js | 125 + .../vendor/angular/i18n/angular-locale_no.js | 125 + .../angular/i18n/angular-locale_nr-za.js | 128 + .../vendor/angular/i18n/angular-locale_nr.js | 128 + .../angular/i18n/angular-locale_nso-za.js | 128 + .../vendor/angular/i18n/angular-locale_nso.js | 128 + .../angular/i18n/angular-locale_nus-sd.js | 143 + .../vendor/angular/i18n/angular-locale_nus.js | 143 + .../angular/i18n/angular-locale_nyn-ug.js | 143 + .../vendor/angular/i18n/angular-locale_nyn.js | 143 + .../angular/i18n/angular-locale_om-et.js | 143 + .../angular/i18n/angular-locale_om-ke.js | 143 + .../vendor/angular/i18n/angular-locale_om.js | 143 + .../angular/i18n/angular-locale_or-in.js | 125 + .../vendor/angular/i18n/angular-locale_or.js | 125 + .../angular/i18n/angular-locale_os-ge.js | 143 + .../angular/i18n/angular-locale_os-ru.js | 143 + .../vendor/angular/i18n/angular-locale_os.js | 143 + .../angular/i18n/angular-locale_pa-arab-pk.js | 125 + .../angular/i18n/angular-locale_pa-arab.js | 125 + .../angular/i18n/angular-locale_pa-guru-in.js | 125 + .../angular/i18n/angular-locale_pa-guru.js | 125 + .../vendor/angular/i18n/angular-locale_pa.js | 125 + .../angular/i18n/angular-locale_pl-pl.js | 143 + .../vendor/angular/i18n/angular-locale_pl.js | 143 + .../angular/i18n/angular-locale_ps-af.js | 143 + .../vendor/angular/i18n/angular-locale_ps.js | 143 + .../angular/i18n/angular-locale_pt-ao.js | 125 + .../angular/i18n/angular-locale_pt-br.js | 125 + .../angular/i18n/angular-locale_pt-cv.js | 125 + .../angular/i18n/angular-locale_pt-gw.js | 125 + .../angular/i18n/angular-locale_pt-mo.js | 125 + .../angular/i18n/angular-locale_pt-mz.js | 125 + .../angular/i18n/angular-locale_pt-pt.js | 125 + .../angular/i18n/angular-locale_pt-st.js | 125 + .../angular/i18n/angular-locale_pt-tl.js | 125 + .../vendor/angular/i18n/angular-locale_pt.js | 125 + .../angular/i18n/angular-locale_qu-bo.js | 143 + .../angular/i18n/angular-locale_qu-ec.js | 143 + .../angular/i18n/angular-locale_qu-pe.js | 143 + .../vendor/angular/i18n/angular-locale_qu.js | 143 + .../angular/i18n/angular-locale_rm-ch.js | 143 + .../vendor/angular/i18n/angular-locale_rm.js | 143 + .../angular/i18n/angular-locale_rn-bi.js | 143 + .../vendor/angular/i18n/angular-locale_rn.js | 143 + .../angular/i18n/angular-locale_ro-md.js | 143 + .../angular/i18n/angular-locale_ro-ro.js | 143 + .../vendor/angular/i18n/angular-locale_ro.js | 143 + .../angular/i18n/angular-locale_rof-tz.js | 143 + .../vendor/angular/i18n/angular-locale_rof.js | 143 + .../angular/i18n/angular-locale_ru-by.js | 143 + .../angular/i18n/angular-locale_ru-kg.js | 143 + .../angular/i18n/angular-locale_ru-kz.js | 143 + .../angular/i18n/angular-locale_ru-md.js | 143 + .../angular/i18n/angular-locale_ru-ru.js | 143 + .../angular/i18n/angular-locale_ru-ua.js | 143 + .../vendor/angular/i18n/angular-locale_ru.js | 143 + .../angular/i18n/angular-locale_rw-rw.js | 143 + .../vendor/angular/i18n/angular-locale_rw.js | 143 + .../angular/i18n/angular-locale_rwk-tz.js | 143 + .../vendor/angular/i18n/angular-locale_rwk.js | 143 + .../angular/i18n/angular-locale_sah-ru.js | 143 + .../vendor/angular/i18n/angular-locale_sah.js | 143 + .../angular/i18n/angular-locale_saq-ke.js | 143 + .../vendor/angular/i18n/angular-locale_saq.js | 143 + .../angular/i18n/angular-locale_sbp-tz.js | 143 + .../vendor/angular/i18n/angular-locale_sbp.js | 143 + .../angular/i18n/angular-locale_se-fi.js | 143 + .../angular/i18n/angular-locale_se-no.js | 143 + .../angular/i18n/angular-locale_se-se.js | 143 + .../vendor/angular/i18n/angular-locale_se.js | 143 + .../angular/i18n/angular-locale_seh-mz.js | 143 + .../vendor/angular/i18n/angular-locale_seh.js | 143 + .../angular/i18n/angular-locale_ses-ml.js | 143 + .../vendor/angular/i18n/angular-locale_ses.js | 143 + .../angular/i18n/angular-locale_sg-cf.js | 143 + .../vendor/angular/i18n/angular-locale_sg.js | 143 + .../i18n/angular-locale_shi-latn-ma.js | 143 + .../angular/i18n/angular-locale_shi-latn.js | 143 + .../i18n/angular-locale_shi-tfng-ma.js | 143 + .../angular/i18n/angular-locale_shi-tfng.js | 143 + .../vendor/angular/i18n/angular-locale_shi.js | 143 + .../angular/i18n/angular-locale_si-lk.js | 143 + .../vendor/angular/i18n/angular-locale_si.js | 143 + .../angular/i18n/angular-locale_sk-sk.js | 143 + .../vendor/angular/i18n/angular-locale_sk.js | 143 + .../angular/i18n/angular-locale_sl-si.js | 143 + .../vendor/angular/i18n/angular-locale_sl.js | 143 + .../angular/i18n/angular-locale_smn-fi.js | 143 + .../vendor/angular/i18n/angular-locale_smn.js | 143 + .../angular/i18n/angular-locale_sn-zw.js | 143 + .../vendor/angular/i18n/angular-locale_sn.js | 143 + .../angular/i18n/angular-locale_so-dj.js | 143 + .../angular/i18n/angular-locale_so-et.js | 143 + .../angular/i18n/angular-locale_so-ke.js | 143 + .../angular/i18n/angular-locale_so-so.js | 143 + .../vendor/angular/i18n/angular-locale_so.js | 143 + .../angular/i18n/angular-locale_sq-al.js | 125 + .../angular/i18n/angular-locale_sq-mk.js | 125 + .../angular/i18n/angular-locale_sq-xk.js | 125 + .../vendor/angular/i18n/angular-locale_sq.js | 125 + .../angular/i18n/angular-locale_sr-cyrl-ba.js | 143 + .../angular/i18n/angular-locale_sr-cyrl-me.js | 143 + .../angular/i18n/angular-locale_sr-cyrl-rs.js | 143 + .../angular/i18n/angular-locale_sr-cyrl-xk.js | 143 + .../angular/i18n/angular-locale_sr-cyrl.js | 143 + .../angular/i18n/angular-locale_sr-latn-ba.js | 143 + .../angular/i18n/angular-locale_sr-latn-me.js | 143 + .../angular/i18n/angular-locale_sr-latn-rs.js | 143 + .../angular/i18n/angular-locale_sr-latn-xk.js | 143 + .../angular/i18n/angular-locale_sr-latn.js | 143 + .../vendor/angular/i18n/angular-locale_sr.js | 143 + .../angular/i18n/angular-locale_ss-sz.js | 128 + .../angular/i18n/angular-locale_ss-za.js | 128 + .../vendor/angular/i18n/angular-locale_ss.js | 128 + .../angular/i18n/angular-locale_ssy-er.js | 128 + .../vendor/angular/i18n/angular-locale_ssy.js | 128 + .../angular/i18n/angular-locale_st-ls.js | 115 + .../angular/i18n/angular-locale_st-za.js | 115 + .../vendor/angular/i18n/angular-locale_st.js | 115 + .../angular/i18n/angular-locale_sv-ax.js | 143 + .../angular/i18n/angular-locale_sv-fi.js | 143 + .../angular/i18n/angular-locale_sv-se.js | 143 + .../vendor/angular/i18n/angular-locale_sv.js | 143 + .../angular/i18n/angular-locale_sw-cd.js | 143 + .../angular/i18n/angular-locale_sw-ke.js | 143 + .../angular/i18n/angular-locale_sw-tz.js | 143 + .../angular/i18n/angular-locale_sw-ug.js | 143 + .../vendor/angular/i18n/angular-locale_sw.js | 143 + .../angular/i18n/angular-locale_swc-cd.js | 128 + .../vendor/angular/i18n/angular-locale_swc.js | 128 + .../angular/i18n/angular-locale_ta-in.js | 125 + .../angular/i18n/angular-locale_ta-lk.js | 125 + .../angular/i18n/angular-locale_ta-my.js | 125 + .../angular/i18n/angular-locale_ta-sg.js | 125 + .../vendor/angular/i18n/angular-locale_ta.js | 125 + .../angular/i18n/angular-locale_te-in.js | 125 + .../vendor/angular/i18n/angular-locale_te.js | 125 + .../angular/i18n/angular-locale_teo-ke.js | 143 + .../angular/i18n/angular-locale_teo-ug.js | 143 + .../vendor/angular/i18n/angular-locale_teo.js | 143 + .../angular/i18n/angular-locale_tg-cyrl-tj.js | 115 + .../angular/i18n/angular-locale_tg-cyrl.js | 115 + .../vendor/angular/i18n/angular-locale_tg.js | 115 + .../angular/i18n/angular-locale_th-th.js | 125 + .../vendor/angular/i18n/angular-locale_th.js | 125 + .../angular/i18n/angular-locale_ti-er.js | 143 + .../angular/i18n/angular-locale_ti-et.js | 143 + .../vendor/angular/i18n/angular-locale_ti.js | 143 + .../angular/i18n/angular-locale_tig-er.js | 115 + .../vendor/angular/i18n/angular-locale_tig.js | 115 + .../vendor/angular/i18n/angular-locale_tl.js | 143 + .../angular/i18n/angular-locale_tn-bw.js | 128 + .../angular/i18n/angular-locale_tn-za.js | 128 + .../vendor/angular/i18n/angular-locale_tn.js | 128 + .../angular/i18n/angular-locale_to-to.js | 143 + .../vendor/angular/i18n/angular-locale_to.js | 143 + .../angular/i18n/angular-locale_tr-cy.js | 125 + .../angular/i18n/angular-locale_tr-tr.js | 125 + .../vendor/angular/i18n/angular-locale_tr.js | 125 + .../angular/i18n/angular-locale_ts-za.js | 128 + .../vendor/angular/i18n/angular-locale_ts.js | 128 + .../angular/i18n/angular-locale_twq-ne.js | 143 + .../vendor/angular/i18n/angular-locale_twq.js | 143 + .../i18n/angular-locale_tzm-latn-ma.js | 143 + .../angular/i18n/angular-locale_tzm-latn.js | 143 + .../vendor/angular/i18n/angular-locale_tzm.js | 143 + .../angular/i18n/angular-locale_ug-arab-cn.js | 143 + .../angular/i18n/angular-locale_ug-arab.js | 143 + .../vendor/angular/i18n/angular-locale_ug.js | 143 + .../angular/i18n/angular-locale_uk-ua.js | 143 + .../vendor/angular/i18n/angular-locale_uk.js | 143 + .../angular/i18n/angular-locale_ur-in.js | 143 + .../angular/i18n/angular-locale_ur-pk.js | 143 + .../vendor/angular/i18n/angular-locale_ur.js | 143 + .../angular/i18n/angular-locale_uz-arab-af.js | 125 + .../angular/i18n/angular-locale_uz-arab.js | 125 + .../angular/i18n/angular-locale_uz-cyrl-uz.js | 125 + .../angular/i18n/angular-locale_uz-cyrl.js | 125 + .../angular/i18n/angular-locale_uz-latn-uz.js | 125 + .../angular/i18n/angular-locale_uz-latn.js | 125 + .../vendor/angular/i18n/angular-locale_uz.js | 125 + .../i18n/angular-locale_vai-latn-lr.js | 143 + .../angular/i18n/angular-locale_vai-latn.js | 143 + .../i18n/angular-locale_vai-vaii-lr.js | 143 + .../angular/i18n/angular-locale_vai-vaii.js | 143 + .../vendor/angular/i18n/angular-locale_vai.js | 143 + .../angular/i18n/angular-locale_ve-za.js | 128 + .../vendor/angular/i18n/angular-locale_ve.js | 128 + .../angular/i18n/angular-locale_vi-vn.js | 125 + .../vendor/angular/i18n/angular-locale_vi.js | 125 + .../angular/i18n/angular-locale_vo-001.js | 128 + .../vendor/angular/i18n/angular-locale_vo.js | 128 + .../angular/i18n/angular-locale_vun-tz.js | 143 + .../vendor/angular/i18n/angular-locale_vun.js | 143 + .../angular/i18n/angular-locale_wae-ch.js | 143 + .../vendor/angular/i18n/angular-locale_wae.js | 143 + .../angular/i18n/angular-locale_wal-et.js | 115 + .../vendor/angular/i18n/angular-locale_wal.js | 115 + .../angular/i18n/angular-locale_xh-za.js | 115 + .../vendor/angular/i18n/angular-locale_xh.js | 115 + .../angular/i18n/angular-locale_xog-ug.js | 143 + .../vendor/angular/i18n/angular-locale_xog.js | 143 + .../angular/i18n/angular-locale_yav-cm.js | 143 + .../vendor/angular/i18n/angular-locale_yav.js | 143 + .../angular/i18n/angular-locale_yi-001.js | 143 + .../vendor/angular/i18n/angular-locale_yi.js | 143 + .../angular/i18n/angular-locale_yo-bj.js | 143 + .../angular/i18n/angular-locale_yo-ng.js | 143 + .../vendor/angular/i18n/angular-locale_yo.js | 143 + .../angular/i18n/angular-locale_zgh-ma.js | 143 + .../vendor/angular/i18n/angular-locale_zgh.js | 143 + .../angular/i18n/angular-locale_zh-cn.js | 125 + .../angular/i18n/angular-locale_zh-hans-cn.js | 125 + .../angular/i18n/angular-locale_zh-hans-hk.js | 125 + .../angular/i18n/angular-locale_zh-hans-mo.js | 125 + .../angular/i18n/angular-locale_zh-hans-sg.js | 125 + .../angular/i18n/angular-locale_zh-hans.js | 125 + .../angular/i18n/angular-locale_zh-hant-hk.js | 125 + .../angular/i18n/angular-locale_zh-hant-mo.js | 125 + .../angular/i18n/angular-locale_zh-hant-tw.js | 125 + .../angular/i18n/angular-locale_zh-hant.js | 125 + .../angular/i18n/angular-locale_zh-hk.js | 125 + .../angular/i18n/angular-locale_zh-tw.js | 125 + .../vendor/angular/i18n/angular-locale_zh.js | 125 + .../angular/i18n/angular-locale_zu-za.js | 125 + .../vendor/angular/i18n/angular-locale_zu.js | 125 + .../notification/angular-ui-notification.css | 105 + .../notification/angular-ui-notification.js | 183 + .../static/vendor/angular/version.json | 1 + .../static/vendor/angular/version.txt | 1 + .../css/bootstrap-material-design.css | 3426 ++ .../css/bootstrap-material-design.css.map | 1 + .../css/bootstrap-material-design.min.css | 2 + .../css/bootstrap-material-design.min.css.map | 1 + .../bootstrap-material-design/css/ripples.css | 47 + .../css/ripples.css.map | 1 + .../css/ripples.min.css | 2 + .../css/ripples.min.css.map | 1 + .../bootstrap-material-design/js/material.js | 352 + .../js/material.min.js | 2 + .../js/material.min.js.map | 1 + .../bootstrap-material-design/js/ripples.js | 324 + .../js/ripples.min.js | 2 + .../js/ripples.min.js.map | 1 + .../sassc/bootstrap-material-design.css | 2234 + .../sassc/bootstrap-material-design.css.map | 7 + .../sassc/ripples.css | 37 + .../sassc/ripples.css.map | 7 + .../vendor/bootstrap/css/bootstrap-theme.css | 587 + .../bootstrap/css/bootstrap-theme.css.map | 1 + .../bootstrap/css/bootstrap-theme.min.css | 5 + .../static/vendor/bootstrap/css/bootstrap.css | 6800 +++ .../vendor/bootstrap/css/bootstrap.css.map | 1 + .../vendor/bootstrap/css/bootstrap.min.css | 5 + .../fonts/glyphicons-halflings-regular.eot | Bin 0 -> 20127 bytes .../fonts/glyphicons-halflings-regular.svg | 288 + .../fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 45404 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 0 -> 23424 bytes .../fonts/glyphicons-halflings-regular.woff2 | Bin 0 -> 18028 bytes .../static/vendor/bootstrap/js/bootstrap.js | 2363 + .../vendor/bootstrap/js/bootstrap.min.js | 7 + .../static/vendor/bootstrap/js/npm.js | 13 + .../static/vendor/chosen/angular-chosen.js | 143 + .../vendor/chosen/angular-chosen.min.js | 7 + .../static/vendor/chosen/chosen-spinner.css | 12 + .../static/vendor/chosen/chosen-sprite.png | Bin 0 -> 514 bytes .../static/vendor/chosen/chosen-sprite@2x.png | Bin 0 -> 690 bytes .../resources/static/vendor/chosen/chosen.css | 450 + .../static/vendor/chosen/chosen.jquery.min.js | 2 + .../static/vendor/chosen/spinner.gif | Bin 0 -> 1849 bytes src/main/resources/static/vendor/common.js | 10 + .../angular-eonasdan-datetimepicker.min.js | 1 + .../bootstrap-datetimepicker.min.css | 5 + .../bootstrap-datetimepicker.min.js | 9 + .../vendor/datatimepicker/moment.min.js | 7 + .../vendor/dropdown/jquery.dropdown.css | 97 + .../static/vendor/dropdown/jquery.dropdown.js | 410 + .../static/vendor/echarts/echarts.min.js | 45 + .../static/vendor/jquery/jquery-3.0.0.min.js | 3 + .../static/vendor/jquery/jquery1.11.3.min.js | 6 + .../static/vendor/json-bigint/json-bigint.js | 2668 + .../static/vendor/loading/css/default.css | 228 + .../vendor/loading/css/ladda-theme.scss | 78 + .../static/vendor/loading/css/ladda.scss | 486 + .../static/vendor/loading/css/normalize.css | 1 + .../vendor/loading/dist/ladda-themeless.css | 330 + .../loading/dist/ladda-themeless.min.css | 7 + .../static/vendor/loading/dist/ladda.css | 392 + .../vendor/loading/dist/ladda.jquery.min.js | 8 + .../static/vendor/loading/dist/ladda.js | 160 + .../static/vendor/loading/dist/ladda.min.css | 7 + .../static/vendor/loading/dist/ladda.min.js | 8 + .../static/vendor/loading/dist/spin.js | 218 + .../static/vendor/loading/dist/spin.min.js | 1 + .../static/vendor/loading/fonts/icomoon.eot | Bin 0 -> 1540 bytes .../static/vendor/loading/fonts/icomoon.svg | 12 + .../static/vendor/loading/fonts/icomoon.ttf | Bin 0 -> 1376 bytes .../static/vendor/loading/fonts/icomoon.woff | Bin 0 -> 1452 bytes .../static/vendor/loading/js/ladda.jquery.js | 46 + .../static/vendor/loading/js/ladda.js | 412 + .../static/vendor/loading/js/spin.js | 349 + .../vendor/md-tab/angular-messages.min.js | 12 + .../static/vendor/md-tab/angular-route.min.js | 15 + .../resources/static/vendor/md-tab/docs.css | 1199 + .../static/vendor/md-tab/svg-assets-cache.js | 71 + .../ng-dialog/ngDialog-theme-default.css | 192 + .../static/vendor/ng-dialog/ngDialog.min.css | 1 + .../static/vendor/ng-dialog/ngDialog.min.js | 890 + .../notification/angular-ui-notification.css | 105 + .../notification/angular-ui-notification.js | 183 + .../static/vendor/pagination/tm.pagination.js | 192 + .../static/vendor/preLoading/main.js | 22 + .../vendor/preLoading/modernizr-2.6.2.min.js | 4 + .../static/vendor/validator/validator.js | 341 + .../static/vendor/validator/validator.min.js | 9 + .../resources/static/view/layout/_footer.html | 16 + .../resources/static/view/layout/_header.html | 72 + src/main/resources/static/view/pages/404.html | 26 + src/main/resources/static/view/pages/50x.html | 16 + .../resources/static/view/pages/cluster.html | 129 + .../resources/static/view/pages/config.html | 103 + .../resources/static/view/pages/consumer.html | 452 + .../resources/static/view/pages/index.html | 58 + .../resources/static/view/pages/login.html | 59 + .../resources/static/view/pages/message.html | 276 + .../static/view/pages/messageTrace.html | 344 + src/main/resources/static/view/pages/ops.html | 57 + .../resources/static/view/pages/producer.html | 56 + .../resources/static/view/pages/topic.html | 550 + .../static/view/pages/un_support_browser.html | 62 + src/main/resources/users.properties | 26 + src/test/resources/application.properties | 32 + src/test/resources/logback.xml | 30 + .../resources/logback_rocketmq_client.xml | 44 + src/test/resources/users.properties | 19 + style/copyright/Apache.xml | 23 + style/copyright/profiles_settings.xml | 64 + style/rmq_checkstyle.xml | 135 + style/rmq_codeStyle.xml | 157 + 1059 files changed, 257006 insertions(+) create mode 100644 .asf.yaml create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/compiler.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 .idea/vcs.xml create mode 100644 .travis.yml create mode 100644 DEPENDENCIES create mode 100644 LICENSE create mode 100644 NOTICE create mode 100644 README.md create mode 100644 docs/1_0_0/Milestone.md create mode 100644 docs/1_0_0/UserGuide_CN.md create mode 100644 docs/1_0_0/UserGuide_EN.md create mode 100644 pom.xml create mode 100644 src/main/docker/Dockerfile create mode 100644 src/main/java/org/apache/rocketmq/dashboard/Application.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/admin/MQAdminFactory.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/admin/MQAdminPooledObjectFactory.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/admin/MqAdminExtObjectPool.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/aspect/admin/MQAdminAspect.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/aspect/admin/annotation/OriginalControllerReturnValue.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/config/AuthWebMVCConfigurerAdapter.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/config/RMQConfigure.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/controller/ClusterController.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/controller/ConfigController.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/controller/ConsumerController.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/controller/DashboardController.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/controller/LoginController.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/controller/MessageController.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/controller/MessageTraceController.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/controller/MonitorController.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/controller/NamesvrController.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/controller/OpsController.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/controller/ProducerController.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/controller/TestController.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/controller/TopicController.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/exception/ServiceException.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/filter/HttpBasicAuthorizedFilter.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/interceptor/AuthInterceptor.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/ConnectionInfo.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/ConsumerGroupRollBackStat.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/ConsumerMonitorConfig.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/GroupConsumeInfo.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/LoginInfo.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/LoginResult.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/MessagePage.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/MessagePageTask.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/MessageQueryByPage.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/MessageTraceView.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/MessageView.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/QueueOffsetInfo.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/QueueStatInfo.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/TopicConsumerInfo.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/User.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/UserInfo.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/request/ConsumerConfigInfo.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/request/DeleteSubGroupRequest.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/request/MessageQuery.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/request/ResetOffsetRequest.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/request/SendTopicMessageRequest.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/request/TopicConfigInfo.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/trace/MessageTraceGraph.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/trace/MessageTraceStatusEnum.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/trace/ProducerNode.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/trace/SubscriptionNode.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/model/trace/TraceNode.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/permisssion/Permission.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/permisssion/PermissionAspect.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/permisssion/UserRoleEnum.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/AbstractCommonService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/ClusterService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/ConfigService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/ConsumerService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/DashboardCollectService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/DashboardService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/LoginService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/MessageService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/MessageTraceService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/MonitorService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/OpsService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/PermissionService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/ProducerService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/TopicService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/UserService.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/checker/CheckerType.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/checker/RocketMqChecker.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/checker/impl/ClusterHealthCheckerImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/checker/impl/TopicOnlyOneBrokerCheckerImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/client/MQAdminExtImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/client/MQAdminInstance.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/AbstractFileStore.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/ClusterServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/ConfigServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/ConsumerServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/DashboardCollectServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/DashboardServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/LoginServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/MessageServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/MessageTraceServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/MonitorServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/OpsServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/PermissionServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/ProducerServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/TopicServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/service/impl/UserServiceImpl.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/support/GlobalExceptionHandler.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/support/GlobalRestfulResponseBodyAdvice.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/support/JsonResult.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/task/DashboardCollectTask.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/task/MonitorTask.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/util/JsonUtil.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/util/MatcherUtil.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/util/MsgTraceDecodeUtil.java create mode 100644 src/main/java/org/apache/rocketmq/dashboard/util/WebUtil.java create mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/logback.xml create mode 100644 src/main/resources/rmqcngkeystore.jks create mode 100644 src/main/resources/role-permission.yml create mode 100644 src/main/resources/static/index.html create mode 100644 src/main/resources/static/src/app.js create mode 100644 src/main/resources/static/src/cluster.js create mode 100644 src/main/resources/static/src/config.js create mode 100644 src/main/resources/static/src/consumer.js create mode 100644 src/main/resources/static/src/controller.js create mode 100644 src/main/resources/static/src/data/dict.json create mode 100644 src/main/resources/static/src/i18n/en.js create mode 100644 src/main/resources/static/src/i18n/zh.js create mode 100644 src/main/resources/static/src/login.js create mode 100644 src/main/resources/static/src/message.js create mode 100644 src/main/resources/static/src/messageTrace.js create mode 100644 src/main/resources/static/src/ops.js create mode 100644 src/main/resources/static/src/producer.js create mode 100644 src/main/resources/static/src/remoteApi/remoteApi.js create mode 100644 src/main/resources/static/src/tools/tools.js create mode 100644 src/main/resources/static/src/topic.js create mode 100644 src/main/resources/static/style/animate.css create mode 100644 src/main/resources/static/style/app.css create mode 100644 src/main/resources/static/style/btn.css create mode 100644 src/main/resources/static/style/img/alipay.png create mode 100644 src/main/resources/static/style/img/alipay_active.png create mode 100644 src/main/resources/static/style/img/icon_user_center.png create mode 100644 src/main/resources/static/style/img/weipay.png create mode 100644 src/main/resources/static/style/img/weipay_active.png create mode 100644 src/main/resources/static/style/login.css create mode 100644 src/main/resources/static/style/preLoading/main.css create mode 100644 src/main/resources/static/style/preLoading/normalize.css create mode 100644 src/main/resources/static/style/theme.css create mode 100644 src/main/resources/static/style/timeline.css create mode 100644 src/main/resources/static/vendor/angular-material/angular-animate.min.js create mode 100644 src/main/resources/static/vendor/angular-material/angular-aria.min.js create mode 100644 src/main/resources/static/vendor/angular-material/angular-material.min.css create mode 100644 src/main/resources/static/vendor/angular-material/angular-material.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-animate.js create mode 100644 src/main/resources/static/vendor/angular/angular-animate.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-animate.min.js.map create mode 100644 src/main/resources/static/vendor/angular/angular-aria.js create mode 100644 src/main/resources/static/vendor/angular/angular-aria.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-aria.min.js.map create mode 100644 src/main/resources/static/vendor/angular/angular-cookies.js create mode 100644 src/main/resources/static/vendor/angular/angular-cookies.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-cookies.min.js.map create mode 100644 src/main/resources/static/vendor/angular/angular-csp.css create mode 100644 src/main/resources/static/vendor/angular/angular-loader.js create mode 100644 src/main/resources/static/vendor/angular/angular-loader.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-loader.min.js.map create mode 100644 src/main/resources/static/vendor/angular/angular-message-format.js create mode 100644 src/main/resources/static/vendor/angular/angular-message-format.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-message-format.min.js.map create mode 100644 src/main/resources/static/vendor/angular/angular-messages.js create mode 100644 src/main/resources/static/vendor/angular/angular-messages.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-messages.min.js.map create mode 100644 src/main/resources/static/vendor/angular/angular-mocks.js create mode 100644 src/main/resources/static/vendor/angular/angular-parse-ext.js create mode 100644 src/main/resources/static/vendor/angular/angular-parse-ext.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-parse-ext.min.js.map create mode 100644 src/main/resources/static/vendor/angular/angular-resource.js create mode 100644 src/main/resources/static/vendor/angular/angular-resource.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-resource.min.js.map create mode 100644 src/main/resources/static/vendor/angular/angular-route.js create mode 100644 src/main/resources/static/vendor/angular/angular-route.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-route.min.js.map create mode 100644 src/main/resources/static/vendor/angular/angular-sanitize.js create mode 100644 src/main/resources/static/vendor/angular/angular-sanitize.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-sanitize.min.js.map create mode 100644 src/main/resources/static/vendor/angular/angular-scenario.js create mode 100644 src/main/resources/static/vendor/angular/angular-touch.js create mode 100644 src/main/resources/static/vendor/angular/angular-touch.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-touch.min.js.map create mode 100644 src/main/resources/static/vendor/angular/angular-translate-storage-cookie/angular-translate-storage-cookie.js create mode 100644 src/main/resources/static/vendor/angular/angular-translate-storage-cookie/angular-translate-storage-cookie.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-translate.js create mode 100644 src/main/resources/static/vendor/angular/angular-translate.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-ui-router.min.js create mode 100644 src/main/resources/static/vendor/angular/angular-websocket.min.js create mode 100644 src/main/resources/static/vendor/angular/angular.js create mode 100644 src/main/resources/static/vendor/angular/angular.min.js create mode 100644 src/main/resources/static/vendor/angular/angular.min.js.map create mode 100644 src/main/resources/static/vendor/angular/errors.json create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_aa-dj.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_aa-er.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_aa-et.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_aa.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_af-na.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_af-za.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_af.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_agq-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_agq.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ak-gh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ak.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_am-et.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_am.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-001.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-ae.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-bh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-dj.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-dz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-eg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-eh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-er.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-il.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-iq.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-jo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-km.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-kw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-lb.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-ly.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-ma.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-mr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-om.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-ps.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-qa.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-sa.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-sd.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-so.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-ss.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-sy.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-td.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-tn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar-ye.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ar.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_as-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_as.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_asa-tz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_asa.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ast-es.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ast.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_az-cyrl-az.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_az-cyrl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_az-latn-az.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_az-latn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_az.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bas-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bas.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_be-by.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_be.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bem-zm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bem.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bez-tz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bez.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bg-bg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bm-latn-ml.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bm-latn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bm-ml.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bn-bd.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bn-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bo-cn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bo-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_br-fr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_br.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_brx-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_brx.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bs-cyrl-ba.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bs-cyrl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bs-latn-ba.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bs-latn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_bs.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_byn-er.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_byn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ca-ad.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ca-es-valencia.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ca-es.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ca-fr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ca-it.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ca.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_cgg-ug.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_cgg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_chr-us.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_chr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ckb-arab-iq.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ckb-arab-ir.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ckb-arab.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ckb-iq.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ckb-ir.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ckb-latn-iq.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ckb-latn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ckb.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_cs-cz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_cs.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_cy-gb.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_cy.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_da-dk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_da-gl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_da.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_dav-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_dav.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_de-at.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_de-be.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_de-ch.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_de-de.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_de-li.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_de-lu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_de.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_dje-ne.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_dje.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_dsb-de.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_dsb.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_dua-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_dua.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_dyo-sn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_dyo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_dz-bt.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_dz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ebu-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ebu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ee-gh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ee-tg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ee.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_el-cy.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_el-gr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_el.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-001.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-150.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ag.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ai.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-as.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-au.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-bb.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-be.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-bm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-bs.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-bw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-bz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ca.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-cc.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ck.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-cx.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-dg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-dm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-er.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-fj.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-fk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-fm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-gb.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-gd.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-gg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-gh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-gi.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-gm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-gu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-gy.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-hk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ie.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-im.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-io.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-iso.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-je.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-jm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ki.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-kn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ky.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-lc.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-lr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ls.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-mg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-mh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-mo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-mp.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ms.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-mt.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-mu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-mw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-my.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-na.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-nf.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ng.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-nr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-nu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-nz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-pg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ph.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-pk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-pn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-pr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-pw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-rw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-sb.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-sc.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-sd.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-sg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-sh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-sl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ss.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-sx.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-sz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-tc.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-tk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-to.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-tt.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-tv.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-tz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ug.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-um.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-us.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-vc.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-vg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-vi.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-vu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-ws.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-za.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-zm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en-zw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_en.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_eo-001.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_eo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-419.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-ar.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-bo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-cl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-co.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-cr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-cu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-do.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-ea.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-ec.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-es.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-gq.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-gt.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-hn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-ic.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-mx.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-ni.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-pa.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-pe.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-ph.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-pr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-py.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-sv.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-us.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-uy.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es-ve.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_es.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_et-ee.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_et.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_eu-es.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_eu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ewo-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ewo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fa-af.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fa-ir.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fa.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ff-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ff-gn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ff-mr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ff-sn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ff.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fi-fi.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fi.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fil-ph.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fil.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fo-fo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-be.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-bf.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-bi.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-bj.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-bl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-ca.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-cd.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-cf.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-cg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-ch.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-ci.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-dj.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-dz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-fr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-ga.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-gf.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-gn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-gp.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-gq.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-ht.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-km.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-lu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-ma.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-mc.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-mf.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-mg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-ml.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-mq.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-mr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-mu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-nc.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-ne.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-pf.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-pm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-re.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-rw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-sc.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-sn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-sy.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-td.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-tg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-tn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-vu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-wf.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr-yt.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fur-it.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fur.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fy-nl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_fy.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ga-ie.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ga.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_gd-gb.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_gd.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_gl-es.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_gl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_gsw-ch.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_gsw-fr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_gsw-li.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_gsw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_gu-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_gu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_guz-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_guz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_gv-im.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_gv.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ha-latn-gh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ha-latn-ne.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ha-latn-ng.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ha-latn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ha.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_haw-us.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_haw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_he-il.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_he.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_hi-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_hi.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_hr-ba.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_hr-hr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_hr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_hsb-de.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_hsb.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_hu-hu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_hu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_hy-am.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_hy.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ia-fr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ia.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_id-id.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_id.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ig-ng.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ig.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ii-cn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ii.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_is-is.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_is.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_it-ch.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_it-it.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_it-sm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_it.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_iw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ja-jp.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ja.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_jgo-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_jgo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_jmc-tz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_jmc.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ka-ge.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ka.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kab-dz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kab.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kam-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kam.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kde-tz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kde.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kea-cv.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kea.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_khq-ml.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_khq.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ki-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ki.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kk-cyrl-kz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kk-cyrl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kkj-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kkj.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kl-gl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kln-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kln.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_km-kh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_km.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kn-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ko-kp.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ko-kr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ko.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kok-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kok.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ks-arab-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ks-arab.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ks.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ksb-tz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ksb.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ksf-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ksf.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ksh-de.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ksh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kw-gb.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_kw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ky-cyrl-kg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ky-cyrl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ky.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lag-tz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lag.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lb-lu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lb.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lg-ug.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lkt-us.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lkt.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ln-ao.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ln-cd.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ln-cf.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ln-cg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ln.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lo-la.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lt-lt.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lt.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lu-cd.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_luo-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_luo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_luy-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_luy.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lv-lv.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_lv.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mas-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mas-tz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mas.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mer-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mer.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mfe-mu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mfe.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mg-mg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mgh-mz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mgh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mgo-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mgo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mk-mk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ml-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ml.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mn-cyrl-mn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mn-cyrl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mr-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ms-latn-bn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ms-latn-my.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ms-latn-sg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ms-latn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ms.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mt-mt.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mt.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mua-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_mua.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_my-mm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_my.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_naq-na.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_naq.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nb-no.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nb-sj.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nb.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nd-zw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nd.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ne-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ne-np.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ne.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nl-aw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nl-be.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nl-bq.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nl-cw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nl-nl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nl-sr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nl-sx.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nmg-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nmg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nn-no.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nnh-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nnh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_no-no.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_no.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nr-za.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nso-za.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nso.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nus-sd.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nus.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nyn-ug.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_nyn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_om-et.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_om-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_om.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_or-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_or.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_os-ge.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_os-ru.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_os.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pa-arab-pk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pa-arab.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pa-guru-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pa-guru.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pa.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pl-pl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ps-af.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ps.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pt-ao.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pt-br.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pt-cv.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pt-gw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pt-mo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pt-mz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pt-pt.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pt-st.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pt-tl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_pt.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_qu-bo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_qu-ec.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_qu-pe.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_qu.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_rm-ch.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_rm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_rn-bi.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_rn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ro-md.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ro-ro.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ro.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_rof-tz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_rof.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ru-by.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ru-kg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ru-kz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ru-md.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ru-ru.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ru-ua.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ru.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_rw-rw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_rw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_rwk-tz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_rwk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sah-ru.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sah.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_saq-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_saq.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sbp-tz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sbp.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_se-fi.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_se-no.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_se-se.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_se.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_seh-mz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_seh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ses-ml.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ses.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sg-cf.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_shi-latn-ma.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_shi-latn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_shi-tfng-ma.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_shi-tfng.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_shi.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_si-lk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_si.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sk-sk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sl-si.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_smn-fi.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_smn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sn-zw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_so-dj.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_so-et.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_so-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_so-so.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_so.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sq-al.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sq-mk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sq-xk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sq.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sr-cyrl-ba.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sr-cyrl-me.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sr-cyrl-rs.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sr-cyrl-xk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sr-cyrl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sr-latn-ba.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sr-latn-me.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sr-latn-rs.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sr-latn-xk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sr-latn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ss-sz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ss-za.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ss.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ssy-er.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ssy.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_st-ls.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_st-za.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_st.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sv-ax.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sv-fi.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sv-se.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sv.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sw-cd.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sw-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sw-tz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sw-ug.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_sw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_swc-cd.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_swc.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ta-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ta-lk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ta-my.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ta-sg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ta.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_te-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_te.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_teo-ke.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_teo-ug.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_teo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tg-cyrl-tj.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tg-cyrl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_th-th.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_th.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ti-er.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ti-et.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ti.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tig-er.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tig.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tn-bw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tn-za.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_to-to.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_to.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tr-cy.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tr-tr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ts-za.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ts.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_twq-ne.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_twq.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tzm-latn-ma.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tzm-latn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_tzm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ug-arab-cn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ug-arab.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ug.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_uk-ua.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_uk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ur-in.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ur-pk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ur.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_uz-arab-af.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_uz-arab.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_uz-cyrl-uz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_uz-cyrl.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_uz-latn-uz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_uz-latn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_uz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_vai-latn-lr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_vai-latn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_vai-vaii-lr.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_vai-vaii.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_vai.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ve-za.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_ve.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_vi-vn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_vi.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_vo-001.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_vo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_vun-tz.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_vun.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_wae-ch.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_wae.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_wal-et.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_wal.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_xh-za.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_xh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_xog-ug.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_xog.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_yav-cm.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_yav.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_yi-001.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_yi.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_yo-bj.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_yo-ng.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_yo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zgh-ma.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zgh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zh-cn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zh-hans-cn.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zh-hans-hk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zh-hans-mo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zh-hans-sg.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zh-hans.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zh-hant-hk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zh-hant-mo.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zh-hant-tw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zh-hant.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zh-hk.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zh-tw.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zh.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zu-za.js create mode 100644 src/main/resources/static/vendor/angular/i18n/angular-locale_zu.js create mode 100644 src/main/resources/static/vendor/angular/notification/angular-ui-notification.css create mode 100644 src/main/resources/static/vendor/angular/notification/angular-ui-notification.js create mode 100644 src/main/resources/static/vendor/angular/version.json create mode 100644 src/main/resources/static/vendor/angular/version.txt create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/css/bootstrap-material-design.css create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/css/bootstrap-material-design.css.map create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/css/bootstrap-material-design.min.css create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/css/bootstrap-material-design.min.css.map create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/css/ripples.css create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/css/ripples.css.map create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/css/ripples.min.css create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/css/ripples.min.css.map create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/js/material.js create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/js/material.min.js create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/js/material.min.js.map create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/js/ripples.js create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/js/ripples.min.js create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/js/ripples.min.js.map create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/sassc/bootstrap-material-design.css create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/sassc/bootstrap-material-design.css.map create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/sassc/ripples.css create mode 100644 src/main/resources/static/vendor/bootstrap-material-design/sassc/ripples.css.map create mode 100644 src/main/resources/static/vendor/bootstrap/css/bootstrap-theme.css create mode 100644 src/main/resources/static/vendor/bootstrap/css/bootstrap-theme.css.map create mode 100644 src/main/resources/static/vendor/bootstrap/css/bootstrap-theme.min.css create mode 100644 src/main/resources/static/vendor/bootstrap/css/bootstrap.css create mode 100644 src/main/resources/static/vendor/bootstrap/css/bootstrap.css.map create mode 100644 src/main/resources/static/vendor/bootstrap/css/bootstrap.min.css create mode 100644 src/main/resources/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot create mode 100644 src/main/resources/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg create mode 100644 src/main/resources/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf create mode 100644 src/main/resources/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff create mode 100644 src/main/resources/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2 create mode 100644 src/main/resources/static/vendor/bootstrap/js/bootstrap.js create mode 100644 src/main/resources/static/vendor/bootstrap/js/bootstrap.min.js create mode 100644 src/main/resources/static/vendor/bootstrap/js/npm.js create mode 100644 src/main/resources/static/vendor/chosen/angular-chosen.js create mode 100644 src/main/resources/static/vendor/chosen/angular-chosen.min.js create mode 100644 src/main/resources/static/vendor/chosen/chosen-spinner.css create mode 100644 src/main/resources/static/vendor/chosen/chosen-sprite.png create mode 100644 src/main/resources/static/vendor/chosen/chosen-sprite@2x.png create mode 100644 src/main/resources/static/vendor/chosen/chosen.css create mode 100644 src/main/resources/static/vendor/chosen/chosen.jquery.min.js create mode 100644 src/main/resources/static/vendor/chosen/spinner.gif create mode 100644 src/main/resources/static/vendor/common.js create mode 100644 src/main/resources/static/vendor/datatimepicker/angular-eonasdan-datetimepicker.min.js create mode 100644 src/main/resources/static/vendor/datatimepicker/bootstrap-datetimepicker.min.css create mode 100644 src/main/resources/static/vendor/datatimepicker/bootstrap-datetimepicker.min.js create mode 100644 src/main/resources/static/vendor/datatimepicker/moment.min.js create mode 100644 src/main/resources/static/vendor/dropdown/jquery.dropdown.css create mode 100644 src/main/resources/static/vendor/dropdown/jquery.dropdown.js create mode 100644 src/main/resources/static/vendor/echarts/echarts.min.js create mode 100644 src/main/resources/static/vendor/jquery/jquery-3.0.0.min.js create mode 100644 src/main/resources/static/vendor/jquery/jquery1.11.3.min.js create mode 100644 src/main/resources/static/vendor/json-bigint/json-bigint.js create mode 100644 src/main/resources/static/vendor/loading/css/default.css create mode 100644 src/main/resources/static/vendor/loading/css/ladda-theme.scss create mode 100644 src/main/resources/static/vendor/loading/css/ladda.scss create mode 100644 src/main/resources/static/vendor/loading/css/normalize.css create mode 100644 src/main/resources/static/vendor/loading/dist/ladda-themeless.css create mode 100644 src/main/resources/static/vendor/loading/dist/ladda-themeless.min.css create mode 100644 src/main/resources/static/vendor/loading/dist/ladda.css create mode 100644 src/main/resources/static/vendor/loading/dist/ladda.jquery.min.js create mode 100644 src/main/resources/static/vendor/loading/dist/ladda.js create mode 100644 src/main/resources/static/vendor/loading/dist/ladda.min.css create mode 100644 src/main/resources/static/vendor/loading/dist/ladda.min.js create mode 100644 src/main/resources/static/vendor/loading/dist/spin.js create mode 100644 src/main/resources/static/vendor/loading/dist/spin.min.js create mode 100644 src/main/resources/static/vendor/loading/fonts/icomoon.eot create mode 100644 src/main/resources/static/vendor/loading/fonts/icomoon.svg create mode 100644 src/main/resources/static/vendor/loading/fonts/icomoon.ttf create mode 100644 src/main/resources/static/vendor/loading/fonts/icomoon.woff create mode 100644 src/main/resources/static/vendor/loading/js/ladda.jquery.js create mode 100644 src/main/resources/static/vendor/loading/js/ladda.js create mode 100644 src/main/resources/static/vendor/loading/js/spin.js create mode 100644 src/main/resources/static/vendor/md-tab/angular-messages.min.js create mode 100644 src/main/resources/static/vendor/md-tab/angular-route.min.js create mode 100644 src/main/resources/static/vendor/md-tab/docs.css create mode 100644 src/main/resources/static/vendor/md-tab/svg-assets-cache.js create mode 100644 src/main/resources/static/vendor/ng-dialog/ngDialog-theme-default.css create mode 100644 src/main/resources/static/vendor/ng-dialog/ngDialog.min.css create mode 100644 src/main/resources/static/vendor/ng-dialog/ngDialog.min.js create mode 100644 src/main/resources/static/vendor/notification/angular-ui-notification.css create mode 100644 src/main/resources/static/vendor/notification/angular-ui-notification.js create mode 100644 src/main/resources/static/vendor/pagination/tm.pagination.js create mode 100644 src/main/resources/static/vendor/preLoading/main.js create mode 100644 src/main/resources/static/vendor/preLoading/modernizr-2.6.2.min.js create mode 100644 src/main/resources/static/vendor/validator/validator.js create mode 100644 src/main/resources/static/vendor/validator/validator.min.js create mode 100644 src/main/resources/static/view/layout/_footer.html create mode 100644 src/main/resources/static/view/layout/_header.html create mode 100644 src/main/resources/static/view/pages/404.html create mode 100644 src/main/resources/static/view/pages/50x.html create mode 100644 src/main/resources/static/view/pages/cluster.html create mode 100644 src/main/resources/static/view/pages/config.html create mode 100644 src/main/resources/static/view/pages/consumer.html create mode 100644 src/main/resources/static/view/pages/index.html create mode 100644 src/main/resources/static/view/pages/login.html create mode 100644 src/main/resources/static/view/pages/message.html create mode 100644 src/main/resources/static/view/pages/messageTrace.html create mode 100644 src/main/resources/static/view/pages/ops.html create mode 100644 src/main/resources/static/view/pages/producer.html create mode 100644 src/main/resources/static/view/pages/topic.html create mode 100644 src/main/resources/static/view/pages/un_support_browser.html create mode 100644 src/main/resources/users.properties create mode 100644 src/test/resources/application.properties create mode 100644 src/test/resources/logback.xml create mode 100644 src/test/resources/logback_rocketmq_client.xml create mode 100644 src/test/resources/users.properties create mode 100644 style/copyright/Apache.xml create mode 100644 style/copyright/profiles_settings.xml create mode 100644 style/rmq_checkstyle.xml create mode 100644 style/rmq_codeStyle.xml diff --git a/.asf.yaml b/.asf.yaml new file mode 100644 index 0000000..685f086 --- /dev/null +++ b/.asf.yaml @@ -0,0 +1,11 @@ + +github: + features: + # Enable issue management + issues: true + # Enable wiki + wiki: true + enabled_merge_buttons: + squash: true + merge: false + rebase: false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..359bb53 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..f94ef27 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..45bb057 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..5f46569 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..930f106 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +dist: trusty + +notifications: + email: + recipients: + - dev@rocketmq.apache.org + on_success: change + on_failure: always + +language: java + +jdk: + - oraclejdk8 + - oraclejdk11 + +script: + - travis_retry mvn -B clean apache-rat:check + - travis_retry mvn -B package findbugs:findbugs jacoco:report coveralls:report + +# - travis_retry mvn -B package findbugs:findbugs coveralls:report +#after_success: +# - mvn sonar:sonar diff --git a/DEPENDENCIES b/DEPENDENCIES new file mode 100644 index 0000000..e06870e --- /dev/null +++ b/DEPENDENCIES @@ -0,0 +1,221 @@ +// ------------------------------------------------------------------ +// Transitive dependencies of this project determined from the +// maven pom organized by organization. +// ------------------------------------------------------------------ + +rocketmq-dashboard + + +From: 'Alibaba Group' (https://github.com/alibaba) + - fastjson (https://github.com/alibaba/fastjson) com.alibaba:fastjson:jar:1.2.76 + License: Apache 2 (http://www.apache.org/licenses/LICENSE-2.0.txt) + +From: 'an unknown organization' + - Code Generation Library (http://cglib.sourceforge.net/) cglib:cglib:jar:2.2.2 + License: ASF 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - jcommander (http://jcommander.org) com.beust:jcommander:jar:1.72 + License: Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + - FindBugs-jsr305 (http://findbugs.sourceforge.net/) com.google.code.findbugs:jsr305:jar:3.0.2 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - error-prone annotations (http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotations) com.google.errorprone:error_prone_annotations:jar:2.3.4 + License: Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - Guava InternalFutureFailureAccess and InternalFutures (https://github.com/google/guava/failureaccess) com.google.guava:failureaccess:bundle:1.0.1 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - Guava: Google Core Libraries for Java (https://github.com/google/guava/guava) com.google.guava:guava:bundle:29.0-jre + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - Guava ListenableFuture only (https://github.com/google/guava/listenablefuture) com.google.guava:listenablefuture:jar:9999.0-empty-to-avoid-conflict-with-guava + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - J2ObjC Annotations (https://github.com/google/j2objc/) com.google.j2objc:j2objc-annotations:jar:1.3 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - micrometer-core (https://github.com/micrometer-metrics/micrometer) io.micrometer:micrometer-core:jar:1.3.1 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - openmessaging-dledger (http://nexus.sonatype.org/oss-repository-hosting.html/dledger) io.openmessaging.storage:dledger:jar:0.2.2 + + - Bean Validation API (http://beanvalidation.org) jakarta.validation:jakarta.validation-api:jar:2.0.1 + License: Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - Java Native Access (https://github.com/java-native-access/jna) net.java.dev.jna:jna:jar:4.2.2 + License: LGPL, version 2.1 (http://www.gnu.org/licenses/licenses.html) License: ASL, version 2 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - tomcat-embed-core (https://tomcat.apache.org/) org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.29 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - tomcat-embed-el (https://tomcat.apache.org/) org.apache.tomcat.embed:tomcat-embed-el:jar:9.0.29 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - tomcat-embed-websocket (https://tomcat.apache.org/) org.apache.tomcat.embed:tomcat-embed-websocket:jar:9.0.29 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - AspectJ runtime (https://www.eclipse.org/aspectj/) org.aspectj:aspectjrt:jar:1.9.6 + License: Eclipse Public License - v 1.0 (http://www.eclipse.org/legal/epl-v10.html) + - AspectJ weaver (https://www.eclipse.org/aspectj/) org.aspectj:aspectjweaver:jar:1.9.6 + License: Eclipse Public License - v 1.0 (http://www.eclipse.org/legal/epl-v10.html) + - Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs (http://www.bouncycastle.org/java.html) org.bouncycastle:bcpkix-jdk15on:jar:1.68 + License: Bouncy Castle Licence (http://www.bouncycastle.org/licence.html) + - Bouncy Castle Provider (http://www.bouncycastle.org/java.html) org.bouncycastle:bcprov-jdk15on:jar:1.68 + License: Bouncy Castle Licence (http://www.bouncycastle.org/licence.html) + - Checker Qual (https://checkerframework.org) org.checkerframework:checker-qual:jar:2.11.1 + License: The MIT License (http://opensource.org/licenses/MIT) + - HdrHistogram (http://hdrhistogram.github.io/HdrHistogram/) org.hdrhistogram:HdrHistogram:bundle:2.1.11 + License: Public Domain, per Creative Commons CC0 (http://creativecommons.org/publicdomain/zero/1.0/) License: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause) + - Hibernate Validator Engine (http://hibernate.org/validator/hibernate-validator) org.hibernate.validator:hibernate-validator:jar:6.0.18.Final + License: Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - jOOR (https://github.com/jOOQ/jOOR) org.jooq:joor:bundle:0.9.6 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + - LatencyUtils (http://latencyutils.github.io/LatencyUtils/) org.latencyutils:LatencyUtils:jar:2.0.3 + License: Public Domain, per Creative Commons CC0 (http://creativecommons.org/publicdomain/zero/1.0/) + - Project Lombok (https://projectlombok.org) org.projectlombok:lombok:jar:1.18.12 + License: The MIT License (https://projectlombok.org/LICENSE) + - SnakeYAML (http://www.snakeyaml.org) org.yaml:snakeyaml:bundle:1.19 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + +From: 'Apache Software Foundation' (http://www.apache.org) + - rocketmq-acl 4.9.0 (http://rocketmq.apache.org/rocketmq-acl/) org.apache.rocketmq:rocketmq-acl:jar:4.9.0 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + - rocketmq-broker 4.9.0 (http://rocketmq.apache.org/rocketmq-broker/) org.apache.rocketmq:rocketmq-broker:jar:4.9.0 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + - rocketmq-client 4.9.0 (http://rocketmq.apache.org/rocketmq-client/) org.apache.rocketmq:rocketmq-client:jar:4.9.0 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + - rocketmq-common 4.9.0 (http://rocketmq.apache.org/rocketmq-common/) org.apache.rocketmq:rocketmq-common:jar:4.9.0 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + - rocketmq-filter 4.9.0 (http://rocketmq.apache.org/rocketmq-filter/) org.apache.rocketmq:rocketmq-filter:jar:4.9.0 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + - rocketmq-logging 4.9.0 (http://rocketmq.apache.org/rocketmq-logging/) org.apache.rocketmq:rocketmq-logging:jar:4.9.0 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + - rocketmq-namesrv 4.9.0 (http://rocketmq.apache.org/rocketmq-namesrv/) org.apache.rocketmq:rocketmq-namesrv:jar:4.9.0 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + - rocketmq-remoting 4.9.0 (http://rocketmq.apache.org/rocketmq-remoting/) org.apache.rocketmq:rocketmq-remoting:jar:4.9.0 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + - rocketmq-srvutil 4.9.0 (http://rocketmq.apache.org/rocketmq-srvutil/) org.apache.rocketmq:rocketmq-srvutil:jar:4.9.0 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + - rocketmq-store 4.9.0 (http://rocketmq.apache.org/rocketmq-store/) org.apache.rocketmq:rocketmq-store:jar:4.9.0 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + - rocketmq-tools 4.9.0 (http://rocketmq.apache.org/rocketmq-tools/) org.apache.rocketmq:rocketmq-tools:jar:4.9.0 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + +From: 'Eclipse Foundation' (https://www.eclipse.org) + - Jakarta Annotations API (https://projects.eclipse.org/projects/ee4j.ca) jakarta.annotation:jakarta.annotation-api:jar:1.3.5 + License: EPL 2.0 (http://www.eclipse.org/legal/epl-2.0) License: GPL2 w/ CPE (https://www.gnu.org/software/classpath/license.html) + +From: 'FasterXML' (http://fasterxml.com/) + - Jackson-annotations (http://github.com/FasterXML/jackson) com.fasterxml.jackson.core:jackson-annotations:bundle:2.10.1 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - Jackson-core (https://github.com/FasterXML/jackson-core) com.fasterxml.jackson.core:jackson-core:bundle:2.10.1 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - jackson-databind (http://github.com/FasterXML/jackson) com.fasterxml.jackson.core:jackson-databind:bundle:2.10.1 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - Jackson datatype: jdk8 (https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) com.fasterxml.jackson.datatype:jackson-datatype-jdk8:bundle:2.10.1 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - Jackson datatype: JSR310 (https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310) com.fasterxml.jackson.datatype:jackson-datatype-jsr310:bundle:2.10.1 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - Jackson-module-parameter-names (https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) com.fasterxml.jackson.module:jackson-module-parameter-names:bundle:2.10.1 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + +From: 'fasterxml.com' (http://fasterxml.com) + - ClassMate (http://github.com/FasterXML/java-classmate) com.fasterxml:classmate:bundle:1.3.4 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + +From: 'JBoss by Red Hat' (http://www.jboss.org) + - JBoss Logging 3 (http://www.jboss.org) org.jboss.logging:jboss-logging:jar:3.3.2.Final + License: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + +From: 'ObjectWeb' (http://www.objectweb.org/) + - ASM Core (http://asm.objectweb.org/asm/) asm:asm:jar:3.3.1 + License: BSD (http://asm.objectweb.org/license.html) + +From: 'Oracle' (http://www.oracle.com) + - JavaBeans Activation Framework API jar (http://java.net/all/javax.activation-api/) javax.activation:javax.activation-api:jar:1.2.0 + License: CDDL/GPLv2+CE (https://github.com/javaee/activation/blob/master/LICENSE.txt) + +From: 'Oracle Corporation' (http://www.oracle.com/) + - jaxb-api (https://github.com/javaee/jaxb-spec/jaxb-api) javax.xml.bind:jaxb-api:jar:2.3.1 + License: CDDL 1.1 (https://oss.oracle.com/licenses/CDDL+GPL-1.1) License: GPL2 w/ CPE (https://oss.oracle.com/licenses/CDDL+GPL-1.1) + +From: 'Pivotal Software, Inc.' (https://spring.io) + - Spring Boot (https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot) org.springframework.boot:spring-boot:jar:2.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Boot Actuator (https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-actuator) org.springframework.boot:spring-boot-actuator:jar:2.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Boot Actuator AutoConfigure (https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-actuator-autoconfigure) org.springframework.boot:spring-boot-actuator-autoconfigure:jar:2.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Boot AutoConfigure (https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-autoconfigure) org.springframework.boot:spring-boot-autoconfigure:jar:2.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Boot Starter (https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter) org.springframework.boot:spring-boot-starter:jar:2.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Boot Actuator Starter (https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-actuator) org.springframework.boot:spring-boot-starter-actuator:jar:2.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Boot Json Starter (https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-json) org.springframework.boot:spring-boot-starter-json:jar:2.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Boot Logging Starter (https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-logging) org.springframework.boot:spring-boot-starter-logging:jar:2.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Boot Tomcat Starter (https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-tomcat) org.springframework.boot:spring-boot-starter-tomcat:jar:2.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Boot Validation Starter (https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-validation) org.springframework.boot:spring-boot-starter-validation:jar:2.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Boot Web Starter (https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-web) org.springframework.boot:spring-boot-starter-web:jar:2.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + +From: 'Pivotal Software, Inc.' (https://www.spring.io) + - Spring Data Core (https://www.spring.io/spring-data/spring-data-commons) org.springframework.data:spring-data-commons:jar:2.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + +From: 'QOS.ch' (http://www.qos.ch) + - Logback Classic Module (http://logback.qos.ch/logback-classic) ch.qos.logback:logback-classic:jar:1.2.3 + License: Eclipse Public License - v 1.0 (http://www.eclipse.org/legal/epl-v10.html) License: GNU Lesser General Public License (http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) + - Logback Core Module (http://logback.qos.ch/logback-core) ch.qos.logback:logback-core:jar:1.2.3 + License: Eclipse Public License - v 1.0 (http://www.eclipse.org/legal/epl-v10.html) License: GNU Lesser General Public License (http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) + - JUL to SLF4J bridge (http://www.slf4j.org) org.slf4j:jul-to-slf4j:jar:1.7.29 + License: MIT License (http://www.opensource.org/licenses/mit-license.php) + - SLF4J API Module (http://www.slf4j.org) org.slf4j:slf4j-api:jar:1.7.26 + License: MIT License (http://www.opensource.org/licenses/mit-license.php) + +From: 'Shigeru Chiba, www.javassist.org' + - Javassist (http://www.javassist.org/) org.javassist:javassist:bundle:3.20.0-GA + License: MPL 1.1 (http://www.mozilla.org/MPL/MPL-1.1.html) License: LGPL 2.1 (http://www.gnu.org/licenses/lgpl-2.1.html) License: Apache License 2.0 (http://www.apache.org/licenses/) + +From: 'Spring IO' (https://spring.io/projects/spring-framework) + - Spring AOP (https://github.com/spring-projects/spring-framework) org.springframework:spring-aop:jar:5.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Beans (https://github.com/spring-projects/spring-framework) org.springframework:spring-beans:jar:5.2.1.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Context (https://github.com/spring-projects/spring-framework) org.springframework:spring-context:jar:5.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Core (https://github.com/spring-projects/spring-framework) org.springframework:spring-core:jar:5.2.1.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Expression Language (SpEL) (https://github.com/spring-projects/spring-framework) org.springframework:spring-expression:jar:5.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Commons Logging Bridge (https://github.com/spring-projects/spring-framework) org.springframework:spring-jcl:jar:5.2.1.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Web (https://github.com/spring-projects/spring-framework) org.springframework:spring-web:jar:5.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + - Spring Web MVC (https://github.com/spring-projects/spring-framework) org.springframework:spring-webmvc:jar:5.2.2.RELEASE + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + +From: 'The Apache Software Foundation' (http://www.apache.org/) + - Commons CLI (http://commons.apache.org/cli/) commons-cli:commons-cli:jar:1.2 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - Apache Commons Codec (http://commons.apache.org/proper/commons-codec/) commons-codec:commons-codec:jar:1.9 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - Apache Commons Collections (http://commons.apache.org/collections/) commons-collections:commons-collections:jar:3.2.2 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - Commons Digester (http://commons.apache.org/digester/) commons-digester:commons-digester:jar:2.1 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - Apache Commons Logging (http://commons.apache.org/proper/commons-logging/) commons-logging:commons-logging:jar:1.2 + License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + - Apache Commons Lang (http://commons.apache.org/proper/commons-lang/) org.apache.commons:commons-lang3:jar:3.4 + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) + +From: 'The Apache Software Foundation' (https://www.apache.org/) + - Apache Commons BeanUtils (https://commons.apache.org/proper/commons-beanutils/) commons-beanutils:commons-beanutils:jar:1.9.4 + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0.txt) + - Apache Commons Validator (http://commons.apache.org/proper/commons-validator/) commons-validator:commons-validator:jar:1.7 + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0.txt) + - Apache Commons Pool (http://commons.apache.org/proper/commons-pool/) org.apache.commons:commons-pool2:jar:2.4.3 + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0.txt) + - Apache Log4j API (https://logging.apache.org/log4j/2.x/log4j-api/) org.apache.logging.log4j:log4j-api:jar:2.12.1 + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0.txt) + - Apache Log4j to SLF4J Adapter (https://logging.apache.org/log4j/2.x/log4j-to-slf4j/) org.apache.logging.log4j:log4j-to-slf4j:jar:2.12.1 + License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0.txt) + +From: 'The Netty Project' (http://netty.io/) + - Netty/All-in-One (http://netty.io/netty-all/) io.netty:netty-all:jar:4.0.42.Final + License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..343b40d --- /dev/null +++ b/LICENSE @@ -0,0 +1,549 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (properties) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +----------------------------------------------------------------------------- + +rocket-console +------------------------------------------- +develop base on didapinche's rocket-console +https://github.com/didapinchegit/rocket-console + Apache License +Version 2.0, January 2004 + +angular +------------------------------------------- +https://github.com/angular/angular +The MIT License + +Copyright (c) 2014-2017 Google, Inc. http://angular.io + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +angular-material +----------------------------------------- +https://github.com/angular/material +The MIT License + +Copyright (c) 2014-2017 Google, Inc. http://angularjs.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +bootstrap +------------------------------------------- +http://getbootstrap.com/ +The MIT License (MIT) + +Copyright (c) 2011-2017 Twitter, Inc. +Copyright (c) 2011-2017 The Bootstrap Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +bootstrap-material-design +------------------------------------------------- + +The MIT License (MIT) + +Copyright (c) 2015-2016, Federico Zivolo and contributors - https://github.com/FezVrasta/bootstrap-material-design + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Acknowledgements: + +Some original Bootstrap code and documentation http://getbootstrap.com Copyright (c) 2011-2015 Twitter, Inc + +Some original MDL code http://www.getmdl.io/ Copyright 2015 Google Inc. All Rights Reserved. + +angular-chosen +-------------------------------------- + +The MIT License + +Copyright (c) 2013 Localytics http://www.localytics.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +bootstrap-datetimepicker +---------------------------------------------------------- +https://github.com/Eonasdan/bootstrap-datetimepicker + +The MIT License (MIT) + +Copyright (c) 2015 Jonathan Peterson (@Eonasdan) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +echarts +---------------------------------- +https://github.com/apache/echarts + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +html5-boilerplate +------------------------------------------ +https://github.com/h5bp/html5-boilerplate +h5bp/html5-boilerplate is licensed under the MIT License +Copyright (c) HTML5 Boilerplate + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +jquery +--------------------- +Copyright JS Foundation and other contributors, https://js.foundation/ + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/jquery/jquery + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +All files located in the node_modules and external directories are +externally maintained libraries used by this software which have their +own licenses; we recommend you read them, as their terms may differ from +the terms above. +12.json-bigint +https://github.com/sidorares/json-bigint + +The MIT License (MIT) + +Copyright (c) 2013 Andrey Sidorov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Ladda +--------------- +https://github.com/hakimel/Ladda +Copyright (C) 2016 Hakim El Hattab, http://hakim.se + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +ngDialog +------------ +http://github.com/likeastore/ngDialog + +MIT Licensed + +Copyright (c) 2013-2015, Likeastore.com info@likeastore.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +notification +--------------------- +https://github.com/alexcrack/angular-ui-notification +The MIT License (MIT) + +Copyright (c) 2014 Alexey Avramchik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +pagination +--------------------- +https://github.com/317482454/tm.pagination + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + +bootstrap-validator +--------------------------- +https://github.com/1000hz/bootstrap-validator + +The MIT License (MIT) + +Copyright (c) 2016 Cina Saffary + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..086ee9f --- /dev/null +++ b/NOTICE @@ -0,0 +1,5 @@ +Apache RocketMQ +Copyright 2016-2021 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/README.md b/README.md new file mode 100644 index 0000000..7ee102d --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +## RocketMQ Dashboard [![Build Status](https://api.travis-ci.com/apache/rocketmq-dashboard.svg?branch=master)](https://travis-ci.com/github/apache/rocketmq-dashboard) [![Coverage Status](https://coveralls.io/repos/github/apache/rocketmq-dashboard/badge.svg?branch=master)](https://coveralls.io/github/apache/rocketmq-dashboard?branch=master) +[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/apache/rocketmq-dashboard.svg)](http://isitmaintained.com/project/apache/rocketmq-dashboard "Average time to resolve an issue") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/apache/rocketmq-dashboard.svg)](http://isitmaintained.com/project/apache/rocketmq-dashboard "Percentage of issues still open") +[![Twitter Follow](https://img.shields.io/twitter/follow/ApacheRocketMQ?style=social)](https://twitter.com/intent/follow?screen_name=ApacheRocketMQ) +## How To Install + +### With Docker + +* get docker image + +``` +mvn clean package -Dmaven.test.skip=true docker:build +``` + +or + +``` +docker pull apacherocketmq/rocketmq-console:2.0.0 +``` + +> currently the newest available docker image is apacherocketmq/rocketmq-console:2.0.0 + + +* run it (change namesvrAddr and port yourself) + +``` +docker run -e "JAVA_OPTS=-Drocketmq.namesrv.addr=127.0.0.1:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" -p 8080:8080 -t apacherocketmq/rocketmq-console:2.0.0 +``` + +### Without Docker +require java 1.8+ +``` +mvn spring-boot:run +``` +or +``` +mvn clean package -Dmaven.test.skip=true +java -jar target/rocketmq-dashboard-1.0.1-SNAPSHOT.jar +``` + +#### Tips +* if you download package slow,you can change maven's mirror(maven's settings.xml) + + ``` + + + alimaven + aliyun maven + http://maven.aliyun.com/nexus/content/groups/public/ + central + + + ``` + +* if you use the rocketmq < 3.5.8,please add -Dcom.rocketmq.sendMessageWithVIPChannel=false when you start rocketmq-dashboard(or you can change it in ops page) +* change the rocketmq.config.namesrvAddr in resource/application.properties.(or you can change it in ops page) + +## UserGuide + +[English](https://github.com/apache/rocketmq-dashboard/blob/master/docs/1_0_0/UserGuide_EN.md) + +[中文](https://github.com/apache/rocketmq-dashboard/blob/master/docs/1_0_0/UserGuide_CN.md) + +## Contributing + +We are always very happy to have contributions, whether for trivial cleanups or big new features. Please see the RocketMQ main website to read [details](http://rocketmq.apache.org/docs/how-to-contribute/). + +## License +[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) Copyright (C) Apache Software Foundation diff --git a/docs/1_0_0/Milestone.md b/docs/1_0_0/Milestone.md new file mode 100644 index 0000000..6a345eb --- /dev/null +++ b/docs/1_0_0/Milestone.md @@ -0,0 +1,102 @@ +#Deploy Plan + +we will deploy the first rocketmq-console-ng use rocketmq-tools 3.5.8(or 4.0.0),base on [rocket-console](https://github.com/didapinchegit/rocket-console),thanks didapinche.com + +## Framework +* 0. we use spring-boot + bootstrap + angularjs + +## something to improve +* 0. clean code (checkStyle codeStyle to be done) +* 1. international +* 2. compress fe'resource +* 3. navigation bar can improve +* 4. write operation need confirm,action show the detail result +* 5. layout/UI should improve +* 6. change to spring-boot + + +## something to fix +* query Message by topic and time is not accurate, will lost some message +* consumer can consume the message when topic has been deleted +* can't show producerList,we can only query a online producer use topic and groupName,not easy to use. +* resetOffset should be improve,online consumer can return the reset result but offline's can't +* we can't set clusterName when create topic or consumer +* when create a new consumer,if not be consumed,can't be found in consumerList + + +## something to add +* 1. dashboard + +# Roadmap + +## Improve +- [x] clean code (checkStyle codeStyle to be done) -- StyleTang +- [x] international -- Deploy by [tcrow](https://github.com/tcrow) +- [x] layout/UI -- Deploy by [tcrow](https://github.com/tcrow) + - [x] compress fe'resource + - [x] navigation bar can improve + - [x] write operation need confirm,action show the detail result || already have + - [x] layout/UI should improve +- [x] change to spring-boot -- Deploy by syzjava +- [x] change to bootstrap angularjs -- Deploy by [tcrow](https://github.com/tcrow) +- [x] improve search message --StyleTang + +## Fix +- [x] query Message by topic and time is not accurate, will lost some message -- StyleTang (need test) +- [x] consumer can consume the message when topic has been deleted // offset be clear.if have problem,reopen it. +- [ ] can't show producerList,we can only query a online producer use topic and groupName,not easy to use. [need this issues](https://issues.apache.org/jira/browse/ROCKETMQ-49)(next milestone) +- [ ] resetOffset should be improve,online consumer can return the reset result but offline's can't //this version(3.5.8) may be can't fix (next milestone) +- [x] we can't set clusterName when create topic or consumer -- StyleTang +- [x] when create a new consumer,if not be consumed,can't be found in consumerList //it Fixed,But this page is too slow,need improve --StyleTang +- [x] message view page,resend message (version >=3.5.8) have bug -- StyleTang + +## Add +- [ ] DashboardController -- Deploy by [tcrow](https://github.com/tcrow) + - [x] rocketmq topic tps 5m line chart + - [x] rocketmq topic top10 table + - [x] broker load 5m line chart + - [x] broker load top10 table + - [ ] topic exception table(next milestone) + +## Already Have (Deploy by StyleTang) But Can Improve +### Cluster +- [x] ClusterController + - [x] Cluster OverView + - [x] Broker Status + - [x] Broker Config + +### Topic +- [x] TopicController + - [x] TopicList + - [x] Topic Status + - [x] Topic Router + - [x] View Topic Config + - [x] Topci Add / Update + - [X] Send A Test Topic + - [x] Reset ConsumerGroup's Offset Under This Topic + - [x] Delete This Topic + +### Producer +- [x] ProducerController + - [x] Producer Client Info + + +### Consumer +- [x] ConsumerController + - [x] ConsumerList + - [x] Consumer Client Info + - [x] Topic Consume Status Under This Consumer Group + - [x] View Consumer Config + - [x] Consumer Add / Update + - [x] Delete This Consumer + +### Message +- [x] MessageController + - [x] Query By Topic And Time + - [x] Query By Topic And Key + - [x] Query By MessageId(OffsetMessageId) + - [x] A Nice Message Detail View + - [x] Message Consume Status + - [x] Resend Message To A Consume Group + + \ No newline at end of file diff --git a/docs/1_0_0/UserGuide_CN.md b/docs/1_0_0/UserGuide_CN.md new file mode 100644 index 0000000..0bf62a4 --- /dev/null +++ b/docs/1_0_0/UserGuide_CN.md @@ -0,0 +1,152 @@ +# RocketMQ使用文档 + +## 运维页面 +* 你可以修改这个服务使用的namesrv的地址 +* 你可以修改这个服务是否使用VIPChannel(如果你的mq server版本小于3.5.8,请设置不使用) + +## 驾驶舱 +* 查看broker的消息量(总量/5分钟图) +* 查看单一主题的消息量(总量/趋势图) + +## 集群页面 +* 查看集群的分布情况 + * cluster与broker关系 + * broker +* 查看broker具体信息/运行信息 +* 查看broker配置信息 + +## 主题页面 +* 展示所有的主题,可以通过搜索框进行过滤 +* 筛选 普通/重试/死信 主题 +* 添加/更新主题 + * clusterName 创建在哪几个cluster上 + * brokerName 创建在哪几个broker上 + * topicName 主题名 + * writeQueueNums 写队列数量 + * readQueueNums 读队列数量 + * perm //2是写 4是读 6是读写 +* 状态 查询消息投递状态(投递到哪些broker/哪些queue/多少量等) +* 路由 查看消息的路由(现在你发这个主题的消息会发往哪些broker,对应broker的queue信息) +* CONSUMER管理(这个topic都被哪些group消费了,消费情况何如) +* topic配置(查看变更当前的配置) +* 发送消息(向这个主题发送一个测试消息) +* 重置消费位点(分为在线和不在线两种情况,不过都需要检查重置是否成功) +* 删除主题 (会删除掉所有broker以及namesrv上的主题配置和路由信息) + +## 消费者页面 +* 展示所有的消费组,可以通过搜索框进行过滤 +* 刷新页面/每隔五秒定时刷新页面 +* 按照订阅组/数量/TPS/延迟 进行排序 +* 添加/更新消费组 + * clusterName 创建在哪几个集群上 + * brokerName 创建在哪几个broker上 + * groupName 消费组名字 + * consumeEnable //是否可以消费 FALSE的话将无法进行消费 + * consumeBroadcastEnable //是否可以广播消费 + * retryQueueNums //重试队列的大小 + * brokerId //正常情况从哪消费 + * whichBrokerWhenConsumeSlowly//出问题了从哪消费 +* 终端 在线的消费客户端查看,包括版本订阅信息和消费模式 +* 消费详情 对应消费组的消费明细查看,这个消费组订阅的所有Topic的消费情况,每个queue对应的消费client查看(包括Retry消息) +* 配置 查看变更消费组的配置 +* 删除 在指定的broker上删除消费组 + +## 发布管理页面 +* 通过Topic和Group查询在线的消息生产者客户端 + * 信息包含客户端主机 版本 + +## 消息查询页面 +* 根据Topic和时间区间查询 + *由于数据量大 最多只会展示2000条,多的会被忽略 +* 根据Topic和Key进行查询 + * 最多只会展示64条 +* 根据消息主题和消息Id进行消息的查询 +* 消息详情可以展示这条消息的详细信息,查看消息对应到具体消费组的消费情况(如果异常,可以查看具体的异常信息)。可以向指定的消费组重发消息。 + + +## HTTPS 方式访问Dashboard +* HTTPS功能实际上是使用SpringBoot提供的配置功能即可完成,首先,需要有一个SSL KeyStore来存放服务端证书,可以使用本工程所提供的测试密钥库: +resources/rmqcngkeystore.jks, 它可以通过如下keytool命令生成 +``` +#生成库并以rmqcngKey别名添加秘钥 +keytool -genkeypair -alias rmqcngKey -keyalg RSA -validity 3650 -keystore rmqcngkeystore.jks +#查看keystore内容 +keytool -list -v -keystore rmqcngkeystore.jks +#转换库格式 +keytool -importkeystore -srckeystore rmqcngkeystore.jks -destkeystore rmqcngkeystore.jks -deststoretype pkcs12 +``` + +* 配置resources/application.properties, 打开SSL的相关选项, 启动dashboard后即开启了HTTPS. +``` +#设置https端口 +server.port=8443 + +### SSL setting +#server.ssl.key-store=classpath:rmqcngkeystore.jks +#server.ssl.key-store-password=rocketmq +#server.ssl.keyStoreType=PKCS12 +#server.ssl.keyAlias=rmqcngkey +``` + +## 登录访问Dashboard +在访问Dashboard时支持按用户名和密码登录控制台,在操作完成后登出。需要做如下的设置: + +* 1.在Spring配置文件resources/application.properties中修改rocketmq.config.loginRequired=true开启登录功能 +```$xslt +# 开启登录功能 +rocketmq.config.loginRequired=true + +# Dashboard文件目录,登录用户配置文件所在目录 +rocketmq.config.dataPath=/tmp/rocketmq-console/data +``` +* 2.确保${rocketmq.config.dataPath}定义的目录存在,并且该目录下创建登录配置文件"users.properties", 如果该目录下不存在此文件,则默认使用resources/users.properties文件。 +users.properties文件格式为: +```$xslt +# 该文件支持热修改,即添加和修改用户时,不需要重新启动console +# 格式, 每行定义一个用户, username=password[,N] #N是可选项,可以为0 (普通用户); 1 (管理员) + +#定义管理员 +admin=admin,1 + +#定义普通用户 +user1=user1 +user2=user2 +``` +* 3.启动控制台则开启了登录功能 + +## 权限检验 +如果用户访问console时开启了登录功能,会按照登录的角色对访问的接口进行权限控制。 +* 1.在Spring配置文件resources/application.properties中修改rocketmq.config.loginRequired=true开启登录功能 +```$xslt +# 开启登录功能 +rocketmq.config.loginRequired=true + +# Dashboard文件目录,登录用户配置文件所在目录 +rocketmq.config.dataPath=/tmp/rocketmq-console/data +``` +* 2.确保${rocketmq.config.dataPath}定义的目录存在,并且该目录下创建访问权限配置文件"role-permission.yml", +如果该目录下不存在此文件,则默认使用resources/role-permission.yml文件。该文件保存了普通用户角色所有能访问的接口地址。 +role-permission.yml文件格式为: +```$xslt +# 该文件支持热修改,即添加和修改用户时,不需要重新启动console +# 格式,如果增加和删除接口权限,直接在列表中增加和删除接口地址即可。 +# 接口路径配置支持通配符 +# * 表示匹配0或多个不是/的字符 +# ** 表示匹配0或多个任意字符 +# ? 表示匹配1个任意字符 + +rolePerms: + # 普通用户 + ordinary: + - /rocketmq/nsaddr + - /ops/* + - /dashboard/** + - /topic/*.query + - /topic/sendTopicMessage.do + - /producer/*.query + - /message/* + - /messageTrace/* + - /monitor/* + .... +``` +* 3.前端页面显示上,为了更好区分普通用户和admin用户权限,关于资源的删除、更新等操作按钮不对普通用户角色显示,如果要执行资源相关操作,需要退出使用admin角色登录。 \ No newline at end of file diff --git a/docs/1_0_0/UserGuide_EN.md b/docs/1_0_0/UserGuide_EN.md new file mode 100644 index 0000000..fd469d2 --- /dev/null +++ b/docs/1_0_0/UserGuide_EN.md @@ -0,0 +1,158 @@ +# RocketMQ User Guide + +## OPS Page +* You can change dashboard's namesrvAddr here +* You can change the value of useVIPChannel here (if you rocketMQ version < 3.5.8,the value of useVIPChannel should be false) + +## DashBoard Page +* broker's message count (broker total message count/5 min trend) +* topic's message count(topic total message count/5 min trend) + +## Cluster Page +* Cluster Detail + * relation between cluster and broker + * broker's master / salve node +* broker'a detail info(runtime info) +* broker's config + +## Topic Page +* show all the topics,you can filter topic by search bar +* filter (Normal/retry/dead) topic +* Add/Update Topic + * clusterName (create on which cluster) + * brokerName (create on which broker) + * topicName + * writeQueueNums + * readQueueNums + * perm //2 for write 4 for read 6 for write and read +* STATUS look over message send status(send to which broker/which queue/how many messages) +* ROUTER look update topic's router(this topic send to which broker,the broker's queue info) +* CONSUMER MANAGE(this topic consume by which group,how about the consume state) +* TOPIC CONFIG(check or change the topic's config) +* SEND MESSAGE(send a test message) +* Reset CONSUMER OFFSET (the consumer online or not online is different,you need check the reset result) +* DELETE (will delete the topic on all broker and namesrv) + +## Consumer Page +* show all the consumers,you can filter consumer by search bar +* refresh page/refresh page per 5 seconds +* order by SubscriptionGroup/Quantity/TPS/Delay +* Add/Update Consumer + * clusterName (create on which cluster) + * brokerName (create on which broker) + * groupName (consumer group name) + * consumeEnable (this group can't consume message if this is false) + * consumeBroadcastEnable (can't use broadcast is this is false) + * retryQueueNums + * brokerId (consume form where when broker is normal) + * whichBrokerWhenConsumeSlowly(consume form where when broker has problem) +* CLIENT (look over online consumer's client,include subscribe info and consume mode) +* CONSUME DETAIL (look over this consumer's consume detail,broker offset and the consumer offset,queue consumed by which client) +* CONFIG (check or change the consumer's config) +* DELETE (delete the consumer group on selected group) + +## Producer Page +* Query online producer client by topic and group + * show client's server / version + +## Message Page +* Query By Topic And Time + *Only Return 2000 Messages,the message more than 2000 will be hide +* Query By Topic And Key + * Only Return 64 Messages +* Query By Topic And MessageId +* look over this message's detail info.you can see the message's consume state(each group has one line),show the exception message if has exception. +you can send this message to the group you selected + + +## Access Dashboard with HTTPS +* SpringBoot itself has provided the SSL configuration. You can use the project test Keystore:resources/rmqcngkeystore.jks. The store is generated with the following unix keytool commands: +``` +#Generate Keystore and add alias rmqcngKey +keytool -genkeypair -alias rmqcngKey -keyalg RSA -validity 3650 -keystore rmqcngkeystore.jks +#View keystore content +keytool -list -v -keystore rmqcngkeystore.jks +#Transfer type as official +keytool -importkeystore -srckeystore rmqcngkeystore.jks -destkeystore rmqcngkeystore.jks -deststoretype pkcs12 +``` + +* Uncomment the following SSL properties in resources/application.properties. restart Dashboard then access with HTTPS. + +``` +#Set https port +server.port=8443 + +### SSL setting +server.ssl.key-store=classpath:rmqcngkeystore.jks +server.ssl.key-store-password=rocketmq +server.ssl.keyStoreType=PKCS12 +server.ssl.keyAlias=rmqcngkey +``` + +## Login/Logout on Dashboard +Access Dashboard with username and password and logout to leave the dashboard。To stage the function on, we need the steps below: + +* 1.Turn on the property in resources/application.properties. +```$xslt +# open the login func +rocketmq.config.loginRequired=true + +# Directory of ashboard & login user configure file +rocketmq.config.dataPath=/tmp/rocketmq-console/data +``` +* 2.Make sure the directory defined in property ${rocketmq.config.dataPath} exists and the file "users.properties" is created under it. +The dashboard system will use the resources/users.properties by default if a customized file is not found。 + +The format in the content of users.properties: +```$xslt +# This file supports hot change, any change will be auto-reloaded without Console restarting. +# Format: a user per line, username=password[,N] #N is optional, 0 (Normal User); 1 (Admin) + +# Define Admin +admin=admin,1 + +# Define Normal users +user1=user1 +user2=user2 +``` +* 3.Restart Console Application after above configuration setting well. + + +## Permission Control +If the login function is enabled when a user accesses the Console, the user controls the access permission of the interface based on the login role. + +* 1.Turn on the property in resources/application.properties. +```$xslt +# open the login func +rocketmq.config.loginRequired=true + +# Directory of ashboard & login user configure file +rocketmq.config.dataPath=/tmp/rocketmq-console/data +``` +* 2.Make sure the directory defined in property ${rocketmq.config.dataPath} exists and the permission control file "role-permission.yml" is created under it. +The console system will use the resources/role-permission.yml by default if a customized file is not found。 + +The format in the content of role-permission.yml: +```$xslt +# This file supports hot change, any change will be auto-reloaded without Console restarting. +# Format: To add or delete interface permissions, add or delete interface addresses from the list. +# the interface paths can be configured with wildcard characters. +# ?: Matches 1 characters. +# *: Matches 0 or more characters that are not /. +# **: Matches 0 or more characters. + +rolePerms: + # ordinary user + ordinary: + - /rocketmq/nsaddr + - /ops/* + - /dashboard/** + - /topic/*.query + - /topic/sendTopicMessage.do + - /producer/*.query + - /message/* + - /messageTrace/* + - /monitor/* + .... +``` +* 3.On the front page, operation buttons such as deleting and updating resources are not displayed for common users in order to better distinguish the rights of common users and admin users. If need to operate related resources, log out and use the admin role to log in \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..c88d2c7 --- /dev/null +++ b/pom.xml @@ -0,0 +1,374 @@ + + + + + + + org.apache + apache + 18 + + + 4.0.0 + org.apache.rocketmq + rocketmq-dashboard + jar + 1.0.0 + rocketmq-dashboard + + + git@github.com:apache/rocketmq-dashboard.git + scm:git:git@github.com:apache/rocketmq-dashboard.git + scm:git:git@github.com:apache/rocketmq-dashboard.git + rocketmq-dashboard-1.0.0 + + + + + Development List + dev-subscribe@rocketmq.apache.org + dev-unsubscribe@rocketmq.apache.org + dev@rocketmq.apache.org + + + User List + users-subscribe@rocketmq.apache.org + users-unsubscribe@rocketmq.apache.org + users@rocketmq.apache.org + + + Commits List + commits-subscribe@rocketmq.apache.org + commits-unsubscribe@rocketmq.apache.org + commits@rocketmq.apache.org + + + + + + Apache RocketMQ + Apache RocketMQ of ASF + https://rocketmq.apache.org/ + + + + + Apache Software Foundation + http://www.apache.org + + + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + repo + + + + + UTF-8 + 1.8 + 1.8 + + 29.0-jre + 2.1 + 2.6 + 2.4 + 1.2 + 4.9.0 + 2.19.1 + 1.9.6 + 1.18.12 + ${basedir}/../.. + apacherocketmq + 2.2.2.RELEASE + 3.3.3 + 2.3.1 + 2.4.3 + + + + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + + + org.springframework.boot + spring-boot-starter-actuator + ${spring.boot.version} + + + org.springframework.data + spring-data-commons + ${spring.boot.version} + + + org.springframework.boot + spring-boot-starter-test + ${spring.boot.version} + test + + + commons-collections + commons-collections + 3.2.2 + + + org.apache.rocketmq + rocketmq-tools + ${rocketmq.version} + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + + + org.apache.rocketmq + rocketmq-namesrv + ${rocketmq.version} + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + + + org.apache.rocketmq + rocketmq-broker + ${rocketmq.version} + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + + + com.google.guava + guava + ${guava.version} + + + org.aspectj + aspectjrt + ${aspectj.version} + + + org.aspectj + aspectjweaver + ${aspectj.version} + + + + cglib + cglib + 2.2.2 + + + org.jooq + joor + 0.9.6 + + + org.bouncycastle + bcpkix-jdk15on + 1.68 + + + javax.xml.bind + jaxb-api + ${jaxb-api.version} + + + org.projectlombok + lombok + ${lombok.version} + + + org.mockito + mockito-inline + ${mockito-inline.version} + test + + + org.apache.commons + commons-pool2 + ${commons-pool2.version} + + + io.netty + netty-all + 4.1.82.Final + + + + + + maven-compiler-plugin + 3.5.1 + + ${maven.compiler.source} + ${maven.compiler.target} + ${maven.compiler.source} + true + true + + + org.projectlombok + lombok + ${lombok.version} + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + + com.spotify + docker-maven-plugin + 0.4.11 + + + javax.activation + javax.activation-api + 1.2.0 + compile + + + + ${docker.image.prefix}/${project.artifactId} + src/main/docker + + + / + ${project.build.directory} + ${project.build.finalName}.jar + + + + ${project.version} + latest + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.17 + + + validate + validate + + src/main/resources + style/rmq_checkstyle.xml + UTF-8 + true + true + + + check + + + + + + org.codehaus.mojo + findbugs-maven-plugin + 3.0.4 + + + org.jacoco + jacoco-maven-plugin + 0.8.7 + + + prepare-agent + + prepare-agent + + + + + + org.eluder.coveralls + coveralls-maven-plugin + 4.3.0 + + + javax.xml.bind + jaxb-api + ${jaxb-api.version} + + + + + org.apache.rat + apache-rat-plugin + 0.12 + + + .gitignore + .travis.yml + .asf.yaml + README.md + .github/** + docs/** + src/main/resources/static/vendor/** + src/main/resources/static/src/data/** + + + + + + diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile new file mode 100644 index 0000000..391fd7d --- /dev/null +++ b/src/main/docker/Dockerfile @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +FROM java:8 +VOLUME /tmp +ADD rocketmq-dashboard-*.jar rocketmq-dashboard.jar +RUN sh -c 'touch /rocketmq-dashboard.jar' +ENV JAVA_OPTS="" +ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -jar /rocketmq-dashboard.jar" ] diff --git a/src/main/java/org/apache/rocketmq/dashboard/Application.java b/src/main/java/org/apache/rocketmq/dashboard/Application.java new file mode 100644 index 0000000..704b029 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/Application.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.context.annotation.EnableMBeanExport; +import org.springframework.jmx.support.RegistrationPolicy; +import org.springframework.scheduling.annotation.EnableScheduling; + +@SpringBootApplication +@EnableScheduling +@ServletComponentScan +@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING) +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/admin/MQAdminFactory.java b/src/main/java/org/apache/rocketmq/dashboard/admin/MQAdminFactory.java new file mode 100644 index 0000000..8bbf19d --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/admin/MQAdminFactory.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.admin; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.rocketmq.acl.common.AclClientRPCHook; +import org.apache.rocketmq.acl.common.SessionCredentials; +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.remoting.RPCHook; +import org.apache.rocketmq.tools.admin.DefaultMQAdminExt; +import org.apache.rocketmq.tools.admin.MQAdminExt; + +@Slf4j +public class MQAdminFactory { + private RMQConfigure rmqConfigure; + + public MQAdminFactory(RMQConfigure rmqConfigure) { + this.rmqConfigure = rmqConfigure; + } + + public MQAdminExt getInstance() throws Exception { + RPCHook rpcHook = null; + final String accessKey = rmqConfigure.getAccessKey(); + final String secretKey = rmqConfigure.getSecretKey(); + boolean isEnableAcl = StringUtils.isNotEmpty(accessKey) && StringUtils.isNotEmpty(secretKey); + if (isEnableAcl) { + rpcHook = new AclClientRPCHook(new SessionCredentials(accessKey, secretKey)); + } + DefaultMQAdminExt mqAdminExt = null; + if (rmqConfigure.getTimeoutMillis() == null) { + mqAdminExt = new DefaultMQAdminExt(rpcHook); + } else { + mqAdminExt = new DefaultMQAdminExt(rpcHook, rmqConfigure.getTimeoutMillis()); + } + mqAdminExt.setVipChannelEnabled(Boolean.parseBoolean(rmqConfigure.getIsVIPChannel())); + mqAdminExt.setUseTLS(rmqConfigure.isUseTLS()); + mqAdminExt.setInstanceName(Long.toString(System.currentTimeMillis())); + mqAdminExt.start(); + log.info("create MQAdmin instance {} success.", mqAdminExt); + return mqAdminExt; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/admin/MQAdminPooledObjectFactory.java b/src/main/java/org/apache/rocketmq/dashboard/admin/MQAdminPooledObjectFactory.java new file mode 100644 index 0000000..b68f931 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/admin/MQAdminPooledObjectFactory.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.admin; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.pool2.PooledObject; +import org.apache.commons.pool2.PooledObjectFactory; +import org.apache.commons.pool2.impl.DefaultPooledObject; +import org.apache.rocketmq.common.protocol.body.ClusterInfo; +import org.apache.rocketmq.tools.admin.MQAdminExt; + +@Slf4j +public class MQAdminPooledObjectFactory implements PooledObjectFactory { + + private MQAdminFactory mqAdminFactory; + + @Override + public PooledObject makeObject() throws Exception { + DefaultPooledObject pooledObject = new DefaultPooledObject<>( + mqAdminFactory.getInstance()); + return pooledObject; + } + + @Override + public void destroyObject(PooledObject p) { + MQAdminExt mqAdmin = p.getObject(); + if (mqAdmin != null) { + try { + mqAdmin.shutdown(); + } catch (Exception e) { + log.warn("MQAdminExt shutdown err", e); + } + } + log.info("destroy object {}", p.getObject()); + } + + @Override + public boolean validateObject(PooledObject p) { + MQAdminExt mqAdmin = p.getObject(); + ClusterInfo clusterInfo = null; + try { + clusterInfo = mqAdmin.examineBrokerClusterInfo(); + } catch (Exception e) { + log.warn("validate object {} err", p.getObject(), e); + } + if (clusterInfo == null || MapUtils.isEmpty(clusterInfo.getBrokerAddrTable())) { + log.warn("validateObject failed, clusterInfo = {}", clusterInfo); + return false; + } + return true; + } + + @Override + public void activateObject(PooledObject p) { + } + + @Override + public void passivateObject(PooledObject p) { + } + + public void setMqAdminFactory(MQAdminFactory mqAdminFactory) { + this.mqAdminFactory = mqAdminFactory; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/admin/MqAdminExtObjectPool.java b/src/main/java/org/apache/rocketmq/dashboard/admin/MqAdminExtObjectPool.java new file mode 100644 index 0000000..976c009 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/admin/MqAdminExtObjectPool.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.admin; + +import org.apache.commons.pool2.impl.GenericObjectPool; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.tools.admin.MQAdminExt; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MqAdminExtObjectPool { + + @Autowired + private RMQConfigure rmqConfigure; + + @Bean + public GenericObjectPool mqAdminExtPool() { + GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig(); + genericObjectPoolConfig.setTestWhileIdle(true); + genericObjectPoolConfig.setMaxWaitMillis(10000); + genericObjectPoolConfig.setTimeBetweenEvictionRunsMillis(20000); + MQAdminPooledObjectFactory mqAdminPooledObjectFactory = new MQAdminPooledObjectFactory(); + MQAdminFactory mqAdminFactory = new MQAdminFactory(rmqConfigure); + mqAdminPooledObjectFactory.setMqAdminFactory(mqAdminFactory); + GenericObjectPool genericObjectPool = new GenericObjectPool( + mqAdminPooledObjectFactory, + genericObjectPoolConfig); + return genericObjectPool; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/aspect/admin/MQAdminAspect.java b/src/main/java/org/apache/rocketmq/dashboard/aspect/admin/MQAdminAspect.java new file mode 100644 index 0000000..56f37f6 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/aspect/admin/MQAdminAspect.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.aspect.admin; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.pool2.impl.GenericObjectPool; +import org.apache.rocketmq.dashboard.service.client.MQAdminInstance; +import org.apache.rocketmq.tools.admin.MQAdminExt; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Aspect +@Service +@Slf4j +public class MQAdminAspect { + + @Autowired + private GenericObjectPool mqAdminExtPool; + + public MQAdminAspect() { + } + + @Pointcut("execution(* org.apache.rocketmq.dashboard.service.client.MQAdminExtImpl..*(..))") + public void mQAdminMethodPointCut() { + + } + + @Around(value = "mQAdminMethodPointCut()") + public Object aroundMQAdminMethod(ProceedingJoinPoint joinPoint) throws Throwable { + long start = System.currentTimeMillis(); + Object obj = null; + try { + MQAdminInstance.createMQAdmin(mqAdminExtPool); + obj = joinPoint.proceed(); + } finally { + MQAdminInstance.returnMQAdmin(mqAdminExtPool); + log.debug("op=look method={} cost={}", joinPoint.getSignature().getName(), System.currentTimeMillis() - start); + } + return obj; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/aspect/admin/annotation/OriginalControllerReturnValue.java b/src/main/java/org/apache/rocketmq/dashboard/aspect/admin/annotation/OriginalControllerReturnValue.java new file mode 100644 index 0000000..d3391bd --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/aspect/admin/annotation/OriginalControllerReturnValue.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.aspect.admin.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface OriginalControllerReturnValue { +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/config/AuthWebMVCConfigurerAdapter.java b/src/main/java/org/apache/rocketmq/dashboard/config/AuthWebMVCConfigurerAdapter.java new file mode 100644 index 0000000..3783eb3 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/config/AuthWebMVCConfigurerAdapter.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.config; + +import org.apache.rocketmq.dashboard.interceptor.AuthInterceptor; +import org.apache.rocketmq.dashboard.model.UserInfo; +import org.apache.rocketmq.dashboard.util.WebUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import org.springframework.web.multipart.support.MissingServletRequestPartException; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +@Configuration +public class AuthWebMVCConfigurerAdapter extends WebMvcConfigurerAdapter { + @Autowired + @Qualifier("authInterceptor") + private AuthInterceptor authInterceptor; + + @Resource + RMQConfigure configure; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + if (configure.isLoginRequired()) { + registry.addInterceptor(authInterceptor).addPathPatterns( + "/cluster/**", + "/consumer/**", + "/dashboard/**", + "/message/**", + "/messageTrace/**", + "/monitor/**", + "/rocketmq/**", + "/ops/**", + "/producer/**", + "/test/**", + "/topic/**", + "/config/**"); + } + } + + @Override + public void addArgumentResolvers(List argumentResolvers) { + argumentResolvers.add(new HandlerMethodArgumentResolver() { + + @Override + public boolean supportsParameter(MethodParameter methodParameter) { + return methodParameter.getParameterType().isAssignableFrom(UserInfo.class); + } + + @Override + public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, + NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception { + UserInfo userInfo = (UserInfo) WebUtil.getValueFromSession((HttpServletRequest) nativeWebRequest.getNativeRequest(), + UserInfo.USER_INFO); + if (userInfo != null) { + return userInfo; + } + throw new MissingServletRequestPartException(UserInfo.USER_INFO); + } + }); + + super.addArgumentResolvers(argumentResolvers); //REVIEW ME + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/config/RMQConfigure.java b/src/main/java/org/apache/rocketmq/dashboard/config/RMQConfigure.java new file mode 100644 index 0000000..1dcd7df --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/config/RMQConfigure.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.config; + +import org.apache.commons.lang3.StringUtils; +import org.apache.rocketmq.common.MixAll; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.web.server.ErrorPage; +import org.springframework.boot.web.server.ErrorPageRegistrar; +import org.springframework.boot.web.server.ErrorPageRegistry; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; + +import java.io.File; + +import static org.apache.rocketmq.client.ClientConfig.SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY; + +@Configuration +@ConfigurationProperties(prefix = "rocketmq.config") +public class RMQConfigure { + + private Logger logger = LoggerFactory.getLogger(RMQConfigure.class); + //use rocketmq.namesrv.addr first,if it is empty,than use system proerty or system env + private volatile String namesrvAddr = System.getProperty(MixAll.NAMESRV_ADDR_PROPERTY, System.getenv(MixAll.NAMESRV_ADDR_ENV)); + + private volatile String isVIPChannel = System.getProperty(SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY, "true"); + + + private String dataPath = "/tmp/rocketmq-console/data"; + + private boolean enableDashBoardCollect; + + private boolean loginRequired = false; + + private String accessKey; + + private String secretKey; + + private boolean useTLS = false; + + private Long timeoutMillis; + + public String getAccessKey() { + return accessKey; + } + + public void setAccessKey(String accessKey) { + this.accessKey = accessKey; + } + + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public String getNamesrvAddr() { + return namesrvAddr; + } + + public void setNamesrvAddr(String namesrvAddr) { + if (StringUtils.isNotBlank(namesrvAddr)) { + this.namesrvAddr = namesrvAddr; + System.setProperty(MixAll.NAMESRV_ADDR_PROPERTY, namesrvAddr); + logger.info("setNameSrvAddrByProperty nameSrvAddr={}", namesrvAddr); + } + } + public boolean isACLEnabled() { + return !(StringUtils.isAnyBlank(this.accessKey, this.secretKey) || + StringUtils.isAnyEmpty(this.accessKey, this.secretKey)); + } + public String getRocketMqDashboardDataPath() { + return dataPath; + } + + public String getDashboardCollectData() { + return dataPath + File.separator + "dashboard"; + } + + public void setDataPath(String dataPath) { + this.dataPath = dataPath; + } + + public String getIsVIPChannel() { + return isVIPChannel; + } + + public void setIsVIPChannel(String isVIPChannel) { + if (StringUtils.isNotBlank(isVIPChannel)) { + this.isVIPChannel = isVIPChannel; + System.setProperty(SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY, isVIPChannel); + logger.info("setIsVIPChannel isVIPChannel={}", isVIPChannel); + } + } + + public boolean isEnableDashBoardCollect() { + return enableDashBoardCollect; + } + + public void setEnableDashBoardCollect(String enableDashBoardCollect) { + this.enableDashBoardCollect = Boolean.valueOf(enableDashBoardCollect); + } + + public boolean isLoginRequired() { + return loginRequired; + } + + public void setLoginRequired(boolean loginRequired) { + this.loginRequired = loginRequired; + } + + public boolean isUseTLS() { + return useTLS; + } + + public void setUseTLS(boolean useTLS) { + this.useTLS = useTLS; + } + + public Long getTimeoutMillis() { + return timeoutMillis; + } + + public void setTimeoutMillis(Long timeoutMillis) { + this.timeoutMillis = timeoutMillis; + } + + // Error Page process logic, move to a central configure later + @Bean + public ErrorPageRegistrar errorPageRegistrar() { + return new MyErrorPageRegistrar(); + } + + private static class MyErrorPageRegistrar implements ErrorPageRegistrar { + + @Override + public void registerErrorPages(ErrorPageRegistry registry) { + registry.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404")); + } + + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/ClusterController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/ClusterController.java new file mode 100644 index 0000000..c3e9fd2 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/controller/ClusterController.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.controller; + +import org.apache.rocketmq.dashboard.permisssion.Permission; +import org.apache.rocketmq.dashboard.service.ClusterService; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import javax.annotation.Resource; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping("/cluster") +@Permission +public class ClusterController { + + @Resource + private ClusterService clusterService; + + @RequestMapping(value = "/list.query", method = RequestMethod.GET) + @ResponseBody + public Object list() { + return clusterService.list(); + } + + @RequestMapping(value = "/brokerConfig.query", method = RequestMethod.GET) + @ResponseBody + public Object brokerConfig(@RequestParam String brokerAddr) { + return clusterService.getBrokerConfig(brokerAddr); + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/ConfigController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/ConfigController.java new file mode 100644 index 0000000..bd9f228 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/controller/ConfigController.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.controller; + +import org.apache.rocketmq.dashboard.permisssion.Permission; +import org.apache.rocketmq.dashboard.service.ConfigService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.Map; + +@Controller +@RequestMapping("/config") +//@Permission +public class ConfigController { + private Logger logger = LoggerFactory.getLogger(ConfigController.class); + + @Resource + private ConfigService configService; + + @RequestMapping(value = "/config.query") + @ResponseBody + public Map query() { + return configService.queryConfigMap(); + } + + @RequestMapping(value = "/config.create") + @ResponseBody + public boolean create(String name, String value) { + return configService.createConfig(name, value); + } + + @RequestMapping(value = "/config.update") + @ResponseBody + public boolean update(String name, String value) { + return configService.updateConfig(name, value); + } + + @RequestMapping(value = "/config.delete") + @ResponseBody + public boolean delete(String name, String value) { + return configService.deleteConfig(name, value); + } + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/ConsumerController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/ConsumerController.java new file mode 100644 index 0000000..f636945 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/controller/ConsumerController.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.controller; + +import com.google.common.base.Preconditions; +import javax.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; +import org.apache.rocketmq.common.protocol.body.ConsumerConnection; +import org.apache.rocketmq.dashboard.model.ConnectionInfo; +import org.apache.rocketmq.dashboard.model.request.ConsumerConfigInfo; +import org.apache.rocketmq.dashboard.model.request.DeleteSubGroupRequest; +import org.apache.rocketmq.dashboard.model.request.ResetOffsetRequest; +import org.apache.rocketmq.dashboard.permisssion.Permission; +import org.apache.rocketmq.dashboard.service.ConsumerService; +import org.apache.rocketmq.dashboard.util.JsonUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping("/consumer") +@Permission +public class ConsumerController { + private Logger logger = LoggerFactory.getLogger(ConsumerController.class); + + @Resource + private ConsumerService consumerService; + + @RequestMapping(value = "/groupList.query") + @ResponseBody + public Object list() { + return consumerService.queryGroupList(); + } + + @RequestMapping(value = "/group.query") + @ResponseBody + public Object groupQuery(@RequestParam String consumerGroup) { + return consumerService.queryGroup(consumerGroup); + } + + @RequestMapping(value = "/resetOffset.do", method = {RequestMethod.POST}) + @ResponseBody + public Object resetOffset(@RequestBody ResetOffsetRequest resetOffsetRequest) { + logger.info("op=look resetOffsetRequest={}", JsonUtil.obj2String(resetOffsetRequest)); + return consumerService.resetOffset(resetOffsetRequest); + } + + @RequestMapping(value = "/skipAccumulate.do", method = {RequestMethod.POST}) + @ResponseBody + public Object skipAccumulate(@RequestBody ResetOffsetRequest resetOffsetRequest) { + logger.info("op=look resetOffsetRequest={}", JsonUtil.obj2String(resetOffsetRequest)); + return consumerService.resetOffset(resetOffsetRequest); + } + + @RequestMapping(value = "/examineSubscriptionGroupConfig.query") + @ResponseBody + public Object examineSubscriptionGroupConfig(@RequestParam String consumerGroup) { + return consumerService.examineSubscriptionGroupConfig(consumerGroup); + } + + @RequestMapping(value = "/deleteSubGroup.do", method = {RequestMethod.POST}) + @ResponseBody + public Object deleteSubGroup(@RequestBody DeleteSubGroupRequest deleteSubGroupRequest) { + return consumerService.deleteSubGroup(deleteSubGroupRequest); + } + + @RequestMapping(value = "/createOrUpdate.do", method = {RequestMethod.POST}) + @ResponseBody + public Object consumerCreateOrUpdateRequest(@RequestBody ConsumerConfigInfo consumerConfigInfo) { + Preconditions.checkArgument(CollectionUtils.isNotEmpty(consumerConfigInfo.getBrokerNameList()) || CollectionUtils.isNotEmpty(consumerConfigInfo.getClusterNameList()), + "clusterName or brokerName can not be all blank"); + return consumerService.createAndUpdateSubscriptionGroupConfig(consumerConfigInfo); + } + + @RequestMapping(value = "/fetchBrokerNameList.query", method = {RequestMethod.GET}) + @ResponseBody + public Object fetchBrokerNameList(@RequestParam String consumerGroup) { + return consumerService.fetchBrokerNameSetBySubscriptionGroup(consumerGroup); + } + + @RequestMapping(value = "/queryTopicByConsumer.query") + @ResponseBody + public Object queryConsumerByTopic(@RequestParam String consumerGroup) { + return consumerService.queryConsumeStatsListByGroupName(consumerGroup); + } + + @RequestMapping(value = "/consumerConnection.query") + @ResponseBody + public Object consumerConnection(@RequestParam(required = false) String consumerGroup) { + ConsumerConnection consumerConnection = consumerService.getConsumerConnection(consumerGroup); + consumerConnection.setConnectionSet(ConnectionInfo.buildConnectionInfoHashSet(consumerConnection.getConnectionSet())); + return consumerConnection; + } + + @RequestMapping(value = "/consumerRunningInfo.query") + @ResponseBody + public Object getConsumerRunningInfo(@RequestParam String consumerGroup, @RequestParam String clientId, + @RequestParam boolean jstack) { + return consumerService.getConsumerRunningInfo(consumerGroup, clientId, jstack); + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/DashboardController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/DashboardController.java new file mode 100644 index 0000000..0b46144 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/controller/DashboardController.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.controller; + +import javax.annotation.Resource; + +import com.google.common.base.Strings; +import org.apache.rocketmq.dashboard.permisssion.Permission; +import org.apache.rocketmq.dashboard.service.DashboardService; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping("/dashboard") +@Permission +public class DashboardController { + + @Resource + DashboardService dashboardService; + + @RequestMapping(value = "/broker.query", method = RequestMethod.GET) + @ResponseBody + public Object broker(@RequestParam String date) { + return dashboardService.queryBrokerData(date); + } + + @RequestMapping(value = "/topic.query", method = RequestMethod.GET) + @ResponseBody + public Object topic(@RequestParam String date, String topicName) { + if (Strings.isNullOrEmpty(topicName)) { + return dashboardService.queryTopicData(date); + } + return dashboardService.queryTopicData(date,topicName); + } + + @RequestMapping(value = "/topicCurrent", method = RequestMethod.GET) + @ResponseBody + public Object topicCurrent() { + return dashboardService.queryTopicCurrentData(); + } + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/LoginController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/LoginController.java new file mode 100644 index 0000000..ac76768 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/controller/LoginController.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.controller; + +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.dashboard.model.LoginInfo; +import org.apache.rocketmq.dashboard.model.LoginResult; +import org.apache.rocketmq.dashboard.model.User; +import org.apache.rocketmq.dashboard.model.UserInfo; +import org.apache.rocketmq.dashboard.service.UserService; +import org.apache.rocketmq.dashboard.support.JsonResult; +import org.apache.rocketmq.dashboard.util.WebUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Controller +@RequestMapping("/login") +public class LoginController { + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Resource + private RMQConfigure configure; + + @Autowired + private UserService userService; + + @Value("${server.servlet.context-path:/}") + private String contextPath; + + @RequestMapping(value = "/check.query", method = RequestMethod.GET) + @ResponseBody + public Object check(HttpServletRequest request) { + LoginInfo loginInfo = new LoginInfo(); + + loginInfo.setLogined(WebUtil.getValueFromSession(request, WebUtil.USER_NAME) != null); + loginInfo.setLoginRequired(configure.isLoginRequired()); + + return loginInfo; + } + + @RequestMapping(value = "/login.do", method = RequestMethod.POST) + @ResponseBody + public Object login(@RequestParam("username") String username, + @RequestParam(value = "password") String password, + HttpServletRequest request, + HttpServletResponse response) throws Exception { + logger.info("user:{} login", username); + User user = userService.queryByUsernameAndPassword(username, password); + + if (user == null) { + throw new IllegalArgumentException("Bad username or password!"); + } else { + user.setPassword(null); + UserInfo userInfo = WebUtil.setLoginInfo(request, response, user); + WebUtil.setSessionValue(request, WebUtil.USER_INFO, userInfo); + WebUtil.setSessionValue(request, WebUtil.USER_NAME, username); + userInfo.setSessionId(WebUtil.getSessionId(request)); + LoginResult result = new LoginResult(username, user.getType(), contextPath); + return result; + } + } + + @RequestMapping(value = "/logout.do", method = RequestMethod.POST) + @ResponseBody + public JsonResult logout(HttpServletRequest request) { + WebUtil.removeSession(request); + return new JsonResult<>(contextPath); + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/MessageController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/MessageController.java new file mode 100644 index 0000000..e4dfcd9 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/controller/MessageController.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.controller; + +import com.google.common.collect.Maps; +import org.apache.rocketmq.common.Pair; +import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult; +import org.apache.rocketmq.dashboard.model.MessagePage; +import org.apache.rocketmq.dashboard.model.MessageView; +import org.apache.rocketmq.dashboard.model.request.MessageQuery; +import org.apache.rocketmq.dashboard.permisssion.Permission; +import org.apache.rocketmq.dashboard.service.MessageService; +import org.apache.rocketmq.dashboard.util.JsonUtil; +import org.apache.rocketmq.tools.admin.api.MessageTrack; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; + +@Controller +@RequestMapping("/message") +@Permission +public class MessageController { + private Logger logger = LoggerFactory.getLogger(MessageController.class); + @Resource + private MessageService messageService; + + @RequestMapping(value = "/viewMessage.query", method = RequestMethod.GET) + @ResponseBody + public Object viewMessage(@RequestParam(required = false) String topic, @RequestParam String msgId) { + Map messageViewMap = Maps.newHashMap(); + Pair> messageViewListPair = messageService.viewMessage(topic, msgId); + messageViewMap.put("messageView", messageViewListPair.getObject1()); + messageViewMap.put("messageTrackList", messageViewListPair.getObject2()); + return messageViewMap; + } + + @PostMapping("/queryMessagePageByTopic.query") + @ResponseBody + public MessagePage queryMessagePageByTopic(@RequestBody MessageQuery query) { + return messageService.queryMessageByPage(query); + } + + @RequestMapping(value = "/queryMessageByTopicAndKey.query", method = RequestMethod.GET) + @ResponseBody + public Object queryMessageByTopicAndKey(@RequestParam String topic, @RequestParam String key) { + return messageService.queryMessageByTopicAndKey(topic, key); + } + + @RequestMapping(value = "/queryMessageByTopic.query", method = RequestMethod.GET) + @ResponseBody + public Object queryMessageByTopic(@RequestParam String topic, @RequestParam long begin, + @RequestParam long end) { + return messageService.queryMessageByTopic(topic, begin, end); + } + + @RequestMapping(value = "/consumeMessageDirectly.do", method = RequestMethod.POST) + @ResponseBody + public Object consumeMessageDirectly(@RequestParam String topic, @RequestParam String consumerGroup, + @RequestParam String msgId, + @RequestParam(required = false) String clientId) { + logger.info("msgId={} consumerGroup={} clientId={}", msgId, consumerGroup, clientId); + ConsumeMessageDirectlyResult consumeMessageDirectlyResult = messageService.consumeMessageDirectly(topic, msgId, consumerGroup, clientId); + logger.info("consumeMessageDirectlyResult={}", JsonUtil.obj2String(consumeMessageDirectlyResult)); + return consumeMessageDirectlyResult; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/MessageTraceController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/MessageTraceController.java new file mode 100644 index 0000000..ef3c1b1 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/controller/MessageTraceController.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.controller; + +import com.google.common.collect.Maps; + +import java.util.List; +import java.util.Map; +import javax.annotation.Resource; + +import org.apache.rocketmq.common.Pair; +import org.apache.rocketmq.dashboard.model.MessageView; +import org.apache.rocketmq.dashboard.model.trace.MessageTraceGraph; +import org.apache.rocketmq.dashboard.permisssion.Permission; +import org.apache.rocketmq.dashboard.service.MessageService; +import org.apache.rocketmq.dashboard.service.MessageTraceService; +import org.apache.rocketmq.tools.admin.api.MessageTrack; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping("/messageTrace") +@Permission +public class MessageTraceController { + + @Resource + private MessageService messageService; + + @Resource + private MessageTraceService messageTraceService; + + @RequestMapping(value = "/viewMessage.query", method = RequestMethod.GET) + @ResponseBody + public Object viewMessage(@RequestParam(required = false) String topic, @RequestParam String msgId) { + Map messageViewMap = Maps.newHashMap(); + Pair> messageViewListPair = messageService.viewMessage(topic, msgId); + messageViewMap.put("messageView", messageViewListPair.getObject1()); + return messageViewMap; + } + + @RequestMapping(value = "/viewMessageTraceDetail.query", method = RequestMethod.GET) + @ResponseBody + public Object viewTraceMessages(@RequestParam String msgId) { + return messageTraceService.queryMessageTraceKey(msgId); + } + + @RequestMapping(value = "/viewMessageTraceGraph.query", method = RequestMethod.GET) + @ResponseBody + public MessageTraceGraph viewMessageTraceGraph(@RequestParam String msgId, + @RequestParam(required = false) String traceTopic) { + return messageTraceService.queryMessageTraceGraph(msgId, traceTopic); + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/MonitorController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/MonitorController.java new file mode 100644 index 0000000..eb1e979 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/controller/MonitorController.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.controller; + +import javax.annotation.Resource; +import org.apache.rocketmq.dashboard.model.ConsumerMonitorConfig; +import org.apache.rocketmq.dashboard.permisssion.Permission; +import org.apache.rocketmq.dashboard.service.MonitorService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping("/monitor") +@Permission +public class MonitorController { + + private Logger logger = LoggerFactory.getLogger(MonitorController.class); + @Resource + private MonitorService monitorService; + + @RequestMapping(value = "/createOrUpdateConsumerMonitor.do", method = {RequestMethod.POST}) + @ResponseBody + public Object createOrUpdateConsumerMonitor(@RequestParam String consumeGroupName, @RequestParam int minCount, + @RequestParam int maxDiffTotal) { + return monitorService.createOrUpdateConsumerMonitor(consumeGroupName, new ConsumerMonitorConfig(minCount, maxDiffTotal)); + } + + @RequestMapping(value = "/consumerMonitorConfig.query", method = {RequestMethod.GET}) + @ResponseBody + public Object consumerMonitorConfig() { + return monitorService.queryConsumerMonitorConfig(); + } + + @RequestMapping(value = "/consumerMonitorConfigByGroupName.query", method = {RequestMethod.GET}) + @ResponseBody + public Object consumerMonitorConfigByGroupName(@RequestParam String consumeGroupName) { + return monitorService.queryConsumerMonitorConfigByGroupName(consumeGroupName); + } + + @RequestMapping(value = "/deleteConsumerMonitor.do", method = {RequestMethod.POST}) + @ResponseBody + public Object deleteConsumerMonitor(@RequestParam String consumeGroupName) { + return monitorService.deleteConsumerMonitor(consumeGroupName); + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/NamesvrController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/NamesvrController.java new file mode 100644 index 0000000..c3f106b --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/controller/NamesvrController.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.controller; + +import javax.annotation.Resource; +import org.apache.rocketmq.dashboard.aspect.admin.annotation.OriginalControllerReturnValue; +import org.apache.rocketmq.dashboard.permisssion.Permission; +import org.apache.rocketmq.dashboard.service.OpsService; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping("/rocketmq") +@Permission +public class NamesvrController { + @Resource + private OpsService opsService; + + @RequestMapping(value = "/nsaddr", method = RequestMethod.GET) + @ResponseBody + @OriginalControllerReturnValue + public Object nsaddr() { + return opsService.getNameSvrList(); + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/OpsController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/OpsController.java new file mode 100644 index 0000000..bd365cb --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/controller/OpsController.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.controller; + +import javax.annotation.Resource; +import org.apache.rocketmq.dashboard.permisssion.Permission; +import org.apache.rocketmq.dashboard.service.OpsService; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping("/ops") +@Permission +public class OpsController { + + @Resource + private OpsService opsService; + + @RequestMapping(value = "/homePage.query", method = RequestMethod.GET) + @ResponseBody + public Object homePage() { + return opsService.homePageInfo(); + } + + @RequestMapping(value = "/updateNameSvrAddr.do", method = RequestMethod.POST) + @ResponseBody + public Object updateNameSvrAddr(@RequestParam String nameSvrAddrList) { + opsService.updateNameSvrAddrList(nameSvrAddrList); + return true; + } + + @RequestMapping(value = "/updateIsVIPChannel.do", method = RequestMethod.POST) + @ResponseBody + public Object updateIsVIPChannel(@RequestParam String useVIPChannel) { + opsService.updateIsVIPChannel(useVIPChannel); + return true; + } + + + @RequestMapping(value = "/rocketMqStatus.query", method = RequestMethod.GET) + @ResponseBody + public Object clusterStatus() { + return opsService.rocketMqStatusCheck(); + } + + + @RequestMapping(value = "/updateUseTLS.do", method = RequestMethod.POST) + @ResponseBody + public Object updateUseTLS(@RequestParam String useTLS) { + opsService.updateUseTLS(Boolean.parseBoolean(useTLS)); + return true; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/ProducerController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/ProducerController.java new file mode 100644 index 0000000..9c1d79d --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/controller/ProducerController.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.controller; + +import javax.annotation.Resource; +import org.apache.rocketmq.common.protocol.body.ProducerConnection; +import org.apache.rocketmq.dashboard.model.ConnectionInfo; +import org.apache.rocketmq.dashboard.permisssion.Permission; +import org.apache.rocketmq.dashboard.service.ProducerService; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping("/producer") +@Permission +public class ProducerController { + + @Resource + private ProducerService producerService; + + @RequestMapping(value = "/producerConnection.query", method = {RequestMethod.GET}) + @ResponseBody + public Object producerConnection(@RequestParam String producerGroup, @RequestParam String topic) { + ProducerConnection producerConnection = producerService.getProducerConnection(producerGroup, topic); + producerConnection.setConnectionSet(ConnectionInfo.buildConnectionInfoHashSet(producerConnection.getConnectionSet())); + return producerConnection; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/TestController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/TestController.java new file mode 100644 index 0000000..e50c46e --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/controller/TestController.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.controller; + +import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; +import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; +import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; +import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.common.consumer.ConsumeFromWhere; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.remoting.exception.RemotingException; +import java.util.List; +import javax.annotation.Resource; +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.dashboard.util.JsonUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping("/test") +public class TestController { + private Logger logger = LoggerFactory.getLogger(TestController.class); + private String testTopic = "TestTopic"; + + @Resource + private RMQConfigure rMQConfigure; + + @RequestMapping(value = "/runTask.do", method = RequestMethod.GET) + @ResponseBody + public Object list() throws MQClientException, RemotingException, InterruptedException { + DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(testTopic + "Group"); + consumer.setNamesrvAddr(rMQConfigure.getNamesrvAddr()); + consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); + consumer.subscribe(testTopic, "*"); + consumer.registerMessageListener(new MessageListenerConcurrently() { + + @Override + public ConsumeConcurrentlyStatus consumeMessage(List msgs, + ConsumeConcurrentlyContext context) { + logger.info("receiveMessage msgSize={}", msgs.size()); + return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; + } + }); + consumer.start(); + final DefaultMQProducer producer = new DefaultMQProducer(testTopic + "Group"); + producer.setInstanceName(String.valueOf(System.currentTimeMillis())); + producer.setNamesrvAddr(rMQConfigure.getNamesrvAddr()); + producer.start(); + + new Thread(new Runnable() { + + @Override public void run() { + + int i = 0; + while (true) { + try { + Message msg = new Message(testTopic, + "TagA" + i, + "KEYS" + i, + ("Hello RocketMQ " + i).getBytes() + ); + Thread.sleep(1000L); + SendResult sendResult = producer.send(msg); + logger.info("sendMessage={}", JsonUtil.obj2String(sendResult)); + } + catch (Exception e) { + e.printStackTrace(); + try { + Thread.sleep(1000); + } + catch (Exception ignore) { + } + } + } + } + }).start(); + return true; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/controller/TopicController.java b/src/main/java/org/apache/rocketmq/dashboard/controller/TopicController.java new file mode 100644 index 0000000..ebed69e --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/controller/TopicController.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.controller; + +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.dashboard.permisssion.Permission; +import org.apache.rocketmq.remoting.exception.RemotingException; +import org.apache.rocketmq.dashboard.model.request.SendTopicMessageRequest; +import org.apache.rocketmq.dashboard.model.request.TopicConfigInfo; +import org.apache.rocketmq.dashboard.service.ConsumerService; +import org.apache.rocketmq.dashboard.service.TopicService; +import org.apache.rocketmq.dashboard.util.JsonUtil; +import com.google.common.base.Preconditions; +import org.apache.commons.collections.CollectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import javax.annotation.Resource; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping("/topic") +@Permission +public class TopicController { + private Logger logger = LoggerFactory.getLogger(TopicController.class); + + @Resource + private TopicService topicService; + + @Resource + private ConsumerService consumerService; + + @RequestMapping(value = "/list.query", method = RequestMethod.GET) + @ResponseBody + public Object list(@RequestParam(value = "skipSysProcess", required = false) boolean skipSysProcess, + @RequestParam(value = "skipRetryAndDlq", required = false) boolean skipRetryAndDlq) { + return topicService.fetchAllTopicList(skipSysProcess, skipRetryAndDlq); + } + + @RequestMapping(value = "/stats.query", method = RequestMethod.GET) + @ResponseBody + public Object stats(@RequestParam String topic) { + return topicService.stats(topic); + } + + @RequestMapping(value = "/route.query", method = RequestMethod.GET) + @ResponseBody + public Object route(@RequestParam String topic) { + return topicService.route(topic); + } + + + @RequestMapping(value = "/createOrUpdate.do", method = { RequestMethod.POST}) + @ResponseBody + public Object topicCreateOrUpdateRequest(@RequestBody TopicConfigInfo topicCreateOrUpdateRequest) { + Preconditions.checkArgument(CollectionUtils.isNotEmpty(topicCreateOrUpdateRequest.getBrokerNameList()) || CollectionUtils.isNotEmpty(topicCreateOrUpdateRequest.getClusterNameList()), + "clusterName or brokerName can not be all blank"); + logger.info("op=look topicCreateOrUpdateRequest={}", JsonUtil.obj2String(topicCreateOrUpdateRequest)); + topicService.createOrUpdate(topicCreateOrUpdateRequest); + return true; + } + + @RequestMapping(value = "/queryConsumerByTopic.query") + @ResponseBody + public Object queryConsumerByTopic(@RequestParam String topic) { + return consumerService.queryConsumeStatsListByTopicName(topic); + } + + @RequestMapping(value = "/queryTopicConsumerInfo.query") + @ResponseBody + public Object queryTopicConsumerInfo(@RequestParam String topic) { + return topicService.queryTopicConsumerInfo(topic); + } + + @RequestMapping(value = "/examineTopicConfig.query") + @ResponseBody + public Object examineTopicConfig(@RequestParam String topic, + @RequestParam(required = false) String brokerName) throws RemotingException, MQClientException, InterruptedException { + return topicService.examineTopicConfig(topic); + } + + @RequestMapping(value = "/sendTopicMessage.do", method = {RequestMethod.POST}) + @ResponseBody + public Object sendTopicMessage( + @RequestBody SendTopicMessageRequest sendTopicMessageRequest) throws RemotingException, MQClientException, InterruptedException { + return topicService.sendTopicMessageRequest(sendTopicMessageRequest); + } + + @RequestMapping(value = "/deleteTopic.do", method = {RequestMethod.POST}) + @ResponseBody + public Object delete(@RequestParam(required = false) String clusterName, @RequestParam String topic) { + return topicService.deleteTopic(topic, clusterName); + } + + @RequestMapping(value = "/deleteTopicByBroker.do", method = {RequestMethod.POST}) + @ResponseBody + public Object deleteTopicByBroker(@RequestParam String brokerName, @RequestParam String topic) { + return topicService.deleteTopicInBroker(brokerName, topic); + } + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/exception/ServiceException.java b/src/main/java/org/apache/rocketmq/dashboard/exception/ServiceException.java new file mode 100644 index 0000000..7b5ce33 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/exception/ServiceException.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.exception; + +public class ServiceException extends RuntimeException { + private static final long serialVersionUID = 9213584003139969215L; + private int code; + + public ServiceException(int code, String message) { + super(message); + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/filter/HttpBasicAuthorizedFilter.java b/src/main/java/org/apache/rocketmq/dashboard/filter/HttpBasicAuthorizedFilter.java new file mode 100644 index 0000000..10c3671 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/filter/HttpBasicAuthorizedFilter.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.filter; + +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletResponse; + + +@WebFilter(urlPatterns = "/*", filterName = "httpBasicAuthorizedFilter") +public class HttpBasicAuthorizedFilter implements Filter { + + @Override + public void init(FilterConfig config) throws ServletException { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + HttpServletResponse httpResponse = (HttpServletResponse) response; + httpResponse.setCharacterEncoding("UTF-8"); + httpResponse.setContentType("application/json; charset=utf-8"); + httpResponse.setHeader("WWW-Authenticate", "Basic realm=\"rocketmq\""); + chain.doFilter(request, response); + } + + @Override + public void destroy() { + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/interceptor/AuthInterceptor.java b/src/main/java/org/apache/rocketmq/dashboard/interceptor/AuthInterceptor.java new file mode 100644 index 0000000..88b742b --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/interceptor/AuthInterceptor.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.interceptor; + +import org.apache.rocketmq.dashboard.service.LoginService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Component +public class AuthInterceptor extends HandlerInterceptorAdapter { + @Autowired + private LoginService loginService; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + return loginService.login(request, response); + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/ConnectionInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/ConnectionInfo.java new file mode 100644 index 0000000..9070542 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/ConnectionInfo.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model; + +import com.google.common.collect.Sets; +import java.util.Collection; +import java.util.HashSet; +import org.apache.rocketmq.common.MQVersion; +import org.apache.rocketmq.common.protocol.body.Connection; + +public class ConnectionInfo extends Connection { + private String versionDesc; + + public static ConnectionInfo buildConnectionInfo(Connection connection) { + ConnectionInfo connectionInfo = new ConnectionInfo(); + connectionInfo.setClientId(connection.getClientId()); + connectionInfo.setClientAddr(connection.getClientAddr()); + connectionInfo.setLanguage(connection.getLanguage()); + connectionInfo.setVersion(connection.getVersion()); + connectionInfo.setVersionDesc(MQVersion.getVersionDesc(connection.getVersion())); + return connectionInfo; + } + + public static HashSet buildConnectionInfoHashSet(Collection connectionList) { + HashSet connectionHashSet = Sets.newHashSet(); + for (Connection connection : connectionList) { + connectionHashSet.add(buildConnectionInfo(connection)); + } + return connectionHashSet; + } + + public String getVersionDesc() { + return versionDesc; + } + + public void setVersionDesc(String versionDesc) { + this.versionDesc = versionDesc; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/ConsumerGroupRollBackStat.java b/src/main/java/org/apache/rocketmq/dashboard/model/ConsumerGroupRollBackStat.java new file mode 100644 index 0000000..f7e4a4e --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/ConsumerGroupRollBackStat.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model; + +import org.apache.rocketmq.common.admin.RollbackStats; +import com.google.common.collect.Lists; + +import java.util.List; + +public class ConsumerGroupRollBackStat { + private boolean status; + private String errMsg; + private List rollbackStatsList = Lists.newArrayList(); + + public ConsumerGroupRollBackStat(boolean status) { + this.status = status; + } + + public ConsumerGroupRollBackStat(boolean status, String errMsg) { + this.status = status; + this.errMsg = errMsg; + } + + public String getErrMsg() { + return errMsg; + } + + public void setErrMsg(String errMsg) { + this.errMsg = errMsg; + } + + public boolean isStatus() { + return status; + } + + public void setStatus(boolean status) { + this.status = status; + } + + public List getRollbackStatsList() { + return rollbackStatsList; + } + + public void setRollbackStatsList(List rollbackStatsList) { + this.rollbackStatsList = rollbackStatsList; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/ConsumerMonitorConfig.java b/src/main/java/org/apache/rocketmq/dashboard/model/ConsumerMonitorConfig.java new file mode 100644 index 0000000..b44adc7 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/ConsumerMonitorConfig.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model; + +public class ConsumerMonitorConfig { + private int minCount; + private int maxDiffTotal; + + public ConsumerMonitorConfig() { + } + + public ConsumerMonitorConfig(int minCount, int maxDiffTotal) { + this.minCount = minCount; + this.maxDiffTotal = maxDiffTotal; + } + + public int getMinCount() { + return minCount; + } + + public void setMinCount(int minCount) { + this.minCount = minCount; + } + + public int getMaxDiffTotal() { + return maxDiffTotal; + } + + public void setMaxDiffTotal(int maxDiffTotal) { + this.maxDiffTotal = maxDiffTotal; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/GroupConsumeInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/GroupConsumeInfo.java new file mode 100644 index 0000000..6429ba7 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/GroupConsumeInfo.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model; + +import org.apache.rocketmq.common.protocol.heartbeat.ConsumeType; +import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; + +public class GroupConsumeInfo implements Comparable { + private String group; + private String version; + private int count; + private ConsumeType consumeType; + private MessageModel messageModel; + private int consumeTps; + private long diffTotal = -1; + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public ConsumeType getConsumeType() { + return consumeType; + } + + public void setConsumeType(ConsumeType consumeType) { + this.consumeType = consumeType; + } + + public MessageModel getMessageModel() { + return messageModel; + } + + public void setMessageModel(MessageModel messageModel) { + this.messageModel = messageModel; + } + + public long getDiffTotal() { + return diffTotal; + } + + public void setDiffTotal(long diffTotal) { + this.diffTotal = diffTotal; + } + + @Override + public int compareTo(GroupConsumeInfo o) { + if (this.count != o.count) { + return Integer.compare(o.count, this.count); + } + return Long.compare(o.diffTotal, this.diffTotal); + } + + public int getConsumeTps() { + return consumeTps; + } + + public void setConsumeTps(int consumeTps) { + this.consumeTps = consumeTps; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/LoginInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/LoginInfo.java new file mode 100644 index 0000000..bebf40c --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/LoginInfo.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.model; + +public class LoginInfo { + private boolean loginRequired; + private boolean logined; + + public boolean isLoginRequired() { + return loginRequired; + } + + public void setLoginRequired(boolean loginRequired) { + this.loginRequired = loginRequired; + } + + public boolean isLogined() { + return logined; + } + + public void setLogined(boolean logined) { + this.logined = logined; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/LoginResult.java b/src/main/java/org/apache/rocketmq/dashboard/model/LoginResult.java new file mode 100644 index 0000000..c1468b3 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/LoginResult.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class LoginResult { + + private String loginUserName; + + /** + * 0: normal 1: admin + */ + private int loginUserRole; + + private String contextPath; +} \ No newline at end of file diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/MessagePage.java b/src/main/java/org/apache/rocketmq/dashboard/model/MessagePage.java new file mode 100644 index 0000000..8d2ba61 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/MessagePage.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model; + + +import org.springframework.data.domain.Page; + +public class MessagePage { + private Page page; + private String taskId; + + public MessagePage(Page page, String taskId) { + this.page = page; + this.taskId = taskId; + } + + public Page getPage() { + return page; + } + + public void setPage(Page page) { + this.page = page; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + @Override + public String toString() { + return "MessagePage{" + + "page=" + page + + ", taskId='" + taskId + '\'' + + '}'; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/MessagePageTask.java b/src/main/java/org/apache/rocketmq/dashboard/model/MessagePageTask.java new file mode 100644 index 0000000..5cd079f --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/MessagePageTask.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model; + +import org.springframework.data.domain.Page; + +import java.util.List; + +public class MessagePageTask { + private Page page; + private List queueOffsetInfos; + + public MessagePageTask(Page page, List queueOffsetInfos) { + this.page = page; + this.queueOffsetInfos = queueOffsetInfos; + } + + public Page getPage() { + return page; + } + + public void setPage(Page page) { + this.page = page; + } + + public List getQueueOffsetInfos() { + return queueOffsetInfos; + } + + public void setQueueOffsetInfos(List queueOffsetInfos) { + this.queueOffsetInfos = queueOffsetInfos; + } + + @Override + public String toString() { + return "MessagePageTask{" + + "page=" + page + + ", queueOffsetInfos=" + queueOffsetInfos + + '}'; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/MessageQueryByPage.java b/src/main/java/org/apache/rocketmq/dashboard/model/MessageQueryByPage.java new file mode 100644 index 0000000..48e2420 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/MessageQueryByPage.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model; + + +import org.springframework.data.domain.PageRequest; + +public class MessageQueryByPage { + public static final int DEFAULT_PAGE = 0; + + public static final int MIN_PAGE_SIZE = 10; + + public static final int MAX_PAGE_SIZE = 100; + + /** + * current page num + */ + private int pageNum; + + private int pageSize; + + private String topic; + private long begin; + private long end; + + public MessageQueryByPage(int pageNum, int pageSize, String topic, long begin, long end) { + this.pageNum = pageNum; + this.pageSize = pageSize; + this.topic = topic; + this.begin = begin; + this.end = end; + } + + public void setPageNum(int pageNum) { + this.pageNum = pageNum; + } + + public void setPageSize(int pageSize) { + this.pageSize = pageSize; + } + + public String getTopic() { + return topic; + } + + public void setTopic(String topic) { + this.topic = topic; + } + + public long getBegin() { + return begin; + } + + public void setBegin(long begin) { + this.begin = begin; + } + + public long getEnd() { + return end; + } + + public void setEnd(long end) { + this.end = end; + } + + public int getPageNum() { + return pageNum <= 0 ? DEFAULT_PAGE : pageNum - 1; + } + + public int getPageSize() { + if (pageSize <= 1) { + return MIN_PAGE_SIZE; + } else if (pageSize > MAX_PAGE_SIZE) { + return MAX_PAGE_SIZE; + } + return this.pageSize; + } + + public PageRequest page() { + return PageRequest.of(this.getPageNum(), this.getPageSize()); + } + + @Override + public String toString() { + return "MessageQueryByPage{" + + "pageNum=" + pageNum + + ", pageSize=" + pageSize + + ", topic='" + topic + '\'' + + ", begin=" + begin + + ", end=" + end + + '}'; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/MessageTraceView.java b/src/main/java/org/apache/rocketmq/dashboard/model/MessageTraceView.java new file mode 100644 index 0000000..dc85508 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/MessageTraceView.java @@ -0,0 +1,237 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.model; + +import com.google.common.base.Charsets; +import org.apache.rocketmq.client.trace.TraceBean; +import org.apache.rocketmq.client.trace.TraceContext; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.dashboard.model.trace.MessageTraceStatusEnum; +import org.apache.rocketmq.dashboard.util.MsgTraceDecodeUtil; + +import java.util.ArrayList; +import java.util.List; + +public class MessageTraceView { + private String requestId; + private String msgId; + private String tags; + private String keys; + private String storeHost; + private String clientHost; + private int costTime; + private String msgType; + private String offSetMsgId; + private long timeStamp; + private String topic; + private String groupName; + private int retryTimes; + private String status; + private String transactionState; + private String transactionId; + private boolean fromTransactionCheck; + private String traceType; + + public MessageTraceView() { + } + + public static List decodeFromTraceTransData(String key, MessageExt messageExt) { + List messageTraceViewList = new ArrayList(); + String messageBody = new String(messageExt.getBody(), Charsets.UTF_8); + if (messageBody == null || messageBody.length() <= 0) { + return messageTraceViewList; + } + + List traceContextList = MsgTraceDecodeUtil.decoderFromTraceDataString(messageBody); + for (TraceContext context : traceContextList) { + MessageTraceView messageTraceView = new MessageTraceView(); + TraceBean traceBean = context.getTraceBeans().get(0); + if (!traceBean.getMsgId().equals(key)) { + continue; + } + messageTraceView.setCostTime(context.getCostTime()); + messageTraceView.setGroupName(context.getGroupName()); + if (context.isSuccess()) { + messageTraceView.setStatus(MessageTraceStatusEnum.SUCCESS.getStatus()); + } else { + messageTraceView.setStatus(MessageTraceStatusEnum.FAILED.getStatus()); + } + messageTraceView.setKeys(traceBean.getKeys()); + messageTraceView.setMsgId(traceBean.getMsgId()); + messageTraceView.setTags(traceBean.getTags()); + messageTraceView.setTopic(traceBean.getTopic()); + messageTraceView.setMsgType(traceBean.getMsgType() == null ? null : traceBean.getMsgType().name()); + messageTraceView.setOffSetMsgId(traceBean.getOffsetMsgId()); + messageTraceView.setTimeStamp(context.getTimeStamp()); + messageTraceView.setStoreHost(traceBean.getStoreHost()); + messageTraceView.setClientHost(messageExt.getBornHostString()); + messageTraceView.setRequestId(context.getRequestId()); + messageTraceView.setRetryTimes(traceBean.getRetryTimes()); + messageTraceView.setTransactionState(traceBean.getTransactionState() == null ? null : traceBean.getTransactionState().name()); + messageTraceView.setTransactionId(traceBean.getTransactionId()); + messageTraceView.setFromTransactionCheck(traceBean.isFromTransactionCheck()); + messageTraceView.setTraceType(context.getTraceType() == null ? null : context.getTraceType().name()); + messageTraceViewList.add(messageTraceView); + } + return messageTraceViewList; + } + + public String getMsgId() { + return msgId; + } + + public void setMsgId(String msgId) { + this.msgId = msgId; + } + + public String getTags() { + return tags; + } + + public void setTags(String tags) { + this.tags = tags; + } + + public String getKeys() { + return keys; + } + + public void setKeys(String keys) { + this.keys = keys; + } + + public String getStoreHost() { + return storeHost; + } + + public void setStoreHost(String storeHost) { + this.storeHost = storeHost; + } + + public int getCostTime() { + return costTime; + } + + public void setCostTime(int costTime) { + this.costTime = costTime; + } + + public String getMsgType() { + return msgType; + } + + public void setMsgType(String msgType) { + this.msgType = msgType; + } + + public String getOffSetMsgId() { + return offSetMsgId; + } + + public void setOffSetMsgId(String offSetMsgId) { + this.offSetMsgId = offSetMsgId; + } + + public long getTimeStamp() { + return timeStamp; + } + + public void setTimeStamp(long timeStamp) { + this.timeStamp = timeStamp; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public String getTopic() { + return topic; + } + + public void setTopic(String topic) { + this.topic = topic; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getClientHost() { + return clientHost; + } + + public void setClientHost(String clientHost) { + this.clientHost = clientHost; + } + + public String getRequestId() { + return requestId; + } + + public void setRequestId(String requestId) { + this.requestId = requestId; + } + + public int getRetryTimes() { + return retryTimes; + } + + public void setRetryTimes(int retryTimes) { + this.retryTimes = retryTimes; + } + + public String getTransactionState() { + return transactionState; + } + + public void setTransactionState(String transactionState) { + this.transactionState = transactionState; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public boolean isFromTransactionCheck() { + return fromTransactionCheck; + } + + public void setFromTransactionCheck(boolean fromTransactionCheck) { + this.fromTransactionCheck = fromTransactionCheck; + } + + public String getTraceType() { + return traceType; + } + + public void setTraceType(String traceType) { + this.traceType = traceType; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/MessageView.java b/src/main/java/org/apache/rocketmq/dashboard/model/MessageView.java new file mode 100644 index 0000000..13a9641 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/MessageView.java @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model; + +import org.apache.rocketmq.common.message.MessageExt; +import com.google.common.base.Charsets; +import org.springframework.beans.BeanUtils; + +import java.net.SocketAddress; +import java.util.Map; + +public class MessageView { + + /** from MessageExt **/ + private int queueId; + private int storeSize; + private long queueOffset; + private int sysFlag; + private long bornTimestamp; + private SocketAddress bornHost; + private long storeTimestamp; + private SocketAddress storeHost; + private String msgId; + private long commitLogOffset; + private int bodyCRC; + private int reconsumeTimes; + private long preparedTransactionOffset; + /**from MessageExt**/ + + /** from Message **/ + private String topic; + private int flag; + private Map properties; + private String messageBody; // body + + /** from Message **/ + + public static MessageView fromMessageExt(MessageExt messageExt) { + MessageView messageView = new MessageView(); + BeanUtils.copyProperties(messageExt, messageView); + if (messageExt.getBody() != null) { + messageView.setMessageBody(new String(messageExt.getBody(), Charsets.UTF_8)); + } + return messageView; + } + + public String getTopic() { + return topic; + } + + public void setTopic(String topic) { + this.topic = topic; + } + + public int getFlag() { + return flag; + } + + public void setFlag(int flag) { + this.flag = flag; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public int getQueueId() { + return queueId; + } + + public void setQueueId(int queueId) { + this.queueId = queueId; + } + + public int getStoreSize() { + return storeSize; + } + + public void setStoreSize(int storeSize) { + this.storeSize = storeSize; + } + + public long getQueueOffset() { + return queueOffset; + } + + public void setQueueOffset(long queueOffset) { + this.queueOffset = queueOffset; + } + + public int getSysFlag() { + return sysFlag; + } + + public void setSysFlag(int sysFlag) { + this.sysFlag = sysFlag; + } + + public long getBornTimestamp() { + return bornTimestamp; + } + + public void setBornTimestamp(long bornTimestamp) { + this.bornTimestamp = bornTimestamp; + } + + public SocketAddress getBornHost() { + return bornHost; + } + + public void setBornHost(SocketAddress bornHost) { + this.bornHost = bornHost; + } + + public long getStoreTimestamp() { + return storeTimestamp; + } + + public void setStoreTimestamp(long storeTimestamp) { + this.storeTimestamp = storeTimestamp; + } + + public SocketAddress getStoreHost() { + return storeHost; + } + + public void setStoreHost(SocketAddress storeHost) { + this.storeHost = storeHost; + } + + public String getMsgId() { + return msgId; + } + + public void setMsgId(String msgId) { + this.msgId = msgId; + } + + public long getCommitLogOffset() { + return commitLogOffset; + } + + public void setCommitLogOffset(long commitLogOffset) { + this.commitLogOffset = commitLogOffset; + } + + public int getBodyCRC() { + return bodyCRC; + } + + public void setBodyCRC(int bodyCRC) { + this.bodyCRC = bodyCRC; + } + + public int getReconsumeTimes() { + return reconsumeTimes; + } + + public void setReconsumeTimes(int reconsumeTimes) { + this.reconsumeTimes = reconsumeTimes; + } + + public long getPreparedTransactionOffset() { + return preparedTransactionOffset; + } + + public void setPreparedTransactionOffset(long preparedTransactionOffset) { + this.preparedTransactionOffset = preparedTransactionOffset; + } + + public String getMessageBody() { + return messageBody; + } + + public void setMessageBody(String messageBody) { + this.messageBody = messageBody; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/QueueOffsetInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/QueueOffsetInfo.java new file mode 100644 index 0000000..15dae34 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/QueueOffsetInfo.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model; + +import org.apache.rocketmq.common.message.MessageQueue; + +public class QueueOffsetInfo { + private Integer idx; + + private Long start; + private Long end; + + private Long startOffset; + private Long endOffset; + private MessageQueue messageQueues; + + public QueueOffsetInfo() { + } + + public QueueOffsetInfo(Integer idx, Long start, Long end, Long startOffset, Long endOffset, MessageQueue messageQueues) { + this.idx = idx; + this.start = start; + this.end = end; + this.startOffset = startOffset; + this.endOffset = endOffset; + this.messageQueues = messageQueues; + } + + public Integer getIdx() { + return idx; + } + + public void setIdx(Integer idx) { + this.idx = idx; + } + + public Long getStart() { + return start; + } + + public void setStart(Long start) { + this.start = start; + } + + public Long getEnd() { + return end; + } + + public void setEnd(Long end) { + this.end = end; + } + + public Long getStartOffset() { + return startOffset; + } + + public void setStartOffset(Long startOffset) { + this.startOffset = startOffset; + } + + public Long getEndOffset() { + return endOffset; + } + + public void setEndOffset(Long endOffset) { + this.endOffset = endOffset; + } + + public MessageQueue getMessageQueues() { + return messageQueues; + } + + public void setMessageQueues(MessageQueue messageQueues) { + this.messageQueues = messageQueues; + } + + public void incStartOffset() { + this.startOffset++; + this.endOffset++; + } + + public void incEndOffset() { + this.endOffset++; + } + + public void incStartOffset(long size) { + this.startOffset += size; + this.endOffset += size; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/QueueStatInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/QueueStatInfo.java new file mode 100644 index 0000000..38daddd --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/QueueStatInfo.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model; + +import org.apache.rocketmq.common.admin.OffsetWrapper; +import org.apache.rocketmq.common.message.MessageQueue; +import org.springframework.beans.BeanUtils; + +public class QueueStatInfo { + private String brokerName; + private int queueId; + private long brokerOffset; + private long consumerOffset; + private String clientInfo; + private long lastTimestamp; + + public static QueueStatInfo fromOffsetTableEntry(MessageQueue key, OffsetWrapper value) { + QueueStatInfo queueStatInfo = new QueueStatInfo(); + BeanUtils.copyProperties(key, queueStatInfo); + BeanUtils.copyProperties(value, queueStatInfo); + return queueStatInfo; + } + + public String getClientInfo() { + return clientInfo; + } + + public void setClientInfo(String clientInfo) { + this.clientInfo = clientInfo; + } + + public String getBrokerName() { + return brokerName; + } + + public void setBrokerName(String brokerName) { + this.brokerName = brokerName; + } + + public int getQueueId() { + return queueId; + } + + public void setQueueId(int queueId) { + this.queueId = queueId; + } + + public long getBrokerOffset() { + return brokerOffset; + } + + public void setBrokerOffset(long brokerOffset) { + this.brokerOffset = brokerOffset; + } + + public long getConsumerOffset() { + return consumerOffset; + } + + public void setConsumerOffset(long consumerOffset) { + this.consumerOffset = consumerOffset; + } + + public long getLastTimestamp() { + return lastTimestamp; + } + + public void setLastTimestamp(long lastTimestamp) { + this.lastTimestamp = lastTimestamp; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/TopicConsumerInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/TopicConsumerInfo.java new file mode 100644 index 0000000..0f97ba9 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/TopicConsumerInfo.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model; + +import com.google.common.collect.Lists; + +import java.util.List; + +public class TopicConsumerInfo { + private String topic; + private long diffTotal; + private long lastTimestamp; + private List queueStatInfoList = Lists.newArrayList(); + + public TopicConsumerInfo(String topic) { + this.topic = topic; + } + + public String getTopic() { + return topic; + } + + public void setTopic(String topic) { + this.topic = topic; + } + + public long getDiffTotal() { + return diffTotal; + } + + public void setDiffTotal(long diffTotal) { + this.diffTotal = diffTotal; + } + + public List getQueueStatInfoList() { + return queueStatInfoList; + } + + public long getLastTimestamp() { + return lastTimestamp; + } + + public void appendQueueStatInfo(QueueStatInfo queueStatInfo) { + queueStatInfoList.add(queueStatInfo); + diffTotal = diffTotal + (queueStatInfo.getBrokerOffset() - queueStatInfo.getConsumerOffset()); + lastTimestamp = Math.max(lastTimestamp, queueStatInfo.getLastTimestamp()); + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/User.java b/src/main/java/org/apache/rocketmq/dashboard/model/User.java new file mode 100644 index 0000000..e049297 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/User.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model; + +import org.hibernate.validator.constraints.Range; + +public class User { + public static final int ORDINARY = 0; + public static final int ADMIN = 1; + + private long id; + private String name; + private String password; + @Range(min = 0, max = 1) + private int type = 0; + + + public User(String name, String password, int type) { + this.name = name; + this.password = password; + this.type = type; + } + + public User cloneOne() { + return new User(this.name, this.password, this.type); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", name='" + name + '\'' + + ", password='" + password + '\'' + + ", type=" + type + + '}'; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/UserInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/UserInfo.java new file mode 100644 index 0000000..fc288af --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/UserInfo.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model; + +public class UserInfo { + public static final String USER_INFO = "userInfo"; + private User user; + private long loginTime; + private String ip; + private String sessionId; + + public long getLoginTime() { + return loginTime; + } + + public void setLoginTime(long loginTime) { + this.loginTime = loginTime; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public String getSessionId() { + return sessionId; + } + + public void setSessionId(String sessionId) { + this.sessionId = sessionId; + } + + @Override + public String toString() { + return "UserInfo{" + + "user=" + user + + ", loginTime=" + loginTime + + ", ip='" + ip + '\'' + + ", sessionId='" + sessionId + '\'' + + '}'; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/request/ConsumerConfigInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/request/ConsumerConfigInfo.java new file mode 100644 index 0000000..acebafc --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/request/ConsumerConfigInfo.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model.request; + +import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig; + +import java.util.List; + +public class ConsumerConfigInfo { + private List clusterNameList; + + private List brokerNameList; + private SubscriptionGroupConfig subscriptionGroupConfig; + + public ConsumerConfigInfo() { + } + + public ConsumerConfigInfo(List brokerNameList, SubscriptionGroupConfig subscriptionGroupConfig) { + this.brokerNameList = brokerNameList; + this.subscriptionGroupConfig = subscriptionGroupConfig; + } + + public List getClusterNameList() { + return clusterNameList; + } + + public void setClusterNameList(List clusterNameList) { + this.clusterNameList = clusterNameList; + } + + public List getBrokerNameList() { + return brokerNameList; + } + + public void setBrokerNameList(List brokerNameList) { + this.brokerNameList = brokerNameList; + } + + public SubscriptionGroupConfig getSubscriptionGroupConfig() { + return subscriptionGroupConfig; + } + + public void setSubscriptionGroupConfig(SubscriptionGroupConfig subscriptionGroupConfig) { + this.subscriptionGroupConfig = subscriptionGroupConfig; + } + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/request/DeleteSubGroupRequest.java b/src/main/java/org/apache/rocketmq/dashboard/model/request/DeleteSubGroupRequest.java new file mode 100644 index 0000000..87f3318 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/request/DeleteSubGroupRequest.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model.request; + +import java.util.List; + +public class DeleteSubGroupRequest { + private String groupName; + private List brokerNameList; + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public List getBrokerNameList() { + return brokerNameList; + } + + public void setBrokerNameList(List brokerNameList) { + this.brokerNameList = brokerNameList; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/request/MessageQuery.java b/src/main/java/org/apache/rocketmq/dashboard/model/request/MessageQuery.java new file mode 100644 index 0000000..86b6c9e --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/request/MessageQuery.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model.request; + +public class MessageQuery { + /** + * current page num + */ + private int pageNum; + + private int pageSize; + + private String topic; + + private String taskId; + + private long begin; + + private long end; + + public int getPageNum() { + return pageNum; + } + + public void setPageNum(int pageNum) { + this.pageNum = pageNum; + } + + public int getPageSize() { + return pageSize; + } + + public void setPageSize(int pageSize) { + this.pageSize = pageSize; + } + + public String getTopic() { + return topic; + } + + public void setTopic(String topic) { + this.topic = topic; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public long getBegin() { + return begin; + } + + public void setBegin(long begin) { + this.begin = begin; + } + + public long getEnd() { + return end; + } + + public void setEnd(long end) { + this.end = end; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/request/ResetOffsetRequest.java b/src/main/java/org/apache/rocketmq/dashboard/model/request/ResetOffsetRequest.java new file mode 100644 index 0000000..d3f6e5d --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/request/ResetOffsetRequest.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model.request; + +import java.util.List; + +public class ResetOffsetRequest { + private List consumerGroupList; + private String topic; + private long resetTime; + private boolean force; + + public List getConsumerGroupList() { + return consumerGroupList; + } + + public void setConsumerGroupList(List consumerGroupList) { + this.consumerGroupList = consumerGroupList; + } + + public String getTopic() { + return topic; + } + + public void setTopic(String topic) { + this.topic = topic; + } + + public long getResetTime() { + return resetTime; + } + + public void setResetTime(long resetTime) { + this.resetTime = resetTime; + } + + public boolean isForce() { + return force; + } + + public void setForce(boolean force) { + this.force = force; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/request/SendTopicMessageRequest.java b/src/main/java/org/apache/rocketmq/dashboard/model/request/SendTopicMessageRequest.java new file mode 100644 index 0000000..76fecee --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/request/SendTopicMessageRequest.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model.request; + +import lombok.Data; + +@Data +public class SendTopicMessageRequest { + private String topic; + private String key; + private String tag; + private String messageBody; + private boolean traceEnabled; +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/request/TopicConfigInfo.java b/src/main/java/org/apache/rocketmq/dashboard/model/request/TopicConfigInfo.java new file mode 100644 index 0000000..32572fe --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/request/TopicConfigInfo.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model.request; + +import com.google.common.base.Objects; + +import java.util.List; + +public class TopicConfigInfo { + + private List clusterNameList; + private List brokerNameList; + + /** topicConfig */ + private String topicName; + private int writeQueueNums; + private int readQueueNums; + private int perm; + private boolean order; + + public List getClusterNameList() { + return clusterNameList; + } + + public void setClusterNameList(List clusterNameList) { + this.clusterNameList = clusterNameList; + } + + /** topicConfig */ + + + + public List getBrokerNameList() { + return brokerNameList; + } + + public void setBrokerNameList(List brokerNameList) { + this.brokerNameList = brokerNameList; + } + + public String getTopicName() { + return topicName; + } + + public void setTopicName(String topicName) { + this.topicName = topicName; + } + + public int getWriteQueueNums() { + return writeQueueNums; + } + + public void setWriteQueueNums(int writeQueueNums) { + this.writeQueueNums = writeQueueNums; + } + + public int getReadQueueNums() { + return readQueueNums; + } + + public void setReadQueueNums(int readQueueNums) { + this.readQueueNums = readQueueNums; + } + + public int getPerm() { + return perm; + } + + public void setPerm(int perm) { + this.perm = perm; + } + + public boolean isOrder() { + return order; + } + + public void setOrder(boolean order) { + this.order = order; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + TopicConfigInfo that = (TopicConfigInfo) o; + return writeQueueNums == that.writeQueueNums && + readQueueNums == that.readQueueNums && + perm == that.perm && + order == that.order && + Objects.equal(topicName, that.topicName); + } + + @Override + public int hashCode() { + return Objects.hashCode(topicName, writeQueueNums, readQueueNums, perm, order); + } + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/trace/MessageTraceGraph.java b/src/main/java/org/apache/rocketmq/dashboard/model/trace/MessageTraceGraph.java new file mode 100644 index 0000000..527399e --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/trace/MessageTraceGraph.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.model.trace; + +import lombok.Data; +import org.apache.rocketmq.dashboard.model.MessageTraceView; + +import java.util.List; + +@Data +public class MessageTraceGraph { + private ProducerNode producerNode; + private List subscriptionNodeList; + private List messageTraceViews; + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/trace/MessageTraceStatusEnum.java b/src/main/java/org/apache/rocketmq/dashboard/model/trace/MessageTraceStatusEnum.java new file mode 100644 index 0000000..a9aead9 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/trace/MessageTraceStatusEnum.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.model.trace; + +import lombok.Getter; + +@Getter +public enum MessageTraceStatusEnum { + SUCCESS("success"), + FAILED("failed"), + UNKNOWN("unknown"); + private final String status; + + MessageTraceStatusEnum(String status) { + this.status = status; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/trace/ProducerNode.java b/src/main/java/org/apache/rocketmq/dashboard/model/trace/ProducerNode.java new file mode 100644 index 0000000..c0a5c61 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/trace/ProducerNode.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model.trace; + +import lombok.Data; + +import java.util.List; + +@Data +public class ProducerNode { + private String msgId; + private String tags; + private String keys; + private String offSetMsgId; + private String topic; + private String groupName; + private TraceNode traceNode; + private List transactionNodeList; +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/trace/SubscriptionNode.java b/src/main/java/org/apache/rocketmq/dashboard/model/trace/SubscriptionNode.java new file mode 100644 index 0000000..8136933 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/trace/SubscriptionNode.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.model.trace; + +import lombok.Data; + +import java.util.List; + +@Data +public class SubscriptionNode { + private String subscriptionGroup; + private List consumeNodeList; +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/trace/TraceNode.java b/src/main/java/org/apache/rocketmq/dashboard/model/trace/TraceNode.java new file mode 100644 index 0000000..3d3e419 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/model/trace/TraceNode.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.model.trace; + +import lombok.Data; + +@Data +public class TraceNode { + private String requestId; + private String storeHost; + private String clientHost; + private int costTime; + private long beginTimestamp; + private long endTimestamp; + private int retryTimes; + private String status; + private String transactionState; + private String transactionId; + private boolean fromTransactionCheck; + private String msgType; +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/permisssion/Permission.java b/src/main/java/org/apache/rocketmq/dashboard/permisssion/Permission.java new file mode 100644 index 0000000..912e4e8 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/permisssion/Permission.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.permisssion; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Permission { +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/permisssion/PermissionAspect.java b/src/main/java/org/apache/rocketmq/dashboard/permisssion/PermissionAspect.java new file mode 100644 index 0000000..fcbfea2 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/permisssion/PermissionAspect.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.permisssion; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.dashboard.exception.ServiceException; +import org.apache.rocketmq.dashboard.model.UserInfo; +import org.apache.rocketmq.dashboard.service.PermissionService; +import org.apache.rocketmq.dashboard.util.WebUtil; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +@Aspect +@Component +public class PermissionAspect { + + @Resource + private RMQConfigure configure; + + @Resource + private PermissionService permissionService; + + /** + * @Permission can be applied to the Controller class to implement Permission verification on all methods in the class + * can also be applied to methods in a class for fine control + */ + @Pointcut("@annotation(org.apache.rocketmq.dashboard.permisssion.Permission) || @within(org.apache.rocketmq.dashboard.permisssion.Permission)") + private void permission() { + + } + + @Around("permission()") + public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable { + if (configure.isLoginRequired()) { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + String url = request.getRequestURI(); + UserInfo userInfo = (UserInfo) request.getSession().getAttribute(WebUtil.USER_INFO); + if (userInfo == null || userInfo.getUser() == null) { + throw new ServiceException(-1, "user not login"); + } + boolean checkResult = permissionService.checkUrlAvailable(userInfo, url); + if (!checkResult) { + throw new ServiceException(-1, "no permission"); + } + } + return joinPoint.proceed(); + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/permisssion/UserRoleEnum.java b/src/main/java/org/apache/rocketmq/dashboard/permisssion/UserRoleEnum.java new file mode 100644 index 0000000..f430a61 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/permisssion/UserRoleEnum.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.permisssion; + +public enum UserRoleEnum { + ADMIN(1, "admin"), + ORDINARY(0, "ordinary"); + + private int roleType; + private String roleName; + + UserRoleEnum(int roleType, String roleName) { + this.roleType = roleType; + this.roleName = roleName; + } + + public int getRoleType() { + return roleType; + } + + public String getRoleName() { + return roleName; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/AbstractCommonService.java b/src/main/java/org/apache/rocketmq/dashboard/service/AbstractCommonService.java new file mode 100644 index 0000000..1f85796 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/AbstractCommonService.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service; + +import org.apache.rocketmq.tools.admin.MQAdminExt; +import com.google.common.base.Throwables; +import com.google.common.collect.Sets; +import java.util.HashMap; +import java.util.List; +import java.util.Set; +import javax.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; + +public abstract class AbstractCommonService { + @Resource + protected MQAdminExt mqAdminExt; + protected final Set changeToBrokerNameSet(HashMap> clusterAddrTable, + List clusterNameList, List brokerNameList) { + Set finalBrokerNameList = Sets.newHashSet(); + if (CollectionUtils.isNotEmpty(clusterNameList)) { + try { + for (String clusterName : clusterNameList) { + finalBrokerNameList.addAll(clusterAddrTable.get(clusterName)); + } + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } + if (CollectionUtils.isNotEmpty(brokerNameList)) { + finalBrokerNameList.addAll(brokerNameList); + } + return finalBrokerNameList; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/ClusterService.java b/src/main/java/org/apache/rocketmq/dashboard/service/ClusterService.java new file mode 100644 index 0000000..ba2a75e --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/ClusterService.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service; + +import java.util.Map; +import java.util.Properties; + +public interface ClusterService { + Map list(); + + Properties getBrokerConfig(String brokerAddr); +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/ConfigService.java b/src/main/java/org/apache/rocketmq/dashboard/service/ConfigService.java new file mode 100644 index 0000000..053d3c9 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/ConfigService.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service; + +import java.util.Map; + +public interface ConfigService { + + Map queryConfigMap(); + + boolean createConfig(String name, String value); + + boolean updateConfig(String name, String value); + + boolean deleteConfig(String name, String value); + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/ConsumerService.java b/src/main/java/org/apache/rocketmq/dashboard/service/ConsumerService.java new file mode 100644 index 0000000..2c18f98 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/ConsumerService.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service; + +import org.apache.rocketmq.common.protocol.body.ConsumerConnection; +import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo; +import org.apache.rocketmq.dashboard.model.ConsumerGroupRollBackStat; +import org.apache.rocketmq.dashboard.model.GroupConsumeInfo; +import org.apache.rocketmq.dashboard.model.TopicConsumerInfo; +import org.apache.rocketmq.dashboard.model.request.ConsumerConfigInfo; +import org.apache.rocketmq.dashboard.model.request.DeleteSubGroupRequest; +import org.apache.rocketmq.dashboard.model.request.ResetOffsetRequest; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface ConsumerService { + List queryGroupList(); + + GroupConsumeInfo queryGroup(String consumerGroup); + + + List queryConsumeStatsListByGroupName(String groupName); + + List queryConsumeStatsList(String topic, String groupName); + + Map queryConsumeStatsListByTopicName(String topic); + + Map resetOffset(ResetOffsetRequest resetOffsetRequest); + + List examineSubscriptionGroupConfig(String group); + + boolean deleteSubGroup(DeleteSubGroupRequest deleteSubGroupRequest); + + boolean createAndUpdateSubscriptionGroupConfig(ConsumerConfigInfo consumerConfigInfo); + + Set fetchBrokerNameSetBySubscriptionGroup(String group); + + ConsumerConnection getConsumerConnection(String consumerGroup); + + ConsumerRunningInfo getConsumerRunningInfo(String consumerGroup, String clientId, boolean jstack); +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/DashboardCollectService.java b/src/main/java/org/apache/rocketmq/dashboard/service/DashboardCollectService.java new file mode 100644 index 0000000..fdee259 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/DashboardCollectService.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service; + +import com.google.common.cache.LoadingCache; +import java.io.File; +import java.util.List; +import java.util.Map; + +public interface DashboardCollectService { + // todo just move the task to org.apache.rocketmq.dashboard.task.DashboardCollectTask + // the code can be reconstruct + LoadingCache> getBrokerMap(); + + LoadingCache> getTopicMap(); + + Map> jsonDataFile2map(File file); + + Map> getBrokerCache(String date); + + Map> getTopicCache(String date); +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/DashboardService.java b/src/main/java/org/apache/rocketmq/dashboard/service/DashboardService.java new file mode 100644 index 0000000..3dc5041 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/DashboardService.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service; + +import java.util.List; +import java.util.Map; + +public interface DashboardService { + /** + * @param date format yyyy-MM-dd + */ + Map> queryBrokerData(String date); + + /** + * @param date format yyyy-MM-dd + */ + Map> queryTopicData(String date); + + /** + * @param date format yyyy-MM-dd + * @param topicName + */ + List queryTopicData(String date, String topicName); + + List queryTopicCurrentData(); + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/LoginService.java b/src/main/java/org/apache/rocketmq/dashboard/service/LoginService.java new file mode 100644 index 0000000..10e5a7f --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/LoginService.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public interface LoginService { + boolean login(HttpServletRequest request, HttpServletResponse response); +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/MessageService.java b/src/main/java/org/apache/rocketmq/dashboard/service/MessageService.java new file mode 100644 index 0000000..802ca45 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/MessageService.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service; + +import org.apache.rocketmq.common.Pair; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult; +import org.apache.rocketmq.dashboard.model.MessagePage; +import org.apache.rocketmq.dashboard.model.request.MessageQuery; +import org.apache.rocketmq.tools.admin.api.MessageTrack; +import org.apache.rocketmq.dashboard.model.MessageView; + +import java.util.List; + +public interface MessageService { + /** + * @param subject + * @param msgId + */ + Pair> viewMessage(String subject, final String msgId); + + List queryMessageByTopicAndKey(final String topic, final String key); + + /** + * @param topic + * @param begin + * @param end + * org.apache.rocketmq.tools.command.message.PrintMessageSubCommand + */ + List queryMessageByTopic(final String topic, final long begin, + final long end); + + List messageTrackDetail(MessageExt msg); + + ConsumeMessageDirectlyResult consumeMessageDirectly(String topic, String msgId, String consumerGroup, + String clientId); + + + MessagePage queryMessageByPage(MessageQuery query); + + + + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/MessageTraceService.java b/src/main/java/org/apache/rocketmq/dashboard/service/MessageTraceService.java new file mode 100644 index 0000000..797fc40 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/MessageTraceService.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service; + +import java.util.List; +import org.apache.rocketmq.dashboard.model.MessageTraceView; +import org.apache.rocketmq.dashboard.model.trace.MessageTraceGraph; + +public interface MessageTraceService { + + List queryMessageTraceKey(final String key); + + List queryMessageTraceByTopicAndKey(final String topic, final String key); + + MessageTraceGraph queryMessageTraceGraph(final String key, final String traceTopic); +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/MonitorService.java b/src/main/java/org/apache/rocketmq/dashboard/service/MonitorService.java new file mode 100644 index 0000000..526bb5c --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/MonitorService.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service; + +import java.util.Map; +import org.apache.rocketmq.dashboard.model.ConsumerMonitorConfig; + +public interface MonitorService { + boolean createOrUpdateConsumerMonitor(String name, ConsumerMonitorConfig config); + + Map queryConsumerMonitorConfig(); + + ConsumerMonitorConfig queryConsumerMonitorConfigByGroupName(String consumeGroupName); + + boolean deleteConsumerMonitor(String consumeGroupName); +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/OpsService.java b/src/main/java/org/apache/rocketmq/dashboard/service/OpsService.java new file mode 100644 index 0000000..4394f44 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/OpsService.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service; + +import java.util.Map; +import org.apache.rocketmq.dashboard.service.checker.CheckerType; + +public interface OpsService { + Map homePageInfo(); + + void updateNameSvrAddrList(String nameSvrAddrList); + + String getNameSvrList(); + + Map rocketMqStatusCheck(); + + boolean updateIsVIPChannel(String useVIPChannel); + + boolean updateUseTLS(boolean useTLS); +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/PermissionService.java b/src/main/java/org/apache/rocketmq/dashboard/service/PermissionService.java new file mode 100644 index 0000000..86729de --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/PermissionService.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service; + +import org.apache.rocketmq.dashboard.model.UserInfo; + +public interface PermissionService { + + boolean checkUrlAvailable(UserInfo userInfo, String url); +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/ProducerService.java b/src/main/java/org/apache/rocketmq/dashboard/service/ProducerService.java new file mode 100644 index 0000000..cd9f582 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/ProducerService.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service; + +import org.apache.rocketmq.common.protocol.body.ProducerConnection; + +public interface ProducerService { + ProducerConnection getProducerConnection(String producerGroup, String topic); +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/TopicService.java b/src/main/java/org/apache/rocketmq/dashboard/service/TopicService.java new file mode 100644 index 0000000..57f0dea --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/TopicService.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service; + +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.common.TopicConfig; +import org.apache.rocketmq.common.admin.TopicStatsTable; +import org.apache.rocketmq.common.protocol.body.GroupList; +import org.apache.rocketmq.common.protocol.body.TopicList; +import org.apache.rocketmq.common.protocol.route.TopicRouteData; +import org.apache.rocketmq.dashboard.model.request.SendTopicMessageRequest; +import org.apache.rocketmq.dashboard.model.request.TopicConfigInfo; + +import java.util.List; + +public interface TopicService { + TopicList fetchAllTopicList(boolean skipSysProcess, boolean skipRetryAndDlq); + + TopicStatsTable stats(String topic); + + TopicRouteData route(String topic); + + GroupList queryTopicConsumerInfo(String topic); + + void createOrUpdate(TopicConfigInfo topicCreateOrUpdateRequest); + + TopicConfig examineTopicConfig(String topic, String brokerName); + + List examineTopicConfig(String topic); + + boolean deleteTopic(String topic, String clusterName); + + boolean deleteTopic(String topic); + + boolean deleteTopicInBroker(String brokerName, String topic); + + SendResult sendTopicMessageRequest(SendTopicMessageRequest sendTopicMessageRequest); + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/UserService.java b/src/main/java/org/apache/rocketmq/dashboard/service/UserService.java new file mode 100644 index 0000000..37ab67f --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/UserService.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service; + +import org.apache.rocketmq.dashboard.model.User; + +public interface UserService { + User queryByName(String name); + + User queryByUsernameAndPassword(String username, String password); +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/checker/CheckerType.java b/src/main/java/org/apache/rocketmq/dashboard/service/checker/CheckerType.java new file mode 100644 index 0000000..a34f98b --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/checker/CheckerType.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service.checker; + +public enum CheckerType { + CLUSTER_HEALTH_CHECK, + TOPIC_ONLY_ONE_BROKER_CHECK + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/checker/RocketMqChecker.java b/src/main/java/org/apache/rocketmq/dashboard/service/checker/RocketMqChecker.java new file mode 100644 index 0000000..51b2dff --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/checker/RocketMqChecker.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service.checker; + +public interface RocketMqChecker { + public Object doCheck(); + + public CheckerType checkerType(); + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/checker/impl/ClusterHealthCheckerImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/checker/impl/ClusterHealthCheckerImpl.java new file mode 100644 index 0000000..601e31b --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/checker/impl/ClusterHealthCheckerImpl.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service.checker.impl; + +import org.apache.rocketmq.dashboard.service.checker.CheckerType; +import org.apache.rocketmq.dashboard.service.checker.RocketMqChecker; +import org.springframework.stereotype.Service; + +@Service +public class ClusterHealthCheckerImpl implements RocketMqChecker { + @Override + public Object doCheck() { + return null; + } + + @Override + public CheckerType checkerType() { + return CheckerType.CLUSTER_HEALTH_CHECK; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/checker/impl/TopicOnlyOneBrokerCheckerImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/checker/impl/TopicOnlyOneBrokerCheckerImpl.java new file mode 100644 index 0000000..f238b8b --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/checker/impl/TopicOnlyOneBrokerCheckerImpl.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service.checker.impl; + +import org.apache.rocketmq.dashboard.service.checker.CheckerType; +import org.apache.rocketmq.dashboard.service.checker.RocketMqChecker; +import org.springframework.stereotype.Service; + + +/** + * TODO + * here the checkers is not implemented yet + */ +@Service +public class TopicOnlyOneBrokerCheckerImpl implements RocketMqChecker { + @Override + public Object doCheck() { + return null; + } + + @Override + public CheckerType checkerType() { + return CheckerType.TOPIC_ONLY_ONE_BROKER_CHECK; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/client/MQAdminExtImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/client/MQAdminExtImpl.java new file mode 100644 index 0000000..81e1202 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/client/MQAdminExtImpl.java @@ -0,0 +1,551 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service.client; + +import com.google.common.base.Throwables; +import java.io.UnsupportedEncodingException; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import org.apache.rocketmq.client.QueryResult; +import org.apache.rocketmq.client.exception.MQBrokerException; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.impl.MQAdminImpl; +import org.apache.rocketmq.common.AclConfig; +import org.apache.rocketmq.common.PlainAccessConfig; +import org.apache.rocketmq.common.TopicConfig; +import org.apache.rocketmq.common.admin.ConsumeStats; +import org.apache.rocketmq.common.admin.RollbackStats; +import org.apache.rocketmq.common.admin.TopicStatsTable; +import org.apache.rocketmq.common.message.MessageClientIDSetter; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.common.message.MessageQueue; +import org.apache.rocketmq.common.protocol.RequestCode; +import org.apache.rocketmq.common.protocol.ResponseCode; +import org.apache.rocketmq.common.protocol.body.BrokerStatsData; +import org.apache.rocketmq.common.protocol.body.ClusterAclVersionInfo; +import org.apache.rocketmq.common.protocol.body.ClusterInfo; +import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult; +import org.apache.rocketmq.common.protocol.body.ConsumeStatsList; +import org.apache.rocketmq.common.protocol.body.ConsumerConnection; +import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo; +import org.apache.rocketmq.common.protocol.body.GroupList; +import org.apache.rocketmq.common.protocol.body.KVTable; +import org.apache.rocketmq.common.protocol.body.ProducerConnection; +import org.apache.rocketmq.common.protocol.body.QueryConsumeQueueResponseBody; +import org.apache.rocketmq.common.protocol.body.QueueTimeSpan; +import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper; +import org.apache.rocketmq.common.protocol.body.TopicConfigSerializeWrapper; +import org.apache.rocketmq.common.protocol.body.TopicList; +import org.apache.rocketmq.common.protocol.route.TopicRouteData; +import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig; +import org.apache.rocketmq.dashboard.util.JsonUtil; +import org.apache.rocketmq.remoting.RemotingClient; +import org.apache.rocketmq.remoting.exception.RemotingCommandException; +import org.apache.rocketmq.remoting.exception.RemotingConnectException; +import org.apache.rocketmq.remoting.exception.RemotingException; +import org.apache.rocketmq.remoting.exception.RemotingSendRequestException; +import org.apache.rocketmq.remoting.exception.RemotingTimeoutException; +import org.apache.rocketmq.remoting.protocol.RemotingCommand; +import org.apache.rocketmq.tools.admin.MQAdminExt; +import org.apache.rocketmq.tools.admin.api.MessageTrack; +import org.joor.Reflect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import static org.apache.rocketmq.remoting.protocol.RemotingSerializable.decode; + +@Service +public class MQAdminExtImpl implements MQAdminExt { + private Logger logger = LoggerFactory.getLogger(MQAdminExtImpl.class); + + public MQAdminExtImpl() { + } + + @Override + public void updateBrokerConfig(String brokerAddr, Properties properties) + throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, + UnsupportedEncodingException, InterruptedException, MQBrokerException { + MQAdminInstance.threadLocalMQAdminExt().updateBrokerConfig(brokerAddr, properties); + } + + @Override + public void createAndUpdateTopicConfig(String addr, TopicConfig config) + throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + MQAdminInstance.threadLocalMQAdminExt().createAndUpdateTopicConfig(addr, config); + } + + @Override public void createAndUpdatePlainAccessConfig(String addr, + PlainAccessConfig plainAccessConfig) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + + } + + @Override public void deletePlainAccessConfig(String addr, + String accessKey) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + + } + + @Override public void updateGlobalWhiteAddrConfig(String addr, + String globalWhiteAddrs) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + + } + + @Override public ClusterAclVersionInfo examineBrokerClusterAclVersionInfo( + String addr) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + return null; + } + + @Override public AclConfig examineBrokerClusterAclConfig( + String addr) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + return null; + } + + @Override + public void createAndUpdateSubscriptionGroupConfig(String addr, SubscriptionGroupConfig config) + throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + MQAdminInstance.threadLocalMQAdminExt().createAndUpdateSubscriptionGroupConfig(addr, config); + } + + @Override + public SubscriptionGroupConfig examineSubscriptionGroupConfig(String addr, String group) { + RemotingClient remotingClient = MQAdminInstance.threadLocalRemotingClient(); + RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ALL_SUBSCRIPTIONGROUP_CONFIG, null); + RemotingCommand response = null; + try { + response = remotingClient.invokeSync(addr, request, 3000); + } + catch (Exception err) { + throw Throwables.propagate(err); + } + assert response != null; + switch (response.getCode()) { + case ResponseCode.SUCCESS: { + SubscriptionGroupWrapper subscriptionGroupWrapper = decode(response.getBody(), SubscriptionGroupWrapper.class); + return subscriptionGroupWrapper.getSubscriptionGroupTable().get(group); + } + default: + throw Throwables.propagate(new MQBrokerException(response.getCode(), response.getRemark())); + } + } + + @Override + public TopicConfig examineTopicConfig(String addr, String topic) { + RemotingClient remotingClient = MQAdminInstance.threadLocalRemotingClient(); + RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ALL_TOPIC_CONFIG, null); + RemotingCommand response = null; + try { + response = remotingClient.invokeSync(addr, request, 3000); + } + catch (Exception err) { + throw Throwables.propagate(err); + } + switch (response.getCode()) { + case ResponseCode.SUCCESS: { + TopicConfigSerializeWrapper topicConfigSerializeWrapper = decode(response.getBody(), TopicConfigSerializeWrapper.class); + return topicConfigSerializeWrapper.getTopicConfigTable().get(topic); + } + default: + throw Throwables.propagate(new MQBrokerException(response.getCode(), response.getRemark())); + } + } + + @Override + public TopicStatsTable examineTopicStats(String topic) + throws RemotingException, MQClientException, InterruptedException, MQBrokerException { + return MQAdminInstance.threadLocalMQAdminExt().examineTopicStats(topic); + } + + @Override + public TopicList fetchAllTopicList() throws RemotingException, MQClientException, InterruptedException { + TopicList topicList = MQAdminInstance.threadLocalMQAdminExt().fetchAllTopicList(); + logger.debug("op=look={}", JsonUtil.obj2String(topicList.getTopicList())); + return topicList; + } + + @Override + public KVTable fetchBrokerRuntimeStats(String brokerAddr) + throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, + InterruptedException, MQBrokerException { + return MQAdminInstance.threadLocalMQAdminExt().fetchBrokerRuntimeStats(brokerAddr); + } + + @Override + public ConsumeStats examineConsumeStats(String consumerGroup) + throws RemotingException, MQClientException, InterruptedException, MQBrokerException { + return MQAdminInstance.threadLocalMQAdminExt().examineConsumeStats(consumerGroup); + } + + @Override + public ConsumeStats examineConsumeStats(String consumerGroup, String topic) + throws RemotingException, MQClientException, InterruptedException, MQBrokerException { + return MQAdminInstance.threadLocalMQAdminExt().examineConsumeStats(consumerGroup, topic); + } + + @Override + public ClusterInfo examineBrokerClusterInfo() + throws InterruptedException, MQBrokerException, RemotingTimeoutException, RemotingSendRequestException, + RemotingConnectException { + return MQAdminInstance.threadLocalMQAdminExt().examineBrokerClusterInfo(); + } + + @Override + public TopicRouteData examineTopicRouteInfo(String topic) + throws RemotingException, MQClientException, InterruptedException { + return MQAdminInstance.threadLocalMQAdminExt().examineTopicRouteInfo(topic); + } + + @Override + public ConsumerConnection examineConsumerConnectionInfo(String consumerGroup) + throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, + InterruptedException, MQBrokerException, RemotingException, MQClientException { + return MQAdminInstance.threadLocalMQAdminExt().examineConsumerConnectionInfo(consumerGroup); + } + + @Override + public ProducerConnection examineProducerConnectionInfo(String producerGroup, String topic) + throws RemotingException, MQClientException, InterruptedException, MQBrokerException { + return MQAdminInstance.threadLocalMQAdminExt().examineProducerConnectionInfo(producerGroup, topic); + } + + @Override + public List getNameServerAddressList() { + return MQAdminInstance.threadLocalMQAdminExt().getNameServerAddressList(); + } + + @Override + public int wipeWritePermOfBroker(String namesrvAddr, String brokerName) + throws RemotingCommandException, RemotingConnectException, RemotingSendRequestException, + RemotingTimeoutException, InterruptedException, MQClientException { + return MQAdminInstance.threadLocalMQAdminExt().wipeWritePermOfBroker(namesrvAddr, brokerName); + } + + @Override + public void putKVConfig(String namespace, String key, String value) { + MQAdminInstance.threadLocalMQAdminExt().putKVConfig(namespace, key, value); + } + + @Override + public String getKVConfig(String namespace, String key) + throws RemotingException, MQClientException, InterruptedException { + return MQAdminInstance.threadLocalMQAdminExt().getKVConfig(namespace, key); + } + + @Override + public KVTable getKVListByNamespace(String namespace) + throws RemotingException, MQClientException, InterruptedException { + return MQAdminInstance.threadLocalMQAdminExt().getKVListByNamespace(namespace); + } + + @Override + public void deleteTopicInBroker(Set addrs, String topic) + throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + logger.info("addrs={} topic={}", JsonUtil.obj2String(addrs), topic); + MQAdminInstance.threadLocalMQAdminExt().deleteTopicInBroker(addrs, topic); + } + + @Override + public void deleteTopicInNameServer(Set addrs, String topic) + throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + MQAdminInstance.threadLocalMQAdminExt().deleteTopicInNameServer(addrs, topic); + } + + @Override + public void deleteSubscriptionGroup(String addr, String groupName) + throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + MQAdminInstance.threadLocalMQAdminExt().deleteSubscriptionGroup(addr, groupName); + } + + @Override + public void deleteSubscriptionGroup(String addr, String groupName, boolean removeOffset) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void createAndUpdateKvConfig(String namespace, String key, String value) + throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + MQAdminInstance.threadLocalMQAdminExt().createAndUpdateKvConfig(namespace, key, value); + } + + @Override + public void deleteKvConfig(String namespace, String key) + throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + MQAdminInstance.threadLocalMQAdminExt().deleteKvConfig(namespace, key); + } + + @Override + public List resetOffsetByTimestampOld(String consumerGroup, String topic, long timestamp, + boolean force) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + return MQAdminInstance.threadLocalMQAdminExt().resetOffsetByTimestampOld(consumerGroup, topic, timestamp, force); + } + + @Override + public Map resetOffsetByTimestamp(String topic, String group, long timestamp, + boolean isForce) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + return MQAdminInstance.threadLocalMQAdminExt().resetOffsetByTimestamp(topic, group, timestamp, isForce); + } + + @Override + public void resetOffsetNew(String consumerGroup, String topic, long timestamp) + throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + MQAdminInstance.threadLocalMQAdminExt().resetOffsetNew(consumerGroup, topic, timestamp); + } + + @Override + public Map> getConsumeStatus(String topic, String group, + String clientAddr) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + return MQAdminInstance.threadLocalMQAdminExt().getConsumeStatus(topic, group, clientAddr); + } + + @Override + public void createOrUpdateOrderConf(String key, String value, boolean isCluster) + throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + MQAdminInstance.threadLocalMQAdminExt().createOrUpdateOrderConf(key, value, isCluster); + } + + @Override + public GroupList queryTopicConsumeByWho(String topic) + throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, + InterruptedException, MQBrokerException, RemotingException, MQClientException { + return MQAdminInstance.threadLocalMQAdminExt().queryTopicConsumeByWho(topic); + } + + @Override + public boolean cleanExpiredConsumerQueue(String cluster) + throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, + InterruptedException { + return MQAdminInstance.threadLocalMQAdminExt().cleanExpiredConsumerQueue(cluster); + } + + @Override + public boolean cleanExpiredConsumerQueueByAddr(String addr) + throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, + InterruptedException { + return MQAdminInstance.threadLocalMQAdminExt().cleanExpiredConsumerQueueByAddr(addr); + } + + @Override + public ConsumerRunningInfo getConsumerRunningInfo(String consumerGroup, String clientId, boolean jstack) + throws RemotingException, MQClientException, InterruptedException { + return MQAdminInstance.threadLocalMQAdminExt().getConsumerRunningInfo(consumerGroup, clientId, jstack); + } + + @Override + public ConsumeMessageDirectlyResult consumeMessageDirectly(String consumerGroup, String clientId, + String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException { + return MQAdminInstance.threadLocalMQAdminExt().consumeMessageDirectly(consumerGroup, clientId, msgId); + } + + @Override + public List messageTrackDetail(MessageExt msg) + throws RemotingException, MQClientException, InterruptedException, MQBrokerException { + return MQAdminInstance.threadLocalMQAdminExt().messageTrackDetail(msg); + } + + @Override + public void cloneGroupOffset(String srcGroup, String destGroup, String topic, boolean isOffline) + throws RemotingException, MQClientException, InterruptedException, MQBrokerException { + MQAdminInstance.threadLocalMQAdminExt().cloneGroupOffset(srcGroup, destGroup, topic, isOffline); + } + + @Override + public void createTopic(String key, String newTopic, int queueNum) throws MQClientException { + MQAdminInstance.threadLocalMQAdminExt().createTopic(key, newTopic, queueNum); + } + + @Override + public void createTopic(String key, String newTopic, int queueNum, int topicSysFlag) + throws MQClientException { + MQAdminInstance.threadLocalMQAdminExt().createTopic(key, newTopic, queueNum, topicSysFlag); + } + + @Override + public long searchOffset(MessageQueue mq, long timestamp) throws MQClientException { + return MQAdminInstance.threadLocalMQAdminExt().searchOffset(mq, timestamp); + } + + @Override + public long maxOffset(MessageQueue mq) throws MQClientException { + return MQAdminInstance.threadLocalMQAdminExt().maxOffset(mq); + } + + @Override + public long minOffset(MessageQueue mq) throws MQClientException { + return MQAdminInstance.threadLocalMQAdminExt().minOffset(mq); + } + + @Override + public long earliestMsgStoreTime(MessageQueue mq) throws MQClientException { + return MQAdminInstance.threadLocalMQAdminExt().earliestMsgStoreTime(mq); + } + + @Override + public MessageExt viewMessage(String msgId) + throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + return MQAdminInstance.threadLocalMQAdminExt().viewMessage(msgId); + } + + @Override + public QueryResult queryMessage(String topic, String key, int maxNum, long begin, long end) + throws MQClientException, InterruptedException { + return MQAdminInstance.threadLocalMQAdminExt().queryMessage(topic, key, maxNum, begin, end); + } + + @Override + @Deprecated + public void start() throws MQClientException { + throw new IllegalStateException("thisMethod is deprecated.use org.apache.rocketmq.dashboard.aspect.admin.MQAdminAspect instead of this"); + } + + @Override + @Deprecated + public void shutdown() { + throw new IllegalStateException("thisMethod is deprecated.use org.apache.rocketmq.dashboard.aspect.admin.MQAdminAspect instead of this"); + } + + // below is 3.2.6->3.5.8 updated + + @Override + public List queryConsumeTimeSpan(String topic, + String group) throws InterruptedException, MQBrokerException, RemotingException, MQClientException { + return MQAdminInstance.threadLocalMQAdminExt().queryConsumeTimeSpan(topic, group); + } + + //MessageClientIDSetter.getNearlyTimeFromID has bug,so we subtract half a day + //next version we will remove it + //https://issues.apache.org/jira/browse/ROCKETMQ-111 + //https://github.com/apache/incubator-rocketmq/pull/69 + @Override + public MessageExt viewMessage(String topic, + String msgId) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + logger.info("MessageClientIDSetter.getNearlyTimeFromID(msgId)={} msgId={}", MessageClientIDSetter.getNearlyTimeFromID(msgId), msgId); + try { + return viewMessage(msgId); + } + catch (Exception e) { + } + MQAdminImpl mqAdminImpl = MQAdminInstance.threadLocalMqClientInstance().getMQAdminImpl(); + QueryResult qr = Reflect.on(mqAdminImpl).call("queryMessage", topic, msgId, 32, + MessageClientIDSetter.getNearlyTimeFromID(msgId).getTime() - 1000 * 60 * 60 * 13L, Long.MAX_VALUE, true).get(); + if (qr != null && qr.getMessageList() != null && qr.getMessageList().size() > 0) { + return qr.getMessageList().get(0); + } + else { + return null; + } + } + + @Override + public ConsumeMessageDirectlyResult consumeMessageDirectly(String consumerGroup, String clientId, String topic, + String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException { + return MQAdminInstance.threadLocalMQAdminExt().consumeMessageDirectly(consumerGroup, clientId, topic, msgId); + } + + @Override + public Properties getBrokerConfig( + String brokerAddr) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, UnsupportedEncodingException, InterruptedException, MQBrokerException { + return MQAdminInstance.threadLocalMQAdminExt().getBrokerConfig(brokerAddr); + } + + @Override + public TopicList fetchTopicsByCLuster( + String clusterName) throws RemotingException, MQClientException, InterruptedException { + return MQAdminInstance.threadLocalMQAdminExt().fetchTopicsByCLuster(clusterName); + } + + @Override + public boolean cleanUnusedTopic( + String cluster) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException { + return MQAdminInstance.threadLocalMQAdminExt().cleanUnusedTopic(cluster); + } + + @Override + public boolean cleanUnusedTopicByAddr( + String addr) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException { + return MQAdminInstance.threadLocalMQAdminExt().cleanUnusedTopicByAddr(addr); + } + + @Override + public BrokerStatsData viewBrokerStatsData(String brokerAddr, String statsName, + String statsKey) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException { + return MQAdminInstance.threadLocalMQAdminExt().viewBrokerStatsData(brokerAddr, statsName, statsKey); + } + + @Override + public Set getClusterList( + String topic) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException { + return MQAdminInstance.threadLocalMQAdminExt().getClusterList(topic); + } + + @Override + public ConsumeStatsList fetchConsumeStatsInBroker(String brokerAddr, boolean isOrder, + long timeoutMillis) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException { + return MQAdminInstance.threadLocalMQAdminExt().fetchConsumeStatsInBroker(brokerAddr, isOrder, timeoutMillis); + } + + @Override + public Set getTopicClusterList( + String topic) throws InterruptedException, MQBrokerException, MQClientException, RemotingException { + return MQAdminInstance.threadLocalMQAdminExt().getTopicClusterList(topic); + } + + @Override + public SubscriptionGroupWrapper getAllSubscriptionGroup(String brokerAddr, + long timeoutMillis) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQBrokerException { + return MQAdminInstance.threadLocalMQAdminExt().getAllSubscriptionGroup(brokerAddr, timeoutMillis); + } + + @Override + public TopicConfigSerializeWrapper getAllTopicGroup(String brokerAddr, + long timeoutMillis) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQBrokerException { + return MQAdminInstance.threadLocalMQAdminExt().getAllTopicGroup(brokerAddr, timeoutMillis); + } + + @Override + public void updateConsumeOffset(String brokerAddr, String consumeGroup, MessageQueue mq, + long offset) throws RemotingException, InterruptedException, MQBrokerException { + MQAdminInstance.threadLocalMQAdminExt().updateConsumeOffset(brokerAddr, consumeGroup, mq, offset); + } + + // 4.0.0 added + @Override public void updateNameServerConfig(Properties properties, + List list) throws InterruptedException, RemotingConnectException, UnsupportedEncodingException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, MQBrokerException { + + } + + @Override public Map getNameServerConfig( + List list) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException, UnsupportedEncodingException { + return null; + } + + @Override public QueryConsumeQueueResponseBody queryConsumeQueue(String brokerAddr, String topic, + int queueId, long index, int count, + String consumerGroup) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException { + return null; + } + + @Override public boolean resumeCheckHalfMessage( + String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException { + return false; + } + + @Override public boolean resumeCheckHalfMessage(String topic, + String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException { + return false; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/client/MQAdminInstance.java b/src/main/java/org/apache/rocketmq/dashboard/service/client/MQAdminInstance.java new file mode 100644 index 0000000..8d9ae4f --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/client/MQAdminInstance.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service.client; + +import org.apache.commons.pool2.impl.GenericObjectPool; +import org.apache.rocketmq.client.impl.MQClientAPIImpl; +import org.apache.rocketmq.client.impl.factory.MQClientInstance; +import org.apache.rocketmq.remoting.RemotingClient; +import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl; +import org.apache.rocketmq.tools.admin.MQAdminExt; +import org.joor.Reflect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MQAdminInstance { + + private static final Logger LOGGER = LoggerFactory.getLogger(MQAdminInstance.class); + private static final ThreadLocal MQ_ADMIN_EXT_THREAD_LOCAL = new ThreadLocal<>(); + + public static MQAdminExt threadLocalMQAdminExt() { + MQAdminExt mqAdminExt = MQ_ADMIN_EXT_THREAD_LOCAL.get(); + if (mqAdminExt == null) { + throw new IllegalStateException("defaultMQAdminExt should be init before you get this"); + } + return mqAdminExt; + } + + public static RemotingClient threadLocalRemotingClient() { + MQClientInstance mqClientInstance = threadLocalMqClientInstance(); + MQClientAPIImpl mQClientAPIImpl = Reflect.on(mqClientInstance).get("mQClientAPIImpl"); + return Reflect.on(mQClientAPIImpl).get("remotingClient"); + } + + public static MQClientInstance threadLocalMqClientInstance() { + DefaultMQAdminExtImpl defaultMQAdminExtImpl = Reflect.on(MQAdminInstance.threadLocalMQAdminExt()).get("defaultMQAdminExtImpl"); + return Reflect.on(defaultMQAdminExtImpl).get("mqClientInstance"); + } + + public static void createMQAdmin(GenericObjectPool mqAdminExtPool) { + try { + // Get the mqAdmin instance from the object pool + MQAdminExt mqAdminExt = mqAdminExtPool.borrowObject(); + MQ_ADMIN_EXT_THREAD_LOCAL.set(mqAdminExt); + } catch (Exception e) { + LOGGER.error("get mqAdmin from pool error", e); + } + } + + public static void returnMQAdmin(GenericObjectPool mqAdminExtPool) { + MQAdminExt mqAdminExt = MQ_ADMIN_EXT_THREAD_LOCAL.get(); + if (mqAdminExt != null) { + try { + // After execution, return the mqAdmin instance to the object pool + mqAdminExtPool.returnObject(mqAdminExt); + } catch (Exception e) { + LOGGER.error("return mqAdmin to pool error", e); + } + } + MQ_ADMIN_EXT_THREAD_LOCAL.remove(); + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/AbstractFileStore.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/AbstractFileStore.java new file mode 100644 index 0000000..045b990 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/AbstractFileStore.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service.impl; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.srvutil.FileWatchService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class AbstractFileStore { + public final Logger log = LoggerFactory.getLogger(this.getClass()); + + public String filePath; + + public AbstractFileStore(RMQConfigure configure, String fileName) { + filePath = configure.getRocketMqDashboardDataPath() + File.separator + fileName; + if (!new File(filePath).exists()) { + // Use the default path + InputStream inputStream = getClass().getResourceAsStream("/" + fileName); + if (inputStream == null) { + log.error(String.format("Can not found the file %s in Spring Boot jar", fileName)); + System.exit(1); + } else { + try { + load(inputStream); + } catch (Exception e) { + log.error("fail to load file {}", filePath, e); + } finally { + try { + inputStream.close(); + } catch (IOException e) { + log.error("inputStream close exception", e); + } + } + } + } else { + log.info(String.format("configure file is %s", filePath)); + load(); + watch(); + } + } + + abstract void load(InputStream inputStream); + + private void load() { + load(null); + } + + private boolean watch() { + try { + FileWatchService fileWatchService = new FileWatchService(new String[] {filePath}, new FileWatchService.Listener() { + @Override + public void onChanged(String path) { + log.info("The file changed, reload the context"); + load(); + } + }); + fileWatchService.start(); + log.info("Succeed to start FileWatcherService"); + return true; + } catch (Exception e) { + log.error("Failed to start FileWatcherService", e); + } + return false; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/ClusterServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/ClusterServiceImpl.java new file mode 100644 index 0000000..c57f7e1 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/ClusterServiceImpl.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service.impl; + +import org.apache.rocketmq.common.protocol.body.ClusterInfo; +import org.apache.rocketmq.common.protocol.body.KVTable; +import org.apache.rocketmq.common.protocol.route.BrokerData; +import org.apache.rocketmq.tools.admin.MQAdminExt; +import org.apache.rocketmq.dashboard.service.ClusterService; +import org.apache.rocketmq.dashboard.util.JsonUtil; +import com.google.common.base.Throwables; +import com.google.common.collect.Maps; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Map; +import java.util.Properties; + +@Service +public class ClusterServiceImpl implements ClusterService { + private Logger logger = LoggerFactory.getLogger(ClusterServiceImpl.class); + @Resource + private MQAdminExt mqAdminExt; + + @Override + public Map list() { + try { + Map resultMap = Maps.newHashMap(); + ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo(); + logger.info("op=look_clusterInfo {}", JsonUtil.obj2String(clusterInfo)); + Map> brokerServer = Maps.newHashMap(); + for (BrokerData brokerData : clusterInfo.getBrokerAddrTable().values()) { + Map brokerMasterSlaveMap = Maps.newHashMap(); + for (Map.Entry brokerAddr : brokerData.getBrokerAddrs().entrySet()) { + KVTable kvTable = mqAdminExt.fetchBrokerRuntimeStats(brokerAddr.getValue()); + brokerMasterSlaveMap.put(brokerAddr.getKey(), kvTable.getTable()); + } + brokerServer.put(brokerData.getBrokerName(), brokerMasterSlaveMap); + } + resultMap.put("clusterInfo", clusterInfo); + resultMap.put("brokerServer", brokerServer); + return resultMap; + } + catch (Exception err) { + throw Throwables.propagate(err); + } + } + + + @Override + public Properties getBrokerConfig(String brokerAddr) { + try { + return mqAdminExt.getBrokerConfig(brokerAddr); + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/ConfigServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/ConfigServiceImpl.java new file mode 100644 index 0000000..805258e --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/ConfigServiceImpl.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service.impl; + +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.dashboard.service.AbstractCommonService; +import org.apache.rocketmq.dashboard.service.ConfigService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.*; +import java.util.*; + +@Service +public class ConfigServiceImpl extends AbstractCommonService implements ConfigService { + + private Logger logger = LoggerFactory.getLogger(ConfigServiceImpl.class); + @Autowired + private RMQConfigure rmqConfigure; + + @Override + public Map queryConfigMap() { + Map result = new HashMap<>(); + String folderPath = rmqConfigure.getRocketMqDashboardDataPath(); // 读取目录下所有txt文件 + File folder = new File(folderPath); + if (folder.exists()) { + File[] files = folder.listFiles(); + for (File file : files) { + if (file.isFile() && file.getName().endsWith(".txt")) { + try (BufferedReader reader = new BufferedReader(new FileReader(file.getAbsolutePath()))) { + String line = reader.readLine(); + result.put(file.getName().replace(".txt", ""), line); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + } + } + } else { + folder.mkdir(); + } + return result; + } + + @Override + public boolean createConfig(String name, String value) { + String fileName = rmqConfigure.getRocketMqDashboardDataPath() + File.separator + name + ".txt"; + File file = new File(fileName); + if(file.exists()) { + logger.error("文件已经存在"); + return false; + } + try { + if (file.createNewFile()) { + FileWriter writer = new FileWriter(file); + writer.write(value); + writer.close(); + } else { + logger.error("文件无法创建"); + return false; + } + } catch (IOException e) { + logger.error(e.getMessage(), e); + return false; + } + return true; + } + + @Override + public boolean updateConfig(String name, String value) { + String fileName = rmqConfigure.getRocketMqDashboardDataPath() + File.separator + name + ".txt"; + File file = new File(fileName); + if(!file.exists()) { + logger.error("文件不存在"); + return false; + } + try { + FileWriter fileWriter = new FileWriter(fileName, false); + fileWriter.write(value); + fileWriter.close(); + } catch (IOException e) { + logger.error(e.getMessage(), e); + return false; + } + return true; + } + + @Override + public boolean deleteConfig(String name, String value) { + String fileName = rmqConfigure.getRocketMqDashboardDataPath() + File.separator + name + ".txt"; + File file = new File(fileName); + if(!file.exists()) { + logger.error("文件不存在"); + return false; + } + return file.delete(); + } + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/ConsumerServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/ConsumerServiceImpl.java new file mode 100644 index 0000000..37073d7 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/ConsumerServiceImpl.java @@ -0,0 +1,332 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service.impl; + +import com.google.common.base.Predicate; +import com.google.common.base.Throwables; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.common.MQVersion; +import org.apache.rocketmq.common.admin.ConsumeStats; +import org.apache.rocketmq.common.admin.RollbackStats; +import org.apache.rocketmq.common.message.MessageQueue; +import org.apache.rocketmq.common.protocol.ResponseCode; +import org.apache.rocketmq.common.protocol.body.ClusterInfo; +import org.apache.rocketmq.common.protocol.body.Connection; +import org.apache.rocketmq.common.protocol.body.ConsumerConnection; +import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo; +import org.apache.rocketmq.common.protocol.body.GroupList; +import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper; +import org.apache.rocketmq.common.protocol.route.BrokerData; +import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig; +import org.apache.rocketmq.dashboard.model.ConsumerGroupRollBackStat; +import org.apache.rocketmq.dashboard.model.GroupConsumeInfo; +import org.apache.rocketmq.dashboard.model.QueueStatInfo; +import org.apache.rocketmq.dashboard.model.TopicConsumerInfo; +import org.apache.rocketmq.dashboard.model.request.ConsumerConfigInfo; +import org.apache.rocketmq.dashboard.model.request.DeleteSubGroupRequest; +import org.apache.rocketmq.dashboard.model.request.ResetOffsetRequest; +import org.apache.rocketmq.dashboard.service.AbstractCommonService; +import org.apache.rocketmq.dashboard.service.ConsumerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import static com.google.common.base.Throwables.propagate; + +@Service +public class ConsumerServiceImpl extends AbstractCommonService implements ConsumerService { + private Logger logger = LoggerFactory.getLogger(ConsumerServiceImpl.class); + + @Override + public List queryGroupList() { + Set consumerGroupSet = Sets.newHashSet(); + try { + ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo(); + for (BrokerData brokerData : clusterInfo.getBrokerAddrTable().values()) { + SubscriptionGroupWrapper subscriptionGroupWrapper = mqAdminExt.getAllSubscriptionGroup(brokerData.selectBrokerAddr(), 3000L); + consumerGroupSet.addAll(subscriptionGroupWrapper.getSubscriptionGroupTable().keySet()); + } + } + catch (Exception err) { + throw Throwables.propagate(err); + } + List groupConsumeInfoList = Lists.newArrayList(); + for (String consumerGroup : consumerGroupSet) { + groupConsumeInfoList.add(queryGroup(consumerGroup)); + } + Collections.sort(groupConsumeInfoList); + return groupConsumeInfoList; + } + + @Override + public GroupConsumeInfo queryGroup(String consumerGroup) { + GroupConsumeInfo groupConsumeInfo = new GroupConsumeInfo(); + try { + ConsumeStats consumeStats = null; + try { + consumeStats = mqAdminExt.examineConsumeStats(consumerGroup); + } + catch (Exception e) { + logger.warn("examineConsumeStats exception to consumerGroup {}, response [{}]", consumerGroup, e.getMessage()); + } + + ConsumerConnection consumerConnection = null; + try { + consumerConnection = mqAdminExt.examineConsumerConnectionInfo(consumerGroup); + } + catch (Exception e) { + logger.warn("examineConsumeStats exception to consumerGroup {}, response [{}]", consumerGroup, e.getMessage()); + } + + groupConsumeInfo.setGroup(consumerGroup); + + if (consumeStats != null) { + groupConsumeInfo.setConsumeTps((int)consumeStats.getConsumeTps()); + groupConsumeInfo.setDiffTotal(consumeStats.computeTotalDiff()); + } + + if (consumerConnection != null) { + groupConsumeInfo.setCount(consumerConnection.getConnectionSet().size()); + groupConsumeInfo.setMessageModel(consumerConnection.getMessageModel()); + groupConsumeInfo.setConsumeType(consumerConnection.getConsumeType()); + groupConsumeInfo.setVersion(MQVersion.getVersionDesc(consumerConnection.computeMinVersion())); + } + } + catch (Exception e) { + logger.warn("examineConsumeStats or examineConsumerConnectionInfo exception, " + + consumerGroup, e); + } + return groupConsumeInfo; + } + + @Override + public List queryConsumeStatsListByGroupName(String groupName) { + return queryConsumeStatsList(null, groupName); + } + + @Override + public List queryConsumeStatsList(final String topic, String groupName) { + ConsumeStats consumeStats = null; + try { + consumeStats = mqAdminExt.examineConsumeStats(groupName, topic); + } + catch (Exception e) { + throw propagate(e); + } + List mqList = Lists.newArrayList(Iterables.filter(consumeStats.getOffsetTable().keySet(), new Predicate() { + @Override + public boolean apply(MessageQueue o) { + return StringUtils.isBlank(topic) || o.getTopic().equals(topic); + } + })); + Collections.sort(mqList); + List topicConsumerInfoList = Lists.newArrayList(); + TopicConsumerInfo nowTopicConsumerInfo = null; + Map messageQueueClientMap = getClientConnection(groupName); + for (MessageQueue mq : mqList) { + if (nowTopicConsumerInfo == null || (!StringUtils.equals(mq.getTopic(), nowTopicConsumerInfo.getTopic()))) { + nowTopicConsumerInfo = new TopicConsumerInfo(mq.getTopic()); + topicConsumerInfoList.add(nowTopicConsumerInfo); + } + QueueStatInfo queueStatInfo = QueueStatInfo.fromOffsetTableEntry(mq, consumeStats.getOffsetTable().get(mq)); + queueStatInfo.setClientInfo(messageQueueClientMap.get(mq)); + nowTopicConsumerInfo.appendQueueStatInfo(queueStatInfo); + } + return topicConsumerInfoList; + } + + private Map getClientConnection(String groupName) { + Map results = Maps.newHashMap(); + try { + ConsumerConnection consumerConnection = mqAdminExt.examineConsumerConnectionInfo(groupName); + for (Connection connection : consumerConnection.getConnectionSet()) { + String clinetId = connection.getClientId(); + ConsumerRunningInfo consumerRunningInfo = mqAdminExt.getConsumerRunningInfo(groupName, clinetId, false); + for (MessageQueue messageQueue : consumerRunningInfo.getMqTable().keySet()) { +// results.put(messageQueue, clinetId + " " + connection.getClientAddr()); + results.put(messageQueue, clinetId); + } + } + } + catch (Exception err) { + logger.error("op=getClientConnection_error", err); + } + return results; + } + + @Override + public Map queryConsumeStatsListByTopicName(String topic) { + Map group2ConsumerInfoMap = Maps.newHashMap(); + try { + GroupList groupList = mqAdminExt.queryTopicConsumeByWho(topic); + for (String group : groupList.getGroupList()) { + List topicConsumerInfoList = null; + try { + topicConsumerInfoList = queryConsumeStatsList(topic, group); + } + catch (Exception ignore) { + } + group2ConsumerInfoMap.put(group, CollectionUtils.isEmpty(topicConsumerInfoList) ? new TopicConsumerInfo(topic) : topicConsumerInfoList.get(0)); + } + return group2ConsumerInfoMap; + } + catch (Exception e) { + throw propagate(e); + } + } + + @Override + public Map resetOffset(ResetOffsetRequest resetOffsetRequest) { + Map groupRollbackStats = Maps.newHashMap(); + for (String consumerGroup : resetOffsetRequest.getConsumerGroupList()) { + try { + Map rollbackStatsMap = + mqAdminExt.resetOffsetByTimestamp(resetOffsetRequest.getTopic(), consumerGroup, resetOffsetRequest.getResetTime(), resetOffsetRequest.isForce()); + ConsumerGroupRollBackStat consumerGroupRollBackStat = new ConsumerGroupRollBackStat(true); + List rollbackStatsList = consumerGroupRollBackStat.getRollbackStatsList(); + for (Map.Entry rollbackStatsEntty : rollbackStatsMap.entrySet()) { + RollbackStats rollbackStats = new RollbackStats(); + rollbackStats.setRollbackOffset(rollbackStatsEntty.getValue()); + rollbackStats.setQueueId(rollbackStatsEntty.getKey().getQueueId()); + rollbackStats.setBrokerName(rollbackStatsEntty.getKey().getBrokerName()); + rollbackStatsList.add(rollbackStats); + } + groupRollbackStats.put(consumerGroup, consumerGroupRollBackStat); + } + catch (MQClientException e) { + if (ResponseCode.CONSUMER_NOT_ONLINE == e.getResponseCode()) { + try { + ConsumerGroupRollBackStat consumerGroupRollBackStat = new ConsumerGroupRollBackStat(true); + List rollbackStatsList = mqAdminExt.resetOffsetByTimestampOld(consumerGroup, resetOffsetRequest.getTopic(), resetOffsetRequest.getResetTime(), true); + consumerGroupRollBackStat.setRollbackStatsList(rollbackStatsList); + groupRollbackStats.put(consumerGroup, consumerGroupRollBackStat); + continue; + } + catch (Exception err) { + logger.error("op=resetOffset_which_not_online_error", err); + } + } + else { + logger.error("op=resetOffset_error", e); + } + groupRollbackStats.put(consumerGroup, new ConsumerGroupRollBackStat(false, e.getMessage())); + } + catch (Exception e) { + logger.error("op=resetOffset_error", e); + groupRollbackStats.put(consumerGroup, new ConsumerGroupRollBackStat(false, e.getMessage())); + } + } + return groupRollbackStats; + } + + @Override + public List examineSubscriptionGroupConfig(String group) { + List consumerConfigInfoList = Lists.newArrayList(); + try { + ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo(); + for (String brokerName : clusterInfo.getBrokerAddrTable().keySet()) { //foreach brokerName + String brokerAddress = clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(); + SubscriptionGroupConfig subscriptionGroupConfig = mqAdminExt.examineSubscriptionGroupConfig(brokerAddress, group); + if (subscriptionGroupConfig == null) { + continue; + } + consumerConfigInfoList.add(new ConsumerConfigInfo(Lists.newArrayList(brokerName), subscriptionGroupConfig)); + } + } + catch (Exception e) { + throw propagate(e); + } + return consumerConfigInfoList; + } + + @Override + public boolean deleteSubGroup(DeleteSubGroupRequest deleteSubGroupRequest) { + try { + ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo(); + for (String brokerName : deleteSubGroupRequest.getBrokerNameList()) { + logger.info("addr={} groupName={}", clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), deleteSubGroupRequest.getGroupName()); + mqAdminExt.deleteSubscriptionGroup(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), deleteSubGroupRequest.getGroupName()); + } + } + catch (Exception e) { + throw propagate(e); + } + return true; + } + + @Override + public boolean createAndUpdateSubscriptionGroupConfig(ConsumerConfigInfo consumerConfigInfo) { + try { + ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo(); + for (String brokerName : changeToBrokerNameSet(clusterInfo.getClusterAddrTable(), + consumerConfigInfo.getClusterNameList(), consumerConfigInfo.getBrokerNameList())) { + mqAdminExt.createAndUpdateSubscriptionGroupConfig(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), consumerConfigInfo.getSubscriptionGroupConfig()); + } + } + catch (Exception err) { + throw Throwables.propagate(err); + } + return true; + } + + @Override + public Set fetchBrokerNameSetBySubscriptionGroup(String group) { + Set brokerNameSet = Sets.newHashSet(); + try { + List consumerConfigInfoList = examineSubscriptionGroupConfig(group); + for (ConsumerConfigInfo consumerConfigInfo : consumerConfigInfoList) { + brokerNameSet.addAll(consumerConfigInfo.getBrokerNameList()); + } + } + catch (Exception e) { + throw Throwables.propagate(e); + } + return brokerNameSet; + + } + + @Override + public ConsumerConnection getConsumerConnection(String consumerGroup) { + try { + return mqAdminExt.examineConsumerConnectionInfo(consumerGroup); + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } + + @Override + public ConsumerRunningInfo getConsumerRunningInfo(String consumerGroup, String clientId, boolean jstack) { + try { + return mqAdminExt.getConsumerRunningInfo(consumerGroup, clientId, jstack); + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/DashboardCollectServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/DashboardCollectServiceImpl.java new file mode 100644 index 0000000..fa8f073 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/DashboardCollectServiceImpl.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.google.common.base.Charsets; +import com.google.common.base.Throwables; +import com.google.common.base.Ticker; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.cache.RemovalListener; +import com.google.common.cache.RemovalNotification; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.io.Files; +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.Resource; +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.dashboard.service.DashboardCollectService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +@Service +public class DashboardCollectServiceImpl implements DashboardCollectService { + + @Resource + private RMQConfigure configure; + + private final static Logger log = LoggerFactory.getLogger(DashboardCollectServiceImpl.class); + + private LoadingCache> brokerMap = CacheBuilder.newBuilder() + .maximumSize(1000) + .concurrencyLevel(10) + .recordStats() + .ticker(Ticker.systemTicker()) + .removalListener(new RemovalListener() { + @Override + public void onRemoval(RemovalNotification notification) { + log.debug(notification.getKey() + " was removed, cause is " + notification.getCause()); + } + }) + .build( + new CacheLoader>() { + @Override + public List load(String key) { + List list = Lists.newArrayList(); + return list; + } + } + ); + + private LoadingCache> topicMap = CacheBuilder.newBuilder() + .maximumSize(1000) + .concurrencyLevel(10) + .recordStats() + .ticker(Ticker.systemTicker()) + .removalListener(new RemovalListener() { + @Override + public void onRemoval(RemovalNotification notification) { + log.debug(notification.getKey() + " was removed, cause is " + notification.getCause()); + } + }) + .build( + new CacheLoader>() { + @Override + public List load(String key) { + List list = Lists.newArrayList(); + return list; + } + } + ); + + @Override + public LoadingCache> getBrokerMap() { + return brokerMap; + } + @Override + public LoadingCache> getTopicMap() { + return topicMap; + } + + @Override + public Map> jsonDataFile2map(File file) { + List strings; + try { + strings = Files.readLines(file, Charsets.UTF_8); + } + catch (IOException e) { + throw Throwables.propagate(e); + } + StringBuffer sb = new StringBuffer(); + for (String string : strings) { + sb.append(string); + } + JSONObject json = (JSONObject) JSONObject.parse(sb.toString()); + Set> entries = json.entrySet(); + Map> map = Maps.newHashMap(); + for (Map.Entry entry : entries) { + JSONArray tpsArray = (JSONArray) entry.getValue(); + if (tpsArray == null) { + continue; + } + Object[] tpsStrArray = tpsArray.toArray(); + List tpsList = Lists.newArrayList(); + for (Object tpsObj : tpsStrArray) { + tpsList.add("" + tpsObj); + } + map.put(entry.getKey(), tpsList); + } + return map; + } + + @Override + public Map> getBrokerCache(String date) { + String dataLocationPath = configure.getDashboardCollectData(); + File file = new File(dataLocationPath + date + ".json"); + if (!file.exists()) { + log.info(String.format("No dashboard data for broker cache data: %s", date)); + return Maps.newHashMap(); + } + return jsonDataFile2map(file); + } + + @Override + public Map> getTopicCache(String date) { + String dataLocationPath = configure.getDashboardCollectData(); + File file = new File(dataLocationPath + date + "_topic" + ".json"); + if (!file.exists()) { + log.info(String.format("No dashboard data for data: %s", date)); + //throw Throwables.propagate(new ServiceException(1, "This date have't data!")); + return Maps.newHashMap(); + } + return jsonDataFile2map(file); + } + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/DashboardServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/DashboardServiceImpl.java new file mode 100644 index 0000000..b2885e4 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/DashboardServiceImpl.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service.impl; + +import com.google.common.collect.Lists; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Map; +import javax.annotation.Resource; +import org.apache.rocketmq.dashboard.service.DashboardCollectService; +import org.apache.rocketmq.dashboard.service.DashboardService; +import org.springframework.stereotype.Service; + +@Service +public class DashboardServiceImpl implements DashboardService { + + @Resource + private DashboardCollectService dashboardCollectService; + /** + * @param date format yyyy-MM-dd + */ + @Override + public Map> queryBrokerData(String date) { + return dashboardCollectService.getBrokerCache(date); + } + + @Override + public Map> queryTopicData(String date) { + return dashboardCollectService.getTopicCache(date); + } + + /** + * @param date format yyyy-MM-dd + * @param topicName + */ + @Override + public List queryTopicData(String date, String topicName) { + if (null != dashboardCollectService.getTopicCache(date)) { + return dashboardCollectService.getTopicCache(date).get(topicName); + } + return null; + } + + @Override + public List queryTopicCurrentData() { + Date date = new Date(); + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Map> topicCache = dashboardCollectService.getTopicCache(format.format(date)); + List result = Lists.newArrayList(); + for (Map.Entry> entry : topicCache.entrySet()) { + List value = entry.getValue(); + result.add(entry.getKey() + "," + value.get(value.size() - 1).split(",")[4]); + } + return result; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/LoginServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/LoginServiceImpl.java new file mode 100644 index 0000000..c4a07c9 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/LoginServiceImpl.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service.impl; + +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.dashboard.service.LoginService; +import org.apache.rocketmq.dashboard.service.UserService; +import org.apache.rocketmq.dashboard.util.WebUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +@Service +public class LoginServiceImpl implements LoginService { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Resource + private RMQConfigure rmqConfigure; + + @Autowired + private UserService userService; + + + @Override + public boolean login(HttpServletRequest request, HttpServletResponse response) { + if (WebUtil.getValueFromSession(request, WebUtil.USER_NAME) != null) { + return true; + } + + auth(request, response); + return false; + } + + protected void auth(HttpServletRequest request, HttpServletResponse response) { + try { + String url = WebUtil.getUrl(request); + try { + url = URLEncoder.encode(url, "UTF-8"); + } catch (UnsupportedEncodingException e) { + logger.error("url encode:{}", url, e); + } + logger.debug("redirect url : {}", url); + WebUtil.redirect(response, request, "/#/login?redirect=" + url); + } catch (IOException e) { + logger.error("redirect err", e); + } + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/MessageServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/MessageServiceImpl.java new file mode 100644 index 0000000..020548e --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/MessageServiceImpl.java @@ -0,0 +1,544 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service.impl; + + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Throwables; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import org.apache.commons.lang3.StringUtils; +import org.apache.rocketmq.acl.common.AclClientRPCHook; +import org.apache.rocketmq.acl.common.SessionCredentials; +import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer; +import org.apache.rocketmq.client.consumer.PullResult; +import org.apache.rocketmq.client.consumer.PullStatus; +import org.apache.rocketmq.common.MixAll; +import org.apache.rocketmq.common.Pair; +import org.apache.rocketmq.common.message.MessageClientIDSetter; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.common.message.MessageQueue; +import org.apache.rocketmq.common.protocol.body.Connection; +import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult; +import org.apache.rocketmq.common.protocol.body.ConsumerConnection; +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.dashboard.exception.ServiceException; +import org.apache.rocketmq.dashboard.model.QueueOffsetInfo; +import org.apache.rocketmq.dashboard.model.MessageView; +import org.apache.rocketmq.dashboard.model.MessagePage; +import org.apache.rocketmq.dashboard.model.MessagePageTask; +import org.apache.rocketmq.dashboard.model.MessageQueryByPage; +import org.apache.rocketmq.dashboard.model.request.MessageQuery; +import org.apache.rocketmq.dashboard.service.MessageService; +import org.apache.rocketmq.remoting.RPCHook; +import org.apache.rocketmq.tools.admin.MQAdminExt; +import org.apache.rocketmq.tools.admin.api.MessageTrack; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Collections; +import java.util.List; +import java.util.Comparator; +import java.util.ArrayList; +import java.util.Set; +import java.util.Collection; +import java.util.Date; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Service +public class MessageServiceImpl implements MessageService { + + private Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class); + + private static final Cache> CACHE = CacheBuilder.newBuilder() + .maximumSize(10000) + .expireAfterWrite(60, TimeUnit.MINUTES) + .build(); + + @Autowired + private RMQConfigure configure; + /** + * @see org.apache.rocketmq.store.config.MessageStoreConfig maxMsgsNumBatch = 64; + * @see org.apache.rocketmq.store.index.IndexService maxNum = Math.min(maxNum, this.defaultMessageStore.getMessageStoreConfig().getMaxMsgsNumBatch()); + */ + private final static int QUERY_MESSAGE_MAX_NUM = 64; + @Resource + private MQAdminExt mqAdminExt; + + @Override + public Pair> viewMessage(String subject, final String msgId) { + try { + + MessageExt messageExt = mqAdminExt.viewMessage(subject, msgId); + List messageTrackList = messageTrackDetail(messageExt); + return new Pair<>(MessageView.fromMessageExt(messageExt), messageTrackList); + } catch (Exception e) { + throw new ServiceException(-1, String.format("Failed to query message by Id: %s", msgId)); + } + } + + @Override + public List queryMessageByTopicAndKey(String topic, String key) { + try { + return Lists.transform(mqAdminExt.queryMessage(topic, key, QUERY_MESSAGE_MAX_NUM, 0, System.currentTimeMillis()).getMessageList(), new Function() { + @Override + public MessageView apply(MessageExt messageExt) { + return MessageView.fromMessageExt(messageExt); + } + }); + } catch (Exception err) { + throw Throwables.propagate(err); + } + } + + @Override + public List queryMessageByTopic(String topic, final long begin, final long end) { + boolean isEnableAcl = !StringUtils.isEmpty(configure.getAccessKey()) && !StringUtils.isEmpty(configure.getSecretKey()); + RPCHook rpcHook = null; + if (isEnableAcl) { + rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey())); + } + DefaultMQPullConsumer consumer = buildDefaultMQPullConsumer(rpcHook, configure.isUseTLS()); + List messageViewList = Lists.newArrayList(); + try { + String subExpression = "*"; + consumer.start(); + Set mqs = consumer.fetchSubscribeMessageQueues(topic); + for (MessageQueue mq : mqs) { + long minOffset = consumer.searchOffset(mq, begin); + long maxOffset = consumer.searchOffset(mq, end); + READQ: + for (long offset = minOffset; offset <= maxOffset; ) { + try { + if (messageViewList.size() > 2000) { + break; + } + PullResult pullResult = consumer.pull(mq, subExpression, offset, 32); + offset = pullResult.getNextBeginOffset(); + switch (pullResult.getPullStatus()) { + case FOUND: + + List messageViewListByQuery = Lists.transform(pullResult.getMsgFoundList(), new Function() { + @Override + public MessageView apply(MessageExt messageExt) { + messageExt.setBody(null); + return MessageView.fromMessageExt(messageExt); + } + }); + List filteredList = Lists.newArrayList(Iterables.filter(messageViewListByQuery, new Predicate() { + @Override + public boolean apply(MessageView messageView) { + if (messageView.getStoreTimestamp() < begin || messageView.getStoreTimestamp() > end) { + logger.info("begin={} end={} time not in range {} {}", begin, end, messageView.getStoreTimestamp(), new Date(messageView.getStoreTimestamp()).toString()); + } + return messageView.getStoreTimestamp() >= begin && messageView.getStoreTimestamp() <= end; + } + })); + messageViewList.addAll(filteredList); + break; + case NO_MATCHED_MSG: + case NO_NEW_MSG: + case OFFSET_ILLEGAL: + break READQ; + } + } catch (Exception e) { + break; + } + } + } + Collections.sort(messageViewList, new Comparator() { + @Override + public int compare(MessageView o1, MessageView o2) { + if (o1.getStoreTimestamp() - o2.getStoreTimestamp() == 0) { + return 0; + } + return (o1.getStoreTimestamp() > o2.getStoreTimestamp()) ? -1 : 1; + } + }); + return messageViewList; + } catch (Exception e) { + throw Throwables.propagate(e); + } finally { + consumer.shutdown(); + } + } + + @Override + public List messageTrackDetail(MessageExt msg) { + try { + return mqAdminExt.messageTrackDetail(msg); + } catch (Exception e) { + logger.error("op=messageTrackDetailError", e); + return Collections.emptyList(); + } + } + + + @Override + public ConsumeMessageDirectlyResult consumeMessageDirectly(String topic, String msgId, String consumerGroup, + String clientId) { + if (StringUtils.isNotBlank(clientId)) { + try { + return mqAdminExt.consumeMessageDirectly(consumerGroup, clientId, topic, msgId); + } catch (Exception e) { + throw Throwables.propagate(e); + } + } + + try { + ConsumerConnection consumerConnection = mqAdminExt.examineConsumerConnectionInfo(consumerGroup); + for (Connection connection : consumerConnection.getConnectionSet()) { + if (StringUtils.isBlank(connection.getClientId())) { + continue; + } + logger.info("clientId={}", connection.getClientId()); + return mqAdminExt.consumeMessageDirectly(consumerGroup, connection.getClientId(), topic, msgId); + } + } catch (Exception e) { + throw Throwables.propagate(e); + } + throw new IllegalStateException("NO CONSUMER"); + + } + + @Override + public MessagePage queryMessageByPage(MessageQuery query) { + MessageQueryByPage queryByPage = new MessageQueryByPage( + query.getPageNum(), + query.getPageSize(), + query.getTopic(), + query.getBegin(), + query.getEnd()); + + List queueOffsetInfos = CACHE.getIfPresent(query.getTaskId()); + + if (queueOffsetInfos == null) { + query.setPageNum(1); + MessagePageTask task = this.queryFirstMessagePage(queryByPage); + String taskId = MessageClientIDSetter.createUniqID(); + CACHE.put(taskId, task.getQueueOffsetInfos()); + + return new MessagePage(task.getPage(), taskId); + } + Page messageViews = queryMessageByTaskPage(queryByPage, queueOffsetInfos); + return new MessagePage(messageViews, query.getTaskId()); + + } + + private MessagePageTask queryFirstMessagePage(MessageQueryByPage query) { + boolean isEnableAcl = !StringUtils.isEmpty(configure.getAccessKey()) && !StringUtils.isEmpty(configure.getSecretKey()); + RPCHook rpcHook = null; + if (isEnableAcl) { + rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey())); + } + DefaultMQPullConsumer consumer = buildDefaultMQPullConsumer(rpcHook, configure.isUseTLS()); + + long total = 0; + List queueOffsetInfos = new ArrayList<>(); + + List messageViews = new ArrayList<>(); + + try { + consumer.start(); + Collection messageQueues = consumer.fetchSubscribeMessageQueues(query.getTopic()); + int idx = 0; + for (MessageQueue messageQueue : messageQueues) { + Long minOffset = consumer.searchOffset(messageQueue, query.getBegin()); + Long maxOffset = consumer.searchOffset(messageQueue, query.getEnd()) + 1; + queueOffsetInfos.add(new QueueOffsetInfo(idx++, minOffset, maxOffset, minOffset, minOffset, messageQueue)); + } + + // check first offset has message + // filter the begin time + for (QueueOffsetInfo queueOffset : queueOffsetInfos) { + Long start = queueOffset.getStart(); + boolean hasData = false; + boolean hasIllegalOffset = true; + while (hasIllegalOffset) { + PullResult pullResult = consumer.pull(queueOffset.getMessageQueues(), "*", start, 32); + if (pullResult.getPullStatus() == PullStatus.FOUND) { + hasData = true; + List msgFoundList = pullResult.getMsgFoundList(); + for (MessageExt messageExt : msgFoundList) { + if (messageExt.getStoreTimestamp() < query.getBegin()) { + start++; + } else { + hasIllegalOffset = false; + break; + } + } + } else { + hasIllegalOffset = false; + } + } + if (!hasData) { + queueOffset.setEnd(queueOffset.getStart()); + } + queueOffset.setStart(start); + queueOffset.setStartOffset(start); + queueOffset.setEndOffset(start); + } + + // filter the end time + for (QueueOffsetInfo queueOffset : queueOffsetInfos) { + if (queueOffset.getStart().equals(queueOffset.getEnd())) { + continue; + } + long end = queueOffset.getEnd(); + long pullOffset = end; + int pullSize = 32; + boolean hasIllegalOffset = true; + while (hasIllegalOffset) { + + if (pullOffset - pullSize > queueOffset.getStart()) { + pullOffset = pullOffset - pullSize; + } else { + pullOffset = queueOffset.getStartOffset(); + pullSize = (int) (end - pullOffset); + } + PullResult pullResult = consumer.pull(queueOffset.getMessageQueues(), "*", pullOffset, pullSize); + if (pullResult.getPullStatus() == PullStatus.FOUND) { + List msgFoundList = pullResult.getMsgFoundList(); + for (int i = msgFoundList.size() - 1; i >= 0; i--) { + MessageExt messageExt = msgFoundList.get(i); + if (messageExt.getStoreTimestamp() < query.getBegin()) { + end--; + } else { + hasIllegalOffset = false; + break; + } + } + } else { + hasIllegalOffset = false; + } + if (pullOffset == queueOffset.getStartOffset()) { + break; + } + } + queueOffset.setEnd(end); + total += queueOffset.getEnd() - queueOffset.getStart(); + } + + long pageSize = total > query.getPageSize() ? query.getPageSize() : total; + + + // move startOffset + int next = moveStartOffset(queueOffsetInfos, query); + moveEndOffset(queueOffsetInfos, query, next); + + // find the first page of message + for (QueueOffsetInfo queueOffsetInfo : queueOffsetInfos) { + Long start = queueOffsetInfo.getStartOffset(); + Long end = queueOffsetInfo.getEndOffset(); + long size = Math.min(end - start, pageSize); + if (size == 0) { + continue; + } + + while (size > 0) { + PullResult pullResult = consumer.pull(queueOffsetInfo.getMessageQueues(), "*", start, 32); + if (pullResult.getPullStatus() == PullStatus.FOUND) { + List poll = pullResult.getMsgFoundList(); + if (poll.size() == 0) { + break; + } + List collect = poll.stream() + .map(MessageView::fromMessageExt).collect(Collectors.toList()); + + for (MessageView view : collect) { + if (size > 0) { + messageViews.add(view); + size--; + } + } + } else { + break; + } + + } + } + PageImpl page = new PageImpl<>(messageViews, query.page(), total); + return new MessagePageTask(page, queueOffsetInfos); + } catch (Exception e) { + throw Throwables.propagate(e); + } finally { + consumer.shutdown(); + } + } + + private Page queryMessageByTaskPage(MessageQueryByPage query, List queueOffsetInfos) { + boolean isEnableAcl = !StringUtils.isEmpty(configure.getAccessKey()) && !StringUtils.isEmpty(configure.getSecretKey()); + RPCHook rpcHook = null; + if (isEnableAcl) { + rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey())); + } + DefaultMQPullConsumer consumer = buildDefaultMQPullConsumer(rpcHook, configure.isUseTLS()); + List messageViews = new ArrayList<>(); + + long offset = query.getPageNum() * query.getPageSize(); + + long total = 0; + try { + consumer.start(); + for (QueueOffsetInfo queueOffsetInfo : queueOffsetInfos) { + long start = queueOffsetInfo.getStart(); + long end = queueOffsetInfo.getEnd(); + queueOffsetInfo.setStartOffset(start); + queueOffsetInfo.setEndOffset(start); + total += end - start; + } + if (total <= offset) { + return Page.empty(); + } + long pageSize = total - offset > query.getPageSize() ? query.getPageSize() : total - offset; + + int next = moveStartOffset(queueOffsetInfos, query); + moveEndOffset(queueOffsetInfos, query, next); + + for (QueueOffsetInfo queueOffsetInfo : queueOffsetInfos) { + Long start = queueOffsetInfo.getStartOffset(); + Long end = queueOffsetInfo.getEndOffset(); + long size = Math.min(end - start, pageSize); + if (size == 0) { + continue; + } + + while (size > 0) { + PullResult pullResult = consumer.pull(queueOffsetInfo.getMessageQueues(), "*", start, 32); + if (pullResult.getPullStatus() == PullStatus.FOUND) { + List poll = pullResult.getMsgFoundList(); + if (poll.size() == 0) { + break; + } + List collect = poll.stream() + .map(MessageView::fromMessageExt).collect(Collectors.toList()); + + for (MessageView view : collect) { + if (size > 0) { + messageViews.add(view); + size--; + } + } + } else { + break; + } + + } + } + return new PageImpl<>(messageViews, query.page(), total); + } catch (Exception e) { + throw Throwables.propagate(e); + } finally { + consumer.shutdown(); + } + } + + private int moveStartOffset(List queueOffsets, MessageQueryByPage query) { + int size = queueOffsets.size(); + int next = 0; + long offset = query.getPageNum() * query.getPageSize(); + if (offset == 0) { + return next; + } + // sort by queueOffset size + List orderQueue = queueOffsets + .stream() + .sorted((o1, o2) -> { + long size1 = o1.getEnd() - o1.getStart(); + long size2 = o2.getEnd() - o2.getStart(); + if (size1 < size2) { + return -1; + } else if (size1 > size2) { + return 1; + } + return 0; + }).collect(Collectors.toList()); + + // Take the smallest one each time + for (int i = 0; i < size && offset >= (size - i); i++) { + long minSize = orderQueue.get(i).getEnd() - orderQueue.get(i).getStartOffset(); + if (minSize == 0) { + continue; + } + long reduce = minSize * (size - i); + if (reduce <= offset) { + offset -= reduce; + for (int j = i; j < size; j++) { + orderQueue.get(j).incStartOffset(minSize); + } + } else { + long addOffset = offset / (size - i); + offset -= addOffset * (size - i); + if (addOffset != 0) { + for (int j = i; j < size; j++) { + orderQueue.get(j).incStartOffset(addOffset); + } + } + } + } + for (QueueOffsetInfo info : orderQueue) { + QueueOffsetInfo queueOffsetInfo = queueOffsets.get(info.getIdx()); + queueOffsetInfo.setStartOffset(info.getStartOffset()); + queueOffsetInfo.setEndOffset(info.getEndOffset()); + } + + for (QueueOffsetInfo info : queueOffsets) { + if (offset == 0) { + break; + } + next = (next + 1) % size; + if (info.getStartOffset() < info.getEnd()) { + info.incStartOffset(); + --offset; + } + } + return next; + } + + private void moveEndOffset(List queueOffsets, MessageQueryByPage query, int next) { + int size = queueOffsets.size(); + for (int j = 0; j < query.getPageSize(); j++) { + QueueOffsetInfo nextQueueOffset = queueOffsets.get(next); + next = (next + 1) % size; + int start = next; + while (nextQueueOffset.getEndOffset() >= nextQueueOffset.getEnd()) { + nextQueueOffset = queueOffsets.get(next); + next = (next + 1) % size; + if (start == next) { + return; + } + } + nextQueueOffset.incEndOffset(); + } + } + + public DefaultMQPullConsumer buildDefaultMQPullConsumer(RPCHook rpcHook, boolean useTLS) { + DefaultMQPullConsumer consumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook); + consumer.setUseTLS(useTLS); + return consumer; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/MessageTraceServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/MessageTraceServiceImpl.java new file mode 100644 index 0000000..b3f3dc4 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/MessageTraceServiceImpl.java @@ -0,0 +1,243 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service.impl; + +import com.google.common.base.Function; +import com.google.common.collect.Lists; + +import com.google.common.base.Throwables; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.annotation.Resource; + +import com.google.common.collect.Maps; +import org.apache.commons.lang3.StringUtils; +import org.apache.rocketmq.client.trace.TraceType; +import org.apache.rocketmq.common.Pair; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.common.topic.TopicValidator; +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.dashboard.model.MessageTraceView; +import org.apache.rocketmq.dashboard.model.trace.ProducerNode; +import org.apache.rocketmq.dashboard.model.trace.MessageTraceGraph; +import org.apache.rocketmq.dashboard.model.trace.SubscriptionNode; +import org.apache.rocketmq.dashboard.model.trace.TraceNode; +import org.apache.rocketmq.dashboard.model.trace.MessageTraceStatusEnum; +import org.apache.rocketmq.dashboard.service.MessageTraceService; +import org.apache.rocketmq.tools.admin.MQAdminExt; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +@Service +public class MessageTraceServiceImpl implements MessageTraceService { + + private Logger logger = LoggerFactory.getLogger(MessageTraceServiceImpl.class); + + private final static int QUERY_MESSAGE_MAX_NUM = 64; + + private final static String UNKNOWN_GROUP_NAME = "%UNKNOWN_GROUP%"; + private final static int MESSAGE_TRACE_MISSING_VALUE = -1; + @Resource + private MQAdminExt mqAdminExt; + + @Resource + private RMQConfigure configure; + + @Override + public List queryMessageTraceKey(String key) { + String queryTopic = TopicValidator.RMQ_SYS_TRACE_TOPIC; + logger.info("query data topic name is:{}", queryTopic); + return queryMessageTraceByTopicAndKey(queryTopic, key); + } + + @Override + public List queryMessageTraceByTopicAndKey(String topic, String key) { + try { + List messageTraceViews = new ArrayList(); + List messageTraceList = mqAdminExt.queryMessage(topic, key, QUERY_MESSAGE_MAX_NUM, 0, System.currentTimeMillis()).getMessageList(); + for (MessageExt messageExt : messageTraceList) { + List messageTraceView = MessageTraceView.decodeFromTraceTransData(key, messageExt); + messageTraceViews.addAll(messageTraceView); + } + return messageTraceViews; + } catch (Exception err) { + throw Throwables.propagate(err); + } + } + + @Override + public MessageTraceGraph queryMessageTraceGraph(String key, String topic) { + if (StringUtils.isEmpty(topic)) { + topic = TopicValidator.RMQ_SYS_TRACE_TOPIC; + } + List messageTraceViews = queryMessageTraceByTopicAndKey(topic, key); + return buildMessageTraceGraph(messageTraceViews); + } + + private MessageTraceGraph buildMessageTraceGraph(List messageTraceViews) { + MessageTraceGraph messageTraceGraph = new MessageTraceGraph(); + messageTraceGraph.setMessageTraceViews(messageTraceViews); + if (CollectionUtils.isEmpty(messageTraceViews)) { + return messageTraceGraph; + } + ProducerNode producerNode = null; + List transactionNodeList = new ArrayList<>(); + Map> requestIdTracePairMap = Maps.newHashMap(); + for (MessageTraceView messageTraceView : messageTraceViews) { + switch (TraceType.valueOf(messageTraceView.getTraceType())) { + case Pub: + producerNode = buildMessageRoot(messageTraceView); + break; + case EndTransaction: + transactionNodeList.add(buildTransactionNode(messageTraceView)); + break; + case SubBefore: + case SubAfter: + putIntoMessageTraceViewGroupMap(messageTraceView, requestIdTracePairMap); + break; + default: + break; + } + } + if (producerNode != null) { + producerNode.setTransactionNodeList(sortTraceNodeListByBeginTimestamp(transactionNodeList)); + } + messageTraceGraph.setProducerNode(producerNode); + messageTraceGraph.setSubscriptionNodeList(buildSubscriptionNodeList(requestIdTracePairMap)); + return messageTraceGraph; + } + + private TraceNode buildTransactionNode(MessageTraceView messageTraceView) { + TraceNode transactionNode = buildTraceNode(messageTraceView); + transactionNode.setCostTime(MESSAGE_TRACE_MISSING_VALUE); + return transactionNode; + } + + private List buildSubscriptionNodeList( + Map> requestIdTracePairMap) { + Map> subscriptionTraceNodeMap = Maps.newHashMap(); + for (Pair traceNodePair : requestIdTracePairMap.values()) { + List traceNodeList = subscriptionTraceNodeMap + .computeIfAbsent(buildGroupName(traceNodePair), (o) -> Lists.newArrayList()); + traceNodeList.add(buildConsumeMessageTraceNode(traceNodePair)); + } + return subscriptionTraceNodeMap.entrySet().stream() + .map((Function>, SubscriptionNode>) subscriptionEntry -> { + List traceNodeList = subscriptionEntry.getValue(); + SubscriptionNode subscriptionNode = new SubscriptionNode(); + subscriptionNode.setSubscriptionGroup(subscriptionEntry.getKey()); + subscriptionNode.setConsumeNodeList(sortTraceNodeListByBeginTimestamp(traceNodeList)); + return subscriptionNode; + }).collect(Collectors.toList()); + } + + private E getTraceValue(Pair traceNodePair, Function function) { + if (traceNodePair.getObject1() != null) { + return function.apply(traceNodePair.getObject1()); + } + return function.apply(traceNodePair.getObject2()); + } + + private String buildGroupName(Pair traceNodePair) { + String groupName = getTraceValue(traceNodePair, MessageTraceView::getGroupName); + if (StringUtils.isNoneBlank(groupName)) { + return groupName; + } + return UNKNOWN_GROUP_NAME; + } + + private TraceNode buildConsumeMessageTraceNode(Pair pair) { + MessageTraceView subBeforeTrace = pair.getObject1(); + MessageTraceView subAfterTrace = pair.getObject2(); + TraceNode consumeNode = new TraceNode(); + consumeNode.setRequestId(getTraceValue(pair, MessageTraceView::getRequestId)); + consumeNode.setStoreHost(getTraceValue(pair, MessageTraceView::getStoreHost)); + consumeNode.setClientHost(getTraceValue(pair, MessageTraceView::getClientHost)); + if (subBeforeTrace != null) { + consumeNode.setRetryTimes(subBeforeTrace.getRetryTimes()); + consumeNode.setBeginTimestamp(subBeforeTrace.getTimeStamp()); + } else { + consumeNode.setRetryTimes(MESSAGE_TRACE_MISSING_VALUE); + consumeNode.setBeginTimestamp(MESSAGE_TRACE_MISSING_VALUE); + } + if (subAfterTrace != null) { + consumeNode.setCostTime(subAfterTrace.getCostTime()); + consumeNode.setStatus(subAfterTrace.getStatus()); + if (subAfterTrace.getTimeStamp() > 0) { + consumeNode.setEndTimestamp(subAfterTrace.getTimeStamp()); + } else { + if (subBeforeTrace != null) { + if (subAfterTrace.getCostTime() >= 0) { + consumeNode.setEndTimestamp(subBeforeTrace.getTimeStamp() + subAfterTrace.getCostTime()); + } else { + consumeNode.setEndTimestamp(subBeforeTrace.getTimeStamp()); + } + } else { + consumeNode.setEndTimestamp(MESSAGE_TRACE_MISSING_VALUE); + } + } + } else { + consumeNode.setCostTime(MESSAGE_TRACE_MISSING_VALUE); + consumeNode.setEndTimestamp(MESSAGE_TRACE_MISSING_VALUE); + consumeNode.setStatus(MessageTraceStatusEnum.UNKNOWN.getStatus()); + } + return consumeNode; + } + + private void putIntoMessageTraceViewGroupMap(MessageTraceView messageTraceView, + Map> messageTraceViewGroupMap) { + Pair messageTracePair = messageTraceViewGroupMap + .computeIfAbsent(messageTraceView.getRequestId(), (o) -> new Pair<>(null, null)); + switch (TraceType.valueOf(messageTraceView.getTraceType())) { + case SubBefore: + messageTracePair.setObject1(messageTraceView); + break; + case SubAfter: + messageTracePair.setObject2(messageTraceView); + break; + default: + break; + } + } + + private ProducerNode buildMessageRoot(MessageTraceView messageTraceView) { + ProducerNode root = new ProducerNode(); + BeanUtils.copyProperties(messageTraceView, root); + root.setTraceNode(buildTraceNode(messageTraceView)); + return root; + } + + private TraceNode buildTraceNode(MessageTraceView messageTraceView) { + TraceNode traceNode = new TraceNode(); + BeanUtils.copyProperties(messageTraceView, traceNode); + traceNode.setBeginTimestamp(messageTraceView.getTimeStamp()); + traceNode.setEndTimestamp(messageTraceView.getTimeStamp() + messageTraceView.getCostTime()); + return traceNode; + } + + private List sortTraceNodeListByBeginTimestamp(List traceNodeList) { + traceNodeList.sort((o1, o2) -> -Long.compare(o1.getBeginTimestamp(), o2.getBeginTimestamp())); + return traceNodeList; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/MonitorServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/MonitorServiceImpl.java new file mode 100644 index 0000000..ea4dd58 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/MonitorServiceImpl.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service.impl; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.base.Throwables; +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import org.apache.rocketmq.common.MixAll; +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.dashboard.model.ConsumerMonitorConfig; +import org.apache.rocketmq.dashboard.service.MonitorService; +import org.apache.rocketmq.dashboard.util.JsonUtil; +import org.springframework.stereotype.Service; + +@Service +public class MonitorServiceImpl implements MonitorService { + + + @Resource + private RMQConfigure configure; + + private Map configMap = new ConcurrentHashMap<>(); + + @Override + public boolean createOrUpdateConsumerMonitor(String name, ConsumerMonitorConfig config) { + configMap.put(name, config);// todo if write map success but write file fail + writeToFile(getConsumerMonitorConfigDataPath(), configMap); + return true; + } + + @Override + public Map queryConsumerMonitorConfig() { + return configMap; + } + + @Override + public ConsumerMonitorConfig queryConsumerMonitorConfigByGroupName(String consumeGroupName) { + return configMap.get(consumeGroupName); + } + + @Override + public boolean deleteConsumerMonitor(String consumeGroupName) { + configMap.remove(consumeGroupName); + writeToFile(getConsumerMonitorConfigDataPath(), configMap); + return true; + } + + //rocketmq.console.data.path/monitor/consumerMonitorConfig.json + private String getConsumerMonitorConfigDataPath() { + return configure.getRocketMqDashboardDataPath() + File.separatorChar + "monitor" + File.separatorChar + "consumerMonitorConfig.json"; + } + + private String getConsumerMonitorConfigDataPathBackUp() { + return getConsumerMonitorConfigDataPath() + ".bak"; + } + + private void writeToFile(String path, Object data) { + writeDataJsonToFile(path, JsonUtil.obj2String(data)); + } + + private void writeDataJsonToFile(String path, String dataStr) { + try { + MixAll.string2File(dataStr, path); + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } + + @PostConstruct + private void loadData() throws IOException { + String content = MixAll.file2String(getConsumerMonitorConfigDataPath()); + if (content == null) { + content = MixAll.file2String(getConsumerMonitorConfigDataPathBackUp()); + } + if (content == null) { + return; + } + configMap = JsonUtil.string2Obj(content, new TypeReference>() { + }); + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/OpsServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/OpsServiceImpl.java new file mode 100644 index 0000000..2d98e12 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/OpsServiceImpl.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service.impl; + +import com.google.common.base.Splitter; +import com.google.common.collect.Maps; +import java.util.List; +import java.util.Map; +import javax.annotation.Resource; +import org.apache.commons.pool2.impl.GenericObjectPool; +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.dashboard.service.AbstractCommonService; +import org.apache.rocketmq.dashboard.service.OpsService; +import org.apache.rocketmq.dashboard.service.checker.CheckerType; +import org.apache.rocketmq.dashboard.service.checker.RocketMqChecker; +import org.apache.rocketmq.tools.admin.MQAdminExt; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class OpsServiceImpl extends AbstractCommonService implements OpsService { + + @Resource + private RMQConfigure configure; + + @Autowired + private GenericObjectPool mqAdminExtPool; + + @Resource + private List rocketMqCheckerList; + + @Override + public Map homePageInfo() { + Map homePageInfoMap = Maps.newHashMap(); + homePageInfoMap.put("namesvrAddrList", Splitter.on(";").splitToList(configure.getNamesrvAddr())); + homePageInfoMap.put("useVIPChannel", Boolean.valueOf(configure.getIsVIPChannel())); + homePageInfoMap.put("useTLS", configure.isUseTLS()); + return homePageInfoMap; + } + + @Override + public void updateNameSvrAddrList(String nameSvrAddrList) { + configure.setNamesrvAddr(nameSvrAddrList); + // when update namesrvAddr, clean the mqAdminExt objects pool. + mqAdminExtPool.clear(); + } + + @Override + public String getNameSvrList() { + return configure.getNamesrvAddr(); + } + + @Override + public Map rocketMqStatusCheck() { + Map checkResultMap = Maps.newHashMap(); + for (RocketMqChecker rocketMqChecker : rocketMqCheckerList) { + checkResultMap.put(rocketMqChecker.checkerType(), rocketMqChecker.doCheck()); + } + return checkResultMap; + } + + @Override public boolean updateIsVIPChannel(String useVIPChannel) { + configure.setIsVIPChannel(useVIPChannel); + mqAdminExtPool.clear(); + return true; + } + + @Override + public boolean updateUseTLS(boolean useTLS) { + configure.setUseTLS(useTLS); + mqAdminExtPool.clear(); + return true; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/PermissionServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/PermissionServiceImpl.java new file mode 100644 index 0000000..9f69fd0 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/PermissionServiceImpl.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.service.impl; + +import com.alibaba.fastjson.JSONObject; +import java.io.FileReader; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Resource; +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.dashboard.exception.ServiceException; +import org.apache.rocketmq.dashboard.model.UserInfo; +import org.apache.rocketmq.dashboard.service.PermissionService; +import org.apache.rocketmq.dashboard.util.MatcherUtil; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.stereotype.Service; +import org.yaml.snakeyaml.Yaml; + +import static org.apache.rocketmq.dashboard.permisssion.UserRoleEnum.ADMIN; +import static org.apache.rocketmq.dashboard.permisssion.UserRoleEnum.ORDINARY; + +@Service +public class PermissionServiceImpl implements PermissionService, InitializingBean { + + @Resource + private RMQConfigure configure; + + private PermissionFileStore permissionFileStore; + + @Override + public void afterPropertiesSet() { + if (configure.isLoginRequired()) { + permissionFileStore = new PermissionFileStore(configure); + } + } + + @Override + public boolean checkUrlAvailable(UserInfo userInfo, String url) { + int type = userInfo.getUser().getType(); + // if it is admin, it could access all resources + if (type == ADMIN.getRoleType()) { + return true; + } + String loginUserRole = ORDINARY.getRoleName(); + Map> rolePerms = PermissionFileStore.rolePerms; + List perms = rolePerms.get(loginUserRole); + for (String perm : perms) { + if (MatcherUtil.match(perm, url)) { + return true; + } + } + return false; + } + + public static class PermissionFileStore extends AbstractFileStore { + private static final String FILE_NAME = "role-permission.yml"; + + private static Map/**accessUrls**/> rolePerms = new ConcurrentHashMap<>(); + + public PermissionFileStore(RMQConfigure configure) { + super(configure, FILE_NAME); + } + + @Override + public void load(InputStream inputStream) { + Yaml yaml = new Yaml(); + JSONObject rolePermsData = null; + try { + if (inputStream == null) { + rolePermsData = yaml.loadAs(new FileReader(filePath), JSONObject.class); + } else { + rolePermsData = yaml.loadAs(inputStream, JSONObject.class); + } + } catch (Exception e) { + log.error("load user-permission.yml failed", e); + throw new ServiceException(0, String.format("Failed to load rolePermission property file: %s", filePath)); + } + rolePerms.clear(); + rolePerms.putAll(rolePermsData.getObject("rolePerms", Map.class)); + } + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/ProducerServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/ProducerServiceImpl.java new file mode 100644 index 0000000..3ce408b --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/ProducerServiceImpl.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service.impl; + +import com.google.common.base.Throwables; +import javax.annotation.Resource; +import org.apache.rocketmq.common.protocol.body.ProducerConnection; +import org.apache.rocketmq.dashboard.service.ProducerService; +import org.apache.rocketmq.tools.admin.MQAdminExt; +import org.springframework.stereotype.Service; + +@Service +public class ProducerServiceImpl implements ProducerService { + @Resource + private MQAdminExt mqAdminExt; + + @Override + public ProducerConnection getProducerConnection(String producerGroup, String topic) { + try { + return mqAdminExt.examineProducerConnectionInfo(producerGroup, topic); + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/TopicServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/TopicServiceImpl.java new file mode 100644 index 0000000..b6197b7 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/TopicServiceImpl.java @@ -0,0 +1,287 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service.impl; + +import com.google.common.base.Throwables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; +import org.apache.rocketmq.acl.common.AclClientRPCHook; +import org.apache.rocketmq.acl.common.SessionCredentials; +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.client.trace.TraceContext; +import org.apache.rocketmq.client.trace.TraceDispatcher; +import org.apache.rocketmq.common.MixAll; +import org.apache.rocketmq.common.TopicConfig; +import org.apache.rocketmq.common.admin.TopicStatsTable; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.common.protocol.body.ClusterInfo; +import org.apache.rocketmq.common.protocol.body.GroupList; +import org.apache.rocketmq.common.protocol.body.TopicList; +import org.apache.rocketmq.common.protocol.route.BrokerData; +import org.apache.rocketmq.common.protocol.route.TopicRouteData; +import org.apache.rocketmq.common.topic.TopicValidator; +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.dashboard.model.request.SendTopicMessageRequest; +import org.apache.rocketmq.dashboard.model.request.TopicConfigInfo; +import org.apache.rocketmq.dashboard.service.AbstractCommonService; +import org.apache.rocketmq.dashboard.service.TopicService; +import org.apache.rocketmq.remoting.RPCHook; +import org.apache.rocketmq.tools.command.CommandUtil; +import org.joor.Reflect; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ArrayBlockingQueue; + +@Service +public class TopicServiceImpl extends AbstractCommonService implements TopicService { + + @Autowired + private RMQConfigure configure; + + @Override + public TopicList fetchAllTopicList(boolean skipSysProcess, boolean skipRetryAndDlq) { + try { + TopicList allTopics = mqAdminExt.fetchAllTopicList(); + TopicList sysTopics = getSystemTopicList(); + Set topics = + allTopics.getTopicList().stream().map(topic -> { + if (!skipSysProcess && sysTopics.getTopicList().contains(topic)) { + topic = String.format("%s%s", "%SYS%", topic); + } + return topic; + }).filter(topic -> { + if (skipRetryAndDlq) { + return !(topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX) + || topic.startsWith(MixAll.DLQ_GROUP_TOPIC_PREFIX)); + } + return true; + }).collect(Collectors.toSet()); + allTopics.getTopicList().clear(); + allTopics.getTopicList().addAll(topics); + return allTopics; + } catch (Exception e) { + throw Throwables.propagate(e); + } + } + + @Override + public TopicStatsTable stats(String topic) { + try { + return mqAdminExt.examineTopicStats(topic); + } catch (Exception e) { + throw Throwables.propagate(e); + } + } + + @Override + public TopicRouteData route(String topic) { + try { + return mqAdminExt.examineTopicRouteInfo(topic); + } catch (Exception ex) { + throw Throwables.propagate(ex); + } + } + + @Override + public GroupList queryTopicConsumerInfo(String topic) { + try { + return mqAdminExt.queryTopicConsumeByWho(topic); + } catch (Exception e) { + throw Throwables.propagate(e); + } + } + + @Override + public void createOrUpdate(TopicConfigInfo topicCreateOrUpdateRequest) { + TopicConfig topicConfig = new TopicConfig(); + BeanUtils.copyProperties(topicCreateOrUpdateRequest, topicConfig); + try { + ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo(); + for (String brokerName : changeToBrokerNameSet(clusterInfo.getClusterAddrTable(), + topicCreateOrUpdateRequest.getClusterNameList(), topicCreateOrUpdateRequest.getBrokerNameList())) { + mqAdminExt.createAndUpdateTopicConfig(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), topicConfig); + } + } catch (Exception err) { + throw Throwables.propagate(err); + } + } + + @Override + public TopicConfig examineTopicConfig(String topic, String brokerName) { + ClusterInfo clusterInfo = null; + try { + clusterInfo = mqAdminExt.examineBrokerClusterInfo(); + } catch (Exception e) { + throw Throwables.propagate(e); + } + return mqAdminExt.examineTopicConfig(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), topic); + } + + @Override + public List examineTopicConfig(String topic) { + List topicConfigInfoList = Lists.newArrayList(); + TopicRouteData topicRouteData = route(topic); + for (BrokerData brokerData : topicRouteData.getBrokerDatas()) { + TopicConfigInfo topicConfigInfo = new TopicConfigInfo(); + TopicConfig topicConfig = examineTopicConfig(topic, brokerData.getBrokerName()); + BeanUtils.copyProperties(topicConfig, topicConfigInfo); + topicConfigInfo.setBrokerNameList(Lists.newArrayList(brokerData.getBrokerName())); + topicConfigInfoList.add(topicConfigInfo); + } + return topicConfigInfoList; + } + + @Override + public boolean deleteTopic(String topic, String clusterName) { + try { + if (StringUtils.isBlank(clusterName)) { + return deleteTopic(topic); + } + Set masterSet = CommandUtil.fetchMasterAddrByClusterName(mqAdminExt, clusterName); + mqAdminExt.deleteTopicInBroker(masterSet, topic); + Set nameServerSet = null; + if (StringUtils.isNotBlank(configure.getNamesrvAddr())) { + String[] ns = configure.getNamesrvAddr().split(";"); + nameServerSet = new HashSet(Arrays.asList(ns)); + } + mqAdminExt.deleteTopicInNameServer(nameServerSet, topic); + } catch (Exception err) { + throw Throwables.propagate(err); + } + return true; + } + + @Override + public boolean deleteTopic(String topic) { + ClusterInfo clusterInfo = null; + try { + clusterInfo = mqAdminExt.examineBrokerClusterInfo(); + } catch (Exception err) { + throw Throwables.propagate(err); + } + for (String clusterName : clusterInfo.getClusterAddrTable().keySet()) { + deleteTopic(topic, clusterName); + } + return true; + } + + @Override + public boolean deleteTopicInBroker(String brokerName, String topic) { + + try { + ClusterInfo clusterInfo = null; + try { + clusterInfo = mqAdminExt.examineBrokerClusterInfo(); + } catch (Exception e) { + throw Throwables.propagate(e); + } + mqAdminExt.deleteTopicInBroker(Sets.newHashSet(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr()), topic); + } catch (Exception e) { + throw Throwables.propagate(e); + } + return true; + } + + public DefaultMQProducer buildDefaultMQProducer(String producerGroup, RPCHook rpcHook) { + return buildDefaultMQProducer(producerGroup, rpcHook, false); + } + + public DefaultMQProducer buildDefaultMQProducer(String producerGroup, RPCHook rpcHook, boolean traceEnabled) { + DefaultMQProducer defaultMQProducer = new DefaultMQProducer(producerGroup, rpcHook, traceEnabled, TopicValidator.RMQ_SYS_TRACE_TOPIC); + defaultMQProducer.setUseTLS(configure.isUseTLS()); + return defaultMQProducer; + } + + private TopicList getSystemTopicList() { + RPCHook rpcHook = null; + boolean isEnableAcl = !StringUtils.isEmpty(configure.getAccessKey()) && !StringUtils.isEmpty(configure.getSecretKey()); + if (isEnableAcl) { + rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey())); + } + DefaultMQProducer producer = buildDefaultMQProducer(MixAll.SELF_TEST_PRODUCER_GROUP, rpcHook); + producer.setInstanceName(String.valueOf(System.currentTimeMillis())); + producer.setNamesrvAddr(configure.getNamesrvAddr()); + + try { + producer.start(); + return producer.getDefaultMQProducerImpl().getmQClientFactory().getMQClientAPIImpl().getSystemTopicList(20000L); + } catch (Exception e) { + throw Throwables.propagate(e); + } finally { + producer.shutdown(); + } + } + + @Override + public SendResult sendTopicMessageRequest(SendTopicMessageRequest sendTopicMessageRequest) { + DefaultMQProducer producer = null; + AclClientRPCHook rpcHook = null; + if (configure.isACLEnabled()) { + rpcHook = new AclClientRPCHook(new SessionCredentials( + configure.getAccessKey(), + configure.getSecretKey() + )); + } + producer = buildDefaultMQProducer(MixAll.SELF_TEST_PRODUCER_GROUP, rpcHook, sendTopicMessageRequest.isTraceEnabled()); + producer.setInstanceName(String.valueOf(System.currentTimeMillis())); + producer.setNamesrvAddr(configure.getNamesrvAddr()); + try { + producer.start(); + Message msg = new Message(sendTopicMessageRequest.getTopic(), + sendTopicMessageRequest.getTag(), + sendTopicMessageRequest.getKey(), + sendTopicMessageRequest.getMessageBody().getBytes() + ); + return producer.send(msg); + } catch (Exception e) { + throw Throwables.propagate(e); + } finally { + waitSendTraceFinish(producer, sendTopicMessageRequest.isTraceEnabled()); + producer.shutdown(); + } + } + + private void waitSendTraceFinish(DefaultMQProducer producer, boolean traceEnabled) { + if (!traceEnabled) { + return; + } + try { + TraceDispatcher traceDispatcher = Reflect.on(producer).field("traceDispatcher").get(); + if (traceDispatcher != null) { + ArrayBlockingQueue traceContextQueue = Reflect.on(traceDispatcher).field("traceContextQueue").get(); + while (traceContextQueue.size() > 0) { + Thread.sleep(1); + } + } + // wait another 150ms until async request send finish + // after new RocketMQ version released, this logic can be removed + // https://github.com/apache/rocketmq/pull/2989 + Thread.sleep(150); + } catch (Exception ignore) { + } + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/service/impl/UserServiceImpl.java b/src/main/java/org/apache/rocketmq/dashboard/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..5e628e4 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/service/impl/UserServiceImpl.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.service.impl; + +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.dashboard.exception.ServiceException; +import org.apache.rocketmq.dashboard.model.User; +import org.apache.rocketmq.dashboard.service.UserService; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.validation.constraints.NotNull; +import java.io.FileReader; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +@Service +public class UserServiceImpl implements UserService, InitializingBean { + @Resource + private RMQConfigure configure; + + private FileBasedUserInfoStore fileBasedUserInfoStore; + + @Override + public User queryByName(String name) { + return fileBasedUserInfoStore.queryByName(name); + } + + @Override + public User queryByUsernameAndPassword(String username, String password) { + return fileBasedUserInfoStore.queryByUsernameAndPassword(username, password); + } + + @Override + public void afterPropertiesSet() throws Exception { + if (configure.isLoginRequired()) { + fileBasedUserInfoStore = new FileBasedUserInfoStore(configure); + } + } + + public static class FileBasedUserInfoStore extends AbstractFileStore { + private static final String FILE_NAME = "users.properties"; + + private static Map userMap = new ConcurrentHashMap<>(); + + public FileBasedUserInfoStore(RMQConfigure configure) { + super(configure, FILE_NAME); + } + + @Override + public void load(InputStream inputStream) { + Properties prop = new Properties(); + try { + if (inputStream == null) { + prop.load(new FileReader(filePath)); + } else { + prop.load(inputStream); + } + } catch (Exception e) { + log.error("load user.properties failed", e); + throw new ServiceException(0, String.format("Failed to load loginUserInfo property file: %s", filePath)); + } + + Map loadUserMap = new HashMap<>(); + String[] arrs; + int role; + for (String key : prop.stringPropertyNames()) { + String v = prop.getProperty(key); + if (v == null) + continue; + arrs = v.split(",", 2); + if (arrs.length == 0) { + continue; + } else if (arrs.length == 1) { + role = 0; + } else { + role = Integer.parseInt(arrs[1].trim()); + } + + loadUserMap.put(key, new User(key, arrs[0].trim(), role)); + } + + userMap.clear(); + userMap.putAll(loadUserMap); + } + + public User queryByName(String name) { + return userMap.get(name); + } + + public User queryByUsernameAndPassword(@NotNull String username, @NotNull String password) { + User user = queryByName(username); + if (user != null && password.equals(user.getPassword())) { + return user.cloneOne(); + } + return null; + } + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/support/GlobalExceptionHandler.java b/src/main/java/org/apache/rocketmq/dashboard/support/GlobalExceptionHandler.java new file mode 100644 index 0000000..c7d915e --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/support/GlobalExceptionHandler.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.support; + +import javax.servlet.http.HttpServletRequest; +import org.apache.rocketmq.dashboard.exception.ServiceException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +@ControllerAdvice(basePackages = "org.apache.rocketmq.dashboard") +public class GlobalExceptionHandler { + private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + @ExceptionHandler(value = Exception.class) + @ResponseBody + public JsonResult jsonErrorHandler(HttpServletRequest req, Exception ex) throws Exception { + JsonResult value = null; + if (ex != null) { + if (ex instanceof ServiceException) { + logger.error("Occur service exception: {}", ex.getMessage()); + value = new JsonResult(((ServiceException) ex).getCode(), ex.getMessage()); + } + else { + logger.error("op=global_exception_handler_print_error", ex); + value = new JsonResult(-1, ex.getMessage() == null ? ex.toString() : ex.getMessage()); + } + } + return value; + } +} \ No newline at end of file diff --git a/src/main/java/org/apache/rocketmq/dashboard/support/GlobalRestfulResponseBodyAdvice.java b/src/main/java/org/apache/rocketmq/dashboard/support/GlobalRestfulResponseBodyAdvice.java new file mode 100644 index 0000000..fd27528 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/support/GlobalRestfulResponseBodyAdvice.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.support; + +import java.lang.annotation.Annotation; +import org.apache.rocketmq.dashboard.aspect.admin.annotation.OriginalControllerReturnValue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +@ControllerAdvice(basePackages = "org.apache.rocketmq.dashboard") +public class GlobalRestfulResponseBodyAdvice implements ResponseBodyAdvice { + + private Logger logger = LoggerFactory.getLogger(GlobalRestfulResponseBodyAdvice.class); + + @Override + public Object beforeBodyWrite( + Object obj, MethodParameter methodParameter, MediaType mediaType, + Class> converterType, + ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { + Annotation originalControllerReturnValue = methodParameter.getMethodAnnotation(OriginalControllerReturnValue.class); + if (originalControllerReturnValue != null) { + return obj; + } + JsonResult value; + if (obj instanceof JsonResult) { + value = (JsonResult)obj; + } + else { + value = new JsonResult(obj); + } + return value; + } + + @Override + public boolean supports(MethodParameter returnType, Class> converterType) { + + return true; + } + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/support/JsonResult.java b/src/main/java/org/apache/rocketmq/dashboard/support/JsonResult.java new file mode 100644 index 0000000..d78c6b8 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/support/JsonResult.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.support; + +public class JsonResult { + private int status = 0; + private T data; + private String errMsg; + + public JsonResult(T data) { + this.data = data; + } + + public JsonResult(int status, String errMsg) { + this.status = status; + this.errMsg = errMsg; + } + + public JsonResult(int status, T data, String errMsg) { + this.status = status; + this.data = data; + this.errMsg = errMsg; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public String getErrMsg() { + return errMsg; + } + + public void setErrMsg(String errMsg) { + this.errMsg = errMsg; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/task/DashboardCollectTask.java b/src/main/java/org/apache/rocketmq/dashboard/task/DashboardCollectTask.java new file mode 100644 index 0000000..c09568d --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/task/DashboardCollectTask.java @@ -0,0 +1,337 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.task; + +import com.google.common.base.Stopwatch; +import org.apache.rocketmq.common.protocol.body.ClusterInfo; +import org.apache.rocketmq.common.protocol.body.GroupList; +import org.apache.rocketmq.common.protocol.body.KVTable; +import org.apache.rocketmq.common.protocol.body.TopicList; +import org.apache.rocketmq.common.protocol.route.BrokerData; +import org.apache.rocketmq.common.protocol.route.TopicRouteData; +import org.apache.rocketmq.common.topic.TopicValidator; +import org.apache.rocketmq.store.stats.BrokerStatsManager; +import org.apache.rocketmq.tools.admin.MQAdminExt; +import org.apache.rocketmq.tools.command.stats.StatsAllSubCommand; +import com.google.common.base.Throwables; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.io.Files; +import java.io.File; +import java.io.IOException; +import java.math.BigDecimal; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import javax.annotation.Resource; +import org.apache.rocketmq.common.MixAll; +import org.apache.rocketmq.common.protocol.body.BrokerStatsData; +import org.apache.rocketmq.dashboard.config.RMQConfigure; +import org.apache.rocketmq.dashboard.service.DashboardCollectService; +import org.apache.rocketmq.dashboard.util.JsonUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +public class DashboardCollectTask { + private Date currentDate = new Date(); + @Resource + private MQAdminExt mqAdminExt; + @Resource + private RMQConfigure rmqConfigure; + + @Resource + private DashboardCollectService dashboardCollectService; + + private final static Logger log = LoggerFactory.getLogger(DashboardCollectTask.class); + + @Scheduled(cron = "30 0/1 * * * ?") + public void collectTopic() { + if (!rmqConfigure.isEnableDashBoardCollect()) { + return; + } + + Date date = new Date(); + Stopwatch stopwatch = Stopwatch.createUnstarted(); + try { + TopicList topicList = mqAdminExt.fetchAllTopicList(); + Set topicSet = topicList.getTopicList(); + this.addSystemTopic(); + for (String topic : topicSet) { + if (topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX) + || topic.startsWith(MixAll.DLQ_GROUP_TOPIC_PREFIX) + || TopicValidator.isSystemTopic(topic)) { + continue; + } + TopicRouteData topicRouteData = mqAdminExt.examineTopicRouteInfo(topic); + + GroupList groupList = mqAdminExt.queryTopicConsumeByWho(topic); + + double inTPS = 0; + + long inMsgCntToday = 0; + + double outTPS = 0; + + long outMsgCntToday = 0; + + for (BrokerData bd : topicRouteData.getBrokerDatas()) { + String masterAddr = bd.getBrokerAddrs().get(MixAll.MASTER_ID); + if (masterAddr != null) { + try { + stopwatch.start(); + log.info("start time: {}", stopwatch.toString()); + BrokerStatsData bsd = mqAdminExt.viewBrokerStatsData(masterAddr, BrokerStatsManager.TOPIC_PUT_NUMS, topic); + stopwatch.stop(); + log.info("stop time : {}", stopwatch.toString()); + + inTPS += bsd.getStatsMinute().getTps(); + inMsgCntToday += StatsAllSubCommand.compute24HourSum(bsd); + } + catch (Exception e) { + stopwatch.reset(); + log.warn("Exception caught: mqAdminExt get broker stats data TOPIC_PUT_NUMS failed"); + log.warn("Response [{}] ", e.getMessage()); + } + } + } + + if (groupList != null && !groupList.getGroupList().isEmpty()) { + + for (String group : groupList.getGroupList()) { + for (BrokerData bd : topicRouteData.getBrokerDatas()) { + String masterAddr = bd.getBrokerAddrs().get(MixAll.MASTER_ID); + if (masterAddr != null) { + try { + String statsKey = String.format("%s@%s", topic, group); + BrokerStatsData bsd = mqAdminExt.viewBrokerStatsData(masterAddr, BrokerStatsManager.GROUP_GET_NUMS, statsKey); + outTPS += bsd.getStatsMinute().getTps(); + outMsgCntToday += StatsAllSubCommand.compute24HourSum(bsd); + } + catch (Exception e) { + log.warn("Exception caught: mqAdminExt get broker stats data GROUP_GET_NUMS failed"); + log.warn("Response [{}] ", e.getMessage()); + } + } + } + } + } + + List list; + try { + list = dashboardCollectService.getTopicMap().get(topic); + } + catch (ExecutionException e) { + throw Throwables.propagate(e); + } + if (null == list) { + list = Lists.newArrayList(); + } + + list.add(date.getTime() + "," + new BigDecimal(inTPS).setScale(5, BigDecimal.ROUND_HALF_UP) + "," + inMsgCntToday + "," + new BigDecimal(outTPS).setScale(5, BigDecimal.ROUND_HALF_UP) + "," + outMsgCntToday); + dashboardCollectService.getTopicMap().put(topic, list); + + } + + log.debug("Topic Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getTopicMap().asMap())); + } + catch (Exception err) { + throw Throwables.propagate(err); + } + } + + @Scheduled(cron = "0 0/1 * * * ?") + public void collectBroker() { + if (!rmqConfigure.isEnableDashBoardCollect()) { + return; + } + try { + Date date = new Date(); + ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo(); + Set> clusterEntries = clusterInfo.getBrokerAddrTable().entrySet(); + + Map addresses = Maps.newHashMap(); + for (Map.Entry clusterEntry : clusterEntries) { + HashMap addrs = clusterEntry.getValue().getBrokerAddrs(); + Set> addrsEntries = addrs.entrySet(); + for (Map.Entry addrEntry : addrsEntries) { + addresses.put(addrEntry.getValue(), clusterEntry.getKey() + ":" + addrEntry.getKey()); + } + } + Set> entries = addresses.entrySet(); + for (Map.Entry entry : entries) { + List list = dashboardCollectService.getBrokerMap().get(entry.getValue()); + if (null == list) { + list = Lists.newArrayList(); + } + KVTable kvTable = fetchBrokerRuntimeStats(entry.getKey(), 3); + if (kvTable == null) { + continue; + } + String[] tpsArray = kvTable.getTable().get("getTotalTps").split(" "); + BigDecimal totalTps = new BigDecimal(0); + for (String tps : tpsArray) { + totalTps = totalTps.add(new BigDecimal(tps)); + } + BigDecimal averageTps = totalTps.divide(new BigDecimal(tpsArray.length), 5, BigDecimal.ROUND_HALF_UP); + list.add(date.getTime() + "," + averageTps.toString()); + dashboardCollectService.getBrokerMap().put(entry.getValue(), list); + } + log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap())); + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } + + private KVTable fetchBrokerRuntimeStats(String brokerAddr, Integer retryTime) { + if (retryTime == 0) { + return null; + } + try { + return mqAdminExt.fetchBrokerRuntimeStats(brokerAddr); + } + catch (Exception e) { + try { + Thread.sleep(1000); + } + catch (InterruptedException e1) { + throw Throwables.propagate(e1); + } + fetchBrokerRuntimeStats(brokerAddr, retryTime - 1); + throw Throwables.propagate(e); + } + } + + @Scheduled(cron = "0/5 * * * * ?") + public void saveData() { + if (!rmqConfigure.isEnableDashBoardCollect()) { + return; + } + //one day refresh cache one time + String dataLocationPath = rmqConfigure.getDashboardCollectData(); + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + String nowDateStr = format.format(new Date()); + String currentDateStr = format.format(currentDate); + if (!currentDateStr.equals(nowDateStr)) { + dashboardCollectService.getBrokerMap().invalidateAll(); + dashboardCollectService.getTopicMap().invalidateAll(); + currentDate = new Date(); + } + File brokerFile = new File(dataLocationPath + nowDateStr + ".json"); + File topicFile = new File(dataLocationPath + nowDateStr + "_topic" + ".json"); + try { + Map> brokerFileMap; + Map> topicFileMap; + if (brokerFile.exists()) { + brokerFileMap = dashboardCollectService.jsonDataFile2map(brokerFile); + } + else { + brokerFileMap = Maps.newHashMap(); + Files.createParentDirs(brokerFile); + } + + if (topicFile.exists()) { + topicFileMap = dashboardCollectService.jsonDataFile2map(topicFile); + } + else { + topicFileMap = Maps.newHashMap(); + Files.createParentDirs(topicFile); + } + + brokerFile.createNewFile(); + topicFile.createNewFile(); + + writeFile(dashboardCollectService.getBrokerMap(), brokerFileMap, brokerFile); + writeFile(dashboardCollectService.getTopicMap(), topicFileMap, topicFile); + log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap())); + log.debug("Topic Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getTopicMap().asMap())); + + } + catch (IOException e) { + throw Throwables.propagate(e); + } + } + + private void writeFile(LoadingCache> map, Map> fileMap, + File file) throws IOException { + Map> newMap = map.asMap(); + Map> resultMap = Maps.newHashMap(); + if (fileMap.size() == 0) { + resultMap = newMap; + } + else { + for (Map.Entry> entry : fileMap.entrySet()) { + List oldList = entry.getValue(); + List newList = newMap.get(entry.getKey()); + resultMap.put(entry.getKey(), appendData(newList, oldList)); + if (newList == null || newList.size() == 0) { + map.put(entry.getKey(), appendData(newList, oldList)); + } + } + + for (Map.Entry> entry : newMap.entrySet()) { + List oldList = fileMap.get(entry.getKey()); + if (oldList == null || oldList.size() == 0) { + resultMap.put(entry.getKey(), entry.getValue()); + } + } + } + Files.write(JsonUtil.obj2String(resultMap).getBytes(), file); + } + + private List appendData(List newTpsList, List oldTpsList) { + List result = Lists.newArrayList(); + if (newTpsList == null || newTpsList.size() == 0) { + return oldTpsList; + } + if (oldTpsList == null || oldTpsList.size() == 0) { + return newTpsList; + } + String oldLastTps = oldTpsList.get(oldTpsList.size() - 1); + Long oldLastTimestamp = Long.parseLong(oldLastTps.split(",")[0]); + String newFirstTps = newTpsList.get(0); + Long newFirstTimestamp = Long.parseLong(newFirstTps.split(",")[0]); + if (oldLastTimestamp.longValue() < newFirstTimestamp.longValue()) { + result.addAll(oldTpsList); + result.addAll(newTpsList); + return result; + } + return newTpsList; + } + + private void addSystemTopic() throws Exception { + ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo(); + HashMap> clusterTable = clusterInfo.getClusterAddrTable(); + for (Map.Entry> entry : clusterTable.entrySet()) { + String clusterName = entry.getKey(); + TopicValidator.addSystemTopic(clusterName); + Set brokerNames = entry.getValue(); + for (String brokerName : brokerNames) { + TopicValidator.addSystemTopic(brokerName); + } + } + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/task/MonitorTask.java b/src/main/java/org/apache/rocketmq/dashboard/task/MonitorTask.java new file mode 100644 index 0000000..710929b --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/task/MonitorTask.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.task; + +import java.util.Map; +import javax.annotation.Resource; +import org.apache.rocketmq.dashboard.model.ConsumerMonitorConfig; +import org.apache.rocketmq.dashboard.model.GroupConsumeInfo; +import org.apache.rocketmq.dashboard.service.ConsumerService; +import org.apache.rocketmq.dashboard.service.MonitorService; +import org.apache.rocketmq.dashboard.util.JsonUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +public class MonitorTask { + private Logger logger = LoggerFactory.getLogger(MonitorTask.class); + + @Resource + private MonitorService monitorService; + + @Resource + private ConsumerService consumerService; + +// @Scheduled(cron = "* * * * * ?") + public void scanProblemConsumeGroup() { + for (Map.Entry configEntry : monitorService.queryConsumerMonitorConfig().entrySet()) { + GroupConsumeInfo consumeInfo = consumerService.queryGroup(configEntry.getKey()); + if (consumeInfo.getCount() < configEntry.getValue().getMinCount() || consumeInfo.getDiffTotal() > configEntry.getValue().getMaxDiffTotal()) { + logger.info("op=look consumeInfo {}", JsonUtil.obj2String(consumeInfo)); // notify the alert system + } + } + } + +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/util/JsonUtil.java b/src/main/java/org/apache/rocketmq/dashboard/util/JsonUtil.java new file mode 100644 index 0000000..6100275 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/util/JsonUtil.java @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.util; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; +import com.google.common.base.Strings; +import com.google.common.base.Throwables; +import java.io.IOException; +import java.io.Writer; +import java.text.SimpleDateFormat; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@SuppressWarnings("unchecked") +public class JsonUtil { + + private static Logger logger = LoggerFactory.getLogger(JsonUtil.class); + private static ObjectMapper objectMapper = new ObjectMapper(); + + private JsonUtil() { + } + + static { + objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true); + objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + objectMapper.setFilters(new SimpleFilterProvider().setFailOnUnknownId(false)); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); + } + + public static void writeValue(Writer writer, Object obj) { + try { + objectMapper.writeValue(writer, obj); + } + catch (IOException e) { + Throwables.propagateIfPossible(e); + } + } + + public static String obj2String(T src) { + if (src == null) { + return null; + } + + try { + return src instanceof String ? (String)src : objectMapper.writeValueAsString(src); + } + catch (Exception e) { + logger.error("Parse Object to String error src=" + src, e); + return null; + } + } + + public static byte[] obj2Byte(T src) { + if (src == null) { + return null; + } + + try { + return src instanceof byte[] ? (byte[])src : objectMapper.writeValueAsBytes(src); + } + catch (Exception e) { + logger.error("Parse Object to byte[] error", e); + return null; + } + } + + public static T string2Obj(String str, Class clazz) { + if (Strings.isNullOrEmpty(str) || clazz == null) { + return null; + } + str = escapesSpecialChar(str); + try { + return clazz.equals(String.class) ? (T)str : objectMapper.readValue(str, clazz); + } + catch (Exception e) { + logger.error("Parse String to Object error\nString: {}\nClass: {}\nError: {}", str, clazz.getName(), e); + return null; + } + } + + public static T byte2Obj(byte[] bytes, Class clazz) { + if (bytes == null || clazz == null) { + return null; + } + try { + return clazz.equals(byte[].class) ? (T)bytes : objectMapper.readValue(bytes, clazz); + } + catch (Exception e) { + logger.error("Parse byte[] to Object error\nbyte[]: {}\nClass: {}\nError: {}", bytes, clazz.getName(), e); + return null; + } + } + + public static T string2Obj(String str, TypeReference typeReference) { + if (Strings.isNullOrEmpty(str) || typeReference == null) { + return null; + } + str = escapesSpecialChar(str); + try { + return (T)(typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference)); + } + catch (Exception e) { + logger.error("Parse String to Object error\nString: {}\nTypeReference: {}\nError: {}", str, + typeReference.getType(), e); + return null; + } + } + + public static T byte2Obj(byte[] bytes, TypeReference typeReference) { + if (bytes == null || typeReference == null) { + return null; + } + try { + return (T)(typeReference.getType().equals(byte[].class) ? bytes : objectMapper.readValue(bytes, + typeReference)); + } + catch (Exception e) { + logger.error("Parse byte[] to Object error\nbyte[]: {}\nTypeReference: {}\nError: {}", bytes, + typeReference.getType(), e); + return null; + } + } + + public static T map2Obj(Map map, Class clazz) { + String str = obj2String(map); + return string2Obj(str, clazz); + } + + private static String escapesSpecialChar(String str) { + return str.replace("\n", "\\n").replace("\r", "\\r"); + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/util/MatcherUtil.java b/src/main/java/org/apache/rocketmq/dashboard/util/MatcherUtil.java new file mode 100644 index 0000000..b7a8cc9 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/util/MatcherUtil.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.dashboard.util; + +import java.util.regex.Pattern; + +public class MatcherUtil { + + public static boolean match(String accessUrl, String reqPath) { + String regPath = getRegPath(accessUrl); + return Pattern.compile(regPath).matcher(reqPath).matches(); + } + + private static String getRegPath(String path) { + char[] chars = path.toCharArray(); + int len = chars.length; + StringBuilder sb = new StringBuilder(); + boolean preX = false; + for (int i = 0; i < len; i++) { + if (chars[i] == '*') { + if (preX) { + sb.append(".*"); + preX = false; + } else if (i + 1 == len) { + sb.append("[^/]*"); + } else { + preX = true; + continue; + } + } else { + if (preX) { + sb.append("[^/]*"); + preX = false; + } + if (chars[i] == '?') { + sb.append('.'); + } else { + sb.append(chars[i]); + } + } + } + return sb.toString(); + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/util/MsgTraceDecodeUtil.java b/src/main/java/org/apache/rocketmq/dashboard/util/MsgTraceDecodeUtil.java new file mode 100644 index 0000000..db724b8 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/util/MsgTraceDecodeUtil.java @@ -0,0 +1,172 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.util; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.rocketmq.client.producer.LocalTransactionState; +import org.apache.rocketmq.client.trace.TraceBean; +import org.apache.rocketmq.client.trace.TraceConstants; +import org.apache.rocketmq.client.trace.TraceContext; +import org.apache.rocketmq.client.trace.TraceType; +import org.apache.rocketmq.common.message.MessageType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.rocketmq.client.trace.TraceType.Pub; + +public class MsgTraceDecodeUtil { + private final static Logger log = LoggerFactory.getLogger(MsgTraceDecodeUtil.class); + + private static final int TRACE_MSG_PUB_V1_LEN = 12; + private static final int TRACE_MSG_PUB_V2_LEN = 13; + private static final int TRACE_MSG_PUB_V3_LEN = 14; + private static final int TRACE_MSG_PUB_V4_LEN = 15; + + private static final int TRACE_MSG_SUBAFTER_V1_LEN = 6; + private static final int TRACE_MSG_SUBAFTER_V2_LEN = 7; + private static final int TRACE_MSG_SUBAFTER_V3_LEN = 9; + + public static List decoderFromTraceDataString(String traceData) { + List resList = new ArrayList(); + if (traceData == null || traceData.length() <= 0) { + return resList; + } + String[] contextList = traceData.split(String.valueOf(TraceConstants.FIELD_SPLITOR)); + for (String context : contextList) { + String[] line = context.split(String.valueOf(TraceConstants.CONTENT_SPLITOR)); + if (line[0].equals(Pub.name())) { + TraceContext pubContext = initTraceContext(); + pubContext.setTraceType(Pub); + pubContext.setTimeStamp(Long.parseLong(line[1])); + pubContext.setRegionId(line[2]); + pubContext.setGroupName(line[3]); + TraceBean bean = new TraceBean(); + bean.setTopic(line[4]); + bean.setMsgId(line[5]); + bean.setTags(line[6]); + bean.setKeys(line[7]); + bean.setStoreHost(line[8]); + bean.setBodyLength(Integer.parseInt(line[9])); + pubContext.setCostTime(Integer.parseInt(line[10])); + bean.setMsgType(MessageType.values()[Integer.parseInt(line[11])]); + // compatible with different version + switch (line.length) { + case TRACE_MSG_PUB_V1_LEN: + break; + case TRACE_MSG_PUB_V2_LEN: + pubContext.setSuccess(Boolean.parseBoolean(line[12])); + break; + case TRACE_MSG_PUB_V3_LEN: + bean.setOffsetMsgId(line[12]); + pubContext.setSuccess(Boolean.parseBoolean(line[13])); + break; + case TRACE_MSG_PUB_V4_LEN: + bean.setOffsetMsgId(line[12]); + pubContext.setSuccess(Boolean.parseBoolean(line[13])); + bean.setClientHost(line[14]); + break; + default: + bean.setOffsetMsgId(line[12]); + pubContext.setSuccess(Boolean.parseBoolean(line[13])); + bean.setClientHost(line[14]); + log.warn("Detect new version trace msg of {} type", Pub.name()); + break; + } + + pubContext.setTraceBeans(new ArrayList(1)); + pubContext.getTraceBeans().add(bean); + resList.add(pubContext); + } else if (line[0].equals(TraceType.SubBefore.name())) { + TraceContext subBeforeContext = initTraceContext(); + subBeforeContext.setTraceType(TraceType.SubBefore); + subBeforeContext.setTimeStamp(Long.parseLong(line[1])); + subBeforeContext.setRegionId(line[2]); + subBeforeContext.setGroupName(line[3]); + subBeforeContext.setRequestId(line[4]); + TraceBean bean = new TraceBean(); + bean.setMsgId(line[5]); + bean.setRetryTimes(Integer.parseInt(line[6])); + bean.setKeys(line[7]); + subBeforeContext.setTraceBeans(new ArrayList(1)); + subBeforeContext.getTraceBeans().add(bean); + resList.add(subBeforeContext); + } else if (line[0].equals(TraceType.SubAfter.name())) { + TraceContext subAfterContext = initTraceContext(); + subAfterContext.setTraceType(TraceType.SubAfter); + subAfterContext.setRequestId(line[1]); + TraceBean bean = new TraceBean(); + bean.setMsgId(line[2]); + bean.setKeys(line[5]); + subAfterContext.setTraceBeans(new ArrayList(1)); + subAfterContext.getTraceBeans().add(bean); + subAfterContext.setCostTime(Integer.parseInt(line[3])); + subAfterContext.setSuccess(Boolean.parseBoolean(line[4])); + // compatible with different version + switch (line.length) { + case TRACE_MSG_SUBAFTER_V1_LEN: + break; + case TRACE_MSG_SUBAFTER_V2_LEN: + subAfterContext.setContextCode(Integer.parseInt(line[6])); + break; + case TRACE_MSG_SUBAFTER_V3_LEN: + subAfterContext.setContextCode(Integer.parseInt(line[6])); + subAfterContext.setTimeStamp(Long.parseLong(line[7])); + subAfterContext.setGroupName(line[8]); + break; + default: + subAfterContext.setContextCode(Integer.parseInt(line[6])); + subAfterContext.setTimeStamp(Long.parseLong(line[7])); + subAfterContext.setGroupName(line[8]); + log.warn("Detect new version trace msg of {} type", TraceType.SubAfter.name()); + break; + } + resList.add(subAfterContext); + } else if (line[0].equals(TraceType.EndTransaction.name())) { + TraceContext endTransactionContext = initTraceContext(); + endTransactionContext.setTraceType(TraceType.EndTransaction); + endTransactionContext.setTimeStamp(Long.parseLong(line[1])); + endTransactionContext.setRegionId(line[2]); + endTransactionContext.setGroupName(line[3]); + TraceBean bean = new TraceBean(); + bean.setTopic(line[4]); + bean.setMsgId(line[5]); + bean.setTags(line[6]); + bean.setKeys(line[7]); + bean.setStoreHost(line[8]); + bean.setMsgType(MessageType.values()[Integer.parseInt(line[9])]); + bean.setTransactionId(line[10]); + bean.setTransactionState(LocalTransactionState.valueOf(line[11])); + bean.setFromTransactionCheck(Boolean.parseBoolean(line[12])); + endTransactionContext.setTraceBeans(new ArrayList(1)); + endTransactionContext.getTraceBeans().add(bean); + resList.add(endTransactionContext); + } + } + return resList; + } + + private static TraceContext initTraceContext() { + TraceContext traceContext = new TraceContext(); + traceContext.setTimeStamp(0L); + traceContext.setCostTime(-1); + traceContext.setRequestId(null); + return traceContext; + } +} diff --git a/src/main/java/org/apache/rocketmq/dashboard/util/WebUtil.java b/src/main/java/org/apache/rocketmq/dashboard/util/WebUtil.java new file mode 100644 index 0000000..49e3fc7 --- /dev/null +++ b/src/main/java/org/apache/rocketmq/dashboard/util/WebUtil.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.dashboard.util; + +import org.apache.commons.lang3.StringUtils; +import org.apache.rocketmq.dashboard.model.User; +import org.apache.rocketmq.dashboard.model.UserInfo; + +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.io.PrintWriter; + +public class WebUtil { + public static final String USER_INFO = "userInfo"; + public static final String USER_NAME = "username"; + public static final String NEED_LOGIN = "needLogin"; + + /** + * Obtain ServletRequest header value + * + * @param request + * @param name + * @return + */ + public static String getHeaderValue(HttpServletRequest request, String name) { + String v = request.getHeader(name); + if (v == null) { + return null; + } + return v.trim(); + } + + /** + * Fetch request ip address + * + * @param request + * @return + */ + public static String getIp(ServletRequest request) { + HttpServletRequest req = (HttpServletRequest) request; + String addr = getHeaderValue(req, "X-Forwarded-For"); + if (StringUtils.isNotEmpty(addr) && addr.contains(",")) { + addr = addr.split(",")[0]; + } + if (StringUtils.isEmpty(addr)) { + addr = getHeaderValue(req, "X-Real-IP"); + } + if (StringUtils.isEmpty(addr)) { + addr = req.getRemoteAddr(); + } + return addr; + } + + public static void redirect(HttpServletResponse response, HttpServletRequest request, String path) throws IOException { + response.sendRedirect(request.getContextPath() + path); + } + + /** + * Obtain the full url path + * + * @param request + * @return + */ + public static String getUrl(HttpServletRequest request) { + String url = request.getRequestURL().toString(); + String queryString = request.getQueryString(); + if (queryString != null) { + url += "?" + request.getQueryString(); + } + return url; + } + + /** + * Write content to front-page/response + * + * @param response + * @param result + * @throws IOException + */ + public static void print(HttpServletResponse response, String result) throws IOException { + response.setContentType("text/html;charset=UTF-8"); + PrintWriter out = response.getWriter(); + out.print(result); + out.flush(); + out.close(); + } + + public static Object getValueFromSession(HttpServletRequest request, String key) { + HttpSession session = request.getSession(false); + + if (session != null) { + return session.getAttribute(key); + } + + return null; + } + + public static UserInfo setLoginInfo(HttpServletRequest request, HttpServletResponse response, User user) { + String ip = WebUtil.getIp(request); + UserInfo userInfo = new UserInfo(); + userInfo.setIp(ip); + userInfo.setLoginTime(System.currentTimeMillis()); + + userInfo.setUser(user); + + return userInfo; + } + + public static void removeSession(HttpServletRequest request) { + HttpSession session = request.getSession(); + session.invalidate(); + } + + public static void setSessionValue(HttpServletRequest request, String key, Object value) { + HttpSession session = request.getSession(); + session.setAttribute(key, value); + } + + public static String getSessionId(HttpServletRequest request) { + return request.getSession().getId(); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..f4bfa6a --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,52 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +server.port=23880 + +### SSL setting +#server.ssl.key-store=classpath:rmqcngkeystore.jks +#server.ssl.key-store-password=rocketmq +#server.ssl.keyStoreType=PKCS12 +#server.ssl.keyAlias=rmqcngkey + +#spring.application.index=true +spring.application.name=rocketmq-dashboard +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +spring.http.encoding.force=true +logging.level.root=INFO +logging.config=classpath:logback.xml +#if this value is empty,use env value rocketmq.config.namesrvAddr NAMESRV_ADDR | now, you can set it in ops page.default localhost:9876 +rocketmq.config.namesrvAddr=dev.ccwin-in.com:23887 +#if you use rocketmq version < 3.5.8, rocketmq.config.isVIPChannel should be false.default true +rocketmq.config.isVIPChannel= +#timeout for mqadminExt, default 5000ms +rocketmq.config.timeoutMillis= +#rocketmq-console's data path:dashboard/monitor +rocketmq.config.dataPath=/opt/rocketmq-console/data +#set it false if you don't want use dashboard.default true +rocketmq.config.enableDashBoardCollect=true +#set the message track trace topic if you don't want use the default one +rocketmq.config.msgTrackTopicName= +rocketmq.config.ticketKey=ticket + +#Must create userInfo file: ${rocketmq.config.dataPath}/users.properties if the login is required +rocketmq.config.loginRequired=true + +#set the accessKey and secretKey if you used acl +#rocketmq.config.accessKey= +#rocketmq.config.secretKey= +rocketmq.config.useTLS=false diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..3fa3fa5 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,50 @@ + + + + + + + [%d{yyyy-MM-dd HH:mm:ss.SSS}] %5p %m%n + + + + + ${user.home}/logs/consolelogs/rocketmq-console.log + true + + ${user.home}/logs/consolelogs/rocketmq-console-%d{yyyy-MM-dd}.%i.log + + + 104857600 + + 10 + + + [%d{yyyy-MM-dd HH:mm:ss.SSS}] %5p %m%n + UTF-8 + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/rmqcngkeystore.jks b/src/main/resources/rmqcngkeystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..5d5e338f20ea0cd9771844edc56d514d3e81f2e2 GIT binary patch literal 2589 zcmY+Ec{~#iAIG?Rxt1r`uE z+Q9^lmLuGVCb0YbU&QVJCa~W=!Zt@^1>yYn7Z(f&Djxp}9Pb{@jvuhy} zhc_Ut9)N>6D*oaR$h(wgk{n2WeR{iWtLL!)5@KeEsTf9bw5P0*J)Ws*+D9k-E=+t- zAcdj3h-*B9{vf<1yJ^`4#0uko2vYk{S+i71y&`XM>OScpWb#>eGJN{@wJSHrN8K@@ znng+?dp5e}P=fTSrMECXuk3(R`0i^ZDr&V+Q`z*|5N%ccakDFGbl8qr!%&J+DsVj0 z@si_*y1Z}E=eQPUkEaX&%JYOG@-+J0RVs}%V4rTB58W9?Y70=l3|7ThBzZorhWA9{ zns|&EBn6t;tD3j8j`T@!U6EGi=$!E%Spv%)$p@E>rCiTg7wqEVwZn#{1V$Ioai}v|Xqv|RqjFKWZ zX<3(*H{eV>g0K;+Wu5_}J}DV{xH8ivP?2&#kshl9D!X;?j>YmqbBK1%V>EMVw`#zw z-$S6)%R3Lu8?5gSbAIsHY)Nfkw!Ge8yLS_Ml8YTfG1Y0Wi|m#6oS6m%E;2(&rN7az zO(Ef>8tD3>&0l4W5N8nbyK0=P=z+>PSp42dea6A&f>RoYVdC%2 z%_r7FYKjYW{^d=>devVqR%Mup)fbD87|InFD^@SBlU+GSB*#AH#K)f2&FArT+In8s znLsDll0NwgKkfS=qe(#b@RW0%MJkX;jZ z>+C4-C#|iZ(AdaGXi6dQk|A>2H^^ML}Y@UJv z`t0ATc&UAHbt`OF+SZ`X(^_)D)+Y5S7U>yTku$!Bdp#F2Fy!hl{_9_yvu#$Z_h-nnC zrzOv^8FDp{+IHvnmAJFUY|+JfOxOw>@~6PZ5rqn#gBxxUzfIl+`qqOK8>JcXqc@mMRn3zfLVz=6axK&?ZnWC}ip_D#jDtcs|YE)jqp|wM)2gvM6#`IEhRTGIO&=NpUR@G#6>m>tpo09E4E&qOSPuCr$;JZTJ>LN z=SBN?nfVVIMH}~Rs3(skYjv3X1EK^<-i%_XN8~N(erYr4HTc-zD4EF>EB+H zbJEjzvo5-7vQ#Lq6TYx0^i*E%v#%J>a3%KmjQl&hn(45;#AhcjaZV<9Z#^n=Wf9+& zs5o8-XomTRwdI>{@R-lLIb@&RB;N|u5*L=Mnok*g{Y7(a<07bJ*DHP!Q?O?UHN ze%N2sDn}m_=s3@@0mtRcQPkG3!Z<4DYH7P2ysYT>g6JN%1ieTg+4-wjEO`K}!{ zNz1r8Yr(vUH@4}2Sy#A8N$p);7|+YS#+hUkF9B&yX$c-dMOI3EM!)^Rt!}nAcu??o zx6=ysp{jL!bHdDY(9-mFYdD@s5-fmQEaW_5HKsq(6UFxU>#oTC(jOqtmPuo+rfI4bu7gQ9RZQA4x+!1qg)VS*@}yde)#GY`xs0uqLm&X%`K|!Og$;`HeFh zKV^v10d#)mN=w1spus~Y=+{?WSHryKHPe=QqJ%!*K|JW(Wf1z{FH*8_QmK#D)O6r8 zs2I!dZFc2Dw|}*59%>iMT + + + + + RocketMQ-Dashboard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/src/app.js b/src/main/resources/static/src/app.js new file mode 100644 index 0000000..ac8cab3 --- /dev/null +++ b/src/main/resources/static/src/app.js @@ -0,0 +1,264 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; +var initFlag = false; +var loginFlag = false; +var app = angular.module('app', [ + 'ngAnimate', + 'ngCookies', + 'ngRoute', + 'ngDialog', + 'ngMaterial', + 'ngSanitize', + 'material.svgAssetsCache', + 'ui-notification', + 'tm.pagination', + 'ae-datetimepicker', + 'localytics.directives', + 'pascalprecht.translate' +]).run( + ['$rootScope','$location','$cookies','$http', '$window','Notification', + function ($rootScope,$location,$cookies,$http, $window, Notification) { + var init = function(callback){ + if (initFlag) return; + initFlag = true; + + var url = 'login/check.query'; + var setting = { + type: "GET", + timeout:15000, + success:callback, + async:false + } + //sync invoke + $.ajax(url,setting) + } + console.log('initFlag0='+ initFlag + ' loginFlag0==='+loginFlag); + + $rootScope.$on('$locationChangeStart', function (event, next, current) { + // redirect to login page if not logged in and trying to access a restricted page + init(function(resp){ + if (resp.status == 0) { + // console.log('resp.data==='+resp.data); + var loginInfo = resp.data; + loginFlag = loginInfo.loginRequired; + if (!loginInfo.logined) { + $window.sessionStorage.clear(); + } + }else { + Notification.error({message: "" + resp.errMsg, delay: 2000}); + } + }); + + console.log('initFlag='+ initFlag + ' loginFlag==='+loginFlag); + $rootScope.username = ''; + if (loginFlag || loginFlag == "true") { + var username = $window.sessionStorage.getItem("username"); + + if (username != null) { + $rootScope.username = username; + } + + // console.log("username " + $rootScope.username); + var restrictedPage = $.inArray($location.path(), ['/login']) === -1; + if (restrictedPage && !username) { + var callback = $location.path(); + $location.path('/login'); + } + } + + }); + + + $rootScope.$on('$routeChangeSuccess', function() { + var pathArray = $location.url().split("/"); + var index = pathArray.indexOf(""); + if(index >= 0){ + pathArray.remove(index); + } + $rootScope.path = pathArray[0]; + + //初始化material UI控件 + $.material.init(); + }); + + $rootScope.$on('$routeChangeStart',function (evt, next,current) { + window.clearInterval($rootScope._thread); + }) + } + ] + ).animation('.view', function () { + return { + animate: function (element, className, from, to, done) { + //styles + } + } + }); + +app.factory('abc', function ($http, $window) { + $http({ + method: "GET", + url: "login/check.query" + }).success(function (resp) { + if (resp.status == 0) { + alert(resp.data) + } + }); + return 1; +}); + +app.provider('getDictName', function () { + + var dictList = []; + + this.init = function () { + var url = "src/data/dict.json";//无法使用common服务类,地址只能写死 + var params = {}; + $.get(url, params, function (ret) { + dictList = ret; + }) + } + + this.$get = function () { + return function (dictType, value) { + for (var i = 0; i < dictList.length; i++) { + var dict = dictList[i]; + if (dict.TYPE == dictType && dict.DICT_VALUE == value) { + return dict.DICT_NAME; + } + } + } + } +}) + +app.config(['$routeProvider', '$httpProvider','$cookiesProvider','getDictNameProvider','$sceProvider','$translateProvider','$mdThemingProvider', + function ($routeProvider, $httpProvider ,$cookiesProvider,getDictNameProvider,$sceProvider,$translateProvider,$mdThemingProvider) { + //关闭html校验,存在安全隐患,但目前没问题,使用ng-bind-html需要注意,防止跨站攻击 + $sceProvider.enabled(false); + //前端字典项目初始化 + getDictNameProvider.init(); + + //init angular + $mdThemingProvider.theme('default') + .primaryPalette('pink') + .accentPalette('light-blue'); + + + //设置ajax默认配置 + $.ajaxSetup({ + type: "POST", + contentType: 'application/json', + cache:false, + timeout : 5000, //超时时间设置,单位毫秒 + converters:{ + "text json": JSONbig.parse + } + }); + + // check login status + + + $httpProvider.defaults.cache = false; + + $routeProvider.when('/', { + templateUrl: 'view/pages/index.html', + controller:'dashboardCtrl' + }).when('/login', { + templateUrl: 'view/pages/login.html', + controller:'loginController' + }).when('/cluster', { + templateUrl: 'view/pages/cluster.html', + controller:'clusterController' + }).when('/topic', { + templateUrl: 'view/pages/topic.html', + controller:'topicController' + }).when('/consumer', { + templateUrl: 'view/pages/consumer.html', + controller:'consumerController' + }).when('/producer', { + templateUrl: 'view/pages/producer.html', + controller:'producerController' + }).when('/message', { + templateUrl: 'view/pages/message.html', + controller:'messageController' + }).when('/messageTrace', { + templateUrl: 'view/pages/messageTrace.html', + controller:'messageTraceController' + }).when('/ops', { + templateUrl: 'view/pages/ops.html', + controller:'opsController' + }).when('/config', { + templateUrl: 'view/pages/config.html', + controller:'configController' + }).when('/404', { + templateUrl: 'view/pages/404.html' + }).otherwise('/404'); + + $translateProvider.translations('en',en); + $translateProvider.translations('zh',zh); + $translateProvider.preferredLanguage('en'); + $translateProvider.useCookieStorage(); +// $translateProvider.useSanitizeValueStrategy('sanitize'); + + }]); + +app.filter('range', function() { + return function(input, range) { + var total = parseInt(range.totalPage) + 1; + var count = 5; + for (var i = range.start; i 0){ + input.push(i); + count -- ; + }else { + break; + } + } + return input; + }; +}); + + +app.filter('dict',['getDictName',function(getDictName){ + return function(value,type){ + return getDictName(type,value); + } +}]) + +/** + * 数组扩展方法,移除数组中某一元素或某一段元素 + * @param from 需要移除元素的索引开始值(只传一个参数表示单独移除该元素) + * @param to 需要移除元素的索引结束值 + * @returns {*} + */ +Array.prototype.remove = function(from, to) { + var rest = this.slice((to || from) + 1 || this.length); + this.length = from < 0 ? this.length + from : from; + return this.push.apply(this, rest); +}; + +/** + * 根据元素值查询数组中元素的索引 + * @param val + * @returns {number} + */ +Array.prototype.indexOf = function(val) { + for (var i = 0; i < this.length; i++) { + if (this[i] == val) return i; + } + return -1; +}; \ No newline at end of file diff --git a/src/main/resources/static/src/cluster.js b/src/main/resources/static/src/cluster.js new file mode 100644 index 0000000..6f1baee --- /dev/null +++ b/src/main/resources/static/src/cluster.js @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +app.controller('clusterController', ['$scope','$location','$http','Notification','remoteApi','tools', function ($scope,$location,$http,Notification,remoteApi,tools) { + $scope.clusterMap = {};//cluster:brokerNameList + $scope.brokerMap = {};//brokerName:{id:addr} + $scope.brokerDetail = {};//{brokerName,id:detail} + $scope.clusterNames = []; + $scope.selectedCluster = ""; + var callback = function (resp) { + if (resp.status == 0) { + $scope.clusterMap = resp.data.clusterInfo.clusterAddrTable; + $scope.brokerMap = resp.data.clusterInfo.brokerAddrTable; + $scope.brokerDetail = resp.data.brokerServer; + $.each($scope.clusterMap,function(clusterName,clusterBrokersNames){ + $scope.clusterNames.push(clusterName); + }); + if ($scope.clusterNames.length > 0) { + $scope.selectedCluster = $scope.clusterNames[0]; + } + $scope.brokers = tools.generateBrokerMap($scope.brokerDetail,$scope.clusterMap,$scope.brokerMap); + $scope.switchCluster(); + }else{ + Notification.error({message: resp.errMsg, delay: 2000}); + } + } + + remoteApi.queryClusterList(callback); + + $scope.switchCluster = function(){ + $scope.instances = $scope.brokers[$scope.selectedCluster]; + } + + $scope.showDetail = function (brokerName,index) { + $scope.detail = $scope.brokerDetail[brokerName][index]; + $scope.brokerName = brokerName; + $scope.index = index; + $(".brokerModal").modal(); + } + + $scope.showConfig = function (brokerAddr,brokerName,index) { + $scope.brokerAddr = brokerAddr; + $scope.brokerName = brokerName; + $scope.index = index; + $http({ + method: "GET", + url: "cluster/brokerConfig.query", + params:{brokerAddr:brokerAddr} + }).success(function (resp) { + if (resp.status == 0) { + $scope.brokerConfig = resp.data; + $(".configModal").modal(); + }else{ + Notification.error({message: resp.errMsg, delay: 2000}); + } + }) + } +}]) diff --git a/src/main/resources/static/src/config.js b/src/main/resources/static/src/config.js new file mode 100644 index 0000000..2dd1ce8 --- /dev/null +++ b/src/main/resources/static/src/config.js @@ -0,0 +1,236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var module = app; + + +var bIsUpdate = true; +module.controller('configController', ['$scope', 'ngDialog', '$http', 'Notification', '$window', function ($scope, ngDialog, $http, Notification, $window) { + $scope.paginationConf = { + currentPage: 1, + totalItems: 0, + itemsPerPage: 10, + pagesLength: 15, + perPageOptions: [10], + rememberPerPage: 'perPageItems', + onChange: function () { + $scope.showConfigList(this.currentPage, this.totalItems); + + } + }; + $scope.filterNormal = true + $scope.filterRetry = false + $scope.filterDLQ = false + $scope.filterSystem = false + $scope.configShowList = []; + $scope.allTopicList = []; + $scope.userRole = $window.sessionStorage.getItem("userrole"); + $scope.writeOperationEnabled = $scope.userRole == null ? true : ($scope.userRole == 1 ? true : false); + + $scope.refreshTopicList = function () { + $http({ + method: "GET", + url: "config/config.query" + }).success(function (resp) { + if (resp.status == 0) { + delete resp.data.project + $scope.allTopicList =[] + let obj = {} + Object.entries(resp.data).forEach(item=>{ + obj = { + name:item[0], + value:item[1] + } + $scope.allTopicList.push(obj) + }) + console.log($scope.allTopicList) + $scope.showConfigList(1, $scope.allTopicList.length); + + } else { + Notification.error({message: resp.errMsg, delay: 5000}); + } + }); + }; + + $scope.refreshTopicList(); + + $scope.filterStr = ""; + $scope.$watch('filterStr', function () { + $scope.filterList(1); + }); + $scope.$watch('filterNormal', function () { + $scope.filterList(1); + }); + $scope.$watch('filterRetry', function () { + $scope.filterList(1); + }); + $scope.$watch('filterDLQ', function () { + $scope.filterList(1); + }); + $scope.$watch('filterSystem', function () { + $scope.filterList(1); + }); + $scope.filterList = function (currentPage) { + var lowExceptStr = $scope.filterStr.toLowerCase(); + var canShowList = []; + + $scope.allTopicList.forEach(function (element) { + if ($scope.filterByType(element)) { + if (element.toLowerCase().indexOf(lowExceptStr) != -1) { + canShowList.push(element); + } + } + }); + $scope.paginationConf.totalItems = canShowList.length; + var perPage = $scope.paginationConf.itemsPerPage; + var from = (currentPage - 1) * perPage; + var to = (from + perPage) > canShowList.length ? canShowList.length : from + perPage; + $scope.configShowList = canShowList.slice(from, to); + }; + + $scope.filterByType = function (str) { + if ($scope.filterRetry) { + if (str.startsWith("%R")) { + return true + } + } + if ($scope.filterDLQ) { + if (str.startsWith("%D")) { + return true + } + } + if ($scope.filterSystem) { + if (str.startsWith("%S")) { + return true + } + } + if ($scope.filterNormal) { + if (str.startsWith("%") == false) { + return true + } + } + return false; + }; + + $scope.showConfigList = function (currentPage, totalItem) { + if ($scope.filterStr != "") { + $scope.filterList(currentPage); + return; + } + var perPage = $scope.paginationConf.itemsPerPage; + var from = (currentPage - 1) * perPage; + var to = (from + perPage) > totalItem ? totalItem : from + perPage; + $scope.configShowList = $scope.allTopicList.slice(from, to); + $scope.paginationConf.totalItems = totalItem; + $scope.filterList(currentPage); + }; + $scope.deleteConfig = function (name) { + $http({ + method: "POST", + url: "config/config.delete", + params: { + name: name + } + }).success(function (resp) { + if (resp.status == 0) { + Notification.info({message: "delete success!", delay: 2000}); + $scope.refreshTopicList(); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + $scope.openDeleteTopicDialog = function (topic) { + ngDialog.open({ + template: 'deleteTopicDialog', + controller: 'deleteTopicDialogController', + data: { + topic: topic, + consumerData: "asd" + } + }); + }; + + $scope.openUpdateDialog = function (data, sysFlag) { + $scope.openCreateOrUpdateDialog(data, sysFlag); + }; + $scope.openCreateOrUpdateDialog = function (request, sysFlag) { + if (request == null) { + request = [{ + name:'', + value:[] + }]; + bIsUpdate = false; + }else{ + bIsUpdate = true; + request = [{ + name:request.name, + value:request.value.split(','), + }] + } + $http({ + method: "GET", + url: "config/config.query" + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + console.log('keys',Object.keys( resp.data)); + ngDialog.open({ + preCloseCallback: function (value) { + // Refresh topic list + $scope.refreshTopicList(); + }, + template: 'configModifyDialog', + controller: 'configModifyDialogController', + data: { + sysFlag: sysFlag, + topicRequestList: request, + allClusterNameList: resp.data.project.split(','), +// allBrokerNameList: Object.keys( resp.data), + bIsUpdate: bIsUpdate, + writeOperationEnabled: $scope.writeOperationEnabled + } + }); + } + }); + } + + $scope.openAddDialog = function () { + $scope.openCreateOrUpdateDialog(null, false); + } + +}]); + +module.controller('configModifyDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) { + $scope.postTopicRequest = function (topicRequestItem) { + console.log(bIsUpdate); + let url = bIsUpdate?'config/config.update' : 'config/config.create' + $http({ + method: "GET", + url:url +"?name="+topicRequestItem.name +'&value=' + topicRequestItem.value.join(','), + data: '' + }).success(function (resp) { + if (resp.status == 0) { + Notification.info({message: "success!", delay: 2000}); + ngDialog.close(this); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + } + }] +); diff --git a/src/main/resources/static/src/consumer.js b/src/main/resources/static/src/consumer.js new file mode 100644 index 0000000..0059192 --- /dev/null +++ b/src/main/resources/static/src/consumer.js @@ -0,0 +1,380 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var module = app; + +module.controller('consumerController', ['$scope', 'ngDialog', '$http', 'Notification', '$window', function ($scope, ngDialog, $http, Notification, $window) { + $scope.paginationConf = { + currentPage: 1, + totalItems: 0, + itemsPerPage: 10, + pagesLength: 15, + perPageOptions: [10], + rememberPerPage: 'perPageItems', + onChange: function () { + $scope.showConsumerGroupList(this.currentPage, this.totalItems); + } + }; + $scope.sortKey = null; + $scope.sortOrder = 1; + $scope.intervalProcessSwitch = false; + $scope.intervalProcess = null; + $scope.allConsumerGrouopList = []; + $scope.consumerGroupShowList = []; + $scope.sortByKey = function (key) { + $scope.paginationConf.currentPage = 1; + $scope.sortOrder = -$scope.sortOrder; + $scope.sortKey = key; + $scope.doSort(); + }; + $scope.userRole = $window.sessionStorage.getItem("userrole"); + $scope.writeOperationEnabled = $scope.userRole == null ? true : ($scope.userRole == 1 ? true : false); + + $scope.doSort = function () {// todo how to change this fe's code ? (it's dirty) + if ($scope.sortKey == 'diffTotal') { + $scope.allConsumerGrouopList.sort(function (a, b) { + return (a.diffTotal > b.diffTotal) ? $scope.sortOrder : ((b.diffTotal > a.diffTotal) ? -$scope.sortOrder : 0); + }); + } + if ($scope.sortKey == 'group') { + $scope.allConsumerGrouopList.sort(function (a, b) { + return (a.group > b.group) ? $scope.sortOrder : ((b.group > a.group) ? -$scope.sortOrder : 0); + }); + } + if ($scope.sortKey == 'count') { + $scope.allConsumerGrouopList.sort(function (a, b) { + return (a.count > b.count) ? $scope.sortOrder : ((b.count > a.count) ? -$scope.sortOrder : 0); + }); + } + if ($scope.sortKey == 'consumeTps') { + $scope.allConsumerGrouopList.sort(function (a, b) { + return (a.consumeTps > b.consumeTps) ? $scope.sortOrder : ((b.consumeTps > a.consumeTps) ? -$scope.sortOrder : 0); + }); + } + $scope.filterList($scope.paginationConf.currentPage) + }; + $scope.refreshConsumerData = function () { + //Show loader + $('#loaderConsumer').removeClass("hide-myloader"); + + $http({ + method: "GET", + url: "consumer/groupList.query" + }).success(function (resp) { + if (resp.status == 0) { + $scope.allConsumerGrouopList = resp.data; + console.log($scope.allConsumerGrouopList); + console.log(JSON.stringify(resp)); + $scope.showConsumerGroupList($scope.paginationConf.currentPage, $scope.allConsumerGrouopList.length); + + //Hide loader + $('#loaderConsumer').addClass("hide-myloader"); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + $scope.monitor = function (consumerGroupName) { + $http({ + method: "GET", + url: "monitor/consumerMonitorConfigByGroupName.query", + params: {consumeGroupName: consumerGroupName} + }).success(function (resp) { + // if(resp.status ==0){ + ngDialog.open({ + template: 'consumerMonitorDialog', + controller: 'consumerMonitorDialogController', + data: {consumerGroupName: consumerGroupName, data: resp.data} + }); + // }else { + // Notification.error({message: resp.errMsg, delay: 2000}); + // } + }); + }; + + + $scope.$watch('intervalProcessSwitch', function () { + if ($scope.intervalProcess != null) { + clearInterval($scope.intervalProcess); + $scope.intervalProcess = null; + } + if ($scope.intervalProcessSwitch) { + $scope.intervalProcess = setInterval($scope.refreshConsumerData, 10000); + } + }); + + + $scope.refreshConsumerData(); + $scope.filterStr = ""; + $scope.$watch('filterStr', function () { + $scope.paginationConf.currentPage = 1; + $scope.filterList(1) + }); + + $scope.filterList = function (currentPage) { + var lowExceptStr = $scope.filterStr.toLowerCase(); + var canShowList = []; + $scope.allConsumerGrouopList.forEach(function (element) { + console.log(element) + if (element.group.toLowerCase().indexOf(lowExceptStr) != -1) { + canShowList.push(element); + } + }); + $scope.paginationConf.totalItems = canShowList.length; + var perPage = $scope.paginationConf.itemsPerPage; + var from = (currentPage - 1) * perPage; + var to = (from + perPage) > canShowList.length ? canShowList.length : from + perPage; + $scope.consumerGroupShowList = canShowList.slice(from, to); + }; + + + $scope.showConsumerGroupList = function (currentPage, totalItem) { + var perPage = $scope.paginationConf.itemsPerPage; + var from = (currentPage - 1) * perPage; + var to = (from + perPage) > totalItem ? totalItem : from + perPage; + $scope.consumerGroupShowList = $scope.allConsumerGrouopList.slice(from, to); + $scope.paginationConf.totalItems = totalItem; + console.log($scope.consumerGroupShowList) + console.log($scope.paginationConf.totalItems) + $scope.doSort() + }; + $scope.openAddDialog = function () { + $scope.openCreateOrUpdateDialog(null); + }; + $scope.openCreateOrUpdateDialog = function (request) { + var bIsUpdate = true; + if (request == null) { + request = [{ + brokerNameList: [], + subscriptionGroupConfig: { + groupName: "", + consumeEnable: true, + consumeFromMinEnable: true, + consumeBroadcastEnable: true, + retryQueueNums: 1, + retryMaxTimes: 16, + brokerId: 0, + whichBrokerWhenConsumeSlowly: 1 + } + }]; + bIsUpdate = false; + } + console.log(request); + $http({ + method: "GET", + url: "cluster/list.query" + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + ngDialog.open({ + preCloseCallback: function (value) { + // Refresh topic list + $scope.refreshConsumerData(); + }, + template: 'consumerModifyDialog', + controller: 'consumerModifyDialogController', + data: { + consumerRequestList: request, + allClusterNameList: Object.keys(resp.data.clusterInfo.clusterAddrTable), + allBrokerNameList: Object.keys(resp.data.brokerServer), + bIsUpdate: bIsUpdate, + writeOperationEnabled: $scope.writeOperationEnabled + } + }); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + $scope.detail = function (consumerGroupName) { + $http({ + method: "GET", + url: "consumer/queryTopicByConsumer.query", + params: {consumerGroup: consumerGroupName} + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + ngDialog.open({ + template: 'consumerTopicViewDialog', + controller: 'consumerTopicViewDialogController', + data: {consumerGroupName: consumerGroupName, data: resp.data} + }); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + + $scope.client = function (consumerGroupName) { + $http({ + method: "GET", + url: "consumer/consumerConnection.query", + params: {consumerGroup: consumerGroupName} + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + ngDialog.open({ + template: 'clientInfoDialog', + // controller: 'addTopicDialogController', + data: {data: resp.data, consumerGroupName: consumerGroupName} + }); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + $scope.updateConfigDialog = function (consumerGroupName) { + $http({ + method: "GET", + url: "consumer/examineSubscriptionGroupConfig.query", + params: {consumerGroup: consumerGroupName} + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + $scope.openCreateOrUpdateDialog(resp.data); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + + + }; + $scope.delete = function (consumerGroupName) { + $http({ + method: "GET", + url: "consumer/fetchBrokerNameList.query", + params: { + consumerGroup: consumerGroupName + } + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + + ngDialog.open({ + preCloseCallback: function (value) { + // Refresh topic list + $scope.refreshConsumerData(); + }, + template: 'deleteConsumerDialog', + controller: 'deleteConsumerDialogController', + data: { + // allClusterList:Object.keys(resp.data.clusterInfo.clusterAddrTable), + allBrokerNameList: resp.data, + consumerGroupName: consumerGroupName + } + }); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + } + +}]) +module.controller('consumerMonitorDialogController', function ($scope, ngDialog, $http, Notification) { + $scope.createOrUpdateConsumerMonitor = function () { + $http({ + method: "POST", + url: "monitor/createOrUpdateConsumerMonitor.do", + params: { + consumeGroupName: $scope.ngDialogData.consumerGroupName, + minCount: $scope.ngDialogData.data.minCount, + maxDiffTotal: $scope.ngDialogData.data.maxDiffTotal + } + }).success(function (resp) { + if (resp.status == 0) { + Notification.info({message: "update success!", delay: 2000}); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + } + } +); + + +module.controller('deleteConsumerDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) { + $scope.selectedClusterList = []; + $scope.selectedBrokerNameList = []; + $scope.delete = function () { + console.log($scope.selectedClusterList); + console.log($scope.selectedBrokerNameList); + console.log($scope.ngDialogData.consumerGroupName); + $http({ + method: "POST", + url: "consumer/deleteSubGroup.do", + data: { + groupName: $scope.ngDialogData.consumerGroupName, + brokerNameList: $scope.selectedBrokerNameList + } + }).success(function (resp) { + if (resp.status == 0) { + Notification.info({message: "delete success!", delay: 2000}); + ngDialog.close(this); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + } + }] +); + +module.controller('consumerModifyDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) { + $scope.postConsumerRequest = function (consumerRequest) { + var request = JSON.parse(JSON.stringify(consumerRequest)); + console.log(request); + $http({ + method: "POST", + url: "consumer/createOrUpdate.do", + data: request + }).success(function (resp) { + if (resp.status == 0) { + Notification.info({message: "update success!", delay: 2000}); + ngDialog.close(this); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + } + }] +); + +module.controller('consumerTopicViewDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) { + $scope.consumerRunningInfo = function (consumerGroup, clientId, jstack) { + $http({ + method: "GET", + url: "consumer/consumerRunningInfo.query", + params: { + consumerGroup: consumerGroup, + clientId: clientId, + jstack: jstack + } + }).success(function (resp) { + if (resp.status == 0) { + ngDialog.open({ + template: 'consumerClientDialog', + data: { + consumerClientInfo: resp.data, + clientId: clientId + } + }); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + }] +); \ No newline at end of file diff --git a/src/main/resources/static/src/controller.js b/src/main/resources/static/src/controller.js new file mode 100644 index 0000000..cdcced5 --- /dev/null +++ b/src/main/resources/static/src/controller.js @@ -0,0 +1,567 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +app.controller('AppCtrl', ['$scope','$window','$translate','$http','Notification', function ($scope,$window,$translate, $http, Notification) { + $scope.changeTranslate = function(langKey){ + $translate.use(langKey); + } + + $scope.logout = function(){ + $http({ + method: "POST", + url: "login/logout.do" + }).success(function (resp) { + window.location = resp.data; + $window.sessionStorage.clear(); + }); + } +}]); + +app.controller('dashboardCtrl', ['$scope','$rootScope','$translate','$filter','Notification','remoteApi','tools', function ($scope,$rootScope,$translate,$filter,Notification,remoteApi,tools) { + + $scope.barChart = echarts.init(document.getElementById('main')); + $scope.lineChart = echarts.init(document.getElementById('line')); + $scope.topicBarChart = echarts.init(document.getElementById('topicBar')); + $scope.topicLineChart = echarts.init(document.getElementById('topicLine')); + $scope.timepickerOptions ={format: 'YYYY-MM-DD', showClear: true}; + $scope.topicNames = []; + + $translate('BROKER').then(function (broker) { + $scope.BROKER_TITLE = broker; + initBrokerBarChart(); + initBrokerLineChart(); + }, function (translationId) { + $scope.BROKER_TITLE = translationId; + }); + + $translate('TOPIC').then(function (topic) { + $scope.TOPIC_TITLE = topic; + initTopicBarChart(); + initTopicLineChart(); + }, function (translationId) { + $scope.TOPIC_TITLE = translationId; + }); + + var initBrokerBarChart = function(){ + $scope.barChart.setOption({ + title: { + text:$scope.BROKER_TITLE + ' TOP 10' + }, + tooltip: {}, + legend: { + data:['TotalMsg'] + }, + axisPointer : { + type : 'shadow' + }, + xAxis: { + data: [], + axisLabel: { + inside: false, + textStyle: { + color: '#000000' + }, + rotate: 0, + interval:0 + }, + axisTick: { + show: true + }, + axisLine: { + show: true + }, + z: 10 + }, + yAxis: { + type: 'value', + boundaryGap: [0, '100%'], + axisLabel: { + formatter: function(value){ + return value.toFixed(2); + } + }, + splitLine: { + show: true + } + }, + series: [{ + name: 'TotalMsg', + type: 'bar', + data: [] + }] + }) + } + + var initBrokerLineChart = function(){ + $scope.lineChart.setOption({ + title: { + text: $scope.BROKER_TITLE + ' 5min trend' + }, + toolbox: { + feature: { + dataZoom: { + yAxisIndex: 'none' + }, + restore: {}, + saveAsImage: {} + } + }, + tooltip: { + trigger: 'axis', + axisPointer: { + animation: false + } + }, + yAxis: { + type: 'value', + boundaryGap: [0, '80%'], + axisLabel: { + formatter: function(value){ + return value.toFixed(2); + } + }, + splitLine: { + show: true + } + }, + dataZoom: [{ + type: 'inside', + start: 90, + end: 100 + }, { + start: 0, + end: 10, + handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', + handleSize: '80%', + handleStyle: { + color: '#fff', + shadowBlur: 3, + shadowColor: 'rgba(0, 0, 0, 0.6)', + shadowOffsetX: 2, + shadowOffsetY: 2 + } + }], + legend: { + data: [], + top:30 + }, + xAxis: { + type: 'time', + boundaryGap: false, + data: [] + }, + series: [] + }) + + } + + var initTopicBarChart = function(){ + $scope.topicBarChart.setOption({ + title: { + text:$scope.TOPIC_TITLE + ' TOP 10' + }, + tooltip: {}, + legend: { + data:['TotalMsg'] + }, + axisPointer : { + type : 'shadow' + }, + xAxis: { + data: [], + axisLabel: { + inside: false, + textStyle: { + color: '#000000' + }, + rotate: 0, + interval:0 + }, + axisTick: { + show: true + }, + axisLine: { + show: true + }, + z: 10 + }, + yAxis: { + type: 'value', + boundaryGap: [0, '100%'], + axisLabel: { + formatter: function(value){ + return value.toFixed(2); + } + }, + splitLine: { + show: true + } + }, + series: [{ + name: 'TotalMsg', + type: 'bar', + data: [] + }] + }) + } + + var initTopicLineChart = function(){ + var _option = { + baseOption:{ + title: { + text: $scope.TOPIC_TITLE + ' 5min trend' + }, + toolbox: { + feature: { + dataZoom: { + yAxisIndex: 'none' + }, + restore: {}, + saveAsImage: {} + } + }, + grid:{ + top:100 + }, + tooltip: { + trigger: 'axis', + axisPointer: { + animation: false + } + }, + yAxis: { + type: 'value', + boundaryGap: [0, '80%'], + axisLabel: { + formatter: function(value){ + return value.toFixed(2); + } + }, + splitLine: { + show: true + } + }, + dataZoom: [{ + type: 'inside', + start: 90, + end: 100 + }, { + start: 0, + end: 10, + handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', + handleSize: '80%', + handleStyle: { + color: '#fff', + shadowBlur: 3, + shadowColor: 'rgba(0, 0, 0, 0.6)', + shadowOffsetX: 2, + shadowOffsetY: 2 + } + }], + legend:{ + data:[], + top:30 + }, + xAxis: { + type: 'time', + boundaryGap: false, + data: [] + }, + series: [] + } + } + $scope.topicLineChart.setOption(_option) + + } + + var getBrokerBarChartOp = function(xAxisData,data){ + + var option = { + xAxis: { + data: xAxisData, + axisLabel: { + inside: false, + textStyle: { + color: '#000000' + }, + rotate: 0, + interval:0 + }, + axisTick: { + show: true + }, + axisLine: { + show: true + }, + z: 10 + }, + series: [{ + name: 'TotalMsg', + type: 'bar', + data: data + }] + }; + + $scope.barChart.setOption(option); + } + + var callback = function (resp) { + $scope.barChart.hideLoading(); + if (resp.status == 0) { + var clusterAddrTable = resp.data.clusterInfo.clusterAddrTable; + var brokerMap = resp.data.clusterInfo.brokerAddrTable; + var brokerDetail = resp.data.brokerServer; + var clusterMap = tools.generateBrokerMap(brokerDetail,clusterAddrTable,brokerMap); + $scope.brokerArray = []; + $.each(clusterMap,function(clusterName,brokers){ + $.each(brokers,function(i,broker){ + $scope.brokerArray.push(broker) + }) + }); + + //sort the brokerArray + $scope.brokerArray.sort(function(firstBroker,lastBroker){ + var firstTotalMsg = parseFloat(firstBroker.msgGetTotalTodayNow); + var lastTotalMsg = parseFloat(lastBroker.msgGetTotalTodayNow); + return lastTotalMsg-firstTotalMsg; + }); + + var xAxisData = [], + data = []; + + $.each($scope.brokerArray,function(i,broker){ + if(i > 9){ + return false; + } + xAxisData.push(broker.brokerName + ":" + broker.index); + data.push(broker.msgGetTotalTodayNow); + }) + getBrokerBarChartOp(xAxisData,data); + }else{ + Notification.error({message: resp.errMsg, delay: 2000}); + } + } + + $scope.barChart.showLoading(); + remoteApi.queryClusterList(callback); + + $scope.topicBarChart.showLoading(); + remoteApi.queryTopicCurrentData(function(resp){ + $scope.topicBarChart.hideLoading(); + if (resp.status == 0) { + var topicList = resp.data; + topicList.sort(function(first,last){ + var firstTotalMsg = parseFloat(first.split(",")[1]); + var lastTotalMsg = parseFloat(last.split(",")[1]); + return lastTotalMsg-firstTotalMsg; + }) + + var xAxisData = []; + var data = []; + $.each(topicList,function (i,currentData) { + var currentArray = currentData.split(","); + $scope.topicNames.push(currentArray[0]); + if(!angular.isDefined($scope.selectedTopic)){ + $scope.selectedTopic = currentArray[0]; + } + }) + $.each(topicList,function (i, currentData) { + if(i > 9){ + return false; + } + var currentArray = currentData.split(","); + xAxisData.push(currentArray[0]); + data.push(currentArray[1]); + }) + // 指定图表的配置项和数据 + var option = { + xAxis: { + data: xAxisData, + axisLabel: { + inside: false, + textStyle: { + color: '#000000' + }, + rotate: 60, + interval:0 + }, + axisTick: { + show: true + }, + axisLine: { + show: true + }, + z: 10 + }, + series: [{ + name: 'TotalMsg', + type: 'bar', + data: data + }] + }; + $scope.topicBarChart.setOption(option); + queryLineData(); + }else{ + Notification.error({message: resp.errMsg, delay: 2000}); + } + }) + + + var getBrokerLineChart = function(legend,data){ + var series = []; + var xAxisData = []; + var flag = true; + var i = 0; + $.each(data,function(key,value){ + // if(i > 9 ){ + // return false; + // } + var _tps = []; + $.each(value,function(i,tpsValue){ + var tpsArray = tpsValue.split(","); + if(flag){ + xAxisData.push($filter('date')(tpsArray[0], "HH:mm:ss")); + } + _tps.push(tpsArray[1]); + }) + flag = false; + var _series = { + name:key, + type:'line', + smooth:true, + symbol: 'none', + sampling: 'average', + data: _tps + } + series.push(_series); + i++ + }) + + var option = { + legend: { + data: legend + }, + color: ["#FF0000", "#00BFFF", "#FF00FF", "#1ce322", "#000000", '#EE7942'], + xAxis: { + type: 'category', + boundaryGap: false, + data: xAxisData + }, + series: series + }; + return option; + } + + var getTopicLineChart = function(legend,data){ + var series = []; + var xAxisData = []; + var flag = true; + var i = 0; + $.each(data,function(key,value){ + var _tps = []; + $.each(value,function(i,tpsValue){ + var tpsArray = tpsValue.split(","); + if(flag){ + xAxisData.push($filter('date')(tpsArray[0], "HH:mm:ss")); + } + _tps.push(tpsArray[2]); + }) + flag = false; + var _series = { + name:key, + type:'line', + smooth:true, + symbol: 'none', + sampling: 'average', + data: _tps + } + series.push(_series); + i++ + }) + + var option = { + baseOption:{ + legend: { + data: legend + }, + // color: ["#FF0000", "#00BFFF", "#FF00FF", "#1ce322", "#000000", '#EE7942'], + xAxis: { + type: 'category', + boundaryGap: false, + data: xAxisData + }, + series: series + }, + media:[ + { + query:{}, + option:{ + + } + } + ] + + }; + return option; + } + + + var queryLineData = function () { + var _date; + if($scope.date != null){ + _date = $filter('date')($scope.date.valueOf(), "yyyy-MM-dd"); + }else{ + _date = $filter('date')(new Date(), "yyyy-MM-dd"); + } + // $scope.lineChart.showLoading(); + remoteApi.queryBrokerHisData(_date,function(resp){ + // $scope.lineChart.hideLoading(); + if (resp.status == 0) { + var _data = {} + var _xAxisData = []; + $.each(resp.data,function(address,values){ + _data[address] = values; + _xAxisData.push(address); + }) + $scope.lineChart.setOption(getBrokerLineChart(_xAxisData,_data)); + }else{ + Notification.error({message: "" + resp.errMsg, delay: 2000}); + } + }) + + $scope.topicLineChart.showLoading(); + remoteApi.queryTopicHisData(_date,$scope.selectedTopic,function (resp) { + $scope.topicLineChart.hideLoading(); + if (resp.status == 0) { + var _data = {}; + _data[$scope.selectedTopic] = resp.data; + var _xAxisData = $scope.selectedTopic; + $scope.topicLineChart.setOption(getTopicLineChart(_xAxisData,_data)); + }else{ + Notification.error({message: "" + resp.errMsg, delay: 2000}); + } + + }) + + } + + //router after will clear this thread + $rootScope._thread = setInterval( queryLineData, tools.dashboardRefreshTime); + + +}]); + + diff --git a/src/main/resources/static/src/data/dict.json b/src/main/resources/static/src/data/dict.json new file mode 100644 index 0000000..defdab3 --- /dev/null +++ b/src/main/resources/static/src/data/dict.json @@ -0,0 +1,4 @@ +[ + {"TYPE":"DEMO_TYPE","DICT_VALUE":"0","DICT_NAME":"test1"}, + {"TYPE":"DEMO_TYPE","DICT_VALUE":"1","DICT_NAME":"test2"} +] \ No newline at end of file diff --git a/src/main/resources/static/src/i18n/en.js b/src/main/resources/static/src/i18n/en.js new file mode 100644 index 0000000..cb8866e --- /dev/null +++ b/src/main/resources/static/src/i18n/en.js @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var en = { + "TITLE": "RocketMQ-Dashboard", + "CLOSE": "Close", + "NO": "NO.", + "ADDRESS": "Address", + "VERSION": "Version", + "PRO_MSG_TPS": "Produce Massage TPS", + "CUS_MSG_TPS": "Consumer Massage TPS", + "YESTERDAY_PRO_COUNT": "Yesterday Produce Count", + "YESTERDAY_CUS_COUNT": "Yesterday Consume Count", + "TODAY_PRO_COUNT": "Today Produce Count", + "TODAY_CUS_COUNT": "Today Consume Count", + "INSTANCE": "Instance", + "SPLIT": "Broker", + "CLUSTER": "Cluster", + "CLUSTER_DETAIL": "Cluster Detail", + "TOPIC": "Topic", + "SUBSCRIPTION_GROUP":"SubscriptionGroup", + "PRODUCER_GROUP":"ProducerGroup", + "CONSUMER":"Consumer", + "PRODUCER":"Producer", + "MESSAGE":"Message", + "MESSAGE_DETAIL":"Message Detail", + "RESEND_MESSAGE":"Resend Message", + "VIEW_EXCEPTION":"View Exception", + "MESSAGETRACE":"MessageTrace", + "CONFIG":"Config", + "COMMIT": "Commit", + "OPERATION": "Operation", + "ADD": "Add", + "UPDATE": "Update", + "STATUS": "Status", + "ROUTER": "Router", + "MANAGE": "Manage", + "CONFIG": "Config", + "SEND_MSG": "Send Massage", + "RESET_CUS_OFFSET": "Reset Consumer Offset", + "EDIT": "Edit", + "DELETE": "Delete", + "CHANGE_LANG": "ChangeLanguage", + "BROKER": "Broker", + "NORMAL": "NORMAL", + "RETRY": "RETRY", + "DLQ": "DLQ", + "QUANTITY":"Quantity", + "TYPE":"Type", + "MODE":"Mode", + "DELAY":"Delay", + "DASHBOARD":"Dashboard", + "CONSUME_DETAIL":"CONSUME DETAIL", + "CLIENT":"CLIENT", + "LAST_CONSUME_TIME":"LastConsumeTime", + "TIME":"Time", + "RESET":"RESET", + "DATE":"Date", + "NO_DATA":"NO DATA", + "SEARCH":"Search", + "BEGIN":"Begin", + "END":"End", + "TOPIC_CHANGE":"Topic Change", + "SEND":"Send", + "SUBSCRIPTION_CHANGE":"Subscription Change", + "QUEUE":"Queue", + "MIN_OFFSET":"minOffset", + "MAX_OFFSET":"maxOffset", + "LAST_UPDATE_TIME_STAMP":"lastUpdateTimeStamp", + "QUEUE_DATAS":"queueDatas", + "READ_QUEUE_NUMS":"readQueueNums", + "WRITE_QUEUE_NUMS":"writeQueueNums", + "PERM":"perm", + "TAG":"Tag", + "KEY":"Key", + "MESSAGE_BODY":"Message Body", + "TOPIC_NAME":"topicName", + "ORDER":"order", + "CONSUMER_CLIENT":"consumerClient", + "BROKER_OFFSET":"brokerOffset", + "CONSUMER_OFFSET":"consumerOffset", + "DIFF_TOTAL":"diffTotal", + "LAST_TIME_STAMP":"lastTimeStamp", + "RESET_OFFSET":"resetOffset", + "CLUSTER_NAME":"clusterName", + "PROJECT":"project", + "OPS":"OPS", + "AUTO_REFRESH":"AUTO_REFRESH", + "REFRESH":"REFRESH", + "LOGOUT":"Logout", + "LOGIN":"Login", + "USER_NAME":"Username", + "PASSWORD":"Password", + "NO_DATA":"Don't have ", + "SYSTEM":"SYSTEM", + "WELCOME":"Hi, welcome using RocketMQ Dashboard", + "ENABLE_MESSAGE_TRACE":"Enable Message Trace", + "MESSAGE_TRACE_DETAIL":"Message Trace Detail", + "TRACE_TOPIC":"TraceTopic", + "SELECT_TRACE_TOPIC":"selectTraceTopic" +} \ No newline at end of file diff --git a/src/main/resources/static/src/i18n/zh.js b/src/main/resources/static/src/i18n/zh.js new file mode 100644 index 0000000..7d8d465 --- /dev/null +++ b/src/main/resources/static/src/i18n/zh.js @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var zh = { + "TITLE": "RocketMQ仪表板", + "CLOSE": "关闭", + "NO": "编号", + "ADDRESS": "地址", + "VERSION": "版本", + "PRO_MSG_TPS": "生产消息TPS", + "CUS_MSG_TPS": "消费消息TPS", + "YESTERDAY_PRO_COUNT": "昨日生产总数", + "YESTERDAY_CUS_COUNT": "昨日消费总数", + "TODAY_PRO_COUNT": "今天生产总数", + "TODAY_CUS_COUNT": "今天消费总数", + "INSTANCE": "实例", + "SPLIT": "分片", + "CLUSTER": "集群", + "CLUSTER_DETAIL": "集群详情", + "COMMIT": "提交", + "TOPIC": "主题", + "SUBSCRIPTION_GROUP":"订阅组", + "PRODUCER_GROUP":"生产组", + "CONSUMER":"消费者", + "PRODUCER":"生产者", + "MESSAGE":"消息", + "MESSAGE_DETAIL":"消息详情", + "RESEND_MESSAGE":"重新消费", + "VIEW_EXCEPTION":"查看异常", + "MESSAGETRACE":"消息轨迹", + "CONFIG":"设置", + "OPERATION": "操作", + "ADD": "新增", + "UPDATE": "更新", + "STATUS": "状态", + "ROUTER": "路由", + "MANAGE": "管理", + "CONFIG": "配置", + "SEND_MSG": "发送消息", + "RESET_CUS_OFFSET": "重置消费位点", + "SKIP_MESSAGE_ACCUMULATE":"跳过堆积", + "EDIT": "编辑", + "DELETE": "删除", + "CHANGE_LANG": "更换语言", + "BROKER": "Broker", + "NORMAL": "普通", + "RETRY": "重试", + "DLQ": "死信", + "QUANTITY":"数量", + "TYPE":"类型", + "MODE":"模式", + "DELAY":"延迟", + "DASHBOARD":"驾驶舱", + "CONSUME_DETAIL":"消费详情", + "CLIENT":"终端", + "LAST_CONSUME_TIME":"最后消费时间", + "TIME":"时间点", + "RESET":"重置", + "DATE":"日期", + "NO_DATA":"暂无数据", + "SEARCH":"搜索", + "BEGIN":"开始", + "END":"结束", + "TOPIC_CHANGE":"修改主题", + "SEND":"发送", + "SUBSCRIPTION_CHANGE":"修改订阅", + "QUEUE":"队列", + "MIN_OFFSET":"最小位点", + "MAX_OFFSET":"最大位点", + "LAST_UPDATE_TIME_STAMP":"上次更新时间", + "QUEUE_DATAS":"队列信息", + "READ_QUEUE_NUMS":"读队列数量", + "WRITE_QUEUE_NUMS":"写队列数量", + "PERM":"perm", + "TAG":"标签", + "KEY":"值", + "MESSAGE_BODY":"消息主体", + "TOPIC_NAME":"主题名", + "ORDER":"顺序", + "CONSUMER_CLIENT":"消费者终端", + "BROKER_OFFSET":"代理者位点", + "CONSUMER_OFFSET":"消费者位点", + "DIFF_TOTAL":"差值", + "LAST_TIME_STAMP":"上次时间", + "RESET_OFFSET":"重置位点", + "CLUSTER_NAME":"集群名", + "PROJECT":"项目", + "OPS":"运维", + "AUTO_REFRESH":"自动刷新", + "REFRESH":"刷新", + "LOGOUT":"退出", + "LOGIN":"登录", + "USER_NAME":"用户名", + "PASSWORD":"密码", + "NO_DATA":"不存在 ", + "SYSTEM":"系统", + "WELCOME":"您好,欢迎使用RocketMQ仪表盘", + "ENABLE_MESSAGE_TRACE":"开启消息轨迹", + "MESSAGE_TRACE_DETAIL":"消息轨迹详情", + "TRACE_TOPIC":"消息轨迹主题", + "SELECT_TRACE_TOPIC":"选择消息轨迹主题" +} \ No newline at end of file diff --git a/src/main/resources/static/src/login.js b/src/main/resources/static/src/login.js new file mode 100644 index 0000000..5a5cbe3 --- /dev/null +++ b/src/main/resources/static/src/login.js @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +app.controller('loginController', ['$scope', '$location', '$http', 'Notification', '$cookies', '$window', function ($scope, $location, $http, Notification, $cookies, $window) { + $scope.login = function () { + if (!$("#username").val()) { + alert("用户名不能为空"); + return; + } + if (!$("#password").val()) { + alert("密码不能为空"); + return; + } + + $http({ + method: "POST", + url: "login/login.do", + params: {username: $("#username").val(), password: $("#password").val()} + }).success(function (resp) { + if (resp.status == 0) { + Notification.info({message: 'Login successful, redirect now', delay: 2000}); + $window.sessionStorage.setItem("username", resp.data.loginUserName); + $window.sessionStorage.setItem("userrole", resp.data.loginUserRole); + window.location = resp.data.contextPath; + initFlag = false; + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; +}]); diff --git a/src/main/resources/static/src/message.js b/src/main/resources/static/src/message.js new file mode 100644 index 0000000..af36808 --- /dev/null +++ b/src/main/resources/static/src/message.js @@ -0,0 +1,262 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var module = app; + +module.controller('messageController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) { + $scope.allTopicList = []; + $scope.selectedTopic = []; + $scope.key = ""; + $scope.messageId = ""; + $scope.queryMessageByTopicResult = []; + $scope.queryMessageByTopicAndKeyResult = []; + $scope.queryMessageByMessageIdResult = {}; + $http({ + method: "GET", + url: "topic/list.query", + params: { + skipSysProcess: true + } + }).success(function (resp) { + if (resp.status == 0) { + $scope.allTopicList = resp.data.topicList.sort(); + console.log($scope.allTopicList); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + $scope.timepickerBegin = moment().subtract(3, 'hour').format('YYYY-MM-DD HH:mm'); + $scope.timepickerEnd = moment().format('YYYY-MM-DD HH:mm'); + $scope.timepickerOptions = {format: 'YYYY-MM-DD HH:mm', showClear: true}; + + $scope.taskId = ""; + + $scope.paginationConf = { + currentPage: 1, + totalItems: 0, + itemsPerPage: 20, + pagesLength: 15, + perPageOptions: [10], + rememberPerPage: 'perPageItems', + onChange: function () { + $scope.queryMessagePageByTopic() + } + }; + + $scope.queryMessagePageByTopic = function () { + if ($scope.timepickerEnd < $scope.timepickerBegin) { + Notification.error({message: "endTime is later than beginTime!", delay: 2000}); + return + } + if ($scope.selectedTopic === [] || (typeof $scope.selectedTopic) == "object") { + return + } + $http({ + method: "POST", + url: "message/queryMessagePageByTopic.query", + data: { + topic: $scope.selectedTopic, + begin: $scope.timepickerBegin.valueOf(), + end: $scope.timepickerEnd.valueOf(), + pageNum: $scope.paginationConf.currentPage, + pageSize: $scope.paginationConf.itemsPerPage, + taskId: $scope.taskId + } + }).success(function (resp) { + if (resp.status === 0) { + console.log(resp); + $scope.messageShowList = resp.data.page.content; + if (resp.data.page.first) { + $scope.paginationConf.currentPage = 1; + } + $scope.paginationConf.currentPage = resp.data.page.number + 1; + $scope.paginationConf.totalItems = resp.data.page.totalElements; + $scope.taskId = resp.data.taskId + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + } + + $scope.queryMessageByTopic = function () { + console.log($scope.selectedTopic); + console.log($scope.timepickerBegin) + console.log($scope.timepickerEnd) + if ($scope.timepickerEnd < $scope.timepickerBegin) { + Notification.error({message: "endTime is later than beginTime!", delay: 2000}); + return + } + + $http({ + method: "GET", + url: "message/queryMessageByTopic.query", + params: { + topic: $scope.selectedTopic, + begin: $scope.timepickerBegin.valueOf(), + end: $scope.timepickerEnd.valueOf() + + } + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + $scope.queryMessageByTopicResult = resp.data; + $scope.changeShowMessageList(1, $scope.queryMessageByTopicResult.length); + // todo + // console.log($scope.queryMessageByTopicResult); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + + $scope.queryMessageByTopicAndKey = function () { + console.log($scope.selectedTopic); + console.log($scope.key); + $http({ + method: "GET", + url: "message/queryMessageByTopicAndKey.query", + params: { + topic: $scope.selectedTopic, + key: $scope.key + } + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + $scope.queryMessageByTopicAndKeyResult = resp.data; + console.log($scope.queryMessageByTopicAndKeyResult); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + + $scope.queryMessageByBrokerAndOffset = function (storeHost, commitLogOffset) { + $http({ + method: "GET", + url: "message/viewMessageByBrokerAndOffset.query", + params: { + brokerHost: storeHost.address, + port: storeHost.port, + offset: commitLogOffset + } + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + ngDialog.open({ + template: 'messageDetailViewDialog', + controller: 'messageDetailViewDialogController', + data: resp.data + }); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + + $scope.queryMessageByMessageId = function (messageId, topic) { + $http({ + method: "GET", + url: "message/viewMessage.query", + params: { + msgId: messageId, + topic: topic + } + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + ngDialog.open({ + template: 'messageDetailViewDialog', + controller: 'messageDetailViewDialogController', + data: resp.data + }); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + + $scope.changeShowMessageList = function (currentPage, totalItem) { + var perPage = $scope.paginationConf.itemsPerPage; + var from = (currentPage - 1) * perPage; + var to = (from + perPage) > totalItem ? totalItem : from + perPage; + $scope.messageShowList = $scope.queryMessageByTopicResult.slice(from, to); + $scope.paginationConf.totalItems = totalItem; + }; + + $scope.onChangeQueryCondition = function () { + console.log("change") + $scope.taskId = ""; + $scope.paginationConf.currentPage = 1; + $scope.paginationConf.totalItems = 0; + } +}]); + +module.controller('messageDetailViewDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) { + + $scope.resendMessage = function (messageView, consumerGroup) { + var topic = messageView.topic; + var msgId = messageView.msgId; + console.log('===' + topic + '===' + msgId); + if (topic.startsWith('%DLQ%')) { + if (messageView.properties.hasOwnProperty("RETRY_TOPIC")) { + topic = messageView.properties.RETRY_TOPIC; + } + if (messageView.properties.hasOwnProperty("ORIGIN_MESSAGE_ID")) { + msgId = messageView.properties.ORIGIN_MESSAGE_ID; + } + + } + console.log('===' + topic + '===' + msgId); + $http({ + method: "POST", + url: "message/consumeMessageDirectly.do", + params: { + msgId: msgId, + consumerGroup: consumerGroup, + topic: topic + } + }).success(function (resp) { + if (resp.status == 0) { + ngDialog.open({ + template: 'operationResultDialog', + data: { + result: resp.data + } + }); + } else { + ngDialog.open({ + template: 'operationResultDialog', + data: { + result: resp.errMsg + } + }); + } + }); + }; + $scope.showExceptionDesc = function (errmsg) { + if (errmsg == null) { + errmsg = "Don't have Exception" + } + ngDialog.open({ + template: 'operationResultDialog', + data: { + result: errmsg + } + }); + }; + }] +); \ No newline at end of file diff --git a/src/main/resources/static/src/messageTrace.js b/src/main/resources/static/src/messageTrace.js new file mode 100644 index 0000000..8cecc25 --- /dev/null +++ b/src/main/resources/static/src/messageTrace.js @@ -0,0 +1,398 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var module = app; +const SUCCESS_COLOR = '#75d874'; +const ERROR_COLOR = 'red'; +const UNKNOWN_COLOR = 'yellow'; +const TRANSACTION_COMMIT_COLOR = SUCCESS_COLOR; +const TRANSACTION_ROLLBACK_COLOR = ERROR_COLOR; +const TRANSACTION_UNKNOWN_COLOR = 'grey' +const TIME_FORMAT_PATTERN = "YYYY-MM-DD HH:mm:ss.SSS"; +const DEFAULT_DISPLAY_DURATION = 10 * 1000 +// transactionTraceNode do not have costTime, assume it cost 50ms +const TRANSACTION_CHECK_COST_TIME = 50; +const RETRY_GROUP_TOPIC_PREFIX = "%RETRY%"; +const DLQ_GROUP_TOPIC_PREFIX = "%DLQ%"; +module.controller('messageTraceController', ['$scope', '$routeParams', 'ngDialog', '$http', 'Notification', function ($scope, $routeParams, ngDialog, $http, Notification) { + $scope.allTopicList = []; + $scope.selectedTopic = []; + $scope.allTraceTopicList = []; + $scope.selectedTraceTopic = []; + $scope.key = ""; + $scope.messageId = $routeParams.messageId; + $scope.queryMessageByTopicAndKeyResult = []; + $scope.queryMessageByMessageIdResult = {}; + $scope.queryMessageTraceListsByTopicAndKeyResult = []; + + $http({ + method: "GET", + url: "topic/list.query", + params: { + skipSysProcess: true + } + }).success(function (resp) { + if (resp.status == 0) { + $scope.allTopicList = resp.data.topicList.sort(); + console.log($scope.allTopicList) + for (const topic of $scope.allTopicList) { + if (topic.startsWith(RETRY_GROUP_TOPIC_PREFIX) + || topic.startsWith(DLQ_GROUP_TOPIC_PREFIX)) { + continue; + } + $scope.allTraceTopicList.push(topic); + } + console.log($scope.allTraceTopicList) + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + + $scope.timepickerBegin = moment().subtract(1, 'hour').format('YYYY-MM-DD HH:mm'); + $scope.timepickerEnd = moment().add(1, 'hour').format('YYYY-MM-DD HH:mm'); + $scope.timepickerOptions = {format: 'YYYY-MM-DD HH:mm', showClear: true}; + + $scope.queryMessageByTopicAndKey = function () { + console.log($scope.selectedTopic); + console.log($scope.key); + $http({ + method: "GET", + url: "message/queryMessageByTopicAndKey.query", + params: { + topic: $scope.selectedTopic, + key: $scope.key + } + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + $scope.queryMessageByTopicAndKeyResult = resp.data; + console.log($scope.queryMessageByTopicAndKeyResult); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + + $scope.queryMessageByMessageId = function (messageId, topic) { + $http({ + method: "GET", + url: "messageTrace/viewMessage.query", + params: { + msgId: messageId, + topic: topic + } + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + $scope.queryMessageByMessageIdResult = resp.data; + console.log($scope.queryMessageByTopicAndKeyResult); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + + $scope.queryMessageTraceByMessageId = function (messageId, topic) { + $http({ + method: "GET", + url: "messageTrace/viewMessageTraceGraph.query", + params: { + msgId: messageId, + traceTopic: topic + } + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + ngDialog.open({ + template: 'messageTraceDetailViewDialog', + controller: 'messageTraceDetailViewDialogController', + data: resp.data + }); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; +}]); + +module.controller('messageTraceDetailViewDialogController', ['$scope', '$timeout', 'ngDialog', '$http', 'Notification', function ($scope, $timeout, ngDialog, $http, Notification) { + $scope.displayMessageTraceGraph = function (messageTraceGraph) { + let dom = document.getElementById("messageTraceGraph"); + $scope.messageTraceGraph = echarts.init(dom); + let option; + let data = []; + let dataZoomEnd = 100; + let startTime = Number.MAX_VALUE; + let endTime = 0; + let messageGroups = []; + if (messageTraceGraph.producerNode) { + startTime = +messageTraceGraph.producerNode.traceNode.beginTimestamp; + endTime = +messageTraceGraph.producerNode.traceNode.endTimestamp; + } + + function buildNodeColor(traceNode) { + if (traceNode.transactionState != null) { + switch (traceNode.transactionState) { + case 'COMMIT_MESSAGE': + return TRANSACTION_COMMIT_COLOR; + case 'ROLLBACK_MESSAGE': + return TRANSACTION_ROLLBACK_COLOR; + case 'UNKNOW': + return TRANSACTION_UNKNOWN_COLOR; + default: + return ERROR_COLOR; + } + } + switch (traceNode.status) { + case 'failed': + return ERROR_COLOR; + case 'unknown': + return UNKNOWN_COLOR; + default: + return SUCCESS_COLOR; + } + } + + function formatXAxisTime(value) { + let duration = Math.max(0, value - startTime); + if (duration < 1000) + return timeFormat(duration, 'ms'); + duration /= 1000; + if (duration < 60) + return timeFormat(duration, 's'); + duration /= 60; + if (duration < 60) + return timeFormat(duration, 'min'); + duration /= 60; + return timeFormat(duration, 'h'); + } + + function timeFormat(duration, unit) { + return duration.toFixed(2) + unit; + } + + + function buildTraceInfo(itemName, itemValue) { + if (itemValue) { + return `${itemName}: ${itemValue}
` + } + return ""; + } + + function formatCostTimeStr(costTime) { + if (costTime < 0) { + return ""; + } + let costTimeStr = costTime; + if (costTime === 0) { + costTimeStr = '<1' + } + return `${costTimeStr}ms`; + } + + function buildCostTimeInfo(costTime) { + if (costTime < 0) { + return ""; + } + return `costTime: ${formatCostTimeStr(costTime)}
` + } + function buildTimeStamp(timestamp){ + if(timestamp < 0){ + return 'N/A'; + } + return new moment(timestamp).format(TIME_FORMAT_PATTERN); + } + + function formatNodeToolTip(params) { + let traceNode = params.data.traceData.traceNode; + return ` + ${buildCostTimeInfo(traceNode.costTime)} + status: ${traceNode.status}
+ ${buildTraceInfo('beginTimestamp', buildTimeStamp(traceNode.beginTimestamp))} + ${buildTraceInfo('endTimestamp', buildTimeStamp(traceNode.endTimestamp))} + clientHost: ${traceNode.clientHost}
+ storeHost: ${traceNode.storeHost}
+ retryTimes: ${traceNode.retryTimes < 0 ? 'N/A' : traceNode.retryTimes}
+ ${buildTraceInfo('msgType', traceNode.msgType)} + ${buildTraceInfo('transactionId', traceNode.transactionId)} + ${buildTraceInfo('transactionState', traceNode.transactionState)} + ${buildTraceInfo('fromTransactionCheck', traceNode.fromTransactionCheck)} + `; + } + + function calcGraphTimestamp(timestamp, relativeTimeStamp, duration, addDuration) { + if (timestamp > 0) { + return timestamp; + } + if (duration < 0) { + return relativeTimeStamp; + } + return addDuration ? relativeTimeStamp + duration : relativeTimeStamp - duration; + } + + function addTraceData(traceNode, index) { + if (traceNode.beginTimestamp < 0 && traceNode.endTimestamp < 0) { + return; + } + let beginTimestamp = calcGraphTimestamp(traceNode.beginTimestamp, traceNode.endTimestamp, traceNode.costTime, false); + let endTimestamp = calcGraphTimestamp(traceNode.endTimestamp, traceNode.beginTimestamp, traceNode.costTime, true); + if (endTimestamp === beginTimestamp) { + endTimestamp = beginTimestamp + 1; + } + console.log("beginTimestamp",beginTimestamp,'endTimestamp',endTimestamp); + data.push({ + value: [ + index, + beginTimestamp, + endTimestamp, + traceNode.costTime + ], + itemStyle: { + normal: { + color: buildNodeColor(traceNode), + opacity: 1 + } + }, + traceData: { + traceNode: traceNode + } + }); + startTime = Math.min(startTime, beginTimestamp); + endTime = Math.max(endTime, endTimestamp); + } + + messageTraceGraph.subscriptionNodeList.forEach(item => { + messageGroups.push(item.subscriptionGroup) + }) + messageTraceGraph.subscriptionNodeList.forEach((subscriptionNode, index) => { + subscriptionNode.consumeNodeList.forEach(traceNode => addTraceData(traceNode, index)) + }) + if (messageTraceGraph.producerNode) { + messageGroups.push(messageTraceGraph.producerNode.groupName) + let producerNodeIndex = messageGroups.length - 1; + addTraceData(messageTraceGraph.producerNode.traceNode, producerNodeIndex); + messageTraceGraph.producerNode.transactionNodeList.forEach(transactionNode => { + transactionNode.beginTimestamp = Math.max(messageTraceGraph.producerNode.traceNode.endTimestamp, + transactionNode.endTimestamp - TRANSACTION_CHECK_COST_TIME); + addTraceData(transactionNode, producerNodeIndex) + endTime = Math.max(endTime, transactionNode.endTimestamp); + }) + } + let totalDuration = endTime - startTime; + if (totalDuration > DEFAULT_DISPLAY_DURATION) { + dataZoomEnd = DEFAULT_DISPLAY_DURATION / totalDuration * 100 + } + + function renderItem(params, api) { + let messageGroup = api.value(0); + let start = api.coord([api.value(1), messageGroup]); + let end = api.coord([api.value(2), messageGroup]); + let height = api.size([0, 1])[1] * 0.6; + + let rectShape = echarts.graphic.clipRectByRect({ + x: start[0], + y: start[1] - height / 2, + width: Math.max(end[0] - start[0], 1), + height: height + }, { + x: params.coordSys.x, + y: params.coordSys.y, + width: params.coordSys.width, + height: params.coordSys.height + }); + + return rectShape && { + type: 'rect', + transition: ['shape'], + shape: rectShape, + style: api.style({ + text: formatCostTimeStr(api.value(3)), + textFill: '#000' + }) + }; + } + + option = { + tooltip: { + formatter: function (params) { + return formatNodeToolTip(params); + } + }, + title: { + text: messageTraceGraph.producerNode ? messageTraceGraph.producerNode.topic : "", + left: 'center' + }, + dataZoom: [{ + type: 'slider', + filterMode: 'weakFilter', + showDataShadow: false, + top: 400, + start: 0, + end: dataZoomEnd, + labelFormatter: '' + }, { + type: 'inside', + filterMode: 'weakFilter' + } + ], + grid: { + height: 300 + }, + xAxis: { + min: startTime, + scale: true, + axisLabel: { + formatter: function (value) { + return formatXAxisTime(value) + } + } + }, + yAxis: { + data: messageGroups + }, + series: [{ + type: 'custom', + renderItem: renderItem, + itemStyle: { + opacity: 0.8 + }, + encode: { + x: [1, 2], + y: 0 + }, + data: data + }] + }; + $scope.messageTraceGraph.setOption(option); + } + $scope.showGraph = function () { + $scope.displayMessageTraceGraph($scope.ngDialogData); + }; + + function initGraph() { + $timeout(function () { + if (document.getElementById('messageTraceGraph') == null) { + initGraph(); + } else { + $scope.showGraph(); + } + }, 50); + } + + initGraph(); + }] +); \ No newline at end of file diff --git a/src/main/resources/static/src/ops.js b/src/main/resources/static/src/ops.js new file mode 100644 index 0000000..8338b06 --- /dev/null +++ b/src/main/resources/static/src/ops.js @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +app.controller('opsController', ['$scope', '$location', '$http', 'Notification', 'remoteApi', 'tools', '$window', function ($scope, $location, $http, Notification, remoteApi, tools, $window) { + $scope.namesvrAddrList = ""; + $scope.useVIPChannel = true; + $scope.useTLS = false; + $scope.userRole = $window.sessionStorage.getItem("userrole"); + $scope.writeOperationEnabled = $scope.userRole == null ? true : ($scope.userRole == 1 ? true : false); + $http({ + method: "GET", + url: "ops/homePage.query" + }).success(function (resp) { + if (resp.status == 0) { + $scope.namesvrAddrList = resp.data.namesvrAddrList.join(";"); + $scope.useVIPChannel = resp.data.useVIPChannel; + $scope.useTLS = resp.data.useTLS; + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + + $scope.updateNameSvrAddr = function () { + $http({ + method: "POST", + url: "ops/updateNameSvrAddr.do", + params: {nameSvrAddrList: $scope.namesvrAddrList} + }).success(function (resp) { + if (resp.status == 0) { + Notification.info({message: "SUCCESS", delay: 2000}); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + $scope.updateIsVIPChannel = function () { + $http({ + method: "POST", + url: "ops/updateIsVIPChannel.do", + params: {useVIPChannel: $scope.useVIPChannel} + }).success(function (resp) { + if (resp.status == 0) { + Notification.info({message: "SUCCESS", delay: 2000}); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + $scope.updateUseTLS = function () { + $http({ + method: "POST", + url: "ops/updateUseTLS.do", + params: {useTLS: $scope.useTLS} + }).success(function (resp) { + if (resp.status == 0) { + Notification.info({message: "SUCCESS", delay: 2000}); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + } +}]); diff --git a/src/main/resources/static/src/producer.js b/src/main/resources/static/src/producer.js new file mode 100644 index 0000000..bdbb605 --- /dev/null +++ b/src/main/resources/static/src/producer.js @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var module = app; +module.controller('producerController', ['$scope', '$http','Notification',function ($scope, $http,Notification) { + $scope.selectedTopic=[]; + $scope.producerGroup=""; + $http({ + method: "GET", + url: "topic/list.query", + params:{ + skipSysProcess: true + } + }).success(function (resp) { + if(resp.status ==0){ + $scope.allTopicList = resp.data.topicList.sort(); + console.log($scope.allTopicList); + }else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + $scope.queryClientByTopicAndGroup = function () { + $http({ + method: "GET", + url: "producer/producerConnection.query", + params:{ + topic:$scope.selectedTopic, + producerGroup:$scope.producerGroup + } + }).success(function (resp) { + if(resp.status ==0){ + $scope.connectionList = resp.data.connectionSet; + }else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + } +} ]); \ No newline at end of file diff --git a/src/main/resources/static/src/remoteApi/remoteApi.js b/src/main/resources/static/src/remoteApi/remoteApi.js new file mode 100644 index 0000000..1189771 --- /dev/null +++ b/src/main/resources/static/src/remoteApi/remoteApi.js @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +app.service('remoteApi', ['$http','tools', function ($http,tools) { + var queryTopic = function(callback){ + $http({ + method: "GET", + url: "topic/list.query" + }).success(callback); + } + + var queryClusterList = function(callback){ + $http({ + method: "GET", + url: "cluster/list.query" + }).success(callback); + } + + var queryBrokerHisData = function(date,callback){ + var url = 'dashboard/broker.query'; + var data = {date:date}; + var setting = { + type: "GET", + data:data, + timeout:15000,//data is too large,so master set time out is long enough + success:callback + } + $.ajax(url,setting) + } + + var queryTopicHisData = function(date,topicName,callback){ + var url = 'dashboard/topic.query'; + var data = {date:date,topicName:topicName}; + var setting = { + type: "GET", + data:data, + timeout:15000,//data is too large,so master set time out is long enough + success:callback + } + $.ajax(url,setting) + } + + var queryTopicCurrentData = function(callback){ + var url = 'dashboard/topicCurrent'; + var setting = { + type: "GET", + timeout:15000,//data is too large,so master set time out is long enough + success:callback + } + $.ajax(url,setting) + } + + + return { + queryTopic:queryTopic, + queryClusterList:queryClusterList, + queryBrokerHisData:queryBrokerHisData, + queryTopicHisData:queryTopicHisData, + queryTopicCurrentData:queryTopicCurrentData + } +}]) + diff --git a/src/main/resources/static/src/tools/tools.js b/src/main/resources/static/src/tools/tools.js new file mode 100644 index 0000000..9ab5ae7 --- /dev/null +++ b/src/main/resources/static/src/tools/tools.js @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +app.service('tools', ['$http', function ($http) { + + var ctx = ""; + var dashboardRefreshTime = 5000; // todo improve. when data size is large,request is too slow + + var generateBrokerMap = function (brokerServer, clusterAddrTable, brokerAddrTable) { + var map = {}; + $.each(brokerServer, function (brokerName, brokerStatusList) { // broker + $.each(clusterAddrTable, function (clusterName, brokerNameList) { //clusterAddrTable + if (angular.isUndefined(map[clusterName])) { + map[clusterName] = []; + } + $.each(brokerNameList, function (listIndex, clusterBrokerName) { + if (clusterBrokerName == brokerName) { + $.each(brokerStatusList, function (index, brokerStatus) { + brokerStatus.split = brokerName; + brokerStatus.index = index; + brokerStatus.address = brokerAddrTable[clusterBrokerName].brokerAddrs[index]; + brokerStatus.brokerName = brokerAddrTable[clusterBrokerName].brokerName; + map[clusterName].push(brokerStatus); + }) + } + }) + }) + }); + return map; + }; + + var fastSort = function (arrayToSort, propertyToSortWith, sortDirection) { + // temporary holder of position and sort-value + var map = arrayToSort.map(function (e, i) { + if (typeof e[propertyToSortWith] === 'string') { + return { index: i, value: e[propertyToSortWith].toLowerCase() }; + } + else { + return { index: i, value: e[propertyToSortWith] }; + } + + }) + + // sorting the map containing the reduced values + map.sort(function (a, b) { + if (sortDirection === "ascending") { + return +(a.value > b.value) || +(a.value === b.value) - 1; + } + else { + return +(a.value < b.value) || +(a.value === b.value) - 1; + } + + }); + + // container for the resulting order + var result = map.map(function (e) { + return arrayToSort[e.index]; + }); + return result; + }; + + return { + generateBrokerMap:generateBrokerMap, + fastSort:fastSort, + ctx:ctx, + dashboardRefreshTime:dashboardRefreshTime + } +}]) + + + diff --git a/src/main/resources/static/src/topic.js b/src/main/resources/static/src/topic.js new file mode 100644 index 0000000..60be40f --- /dev/null +++ b/src/main/resources/static/src/topic.js @@ -0,0 +1,501 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var module = app; + +module.directive('ngConfirmClick', [ + function () { + return { + link: function (scope, element, attr) { + var msg = attr.ngConfirmClick || "Are you sure?"; + var clickAction = attr.confirmedClick; + element.bind('click', function (event) { + if (window.confirm(msg)) { + scope.$eval(clickAction) + } + }); + } + }; + }]); +module.controller('topicController', ['$scope', 'ngDialog', '$http', 'Notification', '$window', function ($scope, ngDialog, $http, Notification, $window) { + $scope.paginationConf = { + currentPage: 1, + totalItems: 0, + itemsPerPage: 10, + pagesLength: 15, + perPageOptions: [10], + rememberPerPage: 'perPageItems', + onChange: function () { + $scope.showTopicList(this.currentPage, this.totalItems); + + } + }; + $scope.filterNormal = true + $scope.filterRetry = false + $scope.filterDLQ = false + $scope.filterSystem = false + $scope.allTopicList = []; + $scope.topicShowList = []; + $scope.userRole = $window.sessionStorage.getItem("userrole"); + $scope.writeOperationEnabled = $scope.userRole == null ? true : ($scope.userRole == 1 ? true : false); + + $scope.refreshTopicList = function () { + $http({ + method: "GET", + url: "topic/list.query" + }).success(function (resp) { + if (resp.status == 0) { + $scope.allTopicList = resp.data.topicList.sort(); + console.log($scope.allTopicList); + console.log(JSON.stringify(resp)); + $scope.showTopicList(1, $scope.allTopicList.length); + + } else { + Notification.error({message: resp.errMsg, delay: 5000}); + } + }); + }; + + $scope.refreshTopicList(); + + $scope.filterStr = ""; + $scope.$watch('filterStr', function () { + $scope.filterList(1); + }); + $scope.$watch('filterNormal', function () { + $scope.filterList(1); + }); + $scope.$watch('filterRetry', function () { + $scope.filterList(1); + }); + $scope.$watch('filterDLQ', function () { + $scope.filterList(1); + }); + $scope.$watch('filterSystem', function () { + $scope.filterList(1); + }); + $scope.filterList = function (currentPage) { + var lowExceptStr = $scope.filterStr.toLowerCase(); + var canShowList = []; + + $scope.allTopicList.forEach(function (element) { + if ($scope.filterByType(element)) { + if (element.toLowerCase().indexOf(lowExceptStr) != -1) { + canShowList.push(element); + } + } + }); + $scope.paginationConf.totalItems = canShowList.length; + var perPage = $scope.paginationConf.itemsPerPage; + var from = (currentPage - 1) * perPage; + var to = (from + perPage) > canShowList.length ? canShowList.length : from + perPage; + $scope.topicShowList = canShowList.slice(from, to); + }; + + $scope.filterByType = function (str) { + if ($scope.filterRetry) { + if (str.startsWith("%R")) { + return true + } + } + if ($scope.filterDLQ) { + if (str.startsWith("%D")) { + return true + } + } + if ($scope.filterSystem) { + if (str.startsWith("%S")) { + return true + } + } + if ($scope.filterNormal) { + if (str.startsWith("%") == false) { + return true + } + } + return false; + }; + + $scope.showTopicList = function (currentPage, totalItem) { + if ($scope.filterStr != "") { + $scope.filterList(currentPage); + return; + } + var perPage = $scope.paginationConf.itemsPerPage; + var from = (currentPage - 1) * perPage; + var to = (from + perPage) > totalItem ? totalItem : from + perPage; + console.log($scope.allTopicList); + console.log(from) + console.log(to) + $scope.topicShowList = $scope.allTopicList.slice(from, to); + $scope.paginationConf.totalItems = totalItem; + console.log($scope.topicShowList) + console.log($scope.paginationConf.totalItems) + $scope.filterList(currentPage); + }; + $scope.deleteTopic = function (topic) { + $http({ + method: "POST", + url: "topic/deleteTopic.do", + params: { + topic: topic + } + }).success(function (resp) { + if (resp.status == 0) { + Notification.info({message: "delete success!", delay: 2000}); + $scope.refreshTopicList(); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + $scope.statsView = function (topic) { + $http({ + method: "GET", + url: "topic/stats.query", + params: {topic: topic} + }).success(function (resp) { + if (resp.status == 0) { + console.log(JSON.stringify(resp)); + ngDialog.open({ + template: 'statsViewDialog', + trapFocus: false, + data: { + topic: topic, + statsData: resp.data + } + }); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }) + }; + $scope.routerView = function (topic) { + $http({ + method: "GET", + url: "topic/route.query", + params: {topic: topic} + }).success(function (resp) { + if (resp.status == 0) { + console.log(JSON.stringify(resp)); + ngDialog.open({ + template: 'routerViewDialog', + controller: 'routerViewDialogController', + trapFocus: false, + data: { + topic: topic, + routeData: resp.data + } + }); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }) + }; + + + $scope.consumerView = function (topic) { + $http({ + method: "GET", + url: "topic/queryConsumerByTopic.query", + params: {topic: topic} + }).success(function (resp) { + if (resp.status == 0) { + console.log(JSON.stringify(resp)); + ngDialog.open({ + template: 'consumerViewDialog', + data: { + topic: topic, + consumerData: resp.data, + consumerGroupCount: Object.keys(resp.data).length + } + }); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }) + }; + $scope.openDeleteTopicDialog = function (topic) { + ngDialog.open({ + template: 'deleteTopicDialog', + controller: 'deleteTopicDialogController', + data: { + topic: topic, + consumerData: "asd" + } + }); + }; + + $scope.openConsumerResetOffsetDialog = function (topic) { + + $http({ + method: "GET", + url: "topic/queryTopicConsumerInfo.query", + params: { + topic: topic + } + }).success(function (resp) { + if (resp.status == 0) { + if (resp.data.groupList == null) { + Notification.error({message: "don't have consume group!", delay: 2000}); + return + } + ngDialog.open({ + template: 'consumerResetOffsetDialog', + controller: 'consumerResetOffsetDialogController', + data: { + topic: topic, + selectedConsumerGroup: [], + allConsumerGroupList: resp.data.groupList + } + }); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + + }; + + $scope.openSkipMessageAccumulateDialog = function (topic) { + $http({ + method: "GET", + url: "topic/queryTopicConsumerInfo.query", + params: { + topic: topic + } + }).success(function (resp) { + if (resp.status == 0) { + if (resp.data.groupList == null) { + Notification.error({message: "don't have consume group!", delay: 2000}); + return + } + ngDialog.open({ + template: 'skipMessageAccumulateDialog', + controller: 'skipMessageAccumulateDialogController', + data: { + topic: topic, + selectedConsumerGroup: [], + allConsumerGroupList: resp.data.groupList + } + }); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + + $scope.openSendTopicMessageDialog = function (topic) { + ngDialog.open({ + template: 'sendTopicMessageDialog', + controller: 'sendTopicMessageDialogController', + data: { + topic: topic + } + }); + }; + + $scope.openUpdateDialog = function (topic, sysFlag) { + $http({ + method: "GET", + url: "topic/examineTopicConfig.query", + params: { + topic: topic + } + }).success(function (resp) { + if (resp.status == 0) { + $scope.openCreateOrUpdateDialog(resp.data, sysFlag); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + }; + + $scope.openCreateOrUpdateDialog = function (request, sysFlag) { + console.log('request',request) + var bIsUpdate = true; + if (request == null) { + request = [{ + writeQueueNums: 16, + readQueueNums: 16, + perm: 6, + order: false, + topicName: "", + brokerNameList: [] + }]; + bIsUpdate = false; + } + $http({ + method: "GET", + url: "cluster/list.query" + }).success(function (resp) { + if (resp.status == 0) { + console.log(resp); + ngDialog.open({ + preCloseCallback: function (value) { + // Refresh topic list + $scope.refreshTopicList(); + }, + template: 'topicModifyDialog', + controller: 'topicModifyDialogController', + data: { + sysFlag: sysFlag, + topicRequestList: request, + allClusterNameList: Object.keys(resp.data.clusterInfo.clusterAddrTable), + allBrokerNameList: Object.keys(resp.data.brokerServer), + bIsUpdate: bIsUpdate, + writeOperationEnabled: $scope.writeOperationEnabled + } + }); + } + }); + } + + $scope.openAddDialog = function () { + $scope.openCreateOrUpdateDialog(null, false); + } + +}]); + +module.controller('topicModifyDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) { + $scope.postTopicRequest = function (topicRequestItem) { + console.log(topicRequestItem); + var request = JSON.parse(JSON.stringify(topicRequestItem)); + console.log(request); + $http({ + method: "POST", + url: "topic/createOrUpdate.do", + data: request + }).success(function (resp) { + if (resp.status == 0) { + Notification.info({message: "success!", delay: 2000}); + ngDialog.close(this); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }); + } + }] +); +module.controller('consumerResetOffsetDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) { + $scope.timepicker = {}; + $scope.timepicker.date = moment().format('YYYY-MM-DD HH:mm'); + $scope.timepicker.options = {format: 'YYYY-MM-DD HH:mm', showClear: true}; + $scope.resetOffset = function () { + console.log($scope.timepicker.date); + console.log($scope.timepicker.date.valueOf()); + console.log($scope.ngDialogData.selectedConsumerGroup); + $http({ + method: "POST", + url: "consumer/resetOffset.do", + data: { + resetTime: $scope.timepicker.date.valueOf(), + consumerGroupList: $scope.ngDialogData.selectedConsumerGroup, + topic: $scope.ngDialogData.topic, + force: true + } + }).success(function (resp) { + if (resp.status == 0) { + ngDialog.open({ + template: 'resetOffsetResultDialog', + data: { + result: resp.data + } + }); + ngDialog.close(this); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }) + } + }] +); + +module.controller('skipMessageAccumulateDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) { + $scope.skipAccumulate = function () { + console.log($scope.ngDialogData.selectedConsumerGroup); + $http({ + method: "POST", + url: "consumer/skipAccumulate.do", + data: { + resetTime: -1, + consumerGroupList: $scope.ngDialogData.selectedConsumerGroup, + topic: $scope.ngDialogData.topic, + force: true + } + }).success(function (resp) { + if (resp.status == 0) { + ngDialog.open({ + template: 'resetOffsetResultDialog', + data: { + result: resp.data + } + }); + ngDialog.close(this); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }) + } + }] +); + +module.controller('sendTopicMessageDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) { + $scope.sendTopicMessage = { + topic: $scope.ngDialogData.topic, + key: "key", + tag: "tag", + messageBody: "messageBody", + traceEnabled: false + }; + $scope.send = function () { + $http({ + method: "POST", + url: "topic/sendTopicMessage.do", + data: $scope.sendTopicMessage + }).success(function (resp) { + if (resp.status == 0) { + ngDialog.open({ + template: 'sendResultDialog', + data: { + result: resp.data + } + }); + ngDialog.close(this); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }) + } + }] +); + +module.controller('routerViewDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) { + $scope.deleteTopicByBroker = function (broker) { + $http({ + method: "POST", + url: "topic/deleteTopicByBroker.do", + params: {brokerName: broker.brokerName, topic: $scope.ngDialogData.topic} + }).success(function (resp) { + if (resp.status == 0) { + Notification.info({message: "delete success", delay: 2000}); + } else { + Notification.error({message: resp.errMsg, delay: 2000}); + } + }) + }; + }] +); \ No newline at end of file diff --git a/src/main/resources/static/style/animate.css b/src/main/resources/static/style/animate.css new file mode 100644 index 0000000..596ef5c --- /dev/null +++ b/src/main/resources/static/style/animate.css @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.view { +} + +.view .ng-enter { + overflow-y: auto; +} + +.view .ng-leave { + opacity: 0; + -webkit-transition: opacity .2s linear; + transition: opacity 0.2s linear; +} + +.ng-hide-add { + transform: rotateZ(0); + transform-origin: right; + transition: all 0.2s ease-in-out; +} + +.ng-hide-add.ng-hide-add-active { + transform: rotateZ(-135deg); +} + +.ng-hide-remove { + transform: rotateY(90deg); + transform-origin: left; + transition: all 0.2s ease; +} + +.ng-hide-remove.ng-hide-remove-active { + transform: rotateY(0); +} diff --git a/src/main/resources/static/style/app.css b/src/main/resources/static/style/app.css new file mode 100644 index 0000000..d9ac084 --- /dev/null +++ b/src/main/resources/static/style/app.css @@ -0,0 +1,291 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Write your styles */ +.scrollTo { + position: fixed; + right: 50px; + bottom: 70px; + z-index: 999; +} + +/*.icon_u_c{background:url(/app/img/icon_user_center.png) no-repeat left top;}*/ +.icon { + float: left; + width: 24px; + height: 24px; + display: inline-block; + margin: 10px 18px 0 5px; +} +.icon_alipay { + background:url("/style/img/alipay.png") no-repeat left top; + height:100px; + width: 100px; + margin-top: -40px; +} + +.icon_alipay_active { + background:url("/style/img/alipay_active.png") no-repeat left top; + height:100px; + width: 100px; + margin-top: -40px; +} + +.icon_weipay { + background:url("/style/img/weipay.png") no-repeat left top; + height:100px; + width: 100px; + margin-top: -40px; +} + +.icon_weipay_active { + background:url("/style/img/weipay_active.png") no-repeat left top; + height:100px; + width: 100px; + margin-top: -40px; +} + +.round { + background:#5bc0de; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius:5px; + color: #FFFFFF; +} + + +/* ANIMATIONS +============================================================================= */ + +/* leaving animations ----------------------------------------- */ +/* rotate and fall */ +@keyframes rotateFall { + 0% { transform: rotateZ(0deg); } + 20% { transform: rotateZ(10deg); animation-timing-function: ease-out; } + 40% { transform: rotateZ(17deg); } + 60% { transform: rotateZ(16deg); } + 100% { transform: translateY(100%) rotateZ(17deg); } +} + +/* slide in from the bottom */ +@keyframes slideOutLeft { + to { transform: translateX(-100%); } +} + +/*!* rotate out newspaper *!*/ +@keyframes rotateOutNewspaper { + to { transform: translateZ(-3000px) rotateZ(360deg); opacity: 0; } +} + +/* entering animations --------------------------------------- */ +/* scale up */ +@keyframes scaleUp { + from { opacity: 0.3; -webkit-transform: scale(0.8); } +} + +/* slide in from the right */ +@keyframes slideInRight { + from { transform:translateX(100%); } + to { transform: translateX(0); } +} + +/* slide in from the bottom */ +@keyframes slideInUp { + from { transform:translateY(100%); } + to { transform: translateY(0); } +} + +.ng-enter { animation: scaleUp 0.5s both ease-in; z-index: 8888; } +.ng-leave { animation: slideOutLeft 0.2s both ease-in; z-index: 9999; } + + + + +.managerList { + list-style:none; + position: relative; + width: 100%; + margin-left: 20px; +} + +.managerList > li { + width: 95%; + margin-top:10px; +} + +.managerList ul { + list-style-type:none; + float:left; + padding: 0; +} + +.managerList li { + list-style-type:none; + float:left; + padding: 0; +} + +.managerList .head { + width: 90%; +} + +.managerList .footer { + width: 90%; + margin-top: 10px; + margin-left: 50px; +} + +.managerList .checkbox { + width: 50px; +} + +.managerList .liCheck { + width: 50px; +} + +.managerList .liContent { + width: 90%; +} + +.managerList .preview { + width: 50%; + margin-left: 50px; + margin-top: 10px; +} + +.managerList .logo { + margin-top: 9px; + width: 50px; +} + +.managerList .content{ + width: 50%; +} + +.managerList .content ul{ + width: 90%; +} + +.managerList .butt{ + /*width: 20%;*/ +} + +.badgeCustomer { + background-color:#f44336; + margin-top:23px; +} + +.chatPanel { + width: 90%; + float: left; + border: 1px solid #d4d4d4; + border-radius: 2px; + padding: 20px; + position: relative; + -webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175); + box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175); + float: right; +} + +.chatPanel:after{ + position: absolute; + top: 27px; + right: -14px; + display: inline-block; + border-top: 14px solid transparent; + border-left: 14px solid #fff; + border-right: 0 solid #fff; + border-bottom: 14px solid transparent; + content: " "; +} + +.chatPanel:before{ + position: absolute; + top: 26px; + right: -15px; + display: inline-block; + border-top: 15px solid transparent; + border-left: 15px solid #ccc; + border-right: 0 solid #ccc; + border-bottom: 15px solid transparent; + content: " "; +} + +.chatPanel-left { + float: left; +} +.chatPanel-left:before { + border-left-width: 0; + border-right-width: 15px; + left: -15px; + right: auto; +} +.chatPanel-left:after { + border-left-width: 0; + border-right-width: 14px; + left: -14px; + right: auto; +} + +.xxs-avatar { + width: 10px; + height: 10px; +} + + +.xs-avatar { + width: 50px; + height: 50px; +} + +.md-avatar { + width: 70px; + height: 70px; +} + +.lg-avatar { + width: 100px; + height: 100px; +} + +.text-truncate { + word-wrap: normal; + /* for IE */ + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} + +.text-yellow { + color:#FFCC66; +} + +.label-xl{ + font-size:150%; +} + +.pointer { + cursor: pointer; +} + +.limit_height{ + height: 600px;overflow-y:auto;overflow-x:hidden; +} + +.table.text-middle>tbody>tr>td,.table.text-middle>tbody>tr>th{ + vertical-align: middle; +} diff --git a/src/main/resources/static/style/btn.css b/src/main/resources/static/style/btn.css new file mode 100644 index 0000000..000a50d --- /dev/null +++ b/src/main/resources/static/style/btn.css @@ -0,0 +1,769 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +.btn, +.navbar .navbar-nav > li > a.btn { + border: none; + border-radius: 3px; + position: relative; + padding: 12px 30px; + margin: 10px 1px; + font-size: 12px; + font-weight: 400; + text-transform: uppercase; + letter-spacing: 0; + will-change: box-shadow, transform; + transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1); +} +.btn::-moz-focus-inner, +.navbar .navbar-nav > li > a.btn::-moz-focus-inner { + border: 0; +} +.btn, .btn.btn-default, +.navbar .navbar-nav > li > a.btn, +.navbar .navbar-nav > li > a.btn.btn-default { + box-shadow: 0 2px 2px 0 rgba(153, 153, 153, 0.14), 0 3px 1px -2px rgba(153, 153, 153, 0.2), 0 1px 5px 0 rgba(153, 153, 153, 0.12); +} +.btn, .btn:hover, .btn:focus, .btn:active, .btn.active, .btn:active:focus, .btn:active:hover, .btn.active:focus, .btn.active:hover, .open > .btn.dropdown-toggle, .open > .btn.dropdown-toggle:focus, .open > .btn.dropdown-toggle:hover, .btn.btn-default, .btn.btn-default:hover, .btn.btn-default:focus, .btn.btn-default:active, .btn.btn-default.active, .btn.btn-default:active:focus, .btn.btn-default:active:hover, .btn.btn-default.active:focus, .btn.btn-default.active:hover, .open > .btn.btn-default.dropdown-toggle, .open > .btn.btn-default.dropdown-toggle:focus, .open > .btn.btn-default.dropdown-toggle:hover, +.navbar .navbar-nav > li > a.btn, +.navbar .navbar-nav > li > a.btn:hover, +.navbar .navbar-nav > li > a.btn:focus, +.navbar .navbar-nav > li > a.btn:active, +.navbar .navbar-nav > li > a.btn.active, +.navbar .navbar-nav > li > a.btn:active:focus, +.navbar .navbar-nav > li > a.btn:active:hover, +.navbar .navbar-nav > li > a.btn.active:focus, +.navbar .navbar-nav > li > a.btn.active:hover, .open > +.navbar .navbar-nav > li > a.btn.dropdown-toggle, .open > +.navbar .navbar-nav > li > a.btn.dropdown-toggle:focus, .open > +.navbar .navbar-nav > li > a.btn.dropdown-toggle:hover, +.navbar .navbar-nav > li > a.btn.btn-default, +.navbar .navbar-nav > li > a.btn.btn-default:hover, +.navbar .navbar-nav > li > a.btn.btn-default:focus, +.navbar .navbar-nav > li > a.btn.btn-default:active, +.navbar .navbar-nav > li > a.btn.btn-default.active, +.navbar .navbar-nav > li > a.btn.btn-default:active:focus, +.navbar .navbar-nav > li > a.btn.btn-default:active:hover, +.navbar .navbar-nav > li > a.btn.btn-default.active:focus, +.navbar .navbar-nav > li > a.btn.btn-default.active:hover, .open > +.navbar .navbar-nav > li > a.btn.btn-default.dropdown-toggle, .open > +.navbar .navbar-nav > li > a.btn.btn-default.dropdown-toggle:focus, .open > +.navbar .navbar-nav > li > a.btn.btn-default.dropdown-toggle:hover { + background-color: #999999; + color: #FFFFFF; +} +.btn:focus, .btn:active, .btn:hover, .btn.btn-default:focus, .btn.btn-default:active, .btn.btn-default:hover, +.navbar .navbar-nav > li > a.btn:focus, +.navbar .navbar-nav > li > a.btn:active, +.navbar .navbar-nav > li > a.btn:hover, +.navbar .navbar-nav > li > a.btn.btn-default:focus, +.navbar .navbar-nav > li > a.btn.btn-default:active, +.navbar .navbar-nav > li > a.btn.btn-default:hover { + box-shadow: 0 14px 26px -12px rgba(153, 153, 153, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(153, 153, 153, 0.2); +} +.btn.disabled, .btn.disabled:hover, .btn.disabled:focus, .btn.disabled.focus, .btn.disabled:active, .btn.disabled.active, .btn:disabled, .btn:disabled:hover, .btn:disabled:focus, .btn:disabled.focus, .btn:disabled:active, .btn:disabled.active, .btn[disabled], .btn[disabled]:hover, .btn[disabled]:focus, .btn[disabled].focus, .btn[disabled]:active, .btn[disabled].active, fieldset[disabled] .btn, fieldset[disabled] .btn:hover, fieldset[disabled] .btn:focus, fieldset[disabled] .btn.focus, fieldset[disabled] .btn:active, fieldset[disabled] .btn.active, .btn.btn-default.disabled, .btn.btn-default.disabled:hover, .btn.btn-default.disabled:focus, .btn.btn-default.disabled.focus, .btn.btn-default.disabled:active, .btn.btn-default.disabled.active, .btn.btn-default:disabled, .btn.btn-default:disabled:hover, .btn.btn-default:disabled:focus, .btn.btn-default:disabled.focus, .btn.btn-default:disabled:active, .btn.btn-default:disabled.active, .btn.btn-default[disabled], .btn.btn-default[disabled]:hover, .btn.btn-default[disabled]:focus, .btn.btn-default[disabled].focus, .btn.btn-default[disabled]:active, .btn.btn-default[disabled].active, fieldset[disabled] .btn.btn-default, fieldset[disabled] .btn.btn-default:hover, fieldset[disabled] .btn.btn-default:focus, fieldset[disabled] .btn.btn-default.focus, fieldset[disabled] .btn.btn-default:active, fieldset[disabled] .btn.btn-default.active, +.navbar .navbar-nav > li > a.btn.disabled, +.navbar .navbar-nav > li > a.btn.disabled:hover, +.navbar .navbar-nav > li > a.btn.disabled:focus, +.navbar .navbar-nav > li > a.btn.disabled.focus, +.navbar .navbar-nav > li > a.btn.disabled:active, +.navbar .navbar-nav > li > a.btn.disabled.active, +.navbar .navbar-nav > li > a.btn:disabled, +.navbar .navbar-nav > li > a.btn:disabled:hover, +.navbar .navbar-nav > li > a.btn:disabled:focus, +.navbar .navbar-nav > li > a.btn:disabled.focus, +.navbar .navbar-nav > li > a.btn:disabled:active, +.navbar .navbar-nav > li > a.btn:disabled.active, +.navbar .navbar-nav > li > a.btn[disabled], +.navbar .navbar-nav > li > a.btn[disabled]:hover, +.navbar .navbar-nav > li > a.btn[disabled]:focus, +.navbar .navbar-nav > li > a.btn[disabled].focus, +.navbar .navbar-nav > li > a.btn[disabled]:active, +.navbar .navbar-nav > li > a.btn[disabled].active, fieldset[disabled] +.navbar .navbar-nav > li > a.btn, fieldset[disabled] +.navbar .navbar-nav > li > a.btn:hover, fieldset[disabled] +.navbar .navbar-nav > li > a.btn:focus, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.focus, fieldset[disabled] +.navbar .navbar-nav > li > a.btn:active, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.active, +.navbar .navbar-nav > li > a.btn.btn-default.disabled, +.navbar .navbar-nav > li > a.btn.btn-default.disabled:hover, +.navbar .navbar-nav > li > a.btn.btn-default.disabled:focus, +.navbar .navbar-nav > li > a.btn.btn-default.disabled.focus, +.navbar .navbar-nav > li > a.btn.btn-default.disabled:active, +.navbar .navbar-nav > li > a.btn.btn-default.disabled.active, +.navbar .navbar-nav > li > a.btn.btn-default:disabled, +.navbar .navbar-nav > li > a.btn.btn-default:disabled:hover, +.navbar .navbar-nav > li > a.btn.btn-default:disabled:focus, +.navbar .navbar-nav > li > a.btn.btn-default:disabled.focus, +.navbar .navbar-nav > li > a.btn.btn-default:disabled:active, +.navbar .navbar-nav > li > a.btn.btn-default:disabled.active, +.navbar .navbar-nav > li > a.btn.btn-default[disabled], +.navbar .navbar-nav > li > a.btn.btn-default[disabled]:hover, +.navbar .navbar-nav > li > a.btn.btn-default[disabled]:focus, +.navbar .navbar-nav > li > a.btn.btn-default[disabled].focus, +.navbar .navbar-nav > li > a.btn.btn-default[disabled]:active, +.navbar .navbar-nav > li > a.btn.btn-default[disabled].active, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-default, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-default:hover, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-default:focus, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-default.focus, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-default:active, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-default.active { + box-shadow: none; +} +.btn.btn-simple, .btn.btn-default.btn-simple, +.navbar .navbar-nav > li > a.btn.btn-simple, +.navbar .navbar-nav > li > a.btn.btn-default.btn-simple { + background-color: transparent; + color: #999999; + box-shadow: none; +} +.btn.btn-simple:hover, .btn.btn-simple:focus, .btn.btn-simple:active, .btn.btn-default.btn-simple:hover, .btn.btn-default.btn-simple:focus, .btn.btn-default.btn-simple:active, +.navbar .navbar-nav > li > a.btn.btn-simple:hover, +.navbar .navbar-nav > li > a.btn.btn-simple:focus, +.navbar .navbar-nav > li > a.btn.btn-simple:active, +.navbar .navbar-nav > li > a.btn.btn-default.btn-simple:hover, +.navbar .navbar-nav > li > a.btn.btn-default.btn-simple:focus, +.navbar .navbar-nav > li > a.btn.btn-default.btn-simple:active { + background-color: transparent; + color: #999999; +} +.btn.btn-primary, +.navbar .navbar-nav > li > a.btn.btn-primary { + box-shadow: 0 2px 2px 0 rgba(156, 39, 176, 0.14), 0 3px 1px -2px rgba(156, 39, 176, 0.2), 0 1px 5px 0 rgba(156, 39, 176, 0.12); +} +.btn.btn-primary, .btn.btn-primary:hover, .btn.btn-primary:focus, .btn.btn-primary:active, .btn.btn-primary.active, .btn.btn-primary:active:focus, .btn.btn-primary:active:hover, .btn.btn-primary.active:focus, .btn.btn-primary.active:hover, .open > .btn.btn-primary.dropdown-toggle, .open > .btn.btn-primary.dropdown-toggle:focus, .open > .btn.btn-primary.dropdown-toggle:hover, +.navbar .navbar-nav > li > a.btn.btn-primary, +.navbar .navbar-nav > li > a.btn.btn-primary:hover, +.navbar .navbar-nav > li > a.btn.btn-primary:focus, +.navbar .navbar-nav > li > a.btn.btn-primary:active, +.navbar .navbar-nav > li > a.btn.btn-primary.active, +.navbar .navbar-nav > li > a.btn.btn-primary:active:focus, +.navbar .navbar-nav > li > a.btn.btn-primary:active:hover, +.navbar .navbar-nav > li > a.btn.btn-primary.active:focus, +.navbar .navbar-nav > li > a.btn.btn-primary.active:hover, .open > +.navbar .navbar-nav > li > a.btn.btn-primary.dropdown-toggle, .open > +.navbar .navbar-nav > li > a.btn.btn-primary.dropdown-toggle:focus, .open > +.navbar .navbar-nav > li > a.btn.btn-primary.dropdown-toggle:hover { + background-color: #9c27b0; + color: #FFFFFF; +} +.btn.btn-primary:focus, .btn.btn-primary:active, .btn.btn-primary:hover, +.navbar .navbar-nav > li > a.btn.btn-primary:focus, +.navbar .navbar-nav > li > a.btn.btn-primary:active, +.navbar .navbar-nav > li > a.btn.btn-primary:hover { + box-shadow: 0 14px 26px -12px rgba(156, 39, 176, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(156, 39, 176, 0.2); +} +.btn.btn-primary.disabled, .btn.btn-primary.disabled:hover, .btn.btn-primary.disabled:focus, .btn.btn-primary.disabled.focus, .btn.btn-primary.disabled:active, .btn.btn-primary.disabled.active, .btn.btn-primary:disabled, .btn.btn-primary:disabled:hover, .btn.btn-primary:disabled:focus, .btn.btn-primary:disabled.focus, .btn.btn-primary:disabled:active, .btn.btn-primary:disabled.active, .btn.btn-primary[disabled], .btn.btn-primary[disabled]:hover, .btn.btn-primary[disabled]:focus, .btn.btn-primary[disabled].focus, .btn.btn-primary[disabled]:active, .btn.btn-primary[disabled].active, fieldset[disabled] .btn.btn-primary, fieldset[disabled] .btn.btn-primary:hover, fieldset[disabled] .btn.btn-primary:focus, fieldset[disabled] .btn.btn-primary.focus, fieldset[disabled] .btn.btn-primary:active, fieldset[disabled] .btn.btn-primary.active, +.navbar .navbar-nav > li > a.btn.btn-primary.disabled, +.navbar .navbar-nav > li > a.btn.btn-primary.disabled:hover, +.navbar .navbar-nav > li > a.btn.btn-primary.disabled:focus, +.navbar .navbar-nav > li > a.btn.btn-primary.disabled.focus, +.navbar .navbar-nav > li > a.btn.btn-primary.disabled:active, +.navbar .navbar-nav > li > a.btn.btn-primary.disabled.active, +.navbar .navbar-nav > li > a.btn.btn-primary:disabled, +.navbar .navbar-nav > li > a.btn.btn-primary:disabled:hover, +.navbar .navbar-nav > li > a.btn.btn-primary:disabled:focus, +.navbar .navbar-nav > li > a.btn.btn-primary:disabled.focus, +.navbar .navbar-nav > li > a.btn.btn-primary:disabled:active, +.navbar .navbar-nav > li > a.btn.btn-primary:disabled.active, +.navbar .navbar-nav > li > a.btn.btn-primary[disabled], +.navbar .navbar-nav > li > a.btn.btn-primary[disabled]:hover, +.navbar .navbar-nav > li > a.btn.btn-primary[disabled]:focus, +.navbar .navbar-nav > li > a.btn.btn-primary[disabled].focus, +.navbar .navbar-nav > li > a.btn.btn-primary[disabled]:active, +.navbar .navbar-nav > li > a.btn.btn-primary[disabled].active, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-primary, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-primary:hover, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-primary:focus, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-primary.focus, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-primary:active, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-primary.active { + box-shadow: none; +} +.btn.btn-primary.btn-simple, +.navbar .navbar-nav > li > a.btn.btn-primary.btn-simple { + background-color: transparent; + color: #9c27b0; + box-shadow: none; +} +.btn.btn-primary.btn-simple:hover, .btn.btn-primary.btn-simple:focus, .btn.btn-primary.btn-simple:active, +.navbar .navbar-nav > li > a.btn.btn-primary.btn-simple:hover, +.navbar .navbar-nav > li > a.btn.btn-primary.btn-simple:focus, +.navbar .navbar-nav > li > a.btn.btn-primary.btn-simple:active { + background-color: transparent; + color: #9c27b0; +} +.btn.btn-info, +.navbar .navbar-nav > li > a.btn.btn-info { + box-shadow: 0 2px 2px 0 rgba(0, 188, 212, 0.14), 0 3px 1px -2px rgba(0, 188, 212, 0.2), 0 1px 5px 0 rgba(0, 188, 212, 0.12); +} +.btn.btn-info, .btn.btn-info:hover, .btn.btn-info:focus, .btn.btn-info:active, .btn.btn-info.active, .btn.btn-info:active:focus, .btn.btn-info:active:hover, .btn.btn-info.active:focus, .btn.btn-info.active:hover, .open > .btn.btn-info.dropdown-toggle, .open > .btn.btn-info.dropdown-toggle:focus, .open > .btn.btn-info.dropdown-toggle:hover, +.navbar .navbar-nav > li > a.btn.btn-info, +.navbar .navbar-nav > li > a.btn.btn-info:hover, +.navbar .navbar-nav > li > a.btn.btn-info:focus, +.navbar .navbar-nav > li > a.btn.btn-info:active, +.navbar .navbar-nav > li > a.btn.btn-info.active, +.navbar .navbar-nav > li > a.btn.btn-info:active:focus, +.navbar .navbar-nav > li > a.btn.btn-info:active:hover, +.navbar .navbar-nav > li > a.btn.btn-info.active:focus, +.navbar .navbar-nav > li > a.btn.btn-info.active:hover, .open > +.navbar .navbar-nav > li > a.btn.btn-info.dropdown-toggle, .open > +.navbar .navbar-nav > li > a.btn.btn-info.dropdown-toggle:focus, .open > +.navbar .navbar-nav > li > a.btn.btn-info.dropdown-toggle:hover { + background-color: #00bcd4; + color: #FFFFFF; +} +.btn.btn-info:focus, .btn.btn-info:active, .btn.btn-info:hover, +.navbar .navbar-nav > li > a.btn.btn-info:focus, +.navbar .navbar-nav > li > a.btn.btn-info:active, +.navbar .navbar-nav > li > a.btn.btn-info:hover { + box-shadow: 0 14px 26px -12px rgba(0, 188, 212, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 188, 212, 0.2); +} +.btn.btn-info.disabled, .btn.btn-info.disabled:hover, .btn.btn-info.disabled:focus, .btn.btn-info.disabled.focus, .btn.btn-info.disabled:active, .btn.btn-info.disabled.active, .btn.btn-info:disabled, .btn.btn-info:disabled:hover, .btn.btn-info:disabled:focus, .btn.btn-info:disabled.focus, .btn.btn-info:disabled:active, .btn.btn-info:disabled.active, .btn.btn-info[disabled], .btn.btn-info[disabled]:hover, .btn.btn-info[disabled]:focus, .btn.btn-info[disabled].focus, .btn.btn-info[disabled]:active, .btn.btn-info[disabled].active, fieldset[disabled] .btn.btn-info, fieldset[disabled] .btn.btn-info:hover, fieldset[disabled] .btn.btn-info:focus, fieldset[disabled] .btn.btn-info.focus, fieldset[disabled] .btn.btn-info:active, fieldset[disabled] .btn.btn-info.active, +.navbar .navbar-nav > li > a.btn.btn-info.disabled, +.navbar .navbar-nav > li > a.btn.btn-info.disabled:hover, +.navbar .navbar-nav > li > a.btn.btn-info.disabled:focus, +.navbar .navbar-nav > li > a.btn.btn-info.disabled.focus, +.navbar .navbar-nav > li > a.btn.btn-info.disabled:active, +.navbar .navbar-nav > li > a.btn.btn-info.disabled.active, +.navbar .navbar-nav > li > a.btn.btn-info:disabled, +.navbar .navbar-nav > li > a.btn.btn-info:disabled:hover, +.navbar .navbar-nav > li > a.btn.btn-info:disabled:focus, +.navbar .navbar-nav > li > a.btn.btn-info:disabled.focus, +.navbar .navbar-nav > li > a.btn.btn-info:disabled:active, +.navbar .navbar-nav > li > a.btn.btn-info:disabled.active, +.navbar .navbar-nav > li > a.btn.btn-info[disabled], +.navbar .navbar-nav > li > a.btn.btn-info[disabled]:hover, +.navbar .navbar-nav > li > a.btn.btn-info[disabled]:focus, +.navbar .navbar-nav > li > a.btn.btn-info[disabled].focus, +.navbar .navbar-nav > li > a.btn.btn-info[disabled]:active, +.navbar .navbar-nav > li > a.btn.btn-info[disabled].active, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-info, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-info:hover, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-info:focus, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-info.focus, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-info:active, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-info.active { + box-shadow: none; +} +.btn.btn-info.btn-simple, +.navbar .navbar-nav > li > a.btn.btn-info.btn-simple { + background-color: transparent; + color: #00bcd4; + box-shadow: none; +} +.btn.btn-info.btn-simple:hover, .btn.btn-info.btn-simple:focus, .btn.btn-info.btn-simple:active, +.navbar .navbar-nav > li > a.btn.btn-info.btn-simple:hover, +.navbar .navbar-nav > li > a.btn.btn-info.btn-simple:focus, +.navbar .navbar-nav > li > a.btn.btn-info.btn-simple:active { + background-color: transparent; + color: #00bcd4; +} +.btn.btn-success, +.navbar .navbar-nav > li > a.btn.btn-success { + box-shadow: 0 2px 2px 0 rgba(76, 175, 80, 0.14), 0 3px 1px -2px rgba(76, 175, 80, 0.2), 0 1px 5px 0 rgba(76, 175, 80, 0.12); +} +.btn.btn-success, .btn.btn-success:hover, .btn.btn-success:focus, .btn.btn-success:active, .btn.btn-success.active, .btn.btn-success:active:focus, .btn.btn-success:active:hover, .btn.btn-success.active:focus, .btn.btn-success.active:hover, .open > .btn.btn-success.dropdown-toggle, .open > .btn.btn-success.dropdown-toggle:focus, .open > .btn.btn-success.dropdown-toggle:hover, +.navbar .navbar-nav > li > a.btn.btn-success, +.navbar .navbar-nav > li > a.btn.btn-success:hover, +.navbar .navbar-nav > li > a.btn.btn-success:focus, +.navbar .navbar-nav > li > a.btn.btn-success:active, +.navbar .navbar-nav > li > a.btn.btn-success.active, +.navbar .navbar-nav > li > a.btn.btn-success:active:focus, +.navbar .navbar-nav > li > a.btn.btn-success:active:hover, +.navbar .navbar-nav > li > a.btn.btn-success.active:focus, +.navbar .navbar-nav > li > a.btn.btn-success.active:hover, .open > +.navbar .navbar-nav > li > a.btn.btn-success.dropdown-toggle, .open > +.navbar .navbar-nav > li > a.btn.btn-success.dropdown-toggle:focus, .open > +.navbar .navbar-nav > li > a.btn.btn-success.dropdown-toggle:hover { + background-color: #4caf50; + color: #FFFFFF; +} +.btn.btn-success:focus, .btn.btn-success:active, .btn.btn-success:hover, +.navbar .navbar-nav > li > a.btn.btn-success:focus, +.navbar .navbar-nav > li > a.btn.btn-success:active, +.navbar .navbar-nav > li > a.btn.btn-success:hover { + box-shadow: 0 14px 26px -12px rgba(76, 175, 80, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(76, 175, 80, 0.2); +} +.btn.btn-success.disabled, .btn.btn-success.disabled:hover, .btn.btn-success.disabled:focus, .btn.btn-success.disabled.focus, .btn.btn-success.disabled:active, .btn.btn-success.disabled.active, .btn.btn-success:disabled, .btn.btn-success:disabled:hover, .btn.btn-success:disabled:focus, .btn.btn-success:disabled.focus, .btn.btn-success:disabled:active, .btn.btn-success:disabled.active, .btn.btn-success[disabled], .btn.btn-success[disabled]:hover, .btn.btn-success[disabled]:focus, .btn.btn-success[disabled].focus, .btn.btn-success[disabled]:active, .btn.btn-success[disabled].active, fieldset[disabled] .btn.btn-success, fieldset[disabled] .btn.btn-success:hover, fieldset[disabled] .btn.btn-success:focus, fieldset[disabled] .btn.btn-success.focus, fieldset[disabled] .btn.btn-success:active, fieldset[disabled] .btn.btn-success.active, +.navbar .navbar-nav > li > a.btn.btn-success.disabled, +.navbar .navbar-nav > li > a.btn.btn-success.disabled:hover, +.navbar .navbar-nav > li > a.btn.btn-success.disabled:focus, +.navbar .navbar-nav > li > a.btn.btn-success.disabled.focus, +.navbar .navbar-nav > li > a.btn.btn-success.disabled:active, +.navbar .navbar-nav > li > a.btn.btn-success.disabled.active, +.navbar .navbar-nav > li > a.btn.btn-success:disabled, +.navbar .navbar-nav > li > a.btn.btn-success:disabled:hover, +.navbar .navbar-nav > li > a.btn.btn-success:disabled:focus, +.navbar .navbar-nav > li > a.btn.btn-success:disabled.focus, +.navbar .navbar-nav > li > a.btn.btn-success:disabled:active, +.navbar .navbar-nav > li > a.btn.btn-success:disabled.active, +.navbar .navbar-nav > li > a.btn.btn-success[disabled], +.navbar .navbar-nav > li > a.btn.btn-success[disabled]:hover, +.navbar .navbar-nav > li > a.btn.btn-success[disabled]:focus, +.navbar .navbar-nav > li > a.btn.btn-success[disabled].focus, +.navbar .navbar-nav > li > a.btn.btn-success[disabled]:active, +.navbar .navbar-nav > li > a.btn.btn-success[disabled].active, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-success, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-success:hover, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-success:focus, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-success.focus, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-success:active, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-success.active { + box-shadow: none; +} +.btn.btn-success.btn-simple, +.navbar .navbar-nav > li > a.btn.btn-success.btn-simple { + background-color: transparent; + color: #4caf50; + box-shadow: none; +} +.btn.btn-success.btn-simple:hover, .btn.btn-success.btn-simple:focus, .btn.btn-success.btn-simple:active, +.navbar .navbar-nav > li > a.btn.btn-success.btn-simple:hover, +.navbar .navbar-nav > li > a.btn.btn-success.btn-simple:focus, +.navbar .navbar-nav > li > a.btn.btn-success.btn-simple:active { + background-color: transparent; + color: #4caf50; +} +.btn.btn-warning, +.navbar .navbar-nav > li > a.btn.btn-warning { + box-shadow: 0 2px 2px 0 rgba(255, 152, 0, 0.14), 0 3px 1px -2px rgba(255, 152, 0, 0.2), 0 1px 5px 0 rgba(255, 152, 0, 0.12); +} +.btn.btn-warning, .btn.btn-warning:hover, .btn.btn-warning:focus, .btn.btn-warning:active, .btn.btn-warning.active, .btn.btn-warning:active:focus, .btn.btn-warning:active:hover, .btn.btn-warning.active:focus, .btn.btn-warning.active:hover, .open > .btn.btn-warning.dropdown-toggle, .open > .btn.btn-warning.dropdown-toggle:focus, .open > .btn.btn-warning.dropdown-toggle:hover, +.navbar .navbar-nav > li > a.btn.btn-warning, +.navbar .navbar-nav > li > a.btn.btn-warning:hover, +.navbar .navbar-nav > li > a.btn.btn-warning:focus, +.navbar .navbar-nav > li > a.btn.btn-warning:active, +.navbar .navbar-nav > li > a.btn.btn-warning.active, +.navbar .navbar-nav > li > a.btn.btn-warning:active:focus, +.navbar .navbar-nav > li > a.btn.btn-warning:active:hover, +.navbar .navbar-nav > li > a.btn.btn-warning.active:focus, +.navbar .navbar-nav > li > a.btn.btn-warning.active:hover, .open > +.navbar .navbar-nav > li > a.btn.btn-warning.dropdown-toggle, .open > +.navbar .navbar-nav > li > a.btn.btn-warning.dropdown-toggle:focus, .open > +.navbar .navbar-nav > li > a.btn.btn-warning.dropdown-toggle:hover { + background-color: #ff9800; + color: #FFFFFF; +} +.btn.btn-warning:focus, .btn.btn-warning:active, .btn.btn-warning:hover, +.navbar .navbar-nav > li > a.btn.btn-warning:focus, +.navbar .navbar-nav > li > a.btn.btn-warning:active, +.navbar .navbar-nav > li > a.btn.btn-warning:hover { + box-shadow: 0 14px 26px -12px rgba(255, 152, 0, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(255, 152, 0, 0.2); +} +.btn.btn-warning.disabled, .btn.btn-warning.disabled:hover, .btn.btn-warning.disabled:focus, .btn.btn-warning.disabled.focus, .btn.btn-warning.disabled:active, .btn.btn-warning.disabled.active, .btn.btn-warning:disabled, .btn.btn-warning:disabled:hover, .btn.btn-warning:disabled:focus, .btn.btn-warning:disabled.focus, .btn.btn-warning:disabled:active, .btn.btn-warning:disabled.active, .btn.btn-warning[disabled], .btn.btn-warning[disabled]:hover, .btn.btn-warning[disabled]:focus, .btn.btn-warning[disabled].focus, .btn.btn-warning[disabled]:active, .btn.btn-warning[disabled].active, fieldset[disabled] .btn.btn-warning, fieldset[disabled] .btn.btn-warning:hover, fieldset[disabled] .btn.btn-warning:focus, fieldset[disabled] .btn.btn-warning.focus, fieldset[disabled] .btn.btn-warning:active, fieldset[disabled] .btn.btn-warning.active, +.navbar .navbar-nav > li > a.btn.btn-warning.disabled, +.navbar .navbar-nav > li > a.btn.btn-warning.disabled:hover, +.navbar .navbar-nav > li > a.btn.btn-warning.disabled:focus, +.navbar .navbar-nav > li > a.btn.btn-warning.disabled.focus, +.navbar .navbar-nav > li > a.btn.btn-warning.disabled:active, +.navbar .navbar-nav > li > a.btn.btn-warning.disabled.active, +.navbar .navbar-nav > li > a.btn.btn-warning:disabled, +.navbar .navbar-nav > li > a.btn.btn-warning:disabled:hover, +.navbar .navbar-nav > li > a.btn.btn-warning:disabled:focus, +.navbar .navbar-nav > li > a.btn.btn-warning:disabled.focus, +.navbar .navbar-nav > li > a.btn.btn-warning:disabled:active, +.navbar .navbar-nav > li > a.btn.btn-warning:disabled.active, +.navbar .navbar-nav > li > a.btn.btn-warning[disabled], +.navbar .navbar-nav > li > a.btn.btn-warning[disabled]:hover, +.navbar .navbar-nav > li > a.btn.btn-warning[disabled]:focus, +.navbar .navbar-nav > li > a.btn.btn-warning[disabled].focus, +.navbar .navbar-nav > li > a.btn.btn-warning[disabled]:active, +.navbar .navbar-nav > li > a.btn.btn-warning[disabled].active, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-warning, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-warning:hover, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-warning:focus, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-warning.focus, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-warning:active, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-warning.active { + box-shadow: none; +} +.btn.btn-warning.btn-simple, +.navbar .navbar-nav > li > a.btn.btn-warning.btn-simple { + background-color: transparent; + color: #ff9800; + box-shadow: none; +} +.btn.btn-warning.btn-simple:hover, .btn.btn-warning.btn-simple:focus, .btn.btn-warning.btn-simple:active, +.navbar .navbar-nav > li > a.btn.btn-warning.btn-simple:hover, +.navbar .navbar-nav > li > a.btn.btn-warning.btn-simple:focus, +.navbar .navbar-nav > li > a.btn.btn-warning.btn-simple:active { + background-color: transparent; + color: #ff9800; +} +.btn.btn-danger, +.navbar .navbar-nav > li > a.btn.btn-danger { + box-shadow: 0 2px 2px 0 rgba(244, 67, 54, 0.14), 0 3px 1px -2px rgba(244, 67, 54, 0.2), 0 1px 5px 0 rgba(244, 67, 54, 0.12); +} +.btn.btn-danger, .btn.btn-danger:hover, .btn.btn-danger:focus, .btn.btn-danger:active, .btn.btn-danger.active, .btn.btn-danger:active:focus, .btn.btn-danger:active:hover, .btn.btn-danger.active:focus, .btn.btn-danger.active:hover, .open > .btn.btn-danger.dropdown-toggle, .open > .btn.btn-danger.dropdown-toggle:focus, .open > .btn.btn-danger.dropdown-toggle:hover, +.navbar .navbar-nav > li > a.btn.btn-danger, +.navbar .navbar-nav > li > a.btn.btn-danger:hover, +.navbar .navbar-nav > li > a.btn.btn-danger:focus, +.navbar .navbar-nav > li > a.btn.btn-danger:active, +.navbar .navbar-nav > li > a.btn.btn-danger.active, +.navbar .navbar-nav > li > a.btn.btn-danger:active:focus, +.navbar .navbar-nav > li > a.btn.btn-danger:active:hover, +.navbar .navbar-nav > li > a.btn.btn-danger.active:focus, +.navbar .navbar-nav > li > a.btn.btn-danger.active:hover, .open > +.navbar .navbar-nav > li > a.btn.btn-danger.dropdown-toggle, .open > +.navbar .navbar-nav > li > a.btn.btn-danger.dropdown-toggle:focus, .open > +.navbar .navbar-nav > li > a.btn.btn-danger.dropdown-toggle:hover { + background-color: #f44336; + color: #FFFFFF; +} +.btn.btn-danger:focus, .btn.btn-danger:active, .btn.btn-danger:hover, +.navbar .navbar-nav > li > a.btn.btn-danger:focus, +.navbar .navbar-nav > li > a.btn.btn-danger:active, +.navbar .navbar-nav > li > a.btn.btn-danger:hover { + box-shadow: 0 14px 26px -12px rgba(244, 67, 54, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(244, 67, 54, 0.2); +} +.btn.btn-danger.disabled, .btn.btn-danger.disabled:hover, .btn.btn-danger.disabled:focus, .btn.btn-danger.disabled.focus, .btn.btn-danger.disabled:active, .btn.btn-danger.disabled.active, .btn.btn-danger:disabled, .btn.btn-danger:disabled:hover, .btn.btn-danger:disabled:focus, .btn.btn-danger:disabled.focus, .btn.btn-danger:disabled:active, .btn.btn-danger:disabled.active, .btn.btn-danger[disabled], .btn.btn-danger[disabled]:hover, .btn.btn-danger[disabled]:focus, .btn.btn-danger[disabled].focus, .btn.btn-danger[disabled]:active, .btn.btn-danger[disabled].active, fieldset[disabled] .btn.btn-danger, fieldset[disabled] .btn.btn-danger:hover, fieldset[disabled] .btn.btn-danger:focus, fieldset[disabled] .btn.btn-danger.focus, fieldset[disabled] .btn.btn-danger:active, fieldset[disabled] .btn.btn-danger.active, +.navbar .navbar-nav > li > a.btn.btn-danger.disabled, +.navbar .navbar-nav > li > a.btn.btn-danger.disabled:hover, +.navbar .navbar-nav > li > a.btn.btn-danger.disabled:focus, +.navbar .navbar-nav > li > a.btn.btn-danger.disabled.focus, +.navbar .navbar-nav > li > a.btn.btn-danger.disabled:active, +.navbar .navbar-nav > li > a.btn.btn-danger.disabled.active, +.navbar .navbar-nav > li > a.btn.btn-danger:disabled, +.navbar .navbar-nav > li > a.btn.btn-danger:disabled:hover, +.navbar .navbar-nav > li > a.btn.btn-danger:disabled:focus, +.navbar .navbar-nav > li > a.btn.btn-danger:disabled.focus, +.navbar .navbar-nav > li > a.btn.btn-danger:disabled:active, +.navbar .navbar-nav > li > a.btn.btn-danger:disabled.active, +.navbar .navbar-nav > li > a.btn.btn-danger[disabled], +.navbar .navbar-nav > li > a.btn.btn-danger[disabled]:hover, +.navbar .navbar-nav > li > a.btn.btn-danger[disabled]:focus, +.navbar .navbar-nav > li > a.btn.btn-danger[disabled].focus, +.navbar .navbar-nav > li > a.btn.btn-danger[disabled]:active, +.navbar .navbar-nav > li > a.btn.btn-danger[disabled].active, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-danger, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-danger:hover, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-danger:focus, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-danger.focus, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-danger:active, fieldset[disabled] +.navbar .navbar-nav > li > a.btn.btn-danger.active { + box-shadow: none; +} +.btn.btn-danger.btn-simple, +.navbar .navbar-nav > li > a.btn.btn-danger.btn-simple { + background-color: transparent; + color: #f44336; + box-shadow: none; +} +.btn.btn-danger.btn-simple:hover, .btn.btn-danger.btn-simple:focus, .btn.btn-danger.btn-simple:active, +.navbar .navbar-nav > li > a.btn.btn-danger.btn-simple:hover, +.navbar .navbar-nav > li > a.btn.btn-danger.btn-simple:focus, +.navbar .navbar-nav > li > a.btn.btn-danger.btn-simple:active { + background-color: transparent; + color: #f44336; +} +.btn.btn-white, .btn.btn-white:focus, .btn.btn-white:hover, +.navbar .navbar-nav > li > a.btn.btn-white, +.navbar .navbar-nav > li > a.btn.btn-white:focus, +.navbar .navbar-nav > li > a.btn.btn-white:hover { + background-color: #FFFFFF; + color: #999999; +} +.btn.btn-white.btn-simple, +.navbar .navbar-nav > li > a.btn.btn-white.btn-simple { + color: #FFFFFF; + background: transparent; + box-shadow: none; +} +.btn.btn-facebook, +.navbar .navbar-nav > li > a.btn.btn-facebook { + background-color: #3b5998; + color: #fff; + box-shadow: 0 2px 2px 0 rgba(59, 89, 152, 0.14), 0 3px 1px -2px rgba(59, 89, 152, 0.2), 0 1px 5px 0 rgba(59, 89, 152, 0.12); +} +.btn.btn-facebook:focus, .btn.btn-facebook:active, .btn.btn-facebook:hover, +.navbar .navbar-nav > li > a.btn.btn-facebook:focus, +.navbar .navbar-nav > li > a.btn.btn-facebook:active, +.navbar .navbar-nav > li > a.btn.btn-facebook:hover { + background-color: #3b5998; + color: #fff; + box-shadow: 0 14px 26px -12px rgba(59, 89, 152, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(59, 89, 152, 0.2); +} +.btn.btn-facebook.btn-simple, +.navbar .navbar-nav > li > a.btn.btn-facebook.btn-simple { + color: #3b5998; + background-color: transparent; + box-shadow: none; +} +.btn.btn-twitter, +.navbar .navbar-nav > li > a.btn.btn-twitter { + background-color: #55acee; + color: #fff; + box-shadow: 0 2px 2px 0 rgba(85, 172, 238, 0.14), 0 3px 1px -2px rgba(85, 172, 238, 0.2), 0 1px 5px 0 rgba(85, 172, 238, 0.12); +} +.btn.btn-twitter:focus, .btn.btn-twitter:active, .btn.btn-twitter:hover, +.navbar .navbar-nav > li > a.btn.btn-twitter:focus, +.navbar .navbar-nav > li > a.btn.btn-twitter:active, +.navbar .navbar-nav > li > a.btn.btn-twitter:hover { + background-color: #55acee; + color: #fff; + box-shadow: 0 14px 26px -12px rgba(85, 172, 238, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(85, 172, 238, 0.2); +} +.btn.btn-twitter.btn-simple, +.navbar .navbar-nav > li > a.btn.btn-twitter.btn-simple { + color: #55acee; + background-color: transparent; + box-shadow: none; +} +.btn.btn-pinterest, +.navbar .navbar-nav > li > a.btn.btn-pinterest { + background-color: #cc2127; + color: #fff; + box-shadow: 0 2px 2px 0 rgba(204, 33, 39, 0.14), 0 3px 1px -2px rgba(204, 33, 39, 0.2), 0 1px 5px 0 rgba(204, 33, 39, 0.12); +} +.btn.btn-pinterest:focus, .btn.btn-pinterest:active, .btn.btn-pinterest:hover, +.navbar .navbar-nav > li > a.btn.btn-pinterest:focus, +.navbar .navbar-nav > li > a.btn.btn-pinterest:active, +.navbar .navbar-nav > li > a.btn.btn-pinterest:hover { + background-color: #cc2127; + color: #fff; + box-shadow: 0 14px 26px -12px rgba(204, 33, 39, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(204, 33, 39, 0.2); +} +.btn.btn-pinterest.btn-simple, +.navbar .navbar-nav > li > a.btn.btn-pinterest.btn-simple { + color: #cc2127; + background-color: transparent; + box-shadow: none; +} +.btn.btn-google, +.navbar .navbar-nav > li > a.btn.btn-google { + background-color: #dd4b39; + color: #fff; + box-shadow: 0 2px 2px 0 rgba(221, 75, 57, 0.14), 0 3px 1px -2px rgba(221, 75, 57, 0.2), 0 1px 5px 0 rgba(221, 75, 57, 0.12); +} +.btn.btn-google:focus, .btn.btn-google:active, .btn.btn-google:hover, +.navbar .navbar-nav > li > a.btn.btn-google:focus, +.navbar .navbar-nav > li > a.btn.btn-google:active, +.navbar .navbar-nav > li > a.btn.btn-google:hover { + background-color: #dd4b39; + color: #fff; + box-shadow: 0 14px 26px -12px rgba(221, 75, 57, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(221, 75, 57, 0.2); +} +.btn.btn-google.btn-simple, +.navbar .navbar-nav > li > a.btn.btn-google.btn-simple { + color: #dd4b39; + background-color: transparent; + box-shadow: none; +} +.btn.btn-instagram, +.navbar .navbar-nav > li > a.btn.btn-instagram { + background-color: #125688; + color: #fff; + box-shadow: 0 2px 2px 0 rgba(18, 86, 136, 0.14), 0 3px 1px -2px rgba(18, 86, 136, 0.2), 0 1px 5px 0 rgba(18, 86, 136, 0.12); +} +.btn.btn-instagram:focus, .btn.btn-instagram:active, .btn.btn-instagram:hover, +.navbar .navbar-nav > li > a.btn.btn-instagram:focus, +.navbar .navbar-nav > li > a.btn.btn-instagram:active, +.navbar .navbar-nav > li > a.btn.btn-instagram:hover { + background-color: #125688; + color: #fff; + box-shadow: 0 14px 26px -12px rgba(18, 86, 136, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(18, 86, 136, 0.2); +} +.btn.btn-instagram.btn-simple, +.navbar .navbar-nav > li > a.btn.btn-instagram.btn-simple { + color: #125688; + background-color: transparent; + box-shadow: none; +} +.btn:focus, .btn:active, .btn:active:focus, +.navbar .navbar-nav > li > a.btn:focus, +.navbar .navbar-nav > li > a.btn:active, +.navbar .navbar-nav > li > a.btn:active:focus { + outline: 0; +} +.btn.btn-round, +.navbar .navbar-nav > li > a.btn.btn-round { + border-radius: 30px; +} +.btn:not(.btn-just-icon):not(.btn-fab) .fa, +.navbar .navbar-nav > li > a.btn:not(.btn-just-icon):not(.btn-fab) .fa { + font-size: 18px; + margin-top: -2px; + position: relative; + top: 2px; +} +.btn.btn-fab, +.navbar .navbar-nav > li > a.btn.btn-fab { + border-radius: 50%; + font-size: 24px; + height: 56px; + margin: auto; + min-width: 56px; + width: 56px; + padding: 0; + overflow: hidden; + position: relative; + line-height: normal; +} +.btn.btn-fab .ripple-container, +.navbar .navbar-nav > li > a.btn.btn-fab .ripple-container { + border-radius: 50%; +} +.btn.btn-fab.btn-fab-mini, .btn-group-sm .btn.btn-fab, +.navbar .navbar-nav > li > a.btn.btn-fab.btn-fab-mini, .btn-group-sm +.navbar .navbar-nav > li > a.btn.btn-fab { + height: 40px; + min-width: 40px; + width: 40px; +} +.btn.btn-fab.btn-fab-mini.material-icons, .btn-group-sm .btn.btn-fab.material-icons, +.navbar .navbar-nav > li > a.btn.btn-fab.btn-fab-mini.material-icons, .btn-group-sm +.navbar .navbar-nav > li > a.btn.btn-fab.material-icons { + top: -3.5px; + left: -3.5px; +} +.btn.btn-fab.btn-fab-mini .material-icons, .btn-group-sm .btn.btn-fab .material-icons, +.navbar .navbar-nav > li > a.btn.btn-fab.btn-fab-mini .material-icons, .btn-group-sm +.navbar .navbar-nav > li > a.btn.btn-fab .material-icons { + font-size: 17px; +} +.btn.btn-fab i.material-icons, +.navbar .navbar-nav > li > a.btn.btn-fab i.material-icons { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-12px, -12px); + line-height: 24px; + width: 24px; + font-size: 24px; +} +.btn.btn-lg, .btn-group-lg .btn, +.navbar .navbar-nav > li > a.btn.btn-lg, .btn-group-lg +.navbar .navbar-nav > li > a.btn { + font-size: 14px; + padding: 18px 36px; +} +.btn.btn-sm, .btn-group-sm .btn, +.navbar .navbar-nav > li > a.btn.btn-sm, .btn-group-sm +.navbar .navbar-nav > li > a.btn { + padding: 5px 20px; + font-size: 11px; +} +.btn.btn-xs, .btn-group-xs .btn, +.navbar .navbar-nav > li > a.btn.btn-xs, .btn-group-xs +.navbar .navbar-nav > li > a.btn { + padding: 4px 15px; + font-size: 10px; +} +.btn.btn-just-icon, +.navbar .navbar-nav > li > a.btn.btn-just-icon { + font-size: 20px; + padding: 12px 12px; + line-height: 1em; +} +.btn.btn-just-icon i, +.navbar .navbar-nav > li > a.btn.btn-just-icon i { + width: 20px; +} +.btn.btn-just-icon.btn-lg, +.navbar .navbar-nav > li > a.btn.btn-just-icon.btn-lg { + font-size: 22px; + padding: 13px 18px; +} + +.btn .material-icons { + vertical-align: middle; + font-size: 17px; + top: -1px; + position: relative; +} + +.navbar .navbar-nav > li > a.btn { + margin-top: 2px; + margin-bottom: 2px; +} +.navbar .navbar-nav > li > a.btn.btn-fab { + margin: 5px 2px; +} +.navbar .navbar-nav > li > a:not(.btn) .material-icons { + margin-top: -3px; + top: 0px; + position: relative; + margin-right: 3px; +} +.navbar .navbar-nav > li > .profile-photo { + margin: 5px 2px; +} + +.navbar-default:not(.navbar-transparent) .navbar-nav > li > a.btn.btn-white.btn-simple { + color: #555555; +} + +.btn-group, +.btn-group-vertical { + position: relative; + margin: 10px 1px; +} +.btn-group.open > .dropdown-toggle.btn, .btn-group.open > .dropdown-toggle.btn.btn-default, +.btn-group-vertical.open > .dropdown-toggle.btn, +.btn-group-vertical.open > .dropdown-toggle.btn.btn-default { + background-color: #FFFFFF; +} +.btn-group.open > .dropdown-toggle.btn.btn-inverse, +.btn-group-vertical.open > .dropdown-toggle.btn.btn-inverse { + background-color: #212121; +} +.btn-group.open > .dropdown-toggle.btn.btn-primary, +.btn-group-vertical.open > .dropdown-toggle.btn.btn-primary { + background-color: #9c27b0; +} +.btn-group.open > .dropdown-toggle.btn.btn-success, +.btn-group-vertical.open > .dropdown-toggle.btn.btn-success { + background-color: #4caf50; +} +.btn-group.open > .dropdown-toggle.btn.btn-info, +.btn-group-vertical.open > .dropdown-toggle.btn.btn-info { + background-color: #00bcd4; +} +.btn-group.open > .dropdown-toggle.btn.btn-warning, +.btn-group-vertical.open > .dropdown-toggle.btn.btn-warning { + background-color: #ff9800; +} +.btn-group.open > .dropdown-toggle.btn.btn-danger, +.btn-group-vertical.open > .dropdown-toggle.btn.btn-danger { + background-color: #f44336; +} +.btn-group.open > .dropdown-toggle.btn.btn-rose, +.btn-group-vertical.open > .dropdown-toggle.btn.btn-rose { + background-color: #e91e63; +} +.btn-group .dropdown-menu, +.btn-group-vertical .dropdown-menu { + border-radius: 0 0 3px 3px; +} +.btn-group.btn-group-raised, +.btn-group-vertical.btn-group-raised { + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); +} +.btn-group .btn + .btn, +.btn-group .btn, +.btn-group .btn:active, +.btn-group .btn-group, +.btn-group-vertical .btn + .btn, +.btn-group-vertical .btn, +.btn-group-vertical .btn:active, +.btn-group-vertical .btn-group { + margin: 0; +} \ No newline at end of file diff --git a/src/main/resources/static/style/img/alipay.png b/src/main/resources/static/style/img/alipay.png new file mode 100644 index 0000000000000000000000000000000000000000..8d72f269474d5351c4e33ca9c238e26675b017cb GIT binary patch literal 2156 zcmcIm`#02!9vx&FlgHO9&x=7qg$Z5b6<^PpGHEd5S)Or_@yJLzo?{GgdzguYq3)!S zQMksVgA*buN*Rxtn!KVpp=bzqoa6oncdfhbUhDJOYwaICKYZ3+YwtpDtcM(26Apnu zi&%mI2J8_oo3xU8a(XP(Em(MK+1>v+%Ds13BS&8t^Prz~@90phxsQwQpNH=dx#dCW6kM1gg+VQ;1xaBU|ntR-@UCXU$ zNnO}{^$5oU2yAsm3U_)HHypthji+Q?Tia%n-=~WGFk3Ux3BL#Kbk^739V6O`cE2#p z{VI^qB4Q>+VA3?CzTTFFHA!{UmhUP1ZILmggqslM(AI(&&pF>4S~nil*H19xG$Lxw zJ)F$rv9@D~X@P$bI#&0TNs2u?b#Zz?U8_O#;PGsWuEjucK5&F{Nyrv7(=oZfD~sM6Q@E9YHf!hu~-Znr!eWd4rz~RwmR|!-&gMr zPQwDy-F2N@oT{CKl<55G>A~vBpb+ZLsCc)4dqrUK*)29CZut&vOwFL6f8|2;Qjt8nG$! zi;PKJZR?T9@l=OW+zC`)pdG7T*zT3op&ecvkZ*K`#Ivn+X>||;?R|HuT+66wDt;rj z$v;y00idr|P^x29g&l2ZeN;4@vpp>J6*6Ufm)*+n?(nL6XArl15|LQ_yG4AyAK?H3 z#j`W0Z#ygsC4yvNGLb`g6Uf+BK$!>9%VeZJePA&R5vd^t_N|KihA6f)+H33}Xq7IR zl~j0nVd`hq;r^TAw_QD^T|uy+sqSIb4*oq_#eC82L10WMF-m_g_DHGL)>Orx1*t}7 zY(RUeUKE>_I`Z!Fo=slHSjAS%c+k4SkpCKQZetH`Bgmd7h=G65_uF2_THg*?NF*7A zP0Hk4gRP6-RpEqsX2?FFd9ohup1Fnf!}{1sch5BQwWAE6>E8pC?Gbv5%#28Ba0_wc0sIQa{ElyoRkv_^g7<3q=W zH(|kVSXX$jmrJtqW|t{_?)cQrd~1MXG%wS{{fIG9gkEJXq(FC1FHl+o&N{bCK5!$q zt6x99VXP-Q9VceJj!1^1yISPHV8j|%Rkn>~A?yhWjzZ~UJ1J3(n-C|xrHf1K6~P7R zHKAdoZB_Sqv$DCGp}hN64n=nnhkKc?_SGk$moYeEz3S0#dmwcL>)7f6M&53G zV`k%AEEIj>-yyZO&G00&mRP!8O>mK+0L?=RuMj%*wXF{wqfYU7c3LIwi!+~h0NjLi z5&6Puh);IA=>ht=32SJ{$FOD(StdMv4!hx97hQ^USHumkS4Kml5`$XG4GrV@WFQTs zq8p!q)Wm-}v(zr2k0bOg^eb$LsuS}_h?M2K18DP{fH8{(#kbIpu85Fuum#@3o<78jU&b#C zV9mUc;!ba7_AB-*uJwoUT%RozKC+3=PdV1MLbqGs*^t38HIPp!dEE9~J#X=_zkm=J zQdfJ39Qv!ERsRxV<#AOb!c}6)!i<|LK8v|?I=E4?LKd3$ZqW1@BW~WzEj|x8?}wWG zR36r2kekd&ns(Lcy|iuVSw6ZbchF?EcM~I%6Wy~uUV0{mH9ucWHsucRk5ykkC-Iu! z!xSc_FJdsL0JE|kjaBlxJsR;gW>*h?*FX%J%T&ANlVTZ6?0mw_{EFMZaUMe*jIDe+ zEqMz7buuo(ya@E{>7<(Ale_8gH8BOobI`33)d-BYag6Py6^_w|vx8uJ!{vw9*qf)D@g{vddxMp96?1^2*f}!dU8Wa>19^T{Z3KT7V zrl3Bxb&FR1RztmUe&JZpT-qHVyutGS8kGNS*6!}N$vW`l)KDOEuSG!6ZdlhwFf{is D0Ds~O literal 0 HcmV?d00001 diff --git a/src/main/resources/static/style/img/alipay_active.png b/src/main/resources/static/style/img/alipay_active.png new file mode 100644 index 0000000000000000000000000000000000000000..c5e24400828d55144fed66dceb92e47a5ad89933 GIT binary patch literal 2275 zcmcJR_fwMz6UQGQLP8B4q?ka22qVmP{VBpgHuh>CP1NLL9( ziXu4#A|Q%{qI3`lD4`Q1NIm4`{SV%mcita%cV=g2cfPYb^Vwu;D{~?Kll%Yx2w7T~ z;*aCa{}`C}*ayvJ*#ZEVYH4a@8|k*@?24B&hptb9IljpPAgYF&i4}AY)TIh2p2bV* z8g)W6FcxtaDfI8hx!TCfH3=?&|8(lR!3xD&oxZOd3@XK%3NBtf6BY4n;b4EIZ5*~( zhy+<@{zec)!rLiWh>bA`4r9pMk;Rk*x!hSVj5DF`U@*#DvV;US!RG2wp{Gq{xZ4>v zcaQ6#Q16ForV!D4@mis_a2#0+M9M4W{~uall;A%{bHW^H2WR&njoJQf=QFwIVE#Io zb1`eDf?M!+t9~ZE#Leo6AHvXxQyk#l;^=K+D75*x z9ea67bA8XTeE6VahT1CbIXE#nTez;r^EzW|xXX?avz`Zo|D1krpRil;$)hJSo6K8! z*n8b`l)96|2LUR_e!0HMFQ@MGn}LXrw)0nt4^JM>%5pnydW^WOh}^<~+d0$}k&|sB za^Zmcr*I7{D5y{XJR&-~Fc!fU!B3KZR#g=Q7rDywFooGZaK!+Y>7yzj<74k!|bX$bxSs$HR=*sK)qe7K14S zL7@67mts;etv-(|Obe;p!33&e*>{+flEd102LjyKSP!A{)*yyk4@pc;OWmWBIZ5G_ zE^P!w+L5;=PDK*8ehyUdcE*G>6eHc7%vmqVP_Ay)Vr_yjeXxDY9AYR*Ydn4{Nhro` zNkOSo4YknUFIoK6BYh(Hg|8D9{{GU2wcbr!eNMcI;fHHSttknXPnkOkWYWN=?D~-? ziU(xIg^g+G4H~Z3(RLtNyO5#V`oKs3X|pxf#m0JbPd85|ys-4?mb}_;Tnpl?ebx2z zi+=v<&K~M|WBDLR-S`5GQQ{PwduT zkCO9$p-_qxPKYRcWYF-$?j2ChtET9K?s*BG-3$I0gWdp9B}79_fegqbdlaxYJSw$! z+Uxq<9TpR+pKxpIp0a7Ky8BeK7wM)YS9}evLSZ&zQe^OHanY=R=v-w#ApdO5dnXC( zlx5h?8G4ITZFUocJK@Q&Fhow+4BYr1v}b%xQ=;@$Fjs-{Y=vc4mc2(;DYsQ-T83PG zLrHy*S0#lXbu4FBk*m=V-#og;a(_H1A)0k|M3O zH5yv@ru>zS1Vr5!ox5*xRv=TZ*=IEFr9~fcgcFvUV%-%^3~VFOCM6&dlg3N>@M+p% zg8OXQI#sfnzWhcSk|$NX7Or?ke6oT zx)kKvU#>28+r`o^`CVClV_^Y9LUNk<>Q9;RMc3GnsESpmX=U4O?R{yU1+B{tf;Rkh z9Jt9D?l>f_3!S0`c}hYLX1x=xO~Y?W%;^NI!oWfDgxaoeVY)E7zjK#Z+4iO%KKMNn zF)4E(#L^{BJsR(kz~m^ap6D><57sNHy09s|5YXz2X5M|B`1VI^8nkE#D6s#VJ{Jrz z>WP#-S7x*ju zd3sISP^Aa_aL>Y+(0smoOvP4Z<5}Zc$Oqjk?+QVds&YLIOvm}$NhyT7Po1>w7p@w@ zbv-y}$+vBsjQcqKl`K@(f$fawLr$tA27bFv2sGC5uH$2(u@(jaosaJX{=*(ndx9K8 z8wF(xIio_WyFq!ijX4X5Z)GA2ao<+{JiaFf%1Y#&aP4qH;`=wn<)ksHPdZ5@3C%Rs z695Wi5^7FKue999h;;@)Z#V;0@@J`_h?k;N$7~~VB{f%9#^BiuJDw*`;Xy&T)em_E z;QHX`gjs#GU)@LLKB8s{)VUn7@x|`yAjzARAugAEd*_@1OIXxX0N|P0+F@OPxI3i>WbCAp};Gh%Y9t0qT{$la{S6(i( zfxePLp>cLS`0tId`7#a4gAUb*mVtRZy5Atj2n@STAJ8v66KLJ7b~-^BTNSEIu}c%0 z9IX4biq`xdI}FaNNhrZ*%W<8yQ<1x{>+zU{Pvl~b0TDY$Zo z{cF!p3Ng`r@GqG;+JRxI%uNif>e}6OzY^x z@xnmf7~fwbICAz;n=-5{UJEI=Lez{`jF~|iD^9*R;=~=ZcRTCKPn1wDPjn2YNx3ob71$!%MKoUaDLg@HfYeNS7$xwv-hUVClfN=)9z#ib$R zW@-^&VW6+>&RPe} zl?-GJe05wdx}6U3cQFYuG<6E`a8h$V2+{sl^O8ECz{@4T@xUc7Pj9&TC9Q)W^{NB! z*RsI}4}2^V;GuQ!*jj@JEDX*b(DCtiIiMt?B<&hNkv8e$N_m-IYqFn z99T|4T24(}QBhr1_P}pH2erQi-f8+fyQ&+XIQ3g|KuYW2#ee``buc(MI9MiFLB_`) z29{G(Qv=J&gXQI=ffCYigm-}BC24QC*e4~Q^_*~lJNdi$2DtfnA6V1tc)=$yK zU}G1!PoTe(3s79_uetyaI2i;C={H3GK+(w{7w>?-RORgSyB^;_f6tGgot?lgo-STM z6*xda?k~sUd(kJr2Y%7#Zvg%-{#%RzZmxgBVomb5)m$#Q{Y|ws$){=`ZR*ohYXXL> zuH*0G7~tb?>f__7{mERIa4`>wZ);J_@$07 zO0L%dEJSC=0LOo%<;${v)?;9x4)ulyIC?v|Ku>4`W0!Grb5>VSkW+S6mUEJJa&=ab zR&`RmAnoYr;wr7G;;iQCDyJZ;=&bT_kbkcJFRD-YI0dd5(r>Dr0oBJ86;8;WRFYLx zIj$-vcT!1BK~?^Q{4qHlWhFUf75S5&)qYX+U)8$!y9K#8pYr$d`nbk@9sS`hYfDG# z;ODjTyHohh5I!zGb+>;V>ZkrFp7{Iz{7cn8`U5D}{3Le%yOAoM{CAqasQQQI-!c3# z)OB_Zpf~b&1Khf!=NGKjX7Im)`7cy_(Fs5s(9CH!IIw;YU$nnA?ltFX;^Oz`+|L3} zw|_V>zfTX~($pQD*4(x>+!2^uP4GuI_c`y6PJODe*3hQ{e=(>3%zXc)`ro$^e9^`G zx8?qqOZAhgkEQ-z3EamuAlT907t`I%*9z7{F&V6jP+GC z!E00J>GtQD`;`0V3H@w@|7rl5&Q9vCKK@>g0ora}jxZOnuQyB+{72>=ZEF6$7pUv_ zc>4GQ9?eBtK@Y8e~|Ey|f zM;AFoc{LS4wXDm3N%g0({F&32ldutG&9yCg&Dj8U>-cen%4vfCCGU&L`zW(mbH$#4 zAIq!A%K|@OxwYTFRsB-DzN+QMRlgLkulh&L^WJU&+Va1(wl4dNT5EB`C(ASeVr3U? zWhEsAB~9@9?DaLyx?OVdv^e1g>~8QipQXGuu#c72Oa8K^?Kh512 zf$c|I|C9@a%|OHs{yk*>mu2v~yZleS`D0A~Nv-R2Z8owF$i~S{TpROQC*8!g4#>vI zOq`w8}nHw-NdyH$i~S{TpROQC*8!g4#>vI zOq`w8}nHw-NdyH$i~S{TpROQC*8!g4#>vI zOq`w8}nHw-NdyH$i~S{TpROQC*8!g4#>vI zOq`w8}nHw-NdyH$i~S{TpROQC*8!g4#>vI zOowFef!6kU|qa{Z@vZtUvU-M36}!C9(%y) zw6OsfSBN+lS9k;$7o7wAo#NsOl;z@@cHrVtzsJSJ?}M?gKgGob5r>{QW_qcAb_jVd zIWSCxy)x^K83KL1?fPx*Z?>Ei-*(7M$||@2Q0g6r>|-X@b5C_~{qET;np}~r@zdm=@Dg0~yeuJXwMW!D)saZy&xVXAlqZr#WQ#IZ{ zq&By*Sj)GVx)7FSnoUwHP$zx^O--S^05!y}kYg#5Q0+KM8g%}ZW`*<2X_L9WlDc5K z{?6^J0AlwY%Tie9>hSZ2I9COTl6#vF4L9cfZA7LbDdV!~!o3KNR<@h+ZTE&Hqcp_h z*g4OMr7J<1UE3{YFia?hKOT*iA_hXsV$q^F=P*ozc%}rx2csfU85nK$PQkq`gH~JK z>c=Uq#Yud9gr#_WvD10Z&F@LkpaJWcR+{pz^bn671F|!2-Hpp75781y*R8%UP-Q;uPTwWfShCWiA3xHIj#9|n~;CzfVu-CBhyFTVEV zHpAyBFPn!%|j z$tR^=ciml}?iO;adZ~-aP6qivsjYhkh~-9a!{!aC%^=QgN`4a=R*ezuNp&@+j$I$F zShgt&q$Ehs7DrFx&C2je3^(P)P9X!8Cfh~P?zjD2v)2O!et22xCL0@VHEz|O+PBnZ z)2t(`T6;fN44AA?6=Hl-0x}?Sp~o8Kq^3fQzz%9Zk7ZUYds>k-)Ss^ie5B}ub3q3N zKw8qZlDbz!f>@>+5GA!M=gYN?m?fu=S3WhE6epXhpa6AH~-rb{9t~ad-0}*dvOffZ%d*{@9Q$r5Z z^~$1;R65VRGLh+1r*^HSGIxtuky7>3aEa|yRH6Zr7mrGGyLizLSyhFz(u4?mn$+^m zJT;BmnL)k#)HGH7UL!{RK}K3!W#z>-{pZa#Ejq<+Mmrch{elICGB8Sc`aYS|W;tkG zECqtKNu?w~&AJiuWqSsO?UP4+2d7XBt=?3B>~rU~N_!;GiK3#`Mz+J+JO9xmt62%C z#OOGh$>Dyu>XJz<-A#J{Ua@3ycpU%Kl(#H%FhH>nK3^`gPv6BQg>NpxOHhC8MPCX2 z$IhJCxy&BOq8eXml~(bb=;YOK+AMVi?ftHXtml8Kg*=008b(YI@XYb|vjQcVo&5^u zt5K3fJeKJjHGr3Nduy{~QoWX$HRd%+#tw~#qN;$kOT*-f&0RDqW_8Va*{x>wS>Ir| zJ;+H?cNdnucP0>+d&};Z{hhO3ym3kwt)Hkvh_TT50_Wn8Y!?$ z-Kff7R*74f&!dC^p(DX-Y7UU%a(6U_A=F(HJWe7rCUFS+ZV8XJqgk=8-qNXdub0CE z-7y{+mxf`Il?w_cHRIF%_42kU-Knnqv?l1X1R^jhOyK2CNdyA(6x5c~fO6MGX1Mpf zI4hg-<~1Wg&!<}rU2+#K;M8h~oc2fQfi9!G2g z4C|1|k#MISlQWG2Hc3TmOQjWtO!PL#TnEwpFp_a?b5QE*=jmFVZszKqRe@I3iCT4a z>Y{TAo{QvkD4zXucd|kCWm66Jp{~m_;nDZ-k^@YYgso{71qZmhP0756ps9B$L?|kw zWLfyAmabp}UkE^%%&{L>{c>1x$k-_QJ$-eo&o5+>2tvOBD8fjwIO^CW{VANq;zWcjX zU2*ya%=L-ar1l0o#$!coo2pcx&>Ya6Z1kLDWHq*J))9F^qElih?l5N1ij4}5GKl1ylMzy&k0T4T zoM5ps`PlA&kGo-M=+M(OhtBA*T<&pZDVhM(6Utz-Rw38*AO@OxbjAFWmk7(vRI95y zGH|Z%?4%QLTTRq@GYX3%;(Mw+LTqJkPv?%!@RkA2J-)R}OCiP8spo@7AW|Y#E6jJ* zGHg&&@wvzM-Ck`whdsJk+$rA`yp(y3I2rZ)*Xytks2j#RcS8>4R!+|T2u-bX3tM=% zlMJ&#T3!17ZS*WYp#)#kyXZ(VDqZYU@Ne+Cv}v`3q`wQj~xDpiSRp!3S>*Y8=1*kz$;;5R)pc5Z4Mlv%Wdy z*nD8{eW4aco^ARWeKut<307w#;&EpNf57o4XE061vyUsKh?2%OZz?StGAdfT>!Eh% z>riGuEFj#IO8u&>C6ChZF(5E1DQHvA!ddmDde7tgnvLL!Lq~#J&=+GoOc0kRTz`uQ z*6+>IFO|RVu9B_)sa{Mf$RgBe8W-X0g(07&R!aq?hdNeL-4cL3aWH?)#|)cIRIYd! z(J#yKt3SlKEBm!%X$=h<-5SUDmB0XNk`C_N-&bJ|#2dA_Yf(`KHG=Tt+(n|I$9be6 zt<7`%!DY+T=Ore<*o>c?i3-zU1DkQvxxD=JikDrzBdraRY8phu*t5l9kWcdi;UVj+ zK^5ozIe~%pFy6HQZdNB9H|~o{%+ZqG2?ROg+9j~Aj6$HCP=Tvv3ovlAr8eiq*GG34 z{y^T|xkFvr;g)5}=r6ZncvW{-F9atS>V4X8a#j~RC?|SLx-DD5<)h6yr7{Y=Sf((^ zxr~9%(eT%WEX5>x<~ii}W6qIa6SS|&mw~7(pLw}k8CtMKC)X3Ef#(l!fASTVLt;q4 z7OwE{Z@3OPY~k7l{8Kn5 z{lLgvPOGbWV(dioLy);dAC5BNs+Ka|{~VJ%{N_F9Oi>z>)df0?u^D=*lyZ-9EO2~b zKW1>yagIfzD(~i44fnhF(I2p$6$y(P6+H;`)Wg7HOT%P|3lajePv|Z#7#|6QJ;q3! zeoV~=QsK$*5vRw1^3V7?Y{|PZjG>Um7Lb@DyE`6V)1U0m92&^upzv{1tiwWP<CVW<*hn&%Fy(0Z}KR+9s&``pQ}U2s%8SdjNH^l4&^J4rO!ph*|g;74}iE zV?0KZ78nYB?0Q&nnn^7bB~2vYd+mRjQWGP-q)xCw1k1?z9uUF`!I{7^O`_UcL5Oov zLnqlk&5^yF_4>x;L}i|jEc#!3v25U?MX=~DkgAs$M?;ap!U$gsCy!381QUdG__f*OGTdMf9*XXJs6)*Iw$G%<3-YQgt{h zSj}{%1lgM;?_dC1G_j`}pe>_P)HF`|VBQ+<>_NL7zXeIU0QuE$xr566=nlU)0?kuO ze@J~k)}g*-Mh?m@-f3Ri^kNJe6nuIY!B!@oBiGWy@>hFE4HiE8TnsCu)zUNja}RXx z-Liirvo?`oXA+5-h+QO!VUw{sjD=fauUFE0MYB`b%0#WP@FyNkbsR@bQlm1Y$cD`9 zyn0}{VT`{wzPxY_cSy&x_-ghy8N=JUTes#7XT3frb_miNluYFORN z)Wu6+^(D$o;_HG!T(AsHpC6A~uT1GW{^_p_&3SekGR~f^}6qUx?foBBt zgYIKoANuhR1_2VsM!@^4V?BqQ|xw2p1b#I4c2cl#Sp@TXy?r>}) zv-Lk56)je*muOiu7P|WS*KXza$!Ywr zaCTr-GW3c0G*bx4d~)>(X}g)hwwp+Ib{+UFUyOF&LDdQJJOoSmC6O@?^-CYHs0cyw z3QKo;m$!|U1_w$Al}EeTd8GuM)$$Z>F^siE`)k+0jw7;tZdl=2X!VYZD)gK+4KIvTU)X_S+FSh@mq8=?3S@9wE$B`jj#vI4%zWLyS^H|*K;@D zEto;Z=!p4h$g__|5DW`(1NTZ@N!LUR;_#}16S`7C4uf|`Z?^HBp^omEc)IF`A?Q!c znkLF~DKorA_>6_@YN~BMXb&F?+-b1QsuW`Ws5d?6K#E`2TW96C%YmSJ-ekf(og^)j zR)rXYLa2KnW&yu%Y+A9Ud4MHc=gIMm(s`0^;AfYKPk&z*o8n*;#WK$Ds1UMsTxE;b zpBtf_IxR8qJ|qxh4m*Rl-k?Ac0a`v z8fC)IghqXzd(A;Y>|F4p;uVi+XKRz$i!AmYMn;CjW9CWvrQCWcnWQoVrlFt2<|I*S zJ8i)828G*eQ|#hK`+pV9v0Pcu_glT6uG$}FZ}7w)TJnuxL5Ruhe&2CLJI1R$10x!a z56O2fRIC*3(Jg1XMNFWi>!-HZS+~AQQ5975`#O90+_i-7Ui44VTr|oa^$MxR@?`AW zWqX&>rg5LufbSjjbxGX+RQ)0IyHy+Y#b2JCnO;a9B%)L!dQ_je2cgP$KIW)7rl-T6 z#lIts!C%^SLg(xgMrYZ(b&FC}h!aCeCPK$hraUFA$UQv_)@#trGQ&7|pXyi$Rke8f znS78)&O~}8oTI<=V$byh_QQ^WwRJni7FsEcgfOZ4?c>8kmpjvqcZVdYVUZmIFn>t? zxDLxFF|?(1596nRXLZicrGoAaDMP>0tag2*_5OKiL8ur01AUK%iOSn%-ee9saO>Qv z9ZxC8TH&5(o*O)A1>cLG_L5Q_Ab{)HNT)3nsMNA$h##-*Y$Z1DZ8?G2!7STTeS>Km z#fta-xksAs$N(o>%J4L+pEr5IKAvcFn>Dfr|3j5{3ZnI>VA#EQm_O&l%Y{i*VNpXr zi7`V{WZ^_{=Wx?OeHUe2SWth$U^TJR|01kY5=6gny=-xIPmf_vvqRm?l1l^~ekuwJ zhA7p$@h;={xv}&2Fl@Yl%`4x?57Q-?q1txdhh9IWAk{jKGTL$5*ui*89;gRkiHQMK=IVHyj}YwQI;#71PB9kvK&*gWGNgXEGd5R3<(?3R zYNt|Y(7gHEf~5OfmAGW{r@ao&8c~6$JeHyssI5O;ONKZ0ux#nta}T#FJ>C)?z1_kC zYjcP4V**{>gay=i%CWwLfK$bBJi!Qu7!+H+^$fxcRd1iZTP;dWg*V z5pH8-qLgD@1cDv0kJDILXj7^V!dHd6Pg-|jmGDQ}FzjLAqk?HR^byANP^{@8V%>u~ zgR9Sn1hi%Q`#K-s^F1#aQQu8SYWV1t+2Up^5Y&nD*~CfThOV>F4ihPKrbHS^UtQE7 zbog|j9iE%Zi`OFNG_}apu3)yrq-ee#tRLtM(#Wq%^pdazgtZcSH5ClV*9- zMu8=X$c5qfBmzM}t2Q=yxggDd!f3^{q3l6x5^wC8j_RMGMn$X5&hI-8S_}p2`9E8w zJPssElMN$znkH_EW4VP*bi|1dhZ~;~_->jls4V1YJXD#9o+O?R=8ZuPYj$%T<@q`X zVu;mvH%}ii=wsE+6f=LqPrTCA)?5jj8K4oWVhF%PR0x0bIcbw_-Z|km8R+}XXtQFU ziy$(mds&l`>@@vN&a%PvGvby(p8iw>u_N}as5R|2HJ(KD<2w@>BmrSt6|JQHI+B&E z1k4j=_)JLSvC{KRA^shAYAwqiW1fz1FIr8h4<1~^{6KkXWMIg7#2||-=@BDRZ8U=!F_^cagUf;oF&SjIxB`d>h;mS~vK`(@3X84*?IXQl#_J9CzV) zcT#%mZ%|`}SH@EVdHkCn>WCd9fkdkoTWgjnSpO6DK6&I~74F(vflRagb6BN(rJYB> z{YaYme505MIyBYyDRp+R4x&-M(4-Z-GsDUtPG+G=OoVXgu$fG2-P1cZi==Amlj7)B zVMw2ekI4v=bXH0?eJ@Qin0z}qV7_aiWb`V9(F4+pQwiy==_*+MC3RLfZ#;S^t*-)g zAV%}SJ&}A>m}SUe8hH|Cwlt2$6(2#K<{17> zx2pYT_<~_=VLZlDna;q~+ zsknHzVVF~h3)PykPS$+aSKs!lA;PAJm66_ooD3VUj->wlwLtJZ1aY2u6^*JOBjY4OOJy2PE#W)?T`RjQU{(e#Ivo?*;?3dRuGvFhM_0$X;vu91pG%I3o2J(Iy{cfSolTkC3>7S=;Y5-Xl;u= zOh^?rM>!2T)sEHa?;6zR2~-SiYj;;FUl@SosxY4p*9r&;&)pNmP+mFhoyci0tOIvT z&BQcwi-b6Lr&`V4CE4_ zS{ByEFqhpr=aiT9iK9PleyB2x%+9eZy40vr9keoE<2w_qkrfH5T$vA3bEO?keHT9Z z^D|ow-XY-eM7`SRA1y7nXOakE0wlf`DK;Ned_vv-E)3) z6$Dt#;+?4H(qz#_6>rGx_URe}9S+9`t_5pFs7uaeR~pHEIAqZ%BQ#A*Obo#?fFuGjMc3I((3+vztgQJWU)94# zlWjykyE|CXnuBZa-=vOsvQ~%aqb?8wqcokt{&UaY)MCkKYMVGqSKLD+PV-G+phTms z@VVzP^TNc~a^ZX3NnsOtBcY4Y+-hez&S@MkrLSLytztqIiRARBzpAhJ?B{GLzc|)z zNxO;v(Y!F%)ZIisizjk_3NcKP=mJ06a90ULM0=4_`G);qspw}}L>&tZd3Bgp>9^pJ zrcha)8z~lJiBUi;-($X+7N!$v*o7*Ef*SCQDBRO5$5Yir_s6CfG{IX|QnW8=cbq}35&4!z(gVN@doTaOD}OUqW<@ zN=V1(+nA=j1)F%*iYMff)pRObe9F=W^^19SAG&9#uas7`l&|)nJz)@%N{Lf@?9?cT zc?iYTFy$d%pj=Iz{-t4#xfwIpWG>&|%Q8wKL!TCD!PMoN5( zfXJCT1~K~a1I(!4)rjXL&pL14Mil`OgW*8Z@L;u1Njo%nB5NyGzvt|gKp%ZS+Y%@t zZlQAIn`<4ukBev<=$@5*42tP7|5v3 zZ98wd_C&KP=l8Er%94U$AC3;O^2Z60g$k7ml^XQv>Zq2>P0`@ij^Q5MQXH&XR9Nab ziLFJP_ab`rfEYogwUK8qC&!ew>1Gp^26&yF6{n;RGy8KHaF&*s8jSWZ8DUd+dPqSv z(F?ghGP^QwOE~W~3v+RVIVRp{HOHOTdpi_fNn`=qgM6!LUq#pl|e4e!)s=<8G{w-y|r=NsFHZ8il zxB`v>hi|~6Ya2}Q_#2LmNgeRg!FRv@S@C>GRGLw?Tj(D5_ciqV2Rkhok$kt@QMInH zmBR1yM9C@`;AI?qeL3;+5Pi)eGl%w+st^@`lao=7ADmu?oOmFj6mVgeNlI0kddOt= z2gF?FgT%-OnRw%yA<;L@@S($+Mm${V2OPxfdCMonE6YwUpuOm*#Y(Y!!LHXGf~T@7 z%Z9t_E_A}><*k^>qlh`+(bHmFgsD-ekFHRVv#MhcQd#3kpFTDoau6pvV9Y^k zlJ$s?#M)D@dJ$nF=j-ZHl8yl%E0wrj?+r82WX}Poy}HR4`dFi#r5?OF0L&* zxQ+rRV{5PP3O;51O*T;SbLq8`pG*IJ_CM77MRWgNZ}>Nl$jxb--tLvBtF@qb3HkyEFOIN|Xf?nH13j!!B62s~W!!miYrkD( zIOFa4;>VmO+q0YzZ5An??MA}ASW;n;$ilQXw+8C* z_oeZ#W^YV@=-HNCoSQO_Ilegdv^miGeD?Evwi@s(-`5ti5SJ#~rZ2C4t5xd*huo?J7wY`75af64S?&n?h>^8!8g13VdoDY7)J$FRq(rtL}&TISX*huXsA zOl75em2n`%84Ny-V5FRO3pgRdoWZCZxsQ5mjtf`708uT6gYBHjKRdWB{Tz$W6gM}Tn>}nXp6_}mlS?G&zBQvM%?@;EXg2q`6 z2nLN>_oJ;ktIf#Yy4YS8Kw9Ai6j`kHTV3%P#sCx49Vey`4;(pRz#!mw|1ee)OcV_o zU_yZ20$V7$Y-g;bP52ZFYEGB0IK`3c`USmQ7PrDR3Qr`;vgm-D@3W0>{af@F*X(-CFmL^;T#R zIUqzofX{Ur=iNIUxeC@VQm8MZn@kVZB{`eZZ8`~zX`K5~SqA~3_oykJ6F8>H{xy-I zZ6>oH()p;9plrj!fjpYP)=u+=h1(}-V_t(ERgSAhy*yL1)CmWSPdou~r2bCO3b2dB z8*qB!Xi77)pnS{wv^K;ObtR&biRMBvc5o#@$_^e#|7mb4F%vCO^?+?}SX9pJHq$t+ zl1v#J`*s3hYsINBc+$CjpRFPbU&?oZznf4JS_3BPnA{x2onzNM4)t`4&*$VF;U16O z9e1rqoZbnn9rlu}1qWTrq~8M>9YPZdYdBC~Ifj-sO%pC{U08hfjTI+(TCcEp+!V>M z$6RXqTn|=V`pD{28iKgQqKHYY*1H^DXbT&!x*| zvXIHZEE-EXCvf;tEU;c7fpll=bW0wUJvJ6bh`Ruqk+#)jk&`F{472g7%UjwQlkfrP z6UQ$KuB9bOOf&HmWZnXLnm6A;Cj>b!%Z(jv?r9_+hUT57K9+~r$PqpE8nfUc1&3(b zni5CO<{$PM%1BtPwp5@&K*SiVW?`te5nXzCX<>f#ut{yj;P#6CM>L2+{dl1>)%IXF z2w|2IPTEC7Lx@QjW(kuO)99e4d__r_Wk29W16EJLK_a#ZdSOP?O{hWmbz=k$7S7~Bpz}zGv0YayE3B_ zjk!=fGO_=h)B);q0qfw|usHv5xFL>VrtaH`e zRuABsT6L#V7Fv042n)EEmRXGrtzd5DEK!%ksdXu1@Z~^9RUGPLA%nVW;VXdK{F_;F(3`Kb2V zYZjbjo}4BVPitGHkwTJG9(d`Ot@&tE444;iSfO(%fH%yM5oIg^YcGV>W)a`TnUGpm zv9OmpN>22xK156ygR=*h78+M!CD`gI!kZb<0ZG`!XG?5n1g8<2!4bAqW^ts6F^y!T zNisJzrpbZ!7KD$FVd-M*X8}*&n3qYg8cL$5B!#jXFq)Md?s#6A*Q*nn4q-V=miTTF zWGMD{R3`;r4}+>Eu)8z?f5q35X{CsseVO(8*o{ znh_!~61sZdI*wB(VMp${*2xhsy_~tdJVSNg{iF=eG6)?%N|ozLued_Yf27(waSPh2 z2i%hePXc{vl}YA!1Y2?R)dU0Y*RW)X1PMeahesyN5coAz7(RnD33CzTl!FL3%8*)s za!)>}@-cCx@}Wen2yV21y+Q+QvGbePJhW+?{&TTL-iXyC`f?tM2EMn-Q1PL^2eu^I zG~pqWsygVvqLXBj**HV2q+m(~-vz+=@U5BhGWC$K(U~H-`pbMO67*PLfOQ@9i=6!Q z-YYaomT1M&4ci)a(qee*9vvL8;Azh~n|^GRhL-8Ww(Re(a6E@WgSlVffKBjT4m%{x z@|J6!cRpk-5HLm0{iX8W0$u z)}o?m2y+?-j0xnUS4bwkHJXp0rjSSW3M}peqo9w z5R{<#dmtx8@2$04UBgz}WkGU359Z=3IU6JMPr}bHgVnVd^NTq1|3b8Tk1zI)xwh;0 z)a;!3k=|!L_Uvg(LPPf=*`4<*u6f)Y;9lC3!L2z?faAq@V0XrrXJcy~gyswg&(pZM zq+gpaGOj_{;>0_TuH(-Dz0 z(b`LvY+5_GNkBKXHy3Ca{#`+;Qbdnd+Ktuw-mF+tYCInj)oO`2V~1GntSUuuIq-hf z9RYh+)mik$tQoxWa1n zL^pEC02lzz44Om|eMPJGZpv&jAIon;Iffk4LbMs+n#>pK>XK%7xW*4SgoSqSNI;@| zoAFg^eIqz==4&YX7g~X8dYDi+gKu#~B{XB;Q{h?#Vyjz?D-07Ha8C4QHoAeWg&?X7 zr{oWZg`qgpEdsgGK_5}m#QWk#caFtpjN67_#{^zRR8OvF>p=TUz0R$>r4nxa8Kqzc4(U(X$iqV4BrBK=$ z8>-}OatKV)^*~@wJZvGI#u{6oE8 zT(8{V!sP#g-Y-Y@#r@2GIE+o~|8iUQpH1f`_D}bVzvS>IYk&0r=VO?)z4gDozyABj zJO4esUvB5?I`$uXO7%ab_lv0He+tk4l-^D38$LVzuHb+7y9WQ!z3fj;>fc`O`hO~f x_|n(@v#tLBoxN-m`=_tweCDuYn6sY?mwp>@h4uqx?LVJ}o-{m>ciiF1{{c7&`8ogq literal 0 HcmV?d00001 diff --git a/src/main/resources/static/style/img/weipay.png b/src/main/resources/static/style/img/weipay.png new file mode 100644 index 0000000000000000000000000000000000000000..8a8c20771e14a954e8e16d7a4516464129a032e6 GIT binary patch literal 1801 zcmd5->pv3;7@k^;!cZ-@+GZ|gr3|s=G8F%WS<2}o#3Fc0%M=>={(vgEs$kmXdyfJncJp=QOjN}l5MP^!bBts;bIhbME@nJ!VH! zknB>1;CYzB-Kfr*1wJlg*40a*%2!2z&bVyvxH361QBiOZb!O=jI)|^>@FDr91d3h4 zth<#cy}fkZXtfPbb?_~N&aBW{yJ9p-tbRz$QN8yOB#HOat#>WI4*PpO3LA#alY_q3 zObsE~Jz{#fYYL{9W7ev<-VtGszs>TiY(HDMO3f;|-Q3~QV4Ke|p+)8vO<@|-Cq;&i z{cpm#;Rfu=hN*BGU&!;1zPeS@SFqc|yL-8DWeC$-VLOs!ak|)b!-jTPEWKj1aiHo<*L#r_tv(wUHF8I2W8u;t?&&JnROIpppuzdQfX&pqZV@%eib!f z2IvP@}sXCdwl|;ym8e!Scbrfc#**$2D3Dyv7JNW3Y1{H0K${VpB=Lu&&}C|QzydzYx$YJ9P{RF%TO z#Xe+Me7<71+o;<=q(^?lEXV1$tOX4u19@2|8{Zu7uQ-kSnG)MR(wBL--)Oev+~Y=J z*-QaJ^E7R~q#I%rs(Ep1JB-OTax8)C7rdbKk@l?hCEjD^5##STu2`i2~dGz*BE)ZXBAU^)A}O?N+%y+}dR{(tVMZ z=b^$pK@2$leLj)-Bbl+@34*0DgQ*6Nxn2fEpq0}FPta4NW;I&OnEz^U(3 zw?(we}<&x1E{S9Pa$dU{58CwKiS zdaC!a<)m{2ZlU^H@U8e5G*g`_yip(n`bg5{eweb4fz=oCc*4cSZ}=302*_Y=tJ#^6 zwK-T#O`|dZLTq_&M1EWwG*#~u#dG{e%?%~ph9n5OKx^_rncll-m5_nCIT@>Mk+6rb zrdI?{dc3h3F>vy|?%s-H>3M$!kLM>ZG>cEwRyEuc*mVlB%xK5(le3ly4%1N7Zh-bu z^TFjijFf$~`5Zwb(!RP$jALiS7K2hJMK{9cjN5Hr0~{ZP&2!H)EZW3#LOHt3hnW>^ zR1D_kN?N2xfSv1h-*WZjx3}xwZSo+crRzO{nMci+wV@74^%+}mWzeSZwOAkJfUDxw zStimRlq5Q*{W@sSlMw5*C3NFSFB)c!A(-{`p!BS2!OR*>5uzO-6&`Ti(hYfDa)gjd z^8O>yB#|lcr%PiitXAU82V$Qr_$nr$tW90O*z@CyQG*HlG3tj+7KLY>5VNf;cD@2+ z+(ukqn#UXq)F&uc-(S^o-%N?qQ1(!^+ks=1nMWpvJu+qH*7Yd<$<3qxF0WuY`b9(b zq_6&7;X#6LdBY*t%?=J3Tc8(xx#zmyHiyCaT||do7XiXfYe$~`(CP%+2OLvcte^~R z*?;Hl_{)j)c?cqIi|86Sa3Vu$Bib?luW0|bUM9V%pSh_tMc>hliUSaAobaXAWcpu^ C|4+>T literal 0 HcmV?d00001 diff --git a/src/main/resources/static/style/img/weipay_active.png b/src/main/resources/static/style/img/weipay_active.png new file mode 100644 index 0000000000000000000000000000000000000000..e54d862256e2cec70721fe7be9b8ef77f9f9c6df GIT binary patch literal 1920 zcmchY`#%#31I9NEGpx;}xsGi`D#_)zO=+4V4IL~Yymnlo$a0@yGgHcI97W`kka*uD z_pBA>l3Wf+;V{=w*jQ`qSmo{gC(aMg^L#$v&-eG|)^Bc3$`CCG002;SK8yGGxxxPr zSng+&7xRe#fV`749!E?J;QP2nYh8p1kcEGIblpN<$g@Oc9Sl?d8`Ni*n<7)GT3FKg z3B)yo*`~Fpy;X4Ws>sV&R*ud`;Iz|13qKR>69>WqQ(@7M2m&AXp&{y=^wkfx^d)YT zptkQYB%1+L$9uzS^p9kokYjea->lK+YQW^2bSoI(pwk*UahVYGzkww3IO-o1>%Go=~@jMG#g3#7M@PMZT2;ASrG+F#! z`l3VNog@72z}S$q><1@a!XW9H?tpiQBLnC=?LQ$k5|k(cpbJr%^X8})13{IP_UL&0 zw<9qzBD1tNHT_sS%leq9Ci~rvja6Avwf;1&P5?_y1e=0CDV5foJhu0KS(q4caHiwy zpVO43;=h*h7^y(uTKCJ0M)L+0YenS^W{Vzuj-7hx-|;~&Dg8T#!cQqPiFSD%#(w?e z==zjy$A=eR<%)_7JaheNn;3wa+$){KrE;*=$Y64_4Ui+-DQBF5023bT^xxHLyu(v$Oy;q6iF z!j?nzEubR!%?Eku(F3?D2;fI9(kNCI#P3gM`Bwq4yyR|vG>3qpC)}I0*=zf{=;(rq zXbc+!rFo1$?x(0uOG~EC&Wf*=_Gfr$F!NOk%&^_gl(AKdZjmtn^#nbZ2~gpowwEUB z$f%4;m4XA3>)>g)hk4c35o55YE;oJO;3b7Oi* zt0M)qU2uV|w=&x755F5@rm8gUg=%D%r~ymq4Q>Qj+FpLk7KeW-wJ&|#zgi0<-Wc(r zR-vJ$-VeW}t(qGZ1d?)or7MHh@LWmQE%W`Mj)V{L*tf?*?y^L- z%?}gGifr2lF$n0Ls^Kd-vGu%LKHpSnP2pD+v-SZ@Zq9aEcL%z*e_1BEO`Fb$q-gs9z=6Eh!yCv#Q};KL!sFPBJZ~-@%BQ_iV<5 z4a#TpIq3TeU~iq) zY?kV&r~qG~OhlVE8_f^}JP+eK6f@*JAdibod zBj`aL!R92KbZ9DE9i<)O zOAp$8u5JOD;tRQ%_2wSXRlzUYMzOuJHc|2s(>#emj#YwXzHqN(vkByU&47RZ3uOD) zAx1bjLR#AAgj}HPTwXR>y1&|$x?P5;4}LsnNVmVeF<#Uxks}9OIxYiV4>>%c5XPiS zJqzO(Yd^BGR9;BYj14y$wtUpaqF2{dn*d~Pj%~vz%vIiqde8IV-E~6dxy{Y<(l1s` zS6(|*l0C1&X`q-|U6V%h2xwcYaxt<$<3`HLwcFEIRmZrtI*ebL$OY@Osn zMy|LLY%r& zsg7`xeUuh=#N+1}TWuK)*lXcG_Tq6K-nmioI472QIRl&}ua1|6_t8&Suff#u8vD5O uHD`!0k`g$*66%g)n(8aDZ2x)To1&)GZeHVtP3_NT1vopp;h#DLGX4iS(QZ8e literal 0 HcmV?d00001 diff --git a/src/main/resources/static/style/login.css b/src/main/resources/static/style/login.css new file mode 100644 index 0000000..eb603b0 --- /dev/null +++ b/src/main/resources/static/style/login.css @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.login-panel{ + +} + +.login-panel .qrcode { + width: 100%; + hight: 100%; +} + +.login-panel .validateCode { + /*height:20px;*/ +} \ No newline at end of file diff --git a/src/main/resources/static/style/preLoading/main.css b/src/main/resources/static/style/preLoading/main.css new file mode 100644 index 0000000..669ebc7 --- /dev/null +++ b/src/main/resources/static/style/preLoading/main.css @@ -0,0 +1,459 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + _____ _ _ _ _ +|_ _| | | | | | | | | + | | | |__ __ _| |_ ___ | |_ ___ _ __ ___ __ _| |_ ___ ___ ___ + | | | '_ \ / _` | __/ _ \ | __/ _ \| '_ ` _ \ / _` | __/ _ \ / _ \/ __| + _| |_ | | | | (_| | || __/ | || (_) | | | | | | (_| | || (_) | __/\__ \ + \___/ |_| |_|\__,_|\__\___| \__\___/|_| |_| |_|\__,_|\__\___/ \___||___/ + +Oh nice, welcome to the stylesheet of dreams. +It has it all. Classes, ID's, comments...the whole lot:) +Enjoy responsibly! +@ihatetomatoes + +*/ + +/* ========================================================================== + Chrome Frame prompt + ========================================================================== */ + +.chromeframe { + margin: 0.2em 0; + background: #ccc; + color: #000; + padding: 0.2em 0; +} + +/* ========================================================================== + Author's custom styles + ========================================================================== */ +p { + line-height: 1.33em; + color: #7E7E7E; +} +h1 { + color: #EEEEEE; +} + +#loader-wrapper { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1000; +} +#loader { + display: block; + position: relative; + left: 50%; + top: 50%; + width: 150px; + height: 150px; + margin: -75px 0 0 -75px; + border-radius: 50%; + border: 3px solid transparent; + border-top-color: #3498db; + + -webkit-animation: spin 2s linear infinite; /* Chrome, Opera 15+, Safari 5+ */ + animation: spin 2s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */ + + z-index: 1001; +} + + #loader:before { + content: ""; + position: absolute; + top: 5px; + left: 5px; + right: 5px; + bottom: 5px; + border-radius: 50%; + border: 3px solid transparent; + border-top-color: #e74c3c; + + -webkit-animation: spin 3s linear infinite; /* Chrome, Opera 15+, Safari 5+ */ + animation: spin 3s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */ + } + + #loader:after { + content: ""; + position: absolute; + top: 15px; + left: 15px; + right: 15px; + bottom: 15px; + border-radius: 50%; + border: 3px solid transparent; + border-top-color: #f9c922; + + -webkit-animation: spin 1.5s linear infinite; /* Chrome, Opera 15+, Safari 5+ */ + animation: spin 1.5s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */ + } + + @-webkit-keyframes spin { + 0% { + -webkit-transform: rotate(0deg); /* Chrome, Opera 15+, Safari 3.1+ */ + -ms-transform: rotate(0deg); /* IE 9 */ + transform: rotate(0deg); /* Firefox 16+, IE 10+, Opera */ + } + 100% { + -webkit-transform: rotate(360deg); /* Chrome, Opera 15+, Safari 3.1+ */ + -ms-transform: rotate(360deg); /* IE 9 */ + transform: rotate(360deg); /* Firefox 16+, IE 10+, Opera */ + } + } + @keyframes spin { + 0% { + -webkit-transform: rotate(0deg); /* Chrome, Opera 15+, Safari 3.1+ */ + -ms-transform: rotate(0deg); /* IE 9 */ + transform: rotate(0deg); /* Firefox 16+, IE 10+, Opera */ + } + 100% { + -webkit-transform: rotate(360deg); /* Chrome, Opera 15+, Safari 3.1+ */ + -ms-transform: rotate(360deg); /* IE 9 */ + transform: rotate(360deg); /* Firefox 16+, IE 10+, Opera */ + } + } + + #loader-wrapper .loader-section { + position: fixed; + top: 0; + width: 51%; + height: 100%; + background: #222222; + z-index: 1000; + -webkit-transform: translateX(0); /* Chrome, Opera 15+, Safari 3.1+ */ + -ms-transform: translateX(0); /* IE 9 */ + transform: translateX(0); /* Firefox 16+, IE 10+, Opera */ + } + + #loader-wrapper .loader-section.section-left { + left: 0; + } + + #loader-wrapper .loader-section.section-right { + right: 0; + } + + /* Loaded */ + .loaded #loader-wrapper .loader-section.section-left { + -webkit-transform: translateX(-100%); /* Chrome, Opera 15+, Safari 3.1+ */ + -ms-transform: translateX(-100%); /* IE 9 */ + transform: translateX(-100%); /* Firefox 16+, IE 10+, Opera */ + + -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); + transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); + } + + .loaded #loader-wrapper .loader-section.section-right { + -webkit-transform: translateX(100%); /* Chrome, Opera 15+, Safari 3.1+ */ + -ms-transform: translateX(100%); /* IE 9 */ + transform: translateX(100%); /* Firefox 16+, IE 10+, Opera */ + +-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); + transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000); + } + + .loaded #loader { + opacity: 0; + -webkit-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; + } + .loaded #loader-wrapper { + visibility: hidden; + + -webkit-transform: translateY(-100%); /* Chrome, Opera 15+, Safari 3.1+ */ + -ms-transform: translateY(-100%); /* IE 9 */ + transform: translateY(-100%); /* Firefox 16+, IE 10+, Opera */ + + -webkit-transition: all 0.3s 1s ease-out; + transition: all 0.3s 1s ease-out; + } + + /* JavaScript Turned Off */ + .no-js #loader-wrapper { + display: none; + } + .no-js h1 { + color: #222222; + } + + #content { + margin: 0 auto; + padding-bottom: 50px; + width: 80%; + max-width: 978px; + } + + + + + + +/* ========================================================================== + Helper classes + ========================================================================== */ + +/* + * Image replacement + */ + +.ir { + background-color: transparent; + border: 0; + overflow: hidden; + /* IE 6/7 fallback */ + *text-indent: -9999px; +} + +.ir:before { + content: ""; + display: block; + width: 0; + height: 150%; +} + +/* + * Hide from both screenreaders and browsers: h5bp.com/u + */ + +.hidden { + display: none !important; + visibility: hidden; +} + +/* + * Hide only visually, but have it available for screenreaders: h5bp.com/v + */ + +.visuallyhidden { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +/* + * Extends the .visuallyhidden class to allow the element to be focusable + * when navigated to via the keyboard: h5bp.com/p + */ + +.visuallyhidden.focusable:active, +.visuallyhidden.focusable:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + width: auto; +} + +/* + * Hide visually and from screenreaders, but maintain layout + */ + +.invisible { + visibility: hidden; +} + +/* + * Clearfix: contain floats + * + * For modern browsers + * 1. The space content is one way to avoid an Opera bug when the + * `contenteditable` attribute is included anywhere else in the document. + * Otherwise it causes space to appear at the top and bottom of elements + * that receive the `clearfix` class. + * 2. The use of `table` rather than `block` is only necessary if using + * `:before` to contain the top-margins of child elements. + */ + +.clearfix:before, +.clearfix:after { + content: " "; /* 1 */ + display: table; /* 2 */ +} + +.clearfix:after { + clear: both; +} + +/* + * For IE 6/7 only + * Include this rule to trigger hasLayout and contain floats. + */ + +.clearfix { + *zoom: 1; +} + +/* ========================================================================== + EXAMPLE Media Queries for Responsive Design. + These examples override the primary ('mobile first') styles. + Modify as content requires. + ========================================================================== */ + +@media only screen and (min-width: 35em) { + /* Style adjustments for viewports that meet the condition */ +} + +@media print, + (-o-min-device-pixel-ratio: 5/4), + (-webkit-min-device-pixel-ratio: 1.25), + (min-resolution: 120dpi) { + /* Style adjustments for high resolution devices */ +} + +/* ========================================================================== + Print styles. + Inlined to avoid required HTTP connection: h5bp.com/r + ========================================================================== */ + +@media print { + * { + background: transparent !important; + color: #000 !important; /* Black prints faster: h5bp.com/s */ + box-shadow: none !important; + text-shadow: none !important; + } + + a, + a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + /* + * Don't show links for images, or javascript/internal links + */ + + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + thead { + display: table-header-group; /* h5bp.com/t */ + } + + tr, + img { + page-break-inside: avoid; + } + + img { + max-width: 100% !important; + } + + @page { + margin: 0.5cm; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } +} + +.back-link a { + color: #4ca340; + text-decoration: none; + border-bottom: 1px #4ca340 solid; +} +.back-link a:hover, +.back-link a:focus { + color: #408536; + text-decoration: none; + border-bottom: 1px #408536 solid; +} +h1 { + height: 100%; + /* The html and body elements cannot have any padding or margin. */ + margin: 0; + font-size: 14px; + font-family: 'Open Sans', sans-serif; + font-size: 32px; + margin-bottom: 3px; +} +.entry-header { + text-align: left; + margin: 0 auto 50px auto; + width: 80%; + max-width: 978px; + position: relative; + z-index: 10001; +} +#demo-content { + padding-top: 100px; +} + +/* + Ok so you have made it this far, that means you are very keen to on my code. + Anyway I don't really mind it. This is a great way to learn so you actually doing the right thing:) + Follow me @ihatetomatoes +*/ + + +.myloader { + border: 16px solid #f3f3f3; /* Light grey */ + border-top: 16px solid #3498db; /* Blue */ + border-radius: 50%; + width: 120px; + height: 120px; + animation: spin 2s linear infinite; + } + + +.hide-myloader { + display:none; +} + +.centered { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} \ No newline at end of file diff --git a/src/main/resources/static/style/preLoading/normalize.css b/src/main/resources/static/style/preLoading/normalize.css new file mode 100644 index 0000000..1fb7c41 --- /dev/null +++ b/src/main/resources/static/style/preLoading/normalize.css @@ -0,0 +1,544 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*! normalize.css v1.1.2 | MIT License | git.io/normalize */ + +/* ========================================================================== + HTML5 display definitions + ========================================================================== */ + +/** + * Correct `block` display not defined in IE 6/7/8/9 and Firefox 3. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} + +/** + * Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. + */ + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. + * Known issue: no IE 6 support. + */ + +[hidden] { + display: none; +} + +/* ========================================================================== + Base + ========================================================================== */ + +/** + * 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using + * `em` units. + * 2. Prevent iOS text size adjust after orientation change, without disabling + * user zoom. + */ + +html { + font-size: 100%; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/** + * Address `font-family` inconsistency between `textarea` and other form + * elements. + */ + +html, +button, +input, +select, +textarea { + font-family: sans-serif; +} + +/** + * Address margins handled incorrectly in IE 6/7. + */ + +body { + margin: 0; +} + +/* ========================================================================== + Links + ========================================================================== */ + +/** + * Address `outline` inconsistency between Chrome and other browsers. + */ + +a:focus { + outline: thin dotted; +} + +/** + * Improve readability when focused and also mouse hovered in all browsers. + */ + +a:active, +a:hover { + outline: 0; +} + +/* ========================================================================== + Typography + ========================================================================== */ + +/** + * Address font sizes and margins set differently in IE 6/7. + * Address font sizes within `section` and `article` in Firefox 4+, Safari 5, + * and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +h2 { + font-size: 1.5em; + margin: 0.83em 0; +} + +h3 { + font-size: 1.17em; + margin: 1em 0; +} + +h4 { + font-size: 1em; + margin: 1.33em 0; +} + +h5 { + font-size: 0.83em; + margin: 1.67em 0; +} + +h6 { + font-size: 0.67em; + margin: 2.33em 0; +} + +/** + * Address styling not present in IE 7/8/9, Safari 5, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/** + * Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +blockquote { + margin: 1em 40px; +} + +/** + * Address styling not present in Safari 5 and Chrome. + */ + +dfn { + font-style: italic; +} + +/** + * Address differences between Firefox and other browsers. + * Known issue: no IE 6/7 normalization. + */ + +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} + +/** + * Address styling not present in IE 6/7/8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +/** + * Address margins set differently in IE 6/7. + */ + +p, +pre { + margin: 1em 0; +} + +/** + * Correct font family set oddly in IE 6, Safari 4/5, and Chrome. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, serif; + _font-family: 'courier new', monospace; + font-size: 1em; +} + +/** + * Improve readability of pre-formatted text in all browsers. + */ + +pre { + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; +} + +/** + * Address CSS quotes not supported in IE 6/7. + */ + +q { + quotes: none; +} + +/** + * Address `quotes` property not supported in Safari 4. + */ + +q:before, +q:after { + content: ''; + content: none; +} + +/** + * Address inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* ========================================================================== + Lists + ========================================================================== */ + +/** + * Address margins set differently in IE 6/7. + */ + +dl, +menu, +ol, +ul { + margin: 1em 0; +} + +dd { + margin: 0 0 0 40px; +} + +/** + * Address paddings set differently in IE 6/7. + */ + +menu, +ol, +ul { + padding: 0 0 0 40px; +} + +/** + * Correct list images handled incorrectly in IE 7. + */ + +nav ul, +nav ol { + list-style: none; + list-style-image: none; +} + +/* ========================================================================== + Embedded content + ========================================================================== */ + +/** + * 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. + * 2. Improve image quality when scaled in IE 7. + */ + +img { + border: 0; /* 1 */ + -ms-interpolation-mode: bicubic; /* 2 */ +} + +/** + * Correct overflow displayed oddly in IE 9. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* ========================================================================== + Figures + ========================================================================== */ + +/** + * Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. + */ + +figure { + margin: 0; +} + +/* ========================================================================== + Forms + ========================================================================== */ + +/** + * Correct margin displayed oddly in IE 6/7. + */ + +form { + margin: 0; +} + +/** + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct color not being inherited in IE 6/7/8/9. + * 2. Correct text not wrapping in Firefox 3. + * 3. Correct alignment displayed oddly in IE 6/7. + */ + +legend { + border: 0; /* 1 */ + padding: 0; + white-space: normal; /* 2 */ + *margin-left: -7px; /* 3 */ +} + +/** + * 1. Correct font size not being inherited in all browsers. + * 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, + * and Chrome. + * 3. Improve appearance and consistency in all browsers. + */ + +button, +input, +select, +textarea { + font-size: 100%; /* 1 */ + margin: 0; /* 2 */ + vertical-align: baseline; /* 3 */ + *vertical-align: middle; /* 3 */ +} + +/** + * Address Firefox 3+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +button, +input { + line-height: normal; +} + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. + * Correct `select` style inheritance in Firefox 4+ and Opera. + */ + +button, +select { + text-transform: none; +} + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + * 4. Remove inner spacing in IE 7 without affecting normal text inputs. + * Known issue: inner spacing remains in IE 6. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ + *overflow: visible; /* 4 */ +} + +/** + * Re-set default cursor for disabled elements. + */ + +button[disabled], +html input[disabled] { + cursor: default; +} + +/** + * 1. Address box sizing set to content-box in IE 8/9. + * 2. Remove excess padding in IE 8/9. + * 3. Remove excess padding in IE 7. + * Known issue: excess padding remains in IE 6. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ + *height: 13px; /* 3 */ + *width: 13px; /* 3 */ +} + +/** + * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome + * (include `-moz` to future-proof). + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; /* 2 */ + box-sizing: content-box; +} + +/** + * Remove inner padding and search cancel button in Safari 5 and Chrome + * on OS X. + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * Remove inner padding and border in Firefox 3+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/** + * 1. Remove default vertical scrollbar in IE 6/7/8/9. + * 2. Improve readability and alignment in all browsers. + */ + +textarea { + overflow: auto; /* 1 */ + vertical-align: top; /* 2 */ +} + +/* ========================================================================== + Tables + ========================================================================== */ + +/** + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/src/main/resources/static/style/theme.css b/src/main/resources/static/style/theme.css new file mode 100644 index 0000000..2b0ddef --- /dev/null +++ b/src/main/resources/static/style/theme.css @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +body{ + background-color: #ffffff; +} + +.navbar.navbar-app { + background-color: #F26E5F; + color: rgba(255,255,255, 0.84); +} + +.foot-wrap{ + background-color: #5B90E7; + height:100px; + color: #FFFFFF; +} + +.foot-wrap > *{ + margin-top:20px; +} + +.avatar{ + border-radius: 100%; +} + +@media (min-width: 1024px){ + .left{ + /*background-color: #5B90E7;*/ + /*color: #FFFFFF;*/ + /*margin-top:-20px;*/ + margin-left: 20px; + display: block; + } +} + + +.left .separator{ + clear: both; + overflow: hidden; + margin-top: 10px; + margin-bottom: 10px; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + content: ""; +} +.left .nav { + font-size: 20px; + color: #000000; +} + +@media (min-width: 1024px) { + .right{ + width: 61.8%; + margin-left: 23.2%; + } } +.right{ + +} + +.right .separator{ + /*clear: both;*/ + overflow: hidden; + margin-top: 10px; + margin-bottom: 10px; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + content: ""; +} + +a, a:hover, a:focus { + color: #000000; +} +a { + color: #337ab7; + text-decoration: none; +} + +.form-group .checkbox label, .form-group .radio label, .form-group label{ + color:#333333; +} + +.form-group.is-focused .radio label:hover, .form-group.is-focused label.radio-inline:hover, .form-group.is-focused .radio label:focus, .form-group.is-focused label.radio-inline:focus{ + color:#333333; +} \ No newline at end of file diff --git a/src/main/resources/static/style/timeline.css b/src/main/resources/static/style/timeline.css new file mode 100644 index 0000000..be50a21 --- /dev/null +++ b/src/main/resources/static/style/timeline.css @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.timeline { + list-style: none; + padding: 20px 0 20px; + position: relative; +} +.timeline:before { + top: 0; + bottom: 0; + position: absolute; + content: " "; + width: 3px; + background-color: #eeeeee; + left: 50%; + margin-left: -1.5px; +} +.timeline > li { + margin-bottom: 20px; + position: relative; +} +.timeline > li:before, +.timeline > li:after { + content: " "; + display: table; +} +.timeline > li:after { + clear: both; +} +.timeline > li:before, +.timeline > li:after { + content: " "; + display: table; +} +.timeline > li:after { + clear: both; +} +.timeline > li > .timeline-panel { + width: 46%; + float: left; + border: 1px solid #d4d4d4; + border-radius: 2px; + padding: 20px; + position: relative; + -webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175); + box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175); +} +.timeline > li > .timeline-panel:before { + position: absolute; + top: 26px; + right: -15px; + display: inline-block; + border-top: 15px solid transparent; + border-left: 15px solid #ccc; + border-right: 0 solid #ccc; + border-bottom: 15px solid transparent; + content: " "; +} +.timeline > li > .timeline-panel:after { + position: absolute; + top: 27px; + right: -14px; + display: inline-block; + border-top: 14px solid transparent; + border-left: 14px solid #fff; + border-right: 0 solid #fff; + border-bottom: 14px solid transparent; + content: " "; +} +.timeline > li > .timeline-badge { + color: #fff; + width: 50px; + height: 50px; + line-height: 50px; + font-size: 1.4em; + text-align: center; + position: absolute; + top: 16px; + left: 50%; + margin-left: -25px; + background-color: #999999; + z-index: 100; + border-top-right-radius: 50%; + border-top-left-radius: 50%; + border-bottom-right-radius: 50%; + border-bottom-left-radius: 50%; +} +.timeline > li.timeline-inverted > .timeline-panel { + float: right; +} +.timeline > li.timeline-inverted > .timeline-panel:before { + border-left-width: 0; + border-right-width: 15px; + left: -15px; + right: auto; +} +.timeline > li.timeline-inverted > .timeline-panel:after { + border-left-width: 0; + border-right-width: 14px; + left: -14px; + right: auto; +} +.timeline-badge.primary { + background-color: #2e6da4 !important; +} +.timeline-badge.success { + background-color: #3f903f !important; +} +.timeline-badge.warning { + background-color: #f0ad4e !important; +} +.timeline-badge.danger { + background-color: #d9534f !important; +} +.timeline-badge.info { + background-color: #5bc0de !important; +} +.timeline-title { + margin-top: 0; + color: inherit; +} +.timeline-body > p, +.timeline-body > ul { + margin-bottom: 0; +} +.timeline-body > p + p { + margin-top: 5px; +} \ No newline at end of file diff --git a/src/main/resources/static/vendor/angular-material/angular-animate.min.js b/src/main/resources/static/vendor/angular-material/angular-animate.min.js new file mode 100644 index 0000000..da9c862 --- /dev/null +++ b/src/main/resources/static/vendor/angular-material/angular-animate.min.js @@ -0,0 +1,56 @@ +/* + AngularJS v1.5.3 + (c) 2010-2016 Google, Inc. http://angularjs.org + License: MIT + */ +(function(E,w,Va){'use strict';function ya(a,b,c){if(!a)throw Ka("areq",b||"?",c||"required");return a}function za(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;ba(a)&&(a=a.join(" "));ba(b)&&(b=b.join(" "));return a+" "+b}function La(a){var b={};a&&(a.to||a.from)&&(b.to=a.to,b.from=a.from);return b}function V(a,b,c){var d="";a=ba(a)?a:a&&I(a)&&a.length?a.split(/\s+/):[];q(a,function(a,f){a&&0=a&&(a=h,h=0,b.push(e),e=[]);e.push(f.fn);f.children.forEach(function(a){h++;c.push(a)});a--}e.length&&b.push(e);return b}(c)}var l=[],w=Q(a);return function(s,x,v){function h(a){a=a.hasAttribute("ng-animate-ref")?[a]:a.querySelectorAll("[ng-animate-ref]");var b=[];q(a,function(a){var c=a.getAttribute("ng-animate-ref");c&&c.length&&b.push(a)});return b}function P(a){var b=[],c={};q(a,function(a,g){var d=A(a.element),k=0<=["enter","move"].indexOf(a.event),d=a.structural? + h(d):[];if(d.length){var e=k?"to":"from";q(d,function(a){var b=a.getAttribute("ng-animate-ref");c[b]=c[b]||{};c[b][e]={animationID:g,element:H(a)}})}else b.push(a)});var d={},k={};q(c,function(c,h){var e=c.from,f=c.to;if(e&&f){var m=a[e.animationID],C=a[f.animationID],n=e.animationID.toString();if(!k[n]){var D=k[n]={structural:!0,beforeStart:function(){m.beforeStart();C.beforeStart()},close:function(){m.close();C.close()},classes:y(m.classes,C.classes),from:m,to:C,anchors:[]};D.classes.length?b.push(D): + (b.push(m),b.push(C))}k[n].anchors.push({out:e.element,"in":f.element})}else e=e?e.animationID:f.animationID,f=e.toString(),d[f]||(d[f]=!0,b.push(a[e]))});return b}function y(a,b){a=a.split(" ");b=b.split(" ");for(var c=[],d=0;d=Q&&b>=L&&(H=!0,n())}function K(){function b(){if(!P){D(!1);q(y,function(a){m.style[a[0]]=a[1]});h(a,g);e.addClass(a,ga);if(p.recalculateTimingStyles){na=m.className+" "+fa;ia=w(m,na);B=v(m,na,ia);$=B.maxDelay;I=Math.max($,0);L=B.maxDuration;if(0===L){n();return}p.hasTransitions=0z.expectedEndTime)?r.cancel(z.timer):f.push(n)}K&&(l=r(c,l,!1),f[0]={timer:l,expectedEndTime:d},f.push(n),a.data("$$animateCss",f)); + if(ea.length)a.on(ea.join(" "),C);g.to&&(g.cleanupStyles&&Ga(G,m,Object.keys(g.to)),Ba(a,g))}}function c(){var b=a.data("$$animateCss");if(b){for(var d=1;d .md-scroll-mask-bar { + display: block; + position: absolute; + background-color: #fafafa; + right: 0; + top: 0; + bottom: 0; + z-index: 65; + box-shadow: inset 0 0 1px rgba(0, 0, 0, .3) +} + +@media (min-width: 960px) { + .md-padding { + padding: 16px + } +} + +body[dir=ltr], body[dir=rtl], html[dir=ltr], html[dir=rtl] { + unicode-bidi: embed +} + +bdo[dir=rtl] { + direction: rtl; + unicode-bidi: bidi-override +} + +bdo[dir=ltr] { + direction: ltr; + unicode-bidi: bidi-override +} + +body, html { + -webkit-tap-highlight-color: transparent; + -webkit-touch-callout: none; + min-height: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale +} + +.md-display-4 { + font-size: 112px; + font-weight: 300; + letter-spacing: -.010em; + line-height: 112px +} + +.md-display-3 { + font-size: 56px; + font-weight: 400; + letter-spacing: -.005em; + line-height: 56px +} + +.md-display-2 { + font-size: 45px; + font-weight: 400; + line-height: 64px +} + +.md-display-1 { + font-size: 34px; + font-weight: 400; + line-height: 40px +} + +.md-headline { + font-size: 24px; + font-weight: 400; + line-height: 32px +} + +.md-title { + font-size: 20px; + font-weight: 500; + letter-spacing: .005em +} + +.md-subhead { + font-size: 16px; + font-weight: 400; + letter-spacing: .010em; + line-height: 24px +} + +.md-body-1 { + font-size: 14px; + font-weight: 400; + letter-spacing: .010em; + line-height: 20px +} + +.md-body-2 { + font-size: 14px; + font-weight: 500; + letter-spacing: .010em; + line-height: 24px +} + +.md-caption { + font-size: 12px; + letter-spacing: .020em +} + +.md-button { + letter-spacing: .010em +} + +button, html, input, select, textarea { + font-family: Roboto, "Helvetica Neue", sans-serif +} + +button, input, select, textarea { + font-size: 100% +} + +@-webkit-keyframes md-autocomplete-list-out { + 0% { + -webkit-animation-timing-function: linear; + animation-timing-function: linear + } + 50% { + opacity: 0; + height: 40px; + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in + } + 100% { + height: 0; + opacity: 0 + } +} + +@keyframes md-autocomplete-list-out { + 0% { + -webkit-animation-timing-function: linear; + animation-timing-function: linear + } + 50% { + opacity: 0; + height: 40px; + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in + } + 100% { + height: 0; + opacity: 0 + } +} + +@-webkit-keyframes md-autocomplete-list-in { + 0% { + opacity: 0; + height: 0; + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out + } + 50% { + opacity: 0; + height: 40px + } + 100% { + opacity: 1; + height: 40px + } +} + +@keyframes md-autocomplete-list-in { + 0% { + opacity: 0; + height: 0; + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out + } + 50% { + opacity: 0; + height: 40px + } + 100% { + opacity: 1; + height: 40px + } +} + +md-autocomplete { + border-radius: 2px; + display: block; + height: 40px; + position: relative; + overflow: visible; + min-width: 190px +} + +md-autocomplete[disabled] input { + cursor: default +} + +md-autocomplete[md-floating-label] { + border-radius: 0; + background: 0 0; + height: auto +} + +md-autocomplete[md-floating-label] md-input-container { + padding-bottom: 0 +} + +md-autocomplete[md-floating-label] md-autocomplete-wrap { + height: auto +} + +md-autocomplete[md-floating-label] button { + position: absolute; + top: auto; + bottom: 0; + right: 0; + width: 30px; + height: 30px +} + +md-autocomplete md-autocomplete-wrap { + display: block; + position: relative; + overflow: visible; + height: 40px +} + +md-autocomplete md-autocomplete-wrap.md-menu-showing { + z-index: 51 +} + +md-autocomplete md-autocomplete-wrap md-progress-linear { + position: absolute; + bottom: -2px; + left: 0 +} + +md-autocomplete md-autocomplete-wrap md-progress-linear.md-inline { + bottom: 40px; + right: 2px; + left: 2px; + width: auto +} + +md-autocomplete md-autocomplete-wrap md-progress-linear ._md-mode-indeterminate { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 3px; + transition: none +} + +md-autocomplete md-autocomplete-wrap md-progress-linear ._md-mode-indeterminate .md-container { + transition: none; + height: 3px +} + +md-autocomplete md-autocomplete-wrap md-progress-linear ._md-mode-indeterminate.ng-enter { + transition: opacity .15s linear +} + +md-autocomplete md-autocomplete-wrap md-progress-linear ._md-mode-indeterminate.ng-enter.ng-enter-active { + opacity: 1 +} + +md-autocomplete md-autocomplete-wrap md-progress-linear ._md-mode-indeterminate.ng-leave { + transition: opacity .15s linear +} + +md-autocomplete md-autocomplete-wrap md-progress-linear ._md-mode-indeterminate.ng-leave.ng-leave-active { + opacity: 0 +} + +md-autocomplete input:not(.md-input) { + font-size: 14px; + box-sizing: border-box; + border: none; + box-shadow: none; + outline: 0; + background: 0 0; + width: 100%; + padding: 0 15px; + line-height: 40px; + height: 40px +} + +md-autocomplete input:not(.md-input)::-ms-clear { + display: none +} + +md-autocomplete button { + position: relative; + line-height: 20px; + text-align: center; + width: 30px; + height: 30px; + cursor: pointer; + border: none; + border-radius: 50%; + padding: 0; + font-size: 12px; + background: 0 0; + margin: auto 5px +} + +md-autocomplete button:after { + content: ''; + position: absolute; + top: -6px; + right: -6px; + bottom: -6px; + left: -6px; + border-radius: 50%; + -webkit-transform: scale(0); + transform: scale(0); + opacity: 0; + transition: all .4s cubic-bezier(.25, .8, .25, 1) +} + +md-autocomplete button:focus { + outline: 0 +} + +md-autocomplete button:focus:after { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1 +} + +md-autocomplete button md-icon { + position: absolute; + top: 50%; + left: 50%; + -webkit-transform: translate3d(-50%, -50%, 0) scale(.9); + transform: translate3d(-50%, -50%, 0) scale(.9) +} + +md-autocomplete button md-icon path { + stroke-width: 0 +} + +md-autocomplete button.ng-enter { + -webkit-transform: scale(0); + transform: scale(0); + transition: -webkit-transform .15s ease-out; + transition: transform .15s ease-out +} + +md-autocomplete button.ng-enter.ng-enter-active { + -webkit-transform: scale(1); + transform: scale(1) +} + +md-autocomplete button.ng-leave { + transition: -webkit-transform .15s ease-out; + transition: transform .15s ease-out +} + +md-autocomplete button.ng-leave.ng-leave-active { + -webkit-transform: scale(0); + transform: scale(0) +} + +@media screen and (-ms-high-contrast: active) { + md-autocomplete input { + border: 1px solid #fff + } + + md-autocomplete li:focus { + color: #fff + } +} + +.md-virtual-repeat-container.md-autocomplete-suggestions-container { + position: absolute; + box-shadow: 0 2px 5px rgba(0, 0, 0, .25); + height: 225.5px; + max-height: 225.5px; + z-index: 100 +} + +.md-virtual-repeat-container.md-not-found { + height: 48px +} + +.md-autocomplete-suggestions { + margin: 0; + list-style: none; + padding: 0 +} + +.md-autocomplete-suggestions li { + font-size: 14px; + overflow: hidden; + padding: 0 15px; + line-height: 48px; + height: 48px; + transition: background .15s linear; + margin: 0; + white-space: nowrap; + text-overflow: ellipsis +} + +.md-autocomplete-suggestions li:focus { + outline: 0 +} + +.md-autocomplete-suggestions li:not(.md-not-found-wrapper) { + cursor: pointer +} + +@media screen and (-ms-high-contrast: active) { + .md-autocomplete-suggestions, md-autocomplete { + border: 1px solid #fff + } +} + +md-bottom-sheet { + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding: 8px 16px 88px; + z-index: 70; + border-top-width: 1px; + border-top-style: solid; + -webkit-transform: translate3d(0, 80px, 0); + transform: translate3d(0, 80px, 0); + transition: all .4s cubic-bezier(.25, .8, .25, 1); + transition-property: -webkit-transform; + transition-property: transform +} + +md-bottom-sheet.md-has-header { + padding-top: 0 +} + +md-bottom-sheet.ng-enter { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0) +} + +md-bottom-sheet.ng-enter-active { + opacity: 1; + display: block; + -webkit-transform: translate3d(0, 80px, 0) !important; + transform: translate3d(0, 80px, 0) !important +} + +md-bottom-sheet.ng-leave-active { + -webkit-transform: translate3d(0, 100%, 0) !important; + transform: translate3d(0, 100%, 0) !important; + transition: all .3s cubic-bezier(.55, 0, .55, .2) +} + +md-bottom-sheet .md-subheader { + background-color: transparent; + font-family: Roboto, "Helvetica Neue", sans-serif; + line-height: 56px; + padding: 0; + white-space: nowrap +} + +md-bottom-sheet md-inline-icon { + display: inline-block; + height: 24px; + width: 24px; + fill: #444 +} + +md-bottom-sheet md-list-item { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + outline: 0 +} + +md-bottom-sheet md-list-item:hover { + cursor: pointer +} + +md-bottom-sheet.md-list md-list-item { + padding: 0; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + height: 48px +} + +md-bottom-sheet.md-grid { + padding-left: 24px; + padding-right: 24px; + padding-top: 0 +} + +md-bottom-sheet.md-grid md-list { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + transition: all .5s; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center +} + +md-bottom-sheet.md-grid md-list-item { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + transition: all .5s; + height: 96px; + margin-top: 8px; + margin-bottom: 8px +} + +@media (max-width: 960px) { + md-bottom-sheet.md-grid md-list-item { + -webkit-flex: 1 1 33.33333%; + -ms-flex: 1 1 33.33333%; + flex: 1 1 33.33333%; + max-width: 33.33333% + } + + md-bottom-sheet.md-grid md-list-item:nth-of-type(3n+1) { + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start + } + + md-bottom-sheet.md-grid md-list-item:nth-of-type(3n) { + -webkit-align-items: flex-end; + -ms-flex-align: end; + align-items: flex-end + } +} + +@media (min-width: 960px) and (max-width: 1279px) { + md-bottom-sheet.md-grid md-list-item { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 25% + } +} + +@media (min-width: 1280px) and (max-width: 1919px) { + md-bottom-sheet.md-grid md-list-item { + -webkit-flex: 1 1 16.66667%; + -ms-flex: 1 1 16.66667%; + flex: 1 1 16.66667%; + max-width: 16.66667% + } +} + +@media (min-width: 1920px) { + md-bottom-sheet.md-grid md-list-item { + -webkit-flex: 1 1 14.28571%; + -ms-flex: 1 1 14.28571%; + flex: 1 1 14.28571%; + max-width: 14.28571% + } +} + +md-bottom-sheet.md-grid md-list-item .md-list-item-content { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + width: 48px; + padding-bottom: 16px +} + +md-bottom-sheet.md-grid md-list-item .md-grid-item-content { + border: 1px solid transparent; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + width: 80px +} + +md-bottom-sheet.md-grid md-list-item .md-grid-text { + font-weight: 400; + line-height: 16px; + font-size: 13px; + margin: 0; + white-space: nowrap; + width: 64px; + text-align: center; + text-transform: none; + padding-top: 8px +} + +@media screen and (-ms-high-contrast: active) { + md-bottom-sheet { + border: 1px solid #fff + } +} + +md-backdrop { + transition: opacity 450ms; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + z-index: 50 +} + +md-backdrop._md-menu-backdrop { + position: fixed !important; + z-index: 99 +} + +md-backdrop._md-select-backdrop { + z-index: 81; + transition-duration: 0 +} + +md-backdrop._md-dialog-backdrop { + z-index: 79 +} + +md-backdrop._md-bottom-sheet-backdrop { + z-index: 69 +} + +md-backdrop._md-sidenav-backdrop { + z-index: 59 +} + +md-backdrop._md-click-catcher { + position: absolute +} + +md-backdrop.md-opaque { + opacity: .48 +} + +md-backdrop.md-opaque.ng-enter { + opacity: 0 +} + +md-backdrop.md-opaque.ng-enter.md-opaque.ng-enter-active { + opacity: .48 +} + +md-backdrop.md-opaque.ng-leave { + opacity: .48; + transition: opacity 400ms +} + +md-backdrop.md-opaque.ng-leave.md-opaque.ng-leave-active { + opacity: 0 +} + +button.md-button::-moz-focus-inner { + border: 0 +} + +.md-button { + border-radius: 3px; + box-sizing: border-box; + color: currentColor; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + position: relative; + outline: 0; + border: 0; + display: inline-block; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + padding: 0 6px; + margin: 6px 8px; + line-height: 36px; + min-height: 36px; + background: 0 0; + white-space: nowrap; + min-width: 88px; + text-align: center; + text-transform: uppercase; + font-weight: 500; + font-size: 14px; + font-style: inherit; + font-variant: inherit; + font-family: inherit; + text-decoration: none; + cursor: pointer; + overflow: hidden; + transition: box-shadow .4s cubic-bezier(.25, .8, .25, 1), background-color .4s cubic-bezier(.25, .8, .25, 1) +} + +.md-button:focus { + outline: 0 +} + +.md-button:focus, .md-button:hover { + text-decoration: none +} + +.md-button.ng-hide, .md-button.ng-leave { + transition: none +} + +.md-button.md-cornered { + border-radius: 0 +} + +.md-button.md-icon { + padding: 0; + background: 0 0 +} + +.md-button.md-raised:not([disabled]) { + box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .26) +} + +.md-button.md-icon-button { + margin: 0 6px; + height: 40px; + min-width: 0; + line-height: 24px; + padding: 8px; + width: 40px; + border-radius: 50% +} + +.md-button.md-icon-button .md-ripple-container { + border-radius: 50%; + background-clip: padding-box; + overflow: hidden; + -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC) +} + +.md-button.md-fab { + z-index: 20; + line-height: 56px; + min-width: 0; + width: 56px; + height: 56px; + vertical-align: middle; + box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .26); + border-radius: 50%; + background-clip: padding-box; + overflow: hidden; + transition: all .3s cubic-bezier(.55, 0, .55, .2); + transition-property: background-color, box-shadow, -webkit-transform; + transition-property: background-color, box-shadow, transform +} + +.md-button.md-fab.md-fab-bottom-right { + top: auto; + right: 20px; + bottom: 20px; + left: auto; + position: absolute +} + +.md-button.md-fab.md-fab-bottom-left { + top: auto; + right: auto; + bottom: 20px; + left: 20px; + position: absolute +} + +.md-button.md-fab.md-fab-top-right { + top: 20px; + right: 20px; + bottom: auto; + left: auto; + position: absolute +} + +.md-button.md-fab.md-fab-top-left { + top: 20px; + right: auto; + bottom: auto; + left: 20px; + position: absolute +} + +.md-button.md-fab .md-ripple-container { + border-radius: 50%; + background-clip: padding-box; + overflow: hidden; + -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC) +} + +.md-button.md-fab.md-mini { + line-height: 40px; + width: 40px; + height: 40px +} + +.md-button.md-fab.ng-hide, .md-button.md-fab.ng-leave { + transition: none +} + +.md-button:not([disabled]).md-fab.md-focused, .md-button:not([disabled]).md-raised.md-focused { + box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .26) +} + +.md-button:not([disabled]).md-fab:active, .md-button:not([disabled]).md-raised:active { + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, .4) +} + +.md-button .md-ripple-container { + border-radius: 3px; + background-clip: padding-box; + overflow: hidden; + -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC) +} + +._md-toast-open-top .md-button.md-fab-top-left, ._md-toast-open-top .md-button.md-fab-top-right { + transition: all .4s cubic-bezier(.25, .8, .25, 1); + -webkit-transform: translate3d(0, 42px, 0); + transform: translate3d(0, 42px, 0) +} + +._md-toast-open-top .md-button.md-fab-top-left:not([disabled]).md-focused, ._md-toast-open-top .md-button.md-fab-top-left:not([disabled]):hover, ._md-toast-open-top .md-button.md-fab-top-right:not([disabled]).md-focused, ._md-toast-open-top .md-button.md-fab-top-right:not([disabled]):hover { + -webkit-transform: translate3d(0, 41px, 0); + transform: translate3d(0, 41px, 0) +} + +._md-toast-open-bottom .md-button.md-fab-bottom-left, ._md-toast-open-bottom .md-button.md-fab-bottom-right { + transition: all .4s cubic-bezier(.25, .8, .25, 1); + -webkit-transform: translate3d(0, -42px, 0); + transform: translate3d(0, -42px, 0) +} + +._md-toast-open-bottom .md-button.md-fab-bottom-left:not([disabled]).md-focused, ._md-toast-open-bottom .md-button.md-fab-bottom-left:not([disabled]):hover, ._md-toast-open-bottom .md-button.md-fab-bottom-right:not([disabled]).md-focused, ._md-toast-open-bottom .md-button.md-fab-bottom-right:not([disabled]):hover { + -webkit-transform: translate3d(0, -43px, 0); + transform: translate3d(0, -43px, 0) +} + +.md-button-group { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + width: 100% +} + +.md-button-group > .md-button { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + display: block; + overflow: hidden; + width: 0; + border-width: 1px 0 1px 1px; + border-radius: 0; + text-align: center; + text-overflow: ellipsis; + white-space: nowrap +} + +.md-button-group > .md-button:first-child { + border-radius: 2px 0 0 2px +} + +.md-button-group > .md-button:last-child { + border-right-width: 1px; + border-radius: 0 2px 2px 0 +} + +@media screen and (-ms-high-contrast: active) { + .md-button.md-fab, .md-button.md-raised { + border: 1px solid #fff + } +} + +md-card { + box-sizing: border-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + margin: 8px; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .2), 0 1px 1px 0 rgba(0, 0, 0, .14), 0 2px 1px -1px rgba(0, 0, 0, .12) +} + +md-card md-card-header { + padding: 16px; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row +} + +md-card md-card-header:first-child md-card-avatar { + margin-right: 12px +} + +[dir=rtl] md-card md-card-header:first-child md-card-avatar { + margin-right: auto; + margin-left: 12px +} + +md-card md-card-header:last-child md-card-avatar { + margin-left: 12px +} + +[dir=rtl] md-card md-card-header:last-child md-card-avatar { + margin-left: auto; + margin-right: 12px +} + +md-card md-card-header md-card-avatar { + width: 40px; + height: 40px +} + +md-card md-card-header md-card-avatar .md-user-avatar, md-card md-card-header md-card-avatar md-icon { + border-radius: 50% +} + +md-card md-card-header md-card-avatar md-icon { + padding: 8px +} + +md-card md-card-header md-card-avatar + md-card-header-text { + max-height: 40px +} + +md-card md-card-header md-card-avatar + md-card-header-text .md-title { + font-size: 14px +} + +md-card md-card-header md-card-header-text { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column +} + +md-card md-card-header md-card-header-text .md-subhead { + font-size: 14px +} + +md-card > :not(md-card-content) img, md-card > img { + box-sizing: border-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: 100%; + height: 100% !important +} + +md-card md-card-title { + padding: 24px 16px 16px; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row +} + +md-card md-card-title + md-card-content { + padding-top: 0 +} + +md-card md-card-title md-card-title-text { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + display: -webkit-flex; + display: -ms-flexbox; + display: flex +} + +md-card md-card-title md-card-title-text .md-subhead { + padding-top: 0; + font-size: 14px +} + +md-card md-card-title md-card-title-text:only-child .md-subhead { + padding-top: 12px +} + +md-card md-card-title md-card-title-media { + margin-top: -8px +} + +md-card md-card-title md-card-title-media .md-media-sm { + height: 80px; + width: 80px +} + +md-card md-card-title md-card-title-media .md-media-md { + height: 112px; + width: 112px +} + +md-card md-card-title md-card-title-media .md-media-lg { + height: 152px; + width: 152px +} + +md-card md-card-content { + display: block; + padding: 16px +} + +md-card md-card-content > p:first-child { + margin-top: 0 +} + +md-card md-card-content > p:last-child { + margin-bottom: 0 +} + +md-card md-card-content .md-media-xl { + height: 240px; + width: 240px +} + +md-card .md-actions, md-card md-card-actions { + margin: 8px +} + +md-card .md-actions.layout-column .md-button:not(.md-icon-button), md-card md-card-actions.layout-column .md-button:not(.md-icon-button) { + margin: 2px 0 +} + +md-card .md-actions.layout-column .md-button:not(.md-icon-button):first-of-type, md-card md-card-actions.layout-column .md-button:not(.md-icon-button):first-of-type { + margin-top: 0 +} + +md-card .md-actions.layout-column .md-button:not(.md-icon-button):last-of-type, md-card md-card-actions.layout-column .md-button:not(.md-icon-button):last-of-type { + margin-bottom: 0 +} + +md-card .md-actions.layout-column .md-button.md-icon-button, md-card md-card-actions.layout-column .md-button.md-icon-button { + margin-top: 6px; + margin-bottom: 6px +} + +md-card .md-actions md-card-icon-actions, md-card md-card-actions md-card-icon-actions { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row +} + +md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button), md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button) { + margin: 0 4px +} + +md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button):first-of-type, md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button):first-of-type { + margin-left: 0 +} + +[dir=rtl] md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button):first-of-type, [dir=rtl] md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button):first-of-type { + margin-left: auto; + margin-right: 0 +} + +md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button):last-of-type, md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button):last-of-type { + margin-right: 0 +} + +[dir=rtl] md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button):last-of-type, [dir=rtl] md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button):last-of-type { + margin-right: auto; + margin-left: 0 +} + +md-card .md-actions:not(.layout-column) .md-button.md-icon-button, md-card md-card-actions:not(.layout-column) .md-button.md-icon-button { + margin-left: 6px; + margin-right: 6px +} + +md-card .md-actions:not(.layout-column) .md-button.md-icon-button:first-of-type, md-card md-card-actions:not(.layout-column) .md-button.md-icon-button:first-of-type { + margin-left: 12px +} + +[dir=rtl] md-card .md-actions:not(.layout-column) .md-button.md-icon-button:first-of-type, [dir=rtl] md-card md-card-actions:not(.layout-column) .md-button.md-icon-button:first-of-type { + margin-left: auto; + margin-right: 12px +} + +md-card .md-actions:not(.layout-column) .md-button.md-icon-button:last-of-type, md-card md-card-actions:not(.layout-column) .md-button.md-icon-button:last-of-type { + margin-right: 12px +} + +[dir=rtl] md-card .md-actions:not(.layout-column) .md-button.md-icon-button:last-of-type, [dir=rtl] md-card md-card-actions:not(.layout-column) .md-button.md-icon-button:last-of-type { + margin-right: auto; + margin-left: 12px +} + +md-card .md-actions:not(.layout-column) .md-button + md-card-icon-actions, md-card md-card-actions:not(.layout-column) .md-button + md-card-icon-actions { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row +} + +md-card md-card-footer { + margin-top: auto; + padding: 16px +} + +@media screen and (-ms-high-contrast: active) { + md-card { + border: 1px solid #fff + } +} + +.md-inline-form md-checkbox { + margin: 19px 0 18px +} + +md-checkbox { + box-sizing: border-box; + display: inline-block; + margin-bottom: 16px; + white-space: nowrap; + cursor: pointer; + outline: 0; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + position: relative; + min-width: 20px; + min-height: 20px; + margin-left: 0; + margin-right: 16px +} + +[dir=rtl] md-checkbox { + margin-left: 16px; + margin-right: 0 +} + +md-checkbox:last-of-type { + margin-left: 0; + margin-right: 0 +} + +md-checkbox.md-focused:not([disabled]) ._md-container:before { + left: -8px; + top: -8px; + right: -8px; + bottom: -8px +} + +md-checkbox.md-focused:not([disabled]):not(.md-checked) ._md-container:before { + background-color: rgba(0, 0, 0, .12) +} + +md-checkbox.md-align-top-left > div._md-container { + top: 12px +} + +md-checkbox ._md-container { + position: absolute; + top: 50%; + -webkit-transform: translateY(-50%); + transform: translateY(-50%); + box-sizing: border-box; + display: inline-block; + width: 20px; + height: 20px; + left: 0; + right: auto +} + +[dir=rtl] md-checkbox ._md-container { + left: auto; + right: 0 +} + +md-checkbox ._md-container:before { + box-sizing: border-box; + background-color: transparent; + border-radius: 50%; + content: ''; + position: absolute; + display: block; + height: auto; + left: 0; + top: 0; + right: 0; + bottom: 0; + transition: all .5s; + width: auto +} + +md-checkbox ._md-container:after { + box-sizing: border-box; + content: ''; + position: absolute; + top: -10px; + right: -10px; + bottom: -10px; + left: -10px +} + +md-checkbox ._md-container .md-ripple-container { + position: absolute; + display: block; + width: auto; + height: auto; + left: -15px; + top: -15px; + right: -15px; + bottom: -15px +} + +md-checkbox ._md-icon { + box-sizing: border-box; + transition: 240ms; + position: absolute; + top: 0; + left: 0; + width: 20px; + height: 20px; + border-width: 2px; + border-style: solid; + border-radius: 2px +} + +md-checkbox.md-checked ._md-icon { + border: none +} + +md-checkbox.md-checked ._md-icon:after { + box-sizing: border-box; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); + position: absolute; + left: 6.67px; + top: 2.22px; + display: table; + width: 6.67px; + height: 13.33px; + border-width: 2px; + border-style: solid; + border-top: 0; + border-left: 0; + content: '' +} + +md-checkbox[disabled] { + cursor: default +} + +md-checkbox.md-indeterminate ._md-icon:after { + box-sizing: border-box; + position: absolute; + top: 50%; + left: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + display: table; + width: 12px; + height: 2px; + border-width: 2px; + border-style: solid; + border-top: 0; + border-left: 0; + content: '' +} + +md-checkbox ._md-label { + box-sizing: border-box; + position: relative; + display: inline-block; + vertical-align: middle; + white-space: normal; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; + margin-left: 30px; + margin-right: 0 +} + +[dir=rtl] md-checkbox ._md-label { + margin-left: 0; + margin-right: 30px +} + +.md-contact-chips .md-chips md-chip { + padding: 0 25px 0 0 +} + +[dir=rtl] .md-contact-chips .md-chips md-chip { + padding: 0 0 0 25px +} + +.md-contact-chips .md-chips md-chip .md-contact-avatar { + float: left +} + +[dir=rtl] .md-contact-chips .md-chips md-chip .md-contact-avatar { + float: right +} + +.md-contact-chips .md-chips md-chip .md-contact-avatar img { + height: 32px; + border-radius: 16px +} + +.md-contact-chips .md-chips md-chip .md-contact-name { + display: inline-block; + height: 32px; + margin-left: 8px +} + +[dir=rtl] .md-contact-chips .md-chips md-chip .md-contact-name { + margin-left: auto; + margin-right: 8px +} + +.md-contact-suggestion { + height: 56px +} + +.md-contact-suggestion img { + height: 40px; + border-radius: 20px; + margin-top: 8px +} + +.md-contact-suggestion .md-contact-name { + margin-left: 8px; + width: 120px +} + +[dir=rtl] .md-contact-suggestion .md-contact-name { + margin-left: auto; + margin-right: 8px +} + +.md-contact-suggestion .md-contact-email, .md-contact-suggestion .md-contact-name { + display: inline-block; + overflow: hidden; + text-overflow: ellipsis +} + +.md-contact-chips-suggestions li { + height: 100% +} + +.md-chips { + display: block; + font-family: Roboto, "Helvetica Neue", sans-serif; + font-size: 16px; + padding: 0 0 8px 3px; + vertical-align: middle +} + +.md-chips:after { + content: ''; + display: table; + clear: both +} + +[dir=rtl] .md-chips { + padding: 0 3px 8px 0 +} + +.md-chips.md-readonly ._md-chip-input-container { + min-height: 32px +} + +.md-chips:not(.md-readonly) { + cursor: text +} + +.md-chips:not(.md-readonly) md-chip:not(.md-readonly) { + padding-right: 22px +} + +[dir=rtl] .md-chips:not(.md-readonly) md-chip:not(.md-readonly) { + padding-right: auto; + padding-left: 22px +} + +.md-chips:not(.md-readonly) md-chip:not(.md-readonly) ._md-chip-content { + padding-right: 4px +} + +[dir=rtl] .md-chips:not(.md-readonly) md-chip:not(.md-readonly) ._md-chip-content { + padding-right: auto; + padding-left: 4px +} + +.md-chips md-chip { + cursor: default; + border-radius: 16px; + display: block; + height: 32px; + line-height: 32px; + margin: 8px 8px 0 0; + padding: 0 12px; + float: left; + box-sizing: border-box; + max-width: 100%; + position: relative +} + +[dir=rtl] .md-chips md-chip { + margin: 8px 0 0 8px; + float: right +} + +.md-chips md-chip ._md-chip-content { + display: block; + float: left; + white-space: nowrap; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis +} + +[dir=rtl] .md-chips md-chip ._md-chip-content { + float: right +} + +.md-chips md-chip ._md-chip-content:focus { + outline: 0 +} + +.md-chips md-chip._md-chip-content-edit-is-enabled { + -webkit-user-select: none; + -moz-user-select: none; + -khtml-user-select: none; + -ms-user-select: none +} + +.md-chips md-chip ._md-chip-remove-container { + position: absolute; + right: 0; + line-height: 22px +} + +[dir=rtl] .md-chips md-chip ._md-chip-remove-container { + right: auto; + left: 0 +} + +.md-chips md-chip ._md-chip-remove { + text-align: center; + width: 32px; + height: 32px; + min-width: 0; + padding: 0; + background: 0 0; + border: none; + box-shadow: none; + margin: 0; + position: relative +} + +.md-chips md-chip ._md-chip-remove md-icon { + height: 18px; + width: 18px; + position: absolute; + top: 50%; + left: 50%; + -webkit-transform: translate3d(-50%, -50%, 0); + transform: translate3d(-50%, -50%, 0) +} + +.md-chips ._md-chip-input-container { + display: block; + line-height: 32px; + margin: 8px 8px 0 0; + padding: 0; + float: left +} + +[dir=rtl] .md-chips ._md-chip-input-container { + margin: 8px 0 0 8px; + float: right +} + +.md-chips ._md-chip-input-container input:not([type]), .md-chips ._md-chip-input-container input[type=url], .md-chips ._md-chip-input-container input[type=text], .md-chips ._md-chip-input-container input[type=email], .md-chips ._md-chip-input-container input[type=number], .md-chips ._md-chip-input-container input[type=tel] { + border: 0; + height: 32px; + line-height: 32px; + padding: 0 +} + +.md-chips ._md-chip-input-container input:not([type]):focus, .md-chips ._md-chip-input-container input[type=url]:focus, .md-chips ._md-chip-input-container input[type=text]:focus, .md-chips ._md-chip-input-container input[type=email]:focus, .md-chips ._md-chip-input-container input[type=number]:focus, .md-chips ._md-chip-input-container input[type=tel]:focus { + outline: 0 +} + +.md-chips ._md-chip-input-container md-autocomplete, .md-chips ._md-chip-input-container md-autocomplete-wrap { + background: 0 0 +} + +.md-chips ._md-chip-input-container md-autocomplete md-autocomplete-wrap { + box-shadow: none +} + +.md-chips ._md-chip-input-container input { + border: 0; + height: 32px; + line-height: 32px; + padding: 0 +} + +.md-chips ._md-chip-input-container input:focus { + outline: 0 +} + +.md-chips ._md-chip-input-container md-autocomplete, .md-chips ._md-chip-input-container md-autocomplete-wrap { + height: 32px +} + +.md-chips ._md-chip-input-container md-autocomplete { + box-shadow: none +} + +.md-chips ._md-chip-input-container md-autocomplete input { + position: relative +} + +.md-chips ._md-chip-input-container:not(:first-child) { + margin: 8px 8px 0 0 +} + +[dir=rtl] .md-chips ._md-chip-input-container:not(:first-child) { + margin: 8px 0 0 8px +} + +.md-chips ._md-chip-input-container input { + background: 0 0; + border-width: 0 +} + +.md-chips md-autocomplete button { + display: none +} + +@media screen and (-ms-high-contrast: active) { + ._md-chip-input-container, md-chip { + border: 1px solid #fff + } + + ._md-chip-input-container md-autocomplete { + border: none + } +} + +md-content { + display: block; + position: relative; + overflow: auto; + -webkit-overflow-scrolling: touch +} + +md-content[md-scroll-y] { + overflow-y: auto; + overflow-x: hidden +} + +md-content[md-scroll-x] { + overflow-x: auto; + overflow-y: hidden +} + +md-content.md-no-momentum { + -webkit-overflow-scrolling: auto +} + +@media print { + md-content { + overflow: visible !important + } +} + +md-calendar { + font-size: 13px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none +} + +.md-calendar-scroll-mask { + display: inline-block; + overflow: hidden; + height: 308px +} + +.md-calendar-scroll-mask .md-virtual-repeat-scroller { + overflow-y: scroll; + -webkit-overflow-scrolling: touch +} + +.md-calendar-scroll-mask .md-virtual-repeat-scroller::-webkit-scrollbar { + display: none +} + +.md-calendar-scroll-mask .md-virtual-repeat-offsetter { + width: 100% +} + +.md-calendar-scroll-container { + box-shadow: inset -3px 3px 6px rgba(0, 0, 0, .2); + display: inline-block; + height: 308px; + width: 346px +} + +.md-calendar-date { + height: 44px; + width: 44px; + text-align: center; + padding: 0; + border: none +} + +.md-calendar-date:first-child { + padding-left: 16px +} + +[dir=rtl] .md-calendar-date:first-child { + padding-left: auto; + padding-right: 16px +} + +.md-calendar-date:last-child { + padding-right: 16px +} + +[dir=rtl] .md-calendar-date:last-child { + padding-right: auto; + padding-left: 16px +} + +.md-calendar-date.md-calendar-date-disabled { + cursor: default +} + +.md-calendar-date-selection-indicator { + transition: background-color, color .4s cubic-bezier(.25, .8, .25, 1); + border-radius: 50%; + display: inline-block; + width: 40px; + height: 40px; + line-height: 40px +} + +.md-calendar-date:not(.md-disabled) .md-calendar-date-selection-indicator { + cursor: pointer +} + +.md-calendar-month-label { + height: 44px; + font-size: 14px; + font-weight: 500; + padding: 0 0 0 24px +} + +[dir=rtl] .md-calendar-month-label { + padding: 0 24px 0 0 +} + +.md-calendar-day-header { + table-layout: fixed; + border-spacing: 0; + border-collapse: collapse +} + +.md-calendar-day-header th { + width: 44px; + text-align: center; + padding: 0; + border: none; + font-weight: 400; + height: 40px +} + +.md-calendar-day-header th:first-child { + padding-left: 16px +} + +[dir=rtl] .md-calendar-day-header th:first-child { + padding-left: auto; + padding-right: 16px +} + +.md-calendar-day-header th:last-child { + padding-right: 16px +} + +[dir=rtl] .md-calendar-day-header th:last-child { + padding-right: auto; + padding-left: 16px +} + +.md-calendar { + table-layout: fixed; + border-spacing: 0; + border-collapse: collapse +} + +.md-calendar tr:last-child td { + border-bottom-width: 1px; + border-bottom-style: solid +} + +.md-calendar:first-child { + border-top: 1px solid transparent +} + +.md-calendar tbody, .md-calendar td, .md-calendar tr { + vertical-align: middle +} + +md-datepicker { + white-space: nowrap; + overflow: hidden; + padding-right: 18px; + margin-right: -18px; + vertical-align: middle +} + +[dir=rtl] md-datepicker { + padding-right: auto; + padding-left: 18px; + margin-right: auto; + margin-left: -18px +} + +.md-inline-form md-datepicker { + margin-top: 12px +} + +.md-datepicker-button { + display: inline-block; + box-sizing: border-box; + background: 0 0 +} + +.md-datepicker-input { + font-size: 14px; + box-sizing: border-box; + border: none; + box-shadow: none; + outline: 0; + background: 0 0; + min-width: 120px; + max-width: 328px +} + +.md-datepicker-input::-ms-clear { + display: none +} + +.md-datepicker-input-container { + position: relative; + padding-bottom: 5px; + border-bottom-width: 1px; + border-bottom-style: solid; + display: inline-block; + width: auto; + margin-left: 12px +} + +[dir=rtl] .md-datepicker-input-container { + margin-left: auto; + margin-right: 12px +} + +.md-datepicker-input-container.md-datepicker-focused { + border-bottom-width: 2px +} + +.md-datepicker-calendar-pane { + position: absolute; + top: 0; + left: 0; + z-index: 100; + border-width: 1px; + border-style: solid; + background: 0 0; + -webkit-transform: scale(0); + transform: scale(0); + -webkit-transform-origin: 0 0; + transform-origin: 0 0; + transition: -webkit-transform .2s cubic-bezier(.25, .8, .25, 1); + transition: transform .2s cubic-bezier(.25, .8, .25, 1) +} + +.md-datepicker-calendar-pane.md-pane-open { + -webkit-transform: scale(1); + transform: scale(1) +} + +.md-datepicker-input-mask { + height: 40px; + width: 340px; + position: relative; + background: 0 0; + pointer-events: none; + cursor: text +} + +.md-datepicker-input-mask-opaque { + position: absolute; + right: 0; + left: 120px; + height: 100%; + margin-left: -1px +} + +.md-datepicker-calendar { + opacity: 0; + transition: opacity .2s cubic-bezier(.5, 0, .25, 1) +} + +.md-pane-open .md-datepicker-calendar { + opacity: 1 +} + +.md-datepicker-calendar md-calendar:focus { + outline: 0 +} + +.md-datepicker-expand-triangle { + position: absolute; + top: 50%; + left: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid +} + +.md-datepicker-triangle-button { + position: absolute; + right: 0; + top: 0; + -webkit-transform: translateY(-25%) translateX(45%); + transform: translateY(-25%) translateX(45%) +} + +[dir=rtl] .md-datepicker-triangle-button { + right: auto; + left: 0; + -webkit-transform: translateY(-25%) translateX(-45%); + transform: translateY(-25%) translateX(-45%) +} + +.md-datepicker-triangle-button.md-button.md-icon-button { + height: 100%; + width: 36px; + position: absolute +} + +md-datepicker[disabled] .md-datepicker-input-container { + border-bottom-color: transparent +} + +md-datepicker[disabled] .md-datepicker-triangle-button { + display: none +} + +.md-datepicker-open .md-datepicker-input-container { + margin-left: -12px; + margin-bottom: -5px; + border: none +} + +[dir=rtl] .md-datepicker-open .md-datepicker-input-container { + margin-left: auto; + margin-right: -12px +} + +.md-datepicker-open .md-datepicker-input { + margin-left: 24px; + height: 40px +} + +[dir=rtl] .md-datepicker-open .md-datepicker-input { + margin-left: auto; + margin-right: 24px +} + +.md-datepicker-open .md-datepicker-triangle-button, .md-datepicker-pos-adjusted .md-datepicker-input-mask { + display: none +} + +.md-datepicker-calendar-pane .md-calendar { + -webkit-transform: translateY(-85px); + transform: translateY(-85px); + transition: -webkit-transform .65s cubic-bezier(.25, .8, .25, 1); + transition: transform .65s cubic-bezier(.25, .8, .25, 1); + transition-delay: .125s +} + +.md-datepicker-calendar-pane.md-pane-open .md-calendar { + -webkit-transform: translateY(0); + transform: translateY(0) +} + +.md-dialog-is-showing { + max-height: 100% +} + +.md-dialog-container { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 80; + overflow: hidden +} + +md-dialog { + opacity: 0; + min-width: 240px; + max-width: 80%; + max-height: 80%; + position: relative; + overflow: auto; + box-shadow: 0 7px 8px -4px rgba(0, 0, 0, .2), 0 13px 19px 2px rgba(0, 0, 0, .14), 0 5px 24px 4px rgba(0, 0, 0, .12); + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column +} + +md-dialog._md-transition-in { + opacity: 1; + transition: all .4s cubic-bezier(.25, .8, .25, 1); + -webkit-transform: translate(0, 0) scale(1); + transform: translate(0, 0) scale(1) +} + +md-dialog._md-transition-out { + opacity: 0; + transition: all .4s cubic-bezier(.25, .8, .25, 1); + -webkit-transform: translate(0, 100%) scale(.2); + transform: translate(0, 100%) scale(.2) +} + +md-dialog > form { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + overflow: auto +} + +md-dialog .md-dialog-content { + padding: 24px +} + +md-dialog md-dialog-content { + -webkit-order: 1; + -ms-flex-order: 1; + order: 1; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + overflow: auto; + -webkit-overflow-scrolling: touch +} + +md-dialog md-dialog-content:not([layout=row]) > :first-child:not(.md-subheader) { + margin-top: 0 +} + +md-dialog md-dialog-content:focus { + outline: 0 +} + +md-dialog md-dialog-content .md-subheader { + margin: 0 +} + +md-dialog md-dialog-content ._md-dialog-content-body { + width: 100% +} + +md-dialog md-dialog-content .md-prompt-input-container { + width: 100%; + box-sizing: border-box +} + +md-dialog .md-actions, md-dialog md-dialog-actions { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-order: 2; + -ms-flex-order: 2; + order: 2; + box-sizing: border-box; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end; + margin-bottom: 0; + padding-right: 8px; + padding-left: 16px; + min-height: 52px; + overflow: hidden +} + +[dir=rtl] md-dialog .md-actions, [dir=rtl] md-dialog md-dialog-actions { + padding-right: 16px; + padding-left: 8px +} + +md-dialog .md-actions .md-button, md-dialog md-dialog-actions .md-button { + margin: 8px 0 8px 8px +} + +[dir=rtl] md-dialog .md-actions .md-button, [dir=rtl] md-dialog md-dialog-actions .md-button { + margin-left: 0; + margin-right: 8px +} + +md-dialog.md-content-overflow .md-actions, md-dialog.md-content-overflow md-dialog-actions { + border-top-width: 1px; + border-top-style: solid +} + +@media screen and (-ms-high-contrast: active) { + md-dialog { + border: 1px solid #fff + } +} + +@media (max-width: 959px) { + md-dialog.md-dialog-fullscreen { + min-height: 100%; + min-width: 100%; + border-radius: 0 + } +} + +md-divider { + display: block; + border-top-width: 1px; + border-top-style: solid; + margin: 0 +} + +md-divider[md-inset] { + margin-left: 80px +} + +[dir=rtl] md-divider[md-inset] { + margin-left: auto; + margin-right: 80px +} + +.layout-gt-lg-row > md-divider, .layout-gt-md-row > md-divider, .layout-gt-sm-row > md-divider, .layout-gt-xs-row > md-divider, .layout-lg-row > md-divider, .layout-md-row > md-divider, .layout-row > md-divider, .layout-sm-row > md-divider, .layout-xl-row > md-divider, .layout-xs-row > md-divider { + border-top-width: 0; + border-right-width: 1px; + border-right-style: solid +} + +md-fab-speed-dial { + position: relative; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + z-index: 20 +} + +md-fab-speed-dial.md-fab-bottom-right { + top: auto; + right: 20px; + bottom: 20px; + left: auto; + position: absolute +} + +md-fab-speed-dial.md-fab-bottom-left { + top: auto; + right: auto; + bottom: 20px; + left: 20px; + position: absolute +} + +md-fab-speed-dial.md-fab-top-right { + top: 20px; + right: 20px; + bottom: auto; + left: auto; + position: absolute +} + +md-fab-speed-dial.md-fab-top-left { + top: 20px; + right: auto; + bottom: auto; + left: 20px; + position: absolute +} + +md-fab-speed-dial:not(.md-hover-full) { + pointer-events: none +} + +md-fab-speed-dial:not(.md-hover-full) .md-fab-action-item, md-fab-speed-dial:not(.md-hover-full) md-fab-trigger, md-fab-speed-dial:not(.md-hover-full).md-is-open { + pointer-events: auto +} + +md-fab-speed-dial ._md-css-variables { + z-index: 20 +} + +md-fab-speed-dial.md-is-open .md-fab-action-item { + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center +} + +md-fab-speed-dial md-fab-actions { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + height: auto +} + +md-fab-speed-dial md-fab-actions .md-fab-action-item { + transition: all .3s cubic-bezier(.55, 0, .55, .2) +} + +md-fab-speed-dial.md-down { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column +} + +md-fab-speed-dial.md-down md-fab-trigger { + -webkit-order: 1; + -ms-flex-order: 1; + order: 1 +} + +md-fab-speed-dial.md-down md-fab-actions { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 +} + +md-fab-speed-dial.md-up { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column +} + +md-fab-speed-dial.md-up md-fab-trigger { + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 +} + +md-fab-speed-dial.md-up md-fab-actions { + -webkit-flex-direction: column-reverse; + -ms-flex-direction: column-reverse; + flex-direction: column-reverse; + -webkit-order: 1; + -ms-flex-order: 1; + order: 1 +} + +md-fab-speed-dial.md-left { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row +} + +md-fab-speed-dial.md-left md-fab-trigger { + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 +} + +md-fab-speed-dial.md-left md-fab-actions { + -webkit-flex-direction: row-reverse; + -ms-flex-direction: row-reverse; + flex-direction: row-reverse; + -webkit-order: 1; + -ms-flex-order: 1; + order: 1 +} + +md-fab-speed-dial.md-left md-fab-actions .md-fab-action-item { + transition: all .3s cubic-bezier(.55, 0, .55, .2) +} + +md-fab-speed-dial.md-right { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row +} + +md-fab-speed-dial.md-right md-fab-trigger { + -webkit-order: 1; + -ms-flex-order: 1; + order: 1 +} + +md-fab-speed-dial.md-right md-fab-actions { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 +} + +md-fab-speed-dial.md-right md-fab-actions .md-fab-action-item { + transition: all .3s cubic-bezier(.55, 0, .55, .2) +} + +md-fab-speed-dial.md-fling-remove .md-fab-action-item > *, md-fab-speed-dial.md-scale-remove .md-fab-action-item > * { + visibility: hidden +} + +md-fab-speed-dial.md-fling .md-fab-action-item { + opacity: 1 +} + +md-fab-speed-dial.md-fling._md-animations-waiting .md-fab-action-item { + opacity: 0; + transition-duration: 0s +} + +md-fab-speed-dial.md-scale .md-fab-action-item { + -webkit-transform: scale(0); + transform: scale(0); + transition: all .3s cubic-bezier(.55, 0, .55, .2); + transition-duration: .14286s +} + +md-fab-toolbar { + display: block +} + +md-fab-toolbar.md-fab-bottom-right { + top: auto; + right: 20px; + bottom: 20px; + left: auto; + position: absolute +} + +md-fab-toolbar.md-fab-bottom-left { + top: auto; + right: auto; + bottom: 20px; + left: 20px; + position: absolute +} + +md-fab-toolbar.md-fab-top-right { + top: 20px; + right: 20px; + bottom: auto; + left: auto; + position: absolute +} + +md-fab-toolbar.md-fab-top-left { + top: 20px; + right: auto; + bottom: auto; + left: 20px; + position: absolute +} + +md-fab-toolbar ._md-fab-toolbar-wrapper { + display: block; + position: relative; + overflow: hidden; + height: 68px +} + +md-fab-toolbar md-fab-trigger { + position: absolute; + z-index: 20 +} + +md-fab-toolbar md-fab-trigger button { + overflow: visible !important +} + +md-fab-toolbar md-fab-trigger ._md-fab-toolbar-background { + display: block; + position: absolute; + z-index: 21; + opacity: 1; + transition: all .3s cubic-bezier(.55, 0, .55, .2) +} + +md-fab-toolbar md-fab-trigger md-icon { + position: relative; + z-index: 22; + opacity: 1; + transition: all 200ms ease-in +} + +md-fab-toolbar.md-left md-fab-trigger { + right: 0 +} + +[dir=rtl] md-fab-toolbar.md-left md-fab-trigger { + right: auto; + left: 0 +} + +md-fab-toolbar.md-left .md-toolbar-tools { + -webkit-flex-direction: row-reverse; + -ms-flex-direction: row-reverse; + flex-direction: row-reverse +} + +md-fab-toolbar.md-left .md-toolbar-tools > .md-button:first-child { + margin-right: .6rem +} + +md-fab-toolbar.md-left .md-toolbar-tools > .md-button:first-child { + margin-left: -.8rem +} + +[dir=rtl] md-fab-toolbar.md-left .md-toolbar-tools > .md-button:first-child { + margin-left: auto; + margin-right: -.8rem +} + +md-fab-toolbar.md-left .md-toolbar-tools > .md-button:last-child { + margin-right: 8px +} + +[dir=rtl] md-fab-toolbar.md-left .md-toolbar-tools > .md-button:last-child { + margin-right: auto; + margin-left: 8px +} + +md-fab-toolbar.md-right md-fab-trigger { + left: 0 +} + +[dir=rtl] md-fab-toolbar.md-right md-fab-trigger { + left: auto; + right: 0 +} + +md-fab-toolbar.md-right .md-toolbar-tools { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row +} + +md-fab-toolbar md-toolbar { + background-color: transparent !important; + pointer-events: none; + z-index: 23 +} + +md-fab-toolbar md-toolbar .md-toolbar-tools { + padding: 0 20px; + margin-top: 3px +} + +md-fab-toolbar md-toolbar .md-fab-action-item { + opacity: 0; + -webkit-transform: scale(0); + transform: scale(0); + transition: all .3s cubic-bezier(.55, 0, .55, .2); + transition-duration: .15s +} + +md-fab-toolbar.md-is-open md-fab-trigger > button { + box-shadow: none +} + +md-fab-toolbar.md-is-open md-fab-trigger > button md-icon { + opacity: 0 +} + +md-fab-toolbar.md-is-open .md-fab-action-item { + opacity: 1; + -webkit-transform: scale(1); + transform: scale(1) +} + +md-grid-list { + box-sizing: border-box; + display: block; + position: relative +} + +md-grid-list md-grid-tile, md-grid-list md-grid-tile-footer, md-grid-list md-grid-tile-header, md-grid-list md-grid-tile > figure { + box-sizing: border-box +} + +md-grid-list md-grid-tile { + display: block; + position: absolute +} + +md-grid-list md-grid-tile figure { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + height: 100%; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 0; + margin: 0 +} + +md-grid-list md-grid-tile md-grid-tile-footer, md-grid-list md-grid-tile md-grid-tile-header { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + height: 48px; + color: #fff; + background: rgba(0, 0, 0, .18); + overflow: hidden; + position: absolute; + left: 0; + right: 0 +} + +md-grid-list md-grid-tile md-grid-tile-footer h3, md-grid-list md-grid-tile md-grid-tile-footer h4, md-grid-list md-grid-tile md-grid-tile-header h3, md-grid-list md-grid-tile md-grid-tile-header h4 { + font-weight: 400; + margin: 0 0 0 16px +} + +md-grid-list md-grid-tile md-grid-tile-footer h3, md-grid-list md-grid-tile md-grid-tile-header h3 { + font-size: 14px +} + +md-grid-list md-grid-tile md-grid-tile-footer h4, md-grid-list md-grid-tile md-grid-tile-header h4 { + font-size: 12px +} + +md-grid-list md-grid-tile md-grid-tile-header { + top: 0 +} + +md-grid-list md-grid-tile md-grid-tile-footer { + bottom: 0 +} + +@media screen and (-ms-high-contrast: active) { + md-grid-tile { + border: 1px solid #fff + } + + md-grid-tile-footer { + border-top: 1px solid #fff + } +} + +md-icon { + margin: auto; + background-repeat: no-repeat no-repeat; + display: inline-block; + vertical-align: middle; + fill: currentColor; + height: 24px; + width: 24px +} + +md-icon svg { + pointer-events: none; + display: block +} + +md-icon[md-font-icon] { + line-height: 24px; + width: auto +} + +md-input-container { + display: inline-block; + position: relative; + padding: 2px; + margin: 18px 0; + vertical-align: middle +} + +md-input-container:after { + content: ''; + display: table; + clear: both +} + +md-input-container.md-block { + display: block +} + +md-input-container .md-errors-spacer { + float: right; + min-height: 24px; + min-width: 1px +} + +[dir=rtl] md-input-container .md-errors-spacer { + float: left +} + +md-input-container > md-icon { + position: absolute; + top: 8px; + left: 2px; + right: auto +} + +[dir=rtl] md-input-container > md-icon { + left: auto; + right: 2px +} + +md-input-container input[type=url], md-input-container input[type=text], md-input-container input[type=password], md-input-container input[type=datetime], md-input-container input[type=datetime-local], md-input-container input[type=date], md-input-container input[type=month], md-input-container input[type=time], md-input-container input[type=week], md-input-container input[type=color], md-input-container input[type=search], md-input-container input[type=email], md-input-container input[type=number], md-input-container input[type=tel], md-input-container textarea { + -moz-appearance: none; + -webkit-appearance: none +} + +md-input-container input[type=datetime-local], md-input-container input[type=date], md-input-container input[type=month], md-input-container input[type=time], md-input-container input[type=week] { + min-height: 26px +} + +md-input-container textarea { + resize: none; + overflow: hidden +} + +md-input-container textarea.md-input { + min-height: 26px; + -ms-flex-preferred-size: auto +} + +md-input-container textarea._md-textarea-scrollable, md-input-container textarea[md-no-autogrow] { + overflow: auto +} + +md-input-container textarea[md-no-autogrow] { + height: auto +} + +md-input-container label:not(._md-container-ignore) { + position: absolute; + bottom: 100%; + left: 0; + right: auto +} + +[dir=rtl] md-input-container label:not(._md-container-ignore) { + left: auto; + right: 0 +} + +md-input-container label:not(._md-container-ignore).md-required:after { + content: ' *'; + font-size: 13px; + vertical-align: top +} + +md-input-container ._md-placeholder, md-input-container label:not(.md-no-float):not(._md-container-ignore) { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 100%; + -webkit-order: 1; + -ms-flex-order: 1; + order: 1; + pointer-events: none; + -webkit-font-smoothing: antialiased; + padding-left: 3px; + padding-right: 0; + z-index: 1; + -webkit-transform: translate3d(0, 28px, 0) scale(1); + transform: translate3d(0, 28px, 0) scale(1); + transition: -webkit-transform .4s cubic-bezier(.25, .8, .25, 1); + transition: transform .4s cubic-bezier(.25, .8, .25, 1); + max-width: 100%; + -webkit-transform-origin: left top; + transform-origin: left top +} + +[dir=rtl] md-input-container ._md-placeholder, [dir=rtl] md-input-container label:not(.md-no-float):not(._md-container-ignore) { + padding-left: 0; + padding-right: 3px; + -webkit-transform-origin: right top; + transform-origin: right top +} + +md-input-container ._md-placeholder { + position: absolute; + top: 0; + opacity: 0; + transition-property: opacity, -webkit-transform; + transition-property: opacity, transform; + -webkit-transform: translate3d(0, 30px, 0); + transform: translate3d(0, 30px, 0) +} + +md-input-container.md-input-focused ._md-placeholder { + opacity: 1; + -webkit-transform: translate3d(0, 24px, 0); + transform: translate3d(0, 24px, 0) +} + +md-input-container.md-input-has-value ._md-placeholder { + transition: none; + opacity: 0 +} + +md-input-container:not(.md-input-has-value) input:not(:focus), md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-ampm-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-day-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-hour-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-millisecond-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-minute-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-month-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-second-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-text, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-week-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-year-field { + color: transparent +} + +md-input-container .md-input { + -webkit-order: 2; + -ms-flex-order: 2; + order: 2; + display: block; + margin-top: 0; + background: 0 0; + padding: 2px 2px 1px; + border-width: 0 0 1px; + line-height: 26px; + height: 30px; + -ms-flex-preferred-size: 26px; + border-radius: 0; + border-style: solid; + width: 100%; + box-sizing: border-box; + float: left +} + +[dir=rtl] md-input-container .md-input { + float: right +} + +md-input-container .md-input:focus { + outline: 0 +} + +md-input-container .md-input:invalid { + outline: 0; + box-shadow: none +} + +md-input-container .md-input.md-no-flex { + -webkit-flex: none !important; + -ms-flex: none !important; + flex: none !important +} + +md-input-container .md-char-counter { + text-align: right; + padding-right: 2px; + padding-left: 0 +} + +[dir=rtl] md-input-container .md-char-counter { + text-align: left; + padding-right: 0; + padding-left: 2px +} + +md-input-container .md-input-messages-animation { + position: relative; + -webkit-order: 4; + -ms-flex-order: 4; + order: 4; + overflow: hidden; + clear: left +} + +[dir=rtl] md-input-container .md-input-messages-animation { + clear: right +} + +md-input-container .md-input-messages-animation.ng-enter .md-input-message-animation { + opacity: 0; + margin-top: -100px +} + +md-input-container .md-char-counter, md-input-container .md-input-message-animation { + font-size: 12px; + line-height: 14px; + overflow: hidden; + transition: all .3s cubic-bezier(.55, 0, .55, .2); + opacity: 1; + margin-top: 0; + padding-top: 5px +} + +md-input-container .md-char-counter:not(.md-char-counter), md-input-container .md-input-message-animation:not(.md-char-counter) { + padding-right: 5px; + padding-left: 0 +} + +[dir=rtl] md-input-container .md-char-counter:not(.md-char-counter), [dir=rtl] md-input-container .md-input-message-animation:not(.md-char-counter) { + padding-right: 0; + padding-left: 5px +} + +md-input-container .md-auto-hide .md-input-message-animation:not(.ng-animate), md-input-container .md-input-message-animation.ng-enter, md-input-container:not(.md-input-invalid) .md-auto-hide .md-input-message-animation { + opacity: 0; + margin-top: -100px +} + +md-input-container.md-input-focused label:not(.md-no-float), md-input-container.md-input-has-placeholder label:not(.md-no-float), md-input-container.md-input-has-value label:not(.md-no-float) { + -webkit-transform: translate3d(0, 6px, 0) scale(.75); + transform: translate3d(0, 6px, 0) scale(.75); + transition: -webkit-transform cubic-bezier(.25, .8, .25, 1) .4s, width cubic-bezier(.25, .8, .25, 1) .4s; + transition: transform cubic-bezier(.25, .8, .25, 1) .4s, width cubic-bezier(.25, .8, .25, 1) .4s; + width: calc((100% - 18px) / .75) +} + +md-input-container.md-input-has-value label { + transition: none +} + +md-input-container .md-input.ng-invalid.ng-dirty, md-input-container.md-input-focused .md-input { + padding-bottom: 0; + border-width: 0 0 2px +} + +[disabled] md-input-container .md-input, md-input-container .md-input[disabled] { + background-position: 0 bottom; + background-size: 4px 1px; + background-repeat: repeat-x; + margin-bottom: -1px +} + +md-input-container.md-icon-float { + transition: margin-top .4s cubic-bezier(.25, .8, .25, 1) +} + +md-input-container.md-icon-float > label { + pointer-events: none; + position: absolute +} + +md-input-container.md-icon-float > md-icon { + top: 8px; + left: 2px; + right: auto +} + +[dir=rtl] md-input-container.md-icon-float > md-icon { + left: auto; + right: 2px +} + +md-input-container.md-icon-left > label .md-placeholder, md-input-container.md-icon-left > label:not(.md-no-float):not(._md-container-ignore), md-input-container.md-icon-right > label .md-placeholder, md-input-container.md-icon-right > label:not(.md-no-float):not(._md-container-ignore) { + width: calc(100% - 36px - 18px) +} + +md-input-container.md-icon-left.md-input-focused > label .md-placeholder, md-input-container.md-icon-left.md-input-focused > label:not(.md-no-float):not(._md-container-ignore), md-input-container.md-icon-left.md-input-has-placeholder > label .md-placeholder, md-input-container.md-icon-left.md-input-has-placeholder > label:not(.md-no-float):not(._md-container-ignore), md-input-container.md-icon-left.md-input-has-value > label .md-placeholder, md-input-container.md-icon-left.md-input-has-value > label:not(.md-no-float):not(._md-container-ignore), md-input-container.md-icon-right.md-input-focused > label .md-placeholder, md-input-container.md-icon-right.md-input-focused > label:not(.md-no-float):not(._md-container-ignore), md-input-container.md-icon-right.md-input-has-placeholder > label .md-placeholder, md-input-container.md-icon-right.md-input-has-placeholder > label:not(.md-no-float):not(._md-container-ignore), md-input-container.md-icon-right.md-input-has-value > label .md-placeholder, md-input-container.md-icon-right.md-input-has-value > label:not(.md-no-float):not(._md-container-ignore) { + width: calc((100% - 36px - 18px) / .75) +} + +md-input-container.md-icon-left { + padding-left: 36px; + padding-right: 0 +} + +[dir=rtl] md-input-container.md-icon-left { + padding-left: 0; + padding-right: 36px +} + +md-input-container.md-icon-left > label { + left: 36px; + right: auto +} + +[dir=rtl] md-input-container.md-icon-left > label { + left: auto; + right: 36px +} + +md-input-container.md-icon-right { + padding-left: 0; + padding-right: 36px +} + +[dir=rtl] md-input-container.md-icon-right { + padding-left: 36px; + padding-right: 0 +} + +md-input-container.md-icon-right > md-icon:last-of-type { + margin: 0; + right: 2px; + left: auto +} + +[dir=rtl] md-input-container.md-icon-right > md-icon:last-of-type { + right: auto; + left: 2px +} + +md-input-container.md-icon-left.md-icon-right { + padding-left: 36px; + padding-right: 36px +} + +md-input-container.md-icon-left.md-icon-right > label .md-placeholder, md-input-container.md-icon-left.md-icon-right > label:not(.md-no-float):not(._md-container-ignore) { + width: calc(100% - (36px * 2)) +} + +md-input-container.md-icon-left.md-icon-right.md-input-focused > label .md-placeholder, md-input-container.md-icon-left.md-icon-right.md-input-focused > label:not(.md-no-float):not(._md-container-ignore), md-input-container.md-icon-left.md-icon-right.md-input-has-placeholder > label .md-placeholder, md-input-container.md-icon-left.md-icon-right.md-input-has-placeholder > label:not(.md-no-float):not(._md-container-ignore), md-input-container.md-icon-left.md-icon-right.md-input-has-value > label .md-placeholder, md-input-container.md-icon-left.md-icon-right.md-input-has-value > label:not(.md-no-float):not(._md-container-ignore) { + width: calc((100% - (36px * 2)) / .75) +} + +@media screen and (-ms-high-contrast: active) { + md-input-container.md-default-theme > md-icon { + fill: #fff + } +} + +md-list { + display: block; + padding: 8px 0 +} + +md-list .md-subheader { + font-size: 14px; + font-weight: 500; + letter-spacing: .010em; + line-height: 1.2em +} + +md-list.md-dense md-list-item, md-list.md-dense md-list-item ._md-list-item-inner { + min-height: 48px +} + +md-list.md-dense md-list-item ._md-list-item-inner md-icon:first-child, md-list.md-dense md-list-item md-icon:first-child { + width: 20px; + height: 20px +} + +md-list.md-dense md-list-item ._md-list-item-inner > md-icon:first-child:not(.md-avatar-icon), md-list.md-dense md-list-item > md-icon:first-child:not(.md-avatar-icon) { + margin-right: 36px +} + +[dir=rtl] md-list.md-dense md-list-item ._md-list-item-inner > md-icon:first-child:not(.md-avatar-icon), [dir=rtl] md-list.md-dense md-list-item > md-icon:first-child:not(.md-avatar-icon) { + margin-right: auto; + margin-left: 36px +} + +md-list.md-dense md-list-item ._md-list-item-inner .md-avatar, md-list.md-dense md-list-item ._md-list-item-inner .md-avatar-icon, md-list.md-dense md-list-item .md-avatar, md-list.md-dense md-list-item .md-avatar-icon { + margin-right: 20px +} + +[dir=rtl] md-list.md-dense md-list-item ._md-list-item-inner .md-avatar, [dir=rtl] md-list.md-dense md-list-item ._md-list-item-inner .md-avatar-icon, [dir=rtl] md-list.md-dense md-list-item .md-avatar, [dir=rtl] md-list.md-dense md-list-item .md-avatar-icon { + margin-right: auto; + margin-left: 20px +} + +md-list.md-dense md-list-item ._md-list-item-inner .md-avatar, md-list.md-dense md-list-item .md-avatar { + width: 36px; + height: 36px +} + +md-list.md-dense md-list-item.md-2-line .md-list-item-text.md-offset, md-list.md-dense md-list-item.md-2-line > ._md-no-style .md-list-item-text.md-offset, md-list.md-dense md-list-item.md-3-line .md-list-item-text.md-offset, md-list.md-dense md-list-item.md-3-line > ._md-no-style .md-list-item-text.md-offset { + margin-left: 56px +} + +[dir=rtl] md-list.md-dense md-list-item.md-2-line .md-list-item-text.md-offset, [dir=rtl] md-list.md-dense md-list-item.md-2-line > ._md-no-style .md-list-item-text.md-offset, [dir=rtl] md-list.md-dense md-list-item.md-3-line .md-list-item-text.md-offset, [dir=rtl] md-list.md-dense md-list-item.md-3-line > ._md-no-style .md-list-item-text.md-offset { + margin-left: auto; + margin-right: 56px +} + +md-list.md-dense md-list-item.md-2-line .md-list-item-text h3, md-list.md-dense md-list-item.md-2-line .md-list-item-text h4, md-list.md-dense md-list-item.md-2-line .md-list-item-text p, md-list.md-dense md-list-item.md-2-line > ._md-no-style .md-list-item-text h3, md-list.md-dense md-list-item.md-2-line > ._md-no-style .md-list-item-text h4, md-list.md-dense md-list-item.md-2-line > ._md-no-style .md-list-item-text p, md-list.md-dense md-list-item.md-3-line .md-list-item-text h3, md-list.md-dense md-list-item.md-3-line .md-list-item-text h4, md-list.md-dense md-list-item.md-3-line .md-list-item-text p, md-list.md-dense md-list-item.md-3-line > ._md-no-style .md-list-item-text h3, md-list.md-dense md-list-item.md-3-line > ._md-no-style .md-list-item-text h4, md-list.md-dense md-list-item.md-3-line > ._md-no-style .md-list-item-text p { + line-height: 1.05; + font-size: 12px +} + +md-list.md-dense md-list-item.md-2-line .md-list-item-text h3, md-list.md-dense md-list-item.md-2-line > ._md-no-style .md-list-item-text h3, md-list.md-dense md-list-item.md-3-line .md-list-item-text h3, md-list.md-dense md-list-item.md-3-line > ._md-no-style .md-list-item-text h3 { + font-size: 13px +} + +md-list.md-dense md-list-item.md-2-line, md-list.md-dense md-list-item.md-2-line > ._md-no-style { + min-height: 60px +} + +md-list.md-dense md-list-item.md-2-line div.md-button:first-child::before, md-list.md-dense md-list-item.md-2-line > ._md-no-style div.md-button:first-child::before { + content: ''; + min-height: 60px; + visibility: hidden +} + +md-list.md-dense md-list-item.md-2-line .md-avatar-icon, md-list.md-dense md-list-item.md-2-line > ._md-no-style .md-avatar-icon, md-list.md-dense md-list-item.md-2-line > ._md-no-style > .md-avatar, md-list.md-dense md-list-item.md-2-line > .md-avatar { + margin-top: 12px +} + +md-list.md-dense md-list-item.md-3-line, md-list.md-dense md-list-item.md-3-line > ._md-no-style { + min-height: 76px +} + +md-list.md-dense md-list-item.md-3-line div.md-button:first-child::before, md-list.md-dense md-list-item.md-3-line > ._md-no-style div.md-button:first-child::before { + content: ''; + min-height: 76px; + visibility: hidden +} + +md-list.md-dense md-list-item.md-3-line > ._md-no-style > .md-avatar, md-list.md-dense md-list-item.md-3-line > ._md-no-style > md-icon:first-child, md-list.md-dense md-list-item.md-3-line > .md-avatar, md-list.md-dense md-list-item.md-3-line > md-icon:first-child { + margin-top: 16px +} + +md-list-item { + position: relative +} + +md-list-item._md-proxy-focus.md-focused ._md-no-style { + transition: background-color .15s linear +} + +md-list-item._md-button-wrap { + position: relative +} + +md-list-item._md-button-wrap > div.md-button:first-child { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + padding: 0 16px; + margin: 0; + background-color: initial; + font-weight: 400; + text-align: left; + border: none +} + +[dir=rtl] md-list-item._md-button-wrap > div.md-button:first-child { + text-align: right +} + +md-list-item._md-button-wrap > div.md-button:first-child > .md-button:first-child { + position: absolute; + top: 0; + left: 0; + height: 100%; + margin: 0; + padding: 0 +} + +md-list-item._md-button-wrap > div.md-button:first-child ._md-list-item-inner { + width: 100%; + height: 100% +} + +md-list-item ._md-no-style, md-list-item._md-no-proxy { + position: relative; + padding: 0 16px; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto +} + +md-list-item ._md-no-style.md-button, md-list-item._md-no-proxy.md-button { + font-size: inherit; + height: inherit; + text-align: left; + text-transform: none; + width: 100%; + white-space: normal; + -webkit-flex-direction: inherit; + -ms-flex-direction: inherit; + flex-direction: inherit; + -webkit-align-items: inherit; + -ms-flex-align: inherit; + align-items: inherit; + border-radius: 0; + margin: 0 +} + +[dir=rtl] md-list-item ._md-no-style.md-button, [dir=rtl] md-list-item._md-no-proxy.md-button { + text-align: right +} + +md-list-item ._md-no-style.md-button > .md-ripple-container, md-list-item._md-no-proxy.md-button > .md-ripple-container { + border-radius: 0 +} + +md-list-item ._md-no-style:focus, md-list-item._md-no-proxy:focus { + outline: 0 +} + +md-list-item.md-clickable:hover { + cursor: pointer +} + +md-list-item md-divider { + position: absolute; + bottom: 0; + left: 0; + width: 100% +} + +[dir=rtl] md-list-item md-divider { + left: auto; + right: 0 +} + +md-list-item md-divider[md-inset] { + left: 72px; + width: calc(100% - 72px); + margin: 0 !important +} + +[dir=rtl] md-list-item md-divider[md-inset] { + left: auto; + right: 72px +} + +md-list-item, md-list-item ._md-list-item-inner { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + min-height: 48px; + height: auto +} + +md-list-item ._md-list-item-inner > div.md-primary > md-icon:not(.md-avatar-icon), md-list-item ._md-list-item-inner > div.md-secondary > md-icon:not(.md-avatar-icon), md-list-item ._md-list-item-inner > md-icon.md-secondary:not(.md-avatar-icon), md-list-item ._md-list-item-inner > md-icon:first-child:not(.md-avatar-icon), md-list-item > div.md-primary > md-icon:not(.md-avatar-icon), md-list-item > div.md-secondary > md-icon:not(.md-avatar-icon), md-list-item > md-icon.md-secondary:not(.md-avatar-icon), md-list-item > md-icon:first-child:not(.md-avatar-icon) { + width: 24px; + margin-top: 16px; + margin-bottom: 12px; + box-sizing: content-box +} + +md-list-item ._md-list-item-inner md-checkbox.md-secondary, md-list-item ._md-list-item-inner > div.md-primary > md-checkbox, md-list-item ._md-list-item-inner > div.md-secondary > md-checkbox, md-list-item ._md-list-item-inner > md-checkbox, md-list-item md-checkbox.md-secondary, md-list-item > div.md-primary > md-checkbox, md-list-item > div.md-secondary > md-checkbox, md-list-item > md-checkbox { + -webkit-align-self: center; + -ms-flex-item-align: center; + align-self: center +} + +md-list-item ._md-list-item-inner md-checkbox.md-secondary .md-label, md-list-item ._md-list-item-inner > div.md-primary > md-checkbox .md-label, md-list-item ._md-list-item-inner > div.md-secondary > md-checkbox .md-label, md-list-item ._md-list-item-inner > md-checkbox .md-label, md-list-item md-checkbox.md-secondary .md-label, md-list-item > div.md-primary > md-checkbox .md-label, md-list-item > div.md-secondary > md-checkbox .md-label, md-list-item > md-checkbox .md-label { + display: none +} + +md-list-item ._md-list-item-inner > md-icon:first-child:not(.md-avatar-icon), md-list-item > md-icon:first-child:not(.md-avatar-icon) { + margin-right: 32px +} + +[dir=rtl] md-list-item ._md-list-item-inner > md-icon:first-child:not(.md-avatar-icon), [dir=rtl] md-list-item > md-icon:first-child:not(.md-avatar-icon) { + margin-right: auto; + margin-left: 32px +} + +md-list-item ._md-list-item-inner .md-avatar, md-list-item ._md-list-item-inner .md-avatar-icon, md-list-item .md-avatar, md-list-item .md-avatar-icon { + margin-top: 8px; + margin-bottom: 8px; + margin-right: 16px; + border-radius: 50%; + box-sizing: content-box +} + +[dir=rtl] md-list-item ._md-list-item-inner .md-avatar, [dir=rtl] md-list-item ._md-list-item-inner .md-avatar-icon, [dir=rtl] md-list-item .md-avatar, [dir=rtl] md-list-item .md-avatar-icon { + margin-right: auto; + margin-left: 16px +} + +md-list-item ._md-list-item-inner .md-avatar, md-list-item .md-avatar { + width: 40px; + height: 40px +} + +md-list-item ._md-list-item-inner .md-avatar-icon, md-list-item .md-avatar-icon { + padding: 8px +} + +md-list-item ._md-list-item-inner > md-checkbox, md-list-item > md-checkbox { + width: 24px; + margin-left: 3px; + margin-right: 29px; + margin-top: 16px +} + +[dir=rtl] md-list-item ._md-list-item-inner > md-checkbox, [dir=rtl] md-list-item > md-checkbox { + margin-left: 29px; + margin-right: 3px +} + +md-list-item ._md-list-item-inner ._md-secondary-container, md-list-item ._md-secondary-container { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + height: 100%; + margin: auto +} + +md-list-item ._md-list-item-inner ._md-secondary-container .md-button:last-of-type, md-list-item ._md-list-item-inner ._md-secondary-container .md-icon-button:last-of-type, md-list-item ._md-secondary-container .md-button:last-of-type, md-list-item ._md-secondary-container .md-icon-button:last-of-type { + margin-right: 0 +} + +[dir=rtl] md-list-item ._md-list-item-inner ._md-secondary-container .md-button:last-of-type, [dir=rtl] md-list-item ._md-list-item-inner ._md-secondary-container .md-icon-button:last-of-type, [dir=rtl] md-list-item ._md-secondary-container .md-button:last-of-type, [dir=rtl] md-list-item ._md-secondary-container .md-icon-button:last-of-type { + margin-right: auto; + margin-left: 0 +} + +md-list-item ._md-list-item-inner ._md-secondary-container md-checkbox, md-list-item ._md-secondary-container md-checkbox { + margin-top: 0; + margin-bottom: 0 +} + +md-list-item ._md-list-item-inner ._md-secondary-container md-checkbox:last-child, md-list-item ._md-secondary-container md-checkbox:last-child { + width: 24px; + margin-right: 0 +} + +[dir=rtl] md-list-item ._md-list-item-inner ._md-secondary-container md-checkbox:last-child, [dir=rtl] md-list-item ._md-secondary-container md-checkbox:last-child { + margin-right: auto; + margin-left: 0 +} + +md-list-item ._md-list-item-inner ._md-secondary-container md-switch, md-list-item ._md-secondary-container md-switch { + margin-top: 0; + margin-bottom: 0; + margin-right: -6px +} + +[dir=rtl] md-list-item ._md-list-item-inner ._md-secondary-container md-switch, [dir=rtl] md-list-item ._md-secondary-container md-switch { + margin-right: auto; + margin-left: -6px +} + +md-list-item ._md-list-item-inner > ._md-list-item-inner > p, md-list-item ._md-list-item-inner > p, md-list-item > ._md-list-item-inner > p, md-list-item > p { + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + margin: 0 +} + +md-list-item.md-2-line, md-list-item.md-2-line > ._md-no-style, md-list-item.md-3-line, md-list-item.md-3-line > ._md-no-style { + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center +} + +md-list-item.md-2-line.md-long-text, md-list-item.md-2-line > ._md-no-style.md-long-text, md-list-item.md-3-line.md-long-text, md-list-item.md-3-line > ._md-no-style.md-long-text { + margin-top: 8px; + margin-bottom: 8px +} + +md-list-item.md-2-line .md-list-item-text, md-list-item.md-2-line > ._md-no-style .md-list-item-text, md-list-item.md-3-line .md-list-item-text, md-list-item.md-3-line > ._md-no-style .md-list-item-text { + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + margin: auto; + text-overflow: ellipsis; + overflow: hidden +} + +md-list-item.md-2-line .md-list-item-text.md-offset, md-list-item.md-2-line > ._md-no-style .md-list-item-text.md-offset, md-list-item.md-3-line .md-list-item-text.md-offset, md-list-item.md-3-line > ._md-no-style .md-list-item-text.md-offset { + margin-left: 56px +} + +[dir=rtl] md-list-item.md-2-line .md-list-item-text.md-offset, [dir=rtl] md-list-item.md-2-line > ._md-no-style .md-list-item-text.md-offset, [dir=rtl] md-list-item.md-3-line .md-list-item-text.md-offset, [dir=rtl] md-list-item.md-3-line > ._md-no-style .md-list-item-text.md-offset { + margin-left: auto; + margin-right: 56px +} + +md-list-item.md-2-line .md-list-item-text h3, md-list-item.md-2-line > ._md-no-style .md-list-item-text h3, md-list-item.md-3-line .md-list-item-text h3, md-list-item.md-3-line > ._md-no-style .md-list-item-text h3 { + font-size: 16px; + font-weight: 400; + letter-spacing: .010em; + margin: 0; + line-height: 1.2em; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis +} + +md-list-item.md-2-line .md-list-item-text h4, md-list-item.md-2-line > ._md-no-style .md-list-item-text h4, md-list-item.md-3-line .md-list-item-text h4, md-list-item.md-3-line > ._md-no-style .md-list-item-text h4 { + font-size: 14px; + letter-spacing: .010em; + margin: 3px 0 1px; + font-weight: 400; + line-height: 1.2em; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis +} + +md-list-item.md-2-line .md-list-item-text p, md-list-item.md-2-line > ._md-no-style .md-list-item-text p, md-list-item.md-3-line .md-list-item-text p, md-list-item.md-3-line > ._md-no-style .md-list-item-text p { + font-size: 14px; + font-weight: 500; + letter-spacing: .010em; + margin: 0; + line-height: 1.6em +} + +md-list-item.md-2-line, md-list-item.md-2-line > ._md-no-style { + height: auto; + min-height: 72px +} + +md-list-item.md-2-line div.md-button:first-child::before, md-list-item.md-2-line > ._md-no-style div.md-button:first-child::before { + content: ''; + min-height: 72px; + visibility: hidden +} + +md-list-item.md-2-line .md-avatar-icon, md-list-item.md-2-line > ._md-no-style .md-avatar-icon, md-list-item.md-2-line > ._md-no-style > .md-avatar, md-list-item.md-2-line > .md-avatar { + margin-top: 12px +} + +md-list-item.md-2-line > ._md-no-style > md-icon:first-child, md-list-item.md-2-line > md-icon:first-child { + -webkit-align-self: flex-start; + -ms-flex-item-align: start; + align-self: flex-start +} + +md-list-item.md-2-line .md-list-item-text, md-list-item.md-2-line > ._md-no-style .md-list-item-text { + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto +} + +md-list-item.md-3-line, md-list-item.md-3-line > ._md-no-style { + height: auto; + min-height: 88px +} + +md-list-item.md-3-line div.md-button:first-child::before, md-list-item.md-3-line > ._md-no-style div.md-button:first-child::before { + content: ''; + min-height: 88px; + visibility: hidden +} + +md-list-item.md-3-line > ._md-no-style > .md-avatar, md-list-item.md-3-line > ._md-no-style > md-icon:first-child, md-list-item.md-3-line > .md-avatar, md-list-item.md-3-line > md-icon:first-child { + margin-top: 16px +} + +md-toolbar.md-menu-toolbar h2.md-toolbar-tools { + line-height: 1rem; + height: auto; + padding: 28px 28px 12px +} + +md-menu-bar { + padding: 0 20px; + display: block; + position: relative; + z-index: 2 +} + +md-menu-bar .md-menu { + display: inline-block; + padding: 0; + position: relative +} + +md-menu-bar button { + font-size: 14px; + padding: 0 10px; + margin: 0; + border: 0; + background-color: transparent; + height: 40px +} + +md-menu-bar md-backdrop._md-menu-backdrop { + z-index: -2 +} + +md-menu-content._md-menu-bar-menu.md-dense { + max-height: none; + padding: 16px 0 +} + +md-menu-content._md-menu-bar-menu.md-dense md-menu-item.md-indent { + position: relative +} + +md-menu-content._md-menu-bar-menu.md-dense md-menu-item.md-indent > md-icon { + position: absolute; + padding: 0; + width: 24px; + top: 6px; + left: 24px +} + +[dir=rtl] md-menu-content._md-menu-bar-menu.md-dense md-menu-item.md-indent > md-icon { + left: auto; + right: 24px +} + +md-menu-content._md-menu-bar-menu.md-dense md-menu-item.md-indent .md-menu > .md-button, md-menu-content._md-menu-bar-menu.md-dense md-menu-item.md-indent > .md-button { + padding: 0 32px 0 64px +} + +[dir=rtl] md-menu-content._md-menu-bar-menu.md-dense md-menu-item.md-indent .md-menu > .md-button, [dir=rtl] md-menu-content._md-menu-bar-menu.md-dense md-menu-item.md-indent > .md-button { + padding: 0 64px 0 32px +} + +md-menu-content._md-menu-bar-menu.md-dense .md-button { + min-height: 0; + height: 32px; + display: -webkit-flex; + display: -ms-flexbox; + display: flex +} + +md-menu-content._md-menu-bar-menu.md-dense .md-button span { + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1 +} + +md-menu-content._md-menu-bar-menu.md-dense .md-button span.md-alt-text { + -webkit-flex-grow: 0; + -ms-flex-positive: 0; + flex-grow: 0; + -webkit-align-self: flex-end; + -ms-flex-item-align: end; + align-self: flex-end; + margin: 0 8px +} + +md-menu-content._md-menu-bar-menu.md-dense md-menu-divider { + margin: 8px 0 +} + +md-menu-content._md-menu-bar-menu.md-dense .md-menu > .md-button, md-menu-content._md-menu-bar-menu.md-dense md-menu-item > .md-button { + text-align: left; + text-align: start +} + +[dir=rtl] md-menu-content._md-menu-bar-menu.md-dense .md-menu > .md-button, [dir=rtl] md-menu-content._md-menu-bar-menu.md-dense md-menu-item > .md-button { + text-align: right +} + +md-menu-content._md-menu-bar-menu.md-dense .md-menu { + padding: 0 +} + +md-menu-content._md-menu-bar-menu.md-dense .md-menu > .md-button { + position: relative; + margin: 0; + width: 100%; + text-transform: none; + font-weight: 400; + border-radius: 0; + padding-left: 16px +} + +[dir=rtl] md-menu-content._md-menu-bar-menu.md-dense .md-menu > .md-button { + padding-left: auto; + padding-right: 16px +} + +md-menu-content._md-menu-bar-menu.md-dense .md-menu > .md-button:after { + display: block; + content: '\25BC'; + position: absolute; + top: 0; + speak: none; + -webkit-transform: rotate(270deg) scaleY(.45) scaleX(.9); + transform: rotate(270deg) scaleY(.45) scaleX(.9); + right: 28px +} + +[dir=rtl] md-menu-content._md-menu-bar-menu.md-dense .md-menu > .md-button:after { + -webkit-transform: rotate(90deg) scaleY(.45) scaleX(.9); + transform: rotate(90deg) scaleY(.45) scaleX(.9); + right: auto; + left: 28px +} + +._md-open-menu-container { + position: fixed; + left: 0; + top: 0; + z-index: 100; + opacity: 0; + border-radius: 2px +} + +._md-open-menu-container md-menu-divider { + margin-top: 4px; + margin-bottom: 4px; + height: 1px; + min-height: 1px; + max-height: 1px; + width: 100% +} + +._md-open-menu-container md-menu-content > * { + opacity: 0 +} + +._md-open-menu-container:not(._md-clickable) { + pointer-events: none +} + +._md-open-menu-container._md-active { + opacity: 1; + transition: all .4s cubic-bezier(.25, .8, .25, 1); + transition-duration: 200ms +} + +._md-open-menu-container._md-active > md-menu-content > * { + opacity: 1; + transition: all .3s cubic-bezier(.55, 0, .55, .2); + transition-duration: 200ms; + transition-delay: 100ms +} + +._md-open-menu-container._md-leave { + opacity: 0; + transition: all .3s cubic-bezier(.55, 0, .55, .2); + transition-duration: 250ms +} + +md-menu-content { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + padding: 8px 0; + max-height: 304px; + overflow-y: auto +} + +md-menu-content.md-dense { + max-height: 208px +} + +md-menu-content.md-dense md-menu-item { + height: 32px; + min-height: 0 +} + +md-menu-item { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + min-height: 48px; + height: 48px; + -webkit-align-content: center; + -ms-flex-line-pack: center; + align-content: center; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start +} + +md-menu-item > * { + width: 100%; + margin: auto 0; + padding-left: 16px; + padding-right: 16px +} + +md-menu-item > .md-button { + border-radius: 0; + margin: auto 0; + font-size: 15px; + text-transform: none; + font-weight: 400; + height: 100%; + padding-left: 16px; + padding-right: 16px; + text-align: left; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: baseline; + -ms-flex-align: baseline; + align-items: baseline; + -webkit-align-content: flex-start; + -ms-flex-line-pack: start; + align-content: flex-start; + width: 100% +} + +[dir=rtl] md-menu-item > .md-button { + text-align: right +} + +md-menu-item > .md-button md-icon { + margin: auto 16px auto 0 +} + +[dir=rtl] md-menu-item > .md-button md-icon { + margin: auto 0 auto 16px +} + +md-menu-item > .md-button p { + display: inline-block; + margin: auto +} + +md-menu-item > .md-button span { + margin-top: auto; + margin-bottom: auto +} + +md-menu-item > .md-button .md-ripple-container { + border-radius: inherit +} + +.md-menu { + padding: 8px 0 +} + +md-toolbar .md-menu { + height: auto; + margin: auto; + padding: 0 +} + +@media (max-width: 959px) { + md-menu-content { + min-width: 112px + } + + md-menu-content[width="3"] { + min-width: 168px + } + + md-menu-content[width="4"] { + min-width: 224px + } + + md-menu-content[width="5"] { + min-width: 280px + } + + md-menu-content[width="6"] { + min-width: 336px + } + + md-menu-content[width="7"] { + min-width: 392px + } +} + +@media (min-width: 960px) { + md-menu-content { + min-width: 96px + } + + md-menu-content[width="3"] { + min-width: 192px + } + + md-menu-content[width="4"] { + min-width: 256px + } + + md-menu-content[width="5"] { + min-width: 320px + } + + md-menu-content[width="6"] { + min-width: 384px + } + + md-menu-content[width="7"] { + min-width: 448px + } +} + +md-progress-circular { + position: relative +} + +md-progress-circular svg { + position: absolute; + overflow: visible; + top: 0; + left: 0 +} + +md-progress-linear { + display: block; + position: relative; + width: 100%; + height: 5px; + padding-top: 0 !important; + margin-bottom: 0 !important +} + +md-progress-linear ._md-container { + display: block; + position: relative; + overflow: hidden; + width: 100%; + height: 5px; + -webkit-transform: translate(0, 0) scale(1, 1); + transform: translate(0, 0) scale(1, 1) +} + +md-progress-linear ._md-container ._md-bar { + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 100%; + height: 5px +} + +md-progress-linear ._md-container ._md-dashed:before { + content: ""; + display: none; + position: absolute; + margin-top: 0; + height: 5px; + width: 100%; + background-color: transparent; + background-size: 10px 10px !important; + background-position: 0 -23px +} + +md-progress-linear ._md-container ._md-bar1, md-progress-linear ._md-container ._md-bar2 { + transition: -webkit-transform .2s linear; + transition: transform .2s linear +} + +md-progress-linear ._md-container._md-mode-query ._md-bar1 { + display: none +} + +md-progress-linear ._md-container._md-mode-query ._md-bar2 { + transition: all .2s linear; + -webkit-animation: query .8s infinite cubic-bezier(.39, .575, .565, 1); + animation: query .8s infinite cubic-bezier(.39, .575, .565, 1) +} + +md-progress-linear ._md-container._md-mode-determinate ._md-bar1 { + display: none +} + +md-progress-linear ._md-container._md-mode-indeterminate ._md-bar1 { + -webkit-animation: md-progress-linear-indeterminate-scale-1 4s infinite, md-progress-linear-indeterminate-1 4s infinite; + animation: md-progress-linear-indeterminate-scale-1 4s infinite, md-progress-linear-indeterminate-1 4s infinite +} + +md-progress-linear ._md-container._md-mode-indeterminate ._md-bar2 { + -webkit-animation: md-progress-linear-indeterminate-scale-2 4s infinite, md-progress-linear-indeterminate-2 4s infinite; + animation: md-progress-linear-indeterminate-scale-2 4s infinite, md-progress-linear-indeterminate-2 4s infinite +} + +md-progress-linear ._md-container.ng-hide { + -webkit-animation: none; + animation: none +} + +md-progress-linear ._md-container.ng-hide ._md-bar1, md-progress-linear ._md-container.ng-hide ._md-bar2 { + -webkit-animation-name: none; + animation-name: none +} + +md-progress-linear ._md-container._md-mode-buffer { + background-color: transparent !important; + transition: all .2s linear +} + +md-progress-linear ._md-container._md-mode-buffer ._md-dashed:before { + display: block; + -webkit-animation: buffer 3s infinite linear; + animation: buffer 3s infinite linear +} + +@-webkit-keyframes query { + 0% { + opacity: 1; + -webkit-transform: translateX(35%) scale(.3, 1); + transform: translateX(35%) scale(.3, 1) + } + 100% { + opacity: 0; + -webkit-transform: translateX(-50%) scale(0, 1); + transform: translateX(-50%) scale(0, 1) + } +} + +@keyframes query { + 0% { + opacity: 1; + -webkit-transform: translateX(35%) scale(.3, 1); + transform: translateX(35%) scale(.3, 1) + } + 100% { + opacity: 0; + -webkit-transform: translateX(-50%) scale(0, 1); + transform: translateX(-50%) scale(0, 1) + } +} + +@-webkit-keyframes buffer { + 0% { + opacity: 1; + background-position: 0 -23px + } + 50% { + opacity: 0 + } + 100% { + opacity: 1; + background-position: -200px -23px + } +} + +@keyframes buffer { + 0% { + opacity: 1; + background-position: 0 -23px + } + 50% { + opacity: 0 + } + 100% { + opacity: 1; + background-position: -200px -23px + } +} + +@-webkit-keyframes md-progress-linear-indeterminate-scale-1 { + 0% { + -webkit-transform: scaleX(.1); + transform: scaleX(.1); + -webkit-animation-timing-function: linear; + animation-timing-function: linear + } + 36.6% { + -webkit-transform: scaleX(.1); + transform: scaleX(.1); + -webkit-animation-timing-function: cubic-bezier(.33473, .12482, .78584, 1); + animation-timing-function: cubic-bezier(.33473, .12482, .78584, 1) + } + 69.15% { + -webkit-transform: scaleX(.83); + transform: scaleX(.83); + -webkit-animation-timing-function: cubic-bezier(.22573, 0, .23365, 1.37098); + animation-timing-function: cubic-bezier(.22573, 0, .23365, 1.37098) + } + 100% { + -webkit-transform: scaleX(.1); + transform: scaleX(.1) + } +} + +@keyframes md-progress-linear-indeterminate-scale-1 { + 0% { + -webkit-transform: scaleX(.1); + transform: scaleX(.1); + -webkit-animation-timing-function: linear; + animation-timing-function: linear + } + 36.6% { + -webkit-transform: scaleX(.1); + transform: scaleX(.1); + -webkit-animation-timing-function: cubic-bezier(.33473, .12482, .78584, 1); + animation-timing-function: cubic-bezier(.33473, .12482, .78584, 1) + } + 69.15% { + -webkit-transform: scaleX(.83); + transform: scaleX(.83); + -webkit-animation-timing-function: cubic-bezier(.22573, 0, .23365, 1.37098); + animation-timing-function: cubic-bezier(.22573, 0, .23365, 1.37098) + } + 100% { + -webkit-transform: scaleX(.1); + transform: scaleX(.1) + } +} + +@-webkit-keyframes md-progress-linear-indeterminate-1 { + 0% { + left: -105.16667%; + -webkit-animation-timing-function: linear; + animation-timing-function: linear + } + 20% { + left: -105.16667%; + -webkit-animation-timing-function: cubic-bezier(.5, 0, .70173, .49582); + animation-timing-function: cubic-bezier(.5, 0, .70173, .49582) + } + 69.15% { + left: 21.5%; + -webkit-animation-timing-function: cubic-bezier(.30244, .38135, .55, .95635); + animation-timing-function: cubic-bezier(.30244, .38135, .55, .95635) + } + 100% { + left: 95.44444% + } +} + +@keyframes md-progress-linear-indeterminate-1 { + 0% { + left: -105.16667%; + -webkit-animation-timing-function: linear; + animation-timing-function: linear + } + 20% { + left: -105.16667%; + -webkit-animation-timing-function: cubic-bezier(.5, 0, .70173, .49582); + animation-timing-function: cubic-bezier(.5, 0, .70173, .49582) + } + 69.15% { + left: 21.5%; + -webkit-animation-timing-function: cubic-bezier(.30244, .38135, .55, .95635); + animation-timing-function: cubic-bezier(.30244, .38135, .55, .95635) + } + 100% { + left: 95.44444% + } +} + +@-webkit-keyframes md-progress-linear-indeterminate-scale-2 { + 0% { + -webkit-transform: scaleX(.1); + transform: scaleX(.1); + -webkit-animation-timing-function: cubic-bezier(.20503, .05705, .57661, .45397); + animation-timing-function: cubic-bezier(.20503, .05705, .57661, .45397) + } + 19.15% { + -webkit-transform: scaleX(.57); + transform: scaleX(.57); + -webkit-animation-timing-function: cubic-bezier(.15231, .19643, .64837, 1.00432); + animation-timing-function: cubic-bezier(.15231, .19643, .64837, 1.00432) + } + 44.15% { + -webkit-transform: scaleX(.91); + transform: scaleX(.91); + -webkit-animation-timing-function: cubic-bezier(.25776, -.00316, .21176, 1.38179); + animation-timing-function: cubic-bezier(.25776, -.00316, .21176, 1.38179) + } + 100% { + -webkit-transform: scaleX(.1); + transform: scaleX(.1) + } +} + +@keyframes md-progress-linear-indeterminate-scale-2 { + 0% { + -webkit-transform: scaleX(.1); + transform: scaleX(.1); + -webkit-animation-timing-function: cubic-bezier(.20503, .05705, .57661, .45397); + animation-timing-function: cubic-bezier(.20503, .05705, .57661, .45397) + } + 19.15% { + -webkit-transform: scaleX(.57); + transform: scaleX(.57); + -webkit-animation-timing-function: cubic-bezier(.15231, .19643, .64837, 1.00432); + animation-timing-function: cubic-bezier(.15231, .19643, .64837, 1.00432) + } + 44.15% { + -webkit-transform: scaleX(.91); + transform: scaleX(.91); + -webkit-animation-timing-function: cubic-bezier(.25776, -.00316, .21176, 1.38179); + animation-timing-function: cubic-bezier(.25776, -.00316, .21176, 1.38179) + } + 100% { + -webkit-transform: scaleX(.1); + transform: scaleX(.1) + } +} + +@-webkit-keyframes md-progress-linear-indeterminate-2 { + 0% { + left: -54.88889%; + -webkit-animation-timing-function: cubic-bezier(.15, 0, .51506, .40968); + animation-timing-function: cubic-bezier(.15, 0, .51506, .40968) + } + 25% { + left: -17.25%; + -webkit-animation-timing-function: cubic-bezier(.31033, .28406, .8, .73372); + animation-timing-function: cubic-bezier(.31033, .28406, .8, .73372) + } + 48.35% { + left: 29.5%; + -webkit-animation-timing-function: cubic-bezier(.4, .62703, .6, .90203); + animation-timing-function: cubic-bezier(.4, .62703, .6, .90203) + } + 100% { + left: 117.38889% + } +} + +@keyframes md-progress-linear-indeterminate-2 { + 0% { + left: -54.88889%; + -webkit-animation-timing-function: cubic-bezier(.15, 0, .51506, .40968); + animation-timing-function: cubic-bezier(.15, 0, .51506, .40968) + } + 25% { + left: -17.25%; + -webkit-animation-timing-function: cubic-bezier(.31033, .28406, .8, .73372); + animation-timing-function: cubic-bezier(.31033, .28406, .8, .73372) + } + 48.35% { + left: 29.5%; + -webkit-animation-timing-function: cubic-bezier(.4, .62703, .6, .90203); + animation-timing-function: cubic-bezier(.4, .62703, .6, .90203) + } + 100% { + left: 117.38889% + } +} + +md-radio-button { + box-sizing: border-box; + display: block; + margin-bottom: 16px; + white-space: nowrap; + cursor: pointer; + position: relative +} + +md-radio-button[disabled], md-radio-button[disabled] ._md-container { + cursor: default +} + +md-radio-button ._md-container { + position: absolute; + top: 50%; + -webkit-transform: translateY(-50%); + transform: translateY(-50%); + box-sizing: border-box; + display: inline-block; + width: 20px; + height: 20px; + cursor: pointer; + left: 0; + right: auto +} + +[dir=rtl] md-radio-button ._md-container { + left: auto; + right: 0 +} + +md-radio-button ._md-container .md-ripple-container { + position: absolute; + display: block; + width: auto; + height: auto; + left: -15px; + top: -15px; + right: -15px; + bottom: -15px +} + +md-radio-button ._md-container:before { + box-sizing: border-box; + background-color: transparent; + border-radius: 50%; + content: ''; + position: absolute; + display: block; + height: auto; + left: 0; + top: 0; + right: 0; + bottom: 0; + transition: all .5s; + width: auto +} + +md-radio-button.md-align-top-left > div._md-container { + top: 12px +} + +md-radio-button ._md-off { + box-sizing: border-box; + position: absolute; + top: 0; + left: 0; + width: 20px; + height: 20px; + border-style: solid; + border-width: 2px; + border-radius: 50%; + transition: border-color ease .28s +} + +md-radio-button ._md-on { + box-sizing: border-box; + position: absolute; + top: 0; + left: 0; + width: 20px; + height: 20px; + border-radius: 50%; + transition: -webkit-transform ease .28s; + transition: transform ease .28s; + -webkit-transform: scale(0); + transform: scale(0) +} + +md-radio-button.md-checked ._md-on { + -webkit-transform: scale(.5); + transform: scale(.5) +} + +md-radio-button ._md-label { + box-sizing: border-box; + position: relative; + display: inline-block; + margin-left: 30px; + margin-right: 0; + vertical-align: middle; + white-space: normal; + pointer-events: none; + width: auto +} + +[dir=rtl] md-radio-button ._md-label { + margin-left: 0; + margin-right: 30px +} + +md-radio-group.layout-column md-radio-button, md-radio-group.layout-gt-lg-column md-radio-button, md-radio-group.layout-gt-md-column md-radio-button, md-radio-group.layout-gt-sm-column md-radio-button, md-radio-group.layout-gt-xs-column md-radio-button, md-radio-group.layout-lg-column md-radio-button, md-radio-group.layout-md-column md-radio-button, md-radio-group.layout-sm-column md-radio-button, md-radio-group.layout-xl-column md-radio-button, md-radio-group.layout-xs-column md-radio-button { + margin-bottom: 16px +} + +md-radio-group.layout-gt-lg-row md-radio-button, md-radio-group.layout-gt-md-row md-radio-button, md-radio-group.layout-gt-sm-row md-radio-button, md-radio-group.layout-gt-xs-row md-radio-button, md-radio-group.layout-lg-row md-radio-button, md-radio-group.layout-md-row md-radio-button, md-radio-group.layout-row md-radio-button, md-radio-group.layout-sm-row md-radio-button, md-radio-group.layout-xl-row md-radio-button, md-radio-group.layout-xs-row md-radio-button { + margin: 0 16px 0 0 +} + +[dir=rtl] md-radio-group.layout-gt-lg-row md-radio-button, [dir=rtl] md-radio-group.layout-gt-md-row md-radio-button, [dir=rtl] md-radio-group.layout-gt-sm-row md-radio-button, [dir=rtl] md-radio-group.layout-gt-xs-row md-radio-button, [dir=rtl] md-radio-group.layout-lg-row md-radio-button, [dir=rtl] md-radio-group.layout-md-row md-radio-button, [dir=rtl] md-radio-group.layout-row md-radio-button, [dir=rtl] md-radio-group.layout-sm-row md-radio-button, [dir=rtl] md-radio-group.layout-xl-row md-radio-button, [dir=rtl] md-radio-group.layout-xs-row md-radio-button { + margin-left: 16px; + margin-right: 0 +} + +md-radio-group.layout-gt-lg-row md-radio-button:last-of-type, md-radio-group.layout-gt-md-row md-radio-button:last-of-type, md-radio-group.layout-gt-sm-row md-radio-button:last-of-type, md-radio-group.layout-gt-xs-row md-radio-button:last-of-type, md-radio-group.layout-lg-row md-radio-button:last-of-type, md-radio-group.layout-md-row md-radio-button:last-of-type, md-radio-group.layout-row md-radio-button:last-of-type, md-radio-group.layout-sm-row md-radio-button:last-of-type, md-radio-group.layout-xl-row md-radio-button:last-of-type, md-radio-group.layout-xs-row md-radio-button:last-of-type { + margin-left: 0; + margin-right: 0 +} + +md-radio-group:focus { + outline: 0 +} + +md-radio-group.md-focused .md-checked ._md-container:before { + left: -8px; + top: -8px; + right: -8px; + bottom: -8px +} + +.md-inline-form md-radio-group { + margin: 18px 0 19px +} + +.md-inline-form md-radio-group md-radio-button { + display: inline-block; + height: 30px; + padding: 2px; + box-sizing: border-box; + margin-top: 0; + margin-bottom: 0 +} + +@media screen and (-ms-high-contrast: active) { + md-radio-button.md-default-theme ._md-on { + background-color: #fff + } +} + +._md-select-menu-container { + position: fixed; + left: 0; + top: 0; + z-index: 90; + opacity: 0; + display: none +} + +._md-select-menu-container:not(._md-clickable) { + pointer-events: none +} + +._md-select-menu-container md-progress-circular { + display: table; + margin: 24px auto !important +} + +._md-select-menu-container._md-active { + display: block; + opacity: 1 +} + +._md-select-menu-container._md-active md-select-menu { + transition: all .4s cubic-bezier(.25, .8, .25, 1); + transition-duration: 150ms +} + +._md-select-menu-container._md-active md-select-menu > * { + opacity: 1; + transition: all .3s cubic-bezier(.55, 0, .55, .2); + transition-duration: 150ms; + transition-delay: 100ms +} + +._md-select-menu-container._md-leave { + opacity: 0; + transition: all .3s cubic-bezier(.55, 0, .55, .2); + transition-duration: 250ms +} + +md-input-container > md-select { + margin: 0; + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 +} + +md-select { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + margin: 20px 0 26px +} + +md-select[disabled] ._md-select-value { + background-position: 0 bottom; + background-size: 4px 1px; + background-repeat: repeat-x; + margin-bottom: -1px +} + +md-select:focus { + outline: 0 +} + +md-select[disabled]:hover { + cursor: default +} + +md-select:not([disabled]):hover { + cursor: pointer +} + +md-select:not([disabled]).ng-invalid.ng-dirty ._md-select-value { + border-bottom: 2px solid; + padding-bottom: 0 +} + +md-select:not([disabled]):focus ._md-select-value { + border-bottom-width: 2px; + border-bottom-style: solid; + padding-bottom: 0 +} + +._md-select-value { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + padding: 2px 2px 1px; + border-bottom-width: 1px; + border-bottom-style: solid; + background-color: transparent; + position: relative; + box-sizing: content-box; + min-width: 64px; + min-height: 26px; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1 +} + +._md-select-value > span:not(._md-select-icon) { + max-width: 100%; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + -webkit-transform: translate3d(0, 2px, 0); + transform: translate3d(0, 2px, 0); + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden +} + +._md-select-value > span:not(._md-select-icon) ._md-text { + display: inline +} + +._md-select-value ._md-select-icon { + display: block; + -webkit-align-items: flex-end; + -ms-flex-align: end; + align-items: flex-end; + text-align: end; + width: 24px; + margin: 0 4px; + -webkit-transform: translate3d(0, 1px, 0); + transform: translate3d(0, 1px, 0) +} + +._md-select-value ._md-select-icon:after { + display: block; + content: '\25BC'; + position: relative; + top: 2px; + speak: none; + -webkit-transform: scaleY(.6) scaleX(1); + transform: scaleY(.6) scaleX(1) +} + +._md-select-value._md-select-placeholder { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-order: 1; + -ms-flex-order: 1; + order: 1; + pointer-events: none; + -webkit-font-smoothing: antialiased; + padding-left: 2px; + z-index: 1 +} + +md-select-menu { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .2), 0 1px 1px 0 rgba(0, 0, 0, .14), 0 2px 1px -1px rgba(0, 0, 0, .12); + max-height: 256px; + min-height: 48px; + overflow-y: hidden; + -webkit-transform-origin: left top; + transform-origin: left top; + -webkit-transform: scale(1); + transform: scale(1) +} + +md-select-menu.md-reverse { + -webkit-flex-direction: column-reverse; + -ms-flex-direction: column-reverse; + flex-direction: column-reverse +} + +md-select-menu:not(._md-overflow) md-content { + padding-top: 8px; + padding-bottom: 8px +} + +[dir=rtl] md-select-menu { + -webkit-transform-origin: right top; + transform-origin: right top +} + +md-select-menu md-content { + min-width: 136px; + min-height: 48px; + max-height: 256px; + overflow-y: auto +} + +md-select-menu > * { + opacity: 0 +} + +md-option { + cursor: pointer; + position: relative; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + width: auto; + transition: background .15s linear; + padding: 0 16px; + height: 48px +} + +md-option[disabled] { + cursor: default +} + +md-option:focus { + outline: 0 +} + +md-option ._md-text { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: auto; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 16px +} + +md-optgroup { + display: block +} + +md-optgroup label { + display: block; + font-size: 14px; + text-transform: uppercase; + padding: 16px; + font-weight: 500 +} + +md-optgroup md-option { + padding-left: 32px; + padding-right: 32px +} + +@media screen and (-ms-high-contrast: active) { + ._md-select-backdrop { + background-color: transparent + } + + md-select-menu { + border: 1px solid #fff + } +} + +md-select-menu[multiple] md-option[md-checkbox-enabled] { + padding-left: 40px; + padding-right: 16px +} + +[dir=rtl] md-select-menu[multiple] md-option[md-checkbox-enabled] { + padding-left: 16px; + padding-right: 40px +} + +md-select-menu[multiple] md-option[md-checkbox-enabled] ._md-container { + position: absolute; + top: 50%; + -webkit-transform: translateY(-50%); + transform: translateY(-50%); + box-sizing: border-box; + display: inline-block; + width: 20px; + height: 20px; + left: 0; + right: auto +} + +[dir=rtl] md-select-menu[multiple] md-option[md-checkbox-enabled] ._md-container { + left: auto; + right: 0 +} + +md-select-menu[multiple] md-option[md-checkbox-enabled] ._md-container:before { + box-sizing: border-box; + background-color: transparent; + border-radius: 50%; + content: ''; + position: absolute; + display: block; + height: auto; + left: 0; + top: 0; + right: 0; + bottom: 0; + transition: all .5s; + width: auto +} + +md-select-menu[multiple] md-option[md-checkbox-enabled] ._md-container:after { + box-sizing: border-box; + content: ''; + position: absolute; + top: -10px; + right: -10px; + bottom: -10px; + left: -10px +} + +md-select-menu[multiple] md-option[md-checkbox-enabled] ._md-container .md-ripple-container { + position: absolute; + display: block; + width: auto; + height: auto; + left: -15px; + top: -15px; + right: -15px; + bottom: -15px +} + +md-select-menu[multiple] md-option[md-checkbox-enabled] ._md-icon { + box-sizing: border-box; + transition: 240ms; + position: absolute; + top: 0; + left: 0; + width: 20px; + height: 20px; + border-width: 2px; + border-style: solid; + border-radius: 2px +} + +md-select-menu[multiple] md-option[md-checkbox-enabled][selected] ._md-icon { + border: none +} + +md-select-menu[multiple] md-option[md-checkbox-enabled][selected] ._md-icon:after { + box-sizing: border-box; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); + position: absolute; + left: 6.67px; + top: 2.22px; + display: table; + width: 6.67px; + height: 13.33px; + border-width: 2px; + border-style: solid; + border-top: 0; + border-left: 0; + content: '' +} + +md-select-menu[multiple] md-option[md-checkbox-enabled][disabled] { + cursor: default +} + +md-select-menu[multiple] md-option[md-checkbox-enabled].md-indeterminate ._md-icon:after { + box-sizing: border-box; + position: absolute; + top: 50%; + left: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + display: table; + width: 12px; + height: 2px; + border-width: 2px; + border-style: solid; + border-top: 0; + border-left: 0; + content: '' +} + +md-select-menu[multiple] md-option[md-checkbox-enabled] ._md-container { + margin-left: 10.67px; + margin-right: auto +} + +[dir=rtl] md-select-menu[multiple] md-option[md-checkbox-enabled] ._md-container { + margin-left: auto; + margin-right: 10.67px +} + +md-sidenav { + box-sizing: border-box; + position: absolute; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + z-index: 60; + width: 320px; + max-width: 320px; + bottom: 0; + overflow: auto; + -webkit-overflow-scrolling: touch +} + +md-sidenav ul { + list-style: none +} + +md-sidenav._md-closed { + display: none +} + +md-sidenav._md-closed-add, md-sidenav._md-closed-remove { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + transition: .2s ease-in all +} + +md-sidenav._md-closed-add._md-closed-add-active, md-sidenav._md-closed-remove._md-closed-remove-active { + transition: all .4s cubic-bezier(.25, .8, .25, 1) +} + +md-sidenav._md-locked-open, md-sidenav._md-locked-open-add, md-sidenav._md-locked-open-remove, md-sidenav._md-locked-open-remove._md-closed, md-sidenav._md-locked-open._md-closed, md-sidenav._md-locked-open._md-closed.md-sidenav-left, md-sidenav._md-locked-open._md-closed.md-sidenav-right { + position: static; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0) +} + +md-sidenav._md-locked-open-remove-active { + transition: width .3s cubic-bezier(.55, 0, .55, .2), min-width .3s cubic-bezier(.55, 0, .55, .2); + width: 0; + min-width: 0 +} + +md-sidenav._md-closed._md-locked-open-add { + width: 0; + min-width: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0) +} + +md-sidenav._md-closed._md-locked-open-add-active { + transition: width .3s cubic-bezier(.55, 0, .55, .2), min-width .3s cubic-bezier(.55, 0, .55, .2); + width: 320px; + min-width: 320px; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0) +} + +._md-sidenav-backdrop._md-locked-open { + display: none +} + +.md-sidenav-left, md-sidenav { + left: 0; + top: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0) +} + +.md-sidenav-left._md-closed, md-sidenav._md-closed { + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0) +} + +.md-sidenav-right { + left: 100%; + top: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0) +} + +.md-sidenav-right._md-closed { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0) +} + +@media (min-width: 600px) { + md-sidenav { + max-width: 400px + } +} + +@media (max-width: 456px) { + md-sidenav { + width: calc(100% - 56px); + min-width: calc(100% - 56px); + max-width: calc(100% - 56px) + } +} + +@media screen and (-ms-high-contrast: active) { + .md-sidenav-left, md-sidenav { + border-right: 1px solid #fff + } + + .md-sidenav-right { + border-left: 1px solid #fff + } +} + +._md-sticky-clone { + z-index: 2; + top: 0; + left: 0; + right: 0; + position: absolute !important; + -webkit-transform: translate3d(-9999px, -9999px, 0); + transform: translate3d(-9999px, -9999px, 0) +} + +._md-sticky-clone[sticky-state=active] { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0) +} + +._md-sticky-clone[sticky-state=active]:not(.md-sticky-no-effect) ._md-subheader-inner { + -webkit-animation: subheaderStickyHoverIn .3s ease-out both; + animation: subheaderStickyHoverIn .3s ease-out both +} + +@-webkit-keyframes sliderFocusThumb { + 0% { + -webkit-transform: scale(.7); + transform: scale(.7) + } + 30% { + -webkit-transform: scale(1); + transform: scale(1) + } + 100% { + -webkit-transform: scale(.7); + transform: scale(.7) + } +} + +@keyframes sliderFocusThumb { + 0% { + -webkit-transform: scale(.7); + transform: scale(.7) + } + 30% { + -webkit-transform: scale(1); + transform: scale(1) + } + 100% { + -webkit-transform: scale(.7); + transform: scale(.7) + } +} + +@-webkit-keyframes sliderDiscreteFocusThumb { + 0% { + -webkit-transform: scale(.7); + transform: scale(.7) + } + 50% { + -webkit-transform: scale(.8); + transform: scale(.8) + } + 100% { + -webkit-transform: scale(0); + transform: scale(0) + } +} + +@keyframes sliderDiscreteFocusThumb { + 0% { + -webkit-transform: scale(.7); + transform: scale(.7) + } + 50% { + -webkit-transform: scale(.8); + transform: scale(.8) + } + 100% { + -webkit-transform: scale(0); + transform: scale(0) + } +} + +@-webkit-keyframes sliderDiscreteFocusRing { + 0% { + -webkit-transform: scale(.7); + transform: scale(.7); + opacity: 0 + } + 50% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1 + } + 100% { + -webkit-transform: scale(0); + transform: scale(0) + } +} + +@keyframes sliderDiscreteFocusRing { + 0% { + -webkit-transform: scale(.7); + transform: scale(.7); + opacity: 0 + } + 50% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1 + } + 100% { + -webkit-transform: scale(0); + transform: scale(0) + } +} + +md-slider { + height: 48px; + min-width: 128px; + position: relative; + margin-left: 4px; + margin-right: 4px; + padding: 0; + display: block; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row +} + +md-slider *, md-slider :after { + box-sizing: border-box +} + +md-slider ._md-slider-wrapper { + outline: 0; + width: 100%; + height: 100% +} + +md-slider ._md-slider-content { + position: relative +} + +md-slider ._md-track-container { + width: 100%; + position: absolute; + top: 23px; + height: 2px +} + +md-slider ._md-track { + position: absolute; + left: 0; + right: 0; + height: 100% +} + +md-slider ._md-track-fill { + transition: all .4s cubic-bezier(.25, .8, .25, 1); + transition-property: width, height +} + +md-slider ._md-track-ticks { + position: absolute; + left: 0; + right: 0; + height: 100% +} + +md-slider ._md-track-ticks canvas { + width: 100%; + height: 100% +} + +md-slider ._md-thumb-container { + position: absolute; + left: 0; + top: 50%; + -webkit-transform: translate3d(-50%, -50%, 0); + transform: translate3d(-50%, -50%, 0); + transition: all .4s cubic-bezier(.25, .8, .25, 1); + transition-property: left, bottom +} + +md-slider ._md-thumb { + z-index: 1; + position: absolute; + left: -10px; + top: 14px; + width: 20px; + height: 20px; + border-radius: 20px; + -webkit-transform: scale(.7); + transform: scale(.7); + transition: all .4s cubic-bezier(.25, .8, .25, 1) +} + +md-slider ._md-thumb:after { + content: ''; + position: absolute; + width: 20px; + height: 20px; + border-radius: 20px; + border-width: 3px; + border-style: solid; + transition: inherit +} + +md-slider ._md-sign { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + position: absolute; + left: -14px; + top: -17px; + width: 28px; + height: 28px; + border-radius: 28px; + -webkit-transform: scale(.4) translate3d(0, 67.5px, 0); + transform: scale(.4) translate3d(0, 67.5px, 0); + transition: all .3s cubic-bezier(.35, 0, .25, 1) +} + +md-slider ._md-sign:after { + position: absolute; + content: ''; + left: 0; + border-radius: 16px; + top: 19px; + border-left: 14px solid transparent; + border-right: 14px solid transparent; + border-top-width: 16px; + border-top-style: solid; + opacity: 0; + -webkit-transform: translate3d(0, -8px, 0); + transform: translate3d(0, -8px, 0); + transition: all .2s cubic-bezier(.35, 0, .25, 1) +} + +md-slider ._md-sign ._md-thumb-text { + z-index: 1; + font-size: 12px; + font-weight: 700 +} + +md-slider ._md-focus-ring { + position: absolute; + left: -17px; + top: 7px; + width: 34px; + height: 34px; + border-radius: 34px; + -webkit-transform: scale(.7); + transform: scale(.7); + opacity: 0; + transition: all .35s cubic-bezier(.35, 0, .25, 1) +} + +md-slider ._md-disabled-thumb { + position: absolute; + left: -14px; + top: 10px; + width: 28px; + height: 28px; + border-radius: 28px; + -webkit-transform: scale(.5); + transform: scale(.5); + border-width: 4px; + border-style: solid; + display: none +} + +md-slider._md-min ._md-sign { + opacity: 0 +} + +md-slider:focus { + outline: 0 +} + +md-slider._md-dragging ._md-thumb-container, md-slider._md-dragging ._md-track-fill { + transition: none +} + +md-slider:not([md-discrete]) ._md-sign, md-slider:not([md-discrete]) ._md-track-ticks { + display: none +} + +md-slider:not([md-discrete]):not([disabled]) ._md-slider-wrapper ._md-thumb:hover { + -webkit-transform: scale(.8); + transform: scale(.8) +} + +md-slider:not([md-discrete]):not([disabled]) ._md-slider-wrapper.md-focused ._md-focus-ring { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1 +} + +md-slider:not([md-discrete]):not([disabled]) ._md-slider-wrapper.md-focused ._md-thumb { + -webkit-animation: sliderFocusThumb .7s cubic-bezier(.35, 0, .25, 1); + animation: sliderFocusThumb .7s cubic-bezier(.35, 0, .25, 1) +} + +md-slider:not([md-discrete]):not([disabled])._md-active ._md-slider-wrapper ._md-thumb { + -webkit-transform: scale(1); + transform: scale(1) +} + +md-slider[md-discrete]:not([disabled]) ._md-slider-wrapper.md-focused ._md-focus-ring { + -webkit-transform: scale(0); + transform: scale(0); + -webkit-animation: sliderDiscreteFocusRing .5s cubic-bezier(.35, 0, .25, 1); + animation: sliderDiscreteFocusRing .5s cubic-bezier(.35, 0, .25, 1) +} + +md-slider[md-discrete]:not([disabled]) ._md-slider-wrapper.md-focused ._md-thumb { + -webkit-animation: sliderDiscreteFocusThumb .5s cubic-bezier(.35, 0, .25, 1); + animation: sliderDiscreteFocusThumb .5s cubic-bezier(.35, 0, .25, 1) +} + +md-slider[md-discrete]:not([disabled]) ._md-slider-wrapper.md-focused ._md-thumb, md-slider[md-discrete]:not([disabled])._md-active ._md-thumb { + -webkit-transform: scale(0); + transform: scale(0) +} + +md-slider[md-discrete]:not([disabled]) ._md-slider-wrapper.md-focused ._md-sign, md-slider[md-discrete]:not([disabled]) ._md-slider-wrapper.md-focused ._md-sign:after, md-slider[md-discrete]:not([disabled])._md-active ._md-sign, md-slider[md-discrete]:not([disabled])._md-active ._md-sign:after { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0) scale(1); + transform: translate3d(0, 0, 0) scale(1) +} + +md-slider[md-discrete][disabled][readonly] ._md-thumb { + -webkit-transform: scale(0); + transform: scale(0) +} + +md-slider[md-discrete][disabled][readonly] ._md-sign, md-slider[md-discrete][disabled][readonly] ._md-sign:after { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0) scale(1); + transform: translate3d(0, 0, 0) scale(1) +} + +md-slider[disabled] ._md-track-fill { + display: none +} + +md-slider[disabled] ._md-track-ticks, md-slider[disabled]:not([readonly]) ._md-sign { + opacity: 0 +} + +md-slider[disabled] ._md-thumb { + -webkit-transform: scale(.5); + transform: scale(.5) +} + +md-slider[disabled] ._md-disabled-thumb { + display: block +} + +md-slider[md-vertical] { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + min-height: 128px; + min-width: 0 +} + +md-slider[md-vertical] ._md-slider-wrapper { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + padding-top: 12px; + padding-bottom: 12px; + width: 48px; + -webkit-align-self: center; + -ms-flex-item-align: center; + align-self: center; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center +} + +md-slider[md-vertical] ._md-track-container { + height: 100%; + width: 2px; + top: 0; + left: calc(50% - (2px / 2)) +} + +md-slider[md-vertical] ._md-thumb-container { + top: auto; + margin-bottom: 23px; + left: calc(50% - 1px); + bottom: 0 +} + +md-slider[md-vertical] ._md-thumb-container ._md-thumb:after { + left: 1px +} + +md-slider[md-vertical] ._md-thumb-container ._md-focus-ring { + left: -16px +} + +md-slider[md-vertical] ._md-track-fill { + bottom: 0 +} + +md-slider[md-vertical][md-discrete] ._md-sign { + left: -40px; + top: 9.5px; + -webkit-transform: scale(.4) translate3d(67.5px, 0, 0); + transform: scale(.4) translate3d(67.5px, 0, 0) +} + +md-slider[md-vertical][md-discrete] ._md-sign:after { + top: 9.5px; + left: 19px; + border-top: 14px solid transparent; + border-right: 0; + border-bottom: 14px solid transparent; + border-left-width: 16px; + border-left-style: solid; + opacity: 0; + -webkit-transform: translate3d(0, -8px, 0); + transform: translate3d(0, -8px, 0); + transition: all .2s ease-in-out +} + +md-slider[md-vertical][md-discrete] ._md-sign ._md-thumb-text { + z-index: 1; + font-size: 12px; + font-weight: 700 +} + +md-slider[md-vertical][md-discrete] .md-focused ._md-sign:after, md-slider[md-vertical][md-discrete]._md-active ._md-sign:after, md-slider[md-vertical][md-discrete][disabled][readonly] ._md-sign:after { + top: 0 +} + +md-slider[md-vertical][disabled][readonly] ._md-thumb { + -webkit-transform: scale(0); + transform: scale(0) +} + +md-slider[md-vertical][disabled][readonly] ._md-sign, md-slider[md-vertical][disabled][readonly] ._md-sign:after { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0) scale(1); + transform: translate3d(0, 0, 0) scale(1) +} + +md-slider-container { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row +} + +md-slider-container > :first-child:not(md-slider), md-slider-container > :last-child:not(md-slider) { + min-width: 25px; + max-width: 42px; + height: 25px; + transition: all .4s cubic-bezier(.25, .8, .25, 1); + transition-property: color, max-width +} + +md-slider-container > :first-child:not(md-slider) { + margin-right: 16px +} + +md-slider-container > :last-child:not(md-slider) { + margin-left: 16px +} + +md-slider-container[md-vertical] { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column +} + +md-slider-container[md-vertical] > :first-child:not(md-slider), md-slider-container[md-vertical] > :last-child:not(md-slider) { + margin-right: 0; + margin-left: 0; + text-align: center +} + +md-slider-container md-input-container input[type=number] { + text-align: center; + padding-left: 15px; + height: 50px; + margin-top: -25px +} + +@media screen and (-ms-high-contrast: active) { + md-slider.md-default-theme ._md-track { + border-bottom: 1px solid #fff + } +} + +@-webkit-keyframes subheaderStickyHoverIn { + 0% { + box-shadow: 0 0 0 0 transparent + } + 100% { + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .16) + } +} + +@keyframes subheaderStickyHoverIn { + 0% { + box-shadow: 0 0 0 0 transparent + } + 100% { + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .16) + } +} + +@-webkit-keyframes subheaderStickyHoverOut { + 0% { + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .16) + } + 100% { + box-shadow: 0 0 0 0 transparent + } +} + +@keyframes subheaderStickyHoverOut { + 0% { + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .16) + } + 100% { + box-shadow: 0 0 0 0 transparent + } +} + +._md-subheader-wrapper:not(.md-sticky-no-effect) { + transition: .2s ease-out margin +} + +._md-subheader-wrapper:not(.md-sticky-no-effect) .md-subheader { + margin: 0 +} + +._md-subheader-wrapper:not(.md-sticky-no-effect).md-sticky-clone { + z-index: 2 +} + +._md-subheader-wrapper:not(.md-sticky-no-effect)[sticky-state=active] { + margin-top: -2px +} + +._md-subheader-wrapper:not(.md-sticky-no-effect):not(.md-sticky-clone)[sticky-prev-state=active] ._md-subheader-inner:after { + -webkit-animation: subheaderStickyHoverOut .3s ease-out both; + animation: subheaderStickyHoverOut .3s ease-out both +} + +.md-subheader { + display: block; + font-size: 14px; + font-weight: 500; + line-height: 1em; + margin: 0; + position: relative +} + +.md-subheader ._md-subheader-inner { + display: block; + padding: 16px +} + +.md-subheader ._md-subheader-content { + display: block; + z-index: 1; + position: relative +} + +.md-inline-form md-switch { + margin-top: 18px; + margin-bottom: 19px +} + +md-switch { + margin: 16px; + margin-left: inherit; + white-space: nowrap; + cursor: pointer; + outline: 0; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + height: 30px; + line-height: 28px; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + display: -webkit-flex; + display: -ms-flexbox; + display: flex +} + +[dir=rtl] md-switch { + margin-left: 16px; + margin-right: inherit +} + +md-switch:last-of-type { + margin-left: inherit; + margin-right: 0 +} + +[dir=rtl] md-switch:last-of-type { + margin-left: 0; + margin-right: inherit +} + +md-switch[disabled], md-switch[disabled] ._md-container { + cursor: default +} + +md-switch ._md-container { + cursor: -webkit-grab; + cursor: grab; + width: 36px; + height: 24px; + position: relative; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + margin-right: 8px; + float: left +} + +[dir=rtl] md-switch ._md-container { + margin-right: auto; + margin-left: 8px +} + +md-switch:not([disabled]) ._md-dragging, md-switch:not([disabled])._md-dragging ._md-container { + cursor: -webkit-grabbing; + cursor: grabbing +} + +md-switch.md-focused:not([disabled]) ._md-thumb:before { + left: -8px; + top: -8px; + right: -8px; + bottom: -8px +} + +md-switch.md-focused:not([disabled]):not(.md-checked) ._md-thumb:before { + background-color: rgba(0, 0, 0, .12) +} + +md-switch ._md-label { + border-color: transparent; + border-width: 0; + float: left +} + +md-switch ._md-bar { + left: 1px; + width: 34px; + top: 5px; + height: 14px; + border-radius: 8px; + position: absolute +} + +md-switch ._md-thumb-container { + top: 2px; + left: 0; + width: 16px; + position: absolute; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + z-index: 1 +} + +md-switch.md-checked ._md-thumb-container { + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0) +} + +md-switch ._md-thumb { + position: absolute; + margin: 0; + left: 0; + top: 0; + outline: 0; + height: 20px; + width: 20px; + border-radius: 50%; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .2), 0 1px 1px 0 rgba(0, 0, 0, .14), 0 2px 1px -1px rgba(0, 0, 0, .12) +} + +md-switch ._md-thumb:before { + background-color: transparent; + border-radius: 50%; + content: ''; + position: absolute; + display: block; + height: auto; + left: 0; + top: 0; + right: 0; + bottom: 0; + transition: all .5s; + width: auto +} + +md-switch ._md-thumb .md-ripple-container { + position: absolute; + display: block; + width: auto; + height: auto; + left: -20px; + top: -20px; + right: -20px; + bottom: -20px +} + +md-switch:not(._md-dragging) ._md-bar, md-switch:not(._md-dragging) ._md-thumb, md-switch:not(._md-dragging) ._md-thumb-container { + transition: all .08s linear; + transition-property: -webkit-transform, background-color; + transition-property: transform, background-color +} + +md-switch:not(._md-dragging) ._md-bar, md-switch:not(._md-dragging) ._md-thumb { + transition-delay: .05s +} + +@media screen and (-ms-high-contrast: active) { + md-switch.md-default-theme ._md-bar { + background-color: #666 + } + + md-switch.md-default-theme.md-checked ._md-bar { + background-color: #9E9E9E + } + + md-switch.md-default-theme ._md-thumb { + background-color: #fff + } +} + +@-webkit-keyframes md-tab-content-hide { + 0%, 50% { + opacity: 1 + } + 100% { + opacity: 0 + } +} + +@keyframes md-tab-content-hide { + 0%, 50% { + opacity: 1 + } + 100% { + opacity: 0 + } +} + +md-tab-data { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: -1; + opacity: 0 +} + +md-tabs { + display: block; + margin: 0; + border-radius: 2px; + overflow: hidden; + position: relative; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0 +} + +md-tabs:not(.md-no-tab-content):not(.md-dynamic-height) { + min-height: 248px +} + +md-tabs[md-align-tabs=bottom] { + padding-bottom: 48px +} + +md-tabs[md-align-tabs=bottom] md-tabs-wrapper { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 48px; + z-index: 2 +} + +md-tabs[md-align-tabs=bottom] md-tabs-content-wrapper { + top: 0; + bottom: 48px +} + +md-tabs.md-dynamic-height md-tabs-content-wrapper { + min-height: 0; + position: relative; + top: auto; + left: auto; + right: auto; + bottom: auto; + overflow: visible +} + +md-tabs.md-dynamic-height md-tab-content.md-active { + position: relative +} + +md-tabs[md-border-bottom] md-tabs-wrapper { + border-width: 0 0 1px; + border-style: solid +} + +md-tabs[md-border-bottom]:not(.md-dynamic-height) md-tabs-content-wrapper { + top: 49px +} + +md-tabs-wrapper { + display: block; + position: relative; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0) +} + +md-tabs-wrapper md-next-button, md-tabs-wrapper md-prev-button { + height: 100%; + width: 32px; + position: absolute; + top: 50%; + -webkit-transform: translateY(-50%); + transform: translateY(-50%); + line-height: 1em; + z-index: 2; + cursor: pointer; + font-size: 16px; + background: center center no-repeat; + transition: all .5s cubic-bezier(.35, 0, .25, 1) +} + +md-tabs-wrapper md-next-button:focus, md-tabs-wrapper md-prev-button:focus { + outline: 0 +} + +md-tabs-wrapper md-next-button.md-disabled, md-tabs-wrapper md-prev-button.md-disabled { + opacity: .25; + cursor: default +} + +md-tabs-wrapper md-next-button.ng-leave, md-tabs-wrapper md-prev-button.ng-leave { + transition: none +} + +md-tabs-wrapper md-next-button md-icon, md-tabs-wrapper md-prev-button md-icon { + position: absolute; + top: 50%; + left: 50%; + -webkit-transform: translate3d(-50%, -50%, 0); + transform: translate3d(-50%, -50%, 0) +} + +md-tabs-wrapper md-prev-button { + left: 0; + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE3LjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPiA8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPiA8c3ZnIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjQgMjQiIHhtbDpzcGFjZT0icHJlc2VydmUiPiA8ZyBpZD0iSGVhZGVyIj4gPGc+IDxyZWN0IHg9Ii02MTgiIHk9Ii0xMjA4IiBmaWxsPSJub25lIiB3aWR0aD0iMTQwMCIgaGVpZ2h0PSIzNjAwIi8+IDwvZz4gPC9nPiA8ZyBpZD0iTGFiZWwiPiA8L2c+IDxnIGlkPSJJY29uIj4gPGc+IDxwb2x5Z29uIHBvaW50cz0iMTUuNCw3LjQgMTQsNiA4LDEyIDE0LDE4IDE1LjQsMTYuNiAxMC44LDEyIAkJIiBzdHlsZT0iZmlsbDp3aGl0ZTsiLz4gPHJlY3QgZmlsbD0ibm9uZSIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ii8+IDwvZz4gPC9nPiA8ZyBpZD0iR3JpZCIgZGlzcGxheT0ibm9uZSI+IDxnIGRpc3BsYXk9ImlubGluZSI+IDwvZz4gPC9nPiA8L3N2Zz4NCg==) +} + +[dir=rtl] md-tabs-wrapper md-prev-button { + left: auto; + right: 0 +} + +md-tabs-wrapper md-next-button { + right: 0; + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE3LjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPiA8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPiA8c3ZnIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjQgMjQiIHhtbDpzcGFjZT0icHJlc2VydmUiPiA8ZyBpZD0iSGVhZGVyIj4gPGc+IDxyZWN0IHg9Ii02MTgiIHk9Ii0xMzM2IiBmaWxsPSJub25lIiB3aWR0aD0iMTQwMCIgaGVpZ2h0PSIzNjAwIi8+IDwvZz4gPC9nPiA8ZyBpZD0iTGFiZWwiPiA8L2c+IDxnIGlkPSJJY29uIj4gPGc+IDxwb2x5Z29uIHBvaW50cz0iMTAsNiA4LjYsNy40IDEzLjIsMTIgOC42LDE2LjYgMTAsMTggMTYsMTIgCQkiIHN0eWxlPSJmaWxsOndoaXRlOyIvPiA8cmVjdCBmaWxsPSJub25lIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiLz4gPC9nPiA8L2c+IDxnIGlkPSJHcmlkIiBkaXNwbGF5PSJub25lIj4gPGcgZGlzcGxheT0iaW5saW5lIj4gPC9nPiA8L2c+IDwvc3ZnPg0K) +} + +[dir=rtl] md-tabs-wrapper md-next-button { + right: auto; + left: 0 +} + +md-tabs-wrapper md-next-button md-icon { + -webkit-transform: translate3d(-50%, -50%, 0) rotate(180deg); + transform: translate3d(-50%, -50%, 0) rotate(180deg) +} + +md-tabs-wrapper.md-stretch-tabs md-pagination-wrapper { + width: 100%; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row +} + +md-tabs-wrapper.md-stretch-tabs md-pagination-wrapper md-tab-item { + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1 +} + +md-tabs-canvas { + position: relative; + overflow: hidden; + display: block; + height: 48px +} + +md-tabs-canvas:after { + content: ''; + display: table; + clear: both +} + +md-tabs-canvas .md-dummy-wrapper { + position: absolute; + top: 0; + left: 0 +} + +[dir=rtl] md-tabs-canvas .md-dummy-wrapper { + left: auto; + right: 0 +} + +md-tabs-canvas.md-paginated { + margin: 0 32px +} + +md-tabs-canvas.md-center-tabs { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + text-align: center +} + +md-tabs-canvas.md-center-tabs .md-tab { + float: none; + display: inline-block +} + +md-pagination-wrapper { + height: 48px; + display: block; + transition: -webkit-transform .5s cubic-bezier(.35, 0, .25, 1); + transition: transform .5s cubic-bezier(.35, 0, .25, 1); + position: absolute; + width: 999999px; + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0) +} + +md-pagination-wrapper:after { + content: ''; + display: table; + clear: both +} + +[dir=rtl] md-pagination-wrapper { + left: auto; + right: 0 +} + +md-pagination-wrapper.md-center-tabs { + position: relative; + width: initial; + margin: 0 auto +} + +md-tabs-content-wrapper { + display: block; + position: absolute; + top: 48px; + left: 0; + right: 0; + bottom: 0; + overflow: hidden +} + +md-tab-content { + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + transition: -webkit-transform .5s cubic-bezier(.35, 0, .25, 1); + transition: transform .5s cubic-bezier(.35, 0, .25, 1); + overflow: auto; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0) +} + +md-tab-content.md-no-scroll { + bottom: auto; + overflow: hidden +} + +md-tab-content.md-no-transition, md-tab-content.ng-leave { + transition: none +} + +md-tab-content.md-left:not(.md-active) { + -webkit-transform: translateX(-100%); + transform: translateX(-100%); + -webkit-animation: 1s md-tab-content-hide; + animation: 1s md-tab-content-hide; + opacity: 0 +} + +[dir=rtl] md-tab-content.md-left:not(.md-active) { + -webkit-transform: translateX(100%); + transform: translateX(100%) +} + +md-tab-content.md-left:not(.md-active) * { + transition: visibility 0s linear; + transition-delay: .5s; + visibility: hidden +} + +md-tab-content.md-right:not(.md-active) { + -webkit-transform: translateX(100%); + transform: translateX(100%); + -webkit-animation: 1s md-tab-content-hide; + animation: 1s md-tab-content-hide; + opacity: 0 +} + +[dir=rtl] md-tab-content.md-right:not(.md-active) { + -webkit-transform: translateX(-100%); + transform: translateX(-100%) +} + +md-tab-content.md-right:not(.md-active) * { + transition: visibility 0s linear; + transition-delay: .5s; + visibility: hidden +} + +md-tab-content > div.ng-leave { + -webkit-animation: 1s md-tab-content-hide; + animation: 1s md-tab-content-hide +} + +md-ink-bar { + position: absolute; + left: auto; + right: auto; + bottom: 0; + height: 2px +} + +md-ink-bar.md-left { + transition: left .125s cubic-bezier(.35, 0, .25, 1), right .25s cubic-bezier(.35, 0, .25, 1) +} + +md-ink-bar.md-right { + transition: left .25s cubic-bezier(.35, 0, .25, 1), right .125s cubic-bezier(.35, 0, .25, 1) +} + +md-tab { + position: absolute; + z-index: -1; + left: -9999px +} + +.md-tab { + font-size: 14px; + text-align: center; + line-height: 24px; + padding: 12px 24px; + transition: background-color .35s cubic-bezier(.35, 0, .25, 1); + cursor: pointer; + white-space: nowrap; + position: relative; + text-transform: uppercase; + float: left; + font-weight: 500; + box-sizing: border-box; + overflow: hidden; + text-overflow: ellipsis +} + +[dir=rtl] .md-tab { + float: right +} + +.md-tab.md-focused { + box-shadow: none; + outline: 0 +} + +.md-tab.md-active { + cursor: default +} + +.md-tab.md-disabled { + pointer-events: none; + -ms-touch-action: pan-y; + touch-action: pan-y; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-user-drag: none; + opacity: .5; + cursor: default +} + +.md-tab.ng-leave { + transition: none +} + +md-toolbar + md-tabs { + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.md-toast-text { + padding: 0 6px +} + +md-toast { + position: absolute; + z-index: 105; + box-sizing: border-box; + cursor: default; + overflow: hidden; + padding: 8px; + opacity: 1; + transition: all .4s cubic-bezier(.25, .8, .25, 1) +} + +md-toast .md-toast-content { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + height: 0; + max-height: 168px; + max-width: 100%; + min-height: 48px; + padding: 0 18px; + box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .26); + border-radius: 2px; + font-size: 14px; + overflow: hidden; + -webkit-transform: translate3d(0, 0, 0) rotateZ(0deg); + transform: translate3d(0, 0, 0) rotateZ(0deg); + transition: all .4s cubic-bezier(.25, .8, .25, 1); + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start +} + +[dir=rtl] md-toast .md-toast-content { + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end +} + +md-toast.md-capsule, md-toast.md-capsule .md-toast-content { + border-radius: 24px +} + +md-toast.ng-leave-active .md-toast-content { + transition: all .3s cubic-bezier(.55, 0, .55, .2) +} + +md-toast._md-swipedown .md-toast-content, md-toast._md-swipeleft .md-toast-content, md-toast._md-swiperight .md-toast-content, md-toast._md-swipeup .md-toast-content { + transition: all .4s cubic-bezier(.25, .8, .25, 1) +} + +md-toast.ng-enter { + opacity: 0 +} + +md-toast.ng-enter .md-toast-content { + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0) +} + +md-toast.ng-enter._md-top .md-toast-content { + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0) +} + +md-toast.ng-enter.ng-enter-active { + opacity: 1 +} + +md-toast.ng-enter.ng-enter-active .md-toast-content { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0) +} + +md-toast.ng-leave.ng-leave-active .md-toast-content { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0) +} + +md-toast.ng-leave.ng-leave-active._md-swipeup .md-toast-content { + -webkit-transform: translate3d(0, -50%, 0); + transform: translate3d(0, -50%, 0) +} + +md-toast.ng-leave.ng-leave-active._md-swipedown .md-toast-content { + -webkit-transform: translate3d(0, 50%, 0); + transform: translate3d(0, 50%, 0) +} + +md-toast.ng-leave.ng-leave-active._md-top .md-toast-content { + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0) +} + +md-toast .md-action { + line-height: 19px; + margin-left: 24px; + margin-right: 0; + cursor: pointer; + text-transform: uppercase; + float: right +} + +md-toast .md-button { + min-width: 0; + margin-right: 0; + margin-left: 12px +} + +[dir=rtl] md-toast .md-button { + margin-right: 12px; + margin-left: 0 +} + +@media (max-width: 959px) { + md-toast { + left: 0; + right: 0; + width: 100%; + max-width: 100%; + min-width: 0; + border-radius: 0; + bottom: 0; + padding: 0 + } + + md-toast.ng-leave.ng-leave-active._md-swipeup .md-toast-content { + -webkit-transform: translate3d(0, -50%, 0); + transform: translate3d(0, -50%, 0) + } + + md-toast.ng-leave.ng-leave-active._md-swipedown .md-toast-content { + -webkit-transform: translate3d(0, 50%, 0); + transform: translate3d(0, 50%, 0) + } +} + +@media (min-width: 960px) { + md-toast { + min-width: 304px + } + + md-toast._md-bottom { + bottom: 0 + } + + md-toast._md-left { + left: 0 + } + + md-toast._md-right { + right: 0 + } + + md-toast._md-top { + top: 0 + } + + md-toast.ng-leave.ng-leave-active._md-swipeleft .md-toast-content { + -webkit-transform: translate3d(-50%, 0, 0); + transform: translate3d(-50%, 0, 0) + } + + md-toast.ng-leave.ng-leave-active._md-swiperight .md-toast-content { + -webkit-transform: translate3d(50%, 0, 0); + transform: translate3d(50%, 0, 0) + } +} + +@media (min-width: 1920px) { + md-toast .md-toast-content { + max-width: 568px + } +} + +@media screen and (-ms-high-contrast: active) { + md-toast { + border: 1px solid #fff + } +} + +._md-toast-animating { + overflow: hidden !important +} + +md-toolbar { + box-sizing: border-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + position: relative; + z-index: 2; + font-size: 20px; + min-height: 64px; + width: 100%; + transition: all .5s cubic-bezier(.35, 0, .25, 1); + transition-property: background-color, fill, color +} + +md-toolbar.md-whiteframe-z1-add, md-toolbar.md-whiteframe-z1-remove { + transition: box-shadow .5s linear +} + +md-toolbar md-toolbar-filler { + width: 72px +} + +md-toolbar *, md-toolbar :after, md-toolbar :before { + box-sizing: border-box +} + +md-toolbar.md-tall { + height: 128px; + min-height: 128px; + max-height: 128px +} + +md-toolbar.md-medium-tall { + height: 88px; + min-height: 88px; + max-height: 88px +} + +md-toolbar.md-medium-tall .md-toolbar-tools { + height: 48px; + min-height: 48px; + max-height: 48px +} + +md-toolbar > .md-indent { + margin-left: 64px +} + +[dir=rtl] md-toolbar > .md-indent { + margin-left: auto; + margin-right: 64px +} + +md-toolbar ~ md-content > md-list { + padding: 0 +} + +md-toolbar ~ md-content > md-list md-list-item:last-child md-divider { + display: none +} + +.md-toolbar-tools { + font-size: 20px; + letter-spacing: .005em; + box-sizing: border-box; + font-weight: 400; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + width: 100%; + height: 64px; + max-height: 64px; + padding: 0 16px; + margin: 0 +} + +.md-toolbar-tools h1, .md-toolbar-tools h2, .md-toolbar-tools h3 { + font-size: inherit; + font-weight: inherit; + margin: inherit +} + +.md-toolbar-tools a { + color: inherit; + text-decoration: none +} + +.md-toolbar-tools .fill-height { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center +} + +.md-toolbar-tools .md-button { + margin-top: 0; + margin-bottom: 0 +} + +.md-toolbar-tools .md-button, .md-toolbar-tools .md-button.md-icon-button md-icon { + transition: all .5s cubic-bezier(.35, 0, .25, 1); + transition-property: background-color, fill, color +} + +.md-toolbar-tools > .md-button:first-child { + margin-left: -8px +} + +[dir=rtl] .md-toolbar-tools > .md-button:first-child { + margin-left: auto; + margin-right: -8px +} + +.md-toolbar-tools > .md-button:last-child { + margin-right: -8px +} + +[dir=rtl] .md-toolbar-tools > .md-button:last-child { + margin-right: auto; + margin-left: -8px +} + +.md-toolbar-tools > md-menu:last-child { + margin-right: -8px +} + +[dir=rtl] .md-toolbar-tools > md-menu:last-child { + margin-right: auto; + margin-left: -8px +} + +.md-toolbar-tools > md-menu:last-child > .md-button { + margin-right: 0 +} + +[dir=rtl] .md-toolbar-tools > md-menu:last-child > .md-button { + margin-right: auto; + margin-left: 0 +} + +@media screen and (-ms-high-contrast: active) { + .md-toolbar-tools { + border-bottom: 1px solid #fff + } +} + +@media (min-width: 0) and (max-width: 959px) and (orientation: portrait) { + md-toolbar { + min-height: 56px + } + + .md-toolbar-tools { + height: 56px; + max-height: 56px + } +} + +@media (min-width: 0) and (max-width: 959px) and (orientation: landscape) { + md-toolbar { + min-height: 48px + } + + .md-toolbar-tools { + height: 48px; + max-height: 48px + } +} + +.md-virtual-repeat-container { + box-sizing: border-box; + display: block; + margin: 0; + overflow: hidden; + padding: 0; + position: relative +} + +.md-virtual-repeat-container .md-virtual-repeat-scroller { + bottom: 0; + box-sizing: border-box; + left: 0; + margin: 0; + overflow-x: hidden; + padding: 0; + position: absolute; + right: 0; + top: 0 +} + +.md-virtual-repeat-container .md-virtual-repeat-sizer { + box-sizing: border-box; + height: 1px; + display: block; + margin: 0; + padding: 0; + width: 1px +} + +.md-virtual-repeat-container .md-virtual-repeat-offsetter { + box-sizing: border-box; + left: 0; + margin: 0; + padding: 0; + position: absolute; + right: 0; + top: 0 +} + +.md-virtual-repeat-container.md-orient-horizontal .md-virtual-repeat-scroller { + overflow-x: auto; + overflow-y: hidden +} + +.md-virtual-repeat-container.md-orient-horizontal .md-virtual-repeat-offsetter { + bottom: 16px; + right: auto; + white-space: nowrap +} + +[dir=rtl] .md-virtual-repeat-container.md-orient-horizontal .md-virtual-repeat-offsetter { + right: auto; + left: auto +} + +md-tooltip { + position: absolute; + z-index: 100; + overflow: hidden; + pointer-events: none; + border-radius: 4px; + font-weight: 500; + font-size: 14px +} + +@media (min-width: 960px) { + md-tooltip { + font-size: 10px + } +} + +md-tooltip ._md-content { + position: relative; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + -webkit-transform-origin: center top; + transform-origin: center top; + -webkit-transform: scale(0); + transform: scale(0); + opacity: 0; + height: 32px; + line-height: 32px; + padding-left: 16px; + padding-right: 16px +} + +@media (min-width: 960px) { + md-tooltip ._md-content { + height: 22px; + line-height: 22px; + padding-left: 8px; + padding-right: 8px + } +} + +md-tooltip ._md-content._md-show-add { + transition: all .4s cubic-bezier(.25, .8, .25, 1); + transition-duration: .2s; + -webkit-transform: scale(0); + transform: scale(0); + opacity: 0 +} + +md-tooltip ._md-content._md-show, md-tooltip ._md-content._md-show-add-active { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1; + -webkit-transform-origin: center top; + transform-origin: center top +} + +md-tooltip ._md-content._md-show-remove { + transition: all .4s cubic-bezier(.25, .8, .25, 1); + transition-duration: .2s +} + +md-tooltip ._md-content._md-show-remove._md-show-remove-active { + -webkit-transform: scale(0); + transform: scale(0); + opacity: 0 +} + +md-tooltip._md-hide { + transition: all .3s cubic-bezier(.55, 0, .55, .2) +} + +md-tooltip._md-show { + transition: all .4s cubic-bezier(.25, .8, .25, 1); + pointer-events: auto; + will-change: opacity, height, width +} + +.md-whiteframe-1dp, .md-whiteframe-z1 { + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .2), 0 1px 1px 0 rgba(0, 0, 0, .14), 0 2px 1px -1px rgba(0, 0, 0, .12) +} + +.md-whiteframe-2dp { + box-shadow: 0 1px 5px 0 rgba(0, 0, 0, .2), 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .12) +} + +.md-whiteframe-3dp { + box-shadow: 0 1px 8px 0 rgba(0, 0, 0, .2), 0 3px 4px 0 rgba(0, 0, 0, .14), 0 3px 3px -2px rgba(0, 0, 0, .12) +} + +.md-whiteframe-4dp, .md-whiteframe-z2 { + box-shadow: 0 2px 4px -1px rgba(0, 0, 0, .2), 0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12) +} + +.md-whiteframe-5dp { + box-shadow: 0 3px 5px -1px rgba(0, 0, 0, .2), 0 5px 8px 0 rgba(0, 0, 0, .14), 0 1px 14px 0 rgba(0, 0, 0, .12) +} + +.md-whiteframe-6dp { + box-shadow: 0 3px 5px -1px rgba(0, 0, 0, .2), 0 6px 10px 0 rgba(0, 0, 0, .14), 0 1px 18px 0 rgba(0, 0, 0, .12) +} + +.md-whiteframe-7dp, .md-whiteframe-z3 { + box-shadow: 0 4px 5px -2px rgba(0, 0, 0, .2), 0 7px 10px 1px rgba(0, 0, 0, .14), 0 2px 16px 1px rgba(0, 0, 0, .12) +} + +.md-whiteframe-8dp { + box-shadow: 0 5px 5px -3px rgba(0, 0, 0, .2), 0 8px 10px 1px rgba(0, 0, 0, .14), 0 3px 14px 2px rgba(0, 0, 0, .12) +} + +.md-whiteframe-9dp { + box-shadow: 0 5px 6px -3px rgba(0, 0, 0, .2), 0 9px 12px 1px rgba(0, 0, 0, .14), 0 3px 16px 2px rgba(0, 0, 0, .12) +} + +.md-whiteframe-10dp, .md-whiteframe-z4 { + box-shadow: 0 6px 6px -3px rgba(0, 0, 0, .2), 0 10px 14px 1px rgba(0, 0, 0, .14), 0 4px 18px 3px rgba(0, 0, 0, .12) +} + +.md-whiteframe-11dp { + box-shadow: 0 6px 7px -4px rgba(0, 0, 0, .2), 0 11px 15px 1px rgba(0, 0, 0, .14), 0 4px 20px 3px rgba(0, 0, 0, .12) +} + +.md-whiteframe-12dp { + box-shadow: 0 7px 8px -4px rgba(0, 0, 0, .2), 0 12px 17px 2px rgba(0, 0, 0, .14), 0 5px 22px 4px rgba(0, 0, 0, .12) +} + +.md-whiteframe-13dp, .md-whiteframe-z5 { + box-shadow: 0 7px 8px -4px rgba(0, 0, 0, .2), 0 13px 19px 2px rgba(0, 0, 0, .14), 0 5px 24px 4px rgba(0, 0, 0, .12) +} + +.md-whiteframe-14dp { + box-shadow: 0 7px 9px -4px rgba(0, 0, 0, .2), 0 14px 21px 2px rgba(0, 0, 0, .14), 0 5px 26px 4px rgba(0, 0, 0, .12) +} + +.md-whiteframe-15dp { + box-shadow: 0 8px 9px -5px rgba(0, 0, 0, .2), 0 15px 22px 2px rgba(0, 0, 0, .14), 0 6px 28px 5px rgba(0, 0, 0, .12) +} + +.md-whiteframe-16dp { + box-shadow: 0 8px 10px -5px rgba(0, 0, 0, .2), 0 16px 24px 2px rgba(0, 0, 0, .14), 0 6px 30px 5px rgba(0, 0, 0, .12) +} + +.md-whiteframe-17dp { + box-shadow: 0 8px 11px -5px rgba(0, 0, 0, .2), 0 17px 26px 2px rgba(0, 0, 0, .14), 0 6px 32px 5px rgba(0, 0, 0, .12) +} + +.md-whiteframe-18dp { + box-shadow: 0 9px 11px -5px rgba(0, 0, 0, .2), 0 18px 28px 2px rgba(0, 0, 0, .14), 0 7px 34px 6px rgba(0, 0, 0, .12) +} + +.md-whiteframe-19dp { + box-shadow: 0 9px 12px -6px rgba(0, 0, 0, .2), 0 19px 29px 2px rgba(0, 0, 0, .14), 0 7px 36px 6px rgba(0, 0, 0, .12) +} + +.md-whiteframe-20dp { + box-shadow: 0 10px 13px -6px rgba(0, 0, 0, .2), 0 20px 31px 3px rgba(0, 0, 0, .14), 0 8px 38px 7px rgba(0, 0, 0, .12) +} + +.md-whiteframe-21dp { + box-shadow: 0 10px 13px -6px rgba(0, 0, 0, .2), 0 21px 33px 3px rgba(0, 0, 0, .14), 0 8px 40px 7px rgba(0, 0, 0, .12) +} + +.md-whiteframe-22dp { + box-shadow: 0 10px 14px -6px rgba(0, 0, 0, .2), 0 22px 35px 3px rgba(0, 0, 0, .14), 0 8px 42px 7px rgba(0, 0, 0, .12) +} + +.md-whiteframe-23dp { + box-shadow: 0 11px 14px -7px rgba(0, 0, 0, .2), 0 23px 36px 3px rgba(0, 0, 0, .14), 0 9px 44px 8px rgba(0, 0, 0, .12) +} + +.md-whiteframe-24dp { + box-shadow: 0 11px 15px -7px rgba(0, 0, 0, .2), 0 24px 38px 3px rgba(0, 0, 0, .14), 0 9px 46px 8px rgba(0, 0, 0, .12) +} + +@media screen and (-ms-high-contrast: active) { + md-whiteframe { + border: 1px solid #fff + } +} + +@-moz-document url-prefix() { + .layout-fill { + margin: 0; + width: 100%; + min-height: 100%; + height: 100% + } +} + +.flex-order { + -webkit-order: 0; + -ms-flex-order: 0; + order: 0 +} + +.flex-order--20 { + -webkit-order: -20; + -ms-flex-order: -20; + order: -20 +} + +.flex-order--19 { + -webkit-order: -19; + -ms-flex-order: -19; + order: -19 +} + +.flex-order--18 { + -webkit-order: -18; + -ms-flex-order: -18; + order: -18 +} + +.flex-order--17 { + -webkit-order: -17; + -ms-flex-order: -17; + order: -17 +} + +.flex-order--16 { + -webkit-order: -16; + -ms-flex-order: -16; + order: -16 +} + +.flex-order--15 { + -webkit-order: -15; + -ms-flex-order: -15; + order: -15 +} + +.flex-order--14 { + -webkit-order: -14; + -ms-flex-order: -14; + order: -14 +} + +.flex-order--13 { + -webkit-order: -13; + -ms-flex-order: -13; + order: -13 +} + +.flex-order--12 { + -webkit-order: -12; + -ms-flex-order: -12; + order: -12 +} + +.flex-order--11 { + -webkit-order: -11; + -ms-flex-order: -11; + order: -11 +} + +.flex-order--10 { + -webkit-order: -10; + -ms-flex-order: -10; + order: -10 +} + +.flex-order--9 { + -webkit-order: -9; + -ms-flex-order: -9; + order: -9 +} + +.flex-order--8 { + -webkit-order: -8; + -ms-flex-order: -8; + order: -8 +} + +.flex-order--7 { + -webkit-order: -7; + -ms-flex-order: -7; + order: -7 +} + +.flex-order--6 { + -webkit-order: -6; + -ms-flex-order: -6; + order: -6 +} + +.flex-order--5 { + -webkit-order: -5; + -ms-flex-order: -5; + order: -5 +} + +.flex-order--4 { + -webkit-order: -4; + -ms-flex-order: -4; + order: -4 +} + +.flex-order--3 { + -webkit-order: -3; + -ms-flex-order: -3; + order: -3 +} + +.flex-order--2 { + -webkit-order: -2; + -ms-flex-order: -2; + order: -2 +} + +.flex-order--1 { + -webkit-order: -1; + -ms-flex-order: -1; + order: -1 +} + +.flex-order-0 { + -webkit-order: 0; + -ms-flex-order: 0; + order: 0 +} + +.flex-order-1 { + -webkit-order: 1; + -ms-flex-order: 1; + order: 1 +} + +.flex-order-2 { + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 +} + +.flex-order-3 { + -webkit-order: 3; + -ms-flex-order: 3; + order: 3 +} + +.flex-order-4 { + -webkit-order: 4; + -ms-flex-order: 4; + order: 4 +} + +.flex-order-5 { + -webkit-order: 5; + -ms-flex-order: 5; + order: 5 +} + +.flex-order-6 { + -webkit-order: 6; + -ms-flex-order: 6; + order: 6 +} + +.flex-order-7 { + -webkit-order: 7; + -ms-flex-order: 7; + order: 7 +} + +.flex-order-8 { + -webkit-order: 8; + -ms-flex-order: 8; + order: 8 +} + +.flex-order-9 { + -webkit-order: 9; + -ms-flex-order: 9; + order: 9 +} + +.flex-order-10 { + -webkit-order: 10; + -ms-flex-order: 10; + order: 10 +} + +.flex-order-11 { + -webkit-order: 11; + -ms-flex-order: 11; + order: 11 +} + +.flex-order-12 { + -webkit-order: 12; + -ms-flex-order: 12; + order: 12 +} + +.flex-order-13 { + -webkit-order: 13; + -ms-flex-order: 13; + order: 13 +} + +.flex-order-14 { + -webkit-order: 14; + -ms-flex-order: 14; + order: 14 +} + +.flex-order-15 { + -webkit-order: 15; + -ms-flex-order: 15; + order: 15 +} + +.flex-order-16 { + -webkit-order: 16; + -ms-flex-order: 16; + order: 16 +} + +.flex-order-17 { + -webkit-order: 17; + -ms-flex-order: 17; + order: 17 +} + +.flex-order-18 { + -webkit-order: 18; + -ms-flex-order: 18; + order: 18 +} + +.flex-order-19 { + -webkit-order: 19; + -ms-flex-order: 19; + order: 19 +} + +.flex-order-20 { + -webkit-order: 20; + -ms-flex-order: 20; + order: 20 +} + +.flex-offset-0, .offset-0 { + margin-left: 0 +} + +[dir=rtl] .flex-offset-0, [dir=rtl] .offset-0 { + margin-left: auto; + margin-right: 0 +} + +.flex-offset-5, .offset-5 { + margin-left: 5% +} + +[dir=rtl] .flex-offset-5, [dir=rtl] .offset-5 { + margin-left: auto; + margin-right: 5% +} + +.flex-offset-10, .offset-10 { + margin-left: 10% +} + +[dir=rtl] .flex-offset-10, [dir=rtl] .offset-10 { + margin-left: auto; + margin-right: 10% +} + +.flex-offset-15, .offset-15 { + margin-left: 15% +} + +[dir=rtl] .flex-offset-15, [dir=rtl] .offset-15 { + margin-left: auto; + margin-right: 15% +} + +.flex-offset-20, .offset-20 { + margin-left: 20% +} + +[dir=rtl] .flex-offset-20, [dir=rtl] .offset-20 { + margin-left: auto; + margin-right: 20% +} + +.flex-offset-25, .offset-25 { + margin-left: 25% +} + +[dir=rtl] .flex-offset-25, [dir=rtl] .offset-25 { + margin-left: auto; + margin-right: 25% +} + +.flex-offset-30, .offset-30 { + margin-left: 30% +} + +[dir=rtl] .flex-offset-30, [dir=rtl] .offset-30 { + margin-left: auto; + margin-right: 30% +} + +.flex-offset-35, .offset-35 { + margin-left: 35% +} + +[dir=rtl] .flex-offset-35, [dir=rtl] .offset-35 { + margin-left: auto; + margin-right: 35% +} + +.flex-offset-40, .offset-40 { + margin-left: 40% +} + +[dir=rtl] .flex-offset-40, [dir=rtl] .offset-40 { + margin-left: auto; + margin-right: 40% +} + +.flex-offset-45, .offset-45 { + margin-left: 45% +} + +[dir=rtl] .flex-offset-45, [dir=rtl] .offset-45 { + margin-left: auto; + margin-right: 45% +} + +.flex-offset-50, .offset-50 { + margin-left: 50% +} + +[dir=rtl] .flex-offset-50, [dir=rtl] .offset-50 { + margin-left: auto; + margin-right: 50% +} + +.flex-offset-55, .offset-55 { + margin-left: 55% +} + +[dir=rtl] .flex-offset-55, [dir=rtl] .offset-55 { + margin-left: auto; + margin-right: 55% +} + +.flex-offset-60, .offset-60 { + margin-left: 60% +} + +[dir=rtl] .flex-offset-60, [dir=rtl] .offset-60 { + margin-left: auto; + margin-right: 60% +} + +.flex-offset-65, .offset-65 { + margin-left: 65% +} + +[dir=rtl] .flex-offset-65, [dir=rtl] .offset-65 { + margin-left: auto; + margin-right: 65% +} + +.flex-offset-70, .offset-70 { + margin-left: 70% +} + +[dir=rtl] .flex-offset-70, [dir=rtl] .offset-70 { + margin-left: auto; + margin-right: 70% +} + +.flex-offset-75, .offset-75 { + margin-left: 75% +} + +[dir=rtl] .flex-offset-75, [dir=rtl] .offset-75 { + margin-left: auto; + margin-right: 75% +} + +.flex-offset-80, .offset-80 { + margin-left: 80% +} + +[dir=rtl] .flex-offset-80, [dir=rtl] .offset-80 { + margin-left: auto; + margin-right: 80% +} + +.flex-offset-85, .offset-85 { + margin-left: 85% +} + +[dir=rtl] .flex-offset-85, [dir=rtl] .offset-85 { + margin-left: auto; + margin-right: 85% +} + +.flex-offset-90, .offset-90 { + margin-left: 90% +} + +[dir=rtl] .flex-offset-90, [dir=rtl] .offset-90 { + margin-left: auto; + margin-right: 90% +} + +.flex-offset-95, .offset-95 { + margin-left: 95% +} + +[dir=rtl] .flex-offset-95, [dir=rtl] .offset-95 { + margin-left: auto; + margin-right: 95% +} + +.flex-offset-33, .offset-33 { + margin-left: calc(100% / 3) +} + +.flex-offset-66, .offset-66 { + margin-left: calc(200% / 3) +} + +[dir=rtl] .flex-offset-66, [dir=rtl] .offset-66 { + margin-left: auto; + margin-right: calc(200% / 3) +} + +.layout-align { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch +} + +.layout-align-start, .layout-align-start-center, .layout-align-start-end, .layout-align-start-start, .layout-align-start-stretch { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start +} + +.layout-align-center, .layout-align-center-center, .layout-align-center-end, .layout-align-center-start, .layout-align-center-stretch { + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center +} + +.layout-align-end, .layout-align-end-center, .layout-align-end-end, .layout-align-end-start, .layout-align-end-stretch { + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end +} + +.layout-align-space-around, .layout-align-space-around-center, .layout-align-space-around-end, .layout-align-space-around-start, .layout-align-space-around-stretch { + -webkit-justify-content: space-around; + -ms-flex-pack: distribute; + justify-content: space-around +} + +.layout-align-space-between, .layout-align-space-between-center, .layout-align-space-between-end, .layout-align-space-between-start, .layout-align-space-between-stretch { + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between +} + +.layout-align-center-start, .layout-align-end-start, .layout-align-space-around-start, .layout-align-space-between-start, .layout-align-start-start { + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + -webkit-align-content: flex-start; + -ms-flex-line-pack: start; + align-content: flex-start +} + +.layout-align-center-center, .layout-align-end-center, .layout-align-space-around-center, .layout-align-space-between-center, .layout-align-start-center { + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-align-content: center; + -ms-flex-line-pack: center; + align-content: center; + max-width: 100% +} + +.layout-align-center-center > *, .layout-align-end-center > *, .layout-align-space-around-center > *, .layout-align-space-between-center > *, .layout-align-start-center > * { + max-width: 100%; + box-sizing: border-box +} + +.layout-align-center-end, .layout-align-end-end, .layout-align-space-around-end, .layout-align-space-between-end, .layout-align-start-end { + -webkit-align-items: flex-end; + -ms-flex-align: end; + align-items: flex-end; + -webkit-align-content: flex-end; + -ms-flex-line-pack: end; + align-content: flex-end +} + +.layout-align-center-stretch, .layout-align-end-stretch, .layout-align-space-around-stretch, .layout-align-space-between-stretch, .layout-align-start-stretch { + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch +} + +.flex { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + box-sizing: border-box +} + +.flex-grow { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + box-sizing: border-box +} + +.flex-initial { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box +} + +.flex-auto { + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + box-sizing: border-box +} + +.flex-none { + -webkit-flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + box-sizing: border-box +} + +.flex-noshrink { + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + box-sizing: border-box +} + +.flex-nogrow { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box +} + +.flex-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box +} + +.layout-row > .flex-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box; + min-width: 0 +} + +.layout-column > .flex-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 100%; + max-height: 0; + box-sizing: border-box; + min-height: 0 +} + +.flex-5, .layout-row > .flex-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 5%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 100%; + max-height: 5%; + box-sizing: border-box +} + +.flex-10, .layout-row > .flex-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 10%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 100%; + max-height: 10%; + box-sizing: border-box +} + +.flex-15, .layout-row > .flex-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 15%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 100%; + max-height: 15%; + box-sizing: border-box +} + +.flex-20, .layout-row > .flex-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 20%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 100%; + max-height: 20%; + box-sizing: border-box +} + +.flex-25, .layout-row > .flex-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 25%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 100%; + max-height: 25%; + box-sizing: border-box +} + +.flex-30, .layout-row > .flex-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 30%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 100%; + max-height: 30%; + box-sizing: border-box +} + +.flex-35, .layout-row > .flex-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 35%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 100%; + max-height: 35%; + box-sizing: border-box +} + +.flex-40, .layout-row > .flex-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 40%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 100%; + max-height: 40%; + box-sizing: border-box +} + +.flex-45, .layout-row > .flex-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 45%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 100%; + max-height: 45%; + box-sizing: border-box +} + +.flex-50, .layout-row > .flex-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 50%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 100%; + max-height: 50%; + box-sizing: border-box +} + +.flex-55, .layout-row > .flex-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 55%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 100%; + max-height: 55%; + box-sizing: border-box +} + +.flex-60, .layout-row > .flex-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 60%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 100%; + max-height: 60%; + box-sizing: border-box +} + +.flex-65, .layout-row > .flex-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 65%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 100%; + max-height: 65%; + box-sizing: border-box +} + +.flex-70, .layout-row > .flex-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 70%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 100%; + max-height: 70%; + box-sizing: border-box +} + +.flex-75, .layout-row > .flex-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 75%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 100%; + max-height: 75%; + box-sizing: border-box +} + +.flex-80, .layout-row > .flex-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 80%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 100%; + max-height: 80%; + box-sizing: border-box +} + +.flex-85, .layout-row > .flex-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 85%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 100%; + max-height: 85%; + box-sizing: border-box +} + +.flex-90, .layout-row > .flex-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 90%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 100%; + max-height: 90%; + box-sizing: border-box +} + +.flex-95, .layout-row > .flex-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 95%; + max-height: 100%; + box-sizing: border-box +} + +.layout-column > .flex-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 100%; + max-height: 95%; + box-sizing: border-box +} + +.flex-100, .layout-column > .flex-100, .layout-row > .flex-100 { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + max-width: 100%; + max-height: 100%; + box-sizing: border-box +} + +.layout-row > .flex-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 33.33%; + max-height: 100%; + box-sizing: border-box +} + +.layout-row > .flex-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 66.66%; + max-height: 100%; + box-sizing: border-box +} + +.layout-row > .flex { + min-width: 0 +} + +.layout-column > .flex-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 100%; + max-height: 33.33%; + box-sizing: border-box +} + +.layout-column > .flex-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 100%; + max-height: 66.66%; + box-sizing: border-box +} + +.layout-column > .flex { + min-height: 0 +} + +.layout, .layout-column, .layout-row { + box-sizing: border-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex +} + +.layout-column { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column +} + +.layout-row { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row +} + +.layout-padding-sm > *, .layout-padding > .flex-sm { + padding: 4px +} + +.layout-padding, .layout-padding-gt-sm, .layout-padding-gt-sm > *, .layout-padding-md, .layout-padding-md > *, .layout-padding > *, .layout-padding > .flex, .layout-padding > .flex-gt-sm, .layout-padding > .flex-md { + padding: 8px +} + +.layout-padding-gt-lg > *, .layout-padding-gt-md > *, .layout-padding-lg > *, .layout-padding > .flex-gt-lg, .layout-padding > .flex-gt-md, .layout-padding > .flex-lg { + padding: 16px +} + +.layout-margin-sm > *, .layout-margin > .flex-sm { + margin: 4px +} + +.layout-margin, .layout-margin-gt-sm, .layout-margin-gt-sm > *, .layout-margin-md, .layout-margin-md > *, .layout-margin > *, .layout-margin > .flex, .layout-margin > .flex-gt-sm, .layout-margin > .flex-md { + margin: 8px +} + +.layout-margin-gt-lg > *, .layout-margin-gt-md > *, .layout-margin-lg > *, .layout-margin > .flex-gt-lg, .layout-margin > .flex-gt-md, .layout-margin > .flex-lg { + margin: 16px +} + +.layout-wrap { + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap +} + +.layout-nowrap { + -webkit-flex-wrap: nowrap; + -ms-flex-wrap: nowrap; + flex-wrap: nowrap +} + +.layout-fill { + margin: 0; + width: 100%; + min-height: 100%; + height: 100% +} + +@media (max-width: 599px) { + .hide-xs:not(.show-xs):not(.show), .hide:not(.show-xs):not(.show) { + display: none + } + + .flex-order-xs--20 { + -webkit-order: -20; + -ms-flex-order: -20; + order: -20 + } + + .flex-order-xs--19 { + -webkit-order: -19; + -ms-flex-order: -19; + order: -19 + } + + .flex-order-xs--18 { + -webkit-order: -18; + -ms-flex-order: -18; + order: -18 + } + + .flex-order-xs--17 { + -webkit-order: -17; + -ms-flex-order: -17; + order: -17 + } + + .flex-order-xs--16 { + -webkit-order: -16; + -ms-flex-order: -16; + order: -16 + } + + .flex-order-xs--15 { + -webkit-order: -15; + -ms-flex-order: -15; + order: -15 + } + + .flex-order-xs--14 { + -webkit-order: -14; + -ms-flex-order: -14; + order: -14 + } + + .flex-order-xs--13 { + -webkit-order: -13; + -ms-flex-order: -13; + order: -13 + } + + .flex-order-xs--12 { + -webkit-order: -12; + -ms-flex-order: -12; + order: -12 + } + + .flex-order-xs--11 { + -webkit-order: -11; + -ms-flex-order: -11; + order: -11 + } + + .flex-order-xs--10 { + -webkit-order: -10; + -ms-flex-order: -10; + order: -10 + } + + .flex-order-xs--9 { + -webkit-order: -9; + -ms-flex-order: -9; + order: -9 + } + + .flex-order-xs--8 { + -webkit-order: -8; + -ms-flex-order: -8; + order: -8 + } + + .flex-order-xs--7 { + -webkit-order: -7; + -ms-flex-order: -7; + order: -7 + } + + .flex-order-xs--6 { + -webkit-order: -6; + -ms-flex-order: -6; + order: -6 + } + + .flex-order-xs--5 { + -webkit-order: -5; + -ms-flex-order: -5; + order: -5 + } + + .flex-order-xs--4 { + -webkit-order: -4; + -ms-flex-order: -4; + order: -4 + } + + .flex-order-xs--3 { + -webkit-order: -3; + -ms-flex-order: -3; + order: -3 + } + + .flex-order-xs--2 { + -webkit-order: -2; + -ms-flex-order: -2; + order: -2 + } + + .flex-order-xs--1 { + -webkit-order: -1; + -ms-flex-order: -1; + order: -1 + } + + .flex-order-xs-0 { + -webkit-order: 0; + -ms-flex-order: 0; + order: 0 + } + + .flex-order-xs-1 { + -webkit-order: 1; + -ms-flex-order: 1; + order: 1 + } + + .flex-order-xs-2 { + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 + } + + .flex-order-xs-3 { + -webkit-order: 3; + -ms-flex-order: 3; + order: 3 + } + + .flex-order-xs-4 { + -webkit-order: 4; + -ms-flex-order: 4; + order: 4 + } + + .flex-order-xs-5 { + -webkit-order: 5; + -ms-flex-order: 5; + order: 5 + } + + .flex-order-xs-6 { + -webkit-order: 6; + -ms-flex-order: 6; + order: 6 + } + + .flex-order-xs-7 { + -webkit-order: 7; + -ms-flex-order: 7; + order: 7 + } + + .flex-order-xs-8 { + -webkit-order: 8; + -ms-flex-order: 8; + order: 8 + } + + .flex-order-xs-9 { + -webkit-order: 9; + -ms-flex-order: 9; + order: 9 + } + + .flex-order-xs-10 { + -webkit-order: 10; + -ms-flex-order: 10; + order: 10 + } + + .flex-order-xs-11 { + -webkit-order: 11; + -ms-flex-order: 11; + order: 11 + } + + .flex-order-xs-12 { + -webkit-order: 12; + -ms-flex-order: 12; + order: 12 + } + + .flex-order-xs-13 { + -webkit-order: 13; + -ms-flex-order: 13; + order: 13 + } + + .flex-order-xs-14 { + -webkit-order: 14; + -ms-flex-order: 14; + order: 14 + } + + .flex-order-xs-15 { + -webkit-order: 15; + -ms-flex-order: 15; + order: 15 + } + + .flex-order-xs-16 { + -webkit-order: 16; + -ms-flex-order: 16; + order: 16 + } + + .flex-order-xs-17 { + -webkit-order: 17; + -ms-flex-order: 17; + order: 17 + } + + .flex-order-xs-18 { + -webkit-order: 18; + -ms-flex-order: 18; + order: 18 + } + + .flex-order-xs-19 { + -webkit-order: 19; + -ms-flex-order: 19; + order: 19 + } + + .flex-order-xs-20 { + -webkit-order: 20; + -ms-flex-order: 20; + order: 20 + } + + .flex-offset-xs-0, .offset-xs-0 { + margin-left: 0 + } + + [dir=rtl] .flex-offset-xs-0, [dir=rtl] .offset-xs-0 { + margin-left: auto; + margin-right: 0 + } + + .flex-offset-xs-5, .offset-xs-5 { + margin-left: 5% + } + + [dir=rtl] .flex-offset-xs-5, [dir=rtl] .offset-xs-5 { + margin-left: auto; + margin-right: 5% + } + + .flex-offset-xs-10, .offset-xs-10 { + margin-left: 10% + } + + [dir=rtl] .flex-offset-xs-10, [dir=rtl] .offset-xs-10 { + margin-left: auto; + margin-right: 10% + } + + .flex-offset-xs-15, .offset-xs-15 { + margin-left: 15% + } + + [dir=rtl] .flex-offset-xs-15, [dir=rtl] .offset-xs-15 { + margin-left: auto; + margin-right: 15% + } + + .flex-offset-xs-20, .offset-xs-20 { + margin-left: 20% + } + + [dir=rtl] .flex-offset-xs-20, [dir=rtl] .offset-xs-20 { + margin-left: auto; + margin-right: 20% + } + + .flex-offset-xs-25, .offset-xs-25 { + margin-left: 25% + } + + [dir=rtl] .flex-offset-xs-25, [dir=rtl] .offset-xs-25 { + margin-left: auto; + margin-right: 25% + } + + .flex-offset-xs-30, .offset-xs-30 { + margin-left: 30% + } + + [dir=rtl] .flex-offset-xs-30, [dir=rtl] .offset-xs-30 { + margin-left: auto; + margin-right: 30% + } + + .flex-offset-xs-35, .offset-xs-35 { + margin-left: 35% + } + + [dir=rtl] .flex-offset-xs-35, [dir=rtl] .offset-xs-35 { + margin-left: auto; + margin-right: 35% + } + + .flex-offset-xs-40, .offset-xs-40 { + margin-left: 40% + } + + [dir=rtl] .flex-offset-xs-40, [dir=rtl] .offset-xs-40 { + margin-left: auto; + margin-right: 40% + } + + .flex-offset-xs-45, .offset-xs-45 { + margin-left: 45% + } + + [dir=rtl] .flex-offset-xs-45, [dir=rtl] .offset-xs-45 { + margin-left: auto; + margin-right: 45% + } + + .flex-offset-xs-50, .offset-xs-50 { + margin-left: 50% + } + + [dir=rtl] .flex-offset-xs-50, [dir=rtl] .offset-xs-50 { + margin-left: auto; + margin-right: 50% + } + + .flex-offset-xs-55, .offset-xs-55 { + margin-left: 55% + } + + [dir=rtl] .flex-offset-xs-55, [dir=rtl] .offset-xs-55 { + margin-left: auto; + margin-right: 55% + } + + .flex-offset-xs-60, .offset-xs-60 { + margin-left: 60% + } + + [dir=rtl] .flex-offset-xs-60, [dir=rtl] .offset-xs-60 { + margin-left: auto; + margin-right: 60% + } + + .flex-offset-xs-65, .offset-xs-65 { + margin-left: 65% + } + + [dir=rtl] .flex-offset-xs-65, [dir=rtl] .offset-xs-65 { + margin-left: auto; + margin-right: 65% + } + + .flex-offset-xs-70, .offset-xs-70 { + margin-left: 70% + } + + [dir=rtl] .flex-offset-xs-70, [dir=rtl] .offset-xs-70 { + margin-left: auto; + margin-right: 70% + } + + .flex-offset-xs-75, .offset-xs-75 { + margin-left: 75% + } + + [dir=rtl] .flex-offset-xs-75, [dir=rtl] .offset-xs-75 { + margin-left: auto; + margin-right: 75% + } + + .flex-offset-xs-80, .offset-xs-80 { + margin-left: 80% + } + + [dir=rtl] .flex-offset-xs-80, [dir=rtl] .offset-xs-80 { + margin-left: auto; + margin-right: 80% + } + + .flex-offset-xs-85, .offset-xs-85 { + margin-left: 85% + } + + [dir=rtl] .flex-offset-xs-85, [dir=rtl] .offset-xs-85 { + margin-left: auto; + margin-right: 85% + } + + .flex-offset-xs-90, .offset-xs-90 { + margin-left: 90% + } + + [dir=rtl] .flex-offset-xs-90, [dir=rtl] .offset-xs-90 { + margin-left: auto; + margin-right: 90% + } + + .flex-offset-xs-95, .offset-xs-95 { + margin-left: 95% + } + + [dir=rtl] .flex-offset-xs-95, [dir=rtl] .offset-xs-95 { + margin-left: auto; + margin-right: 95% + } + + .flex-offset-xs-33, .offset-xs-33 { + margin-left: calc(100% / 3) + } + + .flex-offset-xs-66, .offset-xs-66 { + margin-left: calc(200% / 3) + } + + [dir=rtl] .flex-offset-xs-66, [dir=rtl] .offset-xs-66 { + margin-left: auto; + margin-right: calc(200% / 3) + } + + .layout-align-xs { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch + } + + .layout-align-xs-start, .layout-align-xs-start-center, .layout-align-xs-start-end, .layout-align-xs-start-start, .layout-align-xs-start-stretch { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start + } + + .layout-align-xs-center, .layout-align-xs-center-center, .layout-align-xs-center-end, .layout-align-xs-center-start, .layout-align-xs-center-stretch { + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center + } + + .layout-align-xs-end, .layout-align-xs-end-center, .layout-align-xs-end-end, .layout-align-xs-end-start, .layout-align-xs-end-stretch { + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end + } + + .layout-align-xs-space-around, .layout-align-xs-space-around-center, .layout-align-xs-space-around-end, .layout-align-xs-space-around-start, .layout-align-xs-space-around-stretch { + -webkit-justify-content: space-around; + -ms-flex-pack: distribute; + justify-content: space-around + } + + .layout-align-xs-space-between, .layout-align-xs-space-between-center, .layout-align-xs-space-between-end, .layout-align-xs-space-between-start, .layout-align-xs-space-between-stretch { + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between + } + + .layout-align-xs-center-start, .layout-align-xs-end-start, .layout-align-xs-space-around-start, .layout-align-xs-space-between-start, .layout-align-xs-start-start { + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + -webkit-align-content: flex-start; + -ms-flex-line-pack: start; + align-content: flex-start + } + + .layout-align-xs-center-center, .layout-align-xs-end-center, .layout-align-xs-space-around-center, .layout-align-xs-space-between-center, .layout-align-xs-start-center { + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-align-content: center; + -ms-flex-line-pack: center; + align-content: center; + max-width: 100% + } + + .layout-align-xs-center-center > *, .layout-align-xs-end-center > *, .layout-align-xs-space-around-center > *, .layout-align-xs-space-between-center > *, .layout-align-xs-start-center > * { + max-width: 100%; + box-sizing: border-box + } + + .layout-align-xs-center-end, .layout-align-xs-end-end, .layout-align-xs-space-around-end, .layout-align-xs-space-between-end, .layout-align-xs-start-end { + -webkit-align-items: flex-end; + -ms-flex-align: end; + align-items: flex-end; + -webkit-align-content: flex-end; + -ms-flex-line-pack: end; + align-content: flex-end + } + + .layout-align-xs-center-stretch, .layout-align-xs-end-stretch, .layout-align-xs-space-around-stretch, .layout-align-xs-space-between-stretch, .layout-align-xs-start-stretch { + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch + } + + .flex-xs { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + box-sizing: border-box + } + + .flex-xs-grow { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + box-sizing: border-box + } + + .flex-xs-initial { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-xs-auto { + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + box-sizing: border-box + } + + .flex-xs-none { + -webkit-flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + box-sizing: border-box + } + + .flex-xs-noshrink { + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + box-sizing: border-box + } + + .flex-xs-nogrow { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-xs-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box + } + + .layout-row > .flex-xs-0, .layout-xs-row > .flex-xs-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box; + min-width: 0 + } + + .layout-column > .flex-xs-0, .layout-xs-column > .flex-xs-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 100%; + max-height: 0; + box-sizing: border-box; + min-height: 0 + } + + .flex-xs-5, .layout-row > .flex-xs-5, .layout-xs-row > .flex-xs-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 5%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-5, .layout-xs-column > .flex-xs-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 100%; + max-height: 5%; + box-sizing: border-box + } + + .flex-xs-10, .layout-row > .flex-xs-10, .layout-xs-row > .flex-xs-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 10%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-10, .layout-xs-column > .flex-xs-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 100%; + max-height: 10%; + box-sizing: border-box + } + + .flex-xs-15, .layout-row > .flex-xs-15, .layout-xs-row > .flex-xs-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 15%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-15, .layout-xs-column > .flex-xs-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 100%; + max-height: 15%; + box-sizing: border-box + } + + .flex-xs-20, .layout-row > .flex-xs-20, .layout-xs-row > .flex-xs-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 20%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-20, .layout-xs-column > .flex-xs-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 100%; + max-height: 20%; + box-sizing: border-box + } + + .flex-xs-25, .layout-row > .flex-xs-25, .layout-xs-row > .flex-xs-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 25%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-25, .layout-xs-column > .flex-xs-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 100%; + max-height: 25%; + box-sizing: border-box + } + + .flex-xs-30, .layout-row > .flex-xs-30, .layout-xs-row > .flex-xs-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 30%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-30, .layout-xs-column > .flex-xs-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 100%; + max-height: 30%; + box-sizing: border-box + } + + .flex-xs-35, .layout-row > .flex-xs-35, .layout-xs-row > .flex-xs-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 35%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-35, .layout-xs-column > .flex-xs-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 100%; + max-height: 35%; + box-sizing: border-box + } + + .flex-xs-40, .layout-row > .flex-xs-40, .layout-xs-row > .flex-xs-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 40%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-40, .layout-xs-column > .flex-xs-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 100%; + max-height: 40%; + box-sizing: border-box + } + + .flex-xs-45, .layout-row > .flex-xs-45, .layout-xs-row > .flex-xs-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 45%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-45, .layout-xs-column > .flex-xs-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 100%; + max-height: 45%; + box-sizing: border-box + } + + .flex-xs-50, .layout-row > .flex-xs-50, .layout-xs-row > .flex-xs-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 50%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-50, .layout-xs-column > .flex-xs-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 100%; + max-height: 50%; + box-sizing: border-box + } + + .flex-xs-55, .layout-row > .flex-xs-55, .layout-xs-row > .flex-xs-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 55%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-55, .layout-xs-column > .flex-xs-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 100%; + max-height: 55%; + box-sizing: border-box + } + + .flex-xs-60, .layout-row > .flex-xs-60, .layout-xs-row > .flex-xs-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 60%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-60, .layout-xs-column > .flex-xs-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 100%; + max-height: 60%; + box-sizing: border-box + } + + .flex-xs-65, .layout-row > .flex-xs-65, .layout-xs-row > .flex-xs-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 65%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-65, .layout-xs-column > .flex-xs-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 100%; + max-height: 65%; + box-sizing: border-box + } + + .flex-xs-70, .layout-row > .flex-xs-70, .layout-xs-row > .flex-xs-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 70%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-70, .layout-xs-column > .flex-xs-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 100%; + max-height: 70%; + box-sizing: border-box + } + + .flex-xs-75, .layout-row > .flex-xs-75, .layout-xs-row > .flex-xs-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 75%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-75, .layout-xs-column > .flex-xs-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 100%; + max-height: 75%; + box-sizing: border-box + } + + .flex-xs-80, .layout-row > .flex-xs-80, .layout-xs-row > .flex-xs-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 80%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-80, .layout-xs-column > .flex-xs-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 100%; + max-height: 80%; + box-sizing: border-box + } + + .flex-xs-85, .layout-row > .flex-xs-85, .layout-xs-row > .flex-xs-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 85%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-85, .layout-xs-column > .flex-xs-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 100%; + max-height: 85%; + box-sizing: border-box + } + + .flex-xs-90, .layout-row > .flex-xs-90, .layout-xs-row > .flex-xs-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 90%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-90, .layout-xs-column > .flex-xs-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 100%; + max-height: 90%; + box-sizing: border-box + } + + .flex-xs-95, .layout-row > .flex-xs-95, .layout-xs-row > .flex-xs-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 95%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xs-95, .layout-xs-column > .flex-xs-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 100%; + max-height: 95%; + box-sizing: border-box + } + + .flex-xs-100, .layout-column > .flex-xs-100, .layout-row > .flex-xs-100, .layout-xs-column > .flex-xs-100, .layout-xs-row > .flex-xs-100 { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + max-width: 100%; + max-height: 100%; + box-sizing: border-box + } + + .layout-row > .flex-xs-33, .layout-xs-row > .flex-xs-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 33.33%; + max-height: 100%; + box-sizing: border-box + } + + .layout-row > .flex-xs-66, .layout-xs-row > .flex-xs-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 66.66%; + max-height: 100%; + box-sizing: border-box + } + + .layout-row > .flex, .layout-xs-row > .flex { + min-width: 0 + } + + .layout-column > .flex-xs-33, .layout-xs-column > .flex-xs-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 100%; + max-height: 33.33%; + box-sizing: border-box + } + + .layout-column > .flex-xs-66, .layout-xs-column > .flex-xs-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 100%; + max-height: 66.66%; + box-sizing: border-box + } + + .layout-column > .flex, .layout-xs-column > .flex { + min-height: 0 + } + + .layout-xs, .layout-xs-column, .layout-xs-row { + box-sizing: border-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex + } + + .layout-xs-column { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column + } + + .layout-xs-row { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row + } +} + +@media (min-width: 600px) { + .flex-order-gt-xs--20 { + -webkit-order: -20; + -ms-flex-order: -20; + order: -20 + } + + .flex-order-gt-xs--19 { + -webkit-order: -19; + -ms-flex-order: -19; + order: -19 + } + + .flex-order-gt-xs--18 { + -webkit-order: -18; + -ms-flex-order: -18; + order: -18 + } + + .flex-order-gt-xs--17 { + -webkit-order: -17; + -ms-flex-order: -17; + order: -17 + } + + .flex-order-gt-xs--16 { + -webkit-order: -16; + -ms-flex-order: -16; + order: -16 + } + + .flex-order-gt-xs--15 { + -webkit-order: -15; + -ms-flex-order: -15; + order: -15 + } + + .flex-order-gt-xs--14 { + -webkit-order: -14; + -ms-flex-order: -14; + order: -14 + } + + .flex-order-gt-xs--13 { + -webkit-order: -13; + -ms-flex-order: -13; + order: -13 + } + + .flex-order-gt-xs--12 { + -webkit-order: -12; + -ms-flex-order: -12; + order: -12 + } + + .flex-order-gt-xs--11 { + -webkit-order: -11; + -ms-flex-order: -11; + order: -11 + } + + .flex-order-gt-xs--10 { + -webkit-order: -10; + -ms-flex-order: -10; + order: -10 + } + + .flex-order-gt-xs--9 { + -webkit-order: -9; + -ms-flex-order: -9; + order: -9 + } + + .flex-order-gt-xs--8 { + -webkit-order: -8; + -ms-flex-order: -8; + order: -8 + } + + .flex-order-gt-xs--7 { + -webkit-order: -7; + -ms-flex-order: -7; + order: -7 + } + + .flex-order-gt-xs--6 { + -webkit-order: -6; + -ms-flex-order: -6; + order: -6 + } + + .flex-order-gt-xs--5 { + -webkit-order: -5; + -ms-flex-order: -5; + order: -5 + } + + .flex-order-gt-xs--4 { + -webkit-order: -4; + -ms-flex-order: -4; + order: -4 + } + + .flex-order-gt-xs--3 { + -webkit-order: -3; + -ms-flex-order: -3; + order: -3 + } + + .flex-order-gt-xs--2 { + -webkit-order: -2; + -ms-flex-order: -2; + order: -2 + } + + .flex-order-gt-xs--1 { + -webkit-order: -1; + -ms-flex-order: -1; + order: -1 + } + + .flex-order-gt-xs-0 { + -webkit-order: 0; + -ms-flex-order: 0; + order: 0 + } + + .flex-order-gt-xs-1 { + -webkit-order: 1; + -ms-flex-order: 1; + order: 1 + } + + .flex-order-gt-xs-2 { + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 + } + + .flex-order-gt-xs-3 { + -webkit-order: 3; + -ms-flex-order: 3; + order: 3 + } + + .flex-order-gt-xs-4 { + -webkit-order: 4; + -ms-flex-order: 4; + order: 4 + } + + .flex-order-gt-xs-5 { + -webkit-order: 5; + -ms-flex-order: 5; + order: 5 + } + + .flex-order-gt-xs-6 { + -webkit-order: 6; + -ms-flex-order: 6; + order: 6 + } + + .flex-order-gt-xs-7 { + -webkit-order: 7; + -ms-flex-order: 7; + order: 7 + } + + .flex-order-gt-xs-8 { + -webkit-order: 8; + -ms-flex-order: 8; + order: 8 + } + + .flex-order-gt-xs-9 { + -webkit-order: 9; + -ms-flex-order: 9; + order: 9 + } + + .flex-order-gt-xs-10 { + -webkit-order: 10; + -ms-flex-order: 10; + order: 10 + } + + .flex-order-gt-xs-11 { + -webkit-order: 11; + -ms-flex-order: 11; + order: 11 + } + + .flex-order-gt-xs-12 { + -webkit-order: 12; + -ms-flex-order: 12; + order: 12 + } + + .flex-order-gt-xs-13 { + -webkit-order: 13; + -ms-flex-order: 13; + order: 13 + } + + .flex-order-gt-xs-14 { + -webkit-order: 14; + -ms-flex-order: 14; + order: 14 + } + + .flex-order-gt-xs-15 { + -webkit-order: 15; + -ms-flex-order: 15; + order: 15 + } + + .flex-order-gt-xs-16 { + -webkit-order: 16; + -ms-flex-order: 16; + order: 16 + } + + .flex-order-gt-xs-17 { + -webkit-order: 17; + -ms-flex-order: 17; + order: 17 + } + + .flex-order-gt-xs-18 { + -webkit-order: 18; + -ms-flex-order: 18; + order: 18 + } + + .flex-order-gt-xs-19 { + -webkit-order: 19; + -ms-flex-order: 19; + order: 19 + } + + .flex-order-gt-xs-20 { + -webkit-order: 20; + -ms-flex-order: 20; + order: 20 + } + + .flex-offset-gt-xs-0, .offset-gt-xs-0 { + margin-left: 0 + } + + [dir=rtl] .flex-offset-gt-xs-0, [dir=rtl] .offset-gt-xs-0 { + margin-left: auto; + margin-right: 0 + } + + .flex-offset-gt-xs-5, .offset-gt-xs-5 { + margin-left: 5% + } + + [dir=rtl] .flex-offset-gt-xs-5, [dir=rtl] .offset-gt-xs-5 { + margin-left: auto; + margin-right: 5% + } + + .flex-offset-gt-xs-10, .offset-gt-xs-10 { + margin-left: 10% + } + + [dir=rtl] .flex-offset-gt-xs-10, [dir=rtl] .offset-gt-xs-10 { + margin-left: auto; + margin-right: 10% + } + + .flex-offset-gt-xs-15, .offset-gt-xs-15 { + margin-left: 15% + } + + [dir=rtl] .flex-offset-gt-xs-15, [dir=rtl] .offset-gt-xs-15 { + margin-left: auto; + margin-right: 15% + } + + .flex-offset-gt-xs-20, .offset-gt-xs-20 { + margin-left: 20% + } + + [dir=rtl] .flex-offset-gt-xs-20, [dir=rtl] .offset-gt-xs-20 { + margin-left: auto; + margin-right: 20% + } + + .flex-offset-gt-xs-25, .offset-gt-xs-25 { + margin-left: 25% + } + + [dir=rtl] .flex-offset-gt-xs-25, [dir=rtl] .offset-gt-xs-25 { + margin-left: auto; + margin-right: 25% + } + + .flex-offset-gt-xs-30, .offset-gt-xs-30 { + margin-left: 30% + } + + [dir=rtl] .flex-offset-gt-xs-30, [dir=rtl] .offset-gt-xs-30 { + margin-left: auto; + margin-right: 30% + } + + .flex-offset-gt-xs-35, .offset-gt-xs-35 { + margin-left: 35% + } + + [dir=rtl] .flex-offset-gt-xs-35, [dir=rtl] .offset-gt-xs-35 { + margin-left: auto; + margin-right: 35% + } + + .flex-offset-gt-xs-40, .offset-gt-xs-40 { + margin-left: 40% + } + + [dir=rtl] .flex-offset-gt-xs-40, [dir=rtl] .offset-gt-xs-40 { + margin-left: auto; + margin-right: 40% + } + + .flex-offset-gt-xs-45, .offset-gt-xs-45 { + margin-left: 45% + } + + [dir=rtl] .flex-offset-gt-xs-45, [dir=rtl] .offset-gt-xs-45 { + margin-left: auto; + margin-right: 45% + } + + .flex-offset-gt-xs-50, .offset-gt-xs-50 { + margin-left: 50% + } + + [dir=rtl] .flex-offset-gt-xs-50, [dir=rtl] .offset-gt-xs-50 { + margin-left: auto; + margin-right: 50% + } + + .flex-offset-gt-xs-55, .offset-gt-xs-55 { + margin-left: 55% + } + + [dir=rtl] .flex-offset-gt-xs-55, [dir=rtl] .offset-gt-xs-55 { + margin-left: auto; + margin-right: 55% + } + + .flex-offset-gt-xs-60, .offset-gt-xs-60 { + margin-left: 60% + } + + [dir=rtl] .flex-offset-gt-xs-60, [dir=rtl] .offset-gt-xs-60 { + margin-left: auto; + margin-right: 60% + } + + .flex-offset-gt-xs-65, .offset-gt-xs-65 { + margin-left: 65% + } + + [dir=rtl] .flex-offset-gt-xs-65, [dir=rtl] .offset-gt-xs-65 { + margin-left: auto; + margin-right: 65% + } + + .flex-offset-gt-xs-70, .offset-gt-xs-70 { + margin-left: 70% + } + + [dir=rtl] .flex-offset-gt-xs-70, [dir=rtl] .offset-gt-xs-70 { + margin-left: auto; + margin-right: 70% + } + + .flex-offset-gt-xs-75, .offset-gt-xs-75 { + margin-left: 75% + } + + [dir=rtl] .flex-offset-gt-xs-75, [dir=rtl] .offset-gt-xs-75 { + margin-left: auto; + margin-right: 75% + } + + .flex-offset-gt-xs-80, .offset-gt-xs-80 { + margin-left: 80% + } + + [dir=rtl] .flex-offset-gt-xs-80, [dir=rtl] .offset-gt-xs-80 { + margin-left: auto; + margin-right: 80% + } + + .flex-offset-gt-xs-85, .offset-gt-xs-85 { + margin-left: 85% + } + + [dir=rtl] .flex-offset-gt-xs-85, [dir=rtl] .offset-gt-xs-85 { + margin-left: auto; + margin-right: 85% + } + + .flex-offset-gt-xs-90, .offset-gt-xs-90 { + margin-left: 90% + } + + [dir=rtl] .flex-offset-gt-xs-90, [dir=rtl] .offset-gt-xs-90 { + margin-left: auto; + margin-right: 90% + } + + .flex-offset-gt-xs-95, .offset-gt-xs-95 { + margin-left: 95% + } + + [dir=rtl] .flex-offset-gt-xs-95, [dir=rtl] .offset-gt-xs-95 { + margin-left: auto; + margin-right: 95% + } + + .flex-offset-gt-xs-33, .offset-gt-xs-33 { + margin-left: calc(100% / 3) + } + + .flex-offset-gt-xs-66, .offset-gt-xs-66 { + margin-left: calc(200% / 3) + } + + [dir=rtl] .flex-offset-gt-xs-66, [dir=rtl] .offset-gt-xs-66 { + margin-left: auto; + margin-right: calc(200% / 3) + } + + .layout-align-gt-xs { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch + } + + .layout-align-gt-xs-start, .layout-align-gt-xs-start-center, .layout-align-gt-xs-start-end, .layout-align-gt-xs-start-start, .layout-align-gt-xs-start-stretch { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start + } + + .layout-align-gt-xs-center, .layout-align-gt-xs-center-center, .layout-align-gt-xs-center-end, .layout-align-gt-xs-center-start, .layout-align-gt-xs-center-stretch { + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center + } + + .layout-align-gt-xs-end, .layout-align-gt-xs-end-center, .layout-align-gt-xs-end-end, .layout-align-gt-xs-end-start, .layout-align-gt-xs-end-stretch { + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end + } + + .layout-align-gt-xs-space-around, .layout-align-gt-xs-space-around-center, .layout-align-gt-xs-space-around-end, .layout-align-gt-xs-space-around-start, .layout-align-gt-xs-space-around-stretch { + -webkit-justify-content: space-around; + -ms-flex-pack: distribute; + justify-content: space-around + } + + .layout-align-gt-xs-space-between, .layout-align-gt-xs-space-between-center, .layout-align-gt-xs-space-between-end, .layout-align-gt-xs-space-between-start, .layout-align-gt-xs-space-between-stretch { + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between + } + + .layout-align-gt-xs-center-start, .layout-align-gt-xs-end-start, .layout-align-gt-xs-space-around-start, .layout-align-gt-xs-space-between-start, .layout-align-gt-xs-start-start { + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + -webkit-align-content: flex-start; + -ms-flex-line-pack: start; + align-content: flex-start + } + + .layout-align-gt-xs-center-center, .layout-align-gt-xs-end-center, .layout-align-gt-xs-space-around-center, .layout-align-gt-xs-space-between-center, .layout-align-gt-xs-start-center { + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-align-content: center; + -ms-flex-line-pack: center; + align-content: center; + max-width: 100% + } + + .layout-align-gt-xs-center-center > *, .layout-align-gt-xs-end-center > *, .layout-align-gt-xs-space-around-center > *, .layout-align-gt-xs-space-between-center > *, .layout-align-gt-xs-start-center > * { + max-width: 100%; + box-sizing: border-box + } + + .layout-align-gt-xs-center-end, .layout-align-gt-xs-end-end, .layout-align-gt-xs-space-around-end, .layout-align-gt-xs-space-between-end, .layout-align-gt-xs-start-end { + -webkit-align-items: flex-end; + -ms-flex-align: end; + align-items: flex-end; + -webkit-align-content: flex-end; + -ms-flex-line-pack: end; + align-content: flex-end + } + + .layout-align-gt-xs-center-stretch, .layout-align-gt-xs-end-stretch, .layout-align-gt-xs-space-around-stretch, .layout-align-gt-xs-space-between-stretch, .layout-align-gt-xs-start-stretch { + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch + } + + .flex-gt-xs { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + box-sizing: border-box + } + + .flex-gt-xs-grow { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + box-sizing: border-box + } + + .flex-gt-xs-initial { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-gt-xs-auto { + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + box-sizing: border-box + } + + .flex-gt-xs-none { + -webkit-flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + box-sizing: border-box + } + + .flex-gt-xs-noshrink { + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + box-sizing: border-box + } + + .flex-gt-xs-nogrow { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-gt-xs-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-xs-row > .flex-gt-xs-0, .layout-row > .flex-gt-xs-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box; + min-width: 0 + } + + .layout-column > .flex-gt-xs-0, .layout-gt-xs-column > .flex-gt-xs-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 100%; + max-height: 0; + box-sizing: border-box; + min-height: 0 + } + + .flex-gt-xs-5, .layout-gt-xs-row > .flex-gt-xs-5, .layout-row > .flex-gt-xs-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 5%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-5, .layout-gt-xs-column > .flex-gt-xs-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 100%; + max-height: 5%; + box-sizing: border-box + } + + .flex-gt-xs-10, .layout-gt-xs-row > .flex-gt-xs-10, .layout-row > .flex-gt-xs-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 10%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-10, .layout-gt-xs-column > .flex-gt-xs-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 100%; + max-height: 10%; + box-sizing: border-box + } + + .flex-gt-xs-15, .layout-gt-xs-row > .flex-gt-xs-15, .layout-row > .flex-gt-xs-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 15%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-15, .layout-gt-xs-column > .flex-gt-xs-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 100%; + max-height: 15%; + box-sizing: border-box + } + + .flex-gt-xs-20, .layout-gt-xs-row > .flex-gt-xs-20, .layout-row > .flex-gt-xs-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 20%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-20, .layout-gt-xs-column > .flex-gt-xs-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 100%; + max-height: 20%; + box-sizing: border-box + } + + .flex-gt-xs-25, .layout-gt-xs-row > .flex-gt-xs-25, .layout-row > .flex-gt-xs-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 25%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-25, .layout-gt-xs-column > .flex-gt-xs-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 100%; + max-height: 25%; + box-sizing: border-box + } + + .flex-gt-xs-30, .layout-gt-xs-row > .flex-gt-xs-30, .layout-row > .flex-gt-xs-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 30%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-30, .layout-gt-xs-column > .flex-gt-xs-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 100%; + max-height: 30%; + box-sizing: border-box + } + + .flex-gt-xs-35, .layout-gt-xs-row > .flex-gt-xs-35, .layout-row > .flex-gt-xs-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 35%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-35, .layout-gt-xs-column > .flex-gt-xs-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 100%; + max-height: 35%; + box-sizing: border-box + } + + .flex-gt-xs-40, .layout-gt-xs-row > .flex-gt-xs-40, .layout-row > .flex-gt-xs-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 40%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-40, .layout-gt-xs-column > .flex-gt-xs-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 100%; + max-height: 40%; + box-sizing: border-box + } + + .flex-gt-xs-45, .layout-gt-xs-row > .flex-gt-xs-45, .layout-row > .flex-gt-xs-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 45%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-45, .layout-gt-xs-column > .flex-gt-xs-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 100%; + max-height: 45%; + box-sizing: border-box + } + + .flex-gt-xs-50, .layout-gt-xs-row > .flex-gt-xs-50, .layout-row > .flex-gt-xs-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 50%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-50, .layout-gt-xs-column > .flex-gt-xs-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 100%; + max-height: 50%; + box-sizing: border-box + } + + .flex-gt-xs-55, .layout-gt-xs-row > .flex-gt-xs-55, .layout-row > .flex-gt-xs-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 55%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-55, .layout-gt-xs-column > .flex-gt-xs-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 100%; + max-height: 55%; + box-sizing: border-box + } + + .flex-gt-xs-60, .layout-gt-xs-row > .flex-gt-xs-60, .layout-row > .flex-gt-xs-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 60%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-60, .layout-gt-xs-column > .flex-gt-xs-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 100%; + max-height: 60%; + box-sizing: border-box + } + + .flex-gt-xs-65, .layout-gt-xs-row > .flex-gt-xs-65, .layout-row > .flex-gt-xs-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 65%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-65, .layout-gt-xs-column > .flex-gt-xs-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 100%; + max-height: 65%; + box-sizing: border-box + } + + .flex-gt-xs-70, .layout-gt-xs-row > .flex-gt-xs-70, .layout-row > .flex-gt-xs-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 70%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-70, .layout-gt-xs-column > .flex-gt-xs-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 100%; + max-height: 70%; + box-sizing: border-box + } + + .flex-gt-xs-75, .layout-gt-xs-row > .flex-gt-xs-75, .layout-row > .flex-gt-xs-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 75%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-75, .layout-gt-xs-column > .flex-gt-xs-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 100%; + max-height: 75%; + box-sizing: border-box + } + + .flex-gt-xs-80, .layout-gt-xs-row > .flex-gt-xs-80, .layout-row > .flex-gt-xs-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 80%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-80, .layout-gt-xs-column > .flex-gt-xs-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 100%; + max-height: 80%; + box-sizing: border-box + } + + .flex-gt-xs-85, .layout-gt-xs-row > .flex-gt-xs-85, .layout-row > .flex-gt-xs-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 85%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-85, .layout-gt-xs-column > .flex-gt-xs-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 100%; + max-height: 85%; + box-sizing: border-box + } + + .flex-gt-xs-90, .layout-gt-xs-row > .flex-gt-xs-90, .layout-row > .flex-gt-xs-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 90%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-90, .layout-gt-xs-column > .flex-gt-xs-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 100%; + max-height: 90%; + box-sizing: border-box + } + + .flex-gt-xs-95, .layout-gt-xs-row > .flex-gt-xs-95, .layout-row > .flex-gt-xs-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 95%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-95, .layout-gt-xs-column > .flex-gt-xs-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 100%; + max-height: 95%; + box-sizing: border-box + } + + .flex-gt-xs-100, .layout-column > .flex-gt-xs-100, .layout-gt-xs-column > .flex-gt-xs-100, .layout-gt-xs-row > .flex-gt-xs-100, .layout-row > .flex-gt-xs-100 { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + max-width: 100%; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-xs-row > .flex-gt-xs-33, .layout-row > .flex-gt-xs-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 33.33%; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-xs-row > .flex-gt-xs-66, .layout-row > .flex-gt-xs-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 66.66%; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-xs-row > .flex, .layout-row > .flex { + min-width: 0 + } + + .layout-column > .flex-gt-xs-33, .layout-gt-xs-column > .flex-gt-xs-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 100%; + max-height: 33.33%; + box-sizing: border-box + } + + .layout-column > .flex-gt-xs-66, .layout-gt-xs-column > .flex-gt-xs-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 100%; + max-height: 66.66%; + box-sizing: border-box + } + + .layout-column > .flex, .layout-gt-xs-column > .flex { + min-height: 0 + } + + .layout-gt-xs, .layout-gt-xs-column, .layout-gt-xs-row { + box-sizing: border-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex + } + + .layout-gt-xs-column { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column + } + + .layout-gt-xs-row { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row + } +} + +@media (min-width: 600px) and (max-width: 959px) { + .hide-gt-xs:not(.show-gt-xs):not(.show-sm):not(.show), .hide-sm:not(.show-gt-xs):not(.show-sm):not(.show), .hide:not(.show-gt-xs):not(.show-sm):not(.show) { + display: none + } + + .flex-order-sm--20 { + -webkit-order: -20; + -ms-flex-order: -20; + order: -20 + } + + .flex-order-sm--19 { + -webkit-order: -19; + -ms-flex-order: -19; + order: -19 + } + + .flex-order-sm--18 { + -webkit-order: -18; + -ms-flex-order: -18; + order: -18 + } + + .flex-order-sm--17 { + -webkit-order: -17; + -ms-flex-order: -17; + order: -17 + } + + .flex-order-sm--16 { + -webkit-order: -16; + -ms-flex-order: -16; + order: -16 + } + + .flex-order-sm--15 { + -webkit-order: -15; + -ms-flex-order: -15; + order: -15 + } + + .flex-order-sm--14 { + -webkit-order: -14; + -ms-flex-order: -14; + order: -14 + } + + .flex-order-sm--13 { + -webkit-order: -13; + -ms-flex-order: -13; + order: -13 + } + + .flex-order-sm--12 { + -webkit-order: -12; + -ms-flex-order: -12; + order: -12 + } + + .flex-order-sm--11 { + -webkit-order: -11; + -ms-flex-order: -11; + order: -11 + } + + .flex-order-sm--10 { + -webkit-order: -10; + -ms-flex-order: -10; + order: -10 + } + + .flex-order-sm--9 { + -webkit-order: -9; + -ms-flex-order: -9; + order: -9 + } + + .flex-order-sm--8 { + -webkit-order: -8; + -ms-flex-order: -8; + order: -8 + } + + .flex-order-sm--7 { + -webkit-order: -7; + -ms-flex-order: -7; + order: -7 + } + + .flex-order-sm--6 { + -webkit-order: -6; + -ms-flex-order: -6; + order: -6 + } + + .flex-order-sm--5 { + -webkit-order: -5; + -ms-flex-order: -5; + order: -5 + } + + .flex-order-sm--4 { + -webkit-order: -4; + -ms-flex-order: -4; + order: -4 + } + + .flex-order-sm--3 { + -webkit-order: -3; + -ms-flex-order: -3; + order: -3 + } + + .flex-order-sm--2 { + -webkit-order: -2; + -ms-flex-order: -2; + order: -2 + } + + .flex-order-sm--1 { + -webkit-order: -1; + -ms-flex-order: -1; + order: -1 + } + + .flex-order-sm-0 { + -webkit-order: 0; + -ms-flex-order: 0; + order: 0 + } + + .flex-order-sm-1 { + -webkit-order: 1; + -ms-flex-order: 1; + order: 1 + } + + .flex-order-sm-2 { + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 + } + + .flex-order-sm-3 { + -webkit-order: 3; + -ms-flex-order: 3; + order: 3 + } + + .flex-order-sm-4 { + -webkit-order: 4; + -ms-flex-order: 4; + order: 4 + } + + .flex-order-sm-5 { + -webkit-order: 5; + -ms-flex-order: 5; + order: 5 + } + + .flex-order-sm-6 { + -webkit-order: 6; + -ms-flex-order: 6; + order: 6 + } + + .flex-order-sm-7 { + -webkit-order: 7; + -ms-flex-order: 7; + order: 7 + } + + .flex-order-sm-8 { + -webkit-order: 8; + -ms-flex-order: 8; + order: 8 + } + + .flex-order-sm-9 { + -webkit-order: 9; + -ms-flex-order: 9; + order: 9 + } + + .flex-order-sm-10 { + -webkit-order: 10; + -ms-flex-order: 10; + order: 10 + } + + .flex-order-sm-11 { + -webkit-order: 11; + -ms-flex-order: 11; + order: 11 + } + + .flex-order-sm-12 { + -webkit-order: 12; + -ms-flex-order: 12; + order: 12 + } + + .flex-order-sm-13 { + -webkit-order: 13; + -ms-flex-order: 13; + order: 13 + } + + .flex-order-sm-14 { + -webkit-order: 14; + -ms-flex-order: 14; + order: 14 + } + + .flex-order-sm-15 { + -webkit-order: 15; + -ms-flex-order: 15; + order: 15 + } + + .flex-order-sm-16 { + -webkit-order: 16; + -ms-flex-order: 16; + order: 16 + } + + .flex-order-sm-17 { + -webkit-order: 17; + -ms-flex-order: 17; + order: 17 + } + + .flex-order-sm-18 { + -webkit-order: 18; + -ms-flex-order: 18; + order: 18 + } + + .flex-order-sm-19 { + -webkit-order: 19; + -ms-flex-order: 19; + order: 19 + } + + .flex-order-sm-20 { + -webkit-order: 20; + -ms-flex-order: 20; + order: 20 + } + + .flex-offset-sm-0, .offset-sm-0 { + margin-left: 0 + } + + [dir=rtl] .flex-offset-sm-0, [dir=rtl] .offset-sm-0 { + margin-left: auto; + margin-right: 0 + } + + .flex-offset-sm-5, .offset-sm-5 { + margin-left: 5% + } + + [dir=rtl] .flex-offset-sm-5, [dir=rtl] .offset-sm-5 { + margin-left: auto; + margin-right: 5% + } + + .flex-offset-sm-10, .offset-sm-10 { + margin-left: 10% + } + + [dir=rtl] .flex-offset-sm-10, [dir=rtl] .offset-sm-10 { + margin-left: auto; + margin-right: 10% + } + + .flex-offset-sm-15, .offset-sm-15 { + margin-left: 15% + } + + [dir=rtl] .flex-offset-sm-15, [dir=rtl] .offset-sm-15 { + margin-left: auto; + margin-right: 15% + } + + .flex-offset-sm-20, .offset-sm-20 { + margin-left: 20% + } + + [dir=rtl] .flex-offset-sm-20, [dir=rtl] .offset-sm-20 { + margin-left: auto; + margin-right: 20% + } + + .flex-offset-sm-25, .offset-sm-25 { + margin-left: 25% + } + + [dir=rtl] .flex-offset-sm-25, [dir=rtl] .offset-sm-25 { + margin-left: auto; + margin-right: 25% + } + + .flex-offset-sm-30, .offset-sm-30 { + margin-left: 30% + } + + [dir=rtl] .flex-offset-sm-30, [dir=rtl] .offset-sm-30 { + margin-left: auto; + margin-right: 30% + } + + .flex-offset-sm-35, .offset-sm-35 { + margin-left: 35% + } + + [dir=rtl] .flex-offset-sm-35, [dir=rtl] .offset-sm-35 { + margin-left: auto; + margin-right: 35% + } + + .flex-offset-sm-40, .offset-sm-40 { + margin-left: 40% + } + + [dir=rtl] .flex-offset-sm-40, [dir=rtl] .offset-sm-40 { + margin-left: auto; + margin-right: 40% + } + + .flex-offset-sm-45, .offset-sm-45 { + margin-left: 45% + } + + [dir=rtl] .flex-offset-sm-45, [dir=rtl] .offset-sm-45 { + margin-left: auto; + margin-right: 45% + } + + .flex-offset-sm-50, .offset-sm-50 { + margin-left: 50% + } + + [dir=rtl] .flex-offset-sm-50, [dir=rtl] .offset-sm-50 { + margin-left: auto; + margin-right: 50% + } + + .flex-offset-sm-55, .offset-sm-55 { + margin-left: 55% + } + + [dir=rtl] .flex-offset-sm-55, [dir=rtl] .offset-sm-55 { + margin-left: auto; + margin-right: 55% + } + + .flex-offset-sm-60, .offset-sm-60 { + margin-left: 60% + } + + [dir=rtl] .flex-offset-sm-60, [dir=rtl] .offset-sm-60 { + margin-left: auto; + margin-right: 60% + } + + .flex-offset-sm-65, .offset-sm-65 { + margin-left: 65% + } + + [dir=rtl] .flex-offset-sm-65, [dir=rtl] .offset-sm-65 { + margin-left: auto; + margin-right: 65% + } + + .flex-offset-sm-70, .offset-sm-70 { + margin-left: 70% + } + + [dir=rtl] .flex-offset-sm-70, [dir=rtl] .offset-sm-70 { + margin-left: auto; + margin-right: 70% + } + + .flex-offset-sm-75, .offset-sm-75 { + margin-left: 75% + } + + [dir=rtl] .flex-offset-sm-75, [dir=rtl] .offset-sm-75 { + margin-left: auto; + margin-right: 75% + } + + .flex-offset-sm-80, .offset-sm-80 { + margin-left: 80% + } + + [dir=rtl] .flex-offset-sm-80, [dir=rtl] .offset-sm-80 { + margin-left: auto; + margin-right: 80% + } + + .flex-offset-sm-85, .offset-sm-85 { + margin-left: 85% + } + + [dir=rtl] .flex-offset-sm-85, [dir=rtl] .offset-sm-85 { + margin-left: auto; + margin-right: 85% + } + + .flex-offset-sm-90, .offset-sm-90 { + margin-left: 90% + } + + [dir=rtl] .flex-offset-sm-90, [dir=rtl] .offset-sm-90 { + margin-left: auto; + margin-right: 90% + } + + .flex-offset-sm-95, .offset-sm-95 { + margin-left: 95% + } + + [dir=rtl] .flex-offset-sm-95, [dir=rtl] .offset-sm-95 { + margin-left: auto; + margin-right: 95% + } + + .flex-offset-sm-33, .offset-sm-33 { + margin-left: calc(100% / 3) + } + + .flex-offset-sm-66, .offset-sm-66 { + margin-left: calc(200% / 3) + } + + [dir=rtl] .flex-offset-sm-66, [dir=rtl] .offset-sm-66 { + margin-left: auto; + margin-right: calc(200% / 3) + } + + .layout-align-sm { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch + } + + .layout-align-sm-start, .layout-align-sm-start-center, .layout-align-sm-start-end, .layout-align-sm-start-start, .layout-align-sm-start-stretch { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start + } + + .layout-align-sm-center, .layout-align-sm-center-center, .layout-align-sm-center-end, .layout-align-sm-center-start, .layout-align-sm-center-stretch { + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center + } + + .layout-align-sm-end, .layout-align-sm-end-center, .layout-align-sm-end-end, .layout-align-sm-end-start, .layout-align-sm-end-stretch { + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end + } + + .layout-align-sm-space-around, .layout-align-sm-space-around-center, .layout-align-sm-space-around-end, .layout-align-sm-space-around-start, .layout-align-sm-space-around-stretch { + -webkit-justify-content: space-around; + -ms-flex-pack: distribute; + justify-content: space-around + } + + .layout-align-sm-space-between, .layout-align-sm-space-between-center, .layout-align-sm-space-between-end, .layout-align-sm-space-between-start, .layout-align-sm-space-between-stretch { + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between + } + + .layout-align-sm-center-start, .layout-align-sm-end-start, .layout-align-sm-space-around-start, .layout-align-sm-space-between-start, .layout-align-sm-start-start { + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + -webkit-align-content: flex-start; + -ms-flex-line-pack: start; + align-content: flex-start + } + + .layout-align-sm-center-center, .layout-align-sm-end-center, .layout-align-sm-space-around-center, .layout-align-sm-space-between-center, .layout-align-sm-start-center { + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-align-content: center; + -ms-flex-line-pack: center; + align-content: center; + max-width: 100% + } + + .layout-align-sm-center-center > *, .layout-align-sm-end-center > *, .layout-align-sm-space-around-center > *, .layout-align-sm-space-between-center > *, .layout-align-sm-start-center > * { + max-width: 100%; + box-sizing: border-box + } + + .layout-align-sm-center-end, .layout-align-sm-end-end, .layout-align-sm-space-around-end, .layout-align-sm-space-between-end, .layout-align-sm-start-end { + -webkit-align-items: flex-end; + -ms-flex-align: end; + align-items: flex-end; + -webkit-align-content: flex-end; + -ms-flex-line-pack: end; + align-content: flex-end + } + + .layout-align-sm-center-stretch, .layout-align-sm-end-stretch, .layout-align-sm-space-around-stretch, .layout-align-sm-space-between-stretch, .layout-align-sm-start-stretch { + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch + } + + .flex-sm { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + box-sizing: border-box + } + + .flex-sm-grow { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + box-sizing: border-box + } + + .flex-sm-initial { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-sm-auto { + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + box-sizing: border-box + } + + .flex-sm-none { + -webkit-flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + box-sizing: border-box + } + + .flex-sm-noshrink { + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + box-sizing: border-box + } + + .flex-sm-nogrow { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-sm-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box + } + + .layout-row > .flex-sm-0, .layout-sm-row > .flex-sm-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box; + min-width: 0 + } + + .layout-column > .flex-sm-0, .layout-sm-column > .flex-sm-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 100%; + max-height: 0; + box-sizing: border-box; + min-height: 0 + } + + .flex-sm-5, .layout-row > .flex-sm-5, .layout-sm-row > .flex-sm-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 5%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-5, .layout-sm-column > .flex-sm-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 100%; + max-height: 5%; + box-sizing: border-box + } + + .flex-sm-10, .layout-row > .flex-sm-10, .layout-sm-row > .flex-sm-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 10%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-10, .layout-sm-column > .flex-sm-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 100%; + max-height: 10%; + box-sizing: border-box + } + + .flex-sm-15, .layout-row > .flex-sm-15, .layout-sm-row > .flex-sm-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 15%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-15, .layout-sm-column > .flex-sm-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 100%; + max-height: 15%; + box-sizing: border-box + } + + .flex-sm-20, .layout-row > .flex-sm-20, .layout-sm-row > .flex-sm-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 20%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-20, .layout-sm-column > .flex-sm-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 100%; + max-height: 20%; + box-sizing: border-box + } + + .flex-sm-25, .layout-row > .flex-sm-25, .layout-sm-row > .flex-sm-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 25%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-25, .layout-sm-column > .flex-sm-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 100%; + max-height: 25%; + box-sizing: border-box + } + + .flex-sm-30, .layout-row > .flex-sm-30, .layout-sm-row > .flex-sm-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 30%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-30, .layout-sm-column > .flex-sm-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 100%; + max-height: 30%; + box-sizing: border-box + } + + .flex-sm-35, .layout-row > .flex-sm-35, .layout-sm-row > .flex-sm-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 35%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-35, .layout-sm-column > .flex-sm-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 100%; + max-height: 35%; + box-sizing: border-box + } + + .flex-sm-40, .layout-row > .flex-sm-40, .layout-sm-row > .flex-sm-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 40%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-40, .layout-sm-column > .flex-sm-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 100%; + max-height: 40%; + box-sizing: border-box + } + + .flex-sm-45, .layout-row > .flex-sm-45, .layout-sm-row > .flex-sm-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 45%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-45, .layout-sm-column > .flex-sm-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 100%; + max-height: 45%; + box-sizing: border-box + } + + .flex-sm-50, .layout-row > .flex-sm-50, .layout-sm-row > .flex-sm-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 50%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-50, .layout-sm-column > .flex-sm-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 100%; + max-height: 50%; + box-sizing: border-box + } + + .flex-sm-55, .layout-row > .flex-sm-55, .layout-sm-row > .flex-sm-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 55%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-55, .layout-sm-column > .flex-sm-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 100%; + max-height: 55%; + box-sizing: border-box + } + + .flex-sm-60, .layout-row > .flex-sm-60, .layout-sm-row > .flex-sm-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 60%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-60, .layout-sm-column > .flex-sm-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 100%; + max-height: 60%; + box-sizing: border-box + } + + .flex-sm-65, .layout-row > .flex-sm-65, .layout-sm-row > .flex-sm-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 65%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-65, .layout-sm-column > .flex-sm-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 100%; + max-height: 65%; + box-sizing: border-box + } + + .flex-sm-70, .layout-row > .flex-sm-70, .layout-sm-row > .flex-sm-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 70%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-70, .layout-sm-column > .flex-sm-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 100%; + max-height: 70%; + box-sizing: border-box + } + + .flex-sm-75, .layout-row > .flex-sm-75, .layout-sm-row > .flex-sm-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 75%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-75, .layout-sm-column > .flex-sm-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 100%; + max-height: 75%; + box-sizing: border-box + } + + .flex-sm-80, .layout-row > .flex-sm-80, .layout-sm-row > .flex-sm-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 80%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-80, .layout-sm-column > .flex-sm-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 100%; + max-height: 80%; + box-sizing: border-box + } + + .flex-sm-85, .layout-row > .flex-sm-85, .layout-sm-row > .flex-sm-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 85%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-85, .layout-sm-column > .flex-sm-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 100%; + max-height: 85%; + box-sizing: border-box + } + + .flex-sm-90, .layout-row > .flex-sm-90, .layout-sm-row > .flex-sm-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 90%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-90, .layout-sm-column > .flex-sm-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 100%; + max-height: 90%; + box-sizing: border-box + } + + .flex-sm-95, .layout-row > .flex-sm-95, .layout-sm-row > .flex-sm-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 95%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-sm-95, .layout-sm-column > .flex-sm-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 100%; + max-height: 95%; + box-sizing: border-box + } + + .flex-sm-100, .layout-column > .flex-sm-100, .layout-row > .flex-sm-100, .layout-sm-column > .flex-sm-100, .layout-sm-row > .flex-sm-100 { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + max-width: 100%; + max-height: 100%; + box-sizing: border-box + } + + .layout-row > .flex-sm-33, .layout-sm-row > .flex-sm-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 33.33%; + max-height: 100%; + box-sizing: border-box + } + + .layout-row > .flex-sm-66, .layout-sm-row > .flex-sm-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 66.66%; + max-height: 100%; + box-sizing: border-box + } + + .layout-row > .flex, .layout-sm-row > .flex { + min-width: 0 + } + + .layout-column > .flex-sm-33, .layout-sm-column > .flex-sm-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 100%; + max-height: 33.33%; + box-sizing: border-box + } + + .layout-column > .flex-sm-66, .layout-sm-column > .flex-sm-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 100%; + max-height: 66.66%; + box-sizing: border-box + } + + .layout-column > .flex, .layout-sm-column > .flex { + min-height: 0 + } + + .layout-sm, .layout-sm-column, .layout-sm-row { + box-sizing: border-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex + } + + .layout-sm-column { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column + } + + .layout-sm-row { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row + } +} + +@media (min-width: 960px) { + .flex-order-gt-sm--20 { + -webkit-order: -20; + -ms-flex-order: -20; + order: -20 + } + + .flex-order-gt-sm--19 { + -webkit-order: -19; + -ms-flex-order: -19; + order: -19 + } + + .flex-order-gt-sm--18 { + -webkit-order: -18; + -ms-flex-order: -18; + order: -18 + } + + .flex-order-gt-sm--17 { + -webkit-order: -17; + -ms-flex-order: -17; + order: -17 + } + + .flex-order-gt-sm--16 { + -webkit-order: -16; + -ms-flex-order: -16; + order: -16 + } + + .flex-order-gt-sm--15 { + -webkit-order: -15; + -ms-flex-order: -15; + order: -15 + } + + .flex-order-gt-sm--14 { + -webkit-order: -14; + -ms-flex-order: -14; + order: -14 + } + + .flex-order-gt-sm--13 { + -webkit-order: -13; + -ms-flex-order: -13; + order: -13 + } + + .flex-order-gt-sm--12 { + -webkit-order: -12; + -ms-flex-order: -12; + order: -12 + } + + .flex-order-gt-sm--11 { + -webkit-order: -11; + -ms-flex-order: -11; + order: -11 + } + + .flex-order-gt-sm--10 { + -webkit-order: -10; + -ms-flex-order: -10; + order: -10 + } + + .flex-order-gt-sm--9 { + -webkit-order: -9; + -ms-flex-order: -9; + order: -9 + } + + .flex-order-gt-sm--8 { + -webkit-order: -8; + -ms-flex-order: -8; + order: -8 + } + + .flex-order-gt-sm--7 { + -webkit-order: -7; + -ms-flex-order: -7; + order: -7 + } + + .flex-order-gt-sm--6 { + -webkit-order: -6; + -ms-flex-order: -6; + order: -6 + } + + .flex-order-gt-sm--5 { + -webkit-order: -5; + -ms-flex-order: -5; + order: -5 + } + + .flex-order-gt-sm--4 { + -webkit-order: -4; + -ms-flex-order: -4; + order: -4 + } + + .flex-order-gt-sm--3 { + -webkit-order: -3; + -ms-flex-order: -3; + order: -3 + } + + .flex-order-gt-sm--2 { + -webkit-order: -2; + -ms-flex-order: -2; + order: -2 + } + + .flex-order-gt-sm--1 { + -webkit-order: -1; + -ms-flex-order: -1; + order: -1 + } + + .flex-order-gt-sm-0 { + -webkit-order: 0; + -ms-flex-order: 0; + order: 0 + } + + .flex-order-gt-sm-1 { + -webkit-order: 1; + -ms-flex-order: 1; + order: 1 + } + + .flex-order-gt-sm-2 { + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 + } + + .flex-order-gt-sm-3 { + -webkit-order: 3; + -ms-flex-order: 3; + order: 3 + } + + .flex-order-gt-sm-4 { + -webkit-order: 4; + -ms-flex-order: 4; + order: 4 + } + + .flex-order-gt-sm-5 { + -webkit-order: 5; + -ms-flex-order: 5; + order: 5 + } + + .flex-order-gt-sm-6 { + -webkit-order: 6; + -ms-flex-order: 6; + order: 6 + } + + .flex-order-gt-sm-7 { + -webkit-order: 7; + -ms-flex-order: 7; + order: 7 + } + + .flex-order-gt-sm-8 { + -webkit-order: 8; + -ms-flex-order: 8; + order: 8 + } + + .flex-order-gt-sm-9 { + -webkit-order: 9; + -ms-flex-order: 9; + order: 9 + } + + .flex-order-gt-sm-10 { + -webkit-order: 10; + -ms-flex-order: 10; + order: 10 + } + + .flex-order-gt-sm-11 { + -webkit-order: 11; + -ms-flex-order: 11; + order: 11 + } + + .flex-order-gt-sm-12 { + -webkit-order: 12; + -ms-flex-order: 12; + order: 12 + } + + .flex-order-gt-sm-13 { + -webkit-order: 13; + -ms-flex-order: 13; + order: 13 + } + + .flex-order-gt-sm-14 { + -webkit-order: 14; + -ms-flex-order: 14; + order: 14 + } + + .flex-order-gt-sm-15 { + -webkit-order: 15; + -ms-flex-order: 15; + order: 15 + } + + .flex-order-gt-sm-16 { + -webkit-order: 16; + -ms-flex-order: 16; + order: 16 + } + + .flex-order-gt-sm-17 { + -webkit-order: 17; + -ms-flex-order: 17; + order: 17 + } + + .flex-order-gt-sm-18 { + -webkit-order: 18; + -ms-flex-order: 18; + order: 18 + } + + .flex-order-gt-sm-19 { + -webkit-order: 19; + -ms-flex-order: 19; + order: 19 + } + + .flex-order-gt-sm-20 { + -webkit-order: 20; + -ms-flex-order: 20; + order: 20 + } + + .flex-offset-gt-sm-0, .offset-gt-sm-0 { + margin-left: 0 + } + + [dir=rtl] .flex-offset-gt-sm-0, [dir=rtl] .offset-gt-sm-0 { + margin-left: auto; + margin-right: 0 + } + + .flex-offset-gt-sm-5, .offset-gt-sm-5 { + margin-left: 5% + } + + [dir=rtl] .flex-offset-gt-sm-5, [dir=rtl] .offset-gt-sm-5 { + margin-left: auto; + margin-right: 5% + } + + .flex-offset-gt-sm-10, .offset-gt-sm-10 { + margin-left: 10% + } + + [dir=rtl] .flex-offset-gt-sm-10, [dir=rtl] .offset-gt-sm-10 { + margin-left: auto; + margin-right: 10% + } + + .flex-offset-gt-sm-15, .offset-gt-sm-15 { + margin-left: 15% + } + + [dir=rtl] .flex-offset-gt-sm-15, [dir=rtl] .offset-gt-sm-15 { + margin-left: auto; + margin-right: 15% + } + + .flex-offset-gt-sm-20, .offset-gt-sm-20 { + margin-left: 20% + } + + [dir=rtl] .flex-offset-gt-sm-20, [dir=rtl] .offset-gt-sm-20 { + margin-left: auto; + margin-right: 20% + } + + .flex-offset-gt-sm-25, .offset-gt-sm-25 { + margin-left: 25% + } + + [dir=rtl] .flex-offset-gt-sm-25, [dir=rtl] .offset-gt-sm-25 { + margin-left: auto; + margin-right: 25% + } + + .flex-offset-gt-sm-30, .offset-gt-sm-30 { + margin-left: 30% + } + + [dir=rtl] .flex-offset-gt-sm-30, [dir=rtl] .offset-gt-sm-30 { + margin-left: auto; + margin-right: 30% + } + + .flex-offset-gt-sm-35, .offset-gt-sm-35 { + margin-left: 35% + } + + [dir=rtl] .flex-offset-gt-sm-35, [dir=rtl] .offset-gt-sm-35 { + margin-left: auto; + margin-right: 35% + } + + .flex-offset-gt-sm-40, .offset-gt-sm-40 { + margin-left: 40% + } + + [dir=rtl] .flex-offset-gt-sm-40, [dir=rtl] .offset-gt-sm-40 { + margin-left: auto; + margin-right: 40% + } + + .flex-offset-gt-sm-45, .offset-gt-sm-45 { + margin-left: 45% + } + + [dir=rtl] .flex-offset-gt-sm-45, [dir=rtl] .offset-gt-sm-45 { + margin-left: auto; + margin-right: 45% + } + + .flex-offset-gt-sm-50, .offset-gt-sm-50 { + margin-left: 50% + } + + [dir=rtl] .flex-offset-gt-sm-50, [dir=rtl] .offset-gt-sm-50 { + margin-left: auto; + margin-right: 50% + } + + .flex-offset-gt-sm-55, .offset-gt-sm-55 { + margin-left: 55% + } + + [dir=rtl] .flex-offset-gt-sm-55, [dir=rtl] .offset-gt-sm-55 { + margin-left: auto; + margin-right: 55% + } + + .flex-offset-gt-sm-60, .offset-gt-sm-60 { + margin-left: 60% + } + + [dir=rtl] .flex-offset-gt-sm-60, [dir=rtl] .offset-gt-sm-60 { + margin-left: auto; + margin-right: 60% + } + + .flex-offset-gt-sm-65, .offset-gt-sm-65 { + margin-left: 65% + } + + [dir=rtl] .flex-offset-gt-sm-65, [dir=rtl] .offset-gt-sm-65 { + margin-left: auto; + margin-right: 65% + } + + .flex-offset-gt-sm-70, .offset-gt-sm-70 { + margin-left: 70% + } + + [dir=rtl] .flex-offset-gt-sm-70, [dir=rtl] .offset-gt-sm-70 { + margin-left: auto; + margin-right: 70% + } + + .flex-offset-gt-sm-75, .offset-gt-sm-75 { + margin-left: 75% + } + + [dir=rtl] .flex-offset-gt-sm-75, [dir=rtl] .offset-gt-sm-75 { + margin-left: auto; + margin-right: 75% + } + + .flex-offset-gt-sm-80, .offset-gt-sm-80 { + margin-left: 80% + } + + [dir=rtl] .flex-offset-gt-sm-80, [dir=rtl] .offset-gt-sm-80 { + margin-left: auto; + margin-right: 80% + } + + .flex-offset-gt-sm-85, .offset-gt-sm-85 { + margin-left: 85% + } + + [dir=rtl] .flex-offset-gt-sm-85, [dir=rtl] .offset-gt-sm-85 { + margin-left: auto; + margin-right: 85% + } + + .flex-offset-gt-sm-90, .offset-gt-sm-90 { + margin-left: 90% + } + + [dir=rtl] .flex-offset-gt-sm-90, [dir=rtl] .offset-gt-sm-90 { + margin-left: auto; + margin-right: 90% + } + + .flex-offset-gt-sm-95, .offset-gt-sm-95 { + margin-left: 95% + } + + [dir=rtl] .flex-offset-gt-sm-95, [dir=rtl] .offset-gt-sm-95 { + margin-left: auto; + margin-right: 95% + } + + .flex-offset-gt-sm-33, .offset-gt-sm-33 { + margin-left: calc(100% / 3) + } + + .flex-offset-gt-sm-66, .offset-gt-sm-66 { + margin-left: calc(200% / 3) + } + + [dir=rtl] .flex-offset-gt-sm-66, [dir=rtl] .offset-gt-sm-66 { + margin-left: auto; + margin-right: calc(200% / 3) + } + + .layout-align-gt-sm { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch + } + + .layout-align-gt-sm-start, .layout-align-gt-sm-start-center, .layout-align-gt-sm-start-end, .layout-align-gt-sm-start-start, .layout-align-gt-sm-start-stretch { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start + } + + .layout-align-gt-sm-center, .layout-align-gt-sm-center-center, .layout-align-gt-sm-center-end, .layout-align-gt-sm-center-start, .layout-align-gt-sm-center-stretch { + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center + } + + .layout-align-gt-sm-end, .layout-align-gt-sm-end-center, .layout-align-gt-sm-end-end, .layout-align-gt-sm-end-start, .layout-align-gt-sm-end-stretch { + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end + } + + .layout-align-gt-sm-space-around, .layout-align-gt-sm-space-around-center, .layout-align-gt-sm-space-around-end, .layout-align-gt-sm-space-around-start, .layout-align-gt-sm-space-around-stretch { + -webkit-justify-content: space-around; + -ms-flex-pack: distribute; + justify-content: space-around + } + + .layout-align-gt-sm-space-between, .layout-align-gt-sm-space-between-center, .layout-align-gt-sm-space-between-end, .layout-align-gt-sm-space-between-start, .layout-align-gt-sm-space-between-stretch { + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between + } + + .layout-align-gt-sm-center-start, .layout-align-gt-sm-end-start, .layout-align-gt-sm-space-around-start, .layout-align-gt-sm-space-between-start, .layout-align-gt-sm-start-start { + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + -webkit-align-content: flex-start; + -ms-flex-line-pack: start; + align-content: flex-start + } + + .layout-align-gt-sm-center-center, .layout-align-gt-sm-end-center, .layout-align-gt-sm-space-around-center, .layout-align-gt-sm-space-between-center, .layout-align-gt-sm-start-center { + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-align-content: center; + -ms-flex-line-pack: center; + align-content: center; + max-width: 100% + } + + .layout-align-gt-sm-center-center > *, .layout-align-gt-sm-end-center > *, .layout-align-gt-sm-space-around-center > *, .layout-align-gt-sm-space-between-center > *, .layout-align-gt-sm-start-center > * { + max-width: 100%; + box-sizing: border-box + } + + .layout-align-gt-sm-center-end, .layout-align-gt-sm-end-end, .layout-align-gt-sm-space-around-end, .layout-align-gt-sm-space-between-end, .layout-align-gt-sm-start-end { + -webkit-align-items: flex-end; + -ms-flex-align: end; + align-items: flex-end; + -webkit-align-content: flex-end; + -ms-flex-line-pack: end; + align-content: flex-end + } + + .layout-align-gt-sm-center-stretch, .layout-align-gt-sm-end-stretch, .layout-align-gt-sm-space-around-stretch, .layout-align-gt-sm-space-between-stretch, .layout-align-gt-sm-start-stretch { + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch + } + + .flex-gt-sm { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + box-sizing: border-box + } + + .flex-gt-sm-grow { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + box-sizing: border-box + } + + .flex-gt-sm-initial { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-gt-sm-auto { + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + box-sizing: border-box + } + + .flex-gt-sm-none { + -webkit-flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + box-sizing: border-box + } + + .flex-gt-sm-noshrink { + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + box-sizing: border-box + } + + .flex-gt-sm-nogrow { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-gt-sm-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-sm-row > .flex-gt-sm-0, .layout-row > .flex-gt-sm-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box; + min-width: 0 + } + + .layout-column > .flex-gt-sm-0, .layout-gt-sm-column > .flex-gt-sm-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 100%; + max-height: 0; + box-sizing: border-box; + min-height: 0 + } + + .flex-gt-sm-5, .layout-gt-sm-row > .flex-gt-sm-5, .layout-row > .flex-gt-sm-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 5%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-5, .layout-gt-sm-column > .flex-gt-sm-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 100%; + max-height: 5%; + box-sizing: border-box + } + + .flex-gt-sm-10, .layout-gt-sm-row > .flex-gt-sm-10, .layout-row > .flex-gt-sm-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 10%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-10, .layout-gt-sm-column > .flex-gt-sm-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 100%; + max-height: 10%; + box-sizing: border-box + } + + .flex-gt-sm-15, .layout-gt-sm-row > .flex-gt-sm-15, .layout-row > .flex-gt-sm-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 15%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-15, .layout-gt-sm-column > .flex-gt-sm-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 100%; + max-height: 15%; + box-sizing: border-box + } + + .flex-gt-sm-20, .layout-gt-sm-row > .flex-gt-sm-20, .layout-row > .flex-gt-sm-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 20%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-20, .layout-gt-sm-column > .flex-gt-sm-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 100%; + max-height: 20%; + box-sizing: border-box + } + + .flex-gt-sm-25, .layout-gt-sm-row > .flex-gt-sm-25, .layout-row > .flex-gt-sm-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 25%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-25, .layout-gt-sm-column > .flex-gt-sm-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 100%; + max-height: 25%; + box-sizing: border-box + } + + .flex-gt-sm-30, .layout-gt-sm-row > .flex-gt-sm-30, .layout-row > .flex-gt-sm-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 30%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-30, .layout-gt-sm-column > .flex-gt-sm-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 100%; + max-height: 30%; + box-sizing: border-box + } + + .flex-gt-sm-35, .layout-gt-sm-row > .flex-gt-sm-35, .layout-row > .flex-gt-sm-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 35%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-35, .layout-gt-sm-column > .flex-gt-sm-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 100%; + max-height: 35%; + box-sizing: border-box + } + + .flex-gt-sm-40, .layout-gt-sm-row > .flex-gt-sm-40, .layout-row > .flex-gt-sm-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 40%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-40, .layout-gt-sm-column > .flex-gt-sm-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 100%; + max-height: 40%; + box-sizing: border-box + } + + .flex-gt-sm-45, .layout-gt-sm-row > .flex-gt-sm-45, .layout-row > .flex-gt-sm-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 45%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-45, .layout-gt-sm-column > .flex-gt-sm-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 100%; + max-height: 45%; + box-sizing: border-box + } + + .flex-gt-sm-50, .layout-gt-sm-row > .flex-gt-sm-50, .layout-row > .flex-gt-sm-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 50%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-50, .layout-gt-sm-column > .flex-gt-sm-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 100%; + max-height: 50%; + box-sizing: border-box + } + + .flex-gt-sm-55, .layout-gt-sm-row > .flex-gt-sm-55, .layout-row > .flex-gt-sm-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 55%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-55, .layout-gt-sm-column > .flex-gt-sm-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 100%; + max-height: 55%; + box-sizing: border-box + } + + .flex-gt-sm-60, .layout-gt-sm-row > .flex-gt-sm-60, .layout-row > .flex-gt-sm-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 60%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-60, .layout-gt-sm-column > .flex-gt-sm-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 100%; + max-height: 60%; + box-sizing: border-box + } + + .flex-gt-sm-65, .layout-gt-sm-row > .flex-gt-sm-65, .layout-row > .flex-gt-sm-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 65%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-65, .layout-gt-sm-column > .flex-gt-sm-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 100%; + max-height: 65%; + box-sizing: border-box + } + + .flex-gt-sm-70, .layout-gt-sm-row > .flex-gt-sm-70, .layout-row > .flex-gt-sm-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 70%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-70, .layout-gt-sm-column > .flex-gt-sm-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 100%; + max-height: 70%; + box-sizing: border-box + } + + .flex-gt-sm-75, .layout-gt-sm-row > .flex-gt-sm-75, .layout-row > .flex-gt-sm-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 75%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-75, .layout-gt-sm-column > .flex-gt-sm-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 100%; + max-height: 75%; + box-sizing: border-box + } + + .flex-gt-sm-80, .layout-gt-sm-row > .flex-gt-sm-80, .layout-row > .flex-gt-sm-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 80%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-80, .layout-gt-sm-column > .flex-gt-sm-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 100%; + max-height: 80%; + box-sizing: border-box + } + + .flex-gt-sm-85, .layout-gt-sm-row > .flex-gt-sm-85, .layout-row > .flex-gt-sm-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 85%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-85, .layout-gt-sm-column > .flex-gt-sm-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 100%; + max-height: 85%; + box-sizing: border-box + } + + .flex-gt-sm-90, .layout-gt-sm-row > .flex-gt-sm-90, .layout-row > .flex-gt-sm-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 90%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-90, .layout-gt-sm-column > .flex-gt-sm-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 100%; + max-height: 90%; + box-sizing: border-box + } + + .flex-gt-sm-95, .layout-gt-sm-row > .flex-gt-sm-95, .layout-row > .flex-gt-sm-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 95%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-95, .layout-gt-sm-column > .flex-gt-sm-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 100%; + max-height: 95%; + box-sizing: border-box + } + + .flex-gt-sm-100, .layout-column > .flex-gt-sm-100, .layout-gt-sm-column > .flex-gt-sm-100, .layout-gt-sm-row > .flex-gt-sm-100, .layout-row > .flex-gt-sm-100 { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + max-width: 100%; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-sm-row > .flex-gt-sm-33, .layout-row > .flex-gt-sm-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 33.33%; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-sm-row > .flex-gt-sm-66, .layout-row > .flex-gt-sm-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 66.66%; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-sm-row > .flex, .layout-row > .flex { + min-width: 0 + } + + .layout-column > .flex-gt-sm-33, .layout-gt-sm-column > .flex-gt-sm-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 100%; + max-height: 33.33%; + box-sizing: border-box + } + + .layout-column > .flex-gt-sm-66, .layout-gt-sm-column > .flex-gt-sm-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 100%; + max-height: 66.66%; + box-sizing: border-box + } + + .layout-column > .flex, .layout-gt-sm-column > .flex { + min-height: 0 + } + + .layout-gt-sm, .layout-gt-sm-column, .layout-gt-sm-row { + box-sizing: border-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex + } + + .layout-gt-sm-column { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column + } + + .layout-gt-sm-row { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row + } +} + +@media (min-width: 960px) and (max-width: 1279px) { + .hide-gt-sm:not(.show-gt-xs):not(.show-gt-sm):not(.show-md):not(.show), .hide-gt-xs:not(.show-gt-xs):not(.show-gt-sm):not(.show-md):not(.show), .hide-md:not(.show-md):not(.show-gt-sm):not(.show-gt-xs):not(.show), .hide:not(.show-gt-xs):not(.show-gt-sm):not(.show-md):not(.show) { + display: none + } + + .flex-order-md--20 { + -webkit-order: -20; + -ms-flex-order: -20; + order: -20 + } + + .flex-order-md--19 { + -webkit-order: -19; + -ms-flex-order: -19; + order: -19 + } + + .flex-order-md--18 { + -webkit-order: -18; + -ms-flex-order: -18; + order: -18 + } + + .flex-order-md--17 { + -webkit-order: -17; + -ms-flex-order: -17; + order: -17 + } + + .flex-order-md--16 { + -webkit-order: -16; + -ms-flex-order: -16; + order: -16 + } + + .flex-order-md--15 { + -webkit-order: -15; + -ms-flex-order: -15; + order: -15 + } + + .flex-order-md--14 { + -webkit-order: -14; + -ms-flex-order: -14; + order: -14 + } + + .flex-order-md--13 { + -webkit-order: -13; + -ms-flex-order: -13; + order: -13 + } + + .flex-order-md--12 { + -webkit-order: -12; + -ms-flex-order: -12; + order: -12 + } + + .flex-order-md--11 { + -webkit-order: -11; + -ms-flex-order: -11; + order: -11 + } + + .flex-order-md--10 { + -webkit-order: -10; + -ms-flex-order: -10; + order: -10 + } + + .flex-order-md--9 { + -webkit-order: -9; + -ms-flex-order: -9; + order: -9 + } + + .flex-order-md--8 { + -webkit-order: -8; + -ms-flex-order: -8; + order: -8 + } + + .flex-order-md--7 { + -webkit-order: -7; + -ms-flex-order: -7; + order: -7 + } + + .flex-order-md--6 { + -webkit-order: -6; + -ms-flex-order: -6; + order: -6 + } + + .flex-order-md--5 { + -webkit-order: -5; + -ms-flex-order: -5; + order: -5 + } + + .flex-order-md--4 { + -webkit-order: -4; + -ms-flex-order: -4; + order: -4 + } + + .flex-order-md--3 { + -webkit-order: -3; + -ms-flex-order: -3; + order: -3 + } + + .flex-order-md--2 { + -webkit-order: -2; + -ms-flex-order: -2; + order: -2 + } + + .flex-order-md--1 { + -webkit-order: -1; + -ms-flex-order: -1; + order: -1 + } + + .flex-order-md-0 { + -webkit-order: 0; + -ms-flex-order: 0; + order: 0 + } + + .flex-order-md-1 { + -webkit-order: 1; + -ms-flex-order: 1; + order: 1 + } + + .flex-order-md-2 { + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 + } + + .flex-order-md-3 { + -webkit-order: 3; + -ms-flex-order: 3; + order: 3 + } + + .flex-order-md-4 { + -webkit-order: 4; + -ms-flex-order: 4; + order: 4 + } + + .flex-order-md-5 { + -webkit-order: 5; + -ms-flex-order: 5; + order: 5 + } + + .flex-order-md-6 { + -webkit-order: 6; + -ms-flex-order: 6; + order: 6 + } + + .flex-order-md-7 { + -webkit-order: 7; + -ms-flex-order: 7; + order: 7 + } + + .flex-order-md-8 { + -webkit-order: 8; + -ms-flex-order: 8; + order: 8 + } + + .flex-order-md-9 { + -webkit-order: 9; + -ms-flex-order: 9; + order: 9 + } + + .flex-order-md-10 { + -webkit-order: 10; + -ms-flex-order: 10; + order: 10 + } + + .flex-order-md-11 { + -webkit-order: 11; + -ms-flex-order: 11; + order: 11 + } + + .flex-order-md-12 { + -webkit-order: 12; + -ms-flex-order: 12; + order: 12 + } + + .flex-order-md-13 { + -webkit-order: 13; + -ms-flex-order: 13; + order: 13 + } + + .flex-order-md-14 { + -webkit-order: 14; + -ms-flex-order: 14; + order: 14 + } + + .flex-order-md-15 { + -webkit-order: 15; + -ms-flex-order: 15; + order: 15 + } + + .flex-order-md-16 { + -webkit-order: 16; + -ms-flex-order: 16; + order: 16 + } + + .flex-order-md-17 { + -webkit-order: 17; + -ms-flex-order: 17; + order: 17 + } + + .flex-order-md-18 { + -webkit-order: 18; + -ms-flex-order: 18; + order: 18 + } + + .flex-order-md-19 { + -webkit-order: 19; + -ms-flex-order: 19; + order: 19 + } + + .flex-order-md-20 { + -webkit-order: 20; + -ms-flex-order: 20; + order: 20 + } + + .flex-offset-md-0, .offset-md-0 { + margin-left: 0 + } + + [dir=rtl] .flex-offset-md-0, [dir=rtl] .offset-md-0 { + margin-left: auto; + margin-right: 0 + } + + .flex-offset-md-5, .offset-md-5 { + margin-left: 5% + } + + [dir=rtl] .flex-offset-md-5, [dir=rtl] .offset-md-5 { + margin-left: auto; + margin-right: 5% + } + + .flex-offset-md-10, .offset-md-10 { + margin-left: 10% + } + + [dir=rtl] .flex-offset-md-10, [dir=rtl] .offset-md-10 { + margin-left: auto; + margin-right: 10% + } + + .flex-offset-md-15, .offset-md-15 { + margin-left: 15% + } + + [dir=rtl] .flex-offset-md-15, [dir=rtl] .offset-md-15 { + margin-left: auto; + margin-right: 15% + } + + .flex-offset-md-20, .offset-md-20 { + margin-left: 20% + } + + [dir=rtl] .flex-offset-md-20, [dir=rtl] .offset-md-20 { + margin-left: auto; + margin-right: 20% + } + + .flex-offset-md-25, .offset-md-25 { + margin-left: 25% + } + + [dir=rtl] .flex-offset-md-25, [dir=rtl] .offset-md-25 { + margin-left: auto; + margin-right: 25% + } + + .flex-offset-md-30, .offset-md-30 { + margin-left: 30% + } + + [dir=rtl] .flex-offset-md-30, [dir=rtl] .offset-md-30 { + margin-left: auto; + margin-right: 30% + } + + .flex-offset-md-35, .offset-md-35 { + margin-left: 35% + } + + [dir=rtl] .flex-offset-md-35, [dir=rtl] .offset-md-35 { + margin-left: auto; + margin-right: 35% + } + + .flex-offset-md-40, .offset-md-40 { + margin-left: 40% + } + + [dir=rtl] .flex-offset-md-40, [dir=rtl] .offset-md-40 { + margin-left: auto; + margin-right: 40% + } + + .flex-offset-md-45, .offset-md-45 { + margin-left: 45% + } + + [dir=rtl] .flex-offset-md-45, [dir=rtl] .offset-md-45 { + margin-left: auto; + margin-right: 45% + } + + .flex-offset-md-50, .offset-md-50 { + margin-left: 50% + } + + [dir=rtl] .flex-offset-md-50, [dir=rtl] .offset-md-50 { + margin-left: auto; + margin-right: 50% + } + + .flex-offset-md-55, .offset-md-55 { + margin-left: 55% + } + + [dir=rtl] .flex-offset-md-55, [dir=rtl] .offset-md-55 { + margin-left: auto; + margin-right: 55% + } + + .flex-offset-md-60, .offset-md-60 { + margin-left: 60% + } + + [dir=rtl] .flex-offset-md-60, [dir=rtl] .offset-md-60 { + margin-left: auto; + margin-right: 60% + } + + .flex-offset-md-65, .offset-md-65 { + margin-left: 65% + } + + [dir=rtl] .flex-offset-md-65, [dir=rtl] .offset-md-65 { + margin-left: auto; + margin-right: 65% + } + + .flex-offset-md-70, .offset-md-70 { + margin-left: 70% + } + + [dir=rtl] .flex-offset-md-70, [dir=rtl] .offset-md-70 { + margin-left: auto; + margin-right: 70% + } + + .flex-offset-md-75, .offset-md-75 { + margin-left: 75% + } + + [dir=rtl] .flex-offset-md-75, [dir=rtl] .offset-md-75 { + margin-left: auto; + margin-right: 75% + } + + .flex-offset-md-80, .offset-md-80 { + margin-left: 80% + } + + [dir=rtl] .flex-offset-md-80, [dir=rtl] .offset-md-80 { + margin-left: auto; + margin-right: 80% + } + + .flex-offset-md-85, .offset-md-85 { + margin-left: 85% + } + + [dir=rtl] .flex-offset-md-85, [dir=rtl] .offset-md-85 { + margin-left: auto; + margin-right: 85% + } + + .flex-offset-md-90, .offset-md-90 { + margin-left: 90% + } + + [dir=rtl] .flex-offset-md-90, [dir=rtl] .offset-md-90 { + margin-left: auto; + margin-right: 90% + } + + .flex-offset-md-95, .offset-md-95 { + margin-left: 95% + } + + [dir=rtl] .flex-offset-md-95, [dir=rtl] .offset-md-95 { + margin-left: auto; + margin-right: 95% + } + + .flex-offset-md-33, .offset-md-33 { + margin-left: calc(100% / 3) + } + + .flex-offset-md-66, .offset-md-66 { + margin-left: calc(200% / 3) + } + + [dir=rtl] .flex-offset-md-66, [dir=rtl] .offset-md-66 { + margin-left: auto; + margin-right: calc(200% / 3) + } + + .layout-align-md { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch + } + + .layout-align-md-start, .layout-align-md-start-center, .layout-align-md-start-end, .layout-align-md-start-start, .layout-align-md-start-stretch { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start + } + + .layout-align-md-center, .layout-align-md-center-center, .layout-align-md-center-end, .layout-align-md-center-start, .layout-align-md-center-stretch { + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center + } + + .layout-align-md-end, .layout-align-md-end-center, .layout-align-md-end-end, .layout-align-md-end-start, .layout-align-md-end-stretch { + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end + } + + .layout-align-md-space-around, .layout-align-md-space-around-center, .layout-align-md-space-around-end, .layout-align-md-space-around-start, .layout-align-md-space-around-stretch { + -webkit-justify-content: space-around; + -ms-flex-pack: distribute; + justify-content: space-around + } + + .layout-align-md-space-between, .layout-align-md-space-between-center, .layout-align-md-space-between-end, .layout-align-md-space-between-start, .layout-align-md-space-between-stretch { + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between + } + + .layout-align-md-center-start, .layout-align-md-end-start, .layout-align-md-space-around-start, .layout-align-md-space-between-start, .layout-align-md-start-start { + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + -webkit-align-content: flex-start; + -ms-flex-line-pack: start; + align-content: flex-start + } + + .layout-align-md-center-center, .layout-align-md-end-center, .layout-align-md-space-around-center, .layout-align-md-space-between-center, .layout-align-md-start-center { + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-align-content: center; + -ms-flex-line-pack: center; + align-content: center; + max-width: 100% + } + + .layout-align-md-center-center > *, .layout-align-md-end-center > *, .layout-align-md-space-around-center > *, .layout-align-md-space-between-center > *, .layout-align-md-start-center > * { + max-width: 100%; + box-sizing: border-box + } + + .layout-align-md-center-end, .layout-align-md-end-end, .layout-align-md-space-around-end, .layout-align-md-space-between-end, .layout-align-md-start-end { + -webkit-align-items: flex-end; + -ms-flex-align: end; + align-items: flex-end; + -webkit-align-content: flex-end; + -ms-flex-line-pack: end; + align-content: flex-end + } + + .layout-align-md-center-stretch, .layout-align-md-end-stretch, .layout-align-md-space-around-stretch, .layout-align-md-space-between-stretch, .layout-align-md-start-stretch { + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch + } + + .flex-md { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + box-sizing: border-box + } + + .flex-md-grow { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + box-sizing: border-box + } + + .flex-md-initial { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-md-auto { + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + box-sizing: border-box + } + + .flex-md-none { + -webkit-flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + box-sizing: border-box + } + + .flex-md-noshrink { + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + box-sizing: border-box + } + + .flex-md-nogrow { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-md-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box + } + + .layout-md-row > .flex-md-0, .layout-row > .flex-md-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box; + min-width: 0 + } + + .layout-column > .flex-md-0, .layout-md-column > .flex-md-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 100%; + max-height: 0; + box-sizing: border-box; + min-height: 0 + } + + .flex-md-5, .layout-md-row > .flex-md-5, .layout-row > .flex-md-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 5%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-5, .layout-md-column > .flex-md-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 100%; + max-height: 5%; + box-sizing: border-box + } + + .flex-md-10, .layout-md-row > .flex-md-10, .layout-row > .flex-md-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 10%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-10, .layout-md-column > .flex-md-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 100%; + max-height: 10%; + box-sizing: border-box + } + + .flex-md-15, .layout-md-row > .flex-md-15, .layout-row > .flex-md-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 15%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-15, .layout-md-column > .flex-md-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 100%; + max-height: 15%; + box-sizing: border-box + } + + .flex-md-20, .layout-md-row > .flex-md-20, .layout-row > .flex-md-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 20%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-20, .layout-md-column > .flex-md-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 100%; + max-height: 20%; + box-sizing: border-box + } + + .flex-md-25, .layout-md-row > .flex-md-25, .layout-row > .flex-md-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 25%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-25, .layout-md-column > .flex-md-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 100%; + max-height: 25%; + box-sizing: border-box + } + + .flex-md-30, .layout-md-row > .flex-md-30, .layout-row > .flex-md-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 30%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-30, .layout-md-column > .flex-md-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 100%; + max-height: 30%; + box-sizing: border-box + } + + .flex-md-35, .layout-md-row > .flex-md-35, .layout-row > .flex-md-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 35%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-35, .layout-md-column > .flex-md-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 100%; + max-height: 35%; + box-sizing: border-box + } + + .flex-md-40, .layout-md-row > .flex-md-40, .layout-row > .flex-md-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 40%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-40, .layout-md-column > .flex-md-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 100%; + max-height: 40%; + box-sizing: border-box + } + + .flex-md-45, .layout-md-row > .flex-md-45, .layout-row > .flex-md-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 45%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-45, .layout-md-column > .flex-md-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 100%; + max-height: 45%; + box-sizing: border-box + } + + .flex-md-50, .layout-md-row > .flex-md-50, .layout-row > .flex-md-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 50%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-50, .layout-md-column > .flex-md-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 100%; + max-height: 50%; + box-sizing: border-box + } + + .flex-md-55, .layout-md-row > .flex-md-55, .layout-row > .flex-md-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 55%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-55, .layout-md-column > .flex-md-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 100%; + max-height: 55%; + box-sizing: border-box + } + + .flex-md-60, .layout-md-row > .flex-md-60, .layout-row > .flex-md-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 60%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-60, .layout-md-column > .flex-md-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 100%; + max-height: 60%; + box-sizing: border-box + } + + .flex-md-65, .layout-md-row > .flex-md-65, .layout-row > .flex-md-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 65%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-65, .layout-md-column > .flex-md-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 100%; + max-height: 65%; + box-sizing: border-box + } + + .flex-md-70, .layout-md-row > .flex-md-70, .layout-row > .flex-md-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 70%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-70, .layout-md-column > .flex-md-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 100%; + max-height: 70%; + box-sizing: border-box + } + + .flex-md-75, .layout-md-row > .flex-md-75, .layout-row > .flex-md-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 75%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-75, .layout-md-column > .flex-md-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 100%; + max-height: 75%; + box-sizing: border-box + } + + .flex-md-80, .layout-md-row > .flex-md-80, .layout-row > .flex-md-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 80%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-80, .layout-md-column > .flex-md-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 100%; + max-height: 80%; + box-sizing: border-box + } + + .flex-md-85, .layout-md-row > .flex-md-85, .layout-row > .flex-md-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 85%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-85, .layout-md-column > .flex-md-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 100%; + max-height: 85%; + box-sizing: border-box + } + + .flex-md-90, .layout-md-row > .flex-md-90, .layout-row > .flex-md-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 90%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-90, .layout-md-column > .flex-md-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 100%; + max-height: 90%; + box-sizing: border-box + } + + .flex-md-95, .layout-md-row > .flex-md-95, .layout-row > .flex-md-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 95%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-md-95, .layout-md-column > .flex-md-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 100%; + max-height: 95%; + box-sizing: border-box + } + + .flex-md-100, .layout-column > .flex-md-100, .layout-md-column > .flex-md-100, .layout-md-row > .flex-md-100, .layout-row > .flex-md-100 { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + max-width: 100%; + max-height: 100%; + box-sizing: border-box + } + + .layout-md-row > .flex-md-33, .layout-row > .flex-md-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 33.33%; + max-height: 100%; + box-sizing: border-box + } + + .layout-md-row > .flex-md-66, .layout-row > .flex-md-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 66.66%; + max-height: 100%; + box-sizing: border-box + } + + .layout-md-row > .flex, .layout-row > .flex { + min-width: 0 + } + + .layout-column > .flex-md-33, .layout-md-column > .flex-md-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 100%; + max-height: 33.33%; + box-sizing: border-box + } + + .layout-column > .flex-md-66, .layout-md-column > .flex-md-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 100%; + max-height: 66.66%; + box-sizing: border-box + } + + .layout-column > .flex, .layout-md-column > .flex { + min-height: 0 + } + + .layout-md, .layout-md-column, .layout-md-row { + box-sizing: border-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex + } + + .layout-md-column { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column + } + + .layout-md-row { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row + } +} + +@media (min-width: 1280px) { + .flex-order-gt-md--20 { + -webkit-order: -20; + -ms-flex-order: -20; + order: -20 + } + + .flex-order-gt-md--19 { + -webkit-order: -19; + -ms-flex-order: -19; + order: -19 + } + + .flex-order-gt-md--18 { + -webkit-order: -18; + -ms-flex-order: -18; + order: -18 + } + + .flex-order-gt-md--17 { + -webkit-order: -17; + -ms-flex-order: -17; + order: -17 + } + + .flex-order-gt-md--16 { + -webkit-order: -16; + -ms-flex-order: -16; + order: -16 + } + + .flex-order-gt-md--15 { + -webkit-order: -15; + -ms-flex-order: -15; + order: -15 + } + + .flex-order-gt-md--14 { + -webkit-order: -14; + -ms-flex-order: -14; + order: -14 + } + + .flex-order-gt-md--13 { + -webkit-order: -13; + -ms-flex-order: -13; + order: -13 + } + + .flex-order-gt-md--12 { + -webkit-order: -12; + -ms-flex-order: -12; + order: -12 + } + + .flex-order-gt-md--11 { + -webkit-order: -11; + -ms-flex-order: -11; + order: -11 + } + + .flex-order-gt-md--10 { + -webkit-order: -10; + -ms-flex-order: -10; + order: -10 + } + + .flex-order-gt-md--9 { + -webkit-order: -9; + -ms-flex-order: -9; + order: -9 + } + + .flex-order-gt-md--8 { + -webkit-order: -8; + -ms-flex-order: -8; + order: -8 + } + + .flex-order-gt-md--7 { + -webkit-order: -7; + -ms-flex-order: -7; + order: -7 + } + + .flex-order-gt-md--6 { + -webkit-order: -6; + -ms-flex-order: -6; + order: -6 + } + + .flex-order-gt-md--5 { + -webkit-order: -5; + -ms-flex-order: -5; + order: -5 + } + + .flex-order-gt-md--4 { + -webkit-order: -4; + -ms-flex-order: -4; + order: -4 + } + + .flex-order-gt-md--3 { + -webkit-order: -3; + -ms-flex-order: -3; + order: -3 + } + + .flex-order-gt-md--2 { + -webkit-order: -2; + -ms-flex-order: -2; + order: -2 + } + + .flex-order-gt-md--1 { + -webkit-order: -1; + -ms-flex-order: -1; + order: -1 + } + + .flex-order-gt-md-0 { + -webkit-order: 0; + -ms-flex-order: 0; + order: 0 + } + + .flex-order-gt-md-1 { + -webkit-order: 1; + -ms-flex-order: 1; + order: 1 + } + + .flex-order-gt-md-2 { + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 + } + + .flex-order-gt-md-3 { + -webkit-order: 3; + -ms-flex-order: 3; + order: 3 + } + + .flex-order-gt-md-4 { + -webkit-order: 4; + -ms-flex-order: 4; + order: 4 + } + + .flex-order-gt-md-5 { + -webkit-order: 5; + -ms-flex-order: 5; + order: 5 + } + + .flex-order-gt-md-6 { + -webkit-order: 6; + -ms-flex-order: 6; + order: 6 + } + + .flex-order-gt-md-7 { + -webkit-order: 7; + -ms-flex-order: 7; + order: 7 + } + + .flex-order-gt-md-8 { + -webkit-order: 8; + -ms-flex-order: 8; + order: 8 + } + + .flex-order-gt-md-9 { + -webkit-order: 9; + -ms-flex-order: 9; + order: 9 + } + + .flex-order-gt-md-10 { + -webkit-order: 10; + -ms-flex-order: 10; + order: 10 + } + + .flex-order-gt-md-11 { + -webkit-order: 11; + -ms-flex-order: 11; + order: 11 + } + + .flex-order-gt-md-12 { + -webkit-order: 12; + -ms-flex-order: 12; + order: 12 + } + + .flex-order-gt-md-13 { + -webkit-order: 13; + -ms-flex-order: 13; + order: 13 + } + + .flex-order-gt-md-14 { + -webkit-order: 14; + -ms-flex-order: 14; + order: 14 + } + + .flex-order-gt-md-15 { + -webkit-order: 15; + -ms-flex-order: 15; + order: 15 + } + + .flex-order-gt-md-16 { + -webkit-order: 16; + -ms-flex-order: 16; + order: 16 + } + + .flex-order-gt-md-17 { + -webkit-order: 17; + -ms-flex-order: 17; + order: 17 + } + + .flex-order-gt-md-18 { + -webkit-order: 18; + -ms-flex-order: 18; + order: 18 + } + + .flex-order-gt-md-19 { + -webkit-order: 19; + -ms-flex-order: 19; + order: 19 + } + + .flex-order-gt-md-20 { + -webkit-order: 20; + -ms-flex-order: 20; + order: 20 + } + + .flex-offset-gt-md-0, .offset-gt-md-0 { + margin-left: 0 + } + + [dir=rtl] .flex-offset-gt-md-0, [dir=rtl] .offset-gt-md-0 { + margin-left: auto; + margin-right: 0 + } + + .flex-offset-gt-md-5, .offset-gt-md-5 { + margin-left: 5% + } + + [dir=rtl] .flex-offset-gt-md-5, [dir=rtl] .offset-gt-md-5 { + margin-left: auto; + margin-right: 5% + } + + .flex-offset-gt-md-10, .offset-gt-md-10 { + margin-left: 10% + } + + [dir=rtl] .flex-offset-gt-md-10, [dir=rtl] .offset-gt-md-10 { + margin-left: auto; + margin-right: 10% + } + + .flex-offset-gt-md-15, .offset-gt-md-15 { + margin-left: 15% + } + + [dir=rtl] .flex-offset-gt-md-15, [dir=rtl] .offset-gt-md-15 { + margin-left: auto; + margin-right: 15% + } + + .flex-offset-gt-md-20, .offset-gt-md-20 { + margin-left: 20% + } + + [dir=rtl] .flex-offset-gt-md-20, [dir=rtl] .offset-gt-md-20 { + margin-left: auto; + margin-right: 20% + } + + .flex-offset-gt-md-25, .offset-gt-md-25 { + margin-left: 25% + } + + [dir=rtl] .flex-offset-gt-md-25, [dir=rtl] .offset-gt-md-25 { + margin-left: auto; + margin-right: 25% + } + + .flex-offset-gt-md-30, .offset-gt-md-30 { + margin-left: 30% + } + + [dir=rtl] .flex-offset-gt-md-30, [dir=rtl] .offset-gt-md-30 { + margin-left: auto; + margin-right: 30% + } + + .flex-offset-gt-md-35, .offset-gt-md-35 { + margin-left: 35% + } + + [dir=rtl] .flex-offset-gt-md-35, [dir=rtl] .offset-gt-md-35 { + margin-left: auto; + margin-right: 35% + } + + .flex-offset-gt-md-40, .offset-gt-md-40 { + margin-left: 40% + } + + [dir=rtl] .flex-offset-gt-md-40, [dir=rtl] .offset-gt-md-40 { + margin-left: auto; + margin-right: 40% + } + + .flex-offset-gt-md-45, .offset-gt-md-45 { + margin-left: 45% + } + + [dir=rtl] .flex-offset-gt-md-45, [dir=rtl] .offset-gt-md-45 { + margin-left: auto; + margin-right: 45% + } + + .flex-offset-gt-md-50, .offset-gt-md-50 { + margin-left: 50% + } + + [dir=rtl] .flex-offset-gt-md-50, [dir=rtl] .offset-gt-md-50 { + margin-left: auto; + margin-right: 50% + } + + .flex-offset-gt-md-55, .offset-gt-md-55 { + margin-left: 55% + } + + [dir=rtl] .flex-offset-gt-md-55, [dir=rtl] .offset-gt-md-55 { + margin-left: auto; + margin-right: 55% + } + + .flex-offset-gt-md-60, .offset-gt-md-60 { + margin-left: 60% + } + + [dir=rtl] .flex-offset-gt-md-60, [dir=rtl] .offset-gt-md-60 { + margin-left: auto; + margin-right: 60% + } + + .flex-offset-gt-md-65, .offset-gt-md-65 { + margin-left: 65% + } + + [dir=rtl] .flex-offset-gt-md-65, [dir=rtl] .offset-gt-md-65 { + margin-left: auto; + margin-right: 65% + } + + .flex-offset-gt-md-70, .offset-gt-md-70 { + margin-left: 70% + } + + [dir=rtl] .flex-offset-gt-md-70, [dir=rtl] .offset-gt-md-70 { + margin-left: auto; + margin-right: 70% + } + + .flex-offset-gt-md-75, .offset-gt-md-75 { + margin-left: 75% + } + + [dir=rtl] .flex-offset-gt-md-75, [dir=rtl] .offset-gt-md-75 { + margin-left: auto; + margin-right: 75% + } + + .flex-offset-gt-md-80, .offset-gt-md-80 { + margin-left: 80% + } + + [dir=rtl] .flex-offset-gt-md-80, [dir=rtl] .offset-gt-md-80 { + margin-left: auto; + margin-right: 80% + } + + .flex-offset-gt-md-85, .offset-gt-md-85 { + margin-left: 85% + } + + [dir=rtl] .flex-offset-gt-md-85, [dir=rtl] .offset-gt-md-85 { + margin-left: auto; + margin-right: 85% + } + + .flex-offset-gt-md-90, .offset-gt-md-90 { + margin-left: 90% + } + + [dir=rtl] .flex-offset-gt-md-90, [dir=rtl] .offset-gt-md-90 { + margin-left: auto; + margin-right: 90% + } + + .flex-offset-gt-md-95, .offset-gt-md-95 { + margin-left: 95% + } + + [dir=rtl] .flex-offset-gt-md-95, [dir=rtl] .offset-gt-md-95 { + margin-left: auto; + margin-right: 95% + } + + .flex-offset-gt-md-33, .offset-gt-md-33 { + margin-left: calc(100% / 3) + } + + .flex-offset-gt-md-66, .offset-gt-md-66 { + margin-left: calc(200% / 3) + } + + [dir=rtl] .flex-offset-gt-md-66, [dir=rtl] .offset-gt-md-66 { + margin-left: auto; + margin-right: calc(200% / 3) + } + + .layout-align-gt-md { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch + } + + .layout-align-gt-md-start, .layout-align-gt-md-start-center, .layout-align-gt-md-start-end, .layout-align-gt-md-start-start, .layout-align-gt-md-start-stretch { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start + } + + .layout-align-gt-md-center, .layout-align-gt-md-center-center, .layout-align-gt-md-center-end, .layout-align-gt-md-center-start, .layout-align-gt-md-center-stretch { + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center + } + + .layout-align-gt-md-end, .layout-align-gt-md-end-center, .layout-align-gt-md-end-end, .layout-align-gt-md-end-start, .layout-align-gt-md-end-stretch { + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end + } + + .layout-align-gt-md-space-around, .layout-align-gt-md-space-around-center, .layout-align-gt-md-space-around-end, .layout-align-gt-md-space-around-start, .layout-align-gt-md-space-around-stretch { + -webkit-justify-content: space-around; + -ms-flex-pack: distribute; + justify-content: space-around + } + + .layout-align-gt-md-space-between, .layout-align-gt-md-space-between-center, .layout-align-gt-md-space-between-end, .layout-align-gt-md-space-between-start, .layout-align-gt-md-space-between-stretch { + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between + } + + .layout-align-gt-md-center-start, .layout-align-gt-md-end-start, .layout-align-gt-md-space-around-start, .layout-align-gt-md-space-between-start, .layout-align-gt-md-start-start { + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + -webkit-align-content: flex-start; + -ms-flex-line-pack: start; + align-content: flex-start + } + + .layout-align-gt-md-center-center, .layout-align-gt-md-end-center, .layout-align-gt-md-space-around-center, .layout-align-gt-md-space-between-center, .layout-align-gt-md-start-center { + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-align-content: center; + -ms-flex-line-pack: center; + align-content: center; + max-width: 100% + } + + .layout-align-gt-md-center-center > *, .layout-align-gt-md-end-center > *, .layout-align-gt-md-space-around-center > *, .layout-align-gt-md-space-between-center > *, .layout-align-gt-md-start-center > * { + max-width: 100%; + box-sizing: border-box + } + + .layout-align-gt-md-center-end, .layout-align-gt-md-end-end, .layout-align-gt-md-space-around-end, .layout-align-gt-md-space-between-end, .layout-align-gt-md-start-end { + -webkit-align-items: flex-end; + -ms-flex-align: end; + align-items: flex-end; + -webkit-align-content: flex-end; + -ms-flex-line-pack: end; + align-content: flex-end + } + + .layout-align-gt-md-center-stretch, .layout-align-gt-md-end-stretch, .layout-align-gt-md-space-around-stretch, .layout-align-gt-md-space-between-stretch, .layout-align-gt-md-start-stretch { + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch + } + + .flex-gt-md { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + box-sizing: border-box + } + + .flex-gt-md-grow { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + box-sizing: border-box + } + + .flex-gt-md-initial { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-gt-md-auto { + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + box-sizing: border-box + } + + .flex-gt-md-none { + -webkit-flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + box-sizing: border-box + } + + .flex-gt-md-noshrink { + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + box-sizing: border-box + } + + .flex-gt-md-nogrow { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-gt-md-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-md-row > .flex-gt-md-0, .layout-row > .flex-gt-md-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box; + min-width: 0 + } + + .layout-column > .flex-gt-md-0, .layout-gt-md-column > .flex-gt-md-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 100%; + max-height: 0; + box-sizing: border-box; + min-height: 0 + } + + .flex-gt-md-5, .layout-gt-md-row > .flex-gt-md-5, .layout-row > .flex-gt-md-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 5%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-5, .layout-gt-md-column > .flex-gt-md-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 100%; + max-height: 5%; + box-sizing: border-box + } + + .flex-gt-md-10, .layout-gt-md-row > .flex-gt-md-10, .layout-row > .flex-gt-md-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 10%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-10, .layout-gt-md-column > .flex-gt-md-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 100%; + max-height: 10%; + box-sizing: border-box + } + + .flex-gt-md-15, .layout-gt-md-row > .flex-gt-md-15, .layout-row > .flex-gt-md-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 15%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-15, .layout-gt-md-column > .flex-gt-md-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 100%; + max-height: 15%; + box-sizing: border-box + } + + .flex-gt-md-20, .layout-gt-md-row > .flex-gt-md-20, .layout-row > .flex-gt-md-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 20%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-20, .layout-gt-md-column > .flex-gt-md-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 100%; + max-height: 20%; + box-sizing: border-box + } + + .flex-gt-md-25, .layout-gt-md-row > .flex-gt-md-25, .layout-row > .flex-gt-md-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 25%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-25, .layout-gt-md-column > .flex-gt-md-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 100%; + max-height: 25%; + box-sizing: border-box + } + + .flex-gt-md-30, .layout-gt-md-row > .flex-gt-md-30, .layout-row > .flex-gt-md-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 30%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-30, .layout-gt-md-column > .flex-gt-md-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 100%; + max-height: 30%; + box-sizing: border-box + } + + .flex-gt-md-35, .layout-gt-md-row > .flex-gt-md-35, .layout-row > .flex-gt-md-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 35%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-35, .layout-gt-md-column > .flex-gt-md-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 100%; + max-height: 35%; + box-sizing: border-box + } + + .flex-gt-md-40, .layout-gt-md-row > .flex-gt-md-40, .layout-row > .flex-gt-md-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 40%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-40, .layout-gt-md-column > .flex-gt-md-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 100%; + max-height: 40%; + box-sizing: border-box + } + + .flex-gt-md-45, .layout-gt-md-row > .flex-gt-md-45, .layout-row > .flex-gt-md-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 45%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-45, .layout-gt-md-column > .flex-gt-md-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 100%; + max-height: 45%; + box-sizing: border-box + } + + .flex-gt-md-50, .layout-gt-md-row > .flex-gt-md-50, .layout-row > .flex-gt-md-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 50%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-50, .layout-gt-md-column > .flex-gt-md-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 100%; + max-height: 50%; + box-sizing: border-box + } + + .flex-gt-md-55, .layout-gt-md-row > .flex-gt-md-55, .layout-row > .flex-gt-md-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 55%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-55, .layout-gt-md-column > .flex-gt-md-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 100%; + max-height: 55%; + box-sizing: border-box + } + + .flex-gt-md-60, .layout-gt-md-row > .flex-gt-md-60, .layout-row > .flex-gt-md-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 60%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-60, .layout-gt-md-column > .flex-gt-md-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 100%; + max-height: 60%; + box-sizing: border-box + } + + .flex-gt-md-65, .layout-gt-md-row > .flex-gt-md-65, .layout-row > .flex-gt-md-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 65%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-65, .layout-gt-md-column > .flex-gt-md-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 100%; + max-height: 65%; + box-sizing: border-box + } + + .flex-gt-md-70, .layout-gt-md-row > .flex-gt-md-70, .layout-row > .flex-gt-md-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 70%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-70, .layout-gt-md-column > .flex-gt-md-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 100%; + max-height: 70%; + box-sizing: border-box + } + + .flex-gt-md-75, .layout-gt-md-row > .flex-gt-md-75, .layout-row > .flex-gt-md-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 75%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-75, .layout-gt-md-column > .flex-gt-md-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 100%; + max-height: 75%; + box-sizing: border-box + } + + .flex-gt-md-80, .layout-gt-md-row > .flex-gt-md-80, .layout-row > .flex-gt-md-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 80%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-80, .layout-gt-md-column > .flex-gt-md-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 100%; + max-height: 80%; + box-sizing: border-box + } + + .flex-gt-md-85, .layout-gt-md-row > .flex-gt-md-85, .layout-row > .flex-gt-md-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 85%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-85, .layout-gt-md-column > .flex-gt-md-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 100%; + max-height: 85%; + box-sizing: border-box + } + + .flex-gt-md-90, .layout-gt-md-row > .flex-gt-md-90, .layout-row > .flex-gt-md-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 90%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-90, .layout-gt-md-column > .flex-gt-md-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 100%; + max-height: 90%; + box-sizing: border-box + } + + .flex-gt-md-95, .layout-gt-md-row > .flex-gt-md-95, .layout-row > .flex-gt-md-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 95%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-95, .layout-gt-md-column > .flex-gt-md-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 100%; + max-height: 95%; + box-sizing: border-box + } + + .flex-gt-md-100, .layout-column > .flex-gt-md-100, .layout-gt-md-column > .flex-gt-md-100, .layout-gt-md-row > .flex-gt-md-100, .layout-row > .flex-gt-md-100 { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + max-width: 100%; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-md-row > .flex-gt-md-33, .layout-row > .flex-gt-md-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 33.33%; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-md-row > .flex-gt-md-66, .layout-row > .flex-gt-md-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 66.66%; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-md-row > .flex, .layout-row > .flex { + min-width: 0 + } + + .layout-column > .flex-gt-md-33, .layout-gt-md-column > .flex-gt-md-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 100%; + max-height: 33.33%; + box-sizing: border-box + } + + .layout-column > .flex-gt-md-66, .layout-gt-md-column > .flex-gt-md-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 100%; + max-height: 66.66%; + box-sizing: border-box + } + + .layout-column > .flex, .layout-gt-md-column > .flex { + min-height: 0 + } + + .layout-gt-md, .layout-gt-md-column, .layout-gt-md-row { + box-sizing: border-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex + } + + .layout-gt-md-column { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column + } + + .layout-gt-md-row { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row + } +} + +@media (min-width: 1280px) and (max-width: 1919px) { + .hide-gt-md:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-lg):not(.show), .hide-gt-sm:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-lg):not(.show), .hide-gt-xs:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-lg):not(.show), .hide-lg:not(.show-lg):not(.show-gt-md):not(.show-gt-sm):not(.show-gt-xs):not(.show), .hide:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-lg):not(.show) { + display: none + } + + .flex-order-lg--20 { + -webkit-order: -20; + -ms-flex-order: -20; + order: -20 + } + + .flex-order-lg--19 { + -webkit-order: -19; + -ms-flex-order: -19; + order: -19 + } + + .flex-order-lg--18 { + -webkit-order: -18; + -ms-flex-order: -18; + order: -18 + } + + .flex-order-lg--17 { + -webkit-order: -17; + -ms-flex-order: -17; + order: -17 + } + + .flex-order-lg--16 { + -webkit-order: -16; + -ms-flex-order: -16; + order: -16 + } + + .flex-order-lg--15 { + -webkit-order: -15; + -ms-flex-order: -15; + order: -15 + } + + .flex-order-lg--14 { + -webkit-order: -14; + -ms-flex-order: -14; + order: -14 + } + + .flex-order-lg--13 { + -webkit-order: -13; + -ms-flex-order: -13; + order: -13 + } + + .flex-order-lg--12 { + -webkit-order: -12; + -ms-flex-order: -12; + order: -12 + } + + .flex-order-lg--11 { + -webkit-order: -11; + -ms-flex-order: -11; + order: -11 + } + + .flex-order-lg--10 { + -webkit-order: -10; + -ms-flex-order: -10; + order: -10 + } + + .flex-order-lg--9 { + -webkit-order: -9; + -ms-flex-order: -9; + order: -9 + } + + .flex-order-lg--8 { + -webkit-order: -8; + -ms-flex-order: -8; + order: -8 + } + + .flex-order-lg--7 { + -webkit-order: -7; + -ms-flex-order: -7; + order: -7 + } + + .flex-order-lg--6 { + -webkit-order: -6; + -ms-flex-order: -6; + order: -6 + } + + .flex-order-lg--5 { + -webkit-order: -5; + -ms-flex-order: -5; + order: -5 + } + + .flex-order-lg--4 { + -webkit-order: -4; + -ms-flex-order: -4; + order: -4 + } + + .flex-order-lg--3 { + -webkit-order: -3; + -ms-flex-order: -3; + order: -3 + } + + .flex-order-lg--2 { + -webkit-order: -2; + -ms-flex-order: -2; + order: -2 + } + + .flex-order-lg--1 { + -webkit-order: -1; + -ms-flex-order: -1; + order: -1 + } + + .flex-order-lg-0 { + -webkit-order: 0; + -ms-flex-order: 0; + order: 0 + } + + .flex-order-lg-1 { + -webkit-order: 1; + -ms-flex-order: 1; + order: 1 + } + + .flex-order-lg-2 { + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 + } + + .flex-order-lg-3 { + -webkit-order: 3; + -ms-flex-order: 3; + order: 3 + } + + .flex-order-lg-4 { + -webkit-order: 4; + -ms-flex-order: 4; + order: 4 + } + + .flex-order-lg-5 { + -webkit-order: 5; + -ms-flex-order: 5; + order: 5 + } + + .flex-order-lg-6 { + -webkit-order: 6; + -ms-flex-order: 6; + order: 6 + } + + .flex-order-lg-7 { + -webkit-order: 7; + -ms-flex-order: 7; + order: 7 + } + + .flex-order-lg-8 { + -webkit-order: 8; + -ms-flex-order: 8; + order: 8 + } + + .flex-order-lg-9 { + -webkit-order: 9; + -ms-flex-order: 9; + order: 9 + } + + .flex-order-lg-10 { + -webkit-order: 10; + -ms-flex-order: 10; + order: 10 + } + + .flex-order-lg-11 { + -webkit-order: 11; + -ms-flex-order: 11; + order: 11 + } + + .flex-order-lg-12 { + -webkit-order: 12; + -ms-flex-order: 12; + order: 12 + } + + .flex-order-lg-13 { + -webkit-order: 13; + -ms-flex-order: 13; + order: 13 + } + + .flex-order-lg-14 { + -webkit-order: 14; + -ms-flex-order: 14; + order: 14 + } + + .flex-order-lg-15 { + -webkit-order: 15; + -ms-flex-order: 15; + order: 15 + } + + .flex-order-lg-16 { + -webkit-order: 16; + -ms-flex-order: 16; + order: 16 + } + + .flex-order-lg-17 { + -webkit-order: 17; + -ms-flex-order: 17; + order: 17 + } + + .flex-order-lg-18 { + -webkit-order: 18; + -ms-flex-order: 18; + order: 18 + } + + .flex-order-lg-19 { + -webkit-order: 19; + -ms-flex-order: 19; + order: 19 + } + + .flex-order-lg-20 { + -webkit-order: 20; + -ms-flex-order: 20; + order: 20 + } + + .flex-offset-lg-0, .offset-lg-0 { + margin-left: 0 + } + + [dir=rtl] .flex-offset-lg-0, [dir=rtl] .offset-lg-0 { + margin-left: auto; + margin-right: 0 + } + + .flex-offset-lg-5, .offset-lg-5 { + margin-left: 5% + } + + [dir=rtl] .flex-offset-lg-5, [dir=rtl] .offset-lg-5 { + margin-left: auto; + margin-right: 5% + } + + .flex-offset-lg-10, .offset-lg-10 { + margin-left: 10% + } + + [dir=rtl] .flex-offset-lg-10, [dir=rtl] .offset-lg-10 { + margin-left: auto; + margin-right: 10% + } + + .flex-offset-lg-15, .offset-lg-15 { + margin-left: 15% + } + + [dir=rtl] .flex-offset-lg-15, [dir=rtl] .offset-lg-15 { + margin-left: auto; + margin-right: 15% + } + + .flex-offset-lg-20, .offset-lg-20 { + margin-left: 20% + } + + [dir=rtl] .flex-offset-lg-20, [dir=rtl] .offset-lg-20 { + margin-left: auto; + margin-right: 20% + } + + .flex-offset-lg-25, .offset-lg-25 { + margin-left: 25% + } + + [dir=rtl] .flex-offset-lg-25, [dir=rtl] .offset-lg-25 { + margin-left: auto; + margin-right: 25% + } + + .flex-offset-lg-30, .offset-lg-30 { + margin-left: 30% + } + + [dir=rtl] .flex-offset-lg-30, [dir=rtl] .offset-lg-30 { + margin-left: auto; + margin-right: 30% + } + + .flex-offset-lg-35, .offset-lg-35 { + margin-left: 35% + } + + [dir=rtl] .flex-offset-lg-35, [dir=rtl] .offset-lg-35 { + margin-left: auto; + margin-right: 35% + } + + .flex-offset-lg-40, .offset-lg-40 { + margin-left: 40% + } + + [dir=rtl] .flex-offset-lg-40, [dir=rtl] .offset-lg-40 { + margin-left: auto; + margin-right: 40% + } + + .flex-offset-lg-45, .offset-lg-45 { + margin-left: 45% + } + + [dir=rtl] .flex-offset-lg-45, [dir=rtl] .offset-lg-45 { + margin-left: auto; + margin-right: 45% + } + + .flex-offset-lg-50, .offset-lg-50 { + margin-left: 50% + } + + [dir=rtl] .flex-offset-lg-50, [dir=rtl] .offset-lg-50 { + margin-left: auto; + margin-right: 50% + } + + .flex-offset-lg-55, .offset-lg-55 { + margin-left: 55% + } + + [dir=rtl] .flex-offset-lg-55, [dir=rtl] .offset-lg-55 { + margin-left: auto; + margin-right: 55% + } + + .flex-offset-lg-60, .offset-lg-60 { + margin-left: 60% + } + + [dir=rtl] .flex-offset-lg-60, [dir=rtl] .offset-lg-60 { + margin-left: auto; + margin-right: 60% + } + + .flex-offset-lg-65, .offset-lg-65 { + margin-left: 65% + } + + [dir=rtl] .flex-offset-lg-65, [dir=rtl] .offset-lg-65 { + margin-left: auto; + margin-right: 65% + } + + .flex-offset-lg-70, .offset-lg-70 { + margin-left: 70% + } + + [dir=rtl] .flex-offset-lg-70, [dir=rtl] .offset-lg-70 { + margin-left: auto; + margin-right: 70% + } + + .flex-offset-lg-75, .offset-lg-75 { + margin-left: 75% + } + + [dir=rtl] .flex-offset-lg-75, [dir=rtl] .offset-lg-75 { + margin-left: auto; + margin-right: 75% + } + + .flex-offset-lg-80, .offset-lg-80 { + margin-left: 80% + } + + [dir=rtl] .flex-offset-lg-80, [dir=rtl] .offset-lg-80 { + margin-left: auto; + margin-right: 80% + } + + .flex-offset-lg-85, .offset-lg-85 { + margin-left: 85% + } + + [dir=rtl] .flex-offset-lg-85, [dir=rtl] .offset-lg-85 { + margin-left: auto; + margin-right: 85% + } + + .flex-offset-lg-90, .offset-lg-90 { + margin-left: 90% + } + + [dir=rtl] .flex-offset-lg-90, [dir=rtl] .offset-lg-90 { + margin-left: auto; + margin-right: 90% + } + + .flex-offset-lg-95, .offset-lg-95 { + margin-left: 95% + } + + [dir=rtl] .flex-offset-lg-95, [dir=rtl] .offset-lg-95 { + margin-left: auto; + margin-right: 95% + } + + .flex-offset-lg-33, .offset-lg-33 { + margin-left: calc(100% / 3) + } + + .flex-offset-lg-66, .offset-lg-66 { + margin-left: calc(200% / 3) + } + + [dir=rtl] .flex-offset-lg-66, [dir=rtl] .offset-lg-66 { + margin-left: auto; + margin-right: calc(200% / 3) + } + + .layout-align-lg { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch + } + + .layout-align-lg-start, .layout-align-lg-start-center, .layout-align-lg-start-end, .layout-align-lg-start-start, .layout-align-lg-start-stretch { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start + } + + .layout-align-lg-center, .layout-align-lg-center-center, .layout-align-lg-center-end, .layout-align-lg-center-start, .layout-align-lg-center-stretch { + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center + } + + .layout-align-lg-end, .layout-align-lg-end-center, .layout-align-lg-end-end, .layout-align-lg-end-start, .layout-align-lg-end-stretch { + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end + } + + .layout-align-lg-space-around, .layout-align-lg-space-around-center, .layout-align-lg-space-around-end, .layout-align-lg-space-around-start, .layout-align-lg-space-around-stretch { + -webkit-justify-content: space-around; + -ms-flex-pack: distribute; + justify-content: space-around + } + + .layout-align-lg-space-between, .layout-align-lg-space-between-center, .layout-align-lg-space-between-end, .layout-align-lg-space-between-start, .layout-align-lg-space-between-stretch { + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between + } + + .layout-align-lg-center-start, .layout-align-lg-end-start, .layout-align-lg-space-around-start, .layout-align-lg-space-between-start, .layout-align-lg-start-start { + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + -webkit-align-content: flex-start; + -ms-flex-line-pack: start; + align-content: flex-start + } + + .layout-align-lg-center-center, .layout-align-lg-end-center, .layout-align-lg-space-around-center, .layout-align-lg-space-between-center, .layout-align-lg-start-center { + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-align-content: center; + -ms-flex-line-pack: center; + align-content: center; + max-width: 100% + } + + .layout-align-lg-center-center > *, .layout-align-lg-end-center > *, .layout-align-lg-space-around-center > *, .layout-align-lg-space-between-center > *, .layout-align-lg-start-center > * { + max-width: 100%; + box-sizing: border-box + } + + .layout-align-lg-center-end, .layout-align-lg-end-end, .layout-align-lg-space-around-end, .layout-align-lg-space-between-end, .layout-align-lg-start-end { + -webkit-align-items: flex-end; + -ms-flex-align: end; + align-items: flex-end; + -webkit-align-content: flex-end; + -ms-flex-line-pack: end; + align-content: flex-end + } + + .layout-align-lg-center-stretch, .layout-align-lg-end-stretch, .layout-align-lg-space-around-stretch, .layout-align-lg-space-between-stretch, .layout-align-lg-start-stretch { + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch + } + + .flex-lg { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + box-sizing: border-box + } + + .flex-lg-grow { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + box-sizing: border-box + } + + .flex-lg-initial { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-lg-auto { + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + box-sizing: border-box + } + + .flex-lg-none { + -webkit-flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + box-sizing: border-box + } + + .flex-lg-noshrink { + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + box-sizing: border-box + } + + .flex-lg-nogrow { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-lg-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box + } + + .layout-lg-row > .flex-lg-0, .layout-row > .flex-lg-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box; + min-width: 0 + } + + .layout-column > .flex-lg-0, .layout-lg-column > .flex-lg-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 100%; + max-height: 0; + box-sizing: border-box; + min-height: 0 + } + + .flex-lg-5, .layout-lg-row > .flex-lg-5, .layout-row > .flex-lg-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 5%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-5, .layout-lg-column > .flex-lg-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 100%; + max-height: 5%; + box-sizing: border-box + } + + .flex-lg-10, .layout-lg-row > .flex-lg-10, .layout-row > .flex-lg-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 10%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-10, .layout-lg-column > .flex-lg-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 100%; + max-height: 10%; + box-sizing: border-box + } + + .flex-lg-15, .layout-lg-row > .flex-lg-15, .layout-row > .flex-lg-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 15%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-15, .layout-lg-column > .flex-lg-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 100%; + max-height: 15%; + box-sizing: border-box + } + + .flex-lg-20, .layout-lg-row > .flex-lg-20, .layout-row > .flex-lg-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 20%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-20, .layout-lg-column > .flex-lg-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 100%; + max-height: 20%; + box-sizing: border-box + } + + .flex-lg-25, .layout-lg-row > .flex-lg-25, .layout-row > .flex-lg-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 25%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-25, .layout-lg-column > .flex-lg-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 100%; + max-height: 25%; + box-sizing: border-box + } + + .flex-lg-30, .layout-lg-row > .flex-lg-30, .layout-row > .flex-lg-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 30%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-30, .layout-lg-column > .flex-lg-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 100%; + max-height: 30%; + box-sizing: border-box + } + + .flex-lg-35, .layout-lg-row > .flex-lg-35, .layout-row > .flex-lg-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 35%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-35, .layout-lg-column > .flex-lg-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 100%; + max-height: 35%; + box-sizing: border-box + } + + .flex-lg-40, .layout-lg-row > .flex-lg-40, .layout-row > .flex-lg-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 40%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-40, .layout-lg-column > .flex-lg-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 100%; + max-height: 40%; + box-sizing: border-box + } + + .flex-lg-45, .layout-lg-row > .flex-lg-45, .layout-row > .flex-lg-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 45%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-45, .layout-lg-column > .flex-lg-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 100%; + max-height: 45%; + box-sizing: border-box + } + + .flex-lg-50, .layout-lg-row > .flex-lg-50, .layout-row > .flex-lg-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 50%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-50, .layout-lg-column > .flex-lg-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 100%; + max-height: 50%; + box-sizing: border-box + } + + .flex-lg-55, .layout-lg-row > .flex-lg-55, .layout-row > .flex-lg-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 55%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-55, .layout-lg-column > .flex-lg-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 100%; + max-height: 55%; + box-sizing: border-box + } + + .flex-lg-60, .layout-lg-row > .flex-lg-60, .layout-row > .flex-lg-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 60%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-60, .layout-lg-column > .flex-lg-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 100%; + max-height: 60%; + box-sizing: border-box + } + + .flex-lg-65, .layout-lg-row > .flex-lg-65, .layout-row > .flex-lg-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 65%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-65, .layout-lg-column > .flex-lg-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 100%; + max-height: 65%; + box-sizing: border-box + } + + .flex-lg-70, .layout-lg-row > .flex-lg-70, .layout-row > .flex-lg-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 70%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-70, .layout-lg-column > .flex-lg-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 100%; + max-height: 70%; + box-sizing: border-box + } + + .flex-lg-75, .layout-lg-row > .flex-lg-75, .layout-row > .flex-lg-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 75%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-75, .layout-lg-column > .flex-lg-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 100%; + max-height: 75%; + box-sizing: border-box + } + + .flex-lg-80, .layout-lg-row > .flex-lg-80, .layout-row > .flex-lg-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 80%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-80, .layout-lg-column > .flex-lg-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 100%; + max-height: 80%; + box-sizing: border-box + } + + .flex-lg-85, .layout-lg-row > .flex-lg-85, .layout-row > .flex-lg-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 85%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-85, .layout-lg-column > .flex-lg-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 100%; + max-height: 85%; + box-sizing: border-box + } + + .flex-lg-90, .layout-lg-row > .flex-lg-90, .layout-row > .flex-lg-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 90%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-90, .layout-lg-column > .flex-lg-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 100%; + max-height: 90%; + box-sizing: border-box + } + + .flex-lg-95, .layout-lg-row > .flex-lg-95, .layout-row > .flex-lg-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 95%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-lg-95, .layout-lg-column > .flex-lg-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 100%; + max-height: 95%; + box-sizing: border-box + } + + .flex-lg-100, .layout-column > .flex-lg-100, .layout-lg-column > .flex-lg-100, .layout-lg-row > .flex-lg-100, .layout-row > .flex-lg-100 { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + max-width: 100%; + max-height: 100%; + box-sizing: border-box + } + + .layout-lg-row > .flex-lg-33, .layout-row > .flex-lg-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 33.33%; + max-height: 100%; + box-sizing: border-box + } + + .layout-lg-row > .flex-lg-66, .layout-row > .flex-lg-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 66.66%; + max-height: 100%; + box-sizing: border-box + } + + .layout-lg-row > .flex, .layout-row > .flex { + min-width: 0 + } + + .layout-column > .flex-lg-33, .layout-lg-column > .flex-lg-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 100%; + max-height: 33.33%; + box-sizing: border-box + } + + .layout-column > .flex-lg-66, .layout-lg-column > .flex-lg-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 100%; + max-height: 66.66%; + box-sizing: border-box + } + + .layout-column > .flex, .layout-lg-column > .flex { + min-height: 0 + } + + .layout-lg, .layout-lg-column, .layout-lg-row { + box-sizing: border-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex + } + + .layout-lg-column { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column + } + + .layout-lg-row { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row + } +} + +@media (min-width: 1920px) { + .flex-order-gt-lg--20 { + -webkit-order: -20; + -ms-flex-order: -20; + order: -20 + } + + .flex-order-gt-lg--19 { + -webkit-order: -19; + -ms-flex-order: -19; + order: -19 + } + + .flex-order-gt-lg--18 { + -webkit-order: -18; + -ms-flex-order: -18; + order: -18 + } + + .flex-order-gt-lg--17 { + -webkit-order: -17; + -ms-flex-order: -17; + order: -17 + } + + .flex-order-gt-lg--16 { + -webkit-order: -16; + -ms-flex-order: -16; + order: -16 + } + + .flex-order-gt-lg--15 { + -webkit-order: -15; + -ms-flex-order: -15; + order: -15 + } + + .flex-order-gt-lg--14 { + -webkit-order: -14; + -ms-flex-order: -14; + order: -14 + } + + .flex-order-gt-lg--13 { + -webkit-order: -13; + -ms-flex-order: -13; + order: -13 + } + + .flex-order-gt-lg--12 { + -webkit-order: -12; + -ms-flex-order: -12; + order: -12 + } + + .flex-order-gt-lg--11 { + -webkit-order: -11; + -ms-flex-order: -11; + order: -11 + } + + .flex-order-gt-lg--10 { + -webkit-order: -10; + -ms-flex-order: -10; + order: -10 + } + + .flex-order-gt-lg--9 { + -webkit-order: -9; + -ms-flex-order: -9; + order: -9 + } + + .flex-order-gt-lg--8 { + -webkit-order: -8; + -ms-flex-order: -8; + order: -8 + } + + .flex-order-gt-lg--7 { + -webkit-order: -7; + -ms-flex-order: -7; + order: -7 + } + + .flex-order-gt-lg--6 { + -webkit-order: -6; + -ms-flex-order: -6; + order: -6 + } + + .flex-order-gt-lg--5 { + -webkit-order: -5; + -ms-flex-order: -5; + order: -5 + } + + .flex-order-gt-lg--4 { + -webkit-order: -4; + -ms-flex-order: -4; + order: -4 + } + + .flex-order-gt-lg--3 { + -webkit-order: -3; + -ms-flex-order: -3; + order: -3 + } + + .flex-order-gt-lg--2 { + -webkit-order: -2; + -ms-flex-order: -2; + order: -2 + } + + .flex-order-gt-lg--1 { + -webkit-order: -1; + -ms-flex-order: -1; + order: -1 + } + + .flex-order-gt-lg-0 { + -webkit-order: 0; + -ms-flex-order: 0; + order: 0 + } + + .flex-order-gt-lg-1 { + -webkit-order: 1; + -ms-flex-order: 1; + order: 1 + } + + .flex-order-gt-lg-2 { + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 + } + + .flex-order-gt-lg-3 { + -webkit-order: 3; + -ms-flex-order: 3; + order: 3 + } + + .flex-order-gt-lg-4 { + -webkit-order: 4; + -ms-flex-order: 4; + order: 4 + } + + .flex-order-gt-lg-5 { + -webkit-order: 5; + -ms-flex-order: 5; + order: 5 + } + + .flex-order-gt-lg-6 { + -webkit-order: 6; + -ms-flex-order: 6; + order: 6 + } + + .flex-order-gt-lg-7 { + -webkit-order: 7; + -ms-flex-order: 7; + order: 7 + } + + .flex-order-gt-lg-8 { + -webkit-order: 8; + -ms-flex-order: 8; + order: 8 + } + + .flex-order-gt-lg-9 { + -webkit-order: 9; + -ms-flex-order: 9; + order: 9 + } + + .flex-order-gt-lg-10 { + -webkit-order: 10; + -ms-flex-order: 10; + order: 10 + } + + .flex-order-gt-lg-11 { + -webkit-order: 11; + -ms-flex-order: 11; + order: 11 + } + + .flex-order-gt-lg-12 { + -webkit-order: 12; + -ms-flex-order: 12; + order: 12 + } + + .flex-order-gt-lg-13 { + -webkit-order: 13; + -ms-flex-order: 13; + order: 13 + } + + .flex-order-gt-lg-14 { + -webkit-order: 14; + -ms-flex-order: 14; + order: 14 + } + + .flex-order-gt-lg-15 { + -webkit-order: 15; + -ms-flex-order: 15; + order: 15 + } + + .flex-order-gt-lg-16 { + -webkit-order: 16; + -ms-flex-order: 16; + order: 16 + } + + .flex-order-gt-lg-17 { + -webkit-order: 17; + -ms-flex-order: 17; + order: 17 + } + + .flex-order-gt-lg-18 { + -webkit-order: 18; + -ms-flex-order: 18; + order: 18 + } + + .flex-order-gt-lg-19 { + -webkit-order: 19; + -ms-flex-order: 19; + order: 19 + } + + .flex-order-gt-lg-20 { + -webkit-order: 20; + -ms-flex-order: 20; + order: 20 + } + + .flex-offset-gt-lg-0, .offset-gt-lg-0 { + margin-left: 0 + } + + [dir=rtl] .flex-offset-gt-lg-0, [dir=rtl] .offset-gt-lg-0 { + margin-left: auto; + margin-right: 0 + } + + .flex-offset-gt-lg-5, .offset-gt-lg-5 { + margin-left: 5% + } + + [dir=rtl] .flex-offset-gt-lg-5, [dir=rtl] .offset-gt-lg-5 { + margin-left: auto; + margin-right: 5% + } + + .flex-offset-gt-lg-10, .offset-gt-lg-10 { + margin-left: 10% + } + + [dir=rtl] .flex-offset-gt-lg-10, [dir=rtl] .offset-gt-lg-10 { + margin-left: auto; + margin-right: 10% + } + + .flex-offset-gt-lg-15, .offset-gt-lg-15 { + margin-left: 15% + } + + [dir=rtl] .flex-offset-gt-lg-15, [dir=rtl] .offset-gt-lg-15 { + margin-left: auto; + margin-right: 15% + } + + .flex-offset-gt-lg-20, .offset-gt-lg-20 { + margin-left: 20% + } + + [dir=rtl] .flex-offset-gt-lg-20, [dir=rtl] .offset-gt-lg-20 { + margin-left: auto; + margin-right: 20% + } + + .flex-offset-gt-lg-25, .offset-gt-lg-25 { + margin-left: 25% + } + + [dir=rtl] .flex-offset-gt-lg-25, [dir=rtl] .offset-gt-lg-25 { + margin-left: auto; + margin-right: 25% + } + + .flex-offset-gt-lg-30, .offset-gt-lg-30 { + margin-left: 30% + } + + [dir=rtl] .flex-offset-gt-lg-30, [dir=rtl] .offset-gt-lg-30 { + margin-left: auto; + margin-right: 30% + } + + .flex-offset-gt-lg-35, .offset-gt-lg-35 { + margin-left: 35% + } + + [dir=rtl] .flex-offset-gt-lg-35, [dir=rtl] .offset-gt-lg-35 { + margin-left: auto; + margin-right: 35% + } + + .flex-offset-gt-lg-40, .offset-gt-lg-40 { + margin-left: 40% + } + + [dir=rtl] .flex-offset-gt-lg-40, [dir=rtl] .offset-gt-lg-40 { + margin-left: auto; + margin-right: 40% + } + + .flex-offset-gt-lg-45, .offset-gt-lg-45 { + margin-left: 45% + } + + [dir=rtl] .flex-offset-gt-lg-45, [dir=rtl] .offset-gt-lg-45 { + margin-left: auto; + margin-right: 45% + } + + .flex-offset-gt-lg-50, .offset-gt-lg-50 { + margin-left: 50% + } + + [dir=rtl] .flex-offset-gt-lg-50, [dir=rtl] .offset-gt-lg-50 { + margin-left: auto; + margin-right: 50% + } + + .flex-offset-gt-lg-55, .offset-gt-lg-55 { + margin-left: 55% + } + + [dir=rtl] .flex-offset-gt-lg-55, [dir=rtl] .offset-gt-lg-55 { + margin-left: auto; + margin-right: 55% + } + + .flex-offset-gt-lg-60, .offset-gt-lg-60 { + margin-left: 60% + } + + [dir=rtl] .flex-offset-gt-lg-60, [dir=rtl] .offset-gt-lg-60 { + margin-left: auto; + margin-right: 60% + } + + .flex-offset-gt-lg-65, .offset-gt-lg-65 { + margin-left: 65% + } + + [dir=rtl] .flex-offset-gt-lg-65, [dir=rtl] .offset-gt-lg-65 { + margin-left: auto; + margin-right: 65% + } + + .flex-offset-gt-lg-70, .offset-gt-lg-70 { + margin-left: 70% + } + + [dir=rtl] .flex-offset-gt-lg-70, [dir=rtl] .offset-gt-lg-70 { + margin-left: auto; + margin-right: 70% + } + + .flex-offset-gt-lg-75, .offset-gt-lg-75 { + margin-left: 75% + } + + [dir=rtl] .flex-offset-gt-lg-75, [dir=rtl] .offset-gt-lg-75 { + margin-left: auto; + margin-right: 75% + } + + .flex-offset-gt-lg-80, .offset-gt-lg-80 { + margin-left: 80% + } + + [dir=rtl] .flex-offset-gt-lg-80, [dir=rtl] .offset-gt-lg-80 { + margin-left: auto; + margin-right: 80% + } + + .flex-offset-gt-lg-85, .offset-gt-lg-85 { + margin-left: 85% + } + + [dir=rtl] .flex-offset-gt-lg-85, [dir=rtl] .offset-gt-lg-85 { + margin-left: auto; + margin-right: 85% + } + + .flex-offset-gt-lg-90, .offset-gt-lg-90 { + margin-left: 90% + } + + [dir=rtl] .flex-offset-gt-lg-90, [dir=rtl] .offset-gt-lg-90 { + margin-left: auto; + margin-right: 90% + } + + .flex-offset-gt-lg-95, .offset-gt-lg-95 { + margin-left: 95% + } + + [dir=rtl] .flex-offset-gt-lg-95, [dir=rtl] .offset-gt-lg-95 { + margin-left: auto; + margin-right: 95% + } + + .flex-offset-gt-lg-33, .offset-gt-lg-33 { + margin-left: calc(100% / 3) + } + + .flex-offset-gt-lg-66, .offset-gt-lg-66 { + margin-left: calc(200% / 3) + } + + [dir=rtl] .flex-offset-gt-lg-66, [dir=rtl] .offset-gt-lg-66 { + margin-left: auto; + margin-right: calc(200% / 3) + } + + .layout-align-gt-lg { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch + } + + .layout-align-gt-lg-start, .layout-align-gt-lg-start-center, .layout-align-gt-lg-start-end, .layout-align-gt-lg-start-start, .layout-align-gt-lg-start-stretch { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start + } + + .layout-align-gt-lg-center, .layout-align-gt-lg-center-center, .layout-align-gt-lg-center-end, .layout-align-gt-lg-center-start, .layout-align-gt-lg-center-stretch { + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center + } + + .layout-align-gt-lg-end, .layout-align-gt-lg-end-center, .layout-align-gt-lg-end-end, .layout-align-gt-lg-end-start, .layout-align-gt-lg-end-stretch { + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end + } + + .layout-align-gt-lg-space-around, .layout-align-gt-lg-space-around-center, .layout-align-gt-lg-space-around-end, .layout-align-gt-lg-space-around-start, .layout-align-gt-lg-space-around-stretch { + -webkit-justify-content: space-around; + -ms-flex-pack: distribute; + justify-content: space-around + } + + .layout-align-gt-lg-space-between, .layout-align-gt-lg-space-between-center, .layout-align-gt-lg-space-between-end, .layout-align-gt-lg-space-between-start, .layout-align-gt-lg-space-between-stretch { + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between + } + + .layout-align-gt-lg-center-start, .layout-align-gt-lg-end-start, .layout-align-gt-lg-space-around-start, .layout-align-gt-lg-space-between-start, .layout-align-gt-lg-start-start { + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + -webkit-align-content: flex-start; + -ms-flex-line-pack: start; + align-content: flex-start + } + + .layout-align-gt-lg-center-center, .layout-align-gt-lg-end-center, .layout-align-gt-lg-space-around-center, .layout-align-gt-lg-space-between-center, .layout-align-gt-lg-start-center { + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-align-content: center; + -ms-flex-line-pack: center; + align-content: center; + max-width: 100% + } + + .layout-align-gt-lg-center-center > *, .layout-align-gt-lg-end-center > *, .layout-align-gt-lg-space-around-center > *, .layout-align-gt-lg-space-between-center > *, .layout-align-gt-lg-start-center > * { + max-width: 100%; + box-sizing: border-box + } + + .layout-align-gt-lg-center-end, .layout-align-gt-lg-end-end, .layout-align-gt-lg-space-around-end, .layout-align-gt-lg-space-between-end, .layout-align-gt-lg-start-end { + -webkit-align-items: flex-end; + -ms-flex-align: end; + align-items: flex-end; + -webkit-align-content: flex-end; + -ms-flex-line-pack: end; + align-content: flex-end + } + + .layout-align-gt-lg-center-stretch, .layout-align-gt-lg-end-stretch, .layout-align-gt-lg-space-around-stretch, .layout-align-gt-lg-space-between-stretch, .layout-align-gt-lg-start-stretch { + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch + } + + .flex-gt-lg { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + box-sizing: border-box + } + + .flex-gt-lg-grow { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + box-sizing: border-box + } + + .flex-gt-lg-initial { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-gt-lg-auto { + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + box-sizing: border-box + } + + .flex-gt-lg-none { + -webkit-flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + box-sizing: border-box + } + + .flex-gt-lg-noshrink { + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + box-sizing: border-box + } + + .flex-gt-lg-nogrow { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-gt-lg-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-lg-row > .flex-gt-lg-0, .layout-row > .flex-gt-lg-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box; + min-width: 0 + } + + .layout-column > .flex-gt-lg-0, .layout-gt-lg-column > .flex-gt-lg-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 100%; + max-height: 0; + box-sizing: border-box; + min-height: 0 + } + + .flex-gt-lg-5, .layout-gt-lg-row > .flex-gt-lg-5, .layout-row > .flex-gt-lg-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 5%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-5, .layout-gt-lg-column > .flex-gt-lg-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 100%; + max-height: 5%; + box-sizing: border-box + } + + .flex-gt-lg-10, .layout-gt-lg-row > .flex-gt-lg-10, .layout-row > .flex-gt-lg-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 10%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-10, .layout-gt-lg-column > .flex-gt-lg-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 100%; + max-height: 10%; + box-sizing: border-box + } + + .flex-gt-lg-15, .layout-gt-lg-row > .flex-gt-lg-15, .layout-row > .flex-gt-lg-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 15%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-15, .layout-gt-lg-column > .flex-gt-lg-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 100%; + max-height: 15%; + box-sizing: border-box + } + + .flex-gt-lg-20, .layout-gt-lg-row > .flex-gt-lg-20, .layout-row > .flex-gt-lg-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 20%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-20, .layout-gt-lg-column > .flex-gt-lg-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 100%; + max-height: 20%; + box-sizing: border-box + } + + .flex-gt-lg-25, .layout-gt-lg-row > .flex-gt-lg-25, .layout-row > .flex-gt-lg-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 25%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-25, .layout-gt-lg-column > .flex-gt-lg-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 100%; + max-height: 25%; + box-sizing: border-box + } + + .flex-gt-lg-30, .layout-gt-lg-row > .flex-gt-lg-30, .layout-row > .flex-gt-lg-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 30%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-30, .layout-gt-lg-column > .flex-gt-lg-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 100%; + max-height: 30%; + box-sizing: border-box + } + + .flex-gt-lg-35, .layout-gt-lg-row > .flex-gt-lg-35, .layout-row > .flex-gt-lg-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 35%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-35, .layout-gt-lg-column > .flex-gt-lg-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 100%; + max-height: 35%; + box-sizing: border-box + } + + .flex-gt-lg-40, .layout-gt-lg-row > .flex-gt-lg-40, .layout-row > .flex-gt-lg-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 40%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-40, .layout-gt-lg-column > .flex-gt-lg-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 100%; + max-height: 40%; + box-sizing: border-box + } + + .flex-gt-lg-45, .layout-gt-lg-row > .flex-gt-lg-45, .layout-row > .flex-gt-lg-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 45%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-45, .layout-gt-lg-column > .flex-gt-lg-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 100%; + max-height: 45%; + box-sizing: border-box + } + + .flex-gt-lg-50, .layout-gt-lg-row > .flex-gt-lg-50, .layout-row > .flex-gt-lg-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 50%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-50, .layout-gt-lg-column > .flex-gt-lg-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 100%; + max-height: 50%; + box-sizing: border-box + } + + .flex-gt-lg-55, .layout-gt-lg-row > .flex-gt-lg-55, .layout-row > .flex-gt-lg-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 55%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-55, .layout-gt-lg-column > .flex-gt-lg-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 100%; + max-height: 55%; + box-sizing: border-box + } + + .flex-gt-lg-60, .layout-gt-lg-row > .flex-gt-lg-60, .layout-row > .flex-gt-lg-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 60%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-60, .layout-gt-lg-column > .flex-gt-lg-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 100%; + max-height: 60%; + box-sizing: border-box + } + + .flex-gt-lg-65, .layout-gt-lg-row > .flex-gt-lg-65, .layout-row > .flex-gt-lg-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 65%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-65, .layout-gt-lg-column > .flex-gt-lg-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 100%; + max-height: 65%; + box-sizing: border-box + } + + .flex-gt-lg-70, .layout-gt-lg-row > .flex-gt-lg-70, .layout-row > .flex-gt-lg-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 70%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-70, .layout-gt-lg-column > .flex-gt-lg-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 100%; + max-height: 70%; + box-sizing: border-box + } + + .flex-gt-lg-75, .layout-gt-lg-row > .flex-gt-lg-75, .layout-row > .flex-gt-lg-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 75%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-75, .layout-gt-lg-column > .flex-gt-lg-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 100%; + max-height: 75%; + box-sizing: border-box + } + + .flex-gt-lg-80, .layout-gt-lg-row > .flex-gt-lg-80, .layout-row > .flex-gt-lg-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 80%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-80, .layout-gt-lg-column > .flex-gt-lg-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 100%; + max-height: 80%; + box-sizing: border-box + } + + .flex-gt-lg-85, .layout-gt-lg-row > .flex-gt-lg-85, .layout-row > .flex-gt-lg-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 85%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-85, .layout-gt-lg-column > .flex-gt-lg-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 100%; + max-height: 85%; + box-sizing: border-box + } + + .flex-gt-lg-90, .layout-gt-lg-row > .flex-gt-lg-90, .layout-row > .flex-gt-lg-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 90%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-90, .layout-gt-lg-column > .flex-gt-lg-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 100%; + max-height: 90%; + box-sizing: border-box + } + + .flex-gt-lg-95, .layout-gt-lg-row > .flex-gt-lg-95, .layout-row > .flex-gt-lg-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 95%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-95, .layout-gt-lg-column > .flex-gt-lg-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 100%; + max-height: 95%; + box-sizing: border-box + } + + .flex-gt-lg-100, .layout-column > .flex-gt-lg-100, .layout-gt-lg-column > .flex-gt-lg-100, .layout-gt-lg-row > .flex-gt-lg-100, .layout-row > .flex-gt-lg-100 { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + max-width: 100%; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-lg-row > .flex-gt-lg-33, .layout-row > .flex-gt-lg-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 33.33%; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-lg-row > .flex-gt-lg-66, .layout-row > .flex-gt-lg-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 66.66%; + max-height: 100%; + box-sizing: border-box + } + + .layout-gt-lg-row > .flex, .layout-row > .flex { + min-width: 0 + } + + .layout-column > .flex-gt-lg-33, .layout-gt-lg-column > .flex-gt-lg-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 100%; + max-height: 33.33%; + box-sizing: border-box + } + + .layout-column > .flex-gt-lg-66, .layout-gt-lg-column > .flex-gt-lg-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 100%; + max-height: 66.66%; + box-sizing: border-box + } + + .layout-column > .flex, .layout-gt-lg-column > .flex { + min-height: 0 + } + + .layout-gt-lg, .layout-gt-lg-column, .layout-gt-lg-row { + box-sizing: border-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex + } + + .layout-gt-lg-column { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column + } + + .layout-gt-lg-row { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row + } + + .flex-order-xl--20 { + -webkit-order: -20; + -ms-flex-order: -20; + order: -20 + } + + .flex-order-xl--19 { + -webkit-order: -19; + -ms-flex-order: -19; + order: -19 + } + + .flex-order-xl--18 { + -webkit-order: -18; + -ms-flex-order: -18; + order: -18 + } + + .flex-order-xl--17 { + -webkit-order: -17; + -ms-flex-order: -17; + order: -17 + } + + .flex-order-xl--16 { + -webkit-order: -16; + -ms-flex-order: -16; + order: -16 + } + + .flex-order-xl--15 { + -webkit-order: -15; + -ms-flex-order: -15; + order: -15 + } + + .flex-order-xl--14 { + -webkit-order: -14; + -ms-flex-order: -14; + order: -14 + } + + .flex-order-xl--13 { + -webkit-order: -13; + -ms-flex-order: -13; + order: -13 + } + + .flex-order-xl--12 { + -webkit-order: -12; + -ms-flex-order: -12; + order: -12 + } + + .flex-order-xl--11 { + -webkit-order: -11; + -ms-flex-order: -11; + order: -11 + } + + .flex-order-xl--10 { + -webkit-order: -10; + -ms-flex-order: -10; + order: -10 + } + + .flex-order-xl--9 { + -webkit-order: -9; + -ms-flex-order: -9; + order: -9 + } + + .flex-order-xl--8 { + -webkit-order: -8; + -ms-flex-order: -8; + order: -8 + } + + .flex-order-xl--7 { + -webkit-order: -7; + -ms-flex-order: -7; + order: -7 + } + + .flex-order-xl--6 { + -webkit-order: -6; + -ms-flex-order: -6; + order: -6 + } + + .flex-order-xl--5 { + -webkit-order: -5; + -ms-flex-order: -5; + order: -5 + } + + .flex-order-xl--4 { + -webkit-order: -4; + -ms-flex-order: -4; + order: -4 + } + + .flex-order-xl--3 { + -webkit-order: -3; + -ms-flex-order: -3; + order: -3 + } + + .flex-order-xl--2 { + -webkit-order: -2; + -ms-flex-order: -2; + order: -2 + } + + .flex-order-xl--1 { + -webkit-order: -1; + -ms-flex-order: -1; + order: -1 + } + + .flex-order-xl-0 { + -webkit-order: 0; + -ms-flex-order: 0; + order: 0 + } + + .flex-order-xl-1 { + -webkit-order: 1; + -ms-flex-order: 1; + order: 1 + } + + .flex-order-xl-2 { + -webkit-order: 2; + -ms-flex-order: 2; + order: 2 + } + + .flex-order-xl-3 { + -webkit-order: 3; + -ms-flex-order: 3; + order: 3 + } + + .flex-order-xl-4 { + -webkit-order: 4; + -ms-flex-order: 4; + order: 4 + } + + .flex-order-xl-5 { + -webkit-order: 5; + -ms-flex-order: 5; + order: 5 + } + + .flex-order-xl-6 { + -webkit-order: 6; + -ms-flex-order: 6; + order: 6 + } + + .flex-order-xl-7 { + -webkit-order: 7; + -ms-flex-order: 7; + order: 7 + } + + .flex-order-xl-8 { + -webkit-order: 8; + -ms-flex-order: 8; + order: 8 + } + + .flex-order-xl-9 { + -webkit-order: 9; + -ms-flex-order: 9; + order: 9 + } + + .flex-order-xl-10 { + -webkit-order: 10; + -ms-flex-order: 10; + order: 10 + } + + .flex-order-xl-11 { + -webkit-order: 11; + -ms-flex-order: 11; + order: 11 + } + + .flex-order-xl-12 { + -webkit-order: 12; + -ms-flex-order: 12; + order: 12 + } + + .flex-order-xl-13 { + -webkit-order: 13; + -ms-flex-order: 13; + order: 13 + } + + .flex-order-xl-14 { + -webkit-order: 14; + -ms-flex-order: 14; + order: 14 + } + + .flex-order-xl-15 { + -webkit-order: 15; + -ms-flex-order: 15; + order: 15 + } + + .flex-order-xl-16 { + -webkit-order: 16; + -ms-flex-order: 16; + order: 16 + } + + .flex-order-xl-17 { + -webkit-order: 17; + -ms-flex-order: 17; + order: 17 + } + + .flex-order-xl-18 { + -webkit-order: 18; + -ms-flex-order: 18; + order: 18 + } + + .flex-order-xl-19 { + -webkit-order: 19; + -ms-flex-order: 19; + order: 19 + } + + .flex-order-xl-20 { + -webkit-order: 20; + -ms-flex-order: 20; + order: 20 + } + + .flex-offset-xl-0, .offset-xl-0 { + margin-left: 0 + } + + [dir=rtl] .flex-offset-xl-0, [dir=rtl] .offset-xl-0 { + margin-left: auto; + margin-right: 0 + } + + .flex-offset-xl-5, .offset-xl-5 { + margin-left: 5% + } + + [dir=rtl] .flex-offset-xl-5, [dir=rtl] .offset-xl-5 { + margin-left: auto; + margin-right: 5% + } + + .flex-offset-xl-10, .offset-xl-10 { + margin-left: 10% + } + + [dir=rtl] .flex-offset-xl-10, [dir=rtl] .offset-xl-10 { + margin-left: auto; + margin-right: 10% + } + + .flex-offset-xl-15, .offset-xl-15 { + margin-left: 15% + } + + [dir=rtl] .flex-offset-xl-15, [dir=rtl] .offset-xl-15 { + margin-left: auto; + margin-right: 15% + } + + .flex-offset-xl-20, .offset-xl-20 { + margin-left: 20% + } + + [dir=rtl] .flex-offset-xl-20, [dir=rtl] .offset-xl-20 { + margin-left: auto; + margin-right: 20% + } + + .flex-offset-xl-25, .offset-xl-25 { + margin-left: 25% + } + + [dir=rtl] .flex-offset-xl-25, [dir=rtl] .offset-xl-25 { + margin-left: auto; + margin-right: 25% + } + + .flex-offset-xl-30, .offset-xl-30 { + margin-left: 30% + } + + [dir=rtl] .flex-offset-xl-30, [dir=rtl] .offset-xl-30 { + margin-left: auto; + margin-right: 30% + } + + .flex-offset-xl-35, .offset-xl-35 { + margin-left: 35% + } + + [dir=rtl] .flex-offset-xl-35, [dir=rtl] .offset-xl-35 { + margin-left: auto; + margin-right: 35% + } + + .flex-offset-xl-40, .offset-xl-40 { + margin-left: 40% + } + + [dir=rtl] .flex-offset-xl-40, [dir=rtl] .offset-xl-40 { + margin-left: auto; + margin-right: 40% + } + + .flex-offset-xl-45, .offset-xl-45 { + margin-left: 45% + } + + [dir=rtl] .flex-offset-xl-45, [dir=rtl] .offset-xl-45 { + margin-left: auto; + margin-right: 45% + } + + .flex-offset-xl-50, .offset-xl-50 { + margin-left: 50% + } + + [dir=rtl] .flex-offset-xl-50, [dir=rtl] .offset-xl-50 { + margin-left: auto; + margin-right: 50% + } + + .flex-offset-xl-55, .offset-xl-55 { + margin-left: 55% + } + + [dir=rtl] .flex-offset-xl-55, [dir=rtl] .offset-xl-55 { + margin-left: auto; + margin-right: 55% + } + + .flex-offset-xl-60, .offset-xl-60 { + margin-left: 60% + } + + [dir=rtl] .flex-offset-xl-60, [dir=rtl] .offset-xl-60 { + margin-left: auto; + margin-right: 60% + } + + .flex-offset-xl-65, .offset-xl-65 { + margin-left: 65% + } + + [dir=rtl] .flex-offset-xl-65, [dir=rtl] .offset-xl-65 { + margin-left: auto; + margin-right: 65% + } + + .flex-offset-xl-70, .offset-xl-70 { + margin-left: 70% + } + + [dir=rtl] .flex-offset-xl-70, [dir=rtl] .offset-xl-70 { + margin-left: auto; + margin-right: 70% + } + + .flex-offset-xl-75, .offset-xl-75 { + margin-left: 75% + } + + [dir=rtl] .flex-offset-xl-75, [dir=rtl] .offset-xl-75 { + margin-left: auto; + margin-right: 75% + } + + .flex-offset-xl-80, .offset-xl-80 { + margin-left: 80% + } + + [dir=rtl] .flex-offset-xl-80, [dir=rtl] .offset-xl-80 { + margin-left: auto; + margin-right: 80% + } + + .flex-offset-xl-85, .offset-xl-85 { + margin-left: 85% + } + + [dir=rtl] .flex-offset-xl-85, [dir=rtl] .offset-xl-85 { + margin-left: auto; + margin-right: 85% + } + + .flex-offset-xl-90, .offset-xl-90 { + margin-left: 90% + } + + [dir=rtl] .flex-offset-xl-90, [dir=rtl] .offset-xl-90 { + margin-left: auto; + margin-right: 90% + } + + .flex-offset-xl-95, .offset-xl-95 { + margin-left: 95% + } + + [dir=rtl] .flex-offset-xl-95, [dir=rtl] .offset-xl-95 { + margin-left: auto; + margin-right: 95% + } + + .flex-offset-xl-33, .offset-xl-33 { + margin-left: calc(100% / 3) + } + + .flex-offset-xl-66, .offset-xl-66 { + margin-left: calc(200% / 3) + } + + [dir=rtl] .flex-offset-xl-66, [dir=rtl] .offset-xl-66 { + margin-left: auto; + margin-right: calc(200% / 3) + } + + .layout-align-xl { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch + } + + .layout-align-xl-start, .layout-align-xl-start-center, .layout-align-xl-start-end, .layout-align-xl-start-start, .layout-align-xl-start-stretch { + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start + } + + .layout-align-xl-center, .layout-align-xl-center-center, .layout-align-xl-center-end, .layout-align-xl-center-start, .layout-align-xl-center-stretch { + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center + } + + .layout-align-xl-end, .layout-align-xl-end-center, .layout-align-xl-end-end, .layout-align-xl-end-start, .layout-align-xl-end-stretch { + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end + } + + .layout-align-xl-space-around, .layout-align-xl-space-around-center, .layout-align-xl-space-around-end, .layout-align-xl-space-around-start, .layout-align-xl-space-around-stretch { + -webkit-justify-content: space-around; + -ms-flex-pack: distribute; + justify-content: space-around + } + + .layout-align-xl-space-between, .layout-align-xl-space-between-center, .layout-align-xl-space-between-end, .layout-align-xl-space-between-start, .layout-align-xl-space-between-stretch { + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between + } + + .layout-align-xl-center-start, .layout-align-xl-end-start, .layout-align-xl-space-around-start, .layout-align-xl-space-between-start, .layout-align-xl-start-start { + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + -webkit-align-content: flex-start; + -ms-flex-line-pack: start; + align-content: flex-start + } + + .layout-align-xl-center-center, .layout-align-xl-end-center, .layout-align-xl-space-around-center, .layout-align-xl-space-between-center, .layout-align-xl-start-center { + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-align-content: center; + -ms-flex-line-pack: center; + align-content: center; + max-width: 100% + } + + .layout-align-xl-center-center > *, .layout-align-xl-end-center > *, .layout-align-xl-space-around-center > *, .layout-align-xl-space-between-center > *, .layout-align-xl-start-center > * { + max-width: 100%; + box-sizing: border-box + } + + .layout-align-xl-center-end, .layout-align-xl-end-end, .layout-align-xl-space-around-end, .layout-align-xl-space-between-end, .layout-align-xl-start-end { + -webkit-align-items: flex-end; + -ms-flex-align: end; + align-items: flex-end; + -webkit-align-content: flex-end; + -ms-flex-line-pack: end; + align-content: flex-end + } + + .layout-align-xl-center-stretch, .layout-align-xl-end-stretch, .layout-align-xl-space-around-stretch, .layout-align-xl-space-between-stretch, .layout-align-xl-start-stretch { + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-align-content: stretch; + -ms-flex-line-pack: stretch; + align-content: stretch + } + + .flex-xl { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + box-sizing: border-box + } + + .flex-xl-grow { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + box-sizing: border-box + } + + .flex-xl-initial { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-xl-auto { + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + box-sizing: border-box + } + + .flex-xl-none { + -webkit-flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + box-sizing: border-box + } + + .flex-xl-noshrink { + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + box-sizing: border-box + } + + .flex-xl-nogrow { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + box-sizing: border-box + } + + .flex-xl-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box + } + + .layout-row > .flex-xl-0, .layout-xl-row > .flex-xl-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 0; + max-height: 100%; + box-sizing: border-box; + min-width: 0 + } + + .layout-column > .flex-xl-0, .layout-xl-column > .flex-xl-0 { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0; + flex: 1 1 0; + max-width: 100%; + max-height: 0; + box-sizing: border-box; + min-height: 0 + } + + .flex-xl-5, .layout-row > .flex-xl-5, .layout-xl-row > .flex-xl-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 5%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-5, .layout-xl-column > .flex-xl-5 { + -webkit-flex: 1 1 5%; + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 100%; + max-height: 5%; + box-sizing: border-box + } + + .flex-xl-10, .layout-row > .flex-xl-10, .layout-xl-row > .flex-xl-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 10%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-10, .layout-xl-column > .flex-xl-10 { + -webkit-flex: 1 1 10%; + -ms-flex: 1 1 10%; + flex: 1 1 10%; + max-width: 100%; + max-height: 10%; + box-sizing: border-box + } + + .flex-xl-15, .layout-row > .flex-xl-15, .layout-xl-row > .flex-xl-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 15%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-15, .layout-xl-column > .flex-xl-15 { + -webkit-flex: 1 1 15%; + -ms-flex: 1 1 15%; + flex: 1 1 15%; + max-width: 100%; + max-height: 15%; + box-sizing: border-box + } + + .flex-xl-20, .layout-row > .flex-xl-20, .layout-xl-row > .flex-xl-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 20%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-20, .layout-xl-column > .flex-xl-20 { + -webkit-flex: 1 1 20%; + -ms-flex: 1 1 20%; + flex: 1 1 20%; + max-width: 100%; + max-height: 20%; + box-sizing: border-box + } + + .flex-xl-25, .layout-row > .flex-xl-25, .layout-xl-row > .flex-xl-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 25%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-25, .layout-xl-column > .flex-xl-25 { + -webkit-flex: 1 1 25%; + -ms-flex: 1 1 25%; + flex: 1 1 25%; + max-width: 100%; + max-height: 25%; + box-sizing: border-box + } + + .flex-xl-30, .layout-row > .flex-xl-30, .layout-xl-row > .flex-xl-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 30%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-30, .layout-xl-column > .flex-xl-30 { + -webkit-flex: 1 1 30%; + -ms-flex: 1 1 30%; + flex: 1 1 30%; + max-width: 100%; + max-height: 30%; + box-sizing: border-box + } + + .flex-xl-35, .layout-row > .flex-xl-35, .layout-xl-row > .flex-xl-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 35%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-35, .layout-xl-column > .flex-xl-35 { + -webkit-flex: 1 1 35%; + -ms-flex: 1 1 35%; + flex: 1 1 35%; + max-width: 100%; + max-height: 35%; + box-sizing: border-box + } + + .flex-xl-40, .layout-row > .flex-xl-40, .layout-xl-row > .flex-xl-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 40%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-40, .layout-xl-column > .flex-xl-40 { + -webkit-flex: 1 1 40%; + -ms-flex: 1 1 40%; + flex: 1 1 40%; + max-width: 100%; + max-height: 40%; + box-sizing: border-box + } + + .flex-xl-45, .layout-row > .flex-xl-45, .layout-xl-row > .flex-xl-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 45%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-45, .layout-xl-column > .flex-xl-45 { + -webkit-flex: 1 1 45%; + -ms-flex: 1 1 45%; + flex: 1 1 45%; + max-width: 100%; + max-height: 45%; + box-sizing: border-box + } + + .flex-xl-50, .layout-row > .flex-xl-50, .layout-xl-row > .flex-xl-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 50%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-50, .layout-xl-column > .flex-xl-50 { + -webkit-flex: 1 1 50%; + -ms-flex: 1 1 50%; + flex: 1 1 50%; + max-width: 100%; + max-height: 50%; + box-sizing: border-box + } + + .flex-xl-55, .layout-row > .flex-xl-55, .layout-xl-row > .flex-xl-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 55%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-55, .layout-xl-column > .flex-xl-55 { + -webkit-flex: 1 1 55%; + -ms-flex: 1 1 55%; + flex: 1 1 55%; + max-width: 100%; + max-height: 55%; + box-sizing: border-box + } + + .flex-xl-60, .layout-row > .flex-xl-60, .layout-xl-row > .flex-xl-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 60%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-60, .layout-xl-column > .flex-xl-60 { + -webkit-flex: 1 1 60%; + -ms-flex: 1 1 60%; + flex: 1 1 60%; + max-width: 100%; + max-height: 60%; + box-sizing: border-box + } + + .flex-xl-65, .layout-row > .flex-xl-65, .layout-xl-row > .flex-xl-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 65%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-65, .layout-xl-column > .flex-xl-65 { + -webkit-flex: 1 1 65%; + -ms-flex: 1 1 65%; + flex: 1 1 65%; + max-width: 100%; + max-height: 65%; + box-sizing: border-box + } + + .flex-xl-70, .layout-row > .flex-xl-70, .layout-xl-row > .flex-xl-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 70%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-70, .layout-xl-column > .flex-xl-70 { + -webkit-flex: 1 1 70%; + -ms-flex: 1 1 70%; + flex: 1 1 70%; + max-width: 100%; + max-height: 70%; + box-sizing: border-box + } + + .flex-xl-75, .layout-row > .flex-xl-75, .layout-xl-row > .flex-xl-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 75%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-75, .layout-xl-column > .flex-xl-75 { + -webkit-flex: 1 1 75%; + -ms-flex: 1 1 75%; + flex: 1 1 75%; + max-width: 100%; + max-height: 75%; + box-sizing: border-box + } + + .flex-xl-80, .layout-row > .flex-xl-80, .layout-xl-row > .flex-xl-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 80%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-80, .layout-xl-column > .flex-xl-80 { + -webkit-flex: 1 1 80%; + -ms-flex: 1 1 80%; + flex: 1 1 80%; + max-width: 100%; + max-height: 80%; + box-sizing: border-box + } + + .flex-xl-85, .layout-row > .flex-xl-85, .layout-xl-row > .flex-xl-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 85%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-85, .layout-xl-column > .flex-xl-85 { + -webkit-flex: 1 1 85%; + -ms-flex: 1 1 85%; + flex: 1 1 85%; + max-width: 100%; + max-height: 85%; + box-sizing: border-box + } + + .flex-xl-90, .layout-row > .flex-xl-90, .layout-xl-row > .flex-xl-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 90%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-90, .layout-xl-column > .flex-xl-90 { + -webkit-flex: 1 1 90%; + -ms-flex: 1 1 90%; + flex: 1 1 90%; + max-width: 100%; + max-height: 90%; + box-sizing: border-box + } + + .flex-xl-95, .layout-row > .flex-xl-95, .layout-xl-row > .flex-xl-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 95%; + max-height: 100%; + box-sizing: border-box + } + + .layout-column > .flex-xl-95, .layout-xl-column > .flex-xl-95 { + -webkit-flex: 1 1 95%; + -ms-flex: 1 1 95%; + flex: 1 1 95%; + max-width: 100%; + max-height: 95%; + box-sizing: border-box + } + + .flex-xl-100, .layout-column > .flex-xl-100, .layout-row > .flex-xl-100, .layout-xl-column > .flex-xl-100, .layout-xl-row > .flex-xl-100 { + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + max-width: 100%; + max-height: 100%; + box-sizing: border-box + } + + .layout-row > .flex-xl-33, .layout-xl-row > .flex-xl-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 33.33%; + max-height: 100%; + box-sizing: border-box + } + + .layout-row > .flex-xl-66, .layout-xl-row > .flex-xl-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 66.66%; + max-height: 100%; + box-sizing: border-box + } + + .layout-row > .flex, .layout-xl-row > .flex { + min-width: 0 + } + + .layout-column > .flex-xl-33, .layout-xl-column > .flex-xl-33 { + -webkit-flex: 1 1 33.33%; + -ms-flex: 1 1 33.33%; + flex: 1 1 33.33%; + max-width: 100%; + max-height: 33.33%; + box-sizing: border-box + } + + .layout-column > .flex-xl-66, .layout-xl-column > .flex-xl-66 { + -webkit-flex: 1 1 66.66%; + -ms-flex: 1 1 66.66%; + flex: 1 1 66.66%; + max-width: 100%; + max-height: 66.66%; + box-sizing: border-box + } + + .layout-column > .flex, .layout-xl-column > .flex { + min-height: 0 + } + + .layout-xl, .layout-xl-column, .layout-xl-row { + box-sizing: border-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex + } + + .layout-xl-column { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column + } + + .layout-xl-row { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row + } + + .hide-gt-lg:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-gt-lg):not(.show-xl):not(.show), .hide-gt-md:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-gt-lg):not(.show-xl):not(.show), .hide-gt-sm:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-gt-lg):not(.show-xl):not(.show), .hide-gt-xs:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-gt-lg):not(.show-xl):not(.show), .hide-xl:not(.show-xl):not(.show-gt-lg):not(.show-gt-md):not(.show-gt-sm):not(.show-gt-xs):not(.show), .hide:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-gt-lg):not(.show-xl):not(.show) { + display: none + } +} + +@media print { + .hide-print:not(.show-print):not(.show) { + display: none !important + } +} \ No newline at end of file diff --git a/src/main/resources/static/vendor/angular-material/angular-material.min.js b/src/main/resources/static/vendor/angular-material/angular-material.min.js new file mode 100644 index 0000000..87f1bca --- /dev/null +++ b/src/main/resources/static/vendor/angular-material/angular-material.min.js @@ -0,0 +1,15 @@ +/*! + * Angular Material Design + * https://github.com/angular/material + * @license MIT + * v1.1.0-rc2 + */ +!function(e,t,n){"use strict";!function(){t.module("ngMaterial",["ng","ngAnimate","ngAria","material.core","material.core.gestures","material.core.layout","material.core.theming.palette","material.core.theming","material.core.animate","material.components.autocomplete","material.components.bottomSheet","material.components.backdrop","material.components.button","material.components.card","material.components.checkbox","material.components.chips","material.components.content","material.components.datepicker","material.components.dialog","material.components.divider","material.components.fabActions","material.components.fabShared","material.components.fabSpeedDial","material.components.fabToolbar","material.components.gridList","material.components.fabTrigger","material.components.icon","material.components.input","material.components.list","material.components.menuBar","material.components.menu","material.components.progressCircular","material.components.progressLinear","material.components.radioButton","material.components.select","material.components.showHide","material.components.sidenav","material.components.sticky","material.components.slider","material.components.subheader","material.components.swipe","material.components.switch","material.components.tabs","material.components.toast","material.components.toolbar","material.components.virtualRepeat","material.components.tooltip","material.components.whiteframe"])}(),function(){function e(e,t){if(t.has("$swipe")){var n="You are using the ngTouch module. \nAngular Material already has mobile click, tap, and swipe support... \nngTouch is not supported with Angular Material!";e.warn(n)}}function n(e,t){e.decorator("$$rAF",["$delegate",o]),t.theme("default").primaryPalette("indigo").accentPalette("pink").warnPalette("deep-orange").backgroundPalette("grey")}function o(e){return e.throttle=function(t){var n,o,r,i;return function(){n=arguments,i=this,r=t,o||(o=!0,e(function(){r.apply(i,Array.prototype.slice.call(n)),o=!1}))}},e}t.module("material.core",["ngAnimate","material.core.animate","material.core.layout","material.core.gestures","material.core.theming"]).config(n).run(e),e.$inject=["$log","$injector"],n.$inject=["$provide","$mdThemingProvider"],o.$inject=["$delegate"]}(),function(){function e(){return{restrict:"A",link:n}}function n(e,t,n){var o=n.mdAutoFocus||n.mdAutofocus||n.mdSidenavFocus;e.$watch(o,function(e){t.toggleClass("_md-autofocus",e)})}t.module("material.core").directive("mdAutofocus",e).directive("mdAutoFocus",e).directive("mdSidenavFocus",e)}(),function(){function e(e){function t(e){return n?"webkit"+e.charAt(0).toUpperCase()+e.substring(1):e}var n=/webkit/i.test(e.vendorPrefix);return{KEY_CODE:{COMMA:188,SEMICOLON:186,ENTER:13,ESCAPE:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT_ARROW:37,UP_ARROW:38,RIGHT_ARROW:39,DOWN_ARROW:40,TAB:9,BACKSPACE:8,DELETE:46},CSS:{TRANSITIONEND:"transitionend"+(n?" webkitTransitionEnd":""),ANIMATIONEND:"animationend"+(n?" webkitAnimationEnd":""),TRANSFORM:t("transform"),TRANSFORM_ORIGIN:t("transformOrigin"),TRANSITION:t("transition"),TRANSITION_DURATION:t("transitionDuration"),ANIMATION_PLAY_STATE:t("animationPlayState"),ANIMATION_DURATION:t("animationDuration"),ANIMATION_NAME:t("animationName"),ANIMATION_TIMING:t("animationTimingFunction"),ANIMATION_DIRECTION:t("animationDirection")},MEDIA:{xs:"(max-width: 599px)","gt-xs":"(min-width: 600px)",sm:"(min-width: 600px) and (max-width: 959px)","gt-sm":"(min-width: 960px)",md:"(min-width: 960px) and (max-width: 1279px)","gt-md":"(min-width: 1280px)",lg:"(min-width: 1280px) and (max-width: 1919px)","gt-lg":"(min-width: 1920px)",xl:"(min-width: 1920px)",print:"print"},MEDIA_PRIORITY:["xl","gt-lg","lg","gt-md","md","gt-sm","sm","gt-xs","xs","print"]}}t.module("material.core").factory("$mdConstant",e),e.$inject=["$sniffer"]}(),function(){function e(e,n){function o(){return[].concat(v)}function r(){return v.length}function i(e){return v.length&&e>-1&&e-1}function h(){return v.length?v[0]:null}function f(){return v.length?v[v.length-1]:null}function g(e,o,r,a){r=r||b;for(var d=u(o);;){if(!i(d))return null;var c=d+(e?-1:1),s=null;if(i(c)?s=v[c]:n&&(s=e?f():h(),c=u(s)),null===s||c===a)return null;if(r(s))return s;t.isUndefined(a)&&(a=c),d=c}}var b=function(){return!0};e&&!t.isArray(e)&&(e=Array.prototype.slice.call(e)),n=!!n;var v=e||[];return{items:o,count:r,inRange:i,contains:p,indexOf:u,itemAt:c,findBy:s,add:l,remove:m,first:h,last:f,next:t.bind(null,g,!1),previous:t.bind(null,g,!0),hasPrevious:d,hasNext:a}}t.module("material.core").config(["$provide",function(t){t.decorator("$mdUtil",["$delegate",function(t){return t.iterator=e,t}])}])}(),function(){function e(e,n,o){function r(e){var n=u[e];t.isUndefined(n)&&(n=u[e]=i(e));var o=h[n];return t.isUndefined(o)&&(o=a(n)),o}function i(t){return e.MEDIA[t]||("("!==t.charAt(0)?"("+t+")":t)}function a(e){var t=p[e];return t||(t=p[e]=o.matchMedia(e)),t.addListener(d),h[t.media]=!!t.matches}function d(e){n.$evalAsync(function(){h[e.media]=!!e.matches})}function c(e){return p[e]}function s(t,n){for(var o=0;o-1}var c=!("rtl"==o[0].dir||"rtl"==o[0].body.dir);return 0==arguments.length?c?"ltr":"rtl":void(c&&t.isDefined(r)?t.element(e).css(n,a(r)):!c&&t.isDefined(i)&&t.element(e).css(n,a(i)))},clientRect:function(e,t,n){var o=p(e);t=p(t||o.offsetParent||document.body);var r=o.getBoundingClientRect(),i=n?t.getBoundingClientRect():{left:0,top:0,width:0,height:0};return{left:r.left-i.left,top:r.top-i.top,width:r.width,height:r.height}},offsetRect:function(e,t){return v.clientRect(e,t,!0)},nodesToArray:function(e){e=e||[];for(var t=[],n=0;n
').css("z-index",o);return e.appendChild(r[0]),r.on("wheel",n),r.on("touchmove",n),function(){r.off("wheel"),r.off("touchmove"),r[0].parentNode.removeChild(r[0]),delete v.disableScrollAround._enableScrolling}}function i(){var e=d.parentNode,t=e.style.cssText||"",n=d.style.cssText||"",o=v.scrollTop(d),r=d.clientWidth;return d.scrollHeight>d.clientHeight+1&&(a(d,{position:"fixed",width:"100%",top:-o+"px"}),a(e,{overflowY:"scroll"})),d.clientWidth
").css({width:"100%","z-index":-1,position:"absolute",height:"35px","overflow-y":"scroll"});e.children().css("height","60px"),o[0].body.appendChild(e[0]),this.floatingScrollbars.cached=e[0].offsetWidth==e[0].childNodes[0].offsetWidth,e.remove()}return this.floatingScrollbars.cached},forceFocus:function(t){var n=t[0]||t;document.addEventListener("click",function r(e){e.target===n&&e.$focus&&(n.focus(),e.stopImmediatePropagation(),e.preventDefault(),n.removeEventListener("click",r))},!0);var o=document.createEvent("MouseEvents");o.initMouseEvent("click",!1,!0,e,{},0,0,0,0,!1,!1,!1,!1,0,null),o.$material=!0,o.$focus=!0,n.dispatchEvent(o)},createBackdrop:function(e,t){return a(v.supplant('',[t]))(e)},supplant:function(e,t,n){return n=n||/\{([^\{\}]*)\}/g,e.replace(n,function(e,n){var o=n.split("."),r=t;try{for(var i in o)o.hasOwnProperty(i)&&(r=r[o[i]])}catch(a){r=e}return"string"==typeof r||"number"==typeof r?r:e})},fakeNgModel:function(){return{$fake:!0,$setTouched:t.noop,$setViewValue:function(e){this.$viewValue=e,this.$render(e),this.$viewChangeListeners.forEach(function(e){e()})},$isEmpty:function(e){return 0===(""+e).length},$parsers:[],$formatters:[],$viewChangeListeners:[],$render:t.noop}},debounce:function(e,t,o,r){var a;return function(){var d=o,c=Array.prototype.slice.call(arguments);i.cancel(a),a=i(function(){a=n,e.apply(d,c)},t||10,r)}},throttle:function(e,t){var n;return function(){var o=this,r=arguments,i=v.now();(!n||i-n>t)&&(e.apply(o,r),n=i)}},time:function(e){var t=v.now();return e(),v.now()-t},valueOnUse:function(e,t,n){var o=null,r=Array.prototype.slice.call(arguments),i=r.length>3?r.slice(3):[];Object.defineProperty(e,t,{get:function(){return null===o&&(o=n.apply(e,i)),o}})},nextUid:function(){return""+r++},disconnectScope:function(e){if(e&&e.$root!==e&&!e.$$destroyed){var t=e.$parent;e.$$disconnected=!0,t.$$childHead===e&&(t.$$childHead=e.$$nextSibling),t.$$childTail===e&&(t.$$childTail=e.$$prevSibling),e.$$prevSibling&&(e.$$prevSibling.$$nextSibling=e.$$nextSibling),e.$$nextSibling&&(e.$$nextSibling.$$prevSibling=e.$$prevSibling),e.$$nextSibling=e.$$prevSibling=null}},reconnectScope:function(e){if(e&&e.$root!==e&&e.$$disconnected){var t=e,n=t.$parent;t.$$disconnected=!1,t.$$prevSibling=n.$$childTail,n.$$childHead?(n.$$childTail.$$nextSibling=t,n.$$childTail=t):n.$$childHead=n.$$childTail=t}},getClosest:function(e,n,o){if(e instanceof t.element&&(e=e[0]),n=n.toUpperCase(),o&&(e=e.parentNode),!e)return null;do if(e.nodeName===n)return e;while(e=e.parentNode);return null},elementContains:function(n,o){var r=e.Node&&e.Node.prototype&&Node.prototype.contains,i=r?t.bind(n,n.contains):t.bind(n,function(e){return n===o||!!(16&this.compareDocumentPosition(e))});return i(o)},extractElementByName:function(e,n,o,r){function i(e){return a(e)||(o?d(e):null)}function a(e){if(e)for(var t=0,o=e.length;o>t;t++)if(e[t].nodeName.toLowerCase()===n)return e[t];return null}function d(e){var t;if(e)for(var n=0,o=e.length;o>n;n++){var r=e[n];if(!t)for(var a=0,d=r.childNodes.length;d>a;a++)t=t||i([r.childNodes[a]])}return t}var c=i(e);return!c&&r&&l.warn(v.supplant("Unable to find node '{0}' in element '{1}'.",[n,e[0].outerHTML])),t.element(c||e)},initOptionalProperties:function(e,n,o){o=o||{},t.forEach(e.$$isolateBindings,function(r,i){if(r.optional&&t.isUndefined(e[i])){var a=t.isDefined(n[r.attrName]);e[i]=t.isDefined(o[i])?o[i]:a}})},nextTick:function(e,t,n){function o(){var e=n&&n.$$destroyed,t=e?[]:r.queue,o=e?null:r.digest;r.queue=[],r.timeout=null,r.digest=!1,t.forEach(function(e){e()}),o&&d.$digest()}var r=v.nextTick,a=r.timeout,c=r.queue||[];return c.push(e),null==t&&(t=!0),r.digest=r.digest||t,r.queue=c,a||(r.timeout=i(o,0,!1))},processTemplate:function(e){return g?e:e&&t.isString(e)?e.replace(/\{\{/g,h).replace(/}}/g,f):e},getParentWithPointerEvents:function(e){for(var t=e.parent();b(t,"pointer-events","none");)t=t.parent();return t},getNearestContentElement:function(e){for(var t=e.parent()[0];t&&t!==m[0]&&t!==document.body&&"MD-CONTENT"!==t.nodeName.toUpperCase();)t=t.parentNode;return t},parseAttributeBoolean:function(e,t){return""===e||!!e&&(t===!1||"false"!==e&&"0"!==e)},hasComputedStyle:b};return v.dom.animator=c(v),v}var r=0;t.module("material.core").factory("$mdUtil",o),o.$inject=["$document","$timeout","$compile","$rootScope","$$mdAnimate","$interpolate","$log","$rootElement","$window"],t.element.prototype.focus=t.element.prototype.focus||function(){return this.length&&this[0].focus(),this},t.element.prototype.blur=t.element.prototype.blur||function(){return this.length&&this[0].blur(),this}}(),function(){function e(e,n,o,r){function i(e,o,r){var i=t.element(e)[0]||e;!i||i.hasAttribute(o)&&0!==i.getAttribute(o).length||s(i,o)||(r=t.isString(r)?r.trim():"",r.length?e.attr(o,r):n.warn('ARIA: Attribute "',o,'", required for accessibility, is missing on node:',i))}function a(t,n,o){e(function(){i(t,n,o())})}function d(e,t){var n=c(e)||"",o=n.indexOf(r.startSymbol())>-1;o?a(e,t,function(){return c(e)}):i(e,t,n)}function c(e){return(e.text()||"").trim()}function s(e,t){function n(e){var t=e.currentStyle?e.currentStyle:o.getComputedStyle(e);return"none"===t.display}var r=e.hasChildNodes(),i=!1;if(r)for(var a=e.childNodes,d=0;d").html(o.trim()).contents(),c=r(a);return n={locals:e,element:a,link:function(o){if(e.$scope=o,l){var r=i(l,e,!0);f&&t.extend(r.instance,e);var d=r();a.data("$ngControllerController",d),a.children().data("$ngControllerController",d),m&&(o[m]=d),n.controller=d}return c(o)}}})}}t.module("material.core").service("$mdCompiler",e),e.$inject=["$q","$http","$injector","$compile","$controller","$templateCache"]}(),function(){function n(){}function o(n,o,r){function i(e){return function(t,n){n.distancethis.options.maxDistance&&this.cancel()},onEnd:function(){this.onCancel()}}).handler("drag",{options:{minDistance:6,horizontal:!0,cancelMultiplier:1.5},onStart:function(e){this.state.registeredParent||this.cancel()},onMove:function(e,t){var n,o;e.preventDefault(),this.state.dragPointer?this.dispatchDragMove(e):(this.state.options.horizontal?(n=Math.abs(t.distanceX)>this.state.options.minDistance,o=Math.abs(t.distanceY)>this.state.options.minDistance*this.state.options.cancelMultiplier):(n=Math.abs(t.distanceY)>this.state.options.minDistance,o=Math.abs(t.distanceX)>this.state.options.minDistance*this.state.options.cancelMultiplier),n?(this.state.dragPointer=d(e),l(e,this.state.dragPointer),this.dispatchEvent(e,"$md.dragstart",this.state.dragPointer)):o&&this.cancel())},dispatchDragMove:o.throttle(function(e){this.state.isRunning&&(l(e,this.state.dragPointer),this.dispatchEvent(e,"$md.drag",this.state.dragPointer))}),onEnd:function(e,t){this.state.dragPointer&&(l(e,this.state.dragPointer),this.dispatchEvent(e,"$md.dragend",this.state.dragPointer))}}).handler("swipe",{options:{minVelocity:.65,minDistance:10},onEnd:function(e,t){var n;Math.abs(t.velocityX)>this.state.options.minVelocity&&Math.abs(t.distanceX)>this.state.options.minDistance?(n="left"==t.directionX?"$md.swipeleft":"$md.swiperight",this.dispatchEvent(e,n)):Math.abs(t.velocityY)>this.state.options.minVelocity&&Math.abs(t.distanceY)>this.state.options.minDistance&&(n="up"==t.directionY?"$md.swipeup":"$md.swipedown",this.dispatchEvent(e,n))}})}function r(e){this.name=e,this.state={}}function i(){function n(e,n,o){o=o||u;var r=new t.element.Event(n);r.$material=!0,r.pointer=o,r.srcEvent=e,t.extend(r,{clientX:o.x,clientY:o.y,screenX:o.x,screenY:o.y,pageX:o.x,pageY:o.y,ctrlKey:e.ctrlKey,altKey:e.altKey,shiftKey:e.shiftKey,metaKey:e.metaKey}),t.element(o.target).trigger(r)}function o(t,n,o){o=o||u;var r;"click"===n||"mouseup"==n||"mousedown"==n?(r=document.createEvent("MouseEvents"),r.initMouseEvent(n,!0,!0,e,t.detail,o.x,o.y,o.x,o.y,t.ctrlKey,t.altKey,t.shiftKey,t.metaKey,t.button,t.relatedTarget||null)):(r=document.createEvent("CustomEvent"),r.initCustomEvent(n,!0,!0,{})),r.$material=!0,r.pointer=o,r.srcEvent=t,o.target.dispatchEvent(r)}var i="undefined"!=typeof e.jQuery&&t.element===e.jQuery;return r.prototype={options:{},dispatchEvent:i?n:o,onStart:t.noop,onMove:t.noop,onEnd:t.noop,onCancel:t.noop,start:function(e,n){if(!this.state.isRunning){var o=this.getNearestParent(e.target),r=o&&o.$mdGesture[this.name]||{};this.state={isRunning:!0,options:t.extend({},this.options,r),registeredParent:o},this.onStart(e,n)}},move:function(e,t){this.state.isRunning&&this.onMove(e,t)},end:function(e,t){this.state.isRunning&&(this.onEnd(e,t),this.state.isRunning=!1)},cancel:function(e,t){this.onCancel(e,t),this.state={}},getNearestParent:function(e){for(var t=e;t;){if((t.$mdGesture||{})[this.name])return t;t=t.parentNode}return null},registerElement:function(e,t){function n(){delete e[0].$mdGesture[o.name],e.off("$destroy",n)}var o=this;return e[0].$mdGesture=e[0].$mdGesture||{},e[0].$mdGesture[this.name]=t||{},e.on("$destroy",n),n}},r}function a(e,n){function o(e){var t=!e.clientX&&!e.clientY;t||e.$material||e.isIonicTap||s(e)||(e.preventDefault(),e.stopPropagation())}function r(e){var t=0===e.clientX&&0===e.clientY;t||e.$material||e.isIonicTap||s(e)?(g=null,"label"==e.target.tagName.toLowerCase()&&(g={x:e.x,y:e.y})):(e.preventDefault(),e.stopPropagation(),g=null)}function i(e,t){var o;for(var r in h)o=h[r],o instanceof n&&("start"===e&&o.cancel(),o[e](t,u))}function a(e){if(!u){var t=+Date.now();p&&!c(e,p)&&t-p.endTime<1500||(u=d(e),i("start",e))}}function m(e){u&&c(e,u)&&(l(e,u),i("move",e))}function f(e){u&&c(e,u)&&(l(e,u),u.endTime=+Date.now(),i("end",e),p=u,u=null)}document.contains||(document.contains=function(e){return document.body.contains(e)}),!b&&e.isHijackingClicks&&(document.addEventListener("click",r,!0),document.addEventListener("mouseup",o,!0),document.addEventListener("mousedown",o,!0),document.addEventListener("focus",o,!0),b=!0);var v="mousedown touchstart pointerdown",E="mousemove touchmove pointermove",$="mouseup mouseleave touchend touchcancel pointerup pointercancel";t.element(document).on(v,a).on(E,m).on($,f).on("$$mdGestureReset",function(){p=u=null})}function d(e){var t=m(e),n={startTime:+Date.now(),target:e.target,type:e.type.charAt(0)};return n.startX=n.x=t.pageX,n.startY=n.y=t.pageY,n}function c(e,t){return e&&t&&e.type.charAt(0)===t.type}function s(e){return g&&g.x==e.x&&g.y==e.y}function l(e,t){var n=m(e),o=t.x=n.pageX,r=t.y=n.pageY;t.distanceX=o-t.startX,t.distanceY=r-t.startY,t.distance=Math.sqrt(t.distanceX*t.distanceX+t.distanceY*t.distanceY),t.directionX=t.distanceX>0?"right":t.distanceX<0?"left":"",t.directionY=t.distanceY>0?"down":t.distanceY<0?"up":"",t.duration=+Date.now()-t.startTime,t.velocityX=t.distanceX/t.duration,t.velocityY=t.distanceY/t.duration}function m(e){return e=e.originalEvent||e,e.touches&&e.touches[0]||e.changedTouches&&e.changedTouches[0]||e}var u,p,h={},f=!1,g=null,b=!1;t.module("material.core.gestures",[]).provider("$mdGesture",n).factory("$$MdGestureHandler",i).run(a),n.prototype={skipClickHijack:function(){return f=!0},$get:["$$MdGestureHandler","$$rAF","$timeout",function(e,t,n){return new o(e,t,n)}]},o.$inject=["$$MdGestureHandler","$$rAF","$timeout"],a.$inject=["$mdGesture","$$MdGestureHandler"]}(),function(){function e(){function e(e){function n(e){return c.optionsFactory=e.options,c.methods=(e.methods||[]).concat(a),s}function o(e,t){return d[e]=t,s}function r(t,n){if(n=n||{},n.methods=n.methods||[],n.options=n.options||function(){return{}},/^cancel|hide|show$/.test(t))throw new Error("Preset '"+t+"' in "+e+" is reserved!");if(n.methods.indexOf("_options")>-1)throw new Error("Method '_options' in "+e+" is reserved!");return c.presets[t]={methods:n.methods.concat(a),optionsFactory:n.options,argOption:n.argOption},s}function i(n,o){function r(e){return e=e||{},e._options&&(e=e._options),m.show(t.extend({},l,e))}function i(e){return m.destroy(e)}function a(t,n){var r={};return r[e]=u,o.invoke(t||function(){return n},{},r)}var s,l,m=n(),u={hide:m.hide,cancel:m.cancel,show:r,destroy:i};return s=c.methods||[],l=a(c.optionsFactory,{}),t.forEach(d,function(e,t){u[t]=e}),t.forEach(c.presets,function(e,n){function o(e){this._options=t.extend({},r,e)}var r=a(e.optionsFactory,{}),i=(e.methods||[]).concat(s);if(t.extend(r,{$type:n}),t.forEach(i,function(e){o.prototype[e]=function(t){return this._options[e]=t,this}}),e.argOption){var d="show"+n.charAt(0).toUpperCase()+n.slice(1);u[d]=function(e){var t=u[n](e);return u.show(t)}}u[n]=function(n){return arguments.length&&e.argOption&&!t.isObject(n)&&!t.isArray(n)?(new o)[e.argOption](n):new o(n)}}),u}var a=["onHide","onShow","onRemove"],d={},c={presets:{}},s={setDefaults:n,addPreset:r,addMethod:o,$get:i};return s.addPreset("build",{methods:["controller","controllerAs","resolve","template","templateUrl","themable","transformTemplate","parent"]}),i.$inject=["$$interimElement","$injector"],s}function o(e,o,r,i,a,d,c,s,l,m,u){return function(){function p(e){e=e||{};var t=new b(e||{}),n=!e.skipHide&&$.length?v.hide():o.when(!0);return n["finally"](function(){$.push(t),t.show()["catch"](function(e){return e})}),t.deferred.promise}function h(e,t){function r(n){return n.remove(e,!1,t||{})["catch"](function(e){return e}),n.deferred.promise}if(!$.length)return o.when(e);if(t=t||{},t.closeAll){var i=o.all($.reverse().map(r));return $=[],i}if(t.closeTo!==n)return o.all($.splice(t.closeTo).map(r));var a=$.pop();return r(a)}function f(e,t){var n=$.pop();return n?(n.remove(e,!0,t||{})["catch"](function(e){return e}),n.deferred.promise):o.when(e)}function g(e){var n=e?null:$.shift(),r=t.element(e).length?t.element(e)[0].parentNode:null;if(r){var i=$.filter(function(e){var t=e.options.element[0];return t===r});i.length>0&&(n=i[0],$.splice($.indexOf(n),1))}return n?n.remove(E,!1,{$destroy:!0}):o.when(E)}function b(u){function p(){return o(function(e,t){function n(e){C.deferred.reject(e),t(e)}g(u).then(function(t){A=b(t,u),k=M(A,u,t.controller).then(e,n)},n)})}function h(e,n,r){function i(e){C.deferred.resolve(e)}function a(e){C.deferred.reject(e)}return A?(u=t.extend(u||{},r||{}),u.cancelAutoHide&&u.cancelAutoHide(),u.element.triggerHandler("$mdInterimElementRemove"),u.$destroy===!0?y(u.element,u).then(function(){n&&a(e)||i(e)}):(o.when(k)["finally"](function(){y(u.element,u).then(function(){n&&a(e)||i(e)},a)}),C.deferred.promise)):o.when(!1)}function f(e){return e=e||{},e.template&&(e.template=s.processTemplate(e.template)),t.extend({preserveScope:!1,cancelAutoHide:t.noop,scope:e.scope||i.$new(e.isolateScope),onShow:function(e,t,n){return c.enter(t,n.parent)},onRemove:function(e,t){return t&&c.leave(t)||o.when()}},e)}function g(e){var t=e.skipCompile?null:l.compile(e);return t||o(function(t){t({locals:{},link:function(){return e.element}})})}function b(e,n){t.extend(e.locals,n);var o=e.link(n.scope);return n.element=o,n.parent=E(o,n),n.themable&&m(o),o}function E(n,o){var r=o.parent;if(r=t.isFunction(r)?r(o.scope,n,o):t.isString(r)?t.element(e[0].querySelector(r)):t.element(r),!(r||{}).length){var i;return d[0]&&d[0].querySelector&&(i=d[0].querySelector(":not(svg) > body")),i||(i=d[0]),"#comment"==i.nodeName&&(i=e[0].body),t.element(i)}return r}function $(){var e,o=t.noop;u.hideDelay&&(e=a(v.hide,u.hideDelay),o=function(){a.cancel(e)}),u.cancelAutoHide=function(){o(),u.cancelAutoHide=n}}function M(e,n,r){var i=n.onShowing||t.noop,a=n.onComplete||t.noop;return i(n.scope,e,n,r),o(function(t,i){try{o.when(n.onShow(n.scope,e,n,r)).then(function(){a(n.scope,e,n),$(),t(e)},i)}catch(d){i(d.message)}})}function y(e,n){var o=n.onRemoving||t.noop;return r(function(t,i){try{var a=r.when(n.onRemove(n.scope,e,n)||!0);o(e,a),1==n.$destroy?t(e):a.then(function(){!n.preserveScope&&n.scope&&n.scope.$destroy(),t(e)},i)}catch(d){i(d.message)}})}var C,A,k=o.when(!0);return u=f(u),C={options:u,deferred:o.defer(),show:p,remove:h}}var v,E=!1,$=[];return v={show:p,hide:h,cancel:f,destroy:g,$injector_:u}}}return e.$get=o,o.$inject=["$document","$q","$$q","$rootScope","$timeout","$rootElement","$animate","$mdUtil","$mdCompiler","$mdTheming","$injector"],e}t.module("material.core").provider("$$interimElement",e)}(),function(){!function(){function e(e){function a(e){return e.replace(c,"").replace(s,function(e,t,n,o){return o?n.toUpperCase():n})}var c=/^((?:x|data)[\:\-_])/i,s=/([\:\-\_]+(.))/g,l=["","xs","gt-xs","sm","gt-sm","md","gt-md","lg","gt-lg","xl","print"],m=["layout","flex","flex-order","flex-offset","layout-align"],u=["show","hide","layout-padding","layout-margin"];t.forEach(l,function(n){t.forEach(m,function(t){var o=n?t+"-"+n:t;e.directive(a(o),r(o))}),t.forEach(u,function(t){var o=n?t+"-"+n:t;e.directive(a(o),i(o))})}),e.directive("mdLayoutCss",n).directive("ngCloak",o("ng-cloak")).directive("layoutWrap",i("layout-wrap")).directive("layoutNowrap",i("layout-nowrap")).directive("layoutNoWrap",i("layout-no-wrap")).directive("layoutFill",i("layout-fill")).directive("layoutLtMd",d("layout-lt-md",!0)).directive("layoutLtLg",d("layout-lt-lg",!0)).directive("flexLtMd",d("flex-lt-md",!0)).directive("flexLtLg",d("flex-lt-lg",!0)).directive("layoutAlignLtMd",d("layout-align-lt-md")).directive("layoutAlignLtLg",d("layout-align-lt-lg")).directive("flexOrderLtMd",d("flex-order-lt-md")).directive("flexOrderLtLg",d("flex-order-lt-lg")).directive("offsetLtMd",d("flex-offset-lt-md")).directive("offsetLtLg",d("flex-offset-lt-lg")).directive("hideLtMd",d("hide-lt-md")).directive("hideLtLg",d("hide-lt-lg")).directive("showLtMd",d("show-lt-md")).directive("showLtLg",d("show-lt-lg"))}function n(){return{restrict:"A",priority:"900",compile:function(e,n){return A.enabled=!1,t.noop}}}function o(e){return["$timeout",function(n){return{restrict:"A",priority:-10,compile:function(o){return A.enabled?(o.addClass(e),function(t,o){n(function(){o.removeClass(e)},10,!1)}):t.noop}}}]}function r(e){function n(t,n,o){var r=a(n,e,o),i=o.$observe(o.$normalize(e),r);r(u(e,o,"")),t.$on("$destroy",function(){i()})}return["$mdUtil","$interpolate","$log",function(o,r,i){return f=o,g=r,b=i,{restrict:"A",compile:function(o,r){var i;return A.enabled&&(c(e,r,o,b),s(e,u(e,r,""),l(o,e,r)),i=n),i||t.noop}}}]}function i(e){function n(t,n){n.addClass(e)}return["$mdUtil","$interpolate","$log",function(o,r,i){return f=o,g=r,b=i,{restrict:"A",compile:function(o,r){var i;return A.enabled&&(s(e,u(e,r,""),l(o,e,r)),n(null,o),i=n),i||t.noop}}}]}function a(e,n){var o;return function(r){var i=s(n,r||"");t.isDefined(i)&&(o&&e.removeClass(o),o=i?n+"-"+i.replace(E,"-"):n,e.addClass(o))}}function d(e){var n=e.split("-");return["$log",function(o){return o.warn(e+"has been deprecated. Please use a `"+n[0]+"-gt-` variant."),t.noop}]}function c(e,t,n,o){var r,i,a,d=n[0].nodeName.toLowerCase();switch(e.replace(v,"")){case"flex":"md-button"!=d&&"fieldset"!=d||(i="<"+d+" "+e+">",a="https://github.com/philipwalton/flexbugs#9-some-html-elements-cant-be-flex-containers",r="Markup '{0}' may not work as expected in IE Browsers. Consult '{1}' for details.",o.warn(f.supplant(r,[i,a])))}}function s(e,n,o){var r=n;if(!m(n)){switch(e.replace(v,"")){case"layout":p(n,M)||(n=M[0]);break;case"flex":p(n,$)||isNaN(n)&&(n="");break;case"flex-offset":case"flex-order":n&&!isNaN(+n)||(n="0");break;case"layout-align":var i=h(n);n=f.supplant("{main}-{cross}",i);break;case"layout-padding":case"layout-margin":case"layout-fill":case"layout-wrap":case"layout-nowrap":case"layout-nowrap":n=""}n!=r&&(o||t.noop)(n)}return n}function l(e,t,n){return function(e){m(e)||(n[n.$normalize(t)]=e)}}function m(e){return(e||"").indexOf(g.startSymbol())>-1}function u(e,t,n){var o=t.$normalize(e);return t[o]?t[o].replace(E,"-"):n||null}function p(e,t,n){e=n&&e?e.replace(E,n):e;var o=!1;return e&&t.forEach(function(t){t=n?t.replace(E,n):t,o=o||t===e}),o}function h(e){var t,n={main:"start",cross:"stretch"};return e=e||"",0!=e.indexOf("-")&&0!=e.indexOf(" ")||(e="none"+e),t=e.toLowerCase().trim().replace(E,"-").split("-"),t.length&&"space"===t[0]&&(t=[t[0]+"-"+t[1],t[2]]),t.length>0&&(n.main=t[0]||n.main),t.length>1&&(n.cross=t[1]||n.cross),y.indexOf(n.main)<0&&(n.main="start"),C.indexOf(n.cross)<0&&(n.cross="stretch"),n}var f,g,b,v=/(-gt)?-(sm|md|lg|print)/g,E=/\s+/g,$=["grow","initial","auto","none","noshrink","nogrow"],M=["row","column"],y=["","start","center","end","stretch","space-around","space-between"],C=["","start","center","end","stretch"],A={enabled:!0,breakpoints:[]};e(t.module("material.core.layout",["ng"]))}()}(),function(){function e(e,n){function o(e){return e&&""!==e}var r,i=[],a={};return r={notFoundError:function(t){e.error("No instance found for handle",t); +},getInstances:function(){return i},get:function(e){if(!o(e))return null;var t,n,r;for(t=0,n=i.length;n>t;t++)if(r=i[t],r.$$mdHandle===e)return r;return null},register:function(e,n){function o(){var t=i.indexOf(e);-1!==t&&i.splice(t,1)}function r(){var t=a[n];t&&(t.resolve(e),delete a[n])}return n?(e.$$mdHandle=n,i.push(e),r(),o):t.noop},when:function(e){if(o(e)){var t=n.defer(),i=r.get(e);return i?t.resolve(i):a[e]=t,t.promise}return n.reject("Invalid `md-component-id` value.")}}}t.module("material.core").factory("$mdComponentRegistry",e),e.$inject=["$log","$q"]}(),function(){!function(){function e(e){function n(e){return e.hasClass("md-icon-button")?{isMenuItem:e.hasClass("md-menu-item"),fitRipple:!0,center:!0}:{isMenuItem:e.hasClass("md-menu-item"),dimBackground:!0}}return{attach:function(o,r,i){return i=t.extend(n(r),i),e.attach(o,r,i)}}}t.module("material.core").factory("$mdButtonInkRipple",e),e.$inject=["$mdInkRipple"]}()}(),function(){!function(){function e(e){function n(n,o,r){return e.attach(n,o,t.extend({center:!0,dimBackground:!1,fitRipple:!0},r))}return{attach:n}}t.module("material.core").factory("$mdCheckboxInkRipple",e),e.$inject=["$mdInkRipple"]}()}(),function(){!function(){function e(e){function n(n,o,r){return e.attach(n,o,t.extend({center:!1,dimBackground:!0,outline:!1,rippleSize:"full"},r))}return{attach:n}}t.module("material.core").factory("$mdListInkRipple",e),e.$inject=["$mdInkRipple"]}()}(),function(){function e(e,n){return{controller:t.noop,link:function(t,o,r){r.hasOwnProperty("mdInkRippleCheckbox")?n.attach(t,o):e.attach(t,o)}}}function n(e){function n(n,r,i){return r.controller("mdNoInk")?t.noop:e.instantiate(o,{$scope:n,$element:r,rippleOptions:i})}return{attach:n}}function o(e,n,o,r,i,a){this.$window=r,this.$timeout=i,this.$mdUtil=a,this.$scope=e,this.$element=n,this.options=o,this.mousedown=!1,this.ripples=[],this.timeout=null,this.lastRipple=null,a.valueOnUse(this,"container",this.createContainer),this.$element.addClass("md-ink-ripple"),(n.controller("mdInkRipple")||{}).createRipple=t.bind(this,this.createRipple),(n.controller("mdInkRipple")||{}).setColor=t.bind(this,this.color),this.bindEvents()}function r(e,n){(e.mousedown||e.lastRipple)&&(e.mousedown=!1,e.$mdUtil.nextTick(t.bind(e,n),!1))}function i(){return{controller:t.noop}}t.module("material.core").factory("$mdInkRipple",n).directive("mdInkRipple",e).directive("mdNoInk",i).directive("mdNoBar",i).directive("mdNoStretch",i);var a=450;e.$inject=["$mdButtonInkRipple","$mdCheckboxInkRipple"],n.$inject=["$injector"],o.$inject=["$scope","$element","rippleOptions","$window","$timeout","$mdUtil"],o.prototype.color=function(e){function n(){var e=o.options&&o.options.colorElement?o.options.colorElement:[],t=e.length?e[0]:o.$element[0];return t?o.$window.getComputedStyle(t).color:"rgb(0,0,0)"}var o=this;return t.isDefined(e)&&(o._color=o._parseColor(e)),o._color||o._parseColor(o.inkRipple())||o._parseColor(n())},o.prototype.calculateColor=function(){return this.color()},o.prototype._parseColor=function(e,t){function n(e){var t="#"===e[0]?e.substr(1):e,n=t.length/3,o=t.substr(0,n),r=t.substr(n,n),i=t.substr(2*n);return 1===n&&(o+=o,r+=r,i+=i),"rgba("+parseInt(o,16)+","+parseInt(r,16)+","+parseInt(i,16)+",0.1)"}function o(e){return e.replace(")",", 0.1)").replace("(","a(")}return t=t||1,e?0===e.indexOf("rgba")?e.replace(/\d?\.?\d*\s*\)\s*$/,(.1*t).toString()+")"):0===e.indexOf("rgb")?o(e):0===e.indexOf("#")?n(e):void 0:void 0},o.prototype.bindEvents=function(){this.$element.on("mousedown",t.bind(this,this.handleMousedown)),this.$element.on("mouseup touchend",t.bind(this,this.handleMouseup)),this.$element.on("mouseleave",t.bind(this,this.handleMouseup)),this.$element.on("touchmove",t.bind(this,this.handleTouchmove))},o.prototype.handleMousedown=function(e){if(!this.mousedown)if(e.hasOwnProperty("originalEvent")&&(e=e.originalEvent),this.mousedown=!0,this.options.center)this.createRipple(this.container.prop("clientWidth")/2,this.container.prop("clientWidth")/2);else if(e.srcElement!==this.$element[0]){var t=this.$element[0].getBoundingClientRect(),n=e.clientX-t.left,o=e.clientY-t.top;this.createRipple(n,o)}else this.createRipple(e.offsetX,e.offsetY)},o.prototype.handleMouseup=function(){r(this,this.clearRipples)},o.prototype.handleTouchmove=function(){r(this,this.deleteRipples)},o.prototype.deleteRipples=function(){for(var e=0;e');return this.$element.append(e),e},o.prototype.clearTimeout=function(){this.timeout&&(this.$timeout.cancel(this.timeout),this.timeout=null)},o.prototype.isRippleAllowed=function(){var e=this.$element[0];do{if(!e.tagName||"BODY"===e.tagName)break;if(e&&t.isFunction(e.hasAttribute)){if(e.hasAttribute("disabled"))return!1;if("false"===this.inkRipple()||"0"===this.inkRipple())return!1}}while(e=e.parentNode);return!0},o.prototype.inkRipple=function(){return this.$element.attr("md-ink-ripple")},o.prototype.createRipple=function(e,n){function o(e){return e?e.replace("rgba","rgb").replace(/,[^\),]+\)/,")"):"rgb(0,0,0)"}function r(e,t,n){return e?Math.max(t,n):Math.sqrt(Math.pow(t,2)+Math.pow(n,2))}if(this.isRippleAllowed()){var i=this,d=t.element('
'),c=this.$element.prop("clientWidth"),s=this.$element.prop("clientHeight"),l=2*Math.max(Math.abs(c-e),e),m=2*Math.max(Math.abs(s-n),n),u=r(this.options.fitRipple,l,m),p=this.calculateColor();d.css({left:e+"px",top:n+"px",background:"black",width:u+"px",height:u+"px",backgroundColor:o(p),borderColor:o(p)}),this.lastRipple=d,this.clearTimeout(),this.timeout=this.$timeout(function(){i.clearTimeout(),i.mousedown||i.fadeInComplete(d)},.35*a,!1),this.options.dimBackground&&this.container.css({backgroundColor:p}),this.container.append(d),this.ripples.push(d),d.addClass("md-ripple-placed"),this.$mdUtil.nextTick(function(){d.addClass("md-ripple-scaled md-ripple-active"),i.$timeout(function(){i.clearRipples()},a,!1)},!1)}},o.prototype.fadeInComplete=function(e){this.lastRipple===e?this.timeout||this.mousedown||this.removeRipple(e):this.removeRipple(e)},o.prototype.removeRipple=function(e){var t=this,n=this.ripples.indexOf(e);0>n||(this.ripples.splice(this.ripples.indexOf(e),1),e.removeClass("md-ripple-active"),0===this.ripples.length&&this.container.css({backgroundColor:""}),this.$timeout(function(){t.fadeOutComplete(e)},a,!1))},o.prototype.fadeOutComplete=function(e){e.remove(),this.lastRipple=null}}(),function(){!function(){function e(e){function n(n,o,r){return e.attach(n,o,t.extend({center:!1,dimBackground:!0,outline:!1,rippleSize:"full"},r))}return{attach:n}}t.module("material.core").factory("$mdTabInkRipple",e),e.$inject=["$mdInkRipple"]}()}(),function(){t.module("material.core.theming.palette",[]).constant("$mdColorPalette",{red:{50:"#ffebee",100:"#ffcdd2",200:"#ef9a9a",300:"#e57373",400:"#ef5350",500:"#f44336",600:"#e53935",700:"#d32f2f",800:"#c62828",900:"#b71c1c",A100:"#ff8a80",A200:"#ff5252",A400:"#ff1744",A700:"#d50000",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 300 A100",contrastStrongLightColors:"400 500 600 700 A200 A400 A700"},pink:{50:"#fce4ec",100:"#f8bbd0",200:"#f48fb1",300:"#f06292",400:"#ec407a",500:"#e91e63",600:"#d81b60",700:"#c2185b",800:"#ad1457",900:"#880e4f",A100:"#ff80ab",A200:"#ff4081",A400:"#f50057",A700:"#c51162",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 A100",contrastStrongLightColors:"500 600 A200 A400 A700"},purple:{50:"#f3e5f5",100:"#e1bee7",200:"#ce93d8",300:"#ba68c8",400:"#ab47bc",500:"#9c27b0",600:"#8e24aa",700:"#7b1fa2",800:"#6a1b9a",900:"#4a148c",A100:"#ea80fc",A200:"#e040fb",A400:"#d500f9",A700:"#aa00ff",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 A100",contrastStrongLightColors:"300 400 A200 A400 A700"},"deep-purple":{50:"#ede7f6",100:"#d1c4e9",200:"#b39ddb",300:"#9575cd",400:"#7e57c2",500:"#673ab7",600:"#5e35b1",700:"#512da8",800:"#4527a0",900:"#311b92",A100:"#b388ff",A200:"#7c4dff",A400:"#651fff",A700:"#6200ea",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 A100",contrastStrongLightColors:"300 400 A200"},indigo:{50:"#e8eaf6",100:"#c5cae9",200:"#9fa8da",300:"#7986cb",400:"#5c6bc0",500:"#3f51b5",600:"#3949ab",700:"#303f9f",800:"#283593",900:"#1a237e",A100:"#8c9eff",A200:"#536dfe",A400:"#3d5afe",A700:"#304ffe",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 A100",contrastStrongLightColors:"300 400 A200 A400"},blue:{50:"#e3f2fd",100:"#bbdefb",200:"#90caf9",300:"#64b5f6",400:"#42a5f5",500:"#2196f3",600:"#1e88e5",700:"#1976d2",800:"#1565c0",900:"#0d47a1",A100:"#82b1ff",A200:"#448aff",A400:"#2979ff",A700:"#2962ff",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 300 400 A100",contrastStrongLightColors:"500 600 700 A200 A400 A700"},"light-blue":{50:"#e1f5fe",100:"#b3e5fc",200:"#81d4fa",300:"#4fc3f7",400:"#29b6f6",500:"#03a9f4",600:"#039be5",700:"#0288d1",800:"#0277bd",900:"#01579b",A100:"#80d8ff",A200:"#40c4ff",A400:"#00b0ff",A700:"#0091ea",contrastDefaultColor:"dark",contrastLightColors:"600 700 800 900 A700",contrastStrongLightColors:"600 700 800 A700"},cyan:{50:"#e0f7fa",100:"#b2ebf2",200:"#80deea",300:"#4dd0e1",400:"#26c6da",500:"#00bcd4",600:"#00acc1",700:"#0097a7",800:"#00838f",900:"#006064",A100:"#84ffff",A200:"#18ffff",A400:"#00e5ff",A700:"#00b8d4",contrastDefaultColor:"dark",contrastLightColors:"700 800 900",contrastStrongLightColors:"700 800 900"},teal:{50:"#e0f2f1",100:"#b2dfdb",200:"#80cbc4",300:"#4db6ac",400:"#26a69a",500:"#009688",600:"#00897b",700:"#00796b",800:"#00695c",900:"#004d40",A100:"#a7ffeb",A200:"#64ffda",A400:"#1de9b6",A700:"#00bfa5",contrastDefaultColor:"dark",contrastLightColors:"500 600 700 800 900",contrastStrongLightColors:"500 600 700"},green:{50:"#e8f5e9",100:"#c8e6c9",200:"#a5d6a7",300:"#81c784",400:"#66bb6a",500:"#4caf50",600:"#43a047",700:"#388e3c",800:"#2e7d32",900:"#1b5e20",A100:"#b9f6ca",A200:"#69f0ae",A400:"#00e676",A700:"#00c853",contrastDefaultColor:"dark",contrastLightColors:"500 600 700 800 900",contrastStrongLightColors:"500 600 700"},"light-green":{50:"#f1f8e9",100:"#dcedc8",200:"#c5e1a5",300:"#aed581",400:"#9ccc65",500:"#8bc34a",600:"#7cb342",700:"#689f38",800:"#558b2f",900:"#33691e",A100:"#ccff90",A200:"#b2ff59",A400:"#76ff03",A700:"#64dd17",contrastDefaultColor:"dark",contrastLightColors:"700 800 900",contrastStrongLightColors:"700 800 900"},lime:{50:"#f9fbe7",100:"#f0f4c3",200:"#e6ee9c",300:"#dce775",400:"#d4e157",500:"#cddc39",600:"#c0ca33",700:"#afb42b",800:"#9e9d24",900:"#827717",A100:"#f4ff81",A200:"#eeff41",A400:"#c6ff00",A700:"#aeea00",contrastDefaultColor:"dark",contrastLightColors:"900",contrastStrongLightColors:"900"},yellow:{50:"#fffde7",100:"#fff9c4",200:"#fff59d",300:"#fff176",400:"#ffee58",500:"#ffeb3b",600:"#fdd835",700:"#fbc02d",800:"#f9a825",900:"#f57f17",A100:"#ffff8d",A200:"#ffff00",A400:"#ffea00",A700:"#ffd600",contrastDefaultColor:"dark"},amber:{50:"#fff8e1",100:"#ffecb3",200:"#ffe082",300:"#ffd54f",400:"#ffca28",500:"#ffc107",600:"#ffb300",700:"#ffa000",800:"#ff8f00",900:"#ff6f00",A100:"#ffe57f",A200:"#ffd740",A400:"#ffc400",A700:"#ffab00",contrastDefaultColor:"dark"},orange:{50:"#fff3e0",100:"#ffe0b2",200:"#ffcc80",300:"#ffb74d",400:"#ffa726",500:"#ff9800",600:"#fb8c00",700:"#f57c00",800:"#ef6c00",900:"#e65100",A100:"#ffd180",A200:"#ffab40",A400:"#ff9100",A700:"#ff6d00",contrastDefaultColor:"dark",contrastLightColors:"800 900",contrastStrongLightColors:"800 900"},"deep-orange":{50:"#fbe9e7",100:"#ffccbc",200:"#ffab91",300:"#ff8a65",400:"#ff7043",500:"#ff5722",600:"#f4511e",700:"#e64a19",800:"#d84315",900:"#bf360c",A100:"#ff9e80",A200:"#ff6e40",A400:"#ff3d00",A700:"#dd2c00",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 300 400 A100 A200",contrastStrongLightColors:"500 600 700 800 900 A400 A700"},brown:{50:"#efebe9",100:"#d7ccc8",200:"#bcaaa4",300:"#a1887f",400:"#8d6e63",500:"#795548",600:"#6d4c41",700:"#5d4037",800:"#4e342e",900:"#3e2723",A100:"#d7ccc8",A200:"#bcaaa4",A400:"#8d6e63",A700:"#5d4037",contrastDefaultColor:"light",contrastDarkColors:"50 100 200",contrastStrongLightColors:"300 400"},grey:{50:"#fafafa",100:"#f5f5f5",200:"#eeeeee",300:"#e0e0e0",400:"#bdbdbd",500:"#9e9e9e",600:"#757575",700:"#616161",800:"#424242",900:"#212121",A100:"#ffffff",A200:"#000000",A400:"#303030",A700:"#616161",contrastDefaultColor:"dark",contrastLightColors:"600 700 800 900"},"blue-grey":{50:"#eceff1",100:"#cfd8dc",200:"#b0bec5",300:"#90a4ae",400:"#78909c",500:"#607d8b",600:"#546e7a",700:"#455a64",800:"#37474f",900:"#263238",A100:"#cfd8dc",A200:"#b0bec5",A400:"#78909c",A700:"#455a64",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 300 700",contrastStrongLightColors:"400 500 700"}})}(),function(){function e(e){function o(e,t){return t=t||{},m[e]=a(e,t),v}function r(e,n){return a(e,t.extend({},m[e]||{},n))}function a(e,t){var n=k.filter(function(e){return!t[e]});if(n.length)throw new Error("Missing colors %1 in palette %2!".replace("%1",n.join(", ")).replace("%2",e));return t}function c(e,n){if(u[e])return u[e];n=n||"default";var o="string"==typeof n?u[n]:n,r=new s(e);return o&&t.forEach(o.colors,function(e,n){r.colors[n]={name:e.name,hues:t.extend({},e.hues)}}),u[e]=r,r}function s(e){function n(e){if(e=0===arguments.length?!0:!!e,e!==o.isDark){o.isDark=e,o.foregroundPalette=o.isDark?f:h,o.foregroundShadow=o.isDark?g:b;var n=o.isDark?A:C,r=o.isDark?C:A;return t.forEach(n,function(e,t){var n=o.colors[t],i=r[t];if(n)for(var a in n.hues)n.hues[a]===i[a]&&(n.hues[a]=e[a])}),o}}var o=this;o.name=e,o.colors={},o.dark=n,n(!1),M.forEach(function(e){var n=(o.isDark?A:C)[e];o[e+"Palette"]=function(r,i){var a=o.colors[e]={name:r,hues:t.extend({},n,i)};return Object.keys(a.hues).forEach(function(e){if(!n[e])throw new Error("Invalid hue name '%1' in theme %2's %3 color %4. Available hue names: %4".replace("%1",e).replace("%2",o.name).replace("%3",r).replace("%4",Object.keys(n).join(", ")))}),Object.keys(a.hues).map(function(e){return a.hues[e]}).forEach(function(t){if(-1==k.indexOf(t))throw new Error("Invalid hue value '%1' in theme %2's %3 color %4. Available hue values: %5".replace("%1",t).replace("%2",o.name).replace("%3",e).replace("%4",r).replace("%5",k.join(", ")))}),o},o[e+"Color"]=function(){var t=Array.prototype.slice.call(arguments);return console.warn("$mdThemingProviderTheme."+e+"Color() has been deprecated. Use $mdThemingProviderTheme."+e+"Palette() instead."),o[e+"Palette"].apply(o,t)}})}function p(e,o){function r(e){return e===n||""===e?!0:a.THEMES[e]!==n}function i(n,i){function a(){return c=i.controller("mdTheme")||n.data("$mdThemeController"),c&&c.$mdTheme||("default"==E?"":E)}function d(e){if(e){r(e)||o.warn("Attempted to use unregistered theme '"+e+"'. Register it with $mdThemingProvider.theme().");var t=n.data("$mdThemeName");t&&n.removeClass("md-"+t+"-theme"),n.addClass("md-"+e+"-theme"),n.data("$mdThemeName",e),c&&n.data("$mdThemeController",c)}}var c=i.controller("mdTheme"),s=n.attr("md-theme-watch"),l=($||t.isDefined(s))&&"false"!=s;d(a()),n.on("$destroy",l?e.$watch(a,d):t.noop)}var a=function(t,o){o===n&&(o=t,t=n),t===n&&(t=e),a.inherit(o,o)};return a.THEMES=t.extend({},u),a.inherit=i,a.registered=r,a.defaultTheme=function(){return E},a.generateTheme=function(e){d(e,w)},a}m={},u={};var v,E="default",$=!1;return t.extend(m,e),p.$inject=["$rootScope","$log"],v={definePalette:o,extendPalette:r,theme:c,setNonce:function(e){w=e},setDefaultTheme:function(e){E=e},alwaysWatchTheme:function(e){$=e},generateThemesOnDemand:function(e){T=e},$get:p,_LIGHT_DEFAULT_HUES:C,_DARK_DEFAULT_HUES:A,_PALETTES:m,_THEMES:u,_parseRules:i,_rgba:l}}function o(e,t,n){return{priority:100,link:{pre:function(o,r,i){var a={$setTheme:function(t){e.registered(t)||n.warn("attempted to use unregistered theme '"+t+"'"),a.$mdTheme=t}};r.data("$mdThemeController",a),a.$setTheme(t(i.mdTheme)(o)),i.$observe("mdTheme",a.$setTheme)}}}}function r(e){return e}function i(e,n,o){c(e,n),o=o.replace(/THEME_NAME/g,e.name);var r=[],i=e.colors[n],a=new RegExp(".md-"+e.name+"-theme","g"),d=new RegExp("('|\")?{{\\s*("+n+")-(color|contrast)-?(\\d\\.?\\d*)?\\s*}}(\"|')?","g"),s=/'?"?\{\{\s*([a-zA-Z]+)-(A?\d+|hue\-[0-3]|shadow)-?(\d\.?\d*)?(contrast)?\s*\}\}'?"?/g,u=m[i.name];return o=o.replace(s,function(t,n,o,r,i){return"foreground"===n?"shadow"==o?e.foregroundShadow:e.foregroundPalette[o]||e.foregroundPalette[1]:(0===o.indexOf("hue")&&(o=e.colors[n].hues[o]),l((m[e.colors[n].name][o]||"")[i?"contrast":"value"],r))}),t.forEach(i.hues,function(t,n){var i=o.replace(d,function(e,n,o,r,i){return l(u[t]["color"===r?"value":"contrast"],i)});if("default"!==n&&(i=i.replace(a,".md-"+e.name+"-theme.md-"+n)),"default"==e.name){var c=/((?:(?:(?: |>|\.|\w|-|:|\(|\)|\[|\]|"|'|=)+) )?)((?:(?:\w|\.|-)+)?)\.md-default-theme((?: |>|\.|\w|-|:|\(|\)|\[|\]|"|'|=)*)/g;i=i.replace(c,function(e,t,n,o){return e+", "+t+n+o})}r.push(i)}),r}function a(e){function n(e,n){var o=e.contrastDefaultColor,r=e.contrastLightColors||[],i=e.contrastStrongLightColors||[],a=e.contrastDarkColors||[];"string"==typeof r&&(r=r.split(" ")),"string"==typeof i&&(i=i.split(" ")),"string"==typeof a&&(a=a.split(" ")),delete e.contrastDefaultColor,delete e.contrastLightColors,delete e.contrastStrongLightColors,delete e.contrastDarkColors,t.forEach(e,function(n,d){function c(){return"light"===o?a.indexOf(d)>-1?v:i.indexOf(d)>-1?$:E:r.indexOf(d)>-1?i.indexOf(d)>-1?$:E:v}if(!t.isObject(n)){var l=s(n);if(!l)throw new Error("Color %1, in palette %2's hue %3, is invalid. Hex or rgb(a) color expected.".replace("%1",n).replace("%2",e.name).replace("%3",d));e[d]={value:l,contrast:c()}}})}var o=document.head,r=o?o.firstElementChild:null,i=e.has("$MD_THEME_CSS")?e.get("$MD_THEME_CSS"):"";if(r&&0!==i.length){t.forEach(m,n);var a=i.split(/\}(?!(\}|'|"|;))/).filter(function(e){return e&&e.length}).map(function(e){return e.trim()+"}"}),c=new RegExp("md-("+M.join("|")+")","g");M.forEach(function(e){_[e]=""}),a.forEach(function(e){for(var t,n=(e.match(c),0);t=M[n];n++)if(e.indexOf(".md-"+t)>-1)return _[t]+=e;for(n=0;t=M[n];n++)if(e.indexOf(t)>-1)return _[t]+=e;return _[y]+=e}),T||t.forEach(u,function(e){p[e.name]||d(e.name,w)})}}function d(e,t){var n=u[e],o=document.head,r=o?o.firstElementChild:null;p[e]||(M.forEach(function(e){for(var a=i(n,e,_[e]);a.length;){var d=a.shift();if(d){var c=document.createElement("style");c.setAttribute("md-theme-style",""),t&&c.setAttribute("nonce",t),c.appendChild(document.createTextNode(d)),o.insertBefore(c,r)}}}),n.colors.primary.name==n.colors.accent.name&&console.warn("$mdThemingProvider: Using the same palette for primary and accent. This violates the material design spec."),p[n.name]=!0)}function c(e,t){if(!m[(e.colors[t]||{}).name])throw new Error("You supplied an invalid color palette for theme %1's %2 palette. Available palettes: %3".replace("%1",e.name).replace("%2",t).replace("%3",Object.keys(m).join(", ")))}function s(e){if(t.isArray(e)&&3==e.length)return e;if(/^rgb/.test(e))return e.replace(/(^\s*rgba?\(|\)\s*$)/g,"").split(",").map(function(e,t){return 3==t?parseFloat(e,10):parseInt(e,10)});if("#"==e.charAt(0)&&(e=e.substring(1)),/^([a-fA-F0-9]{3}){1,2}$/g.test(e)){var n=e.length/3,o=e.substr(0,n),r=e.substr(n,n),i=e.substr(2*n);return 1===n&&(o+=o,r+=r,i+=i),[parseInt(o,16),parseInt(r,16),parseInt(i,16)]}}function l(e,n){return e?(4==e.length&&(e=t.copy(e),n?e.pop():n=e.pop()),n&&("number"==typeof n||"string"==typeof n&&n.length)?"rgba("+e.join(",")+","+n+")":"rgb("+e.join(",")+")"):"rgb('0,0,0')"}t.module("material.core.theming",["material.core.theming.palette"]).directive("mdTheme",o).directive("mdThemable",r).provider("$mdTheming",e).run(a);var m,u,p={},h={name:"dark",1:"rgba(0,0,0,0.87)",2:"rgba(0,0,0,0.54)",3:"rgba(0,0,0,0.38)",4:"rgba(0,0,0,0.12)"},f={name:"light",1:"rgba(255,255,255,1.0)",2:"rgba(255,255,255,0.7)",3:"rgba(255,255,255,0.5)",4:"rgba(255,255,255,0.12)"},g="1px 1px 0px rgba(0,0,0,0.4), -1px -1px 0px rgba(0,0,0,0.4)",b="",v=s("rgba(0,0,0,0.87)"),E=s("rgba(255,255,255,0.87)"),$=s("rgb(255,255,255)"),M=["primary","accent","warn","background"],y="primary",C={accent:{"default":"A200","hue-1":"A100","hue-2":"A400","hue-3":"A700"},background:{"default":"50","hue-1":"A100","hue-2":"100","hue-3":"300"}},A={background:{"default":"A400","hue-1":"800","hue-2":"900","hue-3":"A200"}};M.forEach(function(e){var t={"default":"500","hue-1":"300","hue-2":"800","hue-3":"A100"};C[e]||(C[e]=t),A[e]||(A[e]=t)});var k=["50","100","200","300","400","500","600","700","800","900","A100","A200","A400","A700"],T=!1,w=null;e.$inject=["$mdColorPalette"],o.$inject=["$mdTheming","$interpolate","$log"],r.$inject=["$mdTheming"];var _={};a.$inject=["$injector"]}(),function(){function e(e,n,o,r,i){var a;return a={translate3d:function(e,t,n,o){function r(n){return i(e,{to:n||t,addClass:o.transitionOutClass,removeClass:o.transitionInClass}).start()}return i(e,{from:t,to:n,addClass:o.transitionInClass}).start().then(function(){return r})},waitTransitionEnd:function(e,t){var i=3e3;return n(function(n,a){function d(t){t&&t.target!==e[0]||(t&&o.cancel(c),e.off(r.CSS.TRANSITIONEND,d),n())}t=t||{};var c=o(d,t.timeout||i);e.on(r.CSS.TRANSITIONEND,d)})},calculateZoomToOrigin:function(n,o){function r(){var e=n?n.parent():null,t=e?e.parent():null;return t?a.clientRect(t):null}var i=o.element,d=o.bounds,c="translate3d( {centerX}px, {centerY}px, 0 ) scale( {scaleX}, {scaleY} )",s=t.bind(null,e.supplant,c),l=s({centerX:0,centerY:0,scaleX:.5,scaleY:.5});if(i||d){var m=i?a.clientRect(i)||r():a.copyRect(d),u=a.copyRect(n[0].getBoundingClientRect()),p=a.centerPointFor(u),h=a.centerPointFor(m);l=s({centerX:h.x-p.x,centerY:h.y-p.y,scaleX:Math.round(100*Math.min(.5,m.width/u.width))/100,scaleY:Math.round(100*Math.min(.5,m.height/u.height))/100})}return l},toCss:function(e){function n(e,n,r){t.forEach(n.split(" "),function(e){o[e]=r})}var o={},i="left top right bottom width height x y min-width min-height max-width max-height";return t.forEach(e,function(e,a){if(!t.isUndefined(e))if(i.indexOf(a)>=0)o[a]=e+"px";else switch(a){case"transition":n(a,r.CSS.TRANSITION,e);break;case"transform":n(a,r.CSS.TRANSFORM,e);break;case"transformOrigin":n(a,r.CSS.TRANSFORM_ORIGIN,e)}}),o},toTransformCss:function(e,n,o){var i={};return t.forEach(r.CSS.TRANSFORM.split(" "),function(t){i[t]=e}),n&&(o=o||"all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1) !important",i.transition=o),i},copyRect:function(e,n){return e?(n=n||{},t.forEach("left top right bottom width height".split(" "),function(t){n[t]=Math.round(e[t])}),n.width=n.width||n.right-n.left,n.height=n.height||n.bottom-n.top,n):null},clientRect:function(e){var n=t.element(e)[0].getBoundingClientRect(),o=function(e){return e&&e.width>0&&e.height>0};return o(n)?a.copyRect(n):null},centerPointFor:function(e){return e?{x:Math.round(e.left+e.width/2),y:Math.round(e.top+e.height/2)}:{x:0,y:0}}}}t.module("material.core").factory("$$mdAnimate",["$q","$timeout","$mdConstant","$animateCss",function(t,n,o,r){return function(i){return e(i,t,n,o,r)}}])}(),function(){t.version.minor>=4?t.module("material.core.animate",[]):!function(){function e(e){return e.replace(/-[a-z]/g,function(e){return e.charAt(1).toUpperCase()})}var n=t.forEach,o=t.isDefined(document.documentElement.style.WebkitAppearance),r=o?"-webkit-":"",i=(o?"webkitTransitionEnd ":"")+"transitionend",a=(o?"webkitAnimationEnd ":"")+"animationend",d=["$document",function(e){return function(){return e[0].body.clientWidth+1}}],c=["$$rAF",function(e){return function(){var t=!1;return e(function(){t=!0}),function(n){t?n():e(n)}}}],s=["$q","$$rAFMutex",function(e,o){function r(e){this.setHost(e),this._doneCallbacks=[],this._runInAnimationFrame=o(),this._state=0}var i=0,a=1,d=2;return r.prototype={setHost:function(e){this.host=e||{}},done:function(e){this._state===d?e():this._doneCallbacks.push(e)},progress:t.noop,getPromise:function(){if(!this.promise){var t=this;this.promise=e(function(e,n){t.done(function(t){t===!1?n():e()})})}return this.promise},then:function(e,t){return this.getPromise().then(e,t)},"catch":function(e){return this.getPromise()["catch"](e)},"finally":function(e){return this.getPromise()["finally"](e)},pause:function(){this.host.pause&&this.host.pause()},resume:function(){this.host.resume&&this.host.resume()},end:function(){this.host.end&&this.host.end(),this._resolve(!0)},cancel:function(){this.host.cancel&&this.host.cancel(),this._resolve(!1)},complete:function(e){var t=this;t._state===i&&(t._state=a,t._runInAnimationFrame(function(){t._resolve(e)}))},_resolve:function(e){this._state!==d&&(n(this._doneCallbacks,function(t){t(e)}),this._doneCallbacks.length=0,this._state=d)}},r}];t.module("material.core.animate",[]).factory("$$forceReflow",d).factory("$$AnimateRunner",s).factory("$$rAFMutex",c).factory("$animateCss",["$window","$$rAF","$$AnimateRunner","$$forceReflow","$$jqLite","$timeout",function(t,d,c,s,l,m){function u(o,d){var s=[],l=$(o);d.transitionStyle&&s.push([r+"transition",d.transitionStyle]),d.keyframeStyle&&s.push([r+"animation",d.keyframeStyle]),d.delay&&s.push([r+"transition-delay",d.delay+"s"]),d.duration&&s.push([r+"transition-duration",d.duration+"s"]);var u=d.keyframeStyle||d.to&&(d.duration>0||d.transitionStyle),f=!!d.addClass||!!d.removeClass,y=u||f;M(o,!0),v(o,d);var C,A,k=!1;return{close:t.close,start:function(){function t(){return k?void 0:(k=!0,C&&A&&o.off(C,A),p(o,d),b(o,d),n(s,function(t){l.style[e(t[0])]=""}),u.complete(!0),u)}var u=new c;return g(function(){if(M(o,!1),!y)return t();n(s,function(t){var n=t[0],o=t[1];l.style[e(n)]=o}),p(o,d);var c=h(o);if(0===c.duration)return t();var u=[];d.easing&&(c.transitionDuration&&u.push([r+"transition-timing-function",d.easing]),c.animationDuration&&u.push([r+"animation-timing-function",d.easing])),d.delay&&c.animationDelay&&u.push([r+"animation-delay",d.delay+"s"]),d.duration&&c.animationDuration&&u.push([r+"animation-duration",d.duration+"s"]),n(u,function(t){var n=t[0],o=t[1];l.style[e(n)]=o,s.push(t)});var f=c.delay,g=1e3*f,b=c.duration,v=1e3*b,$=Date.now();C=[],c.transitionDuration&&C.push(i),c.animationDuration&&C.push(a),C=C.join(" "),A=function(e){e.stopPropagation();var n=e.originalEvent||e,o=n.timeStamp||Date.now(),r=parseFloat(n.elapsedTime.toFixed(3));Math.max(o-$,0)>=g&&r>=b&&t()},o.on(C,A),E(o,d),m(t,g+1.5*v,!1)}),u}}}function p(e,t){t.addClass&&(l.addClass(e,t.addClass),t.addClass=null),t.removeClass&&(l.removeClass(e,t.removeClass),t.removeClass=null)}function h(e){function n(e){return o?"Webkit"+e.charAt(0).toUpperCase()+e.substr(1):e}var r=$(e),i=t.getComputedStyle(r),a=f(i[n("transitionDuration")]),d=f(i[n("animationDuration")]),c=f(i[n("transitionDelay")]),s=f(i[n("animationDelay")]);d*=parseInt(i[n("animationIterationCount")],10)||1;var l=Math.max(d,a),m=Math.max(s,c);return{duration:l,delay:m,animationDuration:d,transitionDuration:a,animationDelay:s,transitionDelay:c}}function f(e){var t=0,o=(e||"").split(/\s*,\s*/);return n(o,function(e){"s"==e.charAt(e.length-1)&&(e=e.substring(0,e.length-1)),e=parseFloat(e)||0,t=t?Math.max(e,t):e}),t}function g(e){y&&y(),C.push(e),y=d(function(){y=null;for(var e=s(),t=0;to&&(o=Math.max(-r,o/2)),e.css(n.CSS.TRANSFORM,"translate3d(0,"+(r+o)+"px,0)")}function l(t){if(t.pointer.distanceY>0&&(t.pointer.distanceY>20||Math.abs(t.pointer.velocityY)>o)){var r=e.prop("offsetHeight")-t.pointer.distanceY,a=Math.min(r/t.pointer.velocityY*.75,500);e.css(n.CSS.TRANSITION_DURATION,a+"ms"),i.nextTick(d.cancel,!0)}else e.css(n.CSS.TRANSITION_DURATION,""),e.css(n.CSS.TRANSFORM,"")}var m=s.register(t,"drag",{horizontal:!1});return t.on("$md.dragstart",a).on("$md.drag",c).on("$md.dragend",l),{element:e,cleanup:function(){m(),t.off("$md.dragstart",a),t.off("$md.drag",c),t.off("$md.dragend",l)}}}var p;return{themable:!0,onShow:l,onRemove:m,disableBackdrop:!1,escapeToClose:!0,clickOutsideToClose:!0,disableParentScroll:!0}}var o=.5,r=80;return n.$inject=["$animate","$mdConstant","$mdUtil","$mdTheming","$mdBottomSheet","$rootElement","$mdGesture"],e("$mdBottomSheet").setDefaults({methods:["disableParentScroll","escapeToClose","clickOutsideToClose"],options:n})}t.module("material.components.bottomSheet",["material.core","material.components.backdrop"]).directive("mdBottomSheet",e).provider("$mdBottomSheet",n),e.$inject=["$mdBottomSheet"],n.$inject=["$$interimElementProvider"]}(),function(){t.module("material.components.backdrop",["material.core"]).directive("mdBackdrop",["$mdTheming","$animate","$rootElement","$window","$log","$$rAF","$document",function(e,t,n,o,r,i,a){function d(d,s,l){var m=o.getComputedStyle(a[0].body);if("fixed"==m.position){var u=parseInt(m.height,10)+Math.abs(parseInt(m.top,10));s.css({height:u+"px"})}t.pin&&t.pin(s,n),i(function(){var t=s.parent()[0];if(t){"BODY"==t.nodeName&&s.css({position:"fixed"});var n=o.getComputedStyle(t);"static"==n.position&&r.warn(c)}s.parent().length&&e.inherit(s,s.parent())})}var c=" may not work properly in a scrolled, static-positioned parent container.";return{restrict:"E",link:d}}])}(),function(){function e(e,n,o,r){function i(e){return t.isDefined(e.href)||t.isDefined(e.ngHref)||t.isDefined(e.ngLink)||t.isDefined(e.uiSref)}function a(e,t){if(i(t))return'';var n="undefined"==typeof t.type?"button":t.type;return''}function d(a,d,c){n(d),e.attach(a,d),o.expectWithText(d,"aria-label"),i(c)&&t.isDefined(c.ngDisabled)&&a.$watch(c.ngDisabled,function(e){d.attr("tabindex",e?-1:0)}),d.on("click",function(e){c.disabled===!0&&(e.preventDefault(),e.stopImmediatePropagation())}),t.isDefined(c.mdNoFocusStyle)||(a.mouseActive=!1,d.on("mousedown",function(){a.mouseActive=!0,r(function(){a.mouseActive=!1},100)}).on("focus",function(){a.mouseActive===!1&&d.addClass("md-focused")}).on("blur",function(e){d.removeClass("md-focused")}))}return{restrict:"EA",replace:!0,transclude:!0,template:a,link:d}}t.module("material.components.button",["material.core"]).directive("mdButton",e),e.$inject=["$mdButtonInkRipple","$mdTheming","$mdAria","$timeout"]}(),function(){function e(e){return{restrict:"E",link:function(t,n){e(n)}}}t.module("material.components.card",["material.core"]).directive("mdCard",e), + e.$inject=["$mdTheming"]}(),function(){function e(e,n,o,r,i,a){function d(d,s){var l=d.children(),m=s.hasOwnProperty("mdIndeterminate");return s.type="checkbox",s.tabindex=s.tabindex||"0",d.attr("role",s.type),d.on("click",function(e){this.hasAttribute("disabled")&&e.stopImmediatePropagation()}),l.on("focus",function(){d.focus()}),function(d,s,l,u){function p(e,t,n){l[e]&&d.$watch(l[e],function(e){n[e]&&s.attr(t,n[e])})}function h(e){var t=e.which||e.keyCode;t!==o.KEY_CODE.SPACE&&t!==o.KEY_CODE.ENTER||(e.preventDefault(),s.hasClass("md-focused")||s.addClass("md-focused"),f(e))}function f(e){s[0].hasAttribute("disabled")||d.$apply(function(){var t=l.ngChecked?l.checked:!u.$viewValue;u.$setViewValue(t,e&&e.type),u.$render()})}function g(){u.$viewValue&&!v?s.addClass(c):s.removeClass(c)}function b(e){v=e!==!1,v&&s.attr("aria-checked","mixed"),s.toggleClass("md-indeterminate",v)}var v;u=u||i.fakeNgModel(),r(s),m&&(b(),d.$watch(l.mdIndeterminate,b)),l.ngChecked&&d.$watch(d.$eval.bind(d,l.ngChecked),u.$setViewValue.bind(u)),p("ngDisabled","tabindex",{"true":"-1","false":l.tabindex}),n.expectWithText(s,"aria-label"),e.link.pre(d,{on:t.noop,0:{}},l,[u]),d.mouseActive=!1,s.on("click",f).on("keypress",h).on("mousedown",function(){d.mouseActive=!0,a(function(){d.mouseActive=!1},100)}).on("focus",function(){d.mouseActive===!1&&s.addClass("md-focused")}).on("blur",function(){s.removeClass("md-focused")}),u.$render=g}}e=e[0];var c="md-checked";return{restrict:"E",transclude:!0,require:"?ngModel",priority:210,template:'
',compile:d}}t.module("material.components.checkbox",["material.core"]).directive("mdCheckbox",e),e.$inject=["inputDirective","$mdAria","$mdConstant","$mdTheming","$mdUtil","$timeout"]}(),function(){t.module("material.components.chips",["material.core","material.components.autocomplete"])}(),function(){function e(e){function t(e,t){this.$scope=e,this.$element=t}return{restrict:"E",controller:["$scope","$element",t],link:function(t,o,r){o[0];e(o),t.$broadcast("$mdContentLoaded",o),n(o[0])}}}function n(e){t.element(e).on("$md.pressdown",function(t){"t"===t.pointer.type&&(t.$materialScrollFixed||(t.$materialScrollFixed=!0,0===e.scrollTop?e.scrollTop=1:e.scrollHeight===e.scrollTop+e.offsetHeight&&(e.scrollTop-=1)))})}t.module("material.components.content",["material.core"]).directive("mdContent",e),e.$inject=["$mdTheming"]}(),function(){!function(){function e(){return{template:'
',scope:{minDate:"=mdMinDate",maxDate:"=mdMaxDate",dateFilter:"=mdDateFilter"},require:["ngModel","mdCalendar"],controller:n,controllerAs:"ctrl",bindToController:!0,link:function(e,t,n,o){var r=o[0],i=o[1];i.configureNgModel(r)}}}function n(e,t,n,o,r,i,a,c,s,l,m){if(a(e),this.items={length:2e3},this.maxDate&&this.minDate){var u=c.getMonthDistance(this.minDate,this.maxDate)+1;u=Math.max(u,1),u+=1,this.items.length=u}if(this.$animate=o,this.$q=r,this.$mdInkRipple=l,this.$mdUtil=m,this.keyCode=i.KEY_CODE,this.dateUtil=c,this.dateLocale=s,this.$element=e,this.$scope=n,this.calendarElement=e[0].querySelector(".md-calendar"),this.calendarScroller=e[0].querySelector(".md-virtual-repeat-scroller"),this.today=this.dateUtil.createDateAtMidnight(),this.firstRenderableDate=this.dateUtil.incrementMonths(this.today,-this.items.length/2),this.minDate&&this.minDate>this.firstRenderableDate)this.firstRenderableDate=this.minDate;else if(this.maxDate){this.items.length-2;this.firstRenderableDate=this.dateUtil.incrementMonths(this.maxDate,-(this.items.length-2))}this.id=d++,this.ngModelCtrl=null,this.selectedDate=null,this.displayDate=null,this.focusDate=null,this.isInitialized=!1,this.isMonthTransitionInProgress=!1,t.tabindex||e.attr("tabindex","-1");var p=this;this.cellClickHandler=function(){var e=this;this.hasAttribute("data-timestamp")&&n.$apply(function(){var t=Number(e.getAttribute("data-timestamp"));p.setNgModelValue(p.dateUtil.createDateAtMidnight(t))})},this.attachCalendarEventListeners()}t.module("material.components.datepicker",["material.core","material.components.icon","material.components.virtualRepeat"]).directive("mdCalendar",e);var o=265,r=45,i="md-calendar-selected-date",a="md-focus",d=0;n.$inject=["$element","$attrs","$scope","$animate","$q","$mdConstant","$mdTheming","$$mdDateUtil","$mdDateLocale","$mdInkRipple","$mdUtil"],n.prototype.configureNgModel=function(e){this.ngModelCtrl=e;var t=this;e.$render=function(){t.changeSelectedDate(t.ngModelCtrl.$viewValue)}},n.prototype.buildInitialCalendarDisplay=function(){this.buildWeekHeader(),this.hideVerticalScrollbar(),this.displayDate=this.selectedDate||this.today,this.isInitialized=!0},n.prototype.hideVerticalScrollbar=function(){var e=this.$element[0],t=e.querySelector(".md-calendar-scroll-mask"),n=this.calendarScroller,o=e.querySelector(".md-calendar-day-header").clientWidth,r=n.offsetWidth-n.clientWidth;t.style.width=o+"px",n.style.width=o+r+"px",n.style.paddingRight=r+"px"},n.prototype.attachCalendarEventListeners=function(){this.$element.on("keydown",t.bind(this,this.handleKeyEvent))},n.prototype.handleKeyEvent=function(e){var t=this;this.$scope.$apply(function(){if(e.which==t.keyCode.ESCAPE||e.which==t.keyCode.TAB)return t.$scope.$emit("md-calendar-close"),void(e.which==t.keyCode.TAB&&e.preventDefault());if(e.which===t.keyCode.ENTER)return t.setNgModelValue(t.displayDate),void e.preventDefault();var n=t.getFocusDateFromKeyEvent(e);n&&(n=t.boundDateByMinAndMax(n),e.preventDefault(),e.stopPropagation(),t.changeDisplayDate(n).then(function(){t.focus(n)}))})},n.prototype.getFocusDateFromKeyEvent=function(e){var t=this.dateUtil,n=this.keyCode;switch(e.which){case n.RIGHT_ARROW:return t.incrementDays(this.displayDate,1);case n.LEFT_ARROW:return t.incrementDays(this.displayDate,-1);case n.DOWN_ARROW:return e.metaKey?t.incrementMonths(this.displayDate,1):t.incrementDays(this.displayDate,7);case n.UP_ARROW:return e.metaKey?t.incrementMonths(this.displayDate,-1):t.incrementDays(this.displayDate,-7);case n.PAGE_DOWN:return t.incrementMonths(this.displayDate,1);case n.PAGE_UP:return t.incrementMonths(this.displayDate,-1);case n.HOME:return t.getFirstDateOfMonth(this.displayDate);case n.END:return t.getLastDateOfMonth(this.displayDate);default:return null}},n.prototype.getSelectedMonthIndex=function(){return this.dateUtil.getMonthDistance(this.firstRenderableDate,this.selectedDate||this.today)},n.prototype.scrollToMonth=function(e){if(this.dateUtil.isValidDate(e)){var t=this.dateUtil.getMonthDistance(this.firstRenderableDate,e);this.calendarScroller.scrollTop=t*o}},n.prototype.setNgModelValue=function(e){this.$scope.$emit("md-calendar-change",e),this.ngModelCtrl.$setViewValue(e),this.ngModelCtrl.$render()},n.prototype.focus=function(e){var t=e||this.selectedDate||this.today,n=this.calendarElement.querySelector(".md-focus");n&&n.classList.remove(a);var o=this.getDateId(t),r=document.getElementById(o);r?(r.classList.add(a),r.focus()):this.focusDate=t},n.prototype.boundDateByMinAndMax=function(e){var t=e;return this.minDate&&ethis.maxDate&&(t=new Date(this.maxDate.getTime())),t},n.prototype.changeSelectedDate=function(e){var t=this,n=this.selectedDate;this.selectedDate=e,this.changeDisplayDate(e).then(function(){if(n){var o=document.getElementById(t.getDateId(n));o&&(o.classList.remove(i),o.setAttribute("aria-selected","false"))}if(e){var r=document.getElementById(t.getDateId(e));r&&(r.classList.add(i),r.setAttribute("aria-selected","true"))}})},n.prototype.changeDisplayDate=function(e){if(!this.isInitialized)return this.buildInitialCalendarDisplay(),this.$q.when();if(!this.dateUtil.isValidDate(e)||this.isMonthTransitionInProgress)return this.$q.when();this.isMonthTransitionInProgress=!0;var t=this.animateDateChange(e);this.displayDate=e;var n=this;return t.then(function(){n.isMonthTransitionInProgress=!1}),t},n.prototype.animateDateChange=function(e){return this.scrollToMonth(e),this.$q.when()},n.prototype.buildWeekHeader=function(){for(var e=this.dateLocale.firstDayOfWeek,t=this.dateLocale.shortDays,n=document.createElement("tr"),o=0;7>o;o++){var r=document.createElement("th");r.textContent=t[(o+e)%7],n.appendChild(r)}this.$element.find("thead").append(n)},n.prototype.getDateId=function(e){return["md",this.id,e.getFullYear(),e.getMonth(),e.getDate()].join("-")}}()}(),function(){!function(){function e(){return{require:["^^mdCalendar","mdCalendarMonth"],scope:{offset:"=mdMonthOffset"},controller:n,controllerAs:"mdMonthCtrl",bindToController:!0,link:function(e,t,n,o){var r=o[0],i=o[1];i.calendarCtrl=r,i.generateContent(),e.$watch(function(){return i.offset},function(e,t){e!=t&&i.generateContent()})}}}function n(e,t,n){this.dateUtil=t,this.dateLocale=n,this.$element=e,this.calendarCtrl=null,this.offset,this.focusAfterAppend=null}t.module("material.components.datepicker").directive("mdCalendarMonth",e);var o="md-calendar-date-today",r="md-calendar-selected-date",i="md-focus";n.$inject=["$element","$$mdDateUtil","$mdDateLocale"],n.prototype.generateContent=function(){var e=this.calendarCtrl,t=this.dateUtil.incrementMonths(e.firstRenderableDate,this.offset);this.$element.empty(),this.$element.append(this.buildCalendarForMonth(t)),this.focusAfterAppend&&(this.focusAfterAppend.classList.add(i),this.focusAfterAppend.focus(),this.focusAfterAppend=null)},n.prototype.buildDateCell=function(e){var t=this.calendarCtrl,n=document.createElement("td");if(n.tabIndex=-1,n.classList.add("md-calendar-date"),n.setAttribute("role","gridcell"),e){n.setAttribute("tabindex","-1"),n.setAttribute("aria-label",this.dateLocale.longDateFormatter(e)),n.id=t.getDateId(e),n.setAttribute("data-timestamp",e.getTime()),this.dateUtil.isSameDay(e,t.today)&&n.classList.add(o),this.dateUtil.isValidDate(t.selectedDate)&&this.dateUtil.isSameDay(e,t.selectedDate)&&(n.classList.add(r),n.setAttribute("aria-selected","true"));var i=this.dateLocale.dates[e.getDate()];if(this.isDateEnabled(e)){var a=document.createElement("span");n.appendChild(a),a.classList.add("md-calendar-date-selection-indicator"),a.textContent=i,n.addEventListener("click",t.cellClickHandler),t.focusDate&&this.dateUtil.isSameDay(e,t.focusDate)&&(this.focusAfterAppend=n)}else n.classList.add("md-calendar-date-disabled"),n.textContent=i}return n},n.prototype.isDateEnabled=function(e){return this.dateUtil.isDateWithinRange(e,this.calendarCtrl.minDate,this.calendarCtrl.maxDate)&&(!t.isFunction(this.calendarCtrl.dateFilter)||this.calendarCtrl.dateFilter(e))},n.prototype.buildDateRow=function(e){var t=document.createElement("tr");return t.setAttribute("role","row"),t.setAttribute("aria-label",this.dateLocale.weekNumberFormatter(e)),t},n.prototype.buildCalendarForMonth=function(e){var t=this.dateUtil.isValidDate(e)?e:new Date,n=this.dateUtil.getFirstDateOfMonth(t),o=this.getLocaleDay_(n),r=this.dateUtil.getNumberOfDaysInMonth(t),i=document.createDocumentFragment(),a=1,d=this.buildDateRow(a);i.appendChild(d);var c=this.offset===this.calendarCtrl.items.length-1,s=0,l=document.createElement("td");if(l.classList.add("md-calendar-month-label"),this.calendarCtrl.maxDate&&n>this.calendarCtrl.maxDate&&l.classList.add("md-calendar-month-label-disabled"),l.textContent=this.dateLocale.monthHeaderFormatter(t),2>=o){l.setAttribute("colspan","7");var m=this.buildDateRow();if(m.appendChild(l),i.insertBefore(m,d),c)return i}else s=2,l.setAttribute("colspan","2"),d.appendChild(l);for(var u=s;o>u;u++)d.appendChild(this.buildDateCell());for(var p=o,h=n,f=1;r>=f;f++){if(7===p){if(c)return i;p=0,a++,d=this.buildDateRow(a),i.appendChild(d)}h.setDate(f);var g=this.buildDateCell(h);d.appendChild(g),p++}for(;d.childNodes.length<7;)d.appendChild(this.buildDateCell());for(;i.childNodes.length<6;){for(var b=this.buildDateRow(),u=0;7>u;u++)b.appendChild(this.buildDateCell());i.appendChild(b)}return i},n.prototype.getLocaleDay_=function(e){return(e.getDay()+(7-this.dateLocale.firstDayOfWeek))%7}}()}(),function(){!function(){t.module("material.components.datepicker").config(["$provide",function(e){function t(){this.months=null,this.shortMonths=null,this.days=null,this.shortDays=null,this.dates=null,this.firstDayOfWeek=0,this.formatDate=null,this.parseDate=null,this.monthHeaderFormatter=null,this.weekNumberFormatter=null,this.longDateFormatter=null,this.msgCalendar="",this.msgOpenCalendar=""}t.prototype.$get=function(e){function t(e){if(!e)return"";var t=e.toLocaleTimeString(),n=e;return 0!=e.getHours()||-1===t.indexOf("11:")&&-1===t.indexOf("23:")||(n=new Date(e.getFullYear(),e.getMonth(),e.getDate(),1,0,0)),n.toLocaleDateString()}function n(e){return new Date(e)}function o(e){e=e.trim();var t=/^(([a-zA-Z]{3,}|[0-9]{1,4})([ \.,]+|[\/\-])){2}([a-zA-Z]{3,}|[0-9]{1,4})$/;return t.test(e)}function r(e){return u.shortMonths[e.getMonth()]+" "+e.getFullYear()}function i(e){return"Week "+e}function a(e){return[u.days[e.getDay()],u.months[e.getMonth()],u.dates[e.getDate()],e.getFullYear()].join(" ")}for(var d=e.DATETIME_FORMATS.DAY.map(function(e){return e[0]}),c=Array(32),s=1;31>=s;s++)c[s]=s;var l="Calendar",m="Open calendar",u={months:this.months||e.DATETIME_FORMATS.MONTH,shortMonths:this.shortMonths||e.DATETIME_FORMATS.SHORTMONTH,days:this.days||e.DATETIME_FORMATS.DAY,shortDays:this.shortDays||d,dates:this.dates||c,firstDayOfWeek:this.firstDayOfWeek||0,formatDate:this.formatDate||t,parseDate:this.parseDate||n,isDateComplete:this.isDateComplete||o,monthHeaderFormatter:this.monthHeaderFormatter||r,weekNumberFormatter:this.weekNumberFormatter||i,longDateFormatter:this.longDateFormatter||a,msgCalendar:this.msgCalendar||l,msgOpenCalendar:this.msgOpenCalendar||m};return u},t.prototype.$get.$inject=["$locale"],e.provider("$mdDateLocale",new t)}])}()}(),function(){!function(){function n(){return{template:'
',require:["ngModel","mdDatepicker","?^mdInputContainer"],scope:{minDate:"=mdMinDate",maxDate:"=mdMaxDate",placeholder:"@mdPlaceholder",dateFilter:"=mdDateFilter"},controller:o,controllerAs:"ctrl",bindToController:!0,link:function(e,t,n,o){var r=o[0],i=o[1],a=o[2];if(a)throw Error("md-datepicker should not be placed inside md-input-container.");i.configureNgModel(r)}}}function o(e,n,o,r,i,a,d,c,s,l,m,u){this.$compile=r,this.$timeout=i,this.$window=a,this.dateLocale=l,this.dateUtil=m,this.$mdConstant=d,this.$mdUtil=s,this.$$rAF=u,this.documentElement=t.element(document.documentElement),this.ngModelCtrl=null,this.inputElement=n[0].querySelector("input"),this.ngInputElement=t.element(this.inputElement),this.inputContainer=n[0].querySelector(".md-datepicker-input-container"),this.calendarPane=n[0].querySelector(".md-datepicker-calendar-pane"),this.calendarButton=n[0].querySelector(".md-datepicker-button"),this.inputMask=n[0].querySelector(".md-datepicker-input-mask-opaque"),this.$element=n,this.$attrs=o,this.$scope=e,this.date=null,this.isFocused=!1,this.isDisabled,this.setDisabled(n[0].disabled||t.isString(o.disabled)),this.isCalendarOpen=!1,this.calendarPaneOpenedFrom=null,this.calendarPane.id="md-date-pane"+s.nextUid(),c(n),this.bodyClickHandler=t.bind(this,this.handleBodyClick),this.windowResizeHandler=s.debounce(t.bind(this,this.closeCalendarPane),100),o.tabindex||n.attr("tabindex","-1"),this.installPropertyInterceptors(),this.attachChangeListeners(),this.attachInteractionListeners();var p=this;e.$on("$destroy",function(){p.detachCalendarPane()})}t.module("material.components.datepicker").directive("mdDatepicker",n);var r=3,i="md-datepicker-invalid",a=500,d=368,c=360;o.$inject=["$scope","$element","$attrs","$compile","$timeout","$window","$mdConstant","$mdTheming","$mdUtil","$mdDateLocale","$$mdDateUtil","$$rAF"],o.prototype.configureNgModel=function(e){this.ngModelCtrl=e;var t=this;e.$render=function(){var e=t.ngModelCtrl.$viewValue;if(e&&!(e instanceof Date))throw Error("The ng-model for md-datepicker must be a Date instance. Currently the model is a: "+typeof e);t.date=e,t.inputElement.value=t.dateLocale.formatDate(e),t.resizeInputElement(),t.updateErrorState()}},o.prototype.attachChangeListeners=function(){var e=this;e.$scope.$on("md-calendar-change",function(t,n){e.ngModelCtrl.$setViewValue(n),e.date=n,e.inputElement.value=e.dateLocale.formatDate(n),e.closeCalendarPane(),e.resizeInputElement(),e.updateErrorState()}),e.ngInputElement.on("input",t.bind(e,e.resizeInputElement)),e.ngInputElement.on("input",e.$mdUtil.debounce(e.handleInputEvent,a,e))},o.prototype.attachInteractionListeners=function(){var e=this,t=this.$scope,n=this.$mdConstant.KEY_CODE;e.ngInputElement.on("keydown",function(o){o.altKey&&o.keyCode==n.DOWN_ARROW&&(e.openCalendarPane(o),t.$digest())}),t.$on("md-calendar-close",function(){e.closeCalendarPane()})},o.prototype.installPropertyInterceptors=function(){var e=this;if(this.$attrs.ngDisabled){var t=this.$scope.$parent;t&&t.$watch(this.$attrs.ngDisabled,function(t){e.setDisabled(t)})}Object.defineProperty(this,"placeholder",{get:function(){return e.inputElement.placeholder},set:function(t){e.inputElement.placeholder=t||""}})},o.prototype.setDisabled=function(e){this.isDisabled=e,this.inputElement.disabled=e,this.calendarButton.disabled=e},o.prototype.updateErrorState=function(e){var n=e||this.date;if(this.clearErrorState(),this.dateUtil.isValidDate(n)){if(n=this.dateUtil.createDateAtMidnight(n),this.dateUtil.isValidDate(this.minDate)){var o=this.dateUtil.createDateAtMidnight(this.minDate);this.ngModelCtrl.$setValidity("mindate",n>=o)}if(this.dateUtil.isValidDate(this.maxDate)){var r=this.dateUtil.createDateAtMidnight(this.maxDate);this.ngModelCtrl.$setValidity("maxdate",r>=n)}t.isFunction(this.dateFilter)&&this.ngModelCtrl.$setValidity("filtered",this.dateFilter(n))}else this.ngModelCtrl.$setValidity("valid",null==n);this.ngModelCtrl.$valid||this.inputContainer.classList.add(i)},o.prototype.clearErrorState=function(){this.inputContainer.classList.remove(i),["mindate","maxdate","filtered","valid"].forEach(function(e){this.ngModelCtrl.$setValidity(e,!0)},this)},o.prototype.resizeInputElement=function(){this.inputElement.size=this.inputElement.value.length+r},o.prototype.handleInputEvent=function(){var e=this.inputElement.value,t=e?this.dateLocale.parseDate(e):null;this.dateUtil.setDateTimeToMidnight(t);var n=""==e||this.dateUtil.isValidDate(t)&&this.dateLocale.isDateComplete(e)&&this.isDateEnabled(t);n&&(this.ngModelCtrl.$setViewValue(t),this.date=t),this.updateErrorState(t)},o.prototype.isDateEnabled=function(e){return this.dateUtil.isDateWithinRange(e,this.minDate,this.maxDate)&&(!t.isFunction(this.dateFilter)||this.dateFilter(e))},o.prototype.attachCalendarPane=function(){var e=this.calendarPane;e.style.transform="",this.$element.addClass("md-datepicker-open");var t=this.inputContainer.getBoundingClientRect(),n=document.body.getBoundingClientRect(),o=t.top-n.top,r=t.left-n.left,i=n.top<0&&0==document.body.scrollTop?-n.top:document.body.scrollTop,a=n.left<0&&0==document.body.scrollLeft?-n.left:document.body.scrollLeft,s=i+this.$window.innerHeight,l=a+this.$window.innerWidth;if(r+c>l){if(l-c>0)r=l-c;else{r=a;var m=this.$window.innerWidth/c;e.style.transform="scale("+m+")"}e.classList.add("md-datepicker-pos-adjusted")}o+d>s&&s-d>i&&(o=s-d,e.classList.add("md-datepicker-pos-adjusted")),e.style.left=r+"px",e.style.top=o+"px",document.body.appendChild(e),this.inputMask.style.left=t.width+"px",this.$$rAF(function(){e.classList.add("md-pane-open")})},o.prototype.detachCalendarPane=function(){this.$element.removeClass("md-datepicker-open"),this.calendarPane.classList.remove("md-pane-open"),this.calendarPane.classList.remove("md-datepicker-pos-adjusted"),this.isCalendarOpen&&this.$mdUtil.enableScrolling(),this.calendarPane.parentNode&&this.calendarPane.parentNode.removeChild(this.calendarPane)},o.prototype.openCalendarPane=function(t){if(!this.isCalendarOpen&&!this.isDisabled){this.isCalendarOpen=!0,this.calendarPaneOpenedFrom=t.target,this.$mdUtil.disableScrollAround(this.calendarPane),this.attachCalendarPane(),this.focusCalendar();var n=this;this.$mdUtil.nextTick(function(){n.documentElement.on("click touchstart",n.bodyClickHandler)},!1),e.addEventListener("resize",this.windowResizeHandler)}},o.prototype.closeCalendarPane=function(){this.isCalendarOpen&&(this.detachCalendarPane(),this.isCalendarOpen=!1,this.calendarPaneOpenedFrom.focus(),this.calendarPaneOpenedFrom=null,this.ngModelCtrl.$setTouched(),this.documentElement.off("click touchstart",this.bodyClickHandler),e.removeEventListener("resize",this.windowResizeHandler))},o.prototype.getCalendarCtrl=function(){return t.element(this.calendarPane.querySelector("md-calendar")).controller("mdCalendar")},o.prototype.focusCalendar=function(){var e=this;this.$mdUtil.nextTick(function(){e.getCalendarCtrl().focus()},!1)},o.prototype.setFocused=function(e){e||this.ngModelCtrl.$setTouched(),this.isFocused=e},o.prototype.handleBodyClick=function(e){if(this.isCalendarOpen){var t=this.$mdUtil.getClosest(e.target,"md-calendar");t||this.closeCalendarPane(),this.$scope.$digest()}}}()}(),function(){!function(){t.module("material.components.datepicker").factory("$$mdDateUtil",function(){function e(e){return new Date(e.getFullYear(),e.getMonth(),1)}function n(e){return new Date(e.getFullYear(),e.getMonth()+1,0).getDate()}function o(e){return new Date(e.getFullYear(),e.getMonth()+1,1)}function r(e){return new Date(e.getFullYear(),e.getMonth()-1,1)}function i(e,t){return e.getFullYear()===t.getFullYear()&&e.getMonth()===t.getMonth()}function a(e,t){return e.getDate()==t.getDate()&&i(e,t)}function d(e,t){var n=o(e);return i(n,t)}function c(e,t){var n=r(e);return i(t,n)}function s(e,t){return b((e.getTime()+t.getTime())/2)}function l(t){var n=e(t);return Math.floor((n.getDay()+t.getDate()-1)/7)}function m(e,t){return new Date(e.getFullYear(),e.getMonth(),e.getDate()+t)}function u(e,t){var o=new Date(e.getFullYear(),e.getMonth()+t,1),r=n(o);return r=r)&&(!i||i>=o)}return{getFirstDateOfMonth:e,getNumberOfDaysInMonth:n,getDateInNextMonth:o,getDateInPreviousMonth:r,isInNextMonth:d,isInPreviousMonth:c,getDateMidpoint:s,isSameMonthAndYear:i,getWeekOfMonth:l,incrementDays:m,incrementMonths:u,getLastDateOfMonth:h,isSameDay:a,getMonthDistance:p,isValidDate:f,setDateTimeToMidnight:g,createDateAtMidnight:b,isDateWithinRange:v}})}()}(),function(){function e(e,n,o){return{restrict:"E",link:function(r,i,a){n(i),e(function(){function e(){i.toggleClass("md-content-overflow",a.scrollHeight>a.clientHeight)}var n,a=i[0].querySelector("md-dialog-content");a&&(n=a.getElementsByTagName("img"),e(),t.element(n).on("load",e)),r.$on("$destroy",function(){o.destroy(i)})})}}}function o(e){function o(e,t,n){return{template:['',' ','

{{ dialog.title }}

','
','
',"

{{::dialog.mdTextContent}}

","
",' ',' '," ","
"," ",' '," {{ dialog.cancel }}"," ",' '," {{ dialog.ok }}"," "," ","
"].join("").replace(/\s\s+/g,""),controller:function(){this.hide=function(){e.hide("prompt"===this.$type?this.result:!0)},this.abort=function(){e.cancel()},this.keypress=function(t){t.keyCode===n.KEY_CODE.ENTER&&e.hide(this.result)}},controllerAs:"dialog",bindToController:!0,theme:t.defaultTheme()}}function r(e,o,r,d,c,s,l,m,u,p){function h(e,t,n,o){if(o){if(o.mdHtmlContent=o.htmlContent||n.htmlContent||"",o.mdTextContent=o.textContent||n.textContent||o.content||n.content||"",o.mdHtmlContent&&!p.has("$sanitize"))throw Error("The ngSanitize module must be loaded in order to use htmlContent.");if(o.mdHtmlContent&&o.mdTextContent)throw Error("md-dialog cannot have both `htmlContent` and `textContent`")}}function f(e,n,o,i){function a(){var e=n[0].querySelectorAll(".md-actions");e.length>0&&u.warn("Using a class of md-actions is deprecated, please use .")}function d(){function e(){var e=n[0].querySelector(".dialog-close");if(!e){var o=n[0].querySelectorAll(".md-actions button, md-dialog-actions button");e=o[o.length-1]}return t.element(e)}if(o.focusOnOpen){var i=r.findFocusTarget(n)||e();i.focus()}}return t.element(s[0].body).addClass("md-dialog-is-showing"),b(o),$(n.find("md-dialog"),o),E(e,n,o),C(n,o).then(function(){v(n,o),M(n,o),a(),d()})}function g(e,n,o){function r(){return A(n,o)}function d(){t.element(s[0].body).removeClass("md-dialog-is-showing"),n.remove(),o.$destroy||o.origin.focus()}return o.deactivateListeners(),o.unlockScreenReader(),o.hideBackdrop(o.$destroy),i&&i.parentNode&&i.parentNode.removeChild(i),a&&a.parentNode&&a.parentNode.removeChild(a),o.$destroy?d():r().then(d)}function b(e){function o(e,o){var r=t.element(e||{});if(r&&r.length){var i={top:0,left:0,height:0,width:0},a=t.isFunction(r[0].getBoundingClientRect);return t.extend(o||{},{element:a?r:n,bounds:a?r[0].getBoundingClientRect():t.extend({},i,r[0]),focus:t.bind(r,r.focus)})}}function r(e,n){if(t.isString(e)){var o=e,r=s[0].querySelectorAll(o);e=r.length?r[0]:null}return t.element(e||n)}e.origin=t.extend({element:null,bounds:null,focus:t.noop},e.origin||{}),e.parent=r(e.parent,m),e.closeTo=o(r(e.closeTo)),e.openFrom=o(r(e.openFrom)),e.targetEvent&&(e.origin=o(e.targetEvent.target,e.origin))}function v(n,o){var i=t.element(l),a=r.debounce(function(){y(n,o)},60),c=[],s=function(){var t="alert"==o.$type?e.hide:e.cancel;r.nextTick(t,!0)};if(o.escapeToClose){var m=o.parent,u=function(e){e.keyCode===d.KEY_CODE.ESCAPE&&(e.stopPropagation(),e.preventDefault(),s())};n.on("keydown",u),m.on("keydown",u),c.push(function(){n.off("keydown",u),m.off("keydown",u)})}if(i.on("resize",a),c.push(function(){i.off("resize",a)}),o.clickOutsideToClose){var p,h=n,f=function(e){p=e.target},g=function(e){p===h[0]&&e.target===h[0]&&(e.stopPropagation(),e.preventDefault(),s())};h.on("mousedown",f),h.on("mouseup",g),c.push(function(){h.off("mousedown",f),h.off("mouseup",g)})}o.deactivateListeners=function(){c.forEach(function(e){e()}),o.deactivateListeners=null}}function E(e,t,n){n.disableParentScroll&&(n.restoreScroll=r.disableScrollAround(t,n.parent)),n.hasBackdrop&&(n.backdrop=r.createBackdrop(e,"_md-dialog-backdrop md-opaque"),c.enter(n.backdrop,n.parent)),n.hideBackdrop=function(e){n.backdrop&&(e?n.backdrop.remove():c.leave(n.backdrop)),n.disableParentScroll&&(n.restoreScroll(),delete n.restoreScroll),n.hideBackdrop=null}}function $(e,t){var n="alert"===t.$type?"alertdialog":"dialog",d=e.find("md-dialog-content"),c="dialogContent_"+(e.attr("id")||r.nextUid());e.attr({role:n,tabIndex:"-1"}),0===d.length&&(d=e),d.attr("id",c),e.attr("aria-describedby",c),t.ariaLabel?o.expect(e,"aria-label",t.ariaLabel):o.expectAsync(e,"aria-label",function(){var e=d.text().split(/\s+/);return e.length>3&&(e=e.slice(0,3).concat("...")),e.join(" ")}),i=document.createElement("div"),i.classList.add("_md-dialog-focus-trap"),i.tabIndex=0,a=i.cloneNode(!1);var s=function(){e.focus()};i.addEventListener("focus",s),a.addEventListener("focus",s),e[0].parentNode.insertBefore(i,e[0]),e.after(a)}function M(e,t){function n(e){for(;e.parentNode;){if(e===document.body)return;for(var t=e.parentNode.children,r=0;r/g.test(e)?""+(e||"")+"":e||""}return'
'+n(e)+"
"}}}var i,a;return o.$inject=["$mdDialog","$mdTheming","$mdConstant"],r.$inject=["$mdDialog","$mdAria","$mdUtil","$mdConstant","$animate","$document","$window","$rootElement","$log","$injector"],e("$mdDialog").setDefaults({methods:["disableParentScroll","hasBackdrop","clickOutsideToClose","escapeToClose","targetEvent","closeTo","openFrom","parent","fullscreen"], + options:r}).addPreset("alert",{methods:["title","htmlContent","textContent","content","ariaLabel","ok","theme","css"],options:o}).addPreset("confirm",{methods:["title","htmlContent","textContent","content","ariaLabel","ok","cancel","theme","css"],options:o}).addPreset("prompt",{methods:["title","htmlContent","textContent","content","placeholder","ariaLabel","ok","cancel","theme","css"],options:o})}t.module("material.components.dialog",["material.core","material.components.backdrop"]).directive("mdDialog",e).provider("$mdDialog",o),e.$inject=["$$rAF","$mdTheming","$mdDialog"],o.$inject=["$$interimElementProvider"]}(),function(){function e(e){return{restrict:"E",link:e}}t.module("material.components.divider",["material.core"]).directive("mdDivider",e),e.$inject=["$mdTheming"]}(),function(){!function(){function e(){return{restrict:"E",require:["^?mdFabSpeedDial","^?mdFabToolbar"],compile:function(e,n){var o=e.children(),r=!1;t.forEach(["","data-","x-"],function(e){r=r||!!o.attr(e+"ng-repeat")}),r?o.addClass("md-fab-action-item"):o.wrap('
')}}}t.module("material.components.fabActions",["material.core"]).directive("mdFabActions",e)}()}(),function(){!function(){function e(e,n,o,r,i,a){function d(){N.direction=N.direction||"down",N.isOpen=N.isOpen||!1,l(),n.addClass("_md-animations-waiting")}function c(){var o=["click","focusin","focusout"];t.forEach(o,function(e){n.on(e,s)}),e.$on("$destroy",function(){t.forEach(o,function(e){n.off(e,s)}),h()})}function s(e){"click"==e.type&&w(e),"focusout"!=e.type||H||(H=a(function(){N.close()},100,!1)),"focusin"==e.type&&H&&(a.cancel(H),H=null)}function l(){N.currentActionIndex=-1}function m(){e.$watch("vm.direction",function(e,t){o.removeClass(n,"md-"+t),o.addClass(n,"md-"+e),l()});var t,r;e.$watch("vm.isOpen",function(e){l(),t&&r||(t=_(),r=x()),e?p():h();var i=e?"md-is-open":"",a=e?"":"md-is-open";t.attr("aria-haspopup",!0),t.attr("aria-expanded",e),r.attr("aria-hidden",!e),o.setClass(n,i,a)})}function u(){n[0].scrollHeight>0?o.addClass(n,"_md-animations-ready").then(function(){n.removeClass("_md-animations-waiting")}):10>S&&(a(u,100),S+=1)}function p(){n.on("keydown",g),r.nextTick(function(){t.element(document).on("click touchend",f)})}function h(){n.off("keydown",g),t.element(document).off("click touchend",f)}function f(e){if(e.target){var t=r.getClosest(e.target,"md-fab-trigger"),n=r.getClosest(e.target,"md-fab-actions");t||n||N.close()}}function g(e){switch(e.which){case i.KEY_CODE.ESCAPE:return N.close(),e.preventDefault(),!1;case i.KEY_CODE.LEFT_ARROW:return M(e),!1;case i.KEY_CODE.UP_ARROW:return y(e),!1;case i.KEY_CODE.RIGHT_ARROW:return C(e),!1;case i.KEY_CODE.DOWN_ARROW:return A(e),!1}}function b(e){E(e,-1)}function v(e){E(e,1)}function E(e,n){var o=$();N.currentActionIndex=N.currentActionIndex+n,N.currentActionIndex=Math.min(o.length-1,N.currentActionIndex),N.currentActionIndex=Math.max(0,N.currentActionIndex);var r=t.element(o[N.currentActionIndex]).children()[0];t.element(r).attr("tabindex",0),r.focus(),e.preventDefault(),e.stopImmediatePropagation()}function $(){var e=x()[0].querySelectorAll(".md-fab-action-item");return t.forEach(e,function(e){t.element(t.element(e).children()[0]).attr("tabindex",-1)}),e}function M(e){"left"===N.direction?v(e):b(e)}function y(e){"down"===N.direction?b(e):v(e)}function C(e){"left"===N.direction?b(e):v(e)}function A(e){"up"===N.direction?b(e):v(e)}function k(e){return r.getClosest(e,"md-fab-trigger")}function T(e){return r.getClosest(e,"md-fab-actions")}function w(e){k(e.target)&&N.toggle(),T(e.target)&&N.close()}function _(){return n.find("md-fab-trigger")}function x(){return n.find("md-fab-actions")}var N=this;N.open=function(){e.$evalAsync("vm.isOpen = true")},N.close=function(){e.$evalAsync("vm.isOpen = false"),n.find("md-fab-trigger")[0].focus()},N.toggle=function(){e.$evalAsync("vm.isOpen = !vm.isOpen")},d(),c(),m();var S=0;u();var H}t.module("material.components.fabShared",["material.core"]).controller("MdFabController",e),e.$inject=["$scope","$element","$animate","$mdUtil","$mdConstant","$timeout"]}()}(),function(){!function(){function n(){function e(e,t){t.prepend('
')}return{restrict:"E",scope:{direction:"@?mdDirection",isOpen:"=?mdOpen"},bindToController:!0,controller:"MdFabController",controllerAs:"vm",link:e}}function o(n){function o(e){n(e,i,!1)}function r(n){if(!n.hasClass("_md-animations-waiting")||n.hasClass("_md-animations-ready")){var o=n[0],r=n.controller("mdFabSpeedDial"),i=o.querySelectorAll(".md-fab-action-item"),a=o.querySelector("md-fab-trigger"),d=o.querySelector("._md-css-variables"),c=parseInt(e.getComputedStyle(d).zIndex);t.forEach(i,function(e,t){var n=e.style;n.transform=n.webkitTransform="",n.transitionDelay="",n.opacity=1,n.zIndex=i.length-t+c}),a.style.zIndex=c+i.length+1,r.isOpen||t.forEach(i,function(e,t){var n,o,i=e.style,d=(a.clientHeight-e.clientHeight)/2,c=(a.clientWidth-e.clientWidth)/2;switch(r.direction){case"up":n=e.scrollHeight*(t+1)+d,o="Y";break;case"down":n=-(e.scrollHeight*(t+1)+d),o="Y";break;case"left":n=e.scrollWidth*(t+1)+c,o="X";break;case"right":n=-(e.scrollWidth*(t+1)+c),o="X"}var s="translate"+o+"("+n+"px)";i.transform=i.webkitTransform=s})}}return{addClass:function(e,t,n){e.hasClass("md-fling")?(r(e),o(n)):n()},removeClass:function(e,t,n){r(e),o(n)}}}function r(n){function o(e){n(e,i,!1)}function r(n){var o=n[0],r=n.controller("mdFabSpeedDial"),i=o.querySelectorAll(".md-fab-action-item"),d=o.querySelector("._md-css-variables"),c=parseInt(e.getComputedStyle(d).zIndex);t.forEach(i,function(e,t){var n=e.style,o=t*a;n.opacity=r.isOpen?1:0,n.transform=n.webkitTransform=r.isOpen?"scale(1)":"scale(0)",n.transitionDelay=(r.isOpen?o:i.length-o)+"ms",n.zIndex=i.length-t+c})}var a=65;return{addClass:function(e,t,n){r(e),o(n)},removeClass:function(e,t,n){r(e),o(n)}}}var i=300;t.module("material.components.fabSpeedDial",["material.core","material.components.fabShared","material.components.fabTrigger","material.components.fabActions"]).directive("mdFabSpeedDial",n).animation(".md-fling",o).animation(".md-scale",r).service("mdFabSpeedDialFlingAnimation",o).service("mdFabSpeedDialScaleAnimation",r),o.$inject=["$timeout"],r.$inject=["$timeout"]}()}(),function(){!function(){function n(){function e(e,t,n){t.addClass("md-fab-toolbar"),t.find("md-fab-trigger").find("button").prepend('
')}return{restrict:"E",transclude:!0,template:'
',scope:{direction:"@?mdDirection",isOpen:"=?mdOpen"},bindToController:!0,controller:"MdFabController",controllerAs:"vm",link:e}}function o(){function n(n,o,r){if(o){var i=n[0],a=n.controller("mdFabToolbar"),d=i.querySelector("._md-fab-toolbar-background"),c=i.querySelector("md-fab-trigger button"),s=i.querySelector("md-toolbar"),l=i.querySelector("md-fab-trigger button md-icon"),m=n.find("md-fab-actions").children();if(c&&d){var u=e.getComputedStyle(c).getPropertyValue("background-color"),p=i.offsetWidth,h=(i.offsetHeight,2*(p/c.offsetWidth));d.style.backgroundColor=u,d.style.borderRadius=p+"px",a.isOpen?(s.style.pointerEvents="initial",d.style.width=c.offsetWidth+"px",d.style.height=c.offsetHeight+"px",d.style.transform="scale("+h+")",d.style.transitionDelay="0ms",l&&(l.style.transitionDelay=".3s"),t.forEach(m,function(e,t){e.style.transitionDelay=25*(m.length-t)+"ms"})):(s.style.pointerEvents="none",d.style.transform="scale(1)",d.style.top="0",n.hasClass("md-right")&&(d.style.left="0",d.style.right=null),n.hasClass("md-left")&&(d.style.right="0",d.style.left=null),d.style.transitionDelay="200ms",l&&(l.style.transitionDelay="0ms"),t.forEach(m,function(e,t){e.style.transitionDelay=200+25*t+"ms"}))}}}return{addClass:function(e,t,o){n(e,t,o),o()},removeClass:function(e,t,o){n(e,t,o),o()}}}t.module("material.components.fabToolbar",["material.core","material.components.fabShared","material.components.fabTrigger","material.components.fabActions"]).directive("mdFabToolbar",n).animation(".md-fab-toolbar",o).service("mdFabToolbarAnimation",o)}()}(),function(){function e(e,o,r,i){function a(n,a,d,c){function s(){for(var e in o.MEDIA)i(e),i.getQuery(o.MEDIA[e]).addListener(C);return i.watchResponsiveAttributes(["md-cols","md-row-height","md-gutter"],d,m)}function l(){c.layoutDelegate=t.noop,A();for(var e in o.MEDIA)i.getQuery(o.MEDIA[e]).removeListener(C)}function m(e){null==e?c.invalidateLayout():i(e)&&c.invalidateLayout()}function u(e){var o=g(),i={tileSpans:b(o),colCount:v(),rowMode:M(),rowHeight:$(),gutter:E()};if(e||!t.equals(i,k)){var d=r(i.colCount,i.tileSpans,o).map(function(e,n){return{grid:{element:a,style:f(i.colCount,n,i.gutter,i.rowMode,i.rowHeight)},tiles:e.map(function(e,r){return{element:t.element(o[r]),style:h(e.position,e.spans,i.colCount,n,i.gutter,i.rowMode,i.rowHeight)}})}}).reflow().performance();n.mdOnLayout({$event:{performance:d}}),k=i}}function p(e){return T+e+w}function h(e,t,n,o,r,i,a){var d=1/n*100,c=(n-1)/n,s=_({share:d,gutterShare:c,gutter:r}),l={left:x({unit:s,offset:e.col,gutter:r}),width:N({unit:s,span:t.col,gutter:r}),paddingTop:"",marginTop:"",top:"",height:""};switch(i){case"fixed":l.top=x({unit:a,offset:e.row,gutter:r}),l.height=N({unit:a,span:t.row,gutter:r});break;case"ratio":var m=d/a,u=_({share:m,gutterShare:c,gutter:r});l.paddingTop=N({unit:u,span:t.row,gutter:r}),l.marginTop=x({unit:u,offset:e.row,gutter:r});break;case"fit":var p=(o-1)/o,m=1/o*100,u=_({share:m,gutterShare:p,gutter:r});l.top=x({unit:u,offset:e.row,gutter:r}),l.height=N({unit:u,span:t.row,gutter:r})}return l}function f(e,t,n,o,r){var i={};switch(o){case"fixed":i.height=N({unit:r,span:t,gutter:n}),i.paddingBottom="";break;case"ratio":var a=1===e?0:(e-1)/e,d=1/e*100,c=d*(1/r),s=_({share:c,gutterShare:a,gutter:n});i.height="",i.paddingBottom=N({unit:s,span:t,gutter:n});break;case"fit":}return i}function g(){return[].filter.call(a.children(),function(e){return"MD-GRID-TILE"==e.tagName&&!e.$$mdDestroyed})}function b(e){return[].map.call(e,function(e){var n=t.element(e).controller("mdGridTile");return{row:parseInt(i.getResponsiveAttribute(n.$attrs,"md-rowspan"),10)||1,col:parseInt(i.getResponsiveAttribute(n.$attrs,"md-colspan"),10)||1}})}function v(){var e=parseInt(i.getResponsiveAttribute(d,"md-cols"),10);if(isNaN(e))throw"md-grid-list: md-cols attribute was not found, or contained a non-numeric value";return e}function E(){return y(i.getResponsiveAttribute(d,"md-gutter")||1)}function $(){var e=i.getResponsiveAttribute(d,"md-row-height");if(!e)throw"md-grid-list: md-row-height attribute was not found";switch(M()){case"fixed":return y(e);case"ratio":var t=e.split(":");return parseFloat(t[0])/parseFloat(t[1]);case"fit":return 0}}function M(){var e=i.getResponsiveAttribute(d,"md-row-height");if(!e)throw"md-grid-list: md-row-height attribute was not found";return"fit"==e?"fit":-1!==e.indexOf(":")?"ratio":"fixed"}function y(e){return/\D$/.test(e)?e:e+"px"}a.attr("role","list"),c.layoutDelegate=u;var C=t.bind(c,c.invalidateLayout),A=s();n.$on("$destroy",l);var k,T=e.startSymbol(),w=e.endSymbol(),_=e(p("share")+"% - ("+p("gutter")+" * "+p("gutterShare")+")"),x=e("calc(("+p("unit")+" + "+p("gutter")+") * "+p("offset")+")"),N=e("calc(("+p("unit")+") * "+p("span")+" + ("+p("span")+" - 1) * "+p("gutter")+")")}return{restrict:"E",controller:n,scope:{mdOnLayout:"&"},link:a}}function n(e){this.layoutInvalidated=!1,this.tilesInvalidated=!1,this.$timeout_=e.nextTick,this.layoutDelegate=t.noop}function o(e){function n(t,n){var o,a,d,c,s,l;return c=e.time(function(){a=r(t,n)}),o={layoutInfo:function(){return a},map:function(t){return s=e.time(function(){var e=o.layoutInfo();d=t(e.positioning,e.rowCount)}),o},reflow:function(t){return l=e.time(function(){var e=t||i;e(d.grid,d.tiles)}),o},performance:function(){return{tileCount:n.length,layoutTime:c,mapTime:s,reflowTime:l,totalTime:c+s+l}}}}function o(e,t){e.element.css(e.style),t.forEach(function(e){e.element.css(e.style)})}function r(e,t){function n(t,n){if(t.col>e)throw"md-grid-list: Tile at position "+n+" has a colspan ("+t.col+") that exceeds the column count ("+e+")";for(var a=0,l=0;l-a=e?o():(a=s.indexOf(0,d),-1!==a&&-1!==(l=i(a+1))?d=l+1:(a=l=0,o()));return r(a,t.col,t.row),d=a+t.col,{col:a,row:c}}function o(){d=0,c++,r(0,e,-1)}function r(e,t,n){for(var o=e;e+t>o;o++)s[o]=Math.max(s[o]+n,0)}function i(e){var t;for(t=e;tn;n++)t.push(0);return t}var d=0,c=0,s=a();return{positioning:t.map(function(e,t){return{spans:e,position:n(e,t)}}),rowCount:c+Math.max.apply(Math,s)}}var i=o;return n.animateWith=function(e){i=t.isFunction(e)?e:o},n}function r(e){function n(n,o,r,i){o.attr("role","listitem");var a=e.watchResponsiveAttributes(["md-colspan","md-rowspan"],r,t.bind(i,i.invalidateLayout));i.invalidateTiles(),n.$on("$destroy",function(){o[0].$$mdDestroyed=!0,a(),i.invalidateLayout()}),t.isDefined(n.$parent.$index)&&n.$watch(function(){return n.$parent.$index},function(e,t){e!==t&&i.invalidateTiles()})}return{restrict:"E",require:"^mdGridList",template:"
",transclude:!0,scope:{},controller:["$attrs",function(e){this.$attrs=e}],link:n}}function i(){return{template:"
",transclude:!0}}t.module("material.components.gridList",["material.core"]).directive("mdGridList",e).directive("mdGridTile",r).directive("mdGridTileFooter",i).directive("mdGridTileHeader",i).factory("$mdGridLayout",o),e.$inject=["$interpolate","$mdConstant","$mdGridLayout","$mdMedia"],n.$inject=["$mdUtil"],n.prototype={invalidateTiles:function(){this.tilesInvalidated=!0,this.invalidateLayout()},invalidateLayout:function(){this.layoutInvalidated||(this.layoutInvalidated=!0,this.$timeout_(t.bind(this,this.layout)))},layout:function(){try{this.layoutDelegate(this.tilesInvalidated)}finally{this.layoutInvalidated=!1,this.tilesInvalidated=!1}}},o.$inject=["$mdUtil"],r.$inject=["$mdMedia"]}(),function(){!function(){function e(){return{restrict:"E",require:["^?mdFabSpeedDial","^?mdFabToolbar"]}}t.module("material.components.fabTrigger",["material.core"]).directive("mdFabTrigger",e)}()}(),function(){t.module("material.components.icon",["material.core"])}(),function(){function n(e,t){function n(t,n){e(n);var o=n[0].querySelector(i),r=n[0].querySelector(a);o&&n.addClass("md-icon-left"),r&&n.addClass("md-icon-right")}function o(e,n,o,r){var i=this;i.isErrorGetter=o.mdIsError&&t(o.mdIsError),i.delegateClick=function(){i.input.focus()},i.element=n,i.setFocused=function(e){n.toggleClass("md-input-focused",!!e)},i.setHasValue=function(e){n.toggleClass("md-input-has-value",!!e)},i.setHasPlaceholder=function(e){n.toggleClass("md-input-has-placeholder",!!e)},i.setInvalid=function(e){e?r.addClass(n,"md-input-invalid"):r.removeClass(n,"md-input-invalid")},e.$watch(function(){return i.label&&i.input},function(e){e&&!i.label.attr("for")&&i.label.attr("for",i.input.attr("id"))})}var r=["INPUT","TEXTAREA","SELECT","MD-SELECT"],i=r.reduce(function(e,t){return e.concat(["md-icon ~ "+t,".md-icon ~ "+t])},[]).join(","),a=r.reduce(function(e,t){return e.concat([t+" ~ md-icon",t+" ~ .md-icon"])},[]).join(",");return o.$inject=["$scope","$element","$attrs","$animate"],{restrict:"E",link:n,controller:o}}function o(){return{restrict:"E",require:"^?mdInputContainer",link:function(e,t,n,o){!o||n.mdNoFloat||t.hasClass("_md-container-ignore")||(o.label=t,e.$on("$destroy",function(){o.label=null}))}}}function r(e,n,o,r){function i(i,a,d,c){function s(e){return p.setHasValue(!f.$isEmpty(e)),e}function l(){p.label&&d.$observe("required",function(e){p.label.toggleClass("md-required",e&&!b)})}function m(){p.setHasValue(a.val().length>0||(a[0].validity||{}).badInput)}function u(){function o(){if(a.addClass("md-no-flex").attr("rows",1),m){u||(p.style.minHeight=0,u=a.prop("clientHeight"),p.style.minHeight=null);var e=Math.round(Math.round(c()/u)),t=Math.min(e,m);a.css("height",u*t+"px").attr("rows",t).toggleClass("_md-textarea-scrollable",e>=m)}else{a.css("height","auto"),p.scrollTop=0;var n=c();n&&a.css("height",n+"px")}a.removeClass("md-no-flex")}function c(){var e=p.offsetHeight,t=p.scrollHeight-e;return e+(t>0?t:0)}function s(e){p.scrollTop=0;var t=p.scrollHeight-p.offsetHeight,n=p.offsetHeight+t;p.style.height=n+"px"}function l(e){return o(),e}if(!d.hasOwnProperty("mdNoAutogrow")){var m=d.hasOwnProperty("rows")?parseInt(d.rows):NaN,u=null,p=a[0];if(r(function(){e.nextTick(o)},10,!1),h?(f.$formatters.unshift(l),f.$parsers.unshift(l)):a.on("input",o),m||a.attr("rows",1).on("scroll",s),t.element(n).on("resize",o),i.$on("$destroy",function(){t.element(n).off("resize",o)}),d.hasOwnProperty("mdDetectHidden")){var g=function(){var e=!1;return function(){var t=0===p.offsetHeight;t===!1&&e===!0&&o(),e=t}}();i.$watch(function(){return e.nextTick(g,!1),!0})}}}var p=c[0],h=!!c[1],f=c[1]||e.fakeNgModel(),g=t.isDefined(d.readonly),b=e.parseAttributeBoolean(d.mdNoAsterisk);if(p){if("hidden"===d.type)return void a.attr("aria-hidden","true");if(p.input)throw new Error(" can only have *one* , + + + + + + + + + + + + + + + + + + + + + + + + + +
DirectiveHowSourceRendered
ng-bind-htmlAutomatically uses $sanitize
<div ng-bind-html="snippet">
</div>
ng-bind-htmlBypass $sanitize by explicitly trusting the dangerous value +
<div ng-bind-html="deliberatelyTrustDangerousSnippet()">
+</div>
+
ng-bindAutomatically escapes
<div ng-bind="snippet">
</div>
+
+ + + it('should sanitize the html snippet by default', function() { + expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()). + toBe('

an html\nclick here\nsnippet

'); + }); + + it('should inline raw snippet if bound to a trusted value', function() { + expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()). + toBe("

an html\n" + + "click here\n" + + "snippet

"); + }); + + it('should escape snippet without any filter', function() { + expect(element(by.css('#bind-default div')).getInnerHtml()). + toBe("<p style=\"color:blue\">an html\n" + + "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + + "snippet</p>"); + }); + + it('should update', function() { + element(by.model('snippet')).clear(); + element(by.model('snippet')).sendKeys('new text'); + expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()). + toBe('new text'); + expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe( + 'new text'); + expect(element(by.css('#bind-default div')).getInnerHtml()).toBe( + "new <b onclick=\"alert(1)\">text</b>"); + }); +
+ + */ + + +/** + * @ngdoc provider + * @name $sanitizeProvider + * + * @description + * Creates and configures {@link $sanitize} instance. + */ +function $SanitizeProvider() { + var svgEnabled = false; + + this.$get = ['$$sanitizeUri', function($$sanitizeUri) { + if (svgEnabled) { + angular.extend(validElements, svgElements); + } + return function(html) { + var buf = []; + htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) { + return !/^unsafe:/.test($$sanitizeUri(uri, isImage)); + })); + return buf.join(''); + }; + }]; + + + /** + * @ngdoc method + * @name $sanitizeProvider#enableSvg + * @kind function + * + * @description + * Enables a subset of svg to be supported by the sanitizer. + * + *
+ *

By enabling this setting without taking other precautions, you might expose your + * application to click-hijacking attacks. In these attacks, sanitized svg elements could be positioned + * outside of the containing element and be rendered over other elements on the page (e.g. a login + * link). Such behavior can then result in phishing incidents.

+ * + *

To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg + * tags within the sanitized content:

+ * + *
+ * + *

+   *   .rootOfTheIncludedContent svg {
+   *     overflow: hidden !important;
+   *   }
+   *   
+ *
+ * + * @param {boolean=} flag Enable or disable SVG support in the sanitizer. + * @returns {boolean|ng.$sanitizeProvider} Returns the currently configured value if called + * without an argument or self for chaining otherwise. + */ + this.enableSvg = function(enableSvg) { + if (angular.isDefined(enableSvg)) { + svgEnabled = enableSvg; + return this; + } else { + return svgEnabled; + } + }; +} + +function sanitizeText(chars) { + var buf = []; + var writer = htmlSanitizeWriter(buf, angular.noop); + writer.chars(chars); + return buf.join(''); +} + + +// Regular Expressions for parsing tags and attributes +var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, + // Match everything outside of normal chars and " (quote character) + NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g; + + +// Good source of info about elements and attributes +// http://dev.w3.org/html5/spec/Overview.html#semantics +// http://simon.html5.org/html-elements + +// Safe Void Elements - HTML5 +// http://dev.w3.org/html5/spec/Overview.html#void-elements +var voidElements = toMap("area,br,col,hr,img,wbr"); + +// Elements that you can, intentionally, leave open (and which close themselves) +// http://dev.w3.org/html5/spec/Overview.html#optional-tags +var optionalEndTagBlockElements = toMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"), + optionalEndTagInlineElements = toMap("rp,rt"), + optionalEndTagElements = angular.extend({}, + optionalEndTagInlineElements, + optionalEndTagBlockElements); + +// Safe Block Elements - HTML5 +var blockElements = angular.extend({}, optionalEndTagBlockElements, toMap("address,article," + + "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," + + "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul")); + +// Inline Elements - HTML5 +var inlineElements = angular.extend({}, optionalEndTagInlineElements, toMap("a,abbr,acronym,b," + + "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," + + "samp,small,span,strike,strong,sub,sup,time,tt,u,var")); + +// SVG Elements +// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements +// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted. +// They can potentially allow for arbitrary javascript to be executed. See #11290 +var svgElements = toMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," + + "hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," + + "radialGradient,rect,stop,svg,switch,text,title,tspan"); + +// Blocked Elements (will be stripped) +var blockedElements = toMap("script,style"); + +var validElements = angular.extend({}, + voidElements, + blockElements, + inlineElements, + optionalEndTagElements); + +//Attributes that have href and hence need to be sanitized +var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href"); + +var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' + + 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' + + 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' + + 'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' + + 'valign,value,vspace,width'); + +// SVG attributes (without "id" and "name" attributes) +// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes +var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' + + 'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' + + 'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' + + 'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' + + 'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' + + 'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' + + 'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' + + 'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' + + 'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' + + 'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' + + 'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' + + 'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' + + 'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' + + 'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' + + 'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true); + +var validAttrs = angular.extend({}, + uriAttrs, + svgAttrs, + htmlAttrs); + +function toMap(str, lowercaseKeys) { + var obj = {}, items = str.split(','), i; + for (i = 0; i < items.length; i++) { + obj[lowercaseKeys ? angular.lowercase(items[i]) : items[i]] = true; + } + return obj; +} + +var inertBodyElement; +(function(window) { + var doc; + if (window.document && window.document.implementation) { + doc = window.document.implementation.createHTMLDocument("inert"); + } else { + throw $sanitizeMinErr('noinert', "Can't create an inert html document"); + } + var docElement = doc.documentElement || doc.getDocumentElement(); + var bodyElements = docElement.getElementsByTagName('body'); + + // usually there should be only one body element in the document, but IE doesn't have any, so we need to create one + if (bodyElements.length === 1) { + inertBodyElement = bodyElements[0]; + } else { + var html = doc.createElement('html'); + inertBodyElement = doc.createElement('body'); + html.appendChild(inertBodyElement); + doc.appendChild(html); + } +})(window); + +/** + * @example + * htmlParser(htmlString, { + * start: function(tag, attrs) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * }); + * + * @param {string} html string + * @param {object} handler + */ +function htmlParser(html, handler) { + if (html === null || html === undefined) { + html = ''; + } else if (typeof html !== 'string') { + html = '' + html; + } + inertBodyElement.innerHTML = html; + + //mXSS protection + var mXSSAttempts = 5; + do { + if (mXSSAttempts === 0) { + throw $sanitizeMinErr('uinput', "Failed to sanitize html because the input is unstable"); + } + mXSSAttempts--; + + // strip custom-namespaced attributes on IE<=11 + if (window.document.documentMode) { + stripCustomNsAttrs(inertBodyElement); + } + html = inertBodyElement.innerHTML; //trigger mXSS + inertBodyElement.innerHTML = html; + } while (html !== inertBodyElement.innerHTML); + + var node = inertBodyElement.firstChild; + while (node) { + switch (node.nodeType) { + case 1: // ELEMENT_NODE + handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes)); + break; + case 3: // TEXT NODE + handler.chars(node.textContent); + break; + } + + var nextNode; + if (!(nextNode = node.firstChild)) { + if (node.nodeType == 1) { + handler.end(node.nodeName.toLowerCase()); + } + nextNode = node.nextSibling; + if (!nextNode) { + while (nextNode == null) { + node = node.parentNode; + if (node === inertBodyElement) break; + nextNode = node.nextSibling; + if (node.nodeType == 1) { + handler.end(node.nodeName.toLowerCase()); + } + } + } + } + node = nextNode; + } + + while (node = inertBodyElement.firstChild) { + inertBodyElement.removeChild(node); + } +} + +function attrToMap(attrs) { + var map = {}; + for (var i = 0, ii = attrs.length; i < ii; i++) { + var attr = attrs[i]; + map[attr.name] = attr.value; + } + return map; +} + + +/** + * Escapes all potentially dangerous characters, so that the + * resulting string can be safely inserted into attribute or + * element text. + * @param value + * @returns {string} escaped text + */ +function encodeEntities(value) { + return value. + replace(/&/g, '&'). + replace(SURROGATE_PAIR_REGEXP, function(value) { + var hi = value.charCodeAt(0); + var low = value.charCodeAt(1); + return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';'; + }). + replace(NON_ALPHANUMERIC_REGEXP, function(value) { + return '&#' + value.charCodeAt(0) + ';'; + }). + replace(//g, '>'); +} + +/** + * create an HTML/XML writer which writes to buffer + * @param {Array} buf use buf.join('') to get out sanitized html string + * @returns {object} in the form of { + * start: function(tag, attrs) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * } + */ +function htmlSanitizeWriter(buf, uriValidator) { + var ignoreCurrentElement = false; + var out = angular.bind(buf, buf.push); + return { + start: function(tag, attrs) { + tag = angular.lowercase(tag); + if (!ignoreCurrentElement && blockedElements[tag]) { + ignoreCurrentElement = tag; + } + if (!ignoreCurrentElement && validElements[tag] === true) { + out('<'); + out(tag); + angular.forEach(attrs, function(value, key) { + var lkey=angular.lowercase(key); + var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background'); + if (validAttrs[lkey] === true && + (uriAttrs[lkey] !== true || uriValidator(value, isImage))) { + out(' '); + out(key); + out('="'); + out(encodeEntities(value)); + out('"'); + } + }); + out('>'); + } + }, + end: function(tag) { + tag = angular.lowercase(tag); + if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) { + out(''); + } + if (tag == ignoreCurrentElement) { + ignoreCurrentElement = false; + } + }, + chars: function(chars) { + if (!ignoreCurrentElement) { + out(encodeEntities(chars)); + } + } + }; +} + + +/** + * When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare + * ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want + * to allow any of these custom attributes. This method strips them all. + * + * @param node Root element to process + */ +function stripCustomNsAttrs(node) { + if (node.nodeType === window.Node.ELEMENT_NODE) { + var attrs = node.attributes; + for (var i = 0, l = attrs.length; i < l; i++) { + var attrNode = attrs[i]; + var attrName = attrNode.name.toLowerCase(); + if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) { + node.removeAttributeNode(attrNode); + i--; + l--; + } + } + } + + var nextNode = node.firstChild; + if (nextNode) { + stripCustomNsAttrs(nextNode); + } + + nextNode = node.nextSibling; + if (nextNode) { + stripCustomNsAttrs(nextNode); + } +} + + + +// define ngSanitize module and register $sanitize service +angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider); + +/* global sanitizeText: false */ + +/** + * @ngdoc filter + * @name linky + * @kind function + * + * @description + * Finds links in text input and turns them into html links. Supports `http/https/ftp/mailto` and + * plain email address links. + * + * Requires the {@link ngSanitize `ngSanitize`} module to be installed. + * + * @param {string} text Input text. + * @param {string} target Window (`_blank|_self|_parent|_top`) or named frame to open links in. + * @param {object|function(url)} [attributes] Add custom attributes to the link element. + * + * Can be one of: + * + * - `object`: A map of attributes + * - `function`: Takes the url as a parameter and returns a map of attributes + * + * If the map of attributes contains a value for `target`, it overrides the value of + * the target parameter. + * + * + * @returns {string} Html-linkified and {@link $sanitize sanitized} text. + * + * @usage + + * + * @example + + +
+ Snippet: + + + + + + + + + + + + + + + + + + + + + + + + + + +
FilterSourceRendered
linky filter +
<div ng-bind-html="snippet | linky">
</div>
+
+
+
linky target +
<div ng-bind-html="snippetWithSingleURL | linky:'_blank'">
</div>
+
+
+
linky custom attributes +
<div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}">
</div>
+
+
+
no filter
<div ng-bind="snippet">
</div>
+ + + angular.module('linkyExample', ['ngSanitize']) + .controller('ExampleController', ['$scope', function($scope) { + $scope.snippet = + 'Pretty text with some links:\n'+ + 'http://angularjs.org/,\n'+ + 'mailto:us@somewhere.org,\n'+ + 'another@somewhere.org,\n'+ + 'and one more: ftp://127.0.0.1/.'; + $scope.snippetWithSingleURL = 'http://angularjs.org/'; + }]); + + + it('should linkify the snippet with urls', function() { + expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). + toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' + + 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); + expect(element.all(by.css('#linky-filter a')).count()).toEqual(4); + }); + + it('should not linkify snippet without the linky filter', function() { + expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()). + toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' + + 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); + expect(element.all(by.css('#escaped-html a')).count()).toEqual(0); + }); + + it('should update', function() { + element(by.model('snippet')).clear(); + element(by.model('snippet')).sendKeys('new http://link.'); + expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). + toBe('new http://link.'); + expect(element.all(by.css('#linky-filter a')).count()).toEqual(1); + expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()) + .toBe('new http://link.'); + }); + + it('should work with the target property', function() { + expect(element(by.id('linky-target')). + element(by.binding("snippetWithSingleURL | linky:'_blank'")).getText()). + toBe('http://angularjs.org/'); + expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank'); + }); + + it('should optionally add custom attributes', function() { + expect(element(by.id('linky-custom-attributes')). + element(by.binding("snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}")).getText()). + toBe('http://angularjs.org/'); + expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow'); + }); + + + */ +angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) { + var LINKY_URL_REGEXP = + /((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i, + MAILTO_REGEXP = /^mailto:/i; + + var linkyMinErr = angular.$$minErr('linky'); + var isString = angular.isString; + + return function(text, target, attributes) { + if (text == null || text === '') return text; + if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text); + + var match; + var raw = text; + var html = []; + var url; + var i; + while ((match = raw.match(LINKY_URL_REGEXP))) { + // We can not end in these as they are sometimes found at the end of the sentence + url = match[0]; + // if we did not match ftp/http/www/mailto then assume mailto + if (!match[2] && !match[4]) { + url = (match[3] ? 'http://' : 'mailto:') + url; + } + i = match.index; + addText(raw.substr(0, i)); + addLink(url, match[0].replace(MAILTO_REGEXP, '')); + raw = raw.substring(i + match[0].length); + } + addText(raw); + return $sanitize(html.join('')); + + function addText(text) { + if (!text) { + return; + } + html.push(sanitizeText(text)); + } + + function addLink(url, text) { + var key; + html.push(''); + addText(text); + html.push(''); + } + }; +}]); + + +})(window, window.angular); diff --git a/src/main/resources/static/vendor/angular/angular-sanitize.min.js b/src/main/resources/static/vendor/angular/angular-sanitize.min.js new file mode 100644 index 0000000..637f535 --- /dev/null +++ b/src/main/resources/static/vendor/angular/angular-sanitize.min.js @@ -0,0 +1,15 @@ +/* + AngularJS v1.5.6 + (c) 2010-2016 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(n,e){'use strict';function B(a){var c=[];w(c,e.noop).chars(a);return c.join("")}function h(a,c){var b={},d=a.split(","),l;for(l=0;l/g,">")}function w(a,c){var b=!1,d=e.bind(a,a.push);return{start:function(a,f){a=e.lowercase(a);!b&&G[a]&&(b=a);b||!0!==u[a]||(d("<"),d(a),e.forEach(f,function(b,f){var g=e.lowercase(f),h="img"===a&&"src"===g||"background"===g;!0!==H[g]||!0===z[g]&&!c(b,h)||(d(" "),d(f),d('="'),d(y(b)),d('"'))}),d(">"))},end:function(a){a=e.lowercase(a);b||!0!==u[a]||!0===A[a]||(d(""));a== +b&&(b=!1)},chars:function(a){b||d(y(a))}}}function t(a){if(a.nodeType===n.Node.ELEMENT_NODE)for(var c=a.attributes,b=0,d=c.length;b"\u201d\u2019]/i,b=/^mailto:/i,d=e.$$minErr("linky"),g=e.isString;return function(f,h,m){function k(a){a&&p.push(B(a))}function q(a,b){var c;p.push("');k(b);p.push("")}if(null==f||""===f)return f;if(!g(f))throw d("notstring",f);for(var r=f,p=[],s,n;f=r.match(c);)s=f[0],f[2]||f[4]||(s=(f[3]?"http://":"mailto:")+s),n=f.index,k(r.substr(0,n)),q(s,f[0].replace(b,"")),r=r.substring(n+f[0].length);k(r);return a(p.join(""))}}])})(window,window.angular); +//# sourceMappingURL=angular-sanitize.min.js.map diff --git a/src/main/resources/static/vendor/angular/angular-sanitize.min.js.map b/src/main/resources/static/vendor/angular/angular-sanitize.min.js.map new file mode 100644 index 0000000..efc2cff --- /dev/null +++ b/src/main/resources/static/vendor/angular/angular-sanitize.min.js.map @@ -0,0 +1,8 @@ +{ +"version":3, +"file":"angular-sanitize.min.js", +"lineCount":14, +"mappings":"A;;;;;aAKC,SAAQ,CAACA,CAAD,CAASC,CAAT,CAAkB,CAsM3BC,QAASA,EAAY,CAACC,CAAD,CAAQ,CAC3B,IAAIC,EAAM,EACGC,EAAAC,CAAmBF,CAAnBE,CAAwBL,CAAAM,KAAxBD,CACbH,MAAA,CAAaA,CAAb,CACA,OAAOC,EAAAI,KAAA,CAAS,EAAT,CAJoB,CAyF7BC,QAASA,EAAK,CAACC,CAAD,CAAMC,CAAN,CAAqB,CAAA,IAC7BC,EAAM,EADuB,CACnBC,EAAQH,CAAAI,MAAA,CAAU,GAAV,CADW,CACKC,CACtC,KAAKA,CAAL,CAAS,CAAT,CAAYA,CAAZ,CAAgBF,CAAAG,OAAhB,CAA8BD,CAAA,EAA9B,CACEH,CAAA,CAAID,CAAA,CAAgBV,CAAAgB,UAAA,CAAkBJ,CAAA,CAAME,CAAN,CAAlB,CAAhB,CAA8CF,CAAA,CAAME,CAAN,CAAlD,CAAA,CAA8D,CAAA,CAEhE,OAAOH,EAL0B,CA0CnCM,QAASA,EAAU,CAACC,CAAD,CAAOC,CAAP,CAAgB,CACpB,IAAb,GAAID,CAAJ,EAA8BE,IAAAA,EAA9B,GAAqBF,CAArB,CACEA,CADF,CACS,EADT,CAE2B,QAF3B,GAEW,MAAOA,EAFlB,GAGEA,CAHF,CAGS,EAHT,CAGcA,CAHd,CAKAG,EAAAC,UAAA,CAA6BJ,CAG7B,KAAIK,EAAe,CACnB,GAAG,CACD,GAAqB,CAArB,GAAIA,CAAJ,CACE,KAAMC,EAAA,CAAgB,QAAhB,CAAN,CAEFD,CAAA,EAGIxB,EAAA0B,SAAAC,aAAJ,EACEC,CAAA,CAAmBN,CAAnB,CAEFH,EAAA,CAAOG,CAAAC,UACPD,EAAAC,UAAA,CAA6BJ,CAX5B,CAAH,MAYSA,CAZT,GAYkBG,CAAAC,UAZlB,CAeA,KADIM,CACJ,CADWP,CAAAQ,WACX,CAAOD,CAAP,CAAA,CAAa,CACX,OAAQA,CAAAE,SAAR,EACE,KAAK,CAAL,CACEX,CAAAY,MAAA,CAAcH,CAAAI,SAAAC,YAAA,EAAd,CAA2CC,CAAA,CAAUN,CAAAO,WAAV,CAA3C,CACA;KACF,MAAK,CAAL,CACEhB,CAAAjB,MAAA,CAAc0B,CAAAQ,YAAd,CALJ,CASA,IAAIC,CACJ,IAAM,EAAAA,CAAA,CAAWT,CAAAC,WAAX,CAAN,GACuB,CAIhBQ,EAJDT,CAAAE,SAICO,EAHHlB,CAAAmB,IAAA,CAAYV,CAAAI,SAAAC,YAAA,EAAZ,CAGGI,CADLA,CACKA,CADMT,CAAAW,YACNF,CAAAA,CAAAA,CALP,EAMI,IAAA,CAAmB,IAAnB,EAAOA,CAAP,CAAA,CAAyB,CACvBT,CAAA,CAAOA,CAAAY,WACP,IAAIZ,CAAJ,GAAaP,CAAb,CAA+B,KAC/BgB,EAAA,CAAWT,CAAAW,YACU,EAArB,EAAIX,CAAAE,SAAJ,EACEX,CAAAmB,IAAA,CAAYV,CAAAI,SAAAC,YAAA,EAAZ,CALqB,CAU7BL,CAAA,CAAOS,CA3BI,CA8Bb,IAAA,CAAOT,CAAP,CAAcP,CAAAQ,WAAd,CAAA,CACER,CAAAoB,YAAA,CAA6Bb,CAA7B,CAxD+B,CA4DnCM,QAASA,EAAS,CAACQ,CAAD,CAAQ,CAExB,IADA,IAAIC,EAAM,EAAV,CACS7B,EAAI,CADb,CACgB8B,EAAKF,CAAA3B,OAArB,CAAmCD,CAAnC,CAAuC8B,CAAvC,CAA2C9B,CAAA,EAA3C,CAAgD,CAC9C,IAAI+B,EAAOH,CAAA,CAAM5B,CAAN,CACX6B,EAAA,CAAIE,CAAAC,KAAJ,CAAA,CAAiBD,CAAAE,MAF6B,CAIhD,MAAOJ,EANiB,CAiB1BK,QAASA,EAAc,CAACD,CAAD,CAAQ,CAC7B,MAAOA,EAAAE,QAAA,CACG,IADH,CACS,OADT,CAAAA,QAAA,CAEGC,CAFH,CAE0B,QAAQ,CAACH,CAAD,CAAQ,CAC7C,IAAII,EAAKJ,CAAAK,WAAA,CAAiB,CAAjB,CACLC,EAAAA,CAAMN,CAAAK,WAAA,CAAiB,CAAjB,CACV,OAAO,IAAP,EAAgC,IAAhC,EAAiBD,CAAjB,CAAsB,KAAtB;CAA0CE,CAA1C,CAAgD,KAAhD,EAA0D,KAA1D,EAAqE,GAHxB,CAF1C,CAAAJ,QAAA,CAOGK,CAPH,CAO4B,QAAQ,CAACP,CAAD,CAAQ,CAC/C,MAAO,IAAP,CAAcA,CAAAK,WAAA,CAAiB,CAAjB,CAAd,CAAoC,GADW,CAP5C,CAAAH,QAAA,CAUG,IAVH,CAUS,MAVT,CAAAA,QAAA,CAWG,IAXH,CAWS,MAXT,CADsB,CAyB/B7C,QAASA,EAAkB,CAACD,CAAD,CAAMoD,CAAN,CAAoB,CAC7C,IAAIC,EAAuB,CAAA,CAA3B,CACIC,EAAMzD,CAAA0D,KAAA,CAAavD,CAAb,CAAkBA,CAAAwD,KAAlB,CACV,OAAO,CACL5B,MAAOA,QAAQ,CAAC6B,CAAD,CAAMlB,CAAN,CAAa,CAC1BkB,CAAA,CAAM5D,CAAAgB,UAAA,CAAkB4C,CAAlB,CACDJ,EAAAA,CAAL,EAA6BK,CAAA,CAAgBD,CAAhB,CAA7B,GACEJ,CADF,CACyBI,CADzB,CAGKJ,EAAL,EAAoD,CAAA,CAApD,GAA6BM,CAAA,CAAcF,CAAd,CAA7B,GACEH,CAAA,CAAI,GAAJ,CAcA,CAbAA,CAAA,CAAIG,CAAJ,CAaA,CAZA5D,CAAA+D,QAAA,CAAgBrB,CAAhB,CAAuB,QAAQ,CAACK,CAAD,CAAQiB,CAAR,CAAa,CAC1C,IAAIC,EAAKjE,CAAAgB,UAAA,CAAkBgD,CAAlB,CAAT,CACIE,EAAmB,KAAnBA,GAAWN,CAAXM,EAAqC,KAArCA,GAA4BD,CAA5BC,EAAyD,YAAzDA,GAAgDD,CAC3B,EAAA,CAAzB,GAAIE,CAAA,CAAWF,CAAX,CAAJ,EACsB,CAAA,CADtB,GACGG,CAAA,CAASH,CAAT,CADH,EAC8B,CAAAV,CAAA,CAAaR,CAAb,CAAoBmB,CAApB,CAD9B,GAEET,CAAA,CAAI,GAAJ,CAIA,CAHAA,CAAA,CAAIO,CAAJ,CAGA,CAFAP,CAAA,CAAI,IAAJ,CAEA,CADAA,CAAA,CAAIT,CAAA,CAAeD,CAAf,CAAJ,CACA,CAAAU,CAAA,CAAI,GAAJ,CANF,CAH0C,CAA5C,CAYA,CAAAA,CAAA,CAAI,GAAJ,CAfF,CAL0B,CADvB,CAwBLnB,IAAKA,QAAQ,CAACsB,CAAD,CAAM,CACjBA,CAAA,CAAM5D,CAAAgB,UAAA,CAAkB4C,CAAlB,CACDJ,EAAL,EAAoD,CAAA,CAApD,GAA6BM,CAAA,CAAcF,CAAd,CAA7B,EAAkF,CAAA,CAAlF,GAA4DS,CAAA,CAAaT,CAAb,CAA5D,GACEH,CAAA,CAAI,IAAJ,CAEA,CADAA,CAAA,CAAIG,CAAJ,CACA,CAAAH,CAAA,CAAI,GAAJ,CAHF,CAKIG,EAAJ;AAAWJ,CAAX,GACEA,CADF,CACyB,CAAA,CADzB,CAPiB,CAxBd,CAmCLtD,MAAOA,QAAQ,CAACA,CAAD,CAAQ,CAChBsD,CAAL,EACEC,CAAA,CAAIT,CAAA,CAAe9C,CAAf,CAAJ,CAFmB,CAnClB,CAHsC,CAsD/CyB,QAASA,EAAkB,CAACC,CAAD,CAAO,CAChC,GAAIA,CAAAE,SAAJ,GAAsB/B,CAAAuE,KAAAC,aAAtB,CAEE,IADA,IAAI7B,EAAQd,CAAAO,WAAZ,CACSrB,EAAI,CADb,CACgB0D,EAAI9B,CAAA3B,OAApB,CAAkCD,CAAlC,CAAsC0D,CAAtC,CAAyC1D,CAAA,EAAzC,CAA8C,CAC5C,IAAI2D,EAAW/B,CAAA,CAAM5B,CAAN,CAAf,CACI4D,EAAWD,CAAA3B,KAAAb,YAAA,EACf,IAAiB,WAAjB,GAAIyC,CAAJ,EAAoE,CAApE,GAAgCA,CAAAC,YAAA,CAAqB,MAArB,CAA6B,CAA7B,CAAhC,CACE/C,CAAAgD,oBAAA,CAAyBH,CAAzB,CAEA,CADA3D,CAAA,EACA,CAAA0D,CAAA,EAN0C,CAYhD,CADInC,CACJ,CADeT,CAAAC,WACf,GACEF,CAAA,CAAmBU,CAAnB,CAIF,EADAA,CACA,CADWT,CAAAW,YACX,GACEZ,CAAA,CAAmBU,CAAnB,CArB8B,CAxdlC,IAAIb,EAAkBxB,CAAA6E,SAAA,CAAiB,WAAjB,CAAtB,CAkMI3B,EAAwB,iCAlM5B,CAoMEI,EAA0B,eApM5B,CA6MIe,EAAe7D,CAAA,CAAM,wBAAN,CA7MnB,CAiNIsE,EAA8BtE,CAAA,CAAM,gDAAN,CAjNlC,CAkNIuE,EAA+BvE,CAAA,CAAM,OAAN,CAlNnC,CAmNIwE,EAAyBhF,CAAAiF,OAAA,CAAe,EAAf,CACeF,CADf,CAEeD,CAFf,CAnN7B;AAwNII,EAAgBlF,CAAAiF,OAAA,CAAe,EAAf,CAAmBH,CAAnB,CAAgDtE,CAAA,CAAM,qKAAN,CAAhD,CAxNpB,CA6NI2E,EAAiBnF,CAAAiF,OAAA,CAAe,EAAf,CAAmBF,CAAnB,CAAiDvE,CAAA,CAAM,2JAAN,CAAjD,CA7NrB,CAqOI4E,EAAc5E,CAAA,CAAM,wNAAN,CArOlB;AA0OIqD,EAAkBrD,CAAA,CAAM,cAAN,CA1OtB,CA4OIsD,EAAgB9D,CAAAiF,OAAA,CAAe,EAAf,CACeZ,CADf,CAEea,CAFf,CAGeC,CAHf,CAIeH,CAJf,CA5OpB,CAmPIZ,EAAW5D,CAAA,CAAM,8CAAN,CAnPf,CAqPI6E,EAAY7E,CAAA,CAAM,kTAAN,CArPhB,CA6PI8E,EAAW9E,CAAA,CAAM,guCAAN;AAcoE,CAAA,CAdpE,CA7Pf,CA6QI2D,EAAanE,CAAAiF,OAAA,CAAe,EAAf,CACeb,CADf,CAEekB,CAFf,CAGeD,CAHf,CA7QjB,CA0RIhE,CACH,UAAQ,CAACtB,CAAD,CAAS,CAEhB,GAAIA,CAAA0B,SAAJ,EAAuB1B,CAAA0B,SAAA8D,eAAvB,CACEC,CAAA,CAAMzF,CAAA0B,SAAA8D,eAAAE,mBAAA,CAAkD,OAAlD,CADR,KAGE,MAAMjE,EAAA,CAAgB,SAAhB,CAAN,CAGF,IAAIkE,EAAeC,CADFH,CAAAI,gBACED,EADqBH,CAAAK,mBAAA,EACrBF,sBAAA,CAAgC,MAAhC,CAGS,EAA5B,GAAID,CAAA3E,OAAJ,CACEM,CADF,CACqBqE,CAAA,CAAa,CAAb,CADrB,EAGMxE,CAGJ,CAHWsE,CAAAM,cAAA,CAAkB,MAAlB,CAGX,CAFAzE,CAEA,CAFmBmE,CAAAM,cAAA,CAAkB,MAAlB,CAEnB,CADA5E,CAAA6E,YAAA,CAAiB1E,CAAjB,CACA,CAAAmE,CAAAO,YAAA,CAAgB7E,CAAhB,CANF,CAXgB,CAAjB,CAAD,CAmBGnB,CAnBH,CAyNAC,EAAAgG,OAAA,CAAe,YAAf,CAA6B,EAA7B,CAAAC,SAAA,CAA0C,WAA1C,CApXAC,QAA0B,EAAG,CAC3B,IAAIC,EAAa,CAAA,CAEjB,KAAAC,KAAA,CAAY,CAAC,eAAD,CAAkB,QAAQ,CAACC,CAAD,CAAgB,CAChDF,CAAJ,EACEnG,CAAAiF,OAAA,CAAenB,CAAf,CAA8BsB,CAA9B,CAEF,OAAO,SAAQ,CAAClE,CAAD,CAAO,CACpB,IAAIf;AAAM,EACVc,EAAA,CAAWC,CAAX,CAAiBd,CAAA,CAAmBD,CAAnB,CAAwB,QAAQ,CAACmG,CAAD,CAAMpC,CAAN,CAAe,CAC9D,MAAO,CAAC,UAAAqC,KAAA,CAAgBF,CAAA,CAAcC,CAAd,CAAmBpC,CAAnB,CAAhB,CADsD,CAA/C,CAAjB,CAGA,OAAO/D,EAAAI,KAAA,CAAS,EAAT,CALa,CAJ8B,CAA1C,CA4CZ,KAAAiG,UAAA,CAAiBC,QAAQ,CAACD,CAAD,CAAY,CACnC,MAAIxG,EAAA0G,UAAA,CAAkBF,CAAlB,CAAJ,EACEL,CACO,CADMK,CACN,CAAA,IAFT,EAISL,CAL0B,CA/CV,CAoX7B,CAmIAnG,EAAAgG,OAAA,CAAe,YAAf,CAAAW,OAAA,CAAoC,OAApC,CAA6C,CAAC,WAAD,CAAc,QAAQ,CAACC,CAAD,CAAY,CAAA,IACzEC,EACE,yFAFuE,CAGzEC,EAAgB,WAHyD,CAKzEC,EAAc/G,CAAA6E,SAAA,CAAiB,OAAjB,CAL2D,CAMzEmC,EAAWhH,CAAAgH,SAEf,OAAO,SAAQ,CAACC,CAAD,CAAOC,CAAP,CAAe/E,CAAf,CAA2B,CAwBxCgF,QAASA,EAAO,CAACF,CAAD,CAAO,CAChBA,CAAL,EAGA/F,CAAAyC,KAAA,CAAU1D,CAAA,CAAagH,CAAb,CAAV,CAJqB,CAOvBG,QAASA,EAAO,CAACC,CAAD,CAAMJ,CAAN,CAAY,CAC1B,IAAIjD,CACJ9C,EAAAyC,KAAA,CAAU,KAAV,CACI3D,EAAAsH,WAAA,CAAmBnF,CAAnB,CAAJ,GACEA,CADF,CACeA,CAAA,CAAWkF,CAAX,CADf,CAGA,IAAIrH,CAAAuH,SAAA,CAAiBpF,CAAjB,CAAJ,CACE,IAAK6B,CAAL,GAAY7B,EAAZ,CACEjB,CAAAyC,KAAA,CAAUK,CAAV;AAAgB,IAAhB,CAAuB7B,CAAA,CAAW6B,CAAX,CAAvB,CAAyC,IAAzC,CAFJ,KAKE7B,EAAA,CAAa,EAEX,EAAAnC,CAAA0G,UAAA,CAAkBQ,CAAlB,CAAJ,EAAmC,QAAnC,EAA+C/E,EAA/C,EACEjB,CAAAyC,KAAA,CAAU,UAAV,CACUuD,CADV,CAEU,IAFV,CAIFhG,EAAAyC,KAAA,CAAU,QAAV,CACU0D,CAAApE,QAAA,CAAY,IAAZ,CAAkB,QAAlB,CADV,CAEU,IAFV,CAGAkE,EAAA,CAAQF,CAAR,CACA/F,EAAAyC,KAAA,CAAU,MAAV,CAtB0B,CA9B5B,GAAY,IAAZ,EAAIsD,CAAJ,EAA6B,EAA7B,GAAoBA,CAApB,CAAiC,MAAOA,EACxC,IAAK,CAAAD,CAAA,CAASC,CAAT,CAAL,CAAqB,KAAMF,EAAA,CAAY,WAAZ,CAA8DE,CAA9D,CAAN,CAOrB,IAJA,IAAIO,EAAMP,CAAV,CACI/F,EAAO,EADX,CAEImG,CAFJ,CAGIvG,CACJ,CAAQ2G,CAAR,CAAgBD,CAAAC,MAAA,CAAUZ,CAAV,CAAhB,CAAA,CAEEQ,CAQA,CARMI,CAAA,CAAM,CAAN,CAQN,CANKA,CAAA,CAAM,CAAN,CAML,EANkBA,CAAA,CAAM,CAAN,CAMlB,GALEJ,CAKF,EALSI,CAAA,CAAM,CAAN,CAAA,CAAW,SAAX,CAAuB,SAKhC,EAL6CJ,CAK7C,EAHAvG,CAGA,CAHI2G,CAAAC,MAGJ,CAFAP,CAAA,CAAQK,CAAAG,OAAA,CAAW,CAAX,CAAc7G,CAAd,CAAR,CAEA,CADAsG,CAAA,CAAQC,CAAR,CAAaI,CAAA,CAAM,CAAN,CAAAxE,QAAA,CAAiB6D,CAAjB,CAAgC,EAAhC,CAAb,CACA,CAAAU,CAAA,CAAMA,CAAAI,UAAA,CAAc9G,CAAd,CAAkB2G,CAAA,CAAM,CAAN,CAAA1G,OAAlB,CAERoG,EAAA,CAAQK,CAAR,CACA,OAAOZ,EAAA,CAAU1F,CAAAX,KAAA,CAAU,EAAV,CAAV,CAtBiC,CARmC,CAAlC,CAA7C,CApoB2B,CAA1B,CAAD,CAusBGR,MAvsBH,CAusBWA,MAAAC,QAvsBX;", +"sources":["angular-sanitize.js"], +"names":["window","angular","sanitizeText","chars","buf","htmlSanitizeWriter","writer","noop","join","toMap","str","lowercaseKeys","obj","items","split","i","length","lowercase","htmlParser","html","handler","undefined","inertBodyElement","innerHTML","mXSSAttempts","$sanitizeMinErr","document","documentMode","stripCustomNsAttrs","node","firstChild","nodeType","start","nodeName","toLowerCase","attrToMap","attributes","textContent","nextNode","end","nextSibling","parentNode","removeChild","attrs","map","ii","attr","name","value","encodeEntities","replace","SURROGATE_PAIR_REGEXP","hi","charCodeAt","low","NON_ALPHANUMERIC_REGEXP","uriValidator","ignoreCurrentElement","out","bind","push","tag","blockedElements","validElements","forEach","key","lkey","isImage","validAttrs","uriAttrs","voidElements","Node","ELEMENT_NODE","l","attrNode","attrName","lastIndexOf","removeAttributeNode","$$minErr","optionalEndTagBlockElements","optionalEndTagInlineElements","optionalEndTagElements","extend","blockElements","inlineElements","svgElements","htmlAttrs","svgAttrs","implementation","doc","createHTMLDocument","bodyElements","getElementsByTagName","documentElement","getDocumentElement","createElement","appendChild","module","provider","$SanitizeProvider","svgEnabled","$get","$$sanitizeUri","uri","test","enableSvg","this.enableSvg","isDefined","filter","$sanitize","LINKY_URL_REGEXP","MAILTO_REGEXP","linkyMinErr","isString","text","target","addText","addLink","url","isFunction","isObject","raw","match","index","substr","substring"] +} diff --git a/src/main/resources/static/vendor/angular/angular-scenario.js b/src/main/resources/static/vendor/angular/angular-scenario.js new file mode 100644 index 0000000..dab7290 --- /dev/null +++ b/src/main/resources/static/vendor/angular/angular-scenario.js @@ -0,0 +1,43097 @@ +/*! + * jQuery JavaScript Library v2.2.3 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2016-04-05T19:26Z + */ + +(function( global, factory ) {'use strict'; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Support: Firefox 18+ +// Can't be in strict mode, several libs including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// + +var arr = []; + +var document = window.document; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var support = {}; + + + +var + version = "2.2.3", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android<4.1 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isFunction: function( obj ) { + return jQuery.type( obj ) === "function"; + }, + + isArray: Array.isArray, + + isWindow: function( obj ) { + return obj != null && obj === obj.window; + }, + + isNumeric: function( obj ) { + + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + // adding 1 corrects loss of precision from parseFloat (#15100) + var realStringObj = obj && obj.toString(); + return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0; + }, + + isPlainObject: function( obj ) { + var key; + + // Not plain objects: + // - Any object or value whose internal [[Class]] property is not "[object Object]" + // - DOM nodes + // - window + if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call( obj, "constructor" ) && + !hasOwn.call( obj.constructor.prototype || {}, "isPrototypeOf" ) ) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android<4.0, iOS<6 (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + var script, + indirect = eval; + + code = jQuery.trim( code ); + + if ( code ) { + + // If the code includes a valid, prologue position + // strict mode pragma, execute code by injecting a + // script tag into the document. + if ( code.indexOf( "use strict" ) === 1 ) { + script = document.createElement( "script" ); + script.text = code; + document.head.appendChild( script ).parentNode.removeChild( script ); + } else { + + // Otherwise, avoid the DOM node creation, insertion + // and removal by using an indirect global eval + + indirect( code ); + } + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Support: IE9-11+ + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android<4.1 + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +// JSHint would error on this code due to the Symbol not being defined in ES5. +// Defining this global in .jshintrc would create a danger of using the global +// unguarded in another place, it seems safer to just disable JSHint for these +// three lines. +/* jshint ignore: start */ +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} +/* jshint ignore: end */ + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: iOS 8.2 (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.2.1 + * http://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2015-10-17 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // General-purpose constants + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // http://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, nidselect, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + nidselect = ridentifier.test( nid ) ? "#" + nid : "[id='" + nid + "']"; + while ( i-- ) { + groups[i] = nidselect + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, parent, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( (parent = document.defaultView) && parent.top !== parent ) { + // Support: IE 11 + if ( parent.addEventListener ) { + parent.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", unloadHandler ); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( document.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var m = context.getElementById( id ); + return m ? [ m ] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + docElem.appendChild( div ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibing-combinator selector` fails + if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( div ) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === document ? -1 : + b === document ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + !compilerCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); + + if ( (oldCache = uniqueCache[ dir ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context === document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + if ( !context && elem.ownerDocument !== document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context || document, xml) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + +var rsingleTag = ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ ); + + + +var risSimple = /^.[^:#\[\.,]*$/; + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + } ); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + + } + + if ( typeof qualifier === "string" ) { + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, + len = this.length, + ret = [], + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + // Support: Blackberry 4.6 + // gEBID returns nodes no longer in the document (#6963) + if ( elem && elem.parentNode ) { + + // Inject the element directly into the jQuery object + this.length = 1; + this[ 0 ] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( pos ? + pos.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + return elem.contentDocument || jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnotwhite = ( /\S+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( jQuery.isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ], + [ "notify", "progress", jQuery.Callbacks( "memory" ) ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this === promise ? newDefer.promise() : this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( function() { + + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || + ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. + // If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // Add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .progress( updateFunc( i, progressContexts, progressValues ) ) + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ); + } else { + --remaining; + } + } + } + + // If we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +} ); + + +// The deferred used on DOM ready +var readyList; + +jQuery.fn.ready = function( fn ) { + + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +} ); + +/** + * The ready event handler and self cleanup method + */ +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called + // after the browser event has already occurred. + // Support: IE9-10 only + // Older IE sometimes signals "interactive" too soon + if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + + } else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); + } + } + return readyList.promise( obj ); +}; + +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + len ? fn( elems[ 0 ], key ) : emptyGet; +}; +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + /* jshint -W018 */ + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + register: function( owner, initial ) { + var value = initial || {}; + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable, non-writable property + // configurability must be true to allow the property to be + // deleted with the delete operator + } else { + Object.defineProperty( owner, this.expando, { + value: value, + writable: true, + configurable: true + } ); + } + return owner[ this.expando ]; + }, + cache: function( owner ) { + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( !acceptData( owner ) ) { + return {}; + } + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + if ( typeof data === "string" ) { + cache[ data ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ prop ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + owner[ this.expando ] && owner[ this.expando ][ key ]; + }, + access: function( owner, key, value ) { + var stored; + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + stored = this.get( owner, key ); + + return stored !== undefined ? + stored : this.get( owner, jQuery.camelCase( key ) ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, name, camel, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key === undefined ) { + this.register( owner ); + + } else { + + // Support array or space separated string of keys + if ( jQuery.isArray( key ) ) { + + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = key.concat( key.map( jQuery.camelCase ) ); + } else { + camel = jQuery.camelCase( key ); + + // Try the string as a key before any manipulation + if ( key in cache ) { + name = [ key, camel ]; + } else { + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + name = camel; + name = name in cache ? + [ name ] : ( name.match( rnotwhite ) || [] ); + } + } + + i = name.length; + + while ( i-- ) { + delete cache[ name[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <= 35-45+ + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://code.google.com/p/chromium/issues/detail?id=378607 + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE11+ + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data, camelKey; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // with the key as-is + data = dataUser.get( elem, key ) || + + // Try to find dashed key if it exists (gh-2779) + // This is for 2.2.x only + dataUser.get( elem, key.replace( rmultiDash, "-$&" ).toLowerCase() ); + + if ( data !== undefined ) { + return data; + } + + camelKey = jQuery.camelCase( key ); + + // Attempt to get data from the cache + // with the key camelized + data = dataUser.get( elem, camelKey ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, camelKey, undefined ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + camelKey = jQuery.camelCase( key ); + this.each( function() { + + // First, attempt to store a copy or reference of any + // data that might've been store with a camelCased key. + var data = dataUser.get( this, camelKey ); + + // For HTML5 data-* attribute interop, we have to + // store property names with dashes in a camelCase form. + // This might not apply to all properties...* + dataUser.set( this, camelKey, value ); + + // *... In the case of properties that might _actually_ + // have dashes, we need to also store a copy of that + // unchanged property. + if ( key.indexOf( "-" ) > -1 && data !== undefined ) { + dataUser.set( this, key, value ); + } + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHidden = function( elem, el ) { + + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || + !jQuery.contains( elem.ownerDocument, elem ); + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, + scale = 1, + maxIterations = 20, + currentValue = tween ? + function() { return tween.cur(); } : + function() { return jQuery.css( elem, prop, "" ); }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + do { + + // If previous iteration zeroed out, double until we get *something*. + // Use string for doubling so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + initialInUnit = initialInUnit / scale; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Update scale, tolerating zero or NaN from tween.cur() + // Break the loop if scale is unchanged or perfect, or if we've just had enough. + } while ( + scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations + ); + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([\w:-]+)/ ); + +var rscriptType = ( /^$|\/(?:java|ecma)script/i ); + + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // Support: IE9 + option: [ 1, "" ], + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +// Support: IE9 +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + + // Support: IE9-11+ + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== "undefined" ? + context.querySelectorAll( tag || "*" ) : + []; + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], ret ) : + ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + + // Support: Android<4.1, PhantomJS<2 + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android<4.1, PhantomJS<2 + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0-4.3, Safari<=5.1 + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Safari<=5.1, Android<4.2 + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<=11+ + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +} )(); + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE9 +// See #13393 for more info +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, j, ret, matched, handleObj, + handlerQueue = [], + args = slice.call( arguments ), + handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, matches, sel, handleObj, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Support (at least): Chrome, IE9 + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // + // Support: Firefox<=42+ + // Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343) + if ( delegateCount && cur.nodeType && + ( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== "click" ) ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push( { elem: cur, handlers: matches } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " + + "metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split( " " ), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: ( "button buttons clientX clientY offsetX offsetY pageX pageY " + + "screenX screenY toElement" ).split( " " ), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - + ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - + ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: Cordova 2.5 (WebKit) (#13255) + // All events should have a target; Cordova deviceready doesn't + if ( !event.target ) { + event.target = document; + } + + // Support: Safari 6.0+, Chrome<28 + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + this.focus(); + return false; + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android<4.0 + src.returnValue === false ? + returnTrue : + returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://code.google.com/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi, + + // Support: IE 10-11, Edge 10240+ + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName( "tbody" )[ 0 ] || + elem.appendChild( elem.ownerDocument.createElement( "tbody" ) ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.access( src ); + pdataCur = dataPriv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android<4.1, PhantomJS<2 + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <= 35-45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <= 35-45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + + // Keep domManip exposed until 3.0 (gh-2225) + domManip: domManip, + + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: QtWebKit + // .get() because push.apply(_, arraylike) throws + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); + + +var iframe, + elemdisplay = { + + // Support: Firefox + // We have to pre-define these values for FF (#10227) + HTML: "block", + BODY: "block" + }; + +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ + +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + display = jQuery.css( elem[ 0 ], "display" ); + + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); + + return display; +} + +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + + // Use the already-created iframe if possible + iframe = ( iframe || jQuery( "