Remove the 'ng' suffix of sub projects.

This commit is contained in:
yukon
2017-03-16 09:58:57 +08:00
commit b29f52bdfb
1052 changed files with 260816 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
FROM java:8
VOLUME /tmp
ADD rocketmq-console-ng-1.0.0.jar app.jar
RUN sh -c 'touch /app.jar'
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -jar /app.jar" ]

View File

@@ -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.console;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableAutoConfiguration
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

View File

@@ -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.console.aspect.admin;
import java.lang.reflect.Method;
import org.apache.rocketmq.console.aspect.admin.annotation.MultiMQAdminCmdMethod;
import org.apache.rocketmq.console.service.client.MQAdminInstance;
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.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Aspect
@Service
public class MQAdminAspect {
private Logger logger = LoggerFactory.getLogger(MQAdminAspect.class);
public MQAdminAspect() {
}
@Pointcut("execution(* org.apache.rocketmq.console.service.client.MQAdminExtImpl..*(..))")
public void mQAdminMethodPointCut() {
}
@Pointcut("@annotation(org.apache.rocketmq.console.aspect.admin.annotation.MultiMQAdminCmdMethod)")
public void multiMQAdminMethodPointCut() {
}
@Around(value = "mQAdminMethodPointCut() || multiMQAdminMethodPointCut()")
public Object aroundMQAdminMethod(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object obj = null;
try {
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
MultiMQAdminCmdMethod multiMQAdminCmdMethod = method.getAnnotation(MultiMQAdminCmdMethod.class);
if (multiMQAdminCmdMethod != null && multiMQAdminCmdMethod.timeoutMillis() > 0) {
MQAdminInstance.initMQAdminInstance(multiMQAdminCmdMethod.timeoutMillis());
}
else {
MQAdminInstance.initMQAdminInstance(0);
}
obj = joinPoint.proceed();
}
finally {
MQAdminInstance.destroyMQAdminInstance();
logger.debug("op=look method={} cost={}", joinPoint.getSignature().getName(), System.currentTimeMillis() - start);
}
return obj;
}
}

View File

@@ -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.console.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 MultiMQAdminCmdMethod {
long timeoutMillis() default 0;
}

View File

@@ -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.console.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 {
}

View File

@@ -0,0 +1,87 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.console.config;
import java.io.File;
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.context.annotation.Configuration;
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;
private boolean enableDashBoardCollect;
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 String getRocketMqConsoleDataPath() {
return dataPath;
}
public String getConsoleCollectData() {
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);
}
}

View File

@@ -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.console.controller;
import org.apache.rocketmq.console.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")
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);
}
}

View File

@@ -0,0 +1,111 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.console.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.console.model.ConnectionInfo;
import org.apache.rocketmq.console.model.request.ConsumerConfigInfo;
import org.apache.rocketmq.console.model.request.DeleteSubGroupRequest;
import org.apache.rocketmq.console.model.request.ResetOffsetRequest;
import org.apache.rocketmq.console.service.ConsumerService;
import org.apache.rocketmq.console.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")
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 = "/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);
}
}

View File

@@ -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.console.controller;
import javax.annotation.Resource;
import com.google.common.base.Strings;
import org.apache.rocketmq.console.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")
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();
}
}

View File

@@ -0,0 +1,78 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.console.controller;
import org.apache.rocketmq.common.Pair;
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
import org.apache.rocketmq.tools.admin.api.MessageTrack;
import org.apache.rocketmq.console.model.MessageView;
import org.apache.rocketmq.console.service.MessageService;
import org.apache.rocketmq.console.util.JsonUtil;
import com.google.common.collect.Maps;
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 javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/message")
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<String, Object> messageViewMap = Maps.newHashMap();
Pair<MessageView, List<MessageTrack>> messageViewListPair = messageService.viewMessage(topic, msgId);
messageViewMap.put("messageView", messageViewListPair.getObject1());
messageViewMap.put("messageTrackList", messageViewListPair.getObject2());
return messageViewMap;
}
@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;
}
}

View File

@@ -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.console.controller;
import javax.annotation.Resource;
import org.apache.rocketmq.console.model.ConsumerMonitorConfig;
import org.apache.rocketmq.console.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")
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);
}
}

View File

@@ -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.console.controller;
import javax.annotation.Resource;
import org.apache.rocketmq.console.aspect.admin.annotation.OriginalControllerReturnValue;
import org.apache.rocketmq.console.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")
public class NamesvrController {
@Resource
private OpsService opsService;
@RequestMapping(value = "/nsaddr", method = RequestMethod.GET)
@ResponseBody
@OriginalControllerReturnValue
public Object nsaddr() {
return opsService.getNameSvrList();
}
}

View File

@@ -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.console.controller;
import javax.annotation.Resource;
import org.apache.rocketmq.console.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")
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();
}
}

View File

@@ -0,0 +1,43 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.console.controller;
import javax.annotation.Resource;
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
import org.apache.rocketmq.console.model.ConnectionInfo;
import org.apache.rocketmq.console.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")
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;
}
}

View File

@@ -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.console.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.console.config.RMQConfigure;
import org.apache.rocketmq.console.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<MessageExt> 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;
}
}

View File

@@ -0,0 +1,117 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.console.controller;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.console.model.request.SendTopicMessageRequest;
import org.apache.rocketmq.console.model.request.TopicConfigInfo;
import org.apache.rocketmq.console.service.ConsumerService;
import org.apache.rocketmq.console.service.TopicService;
import org.apache.rocketmq.console.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")
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() throws MQClientException, RemotingException, InterruptedException {
return topicService.fetchAllTopicList();
}
@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);
}
}

View File

@@ -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.console.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;
}
}

View File

@@ -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.console.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<Connection> buildConnectionInfoHashSet(Collection<Connection> connectionList) {
HashSet<Connection> 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;
}
}

View File

@@ -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.console.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<RollbackStats> 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<RollbackStats> getRollbackStatsList() {
return rollbackStatsList;
}
public void setRollbackStatsList(List<RollbackStats> rollbackStatsList) {
this.rollbackStatsList = rollbackStatsList;
}
}

View File

@@ -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.console.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;
}
}

View File

@@ -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.console.model;
import org.apache.rocketmq.common.protocol.heartbeat.ConsumeType;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
public class GroupConsumeInfo implements Comparable<GroupConsumeInfo> {
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 o.count - this.count;
}
return (int) (o.diffTotal - 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;
}
}

View File

@@ -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.console.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<String, String> 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<String, String> getProperties() {
return properties;
}
public void setProperties(Map<String, String> 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;
}
}

View File

@@ -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.console.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;
}
}

View File

@@ -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.console.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<QueueStatInfo> 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<QueueStatInfo> 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());
}
}

View File

@@ -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.console.model.request;
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
import java.util.List;
public class ConsumerConfigInfo {
private List<String> clusterNameList;
private List<String> brokerNameList;
private SubscriptionGroupConfig subscriptionGroupConfig;
public ConsumerConfigInfo() {
}
public ConsumerConfigInfo(List<String> brokerNameList, SubscriptionGroupConfig subscriptionGroupConfig) {
this.brokerNameList = brokerNameList;
this.subscriptionGroupConfig = subscriptionGroupConfig;
}
public List<String> getClusterNameList() {
return clusterNameList;
}
public void setClusterNameList(List<String> clusterNameList) {
this.clusterNameList = clusterNameList;
}
public List<String> getBrokerNameList() {
return brokerNameList;
}
public void setBrokerNameList(List<String> brokerNameList) {
this.brokerNameList = brokerNameList;
}
public SubscriptionGroupConfig getSubscriptionGroupConfig() {
return subscriptionGroupConfig;
}
public void setSubscriptionGroupConfig(SubscriptionGroupConfig subscriptionGroupConfig) {
this.subscriptionGroupConfig = subscriptionGroupConfig;
}
}

View File

@@ -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.console.model.request;
import java.util.List;
public class DeleteSubGroupRequest {
private String groupName;
private List<String> brokerNameList;
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public List<String> getBrokerNameList() {
return brokerNameList;
}
public void setBrokerNameList(List<String> brokerNameList) {
this.brokerNameList = brokerNameList;
}
}

View File

@@ -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.console.model.request;
import java.util.List;
public class ResetOffsetRequest {
private List<String> consumerGroupList;
private String topic;
private long resetTime;
private boolean force;
public List<String> getConsumerGroupList() {
return consumerGroupList;
}
public void setConsumerGroupList(List<String> 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;
}
}

View File

@@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.console.model.request;
public class SendTopicMessageRequest {
private String topic;
private String key;
private String tag;
private String messageBody;
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getMessageBody() {
return messageBody;
}
public void setMessageBody(String messageBody) {
this.messageBody = messageBody;
}
}

View File

@@ -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.console.model.request;
import com.google.common.base.Objects;
import java.util.List;
public class TopicConfigInfo {
private List<String> clusterNameList;
private List<String> brokerNameList;
/** topicConfig */
private String topicName;
private int writeQueueNums;
private int readQueueNums;
private int perm;
private boolean order;
public List<String> getClusterNameList() {
return clusterNameList;
}
public void setClusterNameList(List<String> clusterNameList) {
this.clusterNameList = clusterNameList;
}
/** topicConfig */
public List<String> getBrokerNameList() {
return brokerNameList;
}
public void setBrokerNameList(List<String> 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);
}
}

View File

@@ -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.console.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<String> changeToBrokerNameSet(HashMap<String, Set<String>> clusterAddrTable,
List<String> clusterNameList, List<String> brokerNameList) {
Set<String> 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;
}
}

View File

@@ -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.console.service;
import java.util.Map;
import java.util.Properties;
public interface ClusterService {
Map<String, Object> list();
Properties getBrokerConfig(String brokerAddr);
}

View File

@@ -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.console.service;
import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
import org.apache.rocketmq.console.model.ConsumerGroupRollBackStat;
import org.apache.rocketmq.console.model.GroupConsumeInfo;
import org.apache.rocketmq.console.model.TopicConsumerInfo;
import org.apache.rocketmq.console.model.request.ConsumerConfigInfo;
import org.apache.rocketmq.console.model.request.DeleteSubGroupRequest;
import org.apache.rocketmq.console.model.request.ResetOffsetRequest;
import java.util.List;
import java.util.Map;
import java.util.Set;
public interface ConsumerService {
List<GroupConsumeInfo> queryGroupList();
GroupConsumeInfo queryGroup(String consumerGroup);
List<TopicConsumerInfo> queryConsumeStatsListByGroupName(String groupName);
List<TopicConsumerInfo> queryConsumeStatsList(String topic, String groupName);
Map<String, TopicConsumerInfo> queryConsumeStatsListByTopicName(String topic);
Map<String /*consumerGroup*/, ConsumerGroupRollBackStat> resetOffset(ResetOffsetRequest resetOffsetRequest);
List<ConsumerConfigInfo> examineSubscriptionGroupConfig(String group);
boolean deleteSubGroup(DeleteSubGroupRequest deleteSubGroupRequest);
boolean createAndUpdateSubscriptionGroupConfig(ConsumerConfigInfo consumerConfigInfo);
Set<String> fetchBrokerNameSetBySubscriptionGroup(String group);
ConsumerConnection getConsumerConnection(String consumerGroup);
ConsumerRunningInfo getConsumerRunningInfo(String consumerGroup, String clientId, boolean jstack);
}

View File

@@ -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.console.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.console.task.DashboardCollectTask
// the code can be reconstruct
LoadingCache<String, List<String>> getBrokerMap();
LoadingCache<String, List<String>> getTopicMap();
Map<String, List<String>> jsonDataFile2map(File file);
Map<String, List<String>> getBrokerCache(String date);
Map<String, List<String>> getTopicCache(String date);
}

View File

@@ -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.console.service;
import java.util.List;
import java.util.Map;
public interface DashboardService {
/**
* @param date format yyyy-MM-dd
*/
Map<String, List<String>> queryBrokerData(String date);
/**
* @param date format yyyy-MM-dd
*/
Map<String, List<String>> queryTopicData(String date);
/**
* @param date format yyyy-MM-dd
* @param topicName
*/
List<String> queryTopicData(String date, String topicName);
List<String> queryTopicCurrentData();
}

View File

@@ -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.console.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.tools.admin.api.MessageTrack;
import org.apache.rocketmq.console.model.MessageView;
import java.util.List;
public interface MessageService {
/**
* @param subject
* @param msgId
*/
Pair<MessageView, List<MessageTrack>> viewMessage(String subject, final String msgId);
List<MessageView> queryMessageByTopicAndKey(final String topic, final String key);
/**
* @param topic
* @param begin
* @param end
* org.apache.rocketmq.tools.command.message.PrintMessageSubCommand
*/
List<MessageView> queryMessageByTopic(final String topic, final long begin,
final long end);
List<MessageTrack> messageTrackDetail(MessageExt msg);
ConsumeMessageDirectlyResult consumeMessageDirectly(String topic, String msgId, String consumerGroup,
String clientId);
}

View File

@@ -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.console.service;
import java.util.Map;
import org.apache.rocketmq.console.model.ConsumerMonitorConfig;
public interface MonitorService {
boolean createOrUpdateConsumerMonitor(String name, ConsumerMonitorConfig config);
Map<String, ConsumerMonitorConfig> queryConsumerMonitorConfig();
ConsumerMonitorConfig queryConsumerMonitorConfigByGroupName(String consumeGroupName);
boolean deleteConsumerMonitor(String consumeGroupName);
}

View File

@@ -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.console.service;
import java.util.Map;
import org.apache.rocketmq.console.service.checker.CheckerType;
public interface OpsService {
Map<String, Object> homePageInfo();
void updateNameSvrAddrList(String nameSvrAddrList);
String getNameSvrList();
Map<CheckerType,Object> rocketMqStatusCheck();
boolean updateIsVIPChannel(String useVIPChannel);
}

View File

@@ -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.console.service;
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
public interface ProducerService {
ProducerConnection getProducerConnection(String producerGroup, String topic);
}

View File

@@ -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.console.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.console.model.request.SendTopicMessageRequest;
import org.apache.rocketmq.console.model.request.TopicConfigInfo;
import java.util.List;
public interface TopicService {
TopicList fetchAllTopicList();
TopicStatsTable stats(String topic);
TopicRouteData route(String topic);
GroupList queryTopicConsumerInfo(String topic);
void createOrUpdate(TopicConfigInfo topicCreateOrUpdateRequest);
TopicConfig examineTopicConfig(String topic, String brokerName);
List<TopicConfigInfo> examineTopicConfig(String topic);
boolean deleteTopic(String topic, String clusterName);
boolean deleteTopic(String topic);
boolean deleteTopicInBroker(String brokerName, String topic);
SendResult sendTopicMessageRequest(SendTopicMessageRequest sendTopicMessageRequest);
}

View File

@@ -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.console.service.checker;
public enum CheckerType {
CLUSTER_HEALTH_CHECK,
TOPIC_ONLY_ONE_BROKER_CHECK
}

View File

@@ -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.console.service.checker;
public interface RocketMqChecker {
public Object doCheck();
public CheckerType checkerType();
}

View File

@@ -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.console.service.checker.impl;
import org.apache.rocketmq.console.service.checker.CheckerType;
import org.apache.rocketmq.console.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;
}
}

View File

@@ -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.console.service.checker.impl;
import org.apache.rocketmq.console.service.checker.CheckerType;
import org.apache.rocketmq.console.service.checker.RocketMqChecker;
import org.springframework.stereotype.Service;
@Service
public class TopicOnlyOneBrokerCheckerImpl implements RocketMqChecker {
@Override
public Object doCheck() {
return null;
}
@Override
public CheckerType checkerType() {
return CheckerType.TOPIC_ONLY_ONE_BROKER_CHECK;
}
}

View File

@@ -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.
*/
package org.apache.rocketmq.console.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.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.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.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.console.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 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<String> 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<String> 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<String> 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 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<RollbackStats> 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<MessageQueue, Long> 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<String, Map<MessageQueue, Long>> 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<MessageTrack> 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.console.aspect.admin.MQAdminAspect instead of this");
}
@Override
@Deprecated
public void shutdown() {
throw new IllegalStateException("thisMethod is deprecated.use org.apache.rocketmq.console.aspect.admin.MQAdminAspect instead of this");
}
// below is 3.2.6->3.5.8 updated
@Override
public List<QueueTimeSpan> 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<String> 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<String> 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<String> list) throws InterruptedException, RemotingConnectException, UnsupportedEncodingException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, MQBrokerException {
}
@Override public Map<String, Properties> getNameServerConfig(
List<String> list) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException, UnsupportedEncodingException {
return null;
}
}

View File

@@ -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.console.service.client;
import org.apache.rocketmq.client.exception.MQClientException;
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.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.joor.Reflect;
public class MQAdminInstance {
private static final ThreadLocal<DefaultMQAdminExt> MQ_ADMIN_EXT_THREAD_LOCAL = new ThreadLocal<DefaultMQAdminExt>();
private static final ThreadLocal<Integer> INIT_COUNTER = new ThreadLocal<Integer>();
public static MQAdminExt threadLocalMQAdminExt() {
DefaultMQAdminExt defaultMQAdminExt = MQ_ADMIN_EXT_THREAD_LOCAL.get();
if (defaultMQAdminExt == null) {
throw new IllegalStateException("defaultMQAdminExt should be init before you get this");
}
return defaultMQAdminExt;
}
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 initMQAdminInstance(long timeoutMillis) throws MQClientException {
Integer nowCount = INIT_COUNTER.get();
if (nowCount == null) {
DefaultMQAdminExt defaultMQAdminExt;
if (timeoutMillis > 0) {
defaultMQAdminExt = new DefaultMQAdminExt(timeoutMillis);
}
else {
defaultMQAdminExt = new DefaultMQAdminExt();
}
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
defaultMQAdminExt.start();
MQ_ADMIN_EXT_THREAD_LOCAL.set(defaultMQAdminExt);
INIT_COUNTER.set(1);
}
else {
INIT_COUNTER.set(nowCount + 1);
}
}
public static void destroyMQAdminInstance() {
Integer nowCount = INIT_COUNTER.get() - 1;
if (nowCount > 0) {
INIT_COUNTER.set(nowCount);
return;
}
MQAdminExt mqAdminExt = MQ_ADMIN_EXT_THREAD_LOCAL.get();
if (mqAdminExt != null) {
mqAdminExt.shutdown();
MQ_ADMIN_EXT_THREAD_LOCAL.remove();
INIT_COUNTER.remove();
}
}
}

View File

@@ -0,0 +1,77 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.console.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.console.service.ClusterService;
import org.apache.rocketmq.console.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<String, Object> list() {
try {
Map<String, Object> resultMap = Maps.newHashMap();
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
logger.info("op=look_clusterInfo {}", JsonUtil.obj2String(clusterInfo));
Map<String/*brokerName*/, Map<Long/* brokerId */, Object/* brokerDetail */>> brokerServer = Maps.newHashMap();
for (BrokerData brokerData : clusterInfo.getBrokerAddrTable().values()) {
Map<Long, Object> brokerMasterSlaveMap = Maps.newHashMap();
for (Map.Entry<Long/* brokerId */, String/* broker address */> brokerAddr : brokerData.getBrokerAddrs().entrySet()) {
KVTable kvTable = mqAdminExt.fetchBrokerRuntimeStats(brokerAddr.getValue());
// KVTable kvTable = mqAdminExt.fetchBrokerRuntimeStats("127.0.0.1:10911");
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);
}
}
}

View File

@@ -0,0 +1,341 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.console.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.console.aspect.admin.annotation.MultiMQAdminCmdMethod;
import org.apache.rocketmq.console.model.ConsumerGroupRollBackStat;
import org.apache.rocketmq.console.model.GroupConsumeInfo;
import org.apache.rocketmq.console.model.QueueStatInfo;
import org.apache.rocketmq.console.model.TopicConsumerInfo;
import org.apache.rocketmq.console.model.request.ConsumerConfigInfo;
import org.apache.rocketmq.console.model.request.DeleteSubGroupRequest;
import org.apache.rocketmq.console.model.request.ResetOffsetRequest;
import org.apache.rocketmq.console.service.AbstractCommonService;
import org.apache.rocketmq.console.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
@MultiMQAdminCmdMethod
public List<GroupConsumeInfo> queryGroupList() {
Set<String> 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<GroupConsumeInfo> groupConsumeInfoList = Lists.newArrayList();
for (String consumerGroup : consumerGroupSet) {
groupConsumeInfoList.add(queryGroup(consumerGroup));
}
Collections.sort(groupConsumeInfoList);
return groupConsumeInfoList;
}
@Override
@MultiMQAdminCmdMethod
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, " + consumerGroup, e);
}
ConsumerConnection consumerConnection = null;
try {
consumerConnection = mqAdminExt.examineConsumerConnectionInfo(consumerGroup);
}
catch (Exception e) {
logger.warn("examineConsumerConnectionInfo exception, " + consumerGroup, e);
}
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<TopicConsumerInfo> queryConsumeStatsListByGroupName(String groupName) {
return queryConsumeStatsList(null, groupName);
}
@Override
@MultiMQAdminCmdMethod
public List<TopicConsumerInfo> queryConsumeStatsList(final String topic, String groupName) {
ConsumeStats consumeStats = null;
try {
consumeStats = mqAdminExt.examineConsumeStats(groupName, topic);
}
catch (Exception e) {
throw propagate(e);
}
List<MessageQueue> mqList = Lists.newArrayList(Iterables.filter(consumeStats.getOffsetTable().keySet(), new Predicate<MessageQueue>() {
@Override
public boolean apply(MessageQueue o) {
return StringUtils.isBlank(topic) || o.getTopic().equals(topic);
}
}));
Collections.sort(mqList);
List<TopicConsumerInfo> topicConsumerInfoList = Lists.newArrayList();
TopicConsumerInfo nowTopicConsumerInfo = null;
Map<MessageQueue, String> 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<MessageQueue, String> getClientConnection(String groupName) {
Map<MessageQueue, String> 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
@MultiMQAdminCmdMethod
public Map<String /*groupName*/, TopicConsumerInfo> queryConsumeStatsListByTopicName(String topic) {
Map<String, TopicConsumerInfo> group2ConsumerInfoMap = Maps.newHashMap();
try {
GroupList groupList = mqAdminExt.queryTopicConsumeByWho(topic);
for (String group : groupList.getGroupList()) {
List<TopicConsumerInfo> 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
@MultiMQAdminCmdMethod
public Map<String, ConsumerGroupRollBackStat> resetOffset(ResetOffsetRequest resetOffsetRequest) {
Map<String, ConsumerGroupRollBackStat> groupRollbackStats = Maps.newHashMap();
for (String consumerGroup : resetOffsetRequest.getConsumerGroupList()) {
try {
Map<MessageQueue, Long> rollbackStatsMap =
mqAdminExt.resetOffsetByTimestamp(resetOffsetRequest.getTopic(), consumerGroup, resetOffsetRequest.getResetTime(), resetOffsetRequest.isForce());
ConsumerGroupRollBackStat consumerGroupRollBackStat = new ConsumerGroupRollBackStat(true);
List<RollbackStats> rollbackStatsList = consumerGroupRollBackStat.getRollbackStatsList();
for (Map.Entry<MessageQueue, Long> 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<RollbackStats> 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
@MultiMQAdminCmdMethod
public List<ConsumerConfigInfo> examineSubscriptionGroupConfig(String group) {
List<ConsumerConfigInfo> 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
@MultiMQAdminCmdMethod
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
@MultiMQAdminCmdMethod
public Set<String> fetchBrokerNameSetBySubscriptionGroup(String group) {
Set<String> brokerNameSet = Sets.newHashSet();
try {
List<ConsumerConfigInfo> 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);
}
}
}

View File

@@ -0,0 +1,155 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.console.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.console.config.RMQConfigure;
import org.apache.rocketmq.console.exception.ServiceException;
import org.apache.rocketmq.console.service.DashboardCollectService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class DashboardCollectServiceImpl implements DashboardCollectService {
@Resource
private RMQConfigure rmqConfigure;
private final static Logger log = LoggerFactory.getLogger(DashboardCollectServiceImpl.class);
private LoadingCache<String, List<String>> brokerMap = CacheBuilder.newBuilder()
.maximumSize(1000)
.concurrencyLevel(10)
.recordStats()
.ticker(Ticker.systemTicker())
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
log.debug(notification.getKey() + " was removed, cause is " + notification.getCause());
}
})
.build(
new CacheLoader<String, List<String>>() {
@Override
public List<String> load(String key) {
List<String> list = Lists.newArrayList();
return list;
}
}
);
private LoadingCache<String, List<String>> topicMap = CacheBuilder.newBuilder()
.maximumSize(1000)
.concurrencyLevel(10)
.recordStats()
.ticker(Ticker.systemTicker())
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
log.debug(notification.getKey() + " was removed, cause is " + notification.getCause());
}
})
.build(
new CacheLoader<String, List<String>>() {
@Override
public List<String> load(String key) {
List<String> list = Lists.newArrayList();
return list;
}
}
);
@Override
public LoadingCache<String, List<String>> getBrokerMap() {
return brokerMap;
}
@Override
public LoadingCache<String, List<String>> getTopicMap() {
return topicMap;
}
@Override
public Map<String, List<String>> jsonDataFile2map(File file) {
List<String> 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<Map.Entry<String, Object>> entries = json.entrySet();
Map<String, List<String>> map = Maps.newHashMap();
for (Map.Entry<String, Object> entry : entries) {
JSONArray tpsArray = (JSONArray) entry.getValue();
if (tpsArray == null) {
continue;
}
Object[] tpsStrArray = tpsArray.toArray();
List<String> tpsList = Lists.newArrayList();
for (Object tpsObj : tpsStrArray) {
tpsList.add("" + tpsObj);
}
map.put(entry.getKey(), tpsList);
}
return map;
}
@Override
public Map<String, List<String>> getBrokerCache(String date) {
String dataLocationPath = rmqConfigure.getConsoleCollectData();
File file = new File(dataLocationPath + date + ".json");
if (!file.exists()) {
throw Throwables.propagate(new ServiceException(1, "This date have't data!"));
}
return jsonDataFile2map(file);
}
@Override
public Map<String, List<String>> getTopicCache(String date) {
String dataLocationPath = rmqConfigure.getConsoleCollectData();
File file = new File(dataLocationPath + date + "_topic" + ".json");
if (!file.exists()) {
throw Throwables.propagate(new ServiceException(1, "This date have't data!"));
}
return jsonDataFile2map(file);
}
}

View File

@@ -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.console.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.console.service.DashboardCollectService;
import org.apache.rocketmq.console.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<String, List<String>> queryBrokerData(String date) {
return dashboardCollectService.getBrokerCache(date);
}
@Override
public Map<String, List<String>> queryTopicData(String date) {
return dashboardCollectService.getTopicCache(date);
}
/**
* @param date format yyyy-MM-dd
* @param topicName
*/
@Override
public List<String> queryTopicData(String date, String topicName) {
if (null != dashboardCollectService.getTopicCache(date)) {
return dashboardCollectService.getTopicCache(date).get(topicName);
}
return null;
}
@Override
public List<String> queryTopicCurrentData() {
Date date = new Date();
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Map<String, List<String>> topicCache = dashboardCollectService.getTopicCache(format.format(date));
List<String> result = Lists.newArrayList();
for (Map.Entry<String, List<String>> entry : topicCache.entrySet()) {
List<String> value = entry.getValue();
result.add(entry.getKey() + "," + value.get(value.size() - 1).split(",")[4]);
}
return result;
}
}

View File

@@ -0,0 +1,199 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.console.service.impl;
import com.google.common.base.Function;
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 java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.PullResult;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.Pair;
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.console.model.MessageView;
import org.apache.rocketmq.console.service.MessageService;
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.stereotype.Service;
@Service
public class MessageServiceImpl implements MessageService {
private Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class);
/**
* @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;
public Pair<MessageView, List<MessageTrack>> viewMessage(String subject, final String msgId) {
try {
MessageExt messageExt = mqAdminExt.viewMessage(subject, msgId);
List<MessageTrack> messageTrackList = messageTrackDetail(messageExt);
return new Pair<>(MessageView.fromMessageExt(messageExt), messageTrackList);
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
@Override
public List<MessageView> queryMessageByTopicAndKey(String topic, String key) {
try {
return Lists.transform(mqAdminExt.queryMessage(topic, key, QUERY_MESSAGE_MAX_NUM, 0, System.currentTimeMillis()).getMessageList(), new Function<MessageExt, MessageView>() {
@Override
public MessageView apply(MessageExt messageExt) {
return MessageView.fromMessageExt(messageExt);
}
});
}
catch (Exception err) {
throw Throwables.propagate(err);
}
}
@Override
public List<MessageView> queryMessageByTopic(String topic, final long begin, final long end) {
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, null);
List<MessageView> messageViewList = Lists.newArrayList();
try {
String subExpression = "*";
consumer.start();
Set<MessageQueue> 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<MessageView> messageViewListByQuery = Lists.transform(pullResult.getMsgFoundList(), new Function<MessageExt, MessageView>() {
@Override
public MessageView apply(MessageExt messageExt) {
messageExt.setBody(null);
return MessageView.fromMessageExt(messageExt);
}
});
List<MessageView> filteredList = Lists.newArrayList(Iterables.filter(messageViewListByQuery, new Predicate<MessageView>() {
@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<MessageView>() {
@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<MessageTrack> 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");
}
}

View File

@@ -0,0 +1,100 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.console.service.impl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.base.Throwables;
import java.io.File;
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.console.config.RMQConfigure;
import org.apache.rocketmq.console.model.ConsumerMonitorConfig;
import org.apache.rocketmq.console.service.MonitorService;
import org.apache.rocketmq.console.util.JsonUtil;
import org.springframework.stereotype.Service;
@Service
public class MonitorServiceImpl implements MonitorService {
@Resource
private RMQConfigure rmqConfigure;
private Map<String, ConsumerMonitorConfig> 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<String, ConsumerMonitorConfig> 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 rmqConfigure.getRocketMqConsoleDataPath() + 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() {
String content = MixAll.file2String(getConsumerMonitorConfigDataPath());
if (content == null) {
content = MixAll.file2String(getConsumerMonitorConfigDataPathBackUp());
}
if (content == null) {
return;
}
configMap = JsonUtil.string2Obj(content, new TypeReference<ConcurrentHashMap<String, ConsumerMonitorConfig>>() {
});
}
}

View File

@@ -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.console.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.rocketmq.console.config.RMQConfigure;
import org.apache.rocketmq.console.service.AbstractCommonService;
import org.apache.rocketmq.console.service.OpsService;
import org.apache.rocketmq.console.service.checker.CheckerType;
import org.apache.rocketmq.console.service.checker.RocketMqChecker;
import org.springframework.stereotype.Service;
@Service
public class OpsServiceImpl extends AbstractCommonService implements OpsService {
@Resource
private RMQConfigure rMQConfigure;
@Resource
private List<RocketMqChecker> rocketMqCheckerList;
@Override
public Map<String, Object> homePageInfo() {
Map<String, Object> homePageInfoMap = Maps.newHashMap();
homePageInfoMap.put("namesvrAddrList", Splitter.on(";").splitToList(rMQConfigure.getNamesrvAddr()));
homePageInfoMap.put("useVIPChannel", Boolean.valueOf(rMQConfigure.getIsVIPChannel()));
return homePageInfoMap;
}
@Override
public void updateNameSvrAddrList(String nameSvrAddrList) {
rMQConfigure.setNamesrvAddr(nameSvrAddrList);
}
@Override
public String getNameSvrList() {
return rMQConfigure.getNamesrvAddr();
}
@Override
public Map<CheckerType, Object> rocketMqStatusCheck() {
Map<CheckerType, Object> checkResultMap = Maps.newHashMap();
for (RocketMqChecker rocketMqChecker : rocketMqCheckerList) {
checkResultMap.put(rocketMqChecker.checkerType(), rocketMqChecker.doCheck());
}
return checkResultMap;
}
@Override public boolean updateIsVIPChannel(String useVIPChannel) {
rMQConfigure.setIsVIPChannel(useVIPChannel);
return true;
}
}

View File

@@ -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.console.service.impl;
import com.google.common.base.Throwables;
import javax.annotation.Resource;
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
import org.apache.rocketmq.console.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);
}
}
}

View File

@@ -0,0 +1,214 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.console.service.impl;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
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.console.config.RMQConfigure;
import org.apache.rocketmq.console.model.request.SendTopicMessageRequest;
import org.apache.rocketmq.console.model.request.TopicConfigInfo;
import org.apache.rocketmq.console.service.AbstractCommonService;
import org.apache.rocketmq.console.service.TopicService;
import org.apache.rocketmq.tools.command.CommandUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class TopicServiceImpl extends AbstractCommonService implements TopicService {
@Autowired
private RMQConfigure rMQConfigure;
@Override
public TopicList fetchAllTopicList() {
try {
return mqAdminExt.fetchAllTopicList();
}
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<TopicConfigInfo> examineTopicConfig(String topic) {
List<TopicConfigInfo> 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<String> masterSet = CommandUtil.fetchMasterAddrByClusterName(mqAdminExt, clusterName);
mqAdminExt.deleteTopicInBroker(masterSet, topic);
Set<String> nameServerSet = null;
if (StringUtils.isNotBlank(rMQConfigure.getNamesrvAddr())) {
String[] ns = rMQConfigure.getNamesrvAddr().split(";");
nameServerSet = new HashSet<String>(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;
}
@Override
public SendResult sendTopicMessageRequest(SendTopicMessageRequest sendTopicMessageRequest) {
DefaultMQProducer producer = new DefaultMQProducer(MixAll.SELF_TEST_PRODUCER_GROUP);
producer.setInstanceName(String.valueOf(System.currentTimeMillis()));
producer.setNamesrvAddr(rMQConfigure.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 {
producer.shutdown();
}
}
}

View File

@@ -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.console.support;
import javax.servlet.http.HttpServletRequest;
import org.apache.rocketmq.console.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.console")
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(value = Exception.class)
@ResponseBody
public JsonResult<Object> jsonErrorHandler(HttpServletRequest req, Exception ex) throws Exception {
JsonResult<Object> value = null;
if (ex != null) {
logger.error("op=global_exception_handler_print_error", ex);
if (ex instanceof ServiceException) {
value = new JsonResult<Object>(((ServiceException) ex).getCode(), ex.getMessage());
}
else {
value = new JsonResult<Object>(-1, ex.getMessage());
}
}
return value;
}
}

View File

@@ -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.console.support;
import java.lang.annotation.Annotation;
import org.apache.rocketmq.console.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.console")
public class GlobalRestfulResponseBodyAdvice implements ResponseBodyAdvice<Object> {
private Logger logger = LoggerFactory.getLogger(GlobalRestfulResponseBodyAdvice.class);
@Override
public Object beforeBodyWrite(
Object obj, MethodParameter methodParameter, MediaType mediaType,
Class<? extends HttpMessageConverter<?>> 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<? extends HttpMessageConverter<?>> converterType) {
return true;
}
}

View File

@@ -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.console.support;
public class JsonResult<T> {
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;
}
}

View File

@@ -0,0 +1,320 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.console.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.console.aspect.admin.annotation.MultiMQAdminCmdMethod;
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.console.config.RMQConfigure;
import org.apache.rocketmq.console.service.DashboardCollectService;
import org.apache.rocketmq.console.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 * * * ?")
@MultiMQAdminCmdMethod(timeoutMillis = 5000)
public void collectTopic() {
if (!rmqConfigure.isEnableDashBoardCollect()) {
return;
}
Date date = new Date();
Stopwatch stopwatch = Stopwatch.createStarted();
try {
TopicList topicList = mqAdminExt.fetchAllTopicList();
Set<String> topicSet = topicList.getTopicList();
for (String topic : topicSet) {
if (topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX) || topic.startsWith(MixAll.DLQ_GROUP_TOPIC_PREFIX)) {
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());
stopwatch.reset();
inTPS += bsd.getStatsMinute().getTps();
inMsgCntToday += StatsAllSubCommand.compute24HourSum(bsd);
}
catch (Exception e) {
// throw Throwables.propagate(e);
}
}
}
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) {
// throw Throwables.propagate(e);
}
}
}
}
}
List<String> 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<Map.Entry<String, BrokerData>> clusterEntries = clusterInfo.getBrokerAddrTable().entrySet();
Map<String, String> addresses = Maps.newHashMap();
for (Map.Entry<String, BrokerData> clusterEntry : clusterEntries) {
HashMap<Long, String> addrs = clusterEntry.getValue().getBrokerAddrs();
Set<Map.Entry<Long, String>> addrsEntries = addrs.entrySet();
for (Map.Entry<Long, String> addrEntry : addrsEntries) {
addresses.put(addrEntry.getValue(), clusterEntry.getKey() + ":" + addrEntry.getKey());
}
}
Set<Map.Entry<String, String>> entries = addresses.entrySet();
for (Map.Entry<String, String> entry : entries) {
List<String> 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.getConsoleCollectData();
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<String, List<String>> brokerFileMap;
Map<String, List<String>> 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<String, List<String>> map, Map<String, List<String>> fileMap,
File file) throws IOException {
Map<String, List<String>> newMap = map.asMap();
Map<String, List<String>> resultMap = Maps.newHashMap();
if (fileMap.size() == 0) {
resultMap = newMap;
}
else {
for (Map.Entry<String, List<String>> entry : fileMap.entrySet()) {
List<String> oldList = entry.getValue();
List<String> 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<String, List<String>> entry : newMap.entrySet()) {
List<String> 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<String> appendData(List<String> newTpsList, List<String> oldTpsList) {
List<String> 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;
}
}

View File

@@ -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.console.task;
import java.util.Map;
import javax.annotation.Resource;
import org.apache.rocketmq.console.model.ConsumerMonitorConfig;
import org.apache.rocketmq.console.model.GroupConsumeInfo;
import org.apache.rocketmq.console.service.ConsumerService;
import org.apache.rocketmq.console.service.MonitorService;
import org.apache.rocketmq.console.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<String, ConsumerMonitorConfig> 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
}
}
}
}

View File

@@ -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.console.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 <T> 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 <T> 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> T string2Obj(String str, Class<T> 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<T>: {}\nError: {}", str, clazz.getName(), e);
return null;
}
}
public static <T> T byte2Obj(byte[] bytes, Class<T> 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<T>: {}\nError: {}", bytes, clazz.getName(), e);
return null;
}
}
public static <T> T string2Obj(String str, TypeReference<T> 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<T>: {}\nError: {}", str,
typeReference.getType(), e);
return null;
}
}
public static <T> T byte2Obj(byte[] bytes, TypeReference<T> 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<T>: {}\nError: {}", bytes,
typeReference.getType(), e);
return null;
}
}
public static <T> T map2Obj(Map<String, String> map, Class<T> clazz) {
String str = obj2String(map);
return string2Obj(str, clazz);
}
private static String escapesSpecialChar(String str) {
return str.replace("\n", "\\n").replace("\r", "\\r");
}
}

View File

@@ -0,0 +1,16 @@
server.contextPath=
server.port=8080
#spring.application.index=true
spring.application.name=rocketmq-console
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
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=
#if you use rocketmq version < 3.5.8, rocketmq.config.isVIPChannel should be false.default true
rocketmq.config.isVIPChannel=
#rocketmq-console's data path:dashboard/monitor
rocketmq.config.dataPath=/tmp/rocketmq-console/data
#set it false if you don't want use dashboard.default true
rocketmq.config.enableDashBoardCollect=true

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="UTF-8">
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %5p %m%n</pattern>
</encoder>
</appender>
<appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/consolelogs/rocketmq-console.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${user.home}/logs/consolelogs/rocketmq-console-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>104857600</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<MaxHistory>10</MaxHistory>
</rollingPolicy>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %5p %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>

View File

@@ -0,0 +1,117 @@
<!DOCTYPE html>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<html lang="en" ng-app="app">
<head>
<meta charset="UTF-8">
<title>RocketMq-console-ng</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name='description' content=''>
<meta name='keywords' content=''>
<!--iOS -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- preLoading -->
<link rel="stylesheet" href="style/preLoading/normalize.css">
<link rel="stylesheet" href="style/preLoading/main.css">
<script src="vendor/preLoading/modernizr-2.6.2.min.js"></script>
<!-- preLoading -->
<link rel="stylesheet" href="vendor/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="vendor/bootstrap-material-design/css/bootstrap-material-design.css">
<link rel="stylesheet" href="vendor/bootstrap-material-design/css/ripples.css">
<link rel="stylesheet" href="vendor/angular/notification/angular-ui-notification.css">
<link rel="stylesheet" href="vendor/ng-dialog/ngDialog.min.css">
<link rel="stylesheet" href="vendor/ng-dialog/ngDialog-theme-default.css">
<link rel="stylesheet" href="vendor/dropdown/jquery.dropdown.css">
<link rel="stylesheet" href="vendor/datatimepicker/bootstrap-datetimepicker.min.css">
<link rel="stylesheet" href="vendor/font-awesome-4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="vendor/font-awesome-4.7.0/fonts/fontawesome-webfont.svg">
<link rel="stylesheet" type="text/css" href="vendor/chosen/chosen.css"/>
<link rel="stylesheet" type="text/css" href="vendor/chosen/chosen-spinner.css"/>
<link rel="stylesheet" type="text/css" href="vendor/angular-material/angular-material.min.css"/>
<link rel="stylesheet" type="text/css" href="vendor/md-tab/docs.css"/>
<link rel="stylesheet" href="style/animate.css">
<link rel="stylesheet" href="style/theme.css">
<link rel="stylesheet" href="style/app.css">
</head>
<body ng-controller="AppCtrl">
<!--[if lte IE 9]>
<script type="text/javascript">
location.href = 'view/pages/un_support_browser.html';
</script>
<![endif]-->
<div id="loader-wrapper">
<div id="loader"></div>
<div class="loader-section section-left"></div>
<div class="loader-section section-right"></div>
</div>
<div ng-include="'view/layout/_header.html'"></div>
<section ng-view></section>
<div ng-include="'view/layout/_footer.html'"></div>
<script type="text/javascript" src="vendor/jquery/jquery1.11.3.min.js"></script>
<script type="text/javascript" src="vendor/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="vendor/bootstrap-material-design/js/material.min.js"></script>
<script type="text/javascript" src="vendor/bootstrap-material-design/js/ripples.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-translate.min.js"></script>
<script type="text/javascript"
src="vendor/angular/angular-translate-storage-cookie/angular-translate-storage-cookie.min.js"></script>
<script type="text/javascript" src="vendor/angular/i18n/angular-locale_zh-cn.js"></script>
<script type="text/javascript" src="src/i18n/en.js"></script>
<script type="text/javascript" src="src/i18n/zh.js"></script>
<script type="text/javascript" src="vendor/angular/angular-cookies.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-animate.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-route.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-ui-router.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-sanitize.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-aria.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-messages.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-sanitize.min.js"></script>
<script type="text/javascript" src="vendor/angular/notification/angular-ui-notification.js"></script>
<script type="text/javascript" src="vendor/pagination/tm.pagination.js"></script>
<script type="text/javascript" src="vendor/ng-dialog/ngDialog.min.js"></script>
<script type="text/javascript" src="vendor/json-bigint/json-bigint.js"></script>
<script type="text/javascript" src="vendor/dropdown/jquery.dropdown.js"></script>
<script type="text/javascript" src="vendor/datatimepicker/moment.min.js"></script>
<script type="text/javascript" src="vendor/datatimepicker/bootstrap-datetimepicker.min.js"></script>
<script type="text/javascript" src="vendor/datatimepicker/angular-eonasdan-datetimepicker.min.js"></script>
<script type="text/javascript" src="vendor/chosen/angular-chosen.js"></script>
<script type="text/javascript" src="vendor/chosen/chosen.jquery.min.js"></script>
<script type="text/javascript" src='vendor/md-tab/svg-assets-cache.js'></script>
<script type="text/javascript" src='vendor/angular-material/angular-material.min.js'></script>
<script type="text/javascript" src="vendor/echarts/echarts.min.js"></script>
<script type="text/javascript" src="src/app.js"></script>
<script type="text/javascript" src="src/controller.js?v=201702250025"></script>
<script type="text/javascript" src="src/tools/tools.js"></script>
<script type="text/javascript" src="src/cluster.js?timestamp=4"></script>
<script type="text/javascript" src="src/topic.js"></script>
<script type="text/javascript" src="src/consumer.js?timestamp=6"></script>
<script type="text/javascript" src="src/producer.js"></script>
<script type="text/javascript" src="src/message.js"></script>
<script type="text/javascript" src="src/ops.js?timestamp=7"></script>
<script type="text/javascript" src="src/remoteApi/remoteApi.js"></script>
<script type="text/javascript" src="vendor/preLoading/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,208 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 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',
function ($rootScope,$location,$cookies) {
// var filter = function(url){
// var outFilterArrs = []
// outFilterArrs.push("/login");
// outFilterArrs.push("/reg");
// outFilterArrs.push("/logout");
// outFilterArrs.push("/404");
// var flag = false;
// $.each(outFilterArrs,function(i,value){
// if(url.indexOf(value) > -1){
// flag = true;
// return false;
// }
// });
// return flag;
// }
// if(angular.isDefined($cookies.get("isLogin")) && $cookies.get("isLogin") == 'true'){
// chatApi.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.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
}
});
$httpProvider.defaults.cache = false;
$routeProvider.when('/', {
templateUrl: 'view/pages/index.html',
controller:'dashboardCtrl'
}).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('/ops', {
templateUrl: 'view/pages/ops.html',
controller:'opsController'
}).when('/404', {
templateUrl: '404'
}).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<total; i++) {
if(count > 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;
};

View File

@@ -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});
}
})
}
}])

View File

@@ -0,0 +1,350 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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',function ($scope, ngDialog, $http,Notification) {
$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.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 () {
$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);
}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({
template: 'consumerModifyDialog',
controller: 'consumerModifyDialogController',
data:{
consumerRequestList:request,
allClusterNameList:Object.keys(resp.data.clusterInfo.clusterAddrTable),
allBrokerNameList:Object.keys(resp.data.brokerServer),
bIsUpdate:bIsUpdate
}
});
}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({
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: "delete 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});
}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});
}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});
}
});
};
}]
);

View File

@@ -0,0 +1,557 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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','$rootScope','$cookies','$location','$translate', function ($scope,$rootScope,$cookies,$location,$translate) {
$scope.changeTranslate = function(langKey){
$translate.use(langKey);
}
}]);
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 clusterMap = resp.data.clusterInfo.clusterAddrTable;
var brokerMap = resp.data.clusterInfo.brokerAddrTable;
var brokerDetail = resp.data.brokerServer;
var clusterMap = tools.generateBrokerMap(brokerDetail,clusterMap,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[3]);
})
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);
}]);

View File

@@ -0,0 +1,4 @@
[
{"TYPE":"DEMO_TYPE","DICT_VALUE":"0","DICT_NAME":"test1"},
{"TYPE":"DEMO_TYPE","DICT_VALUE":"1","DICT_NAME":"test2"}
]

View File

@@ -0,0 +1,80 @@
var en = {
"TITLE": "RocketMq-Console-Ng",
"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 Ponsume 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",
"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",
"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",
"OPS":"OPS",
"AUTO_REFRESH":"AUTO_REFRESH",
"REFRESH":"REFRESH"
}

View File

@@ -0,0 +1,80 @@
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":"消息",
"OPERATION": "操作",
"ADD": "新增",
"UPDATE": "更新",
"STATUS": "状态",
"ROUTER": "路由",
"MANAGE": "管理",
"CONFIG": "配置",
"SEND_MSG": "发送消息",
"RESET_CUS_OFFSET": "重置消费位点",
"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":"集群名",
"OPS":"运维",
"AUTO_REFRESH":"自动刷新",
"REFRESH":"刷新"
}

View File

@@ -0,0 +1,200 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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"
}).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(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.paginationConf = {
currentPage: 1,
totalItems: 0,
itemsPerPage: 20,
pagesLength: 15,
perPageOptions: [10],
rememberPerPage: 'perPageItems',
onChange: function () {
$scope.changeShowMessageList(this.currentPage,this.totalItems);
}
};
$scope.queryMessageByTopic = function () {
console.log($scope.selectedTopic);
console.log($scope.timepickerBegin)
console.log($scope.timepickerEnd)
$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 ;
};
}]);
module.controller('messageDetailViewDialogController',['$scope', 'ngDialog', '$http','Notification', function ($scope, ngDialog, $http,Notification) {
$scope.resendMessage = function (msgId,topic,consumerGroup) {
$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
}
});
};
}]
);

View File

@@ -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.
*/
app.controller('opsController', ['$scope','$location','$http','Notification','remoteApi','tools', function ($scope,$location,$http,Notification,remoteApi,tools) {
$scope.namesvrAddrList = [];
$scope.useVIPChannel = true;
$http({
method: "GET",
url: "ops/homePage.query"
}).success(function (resp) {
if (resp.status == 0) {
$scope.namesvrAddrList = resp.data.namesvrAddrList;
$scope.useVIPChannel = resp.data.useVIPChannel
}else{
Notification.error({message: resp.errMsg, delay: 2000});
}
});
$scope.updateNameSvrAddr = function () {
$http({
method: "POST",
url: "ops/updateNameSvrAddr.do",
params:{nameSvrAddrList:$scope.namesvrAddrList.join(";")}
}).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});
}
});
}
}]);

View File

@@ -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.
*/
var module = app;
module.controller('producerController', ['$scope', '$http','Notification',function ($scope, $http,Notification) {
$scope.selectedTopic=[];
$scope.producerGroup="";
$http({
method: "GET",
url: "topic/list.query"
}).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});
}
});
}
} ]);

View File

@@ -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
}
}])

View File

@@ -0,0 +1,87 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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(brokerDetail,clusterMap,brokerMap){
var map = {};
$.each(brokerDetail,function(k,v){
$.each(clusterMap,function (ck, cv) {
if(angular.isUndefined(map[ck])){
map[ck] = [];
}
$.each(cv,function(cvi,cvv){
if(cvv == k){
var index = 0;
$.each(v,function(vi,vv){
vv.split = k;
vv.index = index;
vv.address = brokerMap[cvv].brokerAddrs[index];
vv.brokerName = brokerMap[cvv].brokerName;
map[ck].push(vv);
index++;
})
}
})
})
})
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
}
}])

View File

@@ -0,0 +1,406 @@
/**
* Created by tcrow on 2017/1/12 0012.
*/
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',function ($scope, ngDialog, $http,Notification) {
$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.allTopicList = [];
$scope.topicShowList = [];
$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.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.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.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});
}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.openSendTopicMessageDialog = function (topic) {
ngDialog.open({
template: 'sendTopicMessageDialog',
controller: 'sendTopicMessageDialogController',
data: {
topic: topic
}
});
};
$scope.openUpdateDialog = function (topic) {
$http({
method: "GET",
url: "topic/examineTopicConfig.query",
params:{
topic:topic
}
}).success(function (resp) {
if(resp.status ==0){
$scope.openCreateOrUpdateDialog(resp.data);
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.openCreateOrUpdateDialog = function (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({
template: 'topicModifyDialog',
controller: 'topicModifyDialogController',
data:{
topicRequestList:request,
allClusterNameList:Object.keys(resp.data.clusterInfo.clusterAddrTable),
allBrokerNameList:Object.keys(resp.data.brokerServer),
bIsUpdate:bIsUpdate
}
});
}
});
}
$scope.openAddDialog = function () {
$scope.openCreateOrUpdateDialog(null);
}
}]);
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});
}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
}
});
}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"
};
$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
}
});
}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});
}
})
};
}]
);

View File

@@ -0,0 +1,32 @@
.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);
}

View File

@@ -0,0 +1,274 @@
/* 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;
}

View File

@@ -0,0 +1,752 @@
.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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,12 @@
.login-panel{
}
.login-panel .qrcode {
width: 100%;
hight: 100%;
}
.login-panel .validateCode {
/*height:20px;*/
}

View File

@@ -0,0 +1,420 @@
/*
_____ _ _ _ _
|_ _| | | | | | | | |
| | | |__ __ _| |_ ___ | |_ ___ _ __ ___ __ _| |_ ___ ___ ___
| | | '_ \ / _` | __/ _ \ | __/ _ \| '_ ` _ \ / _` | __/ _ \ / _ \/ __|
_| |_ | | | | (_| | || __/ | || (_) | | | | | | (_| | || (_) | __/\__ \
\___/ |_| |_|\__,_|\__\___| \__\___/|_| |_| |_|\__,_|\__\___/ \___||___/
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
*/

View File

@@ -0,0 +1,527 @@
/*! 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;
}

View File

@@ -0,0 +1,80 @@
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;
}

View File

@@ -0,0 +1,126 @@
.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;
}

View File

@@ -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.length&&(d+=0<f?" ":"",d+=c?b+a:a+b)});return d}function Ma(a){if(a instanceof H)switch(a.length){case 0:return[];
case 1:if(1===a[0].nodeType)return a;break;default:return H(ga(a))}if(1===a.nodeType)return H(a)}function ga(a){if(!a[0])return a;for(var b=0;b<a.length;b++){var c=a[b];if(1==c.nodeType)return c}}function Na(a,b,c){q(b,function(b){a.addClass(b,c)})}function Oa(a,b,c){q(b,function(b){a.removeClass(b,c)})}function Q(a){return function(b,c){c.addClass&&(Na(a,b,c.addClass),c.addClass=null);c.removeClass&&(Oa(a,b,c.removeClass),c.removeClass=null)}}function oa(a){a=a||{};if(!a.$$prepared){var b=a.domOperation||
O;a.domOperation=function(){a.$$domOperationFired=!0;b();b=O};a.$$prepared=!0}return a}function ha(a,b){Aa(a,b);Ba(a,b)}function Aa(a,b){b.from&&(a.css(b.from),b.from=null)}function Ba(a,b){b.to&&(a.css(b.to),b.to=null)}function T(a,b,c){var d=b.options||{};c=c.options||{};var e=(d.addClass||"")+" "+(c.addClass||""),f=(d.removeClass||"")+" "+(c.removeClass||"");a=Pa(a.attr("class"),e,f);c.preparationClasses&&(d.preparationClasses=W(c.preparationClasses,d.preparationClasses),delete c.preparationClasses);
e=d.domOperation!==O?d.domOperation:null;Ca(d,c);e&&(d.domOperation=e);d.addClass=a.addClass?a.addClass:null;d.removeClass=a.removeClass?a.removeClass:null;b.addClass=d.addClass;b.removeClass=d.removeClass;return d}function Pa(a,b,c){function d(a){I(a)&&(a=a.split(" "));var b={};q(a,function(a){a.length&&(b[a]=!0)});return b}var e={};a=d(a);b=d(b);q(b,function(a,b){e[b]=1});c=d(c);q(c,function(a,b){e[b]=1===e[b]?null:-1});var f={addClass:"",removeClass:""};q(e,function(b,c){var d,e;1===b?(d="addClass",
e=!a[c]):-1===b&&(d="removeClass",e=a[c]);e&&(f[d].length&&(f[d]+=" "),f[d]+=c)});return f}function A(a){return a instanceof w.element?a[0]:a}function Qa(a,b,c){var d="";b&&(d=V(b,"ng-",!0));c.addClass&&(d=W(d,V(c.addClass,"-add")));c.removeClass&&(d=W(d,V(c.removeClass,"-remove")));d.length&&(c.preparationClasses=d,a.addClass(d))}function pa(a,b){var c=b?"-"+b+"s":"";la(a,[ma,c]);return[ma,c]}function ra(a,b){var c=b?"paused":"",d=X+"PlayState";la(a,[d,c]);return[d,c]}function la(a,b){a.style[b[0]]=
b[1]}function W(a,b){return a?b?a+" "+b:a:b}function Da(a,b,c){var d=Object.create(null),e=a.getComputedStyle(b)||{};q(c,function(a,b){var c=e[a];if(c){var F=c.charAt(0);if("-"===F||"+"===F||0<=F)c=Ra(c);0===c&&(c=null);d[b]=c}});return d}function Ra(a){var b=0;a=a.split(/\s*,\s*/);q(a,function(a){"s"==a.charAt(a.length-1)&&(a=a.substring(0,a.length-1));a=parseFloat(a)||0;b=b?Math.max(a,b):a});return b}function sa(a){return 0===a||null!=a}function Ea(a,b){var c=R,d=a+"s";b?c+="Duration":d+=" linear all";
return[c,d]}function Fa(){var a=Object.create(null);return{flush:function(){a=Object.create(null)},count:function(b){return(b=a[b])?b.total:0},get:function(b){return(b=a[b])&&b.value},put:function(b,c){a[b]?a[b].total++:a[b]={total:1,value:c}}}}function Ga(a,b,c){q(c,function(c){a[c]=Y(a[c])?a[c]:b.style.getPropertyValue(c)})}var O=w.noop,Ha=w.copy,Ca=w.extend,H=w.element,q=w.forEach,ba=w.isArray,I=w.isString,ca=w.isObject,N=w.isUndefined,Y=w.isDefined,Ia=w.isFunction,ta=w.isElement,R,ua,X,va;N(E.ontransitionend)&&
Y(E.onwebkittransitionend)?(R="WebkitTransition",ua="webkitTransitionEnd transitionend"):(R="transition",ua="transitionend");N(E.onanimationend)&&Y(E.onwebkitanimationend)?(X="WebkitAnimation",va="webkitAnimationEnd animationend"):(X="animation",va="animationend");var qa=X+"Delay",wa=X+"Duration",ma=R+"Delay";E=R+"Duration";var Ka=w.$$minErr("ng"),Sa={transitionDuration:E,transitionDelay:ma,transitionProperty:R+"Property",animationDuration:wa,animationDelay:qa,animationIterationCount:X+"IterationCount"},
Ta={transitionDuration:E,transitionDelay:ma,animationDuration:wa,animationDelay:qa};w.module("ngAnimate",[]).directive("ngAnimateSwap",["$animate","$rootScope",function(a,b){return{restrict:"A",transclude:"element",terminal:!0,priority:600,link:function(b,d,e,f,r){var x,F;b.$watchCollection(e.ngAnimateSwap||e["for"],function(e){x&&a.leave(x);F&&(F.$destroy(),F=null);if(e||0===e)F=b.$new(),r(F,function(b){x=b;a.enter(b,null,d)})})}}}]).directive("ngAnimateChildren",["$interpolate",function(a){return{link:function(b,
c,d){function e(a){c.data("$$ngAnimateChildren","on"===a||"true"===a)}var f=d.ngAnimateChildren;w.isString(f)&&0===f.length?c.data("$$ngAnimateChildren",!0):(e(a(f)(b)),d.$observe("ngAnimateChildren",e))}}}]).factory("$$rAFScheduler",["$$rAF",function(a){function b(a){d=d.concat(a);c()}function c(){if(d.length){for(var b=d.shift(),r=0;r<b.length;r++)b[r]();e||a(function(){e||c()})}}var d,e;d=b.queue=[];b.waitUntilQuiet=function(b){e&&e();e=a(function(){e=null;b();c()})};return b}]).provider("$$animateQueue",
["$animateProvider",function(a){function b(a){if(!a)return null;a=a.split(" ");var b=Object.create(null);q(a,function(a){b[a]=!0});return b}function c(a,c){if(a&&c){var d=b(c);return a.split(" ").some(function(a){return d[a]})}}function d(a,b,c,d){return f[a].some(function(a){return a(b,c,d)})}function e(a,b){var c=0<(a.addClass||"").length,d=0<(a.removeClass||"").length;return b?c&&d:c||d}var f=this.rules={skip:[],cancel:[],join:[]};f.join.push(function(a,b,c){return!b.structural&&e(b)});f.skip.push(function(a,
b,c){return!b.structural&&!e(b)});f.skip.push(function(a,b,c){return"leave"==c.event&&b.structural});f.skip.push(function(a,b,c){return c.structural&&2===c.state&&!b.structural});f.cancel.push(function(a,b,c){return c.structural&&b.structural});f.cancel.push(function(a,b,c){return 2===c.state&&b.structural});f.cancel.push(function(a,b,d){if(d.structural)return!1;a=b.addClass;b=b.removeClass;var e=d.addClass;d=d.removeClass;return N(a)&&N(b)||N(e)&&N(d)?!1:c(a,d)||c(b,e)});this.$get=["$$rAF","$rootScope",
"$rootElement","$document","$$HashMap","$$animation","$$AnimateRunner","$templateRequest","$$jqLite","$$forceReflow",function(b,c,f,u,l,w,s,M,v,h){function P(){var a=!1;return function(b){a?b():c.$$postDigest(function(){a=!0;b()})}}function y(a,b,c){var g=A(b),d=A(a),k=[];(a=G[c])&&q(a,function(a){t.call(a.node,g)?k.push(a.callback):"leave"===c&&t.call(a.node,d)&&k.push(a.callback)});return k}function k(a,g,k){function m(c,g,d,k){F(function(){var c=y(J,a,g);c.length&&b(function(){q(c,function(b){b(a,
d,k)})})});c.progress(g,d,k)}function G(b){var c=a,g=h;g.preparationClasses&&(c.removeClass(g.preparationClasses),g.preparationClasses=null);g.activeClasses&&(c.removeClass(g.activeClasses),g.activeClasses=null);Ja(a,h);ha(a,h);h.domOperation();f.complete(!b)}var h=Ha(k),t,J;if(a=Ma(a))t=A(a),J=a.parent();var h=oa(h),f=new s,F=P();ba(h.addClass)&&(h.addClass=h.addClass.join(" "));h.addClass&&!I(h.addClass)&&(h.addClass=null);ba(h.removeClass)&&(h.removeClass=h.removeClass.join(" "));h.removeClass&&
!I(h.removeClass)&&(h.removeClass=null);h.from&&!ca(h.from)&&(h.from=null);h.to&&!ca(h.to)&&(h.to=null);if(!t)return G(),f;k=[t.className,h.addClass,h.removeClass].join(" ");if(!Ua(k))return G(),f;var v=0<=["enter","move","leave"].indexOf(g),l=!K||u[0].hidden||C.get(t);k=!l&&z.get(t)||{};var Z=!!k.state;l||Z&&1==k.state||(l=!n(a,J,g));if(l)return G(),f;v&&xa(a);l={structural:v,element:a,event:g,addClass:h.addClass,removeClass:h.removeClass,close:G,options:h,runner:f};if(Z){if(d("skip",a,l,k)){if(2===
k.state)return G(),f;T(a,k,l);return k.runner}if(d("cancel",a,l,k))if(2===k.state)k.runner.end();else if(k.structural)k.close();else return T(a,k,l),k.runner;else if(d("join",a,l,k))if(2===k.state)T(a,l,{});else return Qa(a,v?g:null,h),g=l.event=k.event,h=T(a,k,l),k.runner}else T(a,l,{});(Z=l.structural)||(Z="animate"===l.event&&0<Object.keys(l.options.to||{}).length||e(l));if(!Z)return G(),ka(a),f;var M=(k.counter||0)+1;l.counter=M;D(a,1,l);c.$$postDigest(function(){var b=z.get(t),c=!b,b=b||{},d=
0<(a.parent()||[]).length&&("animate"===b.event||b.structural||e(b));if(c||b.counter!==M||!d){c&&(Ja(a,h),ha(a,h));if(c||v&&b.event!==g)h.domOperation(),f.end();d||ka(a)}else g=!b.structural&&e(b,!0)?"setClass":b.event,D(a,2),b=w(a,g,b.options),b.done(function(b){G(!b);(b=z.get(t))&&b.counter===M&&ka(A(a));m(f,g,"close",{})}),f.setHost(b),m(f,g,"start",{})});return f}function xa(a){a=A(a).querySelectorAll("[data-ng-animate]");q(a,function(a){var b=parseInt(a.getAttribute("data-ng-animate")),c=z.get(a);
if(c)switch(b){case 2:c.runner.end();case 1:z.remove(a)}})}function ka(a){a=A(a);a.removeAttribute("data-ng-animate");z.remove(a)}function J(a,b){return A(a)===A(b)}function n(a,b,c){c=H(u[0].body);var g=J(a,c)||"HTML"===a[0].nodeName,d=J(a,f),k=!1,h,m=C.get(A(a));(a=H.data(a[0],"$ngAnimatePin"))&&(b=a);for(b=A(b);b;){d||(d=J(b,f));if(1!==b.nodeType)break;a=z.get(b)||{};if(!k){var e=C.get(b);if(!0===e&&!1!==m){m=!0;break}else!1===e&&(m=!1);k=a.structural}if(N(h)||!0===h)a=H.data(b,"$$ngAnimateChildren"),
Y(a)&&(h=a);if(k&&!1===h)break;g||(g=J(b,c));if(g&&d)break;if(!d&&(a=H.data(b,"$ngAnimatePin"))){b=A(a);continue}b=b.parentNode}return(!k||h)&&!0!==m&&d&&g}function D(a,b,c){c=c||{};c.state=b;a=A(a);a.setAttribute("data-ng-animate",b);c=(b=z.get(a))?Ca(b,c):c;z.put(a,c)}var z=new l,C=new l,K=null,g=c.$watch(function(){return 0===M.totalPendingRequests},function(a){a&&(g(),c.$$postDigest(function(){c.$$postDigest(function(){null===K&&(K=!0)})}))}),G={},m=a.classNameFilter(),Ua=m?function(a){return m.test(a)}:
function(){return!0},Ja=Q(v),t=Node.prototype.contains||function(a){return this===a||!!(this.compareDocumentPosition(a)&16)},Z={on:function(a,b,c){var g=ga(b);G[a]=G[a]||[];G[a].push({node:g,callback:c});H(b).on("$destroy",function(){Z.off(a,b,c)})},off:function(a,b,c){function g(a,b,c){var d=ga(b);return a.filter(function(a){return!(a.node===d&&(!c||a.callback===c))})}var d=G[a];d&&(G[a]=1===arguments.length?null:g(d,b,c))},pin:function(a,b){ya(ta(a),"element","not an element");ya(ta(b),"parentElement",
"not an element");a.data("$ngAnimatePin",b)},push:function(a,b,c,g){c=c||{};c.domOperation=g;return k(a,b,c)},enabled:function(a,b){var c=arguments.length;if(0===c)b=!!K;else if(ta(a)){var g=A(a),d=C.get(g);1===c?b=!d:C.put(g,!b)}else b=K=!!a;return b}};return Z}]}]).provider("$$animation",["$animateProvider",function(a){function b(a){return a.data("$$animationRunner")}var c=this.drivers=[];this.$get=["$$jqLite","$rootScope","$injector","$$AnimateRunner","$$HashMap","$$rAFScheduler",function(a,e,
f,r,x,F){function u(a){function b(a){if(a.processed)return a;a.processed=!0;var d=a.domNode,h=d.parentNode;e.put(d,a);for(var f;h;){if(f=e.get(h)){f.processed||(f=b(f));break}h=h.parentNode}(f||c).children.push(a);return a}var c={children:[]},d,e=new x;for(d=0;d<a.length;d++){var f=a[d];e.put(f.domNode,a[d]={domNode:f.domNode,fn:f.fn,children:[]})}for(d=0;d<a.length;d++)b(a[d]);return function(a){var b=[],c=[],d;for(d=0;d<a.children.length;d++)c.push(a.children[d]);a=c.length;var h=0,e=[];for(d=0;d<
c.length;d++){var f=c[d];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<a.length;d++){var k=a[d];if("ng-"!==k.substring(0,3))for(var e=0;e<b.length;e++)if(k===b[e]){c.push(k);break}}return c.join(" ")}function k(a){for(var b=c.length-1;0<=b;b--){var d=c[b];if(f.has(d)&&(d=f.get(d)(a)))return d}}function xa(a,c){a.from&&a.to?(b(a.from.element).setHost(c),
b(a.to.element).setHost(c)):b(a.element).setHost(c)}function ka(){var a=b(s);!a||"leave"===x&&v.$$domOperationFired||a.end()}function J(b){s.off("$destroy",ka);s.removeData("$$animationRunner");w(s,v);ha(s,v);v.domOperation();C&&a.removeClass(s,C);s.removeClass("ng-animate");D.complete(!b)}v=oa(v);var n=0<=["enter","move","leave"].indexOf(x),D=new r({end:function(){J()},cancel:function(){J(!0)}});if(!c.length)return J(),D;s.data("$$animationRunner",D);var z=za(s.attr("class"),za(v.addClass,v.removeClass)),
C=v.tempClasses;C&&(z+=" "+C,v.tempClasses=null);var K;n&&(K="ng-"+x+"-prepare",a.addClass(s,K));l.push({element:s,classes:z,event:x,structural:n,options:v,beforeStart:function(){s.addClass("ng-animate");C&&a.addClass(s,C);K&&(a.removeClass(s,K),K=null)},close:J});s.on("$destroy",ka);if(1<l.length)return D;e.$$postDigest(function(){var a=[];q(l,function(c){b(c.element)?a.push(c):c.close()});l.length=0;var c=P(a),d=[];q(c,function(a){d.push({domNode:A(a.from?a.from.element:a.element),fn:function(){a.beforeStart();
var c,d=a.close;if(b(a.anchors?a.from.element||a.to.element:a.element)){var g=k(a);g&&(c=g.start)}c?(c=c(),c.done(function(a){d(!a)}),xa(a,c)):d()}})});F(u(d))});return D}}]}]).provider("$animateCss",["$animateProvider",function(a){var b=Fa(),c=Fa();this.$get=["$window","$$jqLite","$$AnimateRunner","$timeout","$$forceReflow","$sniffer","$$rAFScheduler","$$animateQueue",function(a,e,f,r,x,F,u,l){function w(a,b){var c=a.parentNode;return(c.$$ngAnimateParentKey||(c.$$ngAnimateParentKey=++P))+"-"+a.getAttribute("class")+
"-"+b}function s(k,h,f,l){var n;0<b.count(f)&&(n=c.get(f),n||(h=V(h,"-stagger"),e.addClass(k,h),n=Da(a,k,l),n.animationDuration=Math.max(n.animationDuration,0),n.transitionDuration=Math.max(n.transitionDuration,0),e.removeClass(k,h),c.put(f,n)));return n||{}}function M(a){y.push(a);u.waitUntilQuiet(function(){b.flush();c.flush();for(var a=x(),d=0;d<y.length;d++)y[d](a);y.length=0})}function v(c,h,e){h=b.get(e);h||(h=Da(a,c,Sa),"infinite"===h.animationIterationCount&&(h.animationIterationCount=1));
b.put(e,h);c=h;e=c.animationDelay;h=c.transitionDelay;c.maxDelay=e&&h?Math.max(e,h):e||h;c.maxDuration=Math.max(c.animationDuration*c.animationIterationCount,c.transitionDuration);return c}var h=Q(e),P=0,y=[];return function(a,c){function d(){n()}function u(){n(!0)}function n(b){if(!(P||H&&da)){P=!0;da=!1;g.$$skipPreparationClasses||e.removeClass(a,fa);e.removeClass(a,ga);ra(m,!1);pa(m,!1);q(y,function(a){m.style[a[0]]=""});h(a,g);ha(a,g);Object.keys(G).length&&q(G,function(a,b){a?m.style.setProperty(b,
a):m.style.removeProperty(b)});if(g.onDone)g.onDone();ea&&ea.length&&a.off(ea.join(" "),C);var c=a.data("$$animateCss");c&&(r.cancel(c[0].timer),a.removeData("$$animateCss"));E&&E.complete(!b)}}function D(a){p.blockTransition&&pa(m,a);p.blockKeyframeAnimation&&ra(m,!!a)}function z(){E=new f({end:d,cancel:u});M(O);n();return{$$willAnimate:!1,start:function(){return E},end:d}}function C(a){a.stopPropagation();var b=a.originalEvent||a;a=b.$manualTimeStamp||Date.now();b=parseFloat(b.elapsedTime.toFixed(3));
Math.max(a-W,0)>=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=0<B.transitionDuration;p.hasAnimations=0<B.animationDuration}p.applyAnimationDelay&&($="boolean"!==typeof g.delay&&sa(g.delay)?parseFloat(g.delay):$,I=Math.max($,0),B.animationDelay=$,aa=[qa,$+"s"],y.push(aa),
m.style[aa[0]]=aa[1]);Q=1E3*I;T=1E3*L;if(g.easing){var d,f=g.easing;p.hasTransitions&&(d=R+"TimingFunction",y.push([d,f]),m.style[d]=f);p.hasAnimations&&(d=X+"TimingFunction",y.push([d,f]),m.style[d]=f)}B.transitionDuration&&ea.push(ua);B.animationDuration&&ea.push(va);W=Date.now();var l=Q+1.5*T;d=W+l;var f=a.data("$$animateCss")||[],K=!0;if(f.length){var z=f[0];(K=d>z.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<b.length;d++)b[d]();a.removeData("$$animateCss")}}if(!P)if(m.parentNode){var d=function(a){if(H)da&&a&&(da=!1,n());else if(da=!a,B.animationDuration)if(a=ra(m,da),da)y.push(a);else{var b=y,c=b.indexOf(a);0<=a&&b.splice(c,1)}},f=0<ca&&(B.transitionDuration&&0===U.transitionDuration||B.animationDuration&&0===U.animationDuration)&&Math.max(U.animationDelay,
U.transitionDelay);f?r(b,Math.floor(f*ca*1E3),!1):b();N.resume=function(){d(!0)};N.pause=function(){d(!1)}}else n()}var g=c||{};g.$$prepared||(g=oa(Ha(g)));var G={},m=A(a);if(!m||!m.parentNode||!l.enabled())return z();var y=[],x=a.attr("class"),t=La(g),P,da,H,E,N,I,Q,L,T,W,ea=[];if(0===g.duration||!F.animations&&!F.transitions)return z();var ja=g.event&&ba(g.event)?g.event.join(" "):g.event,Y="",S="";ja&&g.structural?Y=V(ja,"ng-",!0):ja&&(Y=ja);g.addClass&&(S+=V(g.addClass,"-add"));g.removeClass&&
(S.length&&(S+=" "),S+=V(g.removeClass,"-remove"));g.applyClassesEarly&&S.length&&h(a,g);var fa=[Y,S].join(" ").trim(),na=x+" "+fa,ga=V(fa,"-active"),x=t.to&&0<Object.keys(t.to).length;if(!(0<(g.keyframeStyle||"").length||x||fa))return z();var ia,U;0<g.stagger?(t=parseFloat(g.stagger),U={transitionDelay:t,animationDelay:t,transitionDuration:0,animationDuration:0}):(ia=w(m,na),U=s(m,fa,ia,Ta));g.$$skipPreparationClasses||e.addClass(a,fa);g.transitionStyle&&(t=[R,g.transitionStyle],la(m,t),y.push(t));
0<=g.duration&&(t=0<m.style[R].length,t=Ea(g.duration,t),la(m,t),y.push(t));g.keyframeStyle&&(t=[X,g.keyframeStyle],la(m,t),y.push(t));var ca=U?0<=g.staggerIndex?g.staggerIndex:b.count(ia):0;(ja=0===ca)&&!g.skipBlocking&&pa(m,9999);var B=v(m,na,ia),$=B.maxDelay;I=Math.max($,0);L=B.maxDuration;var p={};p.hasTransitions=0<B.transitionDuration;p.hasAnimations=0<B.animationDuration;p.hasTransitionAll=p.hasTransitions&&"all"==B.transitionProperty;p.applyTransitionDuration=x&&(p.hasTransitions&&!p.hasTransitionAll||
p.hasAnimations&&!p.hasTransitions);p.applyAnimationDuration=g.duration&&p.hasAnimations;p.applyTransitionDelay=sa(g.delay)&&(p.applyTransitionDuration||p.hasTransitions);p.applyAnimationDelay=sa(g.delay)&&p.hasAnimations;p.recalculateTimingStyles=0<S.length;if(p.applyTransitionDuration||p.applyAnimationDuration)L=g.duration?parseFloat(g.duration):L,p.applyTransitionDuration&&(p.hasTransitions=!0,B.transitionDuration=L,t=0<m.style[R+"Property"].length,y.push(Ea(L,t))),p.applyAnimationDuration&&(p.hasAnimations=
!0,B.animationDuration=L,y.push([wa,L+"s"]));if(0===L&&!p.recalculateTimingStyles)return z();if(null!=g.delay){var aa;"boolean"!==typeof g.delay&&(aa=parseFloat(g.delay),I=Math.max(aa,0));p.applyTransitionDelay&&y.push([ma,aa+"s"]);p.applyAnimationDelay&&y.push([qa,aa+"s"])}null==g.duration&&0<B.transitionDuration&&(p.recalculateTimingStyles=p.recalculateTimingStyles||ja);Q=1E3*I;T=1E3*L;g.skipBlocking||(p.blockTransition=0<B.transitionDuration,p.blockKeyframeAnimation=0<B.animationDuration&&0<U.animationDelay&&
0===U.animationDuration);g.from&&(g.cleanupStyles&&Ga(G,m,Object.keys(g.from)),Aa(a,g));p.blockTransition||p.blockKeyframeAnimation?D(L):g.skipBlocking||pa(m,!1);return{$$willAnimate:!0,end:d,start:function(){if(!P)return N={end:d,cancel:u,resume:null,pause:null},E=new f(N),M(K),E}}}}]}]).provider("$$animateCssDriver",["$$animationProvider",function(a){a.drivers.push("$$animateCssDriver");this.$get=["$animateCss","$rootScope","$$AnimateRunner","$rootElement","$sniffer","$$jqLite","$document",function(a,
c,d,e,f,r,x){function F(a){return a.replace(/\bng-\S+\b/g,"")}function u(a,b){I(a)&&(a=a.split(" "));I(b)&&(b=b.split(" "));return a.filter(function(a){return-1===b.indexOf(a)}).join(" ")}function l(c,e,f){function k(a){var b={},c=A(a).getBoundingClientRect();q(["width","height","top","left"],function(a){var d=c[a];switch(a){case "top":d+=M.scrollTop;break;case "left":d+=M.scrollLeft}b[a]=Math.floor(d)+"px"});return b}function l(){var c=F(f.attr("class")||""),d=u(c,n),c=u(n,c),d=a(r,{to:k(f),addClass:"ng-anchor-in "+
d,removeClass:"ng-anchor-out "+c,delay:!0});return d.$$willAnimate?d:null}function x(){r.remove();e.removeClass("ng-animate-shim");f.removeClass("ng-animate-shim")}var r=H(A(e).cloneNode(!0)),n=F(r.attr("class")||"");e.addClass("ng-animate-shim");f.addClass("ng-animate-shim");r.addClass("ng-anchor");v.append(r);var D;c=function(){var c=a(r,{addClass:"ng-anchor-out",delay:!0,from:k(e)});return c.$$willAnimate?c:null}();if(!c&&(D=l(),!D))return x();var z=c||D;return{start:function(){function a(){c&&
c.end()}var b,c=z.start();c.done(function(){c=null;if(!D&&(D=l()))return c=D.start(),c.done(function(){c=null;x();b.complete()}),c;x();b.complete()});return b=new d({end:a,cancel:a})}}}function w(a,b,c,e){var f=s(a,O),r=s(b,O),x=[];q(e,function(a){(a=l(c,a.out,a["in"]))&&x.push(a)});if(f||r||0!==x.length)return{start:function(){function a(){q(b,function(a){a.end()})}var b=[];f&&b.push(f.start());r&&b.push(r.start());q(x,function(a){b.push(a.start())});var c=new d({end:a,cancel:a});d.all(b,function(a){c.complete(a)});
return c}}}function s(c){var d=c.element,e=c.options||{};c.structural&&(e.event=c.event,e.structural=!0,e.applyClassesEarly=!0,"leave"===c.event&&(e.onDone=e.domOperation));e.preparationClasses&&(e.event=W(e.event,e.preparationClasses));c=a(d,e);return c.$$willAnimate?c:null}if(!f.animations&&!f.transitions)return O;var M=x[0].body;c=A(e);var v=H(c.parentNode&&11===c.parentNode.nodeType||M.contains(c)?c:M);Q(r);return function(a){return a.from&&a.to?w(a.from,a.to,a.classes,a.anchors):s(a)}}]}]).provider("$$animateJs",
["$animateProvider",function(a){this.$get=["$injector","$$AnimateRunner","$$jqLite",function(b,c,d){function e(c){c=ba(c)?c:c.split(" ");for(var d=[],e={},f=0;f<c.length;f++){var l=c[f],q=a.$$registeredAnimations[l];q&&!e[l]&&(d.push(b.get(q)),e[l]=!0)}return d}var f=Q(d);return function(a,b,d,u){function l(){u.domOperation();f(a,u)}function w(a,b,d,e,g){switch(d){case "animate":b=[b,e.from,e.to,g];break;case "setClass":b=[b,h,H,g];break;case "addClass":b=[b,h,g];break;case "removeClass":b=[b,H,g];
break;default:b=[b,g]}b.push(e);if(a=a.apply(a,b))if(Ia(a.start)&&(a=a.start()),a instanceof c)a.done(g);else if(Ia(a))return a;return O}function s(a,b,d,e,g){var f=[];q(e,function(e){var h=e[g];h&&f.push(function(){var e,g,f=!1,k=function(a){f||(f=!0,(g||O)(a),e.complete(!a))};e=new c({end:function(){k()},cancel:function(){k(!0)}});g=w(h,a,b,d,function(a){k(!1===a)});return e})});return f}function A(a,b,d,e,g){var f=s(a,b,d,e,g);if(0===f.length){var h,k;"beforeSetClass"===g?(h=s(a,"removeClass",
d,e,"beforeRemoveClass"),k=s(a,"addClass",d,e,"beforeAddClass")):"setClass"===g&&(h=s(a,"removeClass",d,e,"removeClass"),k=s(a,"addClass",d,e,"addClass"));h&&(f=f.concat(h));k&&(f=f.concat(k))}if(0!==f.length)return function(a){var b=[];f.length&&q(f,function(a){b.push(a())});b.length?c.all(b,a):a();return function(a){q(b,function(b){a?b.cancel():b.end()})}}}var v=!1;3===arguments.length&&ca(d)&&(u=d,d=null);u=oa(u);d||(d=a.attr("class")||"",u.addClass&&(d+=" "+u.addClass),u.removeClass&&(d+=" "+
u.removeClass));var h=u.addClass,H=u.removeClass,y=e(d),k,E;if(y.length){var I,J;"leave"==b?(J="leave",I="afterLeave"):(J="before"+b.charAt(0).toUpperCase()+b.substr(1),I=b);"enter"!==b&&"move"!==b&&(k=A(a,b,u,y,J));E=A(a,b,u,y,I)}if(k||E){var n;return{$$willAnimate:!0,end:function(){n?n.end():(v=!0,l(),ha(a,u),n=new c,n.complete(!0));return n},start:function(){function b(c){v=!0;l();ha(a,u);n.complete(c)}if(n)return n;n=new c;var d,e=[];k&&e.push(function(a){d=k(a)});e.length?e.push(function(a){l();
a(!0)}):l();E&&e.push(function(a){d=E(a)});n.setHost({end:function(){v||((d||O)(void 0),b(void 0))},cancel:function(){v||((d||O)(!0),b(!0))}});c.chain(e,b);return n}}}}}]}]).provider("$$animateJsDriver",["$$animationProvider",function(a){a.drivers.push("$$animateJsDriver");this.$get=["$$animateJs","$$AnimateRunner",function(a,c){function d(c){return a(c.element,c.event,c.classes,c.options)}return function(a){if(a.from&&a.to){var b=d(a.from),r=d(a.to);if(b||r)return{start:function(){function a(){return function(){q(d,
function(a){a.end()})}}var d=[];b&&d.push(b.start());r&&d.push(r.start());c.all(d,function(a){e.complete(a)});var e=new c({end:a(),cancel:a()});return e}}}else return d(a)}}]}])})(window,window.angular);
//# sourceMappingURL=angular-animate.min.js.map

View File

@@ -0,0 +1,14 @@
/*
AngularJS v1.5.3
(c) 2010-2016 Google, Inc. http://angularjs.org
License: MIT
*/
(function(s,q,t){'use strict';var f="BUTTON A INPUT TEXTAREA SELECT DETAILS SUMMARY".split(" "),l=function(a,c){if(-1!==c.indexOf(a[0].nodeName))return!0};q.module("ngAria",["ng"]).provider("$aria",function(){function a(a,h,p,n){return function(d,e,b){var g=b.$normalize(h);!c[g]||l(e,p)||b[g]||d.$watch(b[a],function(b){b=n?!b:!!b;e.attr(h,b)})}}var c={ariaHidden:!0,ariaChecked:!0,ariaDisabled:!0,ariaRequired:!0,ariaInvalid:!0,ariaValue:!0,tabindex:!0,bindKeypress:!0,bindRoleForClick:!0};this.config=
function(a){c=q.extend(c,a)};this.$get=function(){return{config:function(a){return c[a]},$$watchExpr:a}}}).directive("ngShow",["$aria",function(a){return a.$$watchExpr("ngShow","aria-hidden",[],!0)}]).directive("ngHide",["$aria",function(a){return a.$$watchExpr("ngHide","aria-hidden",[],!1)}]).directive("ngValue",["$aria",function(a){return a.$$watchExpr("ngValue","aria-checked",f,!1)}]).directive("ngChecked",["$aria",function(a){return a.$$watchExpr("ngChecked","aria-checked",f,!1)}]).directive("ngRequired",
["$aria",function(a){return a.$$watchExpr("ngRequired","aria-required",f,!1)}]).directive("ngModel",["$aria",function(a){function c(c,n,d,e){return a.config(n)&&!d.attr(c)&&(e||!l(d,f))}function m(a,c){return!c.attr("role")&&c.attr("type")===a&&"INPUT"!==c[0].nodeName}function h(a,c){var d=a.type,e=a.role;return"checkbox"===(d||e)||"menuitemcheckbox"===e?"checkbox":"radio"===(d||e)||"menuitemradio"===e?"radio":"range"===d||"progressbar"===e||"slider"===e?"range":""}return{restrict:"A",require:"ngModel",
priority:200,compile:function(f,n){var d=h(n,f);return{pre:function(a,b,c,k){"checkbox"===d&&(k.$isEmpty=function(a){return!1===a})},post:function(e,b,g,k){function f(){return k.$modelValue}function h(a){b.attr("aria-checked",g.value==k.$viewValue)}function n(){b.attr("aria-checked",!k.$isEmpty(k.$viewValue))}var l=c("tabindex","tabindex",b,!1);switch(d){case "radio":case "checkbox":m(d,b)&&b.attr("role",d);c("aria-checked","ariaChecked",b,!1)&&e.$watch(f,"radio"===d?h:n);l&&b.attr("tabindex",0);
break;case "range":m(d,b)&&b.attr("role","slider");if(a.config("ariaValue")){var p=!b.attr("aria-valuemin")&&(g.hasOwnProperty("min")||g.hasOwnProperty("ngMin")),q=!b.attr("aria-valuemax")&&(g.hasOwnProperty("max")||g.hasOwnProperty("ngMax")),r=!b.attr("aria-valuenow");p&&g.$observe("min",function(a){b.attr("aria-valuemin",a)});q&&g.$observe("max",function(a){b.attr("aria-valuemax",a)});r&&e.$watch(f,function(a){b.attr("aria-valuenow",a)})}l&&b.attr("tabindex",0)}!g.hasOwnProperty("ngRequired")&&
k.$validators.required&&c("aria-required","ariaRequired",b,!1)&&g.$observe("required",function(){b.attr("aria-required",!!g.required)});c("aria-invalid","ariaInvalid",b,!0)&&e.$watch(function(){return k.$invalid},function(a){b.attr("aria-invalid",!!a)})}}}}}]).directive("ngDisabled",["$aria",function(a){return a.$$watchExpr("ngDisabled","aria-disabled",f,!1)}]).directive("ngMessages",function(){return{restrict:"A",require:"?ngMessages",link:function(a,c,f,h){c.attr("aria-live")||c.attr("aria-live",
"assertive")}}}).directive("ngClick",["$aria","$parse",function(a,c){return{restrict:"A",compile:function(m,h){var p=c(h.ngClick,null,!0);return function(c,d,e){if(!l(d,f)&&(a.config("bindRoleForClick")&&!d.attr("role")&&d.attr("role","button"),a.config("tabindex")&&!d.attr("tabindex")&&d.attr("tabindex",0),a.config("bindKeypress")&&!e.ngKeypress))d.on("keypress",function(a){function d(){p(c,{$event:a})}var e=a.which||a.keyCode;32!==e&&13!==e||c.$apply(d)})}}}}]).directive("ngDblclick",["$aria",function(a){return function(c,
m,h){!a.config("tabindex")||m.attr("tabindex")||l(m,f)||m.attr("tabindex",0)}}])})(window,window.angular);
//# sourceMappingURL=angular-aria.min.js.map

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
/*
AngularJS v1.5.6
(c) 2010-2016 Google, Inc. http://angularjs.org
License: MIT
*/
(function(S,B){'use strict';function Aa(a,b,c){if(!a)throw Ma("areq",b||"?",c||"required");return a}function Ba(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;ca(a)&&(a=a.join(" "));ca(b)&&(b=b.join(" "));return a+" "+b}function Na(a){var b={};a&&(a.to||a.from)&&(b.to=a.to,b.from=a.from);return b}function Y(a,b,c){var d="";a=ca(a)?a:a&&P(a)&&a.length?a.split(/\s+/):[];s(a,function(a,l){a&&0<a.length&&(d+=0<l?" ":"",d+=c?b+a:a+b)});return d}function Oa(a){if(a instanceof E)switch(a.length){case 0:return a;
case 1:if(1===a[0].nodeType)return a;break;default:return E(da(a))}if(1===a.nodeType)return E(a)}function da(a){if(!a[0])return a;for(var b=0;b<a.length;b++){var c=a[b];if(1==c.nodeType)return c}}function Pa(a,b,c){s(b,function(b){a.addClass(b,c)})}function Qa(a,b,c){s(b,function(b){a.removeClass(b,c)})}function V(a){return function(b,c){c.addClass&&(Pa(a,b,c.addClass),c.addClass=null);c.removeClass&&(Qa(a,b,c.removeClass),c.removeClass=null)}}function pa(a){a=a||{};if(!a.$$prepared){var b=a.domOperation||
R;a.domOperation=function(){a.$$domOperationFired=!0;b();b=R};a.$$prepared=!0}return a}function ha(a,b){Ca(a,b);Da(a,b)}function Ca(a,b){b.from&&(a.css(b.from),b.from=null)}function Da(a,b){b.to&&(a.css(b.to),b.to=null)}function W(a,b,c){var d=b.options||{};c=c.options||{};var e=(d.addClass||"")+" "+(c.addClass||""),l=(d.removeClass||"")+" "+(c.removeClass||"");a=Ra(a.attr("class"),e,l);c.preparationClasses&&(d.preparationClasses=Z(c.preparationClasses,d.preparationClasses),delete c.preparationClasses);
e=d.domOperation!==R?d.domOperation:null;Ea(d,c);e&&(d.domOperation=e);d.addClass=a.addClass?a.addClass:null;d.removeClass=a.removeClass?a.removeClass:null;b.addClass=d.addClass;b.removeClass=d.removeClass;return d}function Ra(a,b,c){function d(a){P(a)&&(a=a.split(" "));var b={};s(a,function(a){a.length&&(b[a]=!0)});return b}var e={};a=d(a);b=d(b);s(b,function(a,b){e[b]=1});c=d(c);s(c,function(a,b){e[b]=1===e[b]?null:-1});var l={addClass:"",removeClass:""};s(e,function(b,c){var d,e;1===b?(d="addClass",
e=!a[c]):-1===b&&(d="removeClass",e=a[c]);e&&(l[d].length&&(l[d]+=" "),l[d]+=c)});return l}function z(a){return a instanceof B.element?a[0]:a}function Sa(a,b,c){var d="";b&&(d=Y(b,"ng-",!0));c.addClass&&(d=Z(d,Y(c.addClass,"-add")));c.removeClass&&(d=Z(d,Y(c.removeClass,"-remove")));d.length&&(c.preparationClasses=d,a.addClass(d))}function qa(a,b){var c=b?"-"+b+"s":"";la(a,[ma,c]);return[ma,c]}function ta(a,b){var c=b?"paused":"",d=$+"PlayState";la(a,[d,c]);return[d,c]}function la(a,b){a.style[b[0]]=
b[1]}function Z(a,b){return a?b?a+" "+b:a:b}function Fa(a,b,c){var d=Object.create(null),e=a.getComputedStyle(b)||{};s(c,function(a,b){var c=e[a];if(c){var k=c.charAt(0);if("-"===k||"+"===k||0<=k)c=Ta(c);0===c&&(c=null);d[b]=c}});return d}function Ta(a){var b=0;a=a.split(/\s*,\s*/);s(a,function(a){"s"==a.charAt(a.length-1)&&(a=a.substring(0,a.length-1));a=parseFloat(a)||0;b=b?Math.max(a,b):a});return b}function ua(a){return 0===a||null!=a}function Ga(a,b){var c=T,d=a+"s";b?c+="Duration":d+=" linear all";
return[c,d]}function Ha(){var a=Object.create(null);return{flush:function(){a=Object.create(null)},count:function(b){return(b=a[b])?b.total:0},get:function(b){return(b=a[b])&&b.value},put:function(b,c){a[b]?a[b].total++:a[b]={total:1,value:c}}}}function Ia(a,b,c){s(c,function(c){a[c]=ea(a[c])?a[c]:b.style.getPropertyValue(c)})}var R=B.noop,Ja=B.copy,Ea=B.extend,E=B.element,s=B.forEach,ca=B.isArray,P=B.isString,va=B.isObject,O=B.isUndefined,ea=B.isDefined,Ka=B.isFunction,wa=B.isElement,T,xa,$,ya;O(S.ontransitionend)&&
ea(S.onwebkittransitionend)?(T="WebkitTransition",xa="webkitTransitionEnd transitionend"):(T="transition",xa="transitionend");O(S.onanimationend)&&ea(S.onwebkitanimationend)?($="WebkitAnimation",ya="webkitAnimationEnd animationend"):($="animation",ya="animationend");var ra=$+"Delay",za=$+"Duration",ma=T+"Delay",La=T+"Duration",Ma=B.$$minErr("ng"),Ua={transitionDuration:La,transitionDelay:ma,transitionProperty:T+"Property",animationDuration:za,animationDelay:ra,animationIterationCount:$+"IterationCount"},
Va={transitionDuration:La,transitionDelay:ma,animationDuration:za,animationDelay:ra};B.module("ngAnimate",[]).directive("ngAnimateSwap",["$animate","$rootScope",function(a,b){return{restrict:"A",transclude:"element",terminal:!0,priority:600,link:function(b,d,e,l,m){var I,k;b.$watchCollection(e.ngAnimateSwap||e["for"],function(e){I&&a.leave(I);k&&(k.$destroy(),k=null);if(e||0===e)k=b.$new(),m(k,function(b){I=b;a.enter(b,null,d)})})}}}]).directive("ngAnimateChildren",["$interpolate",function(a){return{link:function(b,
c,d){function e(a){c.data("$$ngAnimateChildren","on"===a||"true"===a)}var l=d.ngAnimateChildren;B.isString(l)&&0===l.length?c.data("$$ngAnimateChildren",!0):(e(a(l)(b)),d.$observe("ngAnimateChildren",e))}}}]).factory("$$rAFScheduler",["$$rAF",function(a){function b(a){d=d.concat(a);c()}function c(){if(d.length){for(var b=d.shift(),m=0;m<b.length;m++)b[m]();e||a(function(){e||c()})}}var d,e;d=b.queue=[];b.waitUntilQuiet=function(b){e&&e();e=a(function(){e=null;b();c()})};return b}]).provider("$$animateQueue",
["$animateProvider",function(a){function b(a){if(!a)return null;a=a.split(" ");var b=Object.create(null);s(a,function(a){b[a]=!0});return b}function c(a,c){if(a&&c){var d=b(c);return a.split(" ").some(function(a){return d[a]})}}function d(a,b,c,d){return l[a].some(function(a){return a(b,c,d)})}function e(a,b){var c=0<(a.addClass||"").length,d=0<(a.removeClass||"").length;return b?c&&d:c||d}var l=this.rules={skip:[],cancel:[],join:[]};l.join.push(function(a,b,c){return!b.structural&&e(b)});l.skip.push(function(a,
b,c){return!b.structural&&!e(b)});l.skip.push(function(a,b,c){return"leave"==c.event&&b.structural});l.skip.push(function(a,b,c){return c.structural&&2===c.state&&!b.structural});l.cancel.push(function(a,b,c){return c.structural&&b.structural});l.cancel.push(function(a,b,c){return 2===c.state&&b.structural});l.cancel.push(function(a,b,d){if(d.structural)return!1;a=b.addClass;b=b.removeClass;var e=d.addClass;d=d.removeClass;return O(a)&&O(b)||O(e)&&O(d)?!1:c(a,d)||c(b,e)});this.$get=["$$rAF","$rootScope",
"$rootElement","$document","$$HashMap","$$animation","$$AnimateRunner","$templateRequest","$$jqLite","$$forceReflow",function(b,c,k,l,w,Wa,Q,u,F,n){function K(){var a=!1;return function(b){a?b():c.$$postDigest(function(){a=!0;b()})}}function J(a,b,c){var f=z(b),d=z(a),L=[];(a=h[c])&&s(a,function(a){y.call(a.node,f)?L.push(a.callback):"leave"===c&&y.call(a.node,d)&&L.push(a.callback)});return L}function r(a,b,c){var f=da(b);return a.filter(function(a){return!(a.node===f&&(!c||a.callback===c))})}function t(a,
h,x){function r(c,f,d,h){H(function(){var c=J(oa,a,f);c.length?b(function(){s(c,function(b){b(a,d,h)});"close"!==d||a[0].parentNode||sa.off(a)}):"close"!==d||a[0].parentNode||sa.off(a)});c.progress(f,d,h)}function g(b){var c=a,f=p;f.preparationClasses&&(c.removeClass(f.preparationClasses),f.preparationClasses=null);f.activeClasses&&(c.removeClass(f.activeClasses),f.activeClasses=null);D(a,p);ha(a,p);p.domOperation();n.complete(!b)}var p=Ja(x),t,oa;if(a=Oa(a))t=z(a),oa=a.parent();var p=pa(p),n=new Q,
H=K();ca(p.addClass)&&(p.addClass=p.addClass.join(" "));p.addClass&&!P(p.addClass)&&(p.addClass=null);ca(p.removeClass)&&(p.removeClass=p.removeClass.join(" "));p.removeClass&&!P(p.removeClass)&&(p.removeClass=null);p.from&&!va(p.from)&&(p.from=null);p.to&&!va(p.to)&&(p.to=null);if(!t)return g(),n;x=[t.className,p.addClass,p.removeClass].join(" ");if(!Xa(x))return g(),n;var k=0<=["enter","move","leave"].indexOf(h),y=l[0].hidden,u=!f||y||L.get(t);x=!u&&A.get(t)||{};var F=!!x.state;u||F&&1==x.state||
(u=!N(a,oa,h));if(u)return y&&r(n,h,"start"),g(),y&&r(n,h,"close"),n;k&&M(a);y={structural:k,element:a,event:h,addClass:p.addClass,removeClass:p.removeClass,close:g,options:p,runner:n};if(F){if(d("skip",a,y,x)){if(2===x.state)return g(),n;W(a,x,y);return x.runner}if(d("cancel",a,y,x))if(2===x.state)x.runner.end();else if(x.structural)x.close();else return W(a,x,y),x.runner;else if(d("join",a,y,x))if(2===x.state)W(a,y,{});else return Sa(a,k?h:null,p),h=y.event=x.event,p=W(a,x,y),x.runner}else W(a,
y,{});(F=y.structural)||(F="animate"===y.event&&0<Object.keys(y.options.to||{}).length||e(y));if(!F)return g(),ka(a),n;var w=(x.counter||0)+1;y.counter=w;G(a,1,y);c.$$postDigest(function(){var b=A.get(t),c=!b,b=b||{},f=0<(a.parent()||[]).length&&("animate"===b.event||b.structural||e(b));if(c||b.counter!==w||!f){c&&(D(a,p),ha(a,p));if(c||k&&b.event!==h)p.domOperation(),n.end();f||ka(a)}else h=!b.structural&&e(b,!0)?"setClass":b.event,G(a,2),b=Wa(a,h,b.options),n.setHost(b),r(n,h,"start",{}),b.done(function(b){g(!b);
(b=A.get(t))&&b.counter===w&&ka(z(a));r(n,h,"close",{})})});return n}function M(a){a=z(a).querySelectorAll("[data-ng-animate]");s(a,function(a){var b=parseInt(a.getAttribute("data-ng-animate")),c=A.get(a);if(c)switch(b){case 2:c.runner.end();case 1:A.remove(a)}})}function ka(a){a=z(a);a.removeAttribute("data-ng-animate");A.remove(a)}function g(a,b){return z(a)===z(b)}function N(a,b,c){c=E(l[0].body);var f=g(a,c)||"HTML"===a[0].nodeName,d=g(a,k),h=!1,r,e=L.get(z(a));(a=E.data(a[0],"$ngAnimatePin"))&&
(b=a);for(b=z(b);b;){d||(d=g(b,k));if(1!==b.nodeType)break;a=A.get(b)||{};if(!h){var n=L.get(b);if(!0===n&&!1!==e){e=!0;break}else!1===n&&(e=!1);h=a.structural}if(O(r)||!0===r)a=E.data(b,"$$ngAnimateChildren"),ea(a)&&(r=a);if(h&&!1===r)break;f||(f=g(b,c));if(f&&d)break;if(!d&&(a=E.data(b,"$ngAnimatePin"))){b=z(a);continue}b=b.parentNode}return(!h||r)&&!0!==e&&d&&f}function G(a,b,c){c=c||{};c.state=b;a=z(a);a.setAttribute("data-ng-animate",b);c=(b=A.get(a))?Ea(b,c):c;A.put(a,c)}var A=new w,L=new w,
f=null,oa=c.$watch(function(){return 0===u.totalPendingRequests},function(a){a&&(oa(),c.$$postDigest(function(){c.$$postDigest(function(){null===f&&(f=!0)})}))}),h={},H=a.classNameFilter(),Xa=H?function(a){return H.test(a)}:function(){return!0},D=V(F),y=S.Node.prototype.contains||function(a){return this===a||!!(this.compareDocumentPosition(a)&16)},sa={on:function(a,b,c){var f=da(b);h[a]=h[a]||[];h[a].push({node:f,callback:c});E(b).on("$destroy",function(){A.get(f)||sa.off(a,b,c)})},off:function(a,
b,c){if(1!==arguments.length||B.isString(arguments[0])){var f=h[a];f&&(h[a]=1===arguments.length?null:r(f,b,c))}else for(f in b=arguments[0],h)h[f]=r(h[f],b)},pin:function(a,b){Aa(wa(a),"element","not an element");Aa(wa(b),"parentElement","not an element");a.data("$ngAnimatePin",b)},push:function(a,b,c,f){c=c||{};c.domOperation=f;return t(a,b,c)},enabled:function(a,b){var c=arguments.length;if(0===c)b=!!f;else if(wa(a)){var d=z(a),h=L.get(d);1===c?b=!h:L.put(d,!b)}else b=f=!!a;return b}};return sa}]}]).provider("$$animation",
["$animateProvider",function(a){var b=this.drivers=[];this.$get=["$$jqLite","$rootScope","$injector","$$AnimateRunner","$$HashMap","$$rAFScheduler",function(a,d,e,l,m,I){function k(a){function b(a){if(a.processed)return a;a.processed=!0;var d=a.domNode,t=d.parentNode;e.put(d,a);for(var M;t;){if(M=e.get(t)){M.processed||(M=b(M));break}t=t.parentNode}(M||c).children.push(a);return a}var c={children:[]},d,e=new m;for(d=0;d<a.length;d++){var k=a[d];e.put(k.domNode,a[d]={domNode:k.domNode,fn:k.fn,children:[]})}for(d=
0;d<a.length;d++)b(a[d]);return function(a){var b=[],c=[],d;for(d=0;d<a.children.length;d++)c.push(a.children[d]);a=c.length;var e=0,g=[];for(d=0;d<c.length;d++){var n=c[d];0>=a&&(a=e,e=0,b.push(g),g=[]);g.push(n.fn);n.children.forEach(function(a){e++;c.push(a)});a--}g.length&&b.push(g);return b}(c)}var v=[],w=V(a);return function(m,Q,u){function F(a){a=a.hasAttribute("ng-animate-ref")?[a]:a.querySelectorAll("[ng-animate-ref]");var b=[];s(a,function(a){var c=a.getAttribute("ng-animate-ref");c&&c.length&&
b.push(a)});return b}function n(a){var b=[],c={};s(a,function(a,d){var h=z(a.element),e=0<=["enter","move"].indexOf(a.event),h=a.structural?F(h):[];if(h.length){var g=e?"to":"from";s(h,function(a){var b=a.getAttribute("ng-animate-ref");c[b]=c[b]||{};c[b][g]={animationID:d,element:E(a)}})}else b.push(a)});var d={},e={};s(c,function(c,g){var r=c.from,n=c.to;if(r&&n){var A=a[r.animationID],t=a[n.animationID],m=r.animationID.toString();if(!e[m]){var k=e[m]={structural:!0,beforeStart:function(){A.beforeStart();
t.beforeStart()},close:function(){A.close();t.close()},classes:K(A.classes,t.classes),from:A,to:t,anchors:[]};k.classes.length?b.push(k):(b.push(A),b.push(t))}e[m].anchors.push({out:r.element,"in":n.element})}else r=r?r.animationID:n.animationID,n=r.toString(),d[n]||(d[n]=!0,b.push(a[r]))});return b}function K(a,b){a=a.split(" ");b=b.split(" ");for(var c=[],d=0;d<a.length;d++){var e=a[d];if("ng-"!==e.substring(0,3))for(var r=0;r<b.length;r++)if(e===b[r]){c.push(e);break}}return c.join(" ")}function J(a){for(var c=
b.length-1;0<=c;c--){var d=b[c];if(e.has(d)&&(d=e.get(d)(a)))return d}}function r(a,b){function c(a){(a=a.data("$$animationRunner"))&&a.setHost(b)}a.from&&a.to?(c(a.from.element),c(a.to.element)):c(a.element)}function t(){var a=m.data("$$animationRunner");!a||"leave"===Q&&u.$$domOperationFired||a.end()}function M(b){m.off("$destroy",t);m.removeData("$$animationRunner");w(m,u);ha(m,u);u.domOperation();G&&a.removeClass(m,G);m.removeClass("ng-animate");g.complete(!b)}u=pa(u);var ka=0<=["enter","move",
"leave"].indexOf(Q),g=new l({end:function(){M()},cancel:function(){M(!0)}});if(!b.length)return M(),g;m.data("$$animationRunner",g);var N=Ba(m.attr("class"),Ba(u.addClass,u.removeClass)),G=u.tempClasses;G&&(N+=" "+G,u.tempClasses=null);var A;ka&&(A="ng-"+Q+"-prepare",a.addClass(m,A));v.push({element:m,classes:N,event:Q,structural:ka,options:u,beforeStart:function(){m.addClass("ng-animate");G&&a.addClass(m,G);A&&(a.removeClass(m,A),A=null)},close:M});m.on("$destroy",t);if(1<v.length)return g;d.$$postDigest(function(){var a=
[];s(v,function(b){b.element.data("$$animationRunner")?a.push(b):b.close()});v.length=0;var b=n(a),c=[];s(b,function(a){c.push({domNode:z(a.from?a.from.element:a.element),fn:function(){a.beforeStart();var b,c=a.close;if((a.anchors?a.from.element||a.to.element:a.element).data("$$animationRunner")){var d=J(a);d&&(b=d.start)}b?(b=b(),b.done(function(a){c(!a)}),r(a,b)):c()}})});I(k(c))});return g}}]}]).provider("$animateCss",["$animateProvider",function(a){var b=Ha(),c=Ha();this.$get=["$window","$$jqLite",
"$$AnimateRunner","$timeout","$$forceReflow","$sniffer","$$rAFScheduler","$$animateQueue",function(a,e,l,m,I,k,v,w){function B(a,b){var c=a.parentNode;return(c.$$ngAnimateParentKey||(c.$$ngAnimateParentKey=++K))+"-"+a.getAttribute("class")+"-"+b}function Q(r,n,m,k){var g;0<b.count(m)&&(g=c.get(m),g||(n=Y(n,"-stagger"),e.addClass(r,n),g=Fa(a,r,k),g.animationDuration=Math.max(g.animationDuration,0),g.transitionDuration=Math.max(g.transitionDuration,0),e.removeClass(r,n),c.put(m,g)));return g||{}}function u(a){J.push(a);
v.waitUntilQuiet(function(){b.flush();c.flush();for(var a=I(),d=0;d<J.length;d++)J[d](a);J.length=0})}function F(c,e,n){e=b.get(n);e||(e=Fa(a,c,Ua),"infinite"===e.animationIterationCount&&(e.animationIterationCount=1));b.put(n,e);c=e;n=c.animationDelay;e=c.transitionDelay;c.maxDelay=n&&e?Math.max(n,e):n||e;c.maxDuration=Math.max(c.animationDuration*c.animationIterationCount,c.transitionDuration);return c}var n=V(e),K=0,J=[];return function(a,c){function d(){g()}function v(){g(!0)}function g(b){if(!(y||
E&&K)){y=!0;K=!1;f.$$skipPreparationClasses||e.removeClass(a,ga);e.removeClass(a,ea);ta(h,!1);qa(h,!1);s(H,function(a){h.style[a[0]]=""});n(a,f);ha(a,f);Object.keys(J).length&&s(J,function(a,b){a?h.style.setProperty(b,a):h.style.removeProperty(b)});if(f.onDone)f.onDone();fa&&fa.length&&a.off(fa.join(" "),A);var c=a.data("$$animateCss");c&&(m.cancel(c[0].timer),a.removeData("$$animateCss"));O&&O.complete(!b)}}function N(a){q.blockTransition&&qa(h,a);q.blockKeyframeAnimation&&ta(h,!!a)}function G(){O=
new l({end:d,cancel:v});u(R);g();return{$$willAnimate:!1,start:function(){return O},end:d}}function A(a){a.stopPropagation();var b=a.originalEvent||a;a=b.$manualTimeStamp||Date.now();b=parseFloat(b.elapsedTime.toFixed(3));Math.max(a-W,0)>=S&&b>=p&&(E=!0,g())}function L(){function b(){if(!y){N(!1);s(H,function(a){h.style[a[0]]=a[1]});n(a,f);e.addClass(a,ea);if(q.recalculateTimingStyles){na=h.className+" "+ga;ia=B(h,na);C=F(h,na,ia);aa=C.maxDelay;P=Math.max(aa,0);p=C.maxDuration;if(0===p){g();return}q.hasTransitions=
0<C.transitionDuration;q.hasAnimations=0<C.animationDuration}q.applyAnimationDelay&&(aa="boolean"!==typeof f.delay&&ua(f.delay)?parseFloat(f.delay):aa,P=Math.max(aa,0),C.animationDelay=aa,ba=[ra,aa+"s"],H.push(ba),h.style[ba[0]]=ba[1]);S=1E3*P;V=1E3*p;if(f.easing){var d,k=f.easing;q.hasTransitions&&(d=T+"TimingFunction",H.push([d,k]),h.style[d]=k);q.hasAnimations&&(d=$+"TimingFunction",H.push([d,k]),h.style[d]=k)}C.transitionDuration&&fa.push(xa);C.animationDuration&&fa.push(ya);W=Date.now();var L=
S+1.5*V;d=W+L;var k=a.data("$$animateCss")||[],l=!0;if(k.length){var G=k[0];(l=d>G.expectedEndTime)?m.cancel(G.timer):k.push(g)}l&&(L=m(c,L,!1),k[0]={timer:L,expectedEndTime:d},k.push(g),a.data("$$animateCss",k));if(fa.length)a.on(fa.join(" "),A);f.to&&(f.cleanupStyles&&Ia(J,h,Object.keys(f.to)),Da(a,f))}}function c(){var b=a.data("$$animateCss");if(b){for(var d=1;d<b.length;d++)b[d]();a.removeData("$$animateCss")}}if(!y)if(h.parentNode){var d=function(a){if(E)K&&a&&(K=!1,g());else if(K=!a,C.animationDuration)if(a=
ta(h,K),K)H.push(a);else{var b=H,c=b.indexOf(a);0<=a&&b.splice(c,1)}},k=0<da&&(C.transitionDuration&&0===X.transitionDuration||C.animationDuration&&0===X.animationDuration)&&Math.max(X.animationDelay,X.transitionDelay);k?m(b,Math.floor(k*da*1E3),!1):b();x.resume=function(){d(!0)};x.pause=function(){d(!1)}}else g()}var f=c||{};f.$$prepared||(f=pa(Ja(f)));var J={},h=z(a);if(!h||!h.parentNode||!w.enabled())return G();var H=[],I=a.attr("class"),D=Na(f),y,K,E,O,x,P,S,p,V,W,fa=[];if(0===f.duration||!k.animations&&
!k.transitions)return G();var ja=f.event&&ca(f.event)?f.event.join(" "):f.event,Z="",U="";ja&&f.structural?Z=Y(ja,"ng-",!0):ja&&(Z=ja);f.addClass&&(U+=Y(f.addClass,"-add"));f.removeClass&&(U.length&&(U+=" "),U+=Y(f.removeClass,"-remove"));f.applyClassesEarly&&U.length&&n(a,f);var ga=[Z,U].join(" ").trim(),na=I+" "+ga,ea=Y(ga,"-active"),I=D.to&&0<Object.keys(D.to).length;if(!(0<(f.keyframeStyle||"").length||I||ga))return G();var ia,X;0<f.stagger?(D=parseFloat(f.stagger),X={transitionDelay:D,animationDelay:D,
transitionDuration:0,animationDuration:0}):(ia=B(h,na),X=Q(h,ga,ia,Va));f.$$skipPreparationClasses||e.addClass(a,ga);f.transitionStyle&&(D=[T,f.transitionStyle],la(h,D),H.push(D));0<=f.duration&&(D=0<h.style[T].length,D=Ga(f.duration,D),la(h,D),H.push(D));f.keyframeStyle&&(D=[$,f.keyframeStyle],la(h,D),H.push(D));var da=X?0<=f.staggerIndex?f.staggerIndex:b.count(ia):0;(ja=0===da)&&!f.skipBlocking&&qa(h,9999);var C=F(h,na,ia),aa=C.maxDelay;P=Math.max(aa,0);p=C.maxDuration;var q={};q.hasTransitions=
0<C.transitionDuration;q.hasAnimations=0<C.animationDuration;q.hasTransitionAll=q.hasTransitions&&"all"==C.transitionProperty;q.applyTransitionDuration=I&&(q.hasTransitions&&!q.hasTransitionAll||q.hasAnimations&&!q.hasTransitions);q.applyAnimationDuration=f.duration&&q.hasAnimations;q.applyTransitionDelay=ua(f.delay)&&(q.applyTransitionDuration||q.hasTransitions);q.applyAnimationDelay=ua(f.delay)&&q.hasAnimations;q.recalculateTimingStyles=0<U.length;if(q.applyTransitionDuration||q.applyAnimationDuration)p=
f.duration?parseFloat(f.duration):p,q.applyTransitionDuration&&(q.hasTransitions=!0,C.transitionDuration=p,D=0<h.style[T+"Property"].length,H.push(Ga(p,D))),q.applyAnimationDuration&&(q.hasAnimations=!0,C.animationDuration=p,H.push([za,p+"s"]));if(0===p&&!q.recalculateTimingStyles)return G();if(null!=f.delay){var ba;"boolean"!==typeof f.delay&&(ba=parseFloat(f.delay),P=Math.max(ba,0));q.applyTransitionDelay&&H.push([ma,ba+"s"]);q.applyAnimationDelay&&H.push([ra,ba+"s"])}null==f.duration&&0<C.transitionDuration&&
(q.recalculateTimingStyles=q.recalculateTimingStyles||ja);S=1E3*P;V=1E3*p;f.skipBlocking||(q.blockTransition=0<C.transitionDuration,q.blockKeyframeAnimation=0<C.animationDuration&&0<X.animationDelay&&0===X.animationDuration);f.from&&(f.cleanupStyles&&Ia(J,h,Object.keys(f.from)),Ca(a,f));q.blockTransition||q.blockKeyframeAnimation?N(p):f.skipBlocking||qa(h,!1);return{$$willAnimate:!0,end:d,start:function(){if(!y)return x={end:d,cancel:v,resume:null,pause:null},O=new l(x),u(L),O}}}}]}]).provider("$$animateCssDriver",
["$$animationProvider",function(a){a.drivers.push("$$animateCssDriver");this.$get=["$animateCss","$rootScope","$$AnimateRunner","$rootElement","$sniffer","$$jqLite","$document",function(a,c,d,e,l,m,I){function k(a){return a.replace(/\bng-\S+\b/g,"")}function v(a,b){P(a)&&(a=a.split(" "));P(b)&&(b=b.split(" "));return a.filter(function(a){return-1===b.indexOf(a)}).join(" ")}function w(c,e,m){function l(a){var b={},c=z(a).getBoundingClientRect();s(["width","height","top","left"],function(a){var d=c[a];
switch(a){case "top":d+=u.scrollTop;break;case "left":d+=u.scrollLeft}b[a]=Math.floor(d)+"px"});return b}function t(){var c=k(m.attr("class")||""),d=v(c,g),c=v(g,c),d=a(w,{to:l(m),addClass:"ng-anchor-in "+d,removeClass:"ng-anchor-out "+c,delay:!0});return d.$$willAnimate?d:null}function I(){w.remove();e.removeClass("ng-animate-shim");m.removeClass("ng-animate-shim")}var w=E(z(e).cloneNode(!0)),g=k(w.attr("class")||"");e.addClass("ng-animate-shim");m.addClass("ng-animate-shim");w.addClass("ng-anchor");
F.append(w);var N;c=function(){var c=a(w,{addClass:"ng-anchor-out",delay:!0,from:l(e)});return c.$$willAnimate?c:null}();if(!c&&(N=t(),!N))return I();var G=c||N;return{start:function(){function a(){c&&c.end()}var b,c=G.start();c.done(function(){c=null;if(!N&&(N=t()))return c=N.start(),c.done(function(){c=null;I();b.complete()}),c;I();b.complete()});return b=new d({end:a,cancel:a})}}}function B(a,b,c,e){var k=Q(a,R),m=Q(b,R),l=[];s(e,function(a){(a=w(c,a.out,a["in"]))&&l.push(a)});if(k||m||0!==l.length)return{start:function(){function a(){s(b,
function(a){a.end()})}var b=[];k&&b.push(k.start());m&&b.push(m.start());s(l,function(a){b.push(a.start())});var c=new d({end:a,cancel:a});d.all(b,function(a){c.complete(a)});return c}}}function Q(c){var d=c.element,e=c.options||{};c.structural&&(e.event=c.event,e.structural=!0,e.applyClassesEarly=!0,"leave"===c.event&&(e.onDone=e.domOperation));e.preparationClasses&&(e.event=Z(e.event,e.preparationClasses));c=a(d,e);return c.$$willAnimate?c:null}if(!l.animations&&!l.transitions)return R;var u=I[0].body;
c=z(e);var F=E(c.parentNode&&11===c.parentNode.nodeType||u.contains(c)?c:u);V(m);return function(a){return a.from&&a.to?B(a.from,a.to,a.classes,a.anchors):Q(a)}}]}]).provider("$$animateJs",["$animateProvider",function(a){this.$get=["$injector","$$AnimateRunner","$$jqLite",function(b,c,d){function e(c){c=ca(c)?c:c.split(" ");for(var d=[],e={},l=0;l<c.length;l++){var w=c[l],s=a.$$registeredAnimations[w];s&&!e[w]&&(d.push(b.get(s)),e[w]=!0)}return d}var l=V(d);return function(a,b,d,v){function w(){v.domOperation();
l(a,v)}function B(a,b,d,e,f){switch(d){case "animate":b=[b,e.from,e.to,f];break;case "setClass":b=[b,n,K,f];break;case "addClass":b=[b,n,f];break;case "removeClass":b=[b,K,f];break;default:b=[b,f]}b.push(e);if(a=a.apply(a,b))if(Ka(a.start)&&(a=a.start()),a instanceof c)a.done(f);else if(Ka(a))return a;return R}function z(a,b,d,e,f){var g=[];s(e,function(e){var k=e[f];k&&g.push(function(){var e,f,h=!1,g=function(a){h||(h=!0,(f||R)(a),e.complete(!a))};e=new c({end:function(){g()},cancel:function(){g(!0)}});
f=B(k,a,b,d,function(a){g(!1===a)});return e})});return g}function u(a,b,d,e,f){var g=z(a,b,d,e,f);if(0===g.length){var h,k;"beforeSetClass"===f?(h=z(a,"removeClass",d,e,"beforeRemoveClass"),k=z(a,"addClass",d,e,"beforeAddClass")):"setClass"===f&&(h=z(a,"removeClass",d,e,"removeClass"),k=z(a,"addClass",d,e,"addClass"));h&&(g=g.concat(h));k&&(g=g.concat(k))}if(0!==g.length)return function(a){var b=[];g.length&&s(g,function(a){b.push(a())});b.length?c.all(b,a):a();return function(a){s(b,function(b){a?
b.cancel():b.end()})}}}var F=!1;3===arguments.length&&va(d)&&(v=d,d=null);v=pa(v);d||(d=a.attr("class")||"",v.addClass&&(d+=" "+v.addClass),v.removeClass&&(d+=" "+v.removeClass));var n=v.addClass,K=v.removeClass,J=e(d),r,t;if(J.length){var M,E;"leave"==b?(E="leave",M="afterLeave"):(E="before"+b.charAt(0).toUpperCase()+b.substr(1),M=b);"enter"!==b&&"move"!==b&&(r=u(a,b,v,J,E));t=u(a,b,v,J,M)}if(r||t){var g;return{$$willAnimate:!0,end:function(){g?g.end():(F=!0,w(),ha(a,v),g=new c,g.complete(!0));return g},
start:function(){function b(c){F=!0;w();ha(a,v);g.complete(c)}if(g)return g;g=new c;var d,e=[];r&&e.push(function(a){d=r(a)});e.length?e.push(function(a){w();a(!0)}):w();t&&e.push(function(a){d=t(a)});g.setHost({end:function(){F||((d||R)(void 0),b(void 0))},cancel:function(){F||((d||R)(!0),b(!0))}});c.chain(e,b);return g}}}}}]}]).provider("$$animateJsDriver",["$$animationProvider",function(a){a.drivers.push("$$animateJsDriver");this.$get=["$$animateJs","$$AnimateRunner",function(a,c){function d(c){return a(c.element,
c.event,c.classes,c.options)}return function(a){if(a.from&&a.to){var b=d(a.from),m=d(a.to);if(b||m)return{start:function(){function a(){return function(){s(d,function(a){a.end()})}}var d=[];b&&d.push(b.start());m&&d.push(m.start());c.all(d,function(a){e.complete(a)});var e=new c({end:a(),cancel:a()});return e}}}else return d(a)}}]}])})(window,window.angular);
//# sourceMappingURL=angular-animate.min.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,405 @@
/**
* @license AngularJS v1.5.6
* (c) 2010-2016 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular) {'use strict';
/**
* @ngdoc module
* @name ngAria
* @description
*
* The `ngAria` module provides support for common
* [<abbr title="Accessible Rich Internet Applications">ARIA</abbr>](http://www.w3.org/TR/wai-aria/)
* attributes that convey state or semantic information about the application for users
* of assistive technologies, such as screen readers.
*
* <div doc-module-components="ngAria"></div>
*
* ## Usage
*
* For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following
* directives are supported:
* `ngModel`, `ngChecked`, `ngReadonly`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
* `ngDblClick`, and `ngMessages`.
*
* Below is a more detailed breakdown of the attributes handled by ngAria:
*
* | Directive | Supported Attributes |
* |---------------------------------------------|----------------------------------------------------------------------------------------|
* | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
* | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled |
* | {@link ng.directive:ngRequired ngRequired} | aria-required
* | {@link ng.directive:ngChecked ngChecked} | aria-checked
* | {@link ng.directive:ngReadonly ngReadonly} | aria-readonly |
* | {@link ng.directive:ngValue ngValue} | aria-checked |
* | {@link ng.directive:ngShow ngShow} | aria-hidden |
* | {@link ng.directive:ngHide ngHide} | aria-hidden |
* | {@link ng.directive:ngDblclick ngDblclick} | tabindex |
* | {@link module:ngMessages ngMessages} | aria-live |
* | {@link ng.directive:ngClick ngClick} | tabindex, keypress event, button role |
*
* Find out more information about each directive by reading the
* {@link guide/accessibility ngAria Developer Guide}.
*
* ## Example
* Using ngDisabled with ngAria:
* ```html
* <md-checkbox ng-disabled="disabled">
* ```
* Becomes:
* ```html
* <md-checkbox ng-disabled="disabled" aria-disabled="true">
* ```
*
* ## Disabling Attributes
* It's possible to disable individual attributes added by ngAria with the
* {@link ngAria.$ariaProvider#config config} method. For more details, see the
* {@link guide/accessibility Developer Guide}.
*/
/* global -ngAriaModule */
var ngAriaModule = angular.module('ngAria', ['ng']).
provider('$aria', $AriaProvider);
/**
* Internal Utilities
*/
var nodeBlackList = ['BUTTON', 'A', 'INPUT', 'TEXTAREA', 'SELECT', 'DETAILS', 'SUMMARY'];
var isNodeOneOf = function(elem, nodeTypeArray) {
if (nodeTypeArray.indexOf(elem[0].nodeName) !== -1) {
return true;
}
};
/**
* @ngdoc provider
* @name $ariaProvider
*
* @description
*
* Used for configuring the ARIA attributes injected and managed by ngAria.
*
* ```js
* angular.module('myApp', ['ngAria'], function config($ariaProvider) {
* $ariaProvider.config({
* ariaValue: true,
* tabindex: false
* });
* });
*```
*
* ## Dependencies
* Requires the {@link ngAria} module to be installed.
*
*/
function $AriaProvider() {
var config = {
ariaHidden: true,
ariaChecked: true,
ariaReadonly: true,
ariaDisabled: true,
ariaRequired: true,
ariaInvalid: true,
ariaValue: true,
tabindex: true,
bindKeypress: true,
bindRoleForClick: true
};
/**
* @ngdoc method
* @name $ariaProvider#config
*
* @param {object} config object to enable/disable specific ARIA attributes
*
* - **ariaHidden** `{boolean}` Enables/disables aria-hidden tags
* - **ariaChecked** `{boolean}` Enables/disables aria-checked tags
* - **ariaReadonly** `{boolean}` Enables/disables aria-readonly tags
* - **ariaDisabled** `{boolean}` Enables/disables aria-disabled tags
* - **ariaRequired** `{boolean}` Enables/disables aria-required tags
* - **ariaInvalid** `{boolean}` Enables/disables aria-invalid tags
* - **ariaValue** `{boolean}` Enables/disables aria-valuemin, aria-valuemax and aria-valuenow tags
* - **tabindex** `{boolean}` Enables/disables tabindex tags
* - **bindKeypress** `{boolean}` Enables/disables keypress event binding on `div` and
* `li` elements with ng-click
* - **bindRoleForClick** `{boolean}` Adds role=button to non-interactive elements like `div`
* using ng-click, making them more accessible to users of assistive technologies
*
* @description
* Enables/disables various ARIA attributes
*/
this.config = function(newConfig) {
config = angular.extend(config, newConfig);
};
function watchExpr(attrName, ariaAttr, nodeBlackList, negate) {
return function(scope, elem, attr) {
var ariaCamelName = attr.$normalize(ariaAttr);
if (config[ariaCamelName] && !isNodeOneOf(elem, nodeBlackList) && !attr[ariaCamelName]) {
scope.$watch(attr[attrName], function(boolVal) {
// ensure boolean value
boolVal = negate ? !boolVal : !!boolVal;
elem.attr(ariaAttr, boolVal);
});
}
};
}
/**
* @ngdoc service
* @name $aria
*
* @description
* @priority 200
*
* The $aria service contains helper methods for applying common
* [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives.
*
* ngAria injects common accessibility attributes that tell assistive technologies when HTML
* elements are enabled, selected, hidden, and more. To see how this is performed with ngAria,
* let's review a code snippet from ngAria itself:
*
*```js
* ngAriaModule.directive('ngDisabled', ['$aria', function($aria) {
* return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
* }])
*```
* Shown above, the ngAria module creates a directive with the same signature as the
* traditional `ng-disabled` directive. But this ngAria version is dedicated to
* solely managing accessibility attributes on custom elements. The internal `$aria` service is
* used to watch the boolean attribute `ngDisabled`. If it has not been explicitly set by the
* developer, `aria-disabled` is injected as an attribute with its value synchronized to the
* value in `ngDisabled`.
*
* Because ngAria hooks into the `ng-disabled` directive, developers do not have to do
* anything to enable this feature. The `aria-disabled` attribute is automatically managed
* simply as a silent side-effect of using `ng-disabled` with the ngAria module.
*
* The full list of directives that interface with ngAria:
* * **ngModel**
* * **ngChecked**
* * **ngReadonly**
* * **ngRequired**
* * **ngDisabled**
* * **ngValue**
* * **ngShow**
* * **ngHide**
* * **ngClick**
* * **ngDblclick**
* * **ngMessages**
*
* Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each
* directive.
*
*
* ## Dependencies
* Requires the {@link ngAria} module to be installed.
*/
this.$get = function() {
return {
config: function(key) {
return config[key];
},
$$watchExpr: watchExpr
};
};
}
ngAriaModule.directive('ngShow', ['$aria', function($aria) {
return $aria.$$watchExpr('ngShow', 'aria-hidden', [], true);
}])
.directive('ngHide', ['$aria', function($aria) {
return $aria.$$watchExpr('ngHide', 'aria-hidden', [], false);
}])
.directive('ngValue', ['$aria', function($aria) {
return $aria.$$watchExpr('ngValue', 'aria-checked', nodeBlackList, false);
}])
.directive('ngChecked', ['$aria', function($aria) {
return $aria.$$watchExpr('ngChecked', 'aria-checked', nodeBlackList, false);
}])
.directive('ngReadonly', ['$aria', function($aria) {
return $aria.$$watchExpr('ngReadonly', 'aria-readonly', nodeBlackList, false);
}])
.directive('ngRequired', ['$aria', function($aria) {
return $aria.$$watchExpr('ngRequired', 'aria-required', nodeBlackList, false);
}])
.directive('ngModel', ['$aria', function($aria) {
function shouldAttachAttr(attr, normalizedAttr, elem, allowBlacklistEls) {
return $aria.config(normalizedAttr) && !elem.attr(attr) && (allowBlacklistEls || !isNodeOneOf(elem, nodeBlackList));
}
function shouldAttachRole(role, elem) {
// if element does not have role attribute
// AND element type is equal to role (if custom element has a type equaling shape) <-- remove?
// AND element is not INPUT
return !elem.attr('role') && (elem.attr('type') === role) && (elem[0].nodeName !== 'INPUT');
}
function getShape(attr, elem) {
var type = attr.type,
role = attr.role;
return ((type || role) === 'checkbox' || role === 'menuitemcheckbox') ? 'checkbox' :
((type || role) === 'radio' || role === 'menuitemradio') ? 'radio' :
(type === 'range' || role === 'progressbar' || role === 'slider') ? 'range' : '';
}
return {
restrict: 'A',
require: 'ngModel',
priority: 200, //Make sure watches are fired after any other directives that affect the ngModel value
compile: function(elem, attr) {
var shape = getShape(attr, elem);
return {
pre: function(scope, elem, attr, ngModel) {
if (shape === 'checkbox') {
//Use the input[checkbox] $isEmpty implementation for elements with checkbox roles
ngModel.$isEmpty = function(value) {
return value === false;
};
}
},
post: function(scope, elem, attr, ngModel) {
var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem, false);
function ngAriaWatchModelValue() {
return ngModel.$modelValue;
}
function getRadioReaction(newVal) {
var boolVal = (attr.value == ngModel.$viewValue);
elem.attr('aria-checked', boolVal);
}
function getCheckboxReaction() {
elem.attr('aria-checked', !ngModel.$isEmpty(ngModel.$viewValue));
}
switch (shape) {
case 'radio':
case 'checkbox':
if (shouldAttachRole(shape, elem)) {
elem.attr('role', shape);
}
if (shouldAttachAttr('aria-checked', 'ariaChecked', elem, false)) {
scope.$watch(ngAriaWatchModelValue, shape === 'radio' ?
getRadioReaction : getCheckboxReaction);
}
if (needsTabIndex) {
elem.attr('tabindex', 0);
}
break;
case 'range':
if (shouldAttachRole(shape, elem)) {
elem.attr('role', 'slider');
}
if ($aria.config('ariaValue')) {
var needsAriaValuemin = !elem.attr('aria-valuemin') &&
(attr.hasOwnProperty('min') || attr.hasOwnProperty('ngMin'));
var needsAriaValuemax = !elem.attr('aria-valuemax') &&
(attr.hasOwnProperty('max') || attr.hasOwnProperty('ngMax'));
var needsAriaValuenow = !elem.attr('aria-valuenow');
if (needsAriaValuemin) {
attr.$observe('min', function ngAriaValueMinReaction(newVal) {
elem.attr('aria-valuemin', newVal);
});
}
if (needsAriaValuemax) {
attr.$observe('max', function ngAriaValueMinReaction(newVal) {
elem.attr('aria-valuemax', newVal);
});
}
if (needsAriaValuenow) {
scope.$watch(ngAriaWatchModelValue, function ngAriaValueNowReaction(newVal) {
elem.attr('aria-valuenow', newVal);
});
}
}
if (needsTabIndex) {
elem.attr('tabindex', 0);
}
break;
}
if (!attr.hasOwnProperty('ngRequired') && ngModel.$validators.required
&& shouldAttachAttr('aria-required', 'ariaRequired', elem, false)) {
// ngModel.$error.required is undefined on custom controls
attr.$observe('required', function() {
elem.attr('aria-required', !!attr['required']);
});
}
if (shouldAttachAttr('aria-invalid', 'ariaInvalid', elem, true)) {
scope.$watch(function ngAriaInvalidWatch() {
return ngModel.$invalid;
}, function ngAriaInvalidReaction(newVal) {
elem.attr('aria-invalid', !!newVal);
});
}
}
};
}
};
}])
.directive('ngDisabled', ['$aria', function($aria) {
return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
}])
.directive('ngMessages', function() {
return {
restrict: 'A',
require: '?ngMessages',
link: function(scope, elem, attr, ngMessages) {
if (!elem.attr('aria-live')) {
elem.attr('aria-live', 'assertive');
}
}
};
})
.directive('ngClick',['$aria', '$parse', function($aria, $parse) {
return {
restrict: 'A',
compile: function(elem, attr) {
var fn = $parse(attr.ngClick, /* interceptorFn */ null, /* expensiveChecks */ true);
return function(scope, elem, attr) {
if (!isNodeOneOf(elem, nodeBlackList)) {
if ($aria.config('bindRoleForClick') && !elem.attr('role')) {
elem.attr('role', 'button');
}
if ($aria.config('tabindex') && !elem.attr('tabindex')) {
elem.attr('tabindex', 0);
}
if ($aria.config('bindKeypress') && !attr.ngKeypress) {
elem.on('keypress', function(event) {
var keyCode = event.which || event.keyCode;
if (keyCode === 32 || keyCode === 13) {
scope.$apply(callback);
}
function callback() {
fn(scope, { $event: event });
}
});
}
}
};
}
};
}])
.directive('ngDblclick', ['$aria', function($aria) {
return function(scope, elem, attr) {
if ($aria.config('tabindex') && !elem.attr('tabindex') && !isNodeOneOf(elem, nodeBlackList)) {
elem.attr('tabindex', 0);
}
};
}]);
})(window, window.angular);

View File

@@ -0,0 +1,14 @@
/*
AngularJS v1.5.6
(c) 2010-2016 Google, Inc. http://angularjs.org
License: MIT
*/
(function(t,p){'use strict';var b="BUTTON A INPUT TEXTAREA SELECT DETAILS SUMMARY".split(" "),l=function(a,c){if(-1!==c.indexOf(a[0].nodeName))return!0};p.module("ngAria",["ng"]).provider("$aria",function(){function a(a,b,m,h){return function(d,f,e){var q=e.$normalize(b);!c[q]||l(f,m)||e[q]||d.$watch(e[a],function(a){a=h?!a:!!a;f.attr(b,a)})}}var c={ariaHidden:!0,ariaChecked:!0,ariaReadonly:!0,ariaDisabled:!0,ariaRequired:!0,ariaInvalid:!0,ariaValue:!0,tabindex:!0,bindKeypress:!0,bindRoleForClick:!0};
this.config=function(a){c=p.extend(c,a)};this.$get=function(){return{config:function(a){return c[a]},$$watchExpr:a}}}).directive("ngShow",["$aria",function(a){return a.$$watchExpr("ngShow","aria-hidden",[],!0)}]).directive("ngHide",["$aria",function(a){return a.$$watchExpr("ngHide","aria-hidden",[],!1)}]).directive("ngValue",["$aria",function(a){return a.$$watchExpr("ngValue","aria-checked",b,!1)}]).directive("ngChecked",["$aria",function(a){return a.$$watchExpr("ngChecked","aria-checked",b,!1)}]).directive("ngReadonly",
["$aria",function(a){return a.$$watchExpr("ngReadonly","aria-readonly",b,!1)}]).directive("ngRequired",["$aria",function(a){return a.$$watchExpr("ngRequired","aria-required",b,!1)}]).directive("ngModel",["$aria",function(a){function c(c,h,d,f){return a.config(h)&&!d.attr(c)&&(f||!l(d,b))}function g(a,c){return!c.attr("role")&&c.attr("type")===a&&"INPUT"!==c[0].nodeName}function k(a,c){var d=a.type,f=a.role;return"checkbox"===(d||f)||"menuitemcheckbox"===f?"checkbox":"radio"===(d||f)||"menuitemradio"===
f?"radio":"range"===d||"progressbar"===f||"slider"===f?"range":""}return{restrict:"A",require:"ngModel",priority:200,compile:function(b,h){var d=k(h,b);return{pre:function(a,e,c,b){"checkbox"===d&&(b.$isEmpty=function(a){return!1===a})},post:function(f,e,b,n){function h(){return n.$modelValue}function k(a){e.attr("aria-checked",b.value==n.$viewValue)}function l(){e.attr("aria-checked",!n.$isEmpty(n.$viewValue))}var m=c("tabindex","tabindex",e,!1);switch(d){case "radio":case "checkbox":g(d,e)&&e.attr("role",
d);c("aria-checked","ariaChecked",e,!1)&&f.$watch(h,"radio"===d?k:l);m&&e.attr("tabindex",0);break;case "range":g(d,e)&&e.attr("role","slider");if(a.config("ariaValue")){var p=!e.attr("aria-valuemin")&&(b.hasOwnProperty("min")||b.hasOwnProperty("ngMin")),r=!e.attr("aria-valuemax")&&(b.hasOwnProperty("max")||b.hasOwnProperty("ngMax")),s=!e.attr("aria-valuenow");p&&b.$observe("min",function(a){e.attr("aria-valuemin",a)});r&&b.$observe("max",function(a){e.attr("aria-valuemax",a)});s&&f.$watch(h,function(a){e.attr("aria-valuenow",
a)})}m&&e.attr("tabindex",0)}!b.hasOwnProperty("ngRequired")&&n.$validators.required&&c("aria-required","ariaRequired",e,!1)&&b.$observe("required",function(){e.attr("aria-required",!!b.required)});c("aria-invalid","ariaInvalid",e,!0)&&f.$watch(function(){return n.$invalid},function(a){e.attr("aria-invalid",!!a)})}}}}}]).directive("ngDisabled",["$aria",function(a){return a.$$watchExpr("ngDisabled","aria-disabled",b,!1)}]).directive("ngMessages",function(){return{restrict:"A",require:"?ngMessages",
link:function(a,b,g,k){b.attr("aria-live")||b.attr("aria-live","assertive")}}}).directive("ngClick",["$aria","$parse",function(a,c){return{restrict:"A",compile:function(g,k){var m=c(k.ngClick,null,!0);return function(c,d,f){if(!l(d,b)&&(a.config("bindRoleForClick")&&!d.attr("role")&&d.attr("role","button"),a.config("tabindex")&&!d.attr("tabindex")&&d.attr("tabindex",0),a.config("bindKeypress")&&!f.ngKeypress))d.on("keypress",function(a){function b(){m(c,{$event:a})}var d=a.which||a.keyCode;32!==d&&
13!==d||c.$apply(b)})}}}}]).directive("ngDblclick",["$aria",function(a){return function(c,g,k){!a.config("tabindex")||g.attr("tabindex")||l(g,b)||g.attr("tabindex",0)}}])})(window,window.angular);
//# sourceMappingURL=angular-aria.min.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,322 @@
/**
* @license AngularJS v1.5.6
* (c) 2010-2016 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular) {'use strict';
/**
* @ngdoc module
* @name ngCookies
* @description
*
* # ngCookies
*
* The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies.
*
*
* <div doc-module-components="ngCookies"></div>
*
* See {@link ngCookies.$cookies `$cookies`} for usage.
*/
angular.module('ngCookies', ['ng']).
/**
* @ngdoc provider
* @name $cookiesProvider
* @description
* Use `$cookiesProvider` to change the default behavior of the {@link ngCookies.$cookies $cookies} service.
* */
provider('$cookies', [function $CookiesProvider() {
/**
* @ngdoc property
* @name $cookiesProvider#defaults
* @description
*
* Object containing default options to pass when setting cookies.
*
* The object may have following properties:
*
* - **path** - `{string}` - The cookie will be available only for this path and its
* sub-paths. By default, this is the URL that appears in your `<base>` tag.
* - **domain** - `{string}` - The cookie will be available only for this domain and
* its sub-domains. For security reasons the user agent will not accept the cookie
* if the current domain is not a sub-domain of this domain or equal to it.
* - **expires** - `{string|Date}` - String of the form "Wdy, DD Mon YYYY HH:MM:SS GMT"
* or a Date object indicating the exact date/time this cookie will expire.
* - **secure** - `{boolean}` - If `true`, then the cookie will only be available through a
* secured connection.
*
* Note: By default, the address that appears in your `<base>` tag will be used as the path.
* This is important so that cookies will be visible for all routes when html5mode is enabled.
*
**/
var defaults = this.defaults = {};
function calcOptions(options) {
return options ? angular.extend({}, defaults, options) : defaults;
}
/**
* @ngdoc service
* @name $cookies
*
* @description
* Provides read/write access to browser's cookies.
*
* <div class="alert alert-info">
* Up until Angular 1.3, `$cookies` exposed properties that represented the
* current browser cookie values. In version 1.4, this behavior has changed, and
* `$cookies` now provides a standard api of getters, setters etc.
* </div>
*
* Requires the {@link ngCookies `ngCookies`} module to be installed.
*
* @example
*
* ```js
* angular.module('cookiesExample', ['ngCookies'])
* .controller('ExampleController', ['$cookies', function($cookies) {
* // Retrieving a cookie
* var favoriteCookie = $cookies.get('myFavorite');
* // Setting a cookie
* $cookies.put('myFavorite', 'oatmeal');
* }]);
* ```
*/
this.$get = ['$$cookieReader', '$$cookieWriter', function($$cookieReader, $$cookieWriter) {
return {
/**
* @ngdoc method
* @name $cookies#get
*
* @description
* Returns the value of given cookie key
*
* @param {string} key Id to use for lookup.
* @returns {string} Raw cookie value.
*/
get: function(key) {
return $$cookieReader()[key];
},
/**
* @ngdoc method
* @name $cookies#getObject
*
* @description
* Returns the deserialized value of given cookie key
*
* @param {string} key Id to use for lookup.
* @returns {Object} Deserialized cookie value.
*/
getObject: function(key) {
var value = this.get(key);
return value ? angular.fromJson(value) : value;
},
/**
* @ngdoc method
* @name $cookies#getAll
*
* @description
* Returns a key value object with all the cookies
*
* @returns {Object} All cookies
*/
getAll: function() {
return $$cookieReader();
},
/**
* @ngdoc method
* @name $cookies#put
*
* @description
* Sets a value for given cookie key
*
* @param {string} key Id for the `value`.
* @param {string} value Raw value to be stored.
* @param {Object=} options Options object.
* See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
*/
put: function(key, value, options) {
$$cookieWriter(key, value, calcOptions(options));
},
/**
* @ngdoc method
* @name $cookies#putObject
*
* @description
* Serializes and sets a value for given cookie key
*
* @param {string} key Id for the `value`.
* @param {Object} value Value to be stored.
* @param {Object=} options Options object.
* See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
*/
putObject: function(key, value, options) {
this.put(key, angular.toJson(value), options);
},
/**
* @ngdoc method
* @name $cookies#remove
*
* @description
* Remove given cookie
*
* @param {string} key Id of the key-value pair to delete.
* @param {Object=} options Options object.
* See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
*/
remove: function(key, options) {
$$cookieWriter(key, undefined, calcOptions(options));
}
};
}];
}]);
angular.module('ngCookies').
/**
* @ngdoc service
* @name $cookieStore
* @deprecated
* @requires $cookies
*
* @description
* Provides a key-value (string-object) storage, that is backed by session cookies.
* Objects put or retrieved from this storage are automatically serialized or
* deserialized by angular's toJson/fromJson.
*
* Requires the {@link ngCookies `ngCookies`} module to be installed.
*
* <div class="alert alert-danger">
* **Note:** The $cookieStore service is **deprecated**.
* Please use the {@link ngCookies.$cookies `$cookies`} service instead.
* </div>
*
* @example
*
* ```js
* angular.module('cookieStoreExample', ['ngCookies'])
* .controller('ExampleController', ['$cookieStore', function($cookieStore) {
* // Put cookie
* $cookieStore.put('myFavorite','oatmeal');
* // Get cookie
* var favoriteCookie = $cookieStore.get('myFavorite');
* // Removing a cookie
* $cookieStore.remove('myFavorite');
* }]);
* ```
*/
factory('$cookieStore', ['$cookies', function($cookies) {
return {
/**
* @ngdoc method
* @name $cookieStore#get
*
* @description
* Returns the value of given cookie key
*
* @param {string} key Id to use for lookup.
* @returns {Object} Deserialized cookie value, undefined if the cookie does not exist.
*/
get: function(key) {
return $cookies.getObject(key);
},
/**
* @ngdoc method
* @name $cookieStore#put
*
* @description
* Sets a value for given cookie key
*
* @param {string} key Id for the `value`.
* @param {Object} value Value to be stored.
*/
put: function(key, value) {
$cookies.putObject(key, value);
},
/**
* @ngdoc method
* @name $cookieStore#remove
*
* @description
* Remove given cookie
*
* @param {string} key Id of the key-value pair to delete.
*/
remove: function(key) {
$cookies.remove(key);
}
};
}]);
/**
* @name $$cookieWriter
* @requires $document
*
* @description
* This is a private service for writing cookies
*
* @param {string} name Cookie name
* @param {string=} value Cookie value (if undefined, cookie will be deleted)
* @param {Object=} options Object with options that need to be stored for the cookie.
*/
function $$CookieWriter($document, $log, $browser) {
var cookiePath = $browser.baseHref();
var rawDocument = $document[0];
function buildCookieString(name, value, options) {
var path, expires;
options = options || {};
expires = options.expires;
path = angular.isDefined(options.path) ? options.path : cookiePath;
if (angular.isUndefined(value)) {
expires = 'Thu, 01 Jan 1970 00:00:00 GMT';
value = '';
}
if (angular.isString(expires)) {
expires = new Date(expires);
}
var str = encodeURIComponent(name) + '=' + encodeURIComponent(value);
str += path ? ';path=' + path : '';
str += options.domain ? ';domain=' + options.domain : '';
str += expires ? ';expires=' + expires.toUTCString() : '';
str += options.secure ? ';secure' : '';
// per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
// - 300 cookies
// - 20 cookies per unique domain
// - 4096 bytes per cookie
var cookieLength = str.length + 1;
if (cookieLength > 4096) {
$log.warn("Cookie '" + name +
"' possibly not set or overflowed because it was too large (" +
cookieLength + " > 4096 bytes)!");
}
return str;
}
return function(name, value, options) {
rawDocument.cookie = buildCookieString(name, value, options);
};
}
$$CookieWriter.$inject = ['$document', '$log', '$browser'];
angular.module('ngCookies').provider('$$cookieWriter', function $$CookieWriterProvider() {
this.$get = $$CookieWriter;
});
})(window, window.angular);

Some files were not shown because too many files have changed in this diff Show More