first import

This commit is contained in:
Michele Artini 2020-07-02 08:55:42 +02:00
parent ba7d32e2cc
commit 0871f01afe
273 changed files with 39094 additions and 0 deletions

View File

@ -0,0 +1 @@
12

View File

@ -0,0 +1 @@
12

View File

@ -0,0 +1,56 @@
package eu.dnetlib.lbs.openaire;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.search.MatchQuery.ZeroTermsQuery;
import eu.dnetlib.lbs.utils.DateParser;
public class ElasticSearchQueryUtils {
public static void addMapConditionForTrust(final BoolQueryBuilder mapQuery, final String field, final Range trust) {
final double min = NumberUtils.toDouble(trust.getMin(), 0);
final double max = NumberUtils.toDouble(trust.getMax(), 1);
mapQuery.must(QueryBuilders.rangeQuery(field).from(min).to(max));
}
public static void addMapCondition(final BoolQueryBuilder mapQuery, final String field, final String value) {
if (StringUtils.isNotBlank(value)) {
mapQuery.must(QueryBuilders.matchQuery(field, value).operator(Operator.AND).zeroTermsQuery(ZeroTermsQuery.ALL));
}
}
public static void addMapCondition(final BoolQueryBuilder mapQuery, final String field, final List<String> list) {
if (list != null && list.size() > 0) {
final BoolQueryBuilder listQuery = QueryBuilders.boolQuery();
for (final String s : list) {
listQuery.should(QueryBuilders.matchQuery(field, s).operator(Operator.AND).zeroTermsQuery(ZeroTermsQuery.ALL));
}
mapQuery.must(listQuery);
}
}
public static void addMapConditionForDates(final BoolQueryBuilder mapQuery, final String field, final List<Range> list) {
if (list != null && list.size() > 0) {
final BoolQueryBuilder listQuery = QueryBuilders.boolQuery();
for (final Range range : list) {
final long min = calculateTime(range.getMin(), 0);
final long max = calculateTime(range.getMax(), Long.MAX_VALUE);
listQuery.should(QueryBuilders.rangeQuery(field).from(min).to(max));
}
mapQuery.must(listQuery);
}
}
public static long calculateTime(final String s, final long defaultValue) {
final Date date = DateParser.parse(s);
return date != null ? date.getTime() : defaultValue;
}
}

View File

@ -0,0 +1,46 @@
package eu.dnetlib.lbs.controllers.objects;
public class BufferStatus implements Comparable<BufferStatus> {
private final String name;
private final long size;
private final long lost;
private final long skipped;
private final long invalid;
public BufferStatus(final String name, final long size, final long lost, final long skipped, final long invalid) {
this.name = name;
this.size = size;
this.lost = lost;
this.skipped = skipped;
this.invalid = invalid;
}
public String getName() {
return this.name;
}
public long getSize() {
return this.size;
}
public long getLost() {
return this.lost;
}
public long getSkipped() {
return this.skipped;
}
public long getInvalid() {
return this.invalid;
}
@Override
public int compareTo(final BufferStatus o) {
if (this.name == null) { return -1; }
if (o.name == null) { return 1; }
return this.name.compareTo(o.name);
}
}

View File

@ -0,0 +1,50 @@
package eu.dnetlib.lbs.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.lbs.LiteratureBrokerServiceConfiguration;
import eu.dnetlib.lbs.elasticsearch.Notification;
import eu.dnetlib.lbs.elasticsearch.NotificationRepository;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@RestController
@RequestMapping("/api/notifications")
@Api(tags = LiteratureBrokerServiceConfiguration.TAG_NOTIFICATIONS)
public class NotificationsController extends AbstractLbsController {
@Autowired
private NotificationRepository notificationRepository;
@ApiOperation("Return a notification by ID")
@GetMapping("/{id}")
public Notification getNotification(@PathVariable final String id) {
return notificationRepository.findById(id).get();
}
@ApiOperation("Delete a notification by ID")
@DeleteMapping("/{id}")
public void deleteNotification(@PathVariable final String id) {
notificationRepository.deleteById(id);
}
@ApiOperation("Save a notification by ID")
@PostMapping("/{id}")
public Notification saveNotification(@RequestBody final Notification notification) {
return notificationRepository.save(notification);
}
@ApiOperation("Delete all notifications")
@DeleteMapping("")
public void deleteAllNotifications() {
notificationRepository.deleteAll();
}
}

View File

@ -0,0 +1,24 @@
package eu.dnetlib.lbs.elasticsearch;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface NotificationRepository extends ElasticsearchRepository<Notification, String> {
// TODO: use the @Query annotation if necessary
// See: http://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/
Iterable<Notification> findBySubscriptionId(String subscriptionId);
@Override
Page<Notification> findAll(Pageable pageable);
Page<Notification> findByEventId(String topic, Pageable pageable);
long deleteByDateBefore(long date);
long countBySubscriptionId(String subscriptionId);
void deleteBySubscriptionId(String subscriptionId);
}

View File

@ -0,0 +1,108 @@
<h3>Topic typologies</h3>
<form class="form-inline text-right" style="margin-top:40px; margin-bottom:20px">
<button class="btn btn-primary" data-target="#addTopicTypeModal" data-toggle="modal" ng-click="resetFormFields()">
<span class="glyphicon glyphicon-plus"></span>
add
</button>
<button class="btn btn-default" ng-click="refresh()">
<span class="glyphicon glyphicon-refresh"></span>
refresh
</button>
</form>
<table class="table table-striped" style="margin-top:20px; font-size: 12px">
<thead>
<tr>
<th class="col-xs-2">ID</th>
<th class="col-xs-2">Name</th>
<th class="col-xs-1">Producer ID</th>
<th class="col-xs-2">Topic expression</th>
<th class="col-xs-3">Topic regex</th>
<th class="col-xs-2">Required map fields</th>
</tr>
</thead>
<tbody>
<tr ng-if="list.length == 0">
<td colspan="6" class="text-center text-muted">No topic types</td>
</tr>
<tr ng-repeat="t in list">
<th style="font-family: monospace;">{{t.id}}</th>
<td>{{t.name}}</td>
<td>{{t.producerId}}</td>
<td style="font-family: monospace;">{{t.expression}}</td>
<td style="font-family: monospace;">{{t.regex}}</td>
<td>{{t.mapKeys}}</td>
</tr>
</tbody>
</table>
<div id="addTopicTypeModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg"">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">Add topic typology</h4>
</div>
<div class="modal-body">
<form name="newTopicTypeForm">
<div class="form-group" ng-class="{'has-error' : !topicName, 'has-success' : topicName}">
<label class="control-label">Name</label>
<input type="text" class="form-control" placeholder="name" required="required" ng-model="topicName" />
</div>
<div class="form-group" ng-class="{'has-warning' : !topicProducerId, 'has-success' : topicProducerId}">
<label class="control-label">Producer ID</label>
<input type="text" class="form-control" placeholder="producer" ng-model="topicProducerId" />
</div>
<div class="form-group">
<label class="control-label" ng-class="{'text-success' : topicExpression, 'text-danger' : !topicExpression}">Topic expression</label>
<table class="table table-condensed table-bordered">
<tr>
<td ng-repeat="s in segments" valign="middle">
<select class="form-control" required="required" ng-model="s.type" ng-change="updateTopicExpression()">
<option>Fixed</option>
<option>Variable</option>
</select>
</td>
<td rowspan="2" class="text-center" style="width: 80px;">
<div class="btn-group">
<button class="btn btn-sm btn-danger" ng-click="segments.splice(-1,1); updateTopicExpression()" ng-disabled="segments.length < 3">
<span class="glyphicon glyphicon-minus"></span>
</button>
<button class="btn btn-sm btn-success" ng-click="segments.push({}); updateTopicExpression()" ng-disabled="segments.length > 5">
<span class="glyphicon glyphicon-plus"></span>
</button>
</div>
</td>
</tr>
<tr>
<td ng-repeat="s in segments" valign="middle">
<input type="text" class="form-control" required="required" disabled="disabled" ng-if="!s.type" placeholder="..." />
<input type="text" class="form-control" required="required" ng-model="s.value" ng-if="s.type == 'Fixed'" placeholder="segment" ng-pattern="/^[a-zA-Z0-9._-]+$/" ng-change="updateTopicExpression()" />
<input type="text" class="form-control" required="required" ng-model="s.value" ng-if="s.type == 'Variable'" placeholder="variable name" ng-pattern="/^[a-zA-Z0-9._-]+$/" ng-change="updateTopicExpression()" />
</td>
</tr>
<tr>
<td colspan="{{segments.length + 1}}">
<b>Expression:</b> <span style="font-family: monospace;" ng-class="{'text-success' : topicExpression, 'text-danger' : !topicExpression}">{{topicExpression ? topicExpression : 'invalid'}}</span>
</td>
</tr>
</table>
</div>
<div class="form-group" ng-class="{'has-warning' : topicMapKeys.length == 0, 'has-success' : topicMapKeys.length > 0}">
<label class="control-label">Required map fields (comma separated)</label>
<input type="text" class="form-control" ng-model="topicMapKeys" ng-list="," ng-trim="true"></input>
<span class="label label-default" ng-repeat="k in topicMapKeys track by $index" style="margin-left: 4px;">{{k}}</span>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary" data-dismiss="modal" ng-disabled="newTopicTypeForm.$invalid" ng-click="add(topicName, topicProducerId, topicExpression, topicMapKeys)">Add</button>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,45 @@
package eu.dnetlib.lbs.integration;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.web.client.RestTemplate;
import eu.dnetlib.broker.objects.OpenAireEventPayload;
import eu.dnetlib.lbs.openaire.ScrollPage;
public class ScrollTest {
private static final String baseUrl = "http://...";
// private static final String baseUrl = "http://broker1-dev-dnet.d4science.org:8080"; // DEV
// private static final String baseUrl = "http://lbs.openaire.eu:8080"; // PRODUCTION
private static final String subscriptionId = "sub-c9767c84-3597-462b-803b-2d3e09de44c4";
@Test
@Ignore
public void testScroll() {
int total = 0;
ScrollPage page = getPage(baseUrl + "/api/openaireBroker/scroll/notifications/start/ " + subscriptionId);
total += page.getValues().size();
while (!page.isCompleted()) {
page = getPage(baseUrl + "/api/openaireBroker/scroll/notifications/ " + page.getId());
total += page.getValues().size();
for (final OpenAireEventPayload p : page.getValues()) {
// DO SOMETHING
}
}
System.out.println("\nTOTAL: " + total);
}
private ScrollPage getPage(final String url) {
System.out.println(url);
final ScrollPage p = new RestTemplate().getForObject(url, ScrollPage.class);
System.out.println("Page size: " + p.getValues().size());
return p;
}
}

View File

@ -0,0 +1,126 @@
package eu.dnetlib.lbs.topics;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Sets;
import eu.dnetlib.lbs.elasticsearch.Event;
@Entity(name = "topic_types")
@Table(name = "topic_types")
public class TopicType {
@Id
@Column(name = "id")
private String id;
@Column(name = "name", nullable = false, unique = true)
private String name;
@Column(name = "expression", length = 4096, unique = true, nullable = false)
private String expression;
@Column(name = "regex", length = 4096, unique = true, nullable = false)
private String regex;
@Column(name = "producerId", length = 4096)
private String producerId;
@Column(name = "mapKeys", length = 4096)
private String mapKeys;
public TopicType() {}
public TopicType(final String id, final String name, final String expression, final String producerId, final String mapKeys) {
this.id = id;
this.name = name;
this.expression = expression;
this.producerId = producerId;
this.mapKeys = mapKeys;
updateRegex();
}
public TopicType(final String id, final String name, final String expression, final String producerId, final Set<String> mapKeys) {
this(id, name, expression, producerId, Joiner.on(",").join(mapKeys));
}
public String getId() {
return this.id;
}
public void setId(final String id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(final String name) {
this.name = name;
}
public String getProducerId() {
return this.producerId;
}
public void setProducerId(final String producerId) {
this.producerId = producerId;
}
public String getMapKeys() {
return this.mapKeys;
}
public void setMapKeys(final Set<String> mapKeys) {
this.mapKeys = Joiner.on(",").join(mapKeys);
}
public Set<String> getMapKeysAsSet() {
return Sets.newHashSet(Splitter.on(",").trimResults().omitEmptyStrings().split(this.mapKeys));
}
public void setMapKeys(final String mapKeys) {
this.mapKeys = mapKeys;
}
public String getExpression() {
return this.expression;
}
public void setExpression(final String expression) {
this.expression = expression;
updateRegex();
}
private void updateRegex() {
this.regex = "^" + this.expression.replaceAll("<[a-zA-Z0-9._-]+>", "[a-zA-Z0-9._-]+").replaceAll("\\/", "\\\\/").trim() + "$";
}
public String getRegex() {
return this.regex;
}
public Predicate<Event> asValidator() {
final Predicate<String> p = Pattern.compile(getRegex()).asPredicate();
final Set<String> keys = getMapKeysAsSet();
return e -> e != null
&& StringUtils.isNotBlank(e.getTopic())
&& p.test(e.getTopic())
&& (StringUtils.isBlank(TopicType.this.producerId) ||
TopicType.this.producerId.equals(e.getProducerId()))
&& e.getMap().keySet().containsAll(keys);
}
}

View File

@ -0,0 +1,14 @@
<tr>
<td style="width: 300px">
<button class="btn"
ng-class="{'btn-warning' : status == 0, 'btn-success' : status != 0 }"
style="width: 100%"
ng-click="doAction()">
<span ng-show="status == 0">{{label}}</span>
<span ng-show="status == 1">confirm ?</span>
</button>
</td>
<td style="vertical-align: middle;">
{{description}} <b class="pull-right" ng-if="alert">{{alert}}</b>
</td>
</tr>

View File

@ -0,0 +1,104 @@
package eu.dnetlib.lbs.events.input;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.utils.LbsQueue;
public class RabbitMQConsumer implements Runnable {
private final String queue;
private final String host;
private final int port;
private final String username;
private final String password;
private final LbsQueue<String, Event> localQueue;
private static final Log log = LogFactory.getLog(RabbitMQConsumer.class);
public RabbitMQConsumer(final String queue, final String host, final int port, final String username, final String password,
final LbsQueue<String, Event> localQueue) {
super();
this.queue = queue;
this.host = host;
this.port = port;
this.username = username;
this.password = password;
this.localQueue = localQueue;
}
@Override
public void run() {
final ConnectionFactory factory = new ConnectionFactory();
factory.setHost(this.host);
factory.setPort(this.port);
factory.setUsername(this.username);
factory.setPassword(this.password);
factory.setAutomaticRecoveryEnabled(true);
log.info("Starting rabbitMQ consumer: " + Thread.currentThread().getName());
try {
final Connection connection = factory.newConnection();
final Channel channel = connection.createChannel();
final DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(final String consumerTag,
final Envelope envelope,
final AMQP.BasicProperties properties,
final byte[] body)
throws IOException {
try {
getLocalQueue().offer(new String(body, "UTF-8"));
} catch (final Throwable e) {
log.error("Error processing event", e);
} finally {
getChannel().basicAck(envelope.getDeliveryTag(), false);
}
}
};
channel.queueDeclare(this.queue, true, false, false, null);
channel.basicConsume(this.queue, false, consumer);
} catch (final Exception e) {
log.error("Error creating consumer", e);
}
}
public String getQueue() {
return this.queue;
}
public String getHost() {
return this.host;
}
public int getPort() {
return this.port;
}
public String getUsername() {
return this.username;
}
public String getPassword() {
return this.password;
}
public LbsQueue<String, Event> getLocalQueue() {
return this.localQueue;
}
}

View File

@ -0,0 +1,84 @@
package eu.dnetlib.lbs.controllers;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.lbs.LiteratureBrokerServiceConfiguration;
import eu.dnetlib.lbs.topics.TopicType;
import eu.dnetlib.lbs.topics.TopicTypeRepository;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@RestController
@RequestMapping("/api/topic-types")
@Api(tags = LiteratureBrokerServiceConfiguration.TAG_TOPIC_TYPES)
public class TopicsController extends AbstractLbsController {
@Autowired
private TopicTypeRepository topicTypeRepo;
private final Predicate<String> verifyExpression =
Pattern.compile("^([a-zA-Z0-9._-]+|<[a-zA-Z0-9._-]+>)(\\/([a-zA-Z0-9._-]+|<[a-zA-Z0-9._-]+>))+$").asPredicate();
@ApiOperation("Return the list of topic types")
@GetMapping("")
public Iterable<TopicType> listTopicTypes() {
return topicTypeRepo.findAll();
}
@ApiOperation("Register a new topic type")
@PostMapping("/add")
public TopicType registerTopicType(@RequestParam final String name,
@RequestParam final String expression,
@RequestParam final String producerId,
@RequestParam final String mapKeys) {
if (StringUtils.isBlank(name)) { throw new IllegalArgumentException("name is empty"); }
if (StringUtils.isBlank(expression)) { throw new IllegalArgumentException("expression is empty"); }
if (!verifyExpression.test(expression)) { throw new IllegalArgumentException("Invalid expression: " + expression); }
final String id = "tt-" + UUID.randomUUID();
final TopicType type = new TopicType(id, name, expression, producerId, mapKeys);
topicTypeRepo.save(type);
return type;
}
@ApiOperation("Return a topic type by ID")
@GetMapping("/{id}")
public TopicType getTopicType(@PathVariable final String id) {
return topicTypeRepo.findById(id).get();
}
@ApiOperation("Delete a topic type by ID")
@DeleteMapping("/{id}")
public List<String> deleteTopicType(@PathVariable final String id) {
topicTypeRepo.deleteById(id);
return Arrays.asList("Done.");
}
@ApiOperation("Delete all topic types")
@DeleteMapping("")
public Map<String, Object> clearTopicTypes() {
final Map<String, Object> res = new HashMap<>();
topicTypeRepo.deleteAll();
res.put("deleted", "all");
return res;
}
}

View File

@ -0,0 +1,116 @@
package eu.dnetlib.lbs.elasticsearch;
import java.util.Map;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Document(indexName = "#{elasticSearchProperties.eventsIndexName}", type = "#{elasticSearchProperties.eventsIndexType}")
public class Event {
@Id
private String eventId;
@Field(type = FieldType.Keyword)
private String producerId;
@Field(type = FieldType.Keyword)
private String topic;
@Field(type = FieldType.Text)
private String payload;
@Field(type = FieldType.Long)
private Long creationDate;
@Field(type = FieldType.Long)
private Long expiryDate;
@Field(type = FieldType.Boolean)
private boolean instantMessage;
@Field(type = FieldType.Nested)
private Map<String, Object> map;
public Event() {}
public Event(final String producerId, final String eventId, final String topic, final String payload, final Long creationDate, final Long expiryDate,
final boolean instantMessage,
final Map<String, Object> map) {
this.producerId = producerId;
this.eventId = eventId;
this.topic = topic;
this.payload = payload;
this.creationDate = creationDate;
this.expiryDate = expiryDate;
this.instantMessage = instantMessage;
this.map = map;
}
public String getProducerId() {
return producerId;
}
public void setProducerId(final String producerId) {
this.producerId = producerId;
}
public String getEventId() {
return eventId;
}
public void setEventId(final String eventId) {
this.eventId = eventId;
}
public String getTopic() {
return topic;
}
public void setTopic(final String topic) {
this.topic = topic;
}
public String getPayload() {
return payload;
}
public void setPayload(final String payload) {
this.payload = payload;
}
public Long getCreationDate() {
return creationDate;
}
public void setCreationDate(final Long creationDate) {
this.creationDate = creationDate;
}
public Long getExpiryDate() {
return expiryDate;
}
public void setExpiryDate(final Long expiryDate) {
this.expiryDate = expiryDate;
}
public boolean isInstantMessage() {
return instantMessage;
}
public void setInstantMessage(final boolean instantMessage) {
this.instantMessage = instantMessage;
}
public Map<String, Object> getMap() {
return map;
}
public void setMap(final Map<String, Object> map) {
this.map = map;
}
}

View File

@ -0,0 +1,105 @@
package eu.dnetlib.lbs.clients;
import java.util.Date;
import java.util.Objects;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.util.CloseableIterator;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.subscriptions.MapCondition;
import eu.dnetlib.lbs.subscriptions.NotificationFrequency;
import eu.dnetlib.lbs.subscriptions.NotificationMode;
import eu.dnetlib.lbs.subscriptions.Subscription;
import eu.dnetlib.lbs.utils.DateParser;
@Ignore
// @RunWith(SpringJUnit4ClassRunner.class)
// @ContextConfiguration(locations = { "classpath:/applicationContext-test-queries.xml" })
public class IndexClientTest {
private static final String topic = "ENRICH/MORE/PID";
private static final Date fromDate = DateParser.parse("2016-01-31");
private static final String indexName = Event.class.getAnnotation(Document.class).indexName();
@Autowired
private ElasticsearchOperations esOperations;
@Test
public void testSearchTopic() {
System.out.println("Start searching");
final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("topic", topic))
.must(QueryBuilders.rangeQuery("creationDate").from(fromDate)))
.withSearchType(SearchType.DEFAULT)
.withPageable(PageRequest.of(0, 10))
.build();
int count = 0;
final CloseableIterator<SearchHit<Event>> it = esOperations.searchForStream(searchQuery, Event.class, IndexCoordinates.of(indexName));
while (it.hasNext()) {
System.out.println(" > " + it.next());
count++;
}
System.out.println("SIZE: " + count);
}
@Test
@Ignore
public void testSearchSubscription() {
System.out.println("Start searching");
final Subscription s = new Subscription();
s.setSubscriptionId("sub-db0b35d4-1b0f-4660-a849-34fbec8fb6f7");
s.setSubscriber("artini@isti.cnr.it");
s.setTopic("ENRICH/MORE/OPENACCESS_VERSION");
s.setFrequency(NotificationFrequency.daily);
s.setMode(NotificationMode.MOCK);
s.setConditions(
"[{\"field\":\"target_datasource_name\",\"fieldType\":\"STRING\",\"operator\":\"EXACT\",\"listParams\":[{\"value\":\"Research Papers in Economics\"}]},{\"field\":\"trust\",\"fieldType\":\"FLOAT\",\"operator\":\"RANGE\",\"listParams\":[{\"value\":\"0\",\"otherValue\":\"1\"}]}]");
final BoolQueryBuilder mapQuery = QueryBuilders.boolQuery();
s.getConditionsAsList().stream()
.map(MapCondition::asQueryBuilder)
.filter(Objects::nonNull)
.forEach(mapQuery::must);
final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("topic", s.getTopic()))
.must(QueryBuilders.rangeQuery("creationDate").from(s.getLastNotificationDate()))
.must(QueryBuilders.nestedQuery("map", mapQuery, ScoreMode.None)))
.withSearchType(SearchType.DEFAULT)
.withPageable(PageRequest.of(0, 10))
.build();
int count = 0;
final CloseableIterator<SearchHit<Event>> it = esOperations.searchForStream(searchQuery, Event.class, IndexCoordinates.of(indexName));
while (it.hasNext()) {
System.out.println(" > " + it.next());
count++;
}
System.out.println("SIZE: " + count);
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,28 @@
<div class="well col-xs-12 col-md-offset-3 col-md-6 col-lg-offset-4 col-lg-4" ng-if="isEmpty()">
No subscriptions
</div>
<div class="row" ng-repeat="(ds, list) in subscriptions">
<div class="well col-xs-12 col-md-8 col-lg-6">
<table class="table table-condensed table-striped">
<thead>
<tr>
<th colspan="2" class="text-center">
Datasource: <i>{{ds}}</i>
</th>
</tr>
<tr>
<th>Topic</th>
<th class="text-right" style="white-space: nowrap;"># notifications</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="s in list">
<td><a href="#/ntf/0/{{s.id}}" title="{{s.id}}">{{s.topic}}</a></td>
<td class="text-right">{{s.count}}</td>
</tr>
</tbody>
</table>
</div>
</div>

View File

@ -0,0 +1,5 @@
package eu.dnetlib.lbs.subscriptions;
public enum NotificationFrequency {
never, realtime, daily, weekly, monthly
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html>
<head>
<title>OpenAIRE - Literature Broker Service</title>
<link rel="stylesheet" href="../css/bootstrap.min.css" />
<link rel="stylesheet" href="../css/bootstrap-theme.min.css" />
<link rel="stylesheet" href="../css/angular-backtop.css" />
<link rel="stylesheet" href="../css/rzslider.min.css" />
<script src="../js/jquery-1.12.3.min.js"></script>
<script src="../js/bootstrap.min.js"></script>
<script src="../js/angular.min.js"></script>
<script src="../js/angular-route.min.js"></script>
<script src="../js/angular-backtop.min.js"></script>
<script src="../js/angular-local-storage.min.js"></script>
<script src="../js/angular-base64.min.js"></script>
<script src="../js/rzslider.min.js"></script>
<script src="openaire-lbs-ui.js"></script>
</head>
<body ng-app="openaireBroker">
<nav class="navbar navbar-default" ng-controller="moduleMenuCtrl">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">OpenAIRE Literature Broker Service</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li ng-class="{active : isActive('^\/datasources')}"><a href="#/datasources">Potential events</a></li>
<li ng-class="{active : isActive('^\/subscriptions')}"><a href="#/subscriptions">My subscriptions</a></li>
</ul>
<ul class="nav navbar-nav navbar-right" ng-if="isLogged()">
<li class="dropdown">
<a href="javascript:void(0)" class="dropdown-toggle" data-toggle="dropdown"><b>User: </b>{{getUser()}}</a>
<ul class="dropdown-menu">
<li><a href="javascript:void(0)" ng-click="logout()">Logout</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<div ng-view style="margin: 20px;"></div>
</body>
</html>

View File

@ -0,0 +1,21 @@
package eu.dnetlib.lbs.controllers.objects;
public class Tool {
private final String name;
private final String url;
public Tool(final String name, final String url) {
this.name = name;
this.url = url;
}
public String getName() {
return this.name;
}
public String getUrl() {
return this.url;
}
}

View File

@ -0,0 +1,314 @@
/*
AngularJS v1.5.5
(c) 2010-2016 Google, Inc. http://angularjs.org
License: MIT
*/
(function(v){'use strict';function O(a){return function(){var b=arguments[0],d;d="["+(a?a+":":"")+b+"] http://errors.angularjs.org/1.5.5/"+(a?a+"/":"")+b;for(b=1;b<arguments.length;b++){d=d+(1==b?"?":"&")+"p"+(b-1)+"=";var c=encodeURIComponent,e;e=arguments[b];e="function"==typeof e?e.toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof e?"undefined":"string"!=typeof e?JSON.stringify(e):e;d+=c(e)}return Error(d)}}function ya(a){if(null==a||Va(a))return!1;if(K(a)||F(a)||B&&a instanceof B)return!0;
var b="length"in Object(a)&&a.length;return Q(b)&&(0<=b&&(b-1 in a||a instanceof Array)||"function"==typeof a.item)}function q(a,b,d){var c,e;if(a)if(E(a))for(c in a)"prototype"==c||"length"==c||"name"==c||a.hasOwnProperty&&!a.hasOwnProperty(c)||b.call(d,a[c],c,a);else if(K(a)||ya(a)){var f="object"!==typeof a;c=0;for(e=a.length;c<e;c++)(f||c in a)&&b.call(d,a[c],c,a)}else if(a.forEach&&a.forEach!==q)a.forEach(b,d,a);else if(oc(a))for(c in a)b.call(d,a[c],c,a);else if("function"===typeof a.hasOwnProperty)for(c in a)a.hasOwnProperty(c)&&
b.call(d,a[c],c,a);else for(c in a)ua.call(a,c)&&b.call(d,a[c],c,a);return a}function pc(a,b,d){for(var c=Object.keys(a).sort(),e=0;e<c.length;e++)b.call(d,a[c[e]],c[e]);return c}function qc(a){return function(b,d){a(d,b)}}function Xd(){return++nb}function Nb(a,b,d){for(var c=a.$$hashKey,e=0,f=b.length;e<f;++e){var g=b[e];if(G(g)||E(g))for(var h=Object.keys(g),k=0,l=h.length;k<l;k++){var n=h[k],m=g[n];d&&G(m)?fa(m)?a[n]=new Date(m.valueOf()):Wa(m)?a[n]=new RegExp(m):m.nodeName?a[n]=m.cloneNode(!0):
Ob(m)?a[n]=m.clone():(G(a[n])||(a[n]=K(m)?[]:{}),Nb(a[n],[m],!0)):a[n]=m}}c?a.$$hashKey=c:delete a.$$hashKey;return a}function R(a){return Nb(a,za.call(arguments,1),!1)}function Yd(a){return Nb(a,za.call(arguments,1),!0)}function X(a){return parseInt(a,10)}function Pb(a,b){return R(Object.create(a),b)}function C(){}function Xa(a){return a}function da(a){return function(){return a}}function rc(a){return E(a.toString)&&a.toString!==ma}function y(a){return"undefined"===typeof a}function x(a){return"undefined"!==
typeof a}function G(a){return null!==a&&"object"===typeof a}function oc(a){return null!==a&&"object"===typeof a&&!sc(a)}function F(a){return"string"===typeof a}function Q(a){return"number"===typeof a}function fa(a){return"[object Date]"===ma.call(a)}function E(a){return"function"===typeof a}function Wa(a){return"[object RegExp]"===ma.call(a)}function Va(a){return a&&a.window===a}function Ya(a){return a&&a.$evalAsync&&a.$watch}function Da(a){return"boolean"===typeof a}function Zd(a){return a&&Q(a.length)&&
$d.test(ma.call(a))}function Ob(a){return!(!a||!(a.nodeName||a.prop&&a.attr&&a.find))}function ae(a){var b={};a=a.split(",");var d;for(d=0;d<a.length;d++)b[a[d]]=!0;return b}function va(a){return P(a.nodeName||a[0]&&a[0].nodeName)}function Za(a,b){var d=a.indexOf(b);0<=d&&a.splice(d,1);return d}function qa(a,b){function d(a,b){var d=b.$$hashKey,e;if(K(a)){e=0;for(var f=a.length;e<f;e++)b.push(c(a[e]))}else if(oc(a))for(e in a)b[e]=c(a[e]);else if(a&&"function"===typeof a.hasOwnProperty)for(e in a)a.hasOwnProperty(e)&&
(b[e]=c(a[e]));else for(e in a)ua.call(a,e)&&(b[e]=c(a[e]));d?b.$$hashKey=d:delete b.$$hashKey;return b}function c(a){if(!G(a))return a;var b=f.indexOf(a);if(-1!==b)return g[b];if(Va(a)||Ya(a))throw Aa("cpws");var b=!1,c=e(a);void 0===c&&(c=K(a)?[]:Object.create(sc(a)),b=!0);f.push(a);g.push(c);return b?d(a,c):c}function e(a){switch(ma.call(a)){case "[object Int8Array]":case "[object Int16Array]":case "[object Int32Array]":case "[object Float32Array]":case "[object Float64Array]":case "[object Uint8Array]":case "[object Uint8ClampedArray]":case "[object Uint16Array]":case "[object Uint32Array]":return new a.constructor(c(a.buffer));
case "[object ArrayBuffer]":if(!a.slice){var b=new ArrayBuffer(a.byteLength);(new Uint8Array(b)).set(new Uint8Array(a));return b}return a.slice(0);case "[object Boolean]":case "[object Number]":case "[object String]":case "[object Date]":return new a.constructor(a.valueOf());case "[object RegExp]":return b=new RegExp(a.source,a.toString().match(/[^\/]*$/)[0]),b.lastIndex=a.lastIndex,b;case "[object Blob]":return new a.constructor([a],{type:a.type})}if(E(a.cloneNode))return a.cloneNode(!0)}var f=[],
g=[];if(b){if(Zd(b)||"[object ArrayBuffer]"===ma.call(b))throw Aa("cpta");if(a===b)throw Aa("cpi");K(b)?b.length=0:q(b,function(a,d){"$$hashKey"!==d&&delete b[d]});f.push(a);g.push(b);return d(a,b)}return c(a)}function ha(a,b){if(K(a)){b=b||[];for(var d=0,c=a.length;d<c;d++)b[d]=a[d]}else if(G(a))for(d in b=b||{},a)if("$"!==d.charAt(0)||"$"!==d.charAt(1))b[d]=a[d];return b||a}function pa(a,b){if(a===b)return!0;if(null===a||null===b)return!1;if(a!==a&&b!==b)return!0;var d=typeof a,c;if(d==typeof b&&
"object"==d)if(K(a)){if(!K(b))return!1;if((d=a.length)==b.length){for(c=0;c<d;c++)if(!pa(a[c],b[c]))return!1;return!0}}else{if(fa(a))return fa(b)?pa(a.getTime(),b.getTime()):!1;if(Wa(a))return Wa(b)?a.toString()==b.toString():!1;if(Ya(a)||Ya(b)||Va(a)||Va(b)||K(b)||fa(b)||Wa(b))return!1;d=T();for(c in a)if("$"!==c.charAt(0)&&!E(a[c])){if(!pa(a[c],b[c]))return!1;d[c]=!0}for(c in b)if(!(c in d)&&"$"!==c.charAt(0)&&x(b[c])&&!E(b[c]))return!1;return!0}return!1}function $a(a,b,d){return a.concat(za.call(b,
d))}function tc(a,b){var d=2<arguments.length?za.call(arguments,2):[];return!E(b)||b instanceof RegExp?b:d.length?function(){return arguments.length?b.apply(a,$a(d,arguments,0)):b.apply(a,d)}:function(){return arguments.length?b.apply(a,arguments):b.call(a)}}function be(a,b){var d=b;"string"===typeof a&&"$"===a.charAt(0)&&"$"===a.charAt(1)?d=void 0:Va(b)?d="$WINDOW":b&&v.document===b?d="$DOCUMENT":Ya(b)&&(d="$SCOPE");return d}function ab(a,b){if(!y(a))return Q(b)||(b=b?2:null),JSON.stringify(a,be,
b)}function uc(a){return F(a)?JSON.parse(a):a}function vc(a,b){a=a.replace(ce,"");var d=Date.parse("Jan 01, 1970 00:00:00 "+a)/6E4;return isNaN(d)?b:d}function Qb(a,b,d){d=d?-1:1;var c=a.getTimezoneOffset();b=vc(b,c);d*=b-c;a=new Date(a.getTime());a.setMinutes(a.getMinutes()+d);return a}function wa(a){a=B(a).clone();try{a.empty()}catch(b){}var d=B("<div>").append(a).html();try{return a[0].nodeType===Ma?P(d):d.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+P(b)})}catch(c){return P(d)}}
function wc(a){try{return decodeURIComponent(a)}catch(b){}}function xc(a){var b={};q((a||"").split("&"),function(a){var c,e,f;a&&(e=a=a.replace(/\+/g,"%20"),c=a.indexOf("="),-1!==c&&(e=a.substring(0,c),f=a.substring(c+1)),e=wc(e),x(e)&&(f=x(f)?wc(f):!0,ua.call(b,e)?K(b[e])?b[e].push(f):b[e]=[b[e],f]:b[e]=f))});return b}function Rb(a){var b=[];q(a,function(a,c){K(a)?q(a,function(a){b.push(ja(c,!0)+(!0===a?"":"="+ja(a,!0)))}):b.push(ja(c,!0)+(!0===a?"":"="+ja(a,!0)))});return b.length?b.join("&"):""}
function ob(a){return ja(a,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function ja(a,b){return encodeURIComponent(a).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,b?"%20":"+")}function de(a,b){var d,c,e=Na.length;for(c=0;c<e;++c)if(d=Na[c]+b,F(d=a.getAttribute(d)))return d;return null}function ee(a,b){var d,c,e={};q(Na,function(b){b+="app";!d&&a.hasAttribute&&a.hasAttribute(b)&&(d=a,c=a.getAttribute(b))});
q(Na,function(b){b+="app";var e;!d&&(e=a.querySelector("["+b.replace(":","\\:")+"]"))&&(d=e,c=e.getAttribute(b))});d&&(e.strictDi=null!==de(d,"strict-di"),b(d,c?[c]:[],e))}function yc(a,b,d){G(d)||(d={});d=R({strictDi:!1},d);var c=function(){a=B(a);if(a.injector()){var c=a[0]===v.document?"document":wa(a);throw Aa("btstrpd",c.replace(/</,"&lt;").replace(/>/,"&gt;"));}b=b||[];b.unshift(["$provide",function(b){b.value("$rootElement",a)}]);d.debugInfoEnabled&&b.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);
b.unshift("ng");c=bb(b,d.strictDi);c.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;v&&e.test(v.name)&&(d.debugInfoEnabled=!0,v.name=v.name.replace(e,""));if(v&&!f.test(v.name))return c();v.name=v.name.replace(f,"");ea.resumeBootstrap=function(a){q(a,function(a){b.push(a)});return c()};E(ea.resumeDeferredBootstrap)&&ea.resumeDeferredBootstrap()}function fe(){v.name=
"NG_ENABLE_DEBUG_INFO!"+v.name;v.location.reload()}function ge(a){a=ea.element(a).injector();if(!a)throw Aa("test");return a.get("$$testability")}function zc(a,b){b=b||"_";return a.replace(he,function(a,c){return(c?b:"")+a.toLowerCase()})}function ie(){var a;if(!Ac){var b=pb();(Z=y(b)?v.jQuery:b?v[b]:void 0)&&Z.fn.on?(B=Z,R(Z.fn,{scope:Oa.scope,isolateScope:Oa.isolateScope,controller:Oa.controller,injector:Oa.injector,inheritedData:Oa.inheritedData}),a=Z.cleanData,Z.cleanData=function(b){for(var c,
e=0,f;null!=(f=b[e]);e++)(c=Z._data(f,"events"))&&c.$destroy&&Z(f).triggerHandler("$destroy");a(b)}):B=U;ea.element=B;Ac=!0}}function qb(a,b,d){if(!a)throw Aa("areq",b||"?",d||"required");return a}function Pa(a,b,d){d&&K(a)&&(a=a[a.length-1]);qb(E(a),b,"not a function, got "+(a&&"object"===typeof a?a.constructor.name||"Object":typeof a));return a}function Qa(a,b){if("hasOwnProperty"===a)throw Aa("badname",b);}function Bc(a,b,d){if(!b)return a;b=b.split(".");for(var c,e=a,f=b.length,g=0;g<f;g++)c=
b[g],a&&(a=(e=a)[c]);return!d&&E(a)?tc(e,a):a}function rb(a){for(var b=a[0],d=a[a.length-1],c,e=1;b!==d&&(b=b.nextSibling);e++)if(c||a[e]!==b)c||(c=B(za.call(a,0,e))),c.push(b);return c||a}function T(){return Object.create(null)}function je(a){function b(a,b,c){return a[b]||(a[b]=c())}var d=O("$injector"),c=O("ng");a=b(a,"angular",Object);a.$$minErr=a.$$minErr||O;return b(a,"module",function(){var a={};return function(f,g,h){if("hasOwnProperty"===f)throw c("badname","module");g&&a.hasOwnProperty(f)&&
(a[f]=null);return b(a,f,function(){function a(b,d,e,f){f||(f=c);return function(){f[e||"push"]([b,d,arguments]);return M}}function b(a,d){return function(b,e){e&&E(e)&&(e.$$moduleName=f);c.push([a,d,arguments]);return M}}if(!g)throw d("nomod",f);var c=[],e=[],r=[],N=a("$injector","invoke","push",e),M={_invokeQueue:c,_configBlocks:e,_runBlocks:r,requires:g,name:f,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:a("$provide","value"),constant:a("$provide",
"constant","unshift"),decorator:b("$provide","decorator"),animation:b("$animateProvider","register"),filter:b("$filterProvider","register"),controller:b("$controllerProvider","register"),directive:b("$compileProvider","directive"),component:b("$compileProvider","component"),config:N,run:function(a){r.push(a);return this}};h&&N(h);return M})}})}function ke(a){R(a,{bootstrap:yc,copy:qa,extend:R,merge:Yd,equals:pa,element:B,forEach:q,injector:bb,noop:C,bind:tc,toJson:ab,fromJson:uc,identity:Xa,isUndefined:y,
isDefined:x,isString:F,isFunction:E,isObject:G,isNumber:Q,isElement:Ob,isArray:K,version:le,isDate:fa,lowercase:P,uppercase:sb,callbacks:{counter:0},getTestability:ge,$$minErr:O,$$csp:Ea,reloadWithDebugInfo:fe});Sb=je(v);Sb("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:me});a.provider("$compile",Cc).directive({a:ne,input:Dc,textarea:Dc,form:oe,script:pe,select:qe,style:re,option:se,ngBind:te,ngBindHtml:ue,ngBindTemplate:ve,ngClass:we,ngClassEven:xe,ngClassOdd:ye,ngCloak:ze,ngController:Ae,
ngForm:Be,ngHide:Ce,ngIf:De,ngInclude:Ee,ngInit:Fe,ngNonBindable:Ge,ngPluralize:He,ngRepeat:Ie,ngShow:Je,ngStyle:Ke,ngSwitch:Le,ngSwitchWhen:Me,ngSwitchDefault:Ne,ngOptions:Oe,ngTransclude:Pe,ngModel:Qe,ngList:Re,ngChange:Se,pattern:Ec,ngPattern:Ec,required:Fc,ngRequired:Fc,minlength:Gc,ngMinlength:Gc,maxlength:Hc,ngMaxlength:Hc,ngValue:Te,ngModelOptions:Ue}).directive({ngInclude:Ve}).directive(tb).directive(Ic);a.provider({$anchorScroll:We,$animate:Xe,$animateCss:Ye,$$animateJs:Ze,$$animateQueue:$e,
$$AnimateRunner:af,$$animateAsyncRun:bf,$browser:cf,$cacheFactory:df,$controller:ef,$document:ff,$exceptionHandler:gf,$filter:Jc,$$forceReflow:hf,$interpolate:jf,$interval:kf,$http:lf,$httpParamSerializer:mf,$httpParamSerializerJQLike:nf,$httpBackend:of,$xhrFactory:pf,$location:qf,$log:rf,$parse:sf,$rootScope:tf,$q:uf,$$q:vf,$sce:wf,$sceDelegate:xf,$sniffer:yf,$templateCache:zf,$templateRequest:Af,$$testability:Bf,$timeout:Cf,$window:Df,$$rAF:Ef,$$jqLite:Ff,$$HashMap:Gf,$$cookieReader:Hf})}])}function cb(a){return a.replace(If,
function(a,d,c,e){return e?c.toUpperCase():c}).replace(Jf,"Moz$1")}function Kc(a){a=a.nodeType;return 1===a||!a||9===a}function Lc(a,b){var d,c,e=b.createDocumentFragment(),f=[];if(Tb.test(a)){d=d||e.appendChild(b.createElement("div"));c=(Kf.exec(a)||["",""])[1].toLowerCase();c=ia[c]||ia._default;d.innerHTML=c[1]+a.replace(Lf,"<$1></$2>")+c[2];for(c=c[0];c--;)d=d.lastChild;f=$a(f,d.childNodes);d=e.firstChild;d.textContent=""}else f.push(b.createTextNode(a));e.textContent="";e.innerHTML="";q(f,function(a){e.appendChild(a)});
return e}function Mc(a,b){var d=a.parentNode;d&&d.replaceChild(b,a);b.appendChild(a)}function U(a){if(a instanceof U)return a;var b;F(a)&&(a=V(a),b=!0);if(!(this instanceof U)){if(b&&"<"!=a.charAt(0))throw Ub("nosel");return new U(a)}if(b){b=v.document;var d;a=(d=Mf.exec(a))?[b.createElement(d[1])]:(d=Lc(a,b))?d.childNodes:[]}Nc(this,a)}function Vb(a){return a.cloneNode(!0)}function ub(a,b){b||db(a);if(a.querySelectorAll)for(var d=a.querySelectorAll("*"),c=0,e=d.length;c<e;c++)db(d[c])}function Oc(a,
b,d,c){if(x(c))throw Ub("offargs");var e=(c=vb(a))&&c.events,f=c&&c.handle;if(f)if(b){var g=function(b){var c=e[b];x(d)&&Za(c||[],d);x(d)&&c&&0<c.length||(a.removeEventListener(b,f,!1),delete e[b])};q(b.split(" "),function(a){g(a);wb[a]&&g(wb[a])})}else for(b in e)"$destroy"!==b&&a.removeEventListener(b,f,!1),delete e[b]}function db(a,b){var d=a.ng339,c=d&&eb[d];c&&(b?delete c.data[b]:(c.handle&&(c.events.$destroy&&c.handle({},"$destroy"),Oc(a)),delete eb[d],a.ng339=void 0))}function vb(a,b){var d=
a.ng339,d=d&&eb[d];b&&!d&&(a.ng339=d=++Nf,d=eb[d]={events:{},data:{},handle:void 0});return d}function Wb(a,b,d){if(Kc(a)){var c=x(d),e=!c&&b&&!G(b),f=!b;a=(a=vb(a,!e))&&a.data;if(c)a[b]=d;else{if(f)return a;if(e)return a&&a[b];R(a,b)}}}function xb(a,b){return a.getAttribute?-1<(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+b+" "):!1}function yb(a,b){b&&a.setAttribute&&q(b.split(" "),function(b){a.setAttribute("class",V((" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g,
" ").replace(" "+V(b)+" "," ")))})}function zb(a,b){if(b&&a.setAttribute){var d=(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");q(b.split(" "),function(a){a=V(a);-1===d.indexOf(" "+a+" ")&&(d+=a+" ")});a.setAttribute("class",V(d))}}function Nc(a,b){if(b)if(b.nodeType)a[a.length++]=b;else{var d=b.length;if("number"===typeof d&&b.window!==b){if(d)for(var c=0;c<d;c++)a[a.length++]=b[c]}else a[a.length++]=b}}function Pc(a,b){return Ab(a,"$"+(b||"ngController")+"Controller")}function Ab(a,
b,d){9==a.nodeType&&(a=a.documentElement);for(b=K(b)?b:[b];a;){for(var c=0,e=b.length;c<e;c++)if(x(d=B.data(a,b[c])))return d;a=a.parentNode||11===a.nodeType&&a.host}}function Qc(a){for(ub(a,!0);a.firstChild;)a.removeChild(a.firstChild)}function Bb(a,b){b||ub(a);var d=a.parentNode;d&&d.removeChild(a)}function Of(a,b){b=b||v;if("complete"===b.document.readyState)b.setTimeout(a);else B(b).on("load",a)}function Rc(a,b){var d=Cb[b.toLowerCase()];return d&&Sc[va(a)]&&d}function Pf(a,b){var d=function(c,
d){c.isDefaultPrevented=function(){return c.defaultPrevented};var f=b[d||c.type],g=f?f.length:0;if(g){if(y(c.immediatePropagationStopped)){var h=c.stopImmediatePropagation;c.stopImmediatePropagation=function(){c.immediatePropagationStopped=!0;c.stopPropagation&&c.stopPropagation();h&&h.call(c)}}c.isImmediatePropagationStopped=function(){return!0===c.immediatePropagationStopped};var k=f.specialHandlerWrapper||Qf;1<g&&(f=ha(f));for(var l=0;l<g;l++)c.isImmediatePropagationStopped()||k(a,c,f[l])}};d.elem=
a;return d}function Qf(a,b,d){d.call(a,b)}function Rf(a,b,d){var c=b.relatedTarget;c&&(c===a||Sf.call(a,c))||d.call(a,b)}function Ff(){this.$get=function(){return R(U,{hasClass:function(a,b){a.attr&&(a=a[0]);return xb(a,b)},addClass:function(a,b){a.attr&&(a=a[0]);return zb(a,b)},removeClass:function(a,b){a.attr&&(a=a[0]);return yb(a,b)}})}}function Fa(a,b){var d=a&&a.$$hashKey;if(d)return"function"===typeof d&&(d=a.$$hashKey()),d;d=typeof a;return d="function"==d||"object"==d&&null!==a?a.$$hashKey=
d+":"+(b||Xd)():d+":"+a}function Ra(a,b){if(b){var d=0;this.nextUid=function(){return++d}}q(a,this.put,this)}function Tc(a){a=Function.prototype.toString.call(a).replace(Tf,"");return a.match(Uf)||a.match(Vf)}function Wf(a){return(a=Tc(a))?"function("+(a[1]||"").replace(/[\s\r\n]+/," ")+")":"fn"}function bb(a,b){function d(a){return function(b,c){if(G(b))q(b,qc(a));else return a(b,c)}}function c(a,b){Qa(a,"service");if(E(b)||K(b))b=r.instantiate(b);if(!b.$get)throw Ga("pget",a);return m[a+"Provider"]=
b}function e(a,b){return function(){var c=w.invoke(b,this);if(y(c))throw Ga("undef",a);return c}}function f(a,b,d){return c(a,{$get:!1!==d?e(a,b):b})}function g(a){qb(y(a)||K(a),"modulesToLoad","not an array");var b=[],c;q(a,function(a){function d(a){var b,c;b=0;for(c=a.length;b<c;b++){var e=a[b],f=r.get(e[0]);f[e[1]].apply(f,e[2])}}if(!n.get(a)){n.put(a,!0);try{F(a)?(c=Sb(a),b=b.concat(g(c.requires)).concat(c._runBlocks),d(c._invokeQueue),d(c._configBlocks)):E(a)?b.push(r.invoke(a)):K(a)?b.push(r.invoke(a)):
Pa(a,"module")}catch(e){throw K(a)&&(a=a[a.length-1]),e.message&&e.stack&&-1==e.stack.indexOf(e.message)&&(e=e.message+"\n"+e.stack),Ga("modulerr",a,e.stack||e.message||e);}}});return b}function h(a,c){function d(b,e){if(a.hasOwnProperty(b)){if(a[b]===k)throw Ga("cdep",b+" <- "+l.join(" <- "));return a[b]}try{return l.unshift(b),a[b]=k,a[b]=c(b,e)}catch(f){throw a[b]===k&&delete a[b],f;}finally{l.shift()}}function e(a,c,f){var g=[];a=bb.$$annotate(a,b,f);for(var h=0,k=a.length;h<k;h++){var l=a[h];
if("string"!==typeof l)throw Ga("itkn",l);g.push(c&&c.hasOwnProperty(l)?c[l]:d(l,f))}return g}return{invoke:function(a,b,c,d){"string"===typeof c&&(d=c,c=null);c=e(a,c,d);K(a)&&(a=a[a.length-1]);d=11>=Ca?!1:"function"===typeof a&&/^(?:class\s|constructor\()/.test(Function.prototype.toString.call(a));return d?(c.unshift(null),new (Function.prototype.bind.apply(a,c))):a.apply(b,c)},instantiate:function(a,b,c){var d=K(a)?a[a.length-1]:a;a=e(a,b,c);a.unshift(null);return new (Function.prototype.bind.apply(d,
a))},get:d,annotate:bb.$$annotate,has:function(b){return m.hasOwnProperty(b+"Provider")||a.hasOwnProperty(b)}}}b=!0===b;var k={},l=[],n=new Ra([],!0),m={$provide:{provider:d(c),factory:d(f),service:d(function(a,b){return f(a,["$injector",function(a){return a.instantiate(b)}])}),value:d(function(a,b){return f(a,da(b),!1)}),constant:d(function(a,b){Qa(a,"constant");m[a]=b;N[a]=b}),decorator:function(a,b){var c=r.get(a+"Provider"),d=c.$get;c.$get=function(){var a=w.invoke(d,c);return w.invoke(b,null,
{$delegate:a})}}}},r=m.$injector=h(m,function(a,b){ea.isString(b)&&l.push(b);throw Ga("unpr",l.join(" <- "));}),N={},M=h(N,function(a,b){var c=r.get(a+"Provider",b);return w.invoke(c.$get,c,void 0,a)}),w=M;m.$injectorProvider={$get:da(M)};var p=g(a),w=M.get("$injector");w.strictDi=b;q(p,function(a){a&&w.invoke(a)});return w}function We(){var a=!0;this.disableAutoScrolling=function(){a=!1};this.$get=["$window","$location","$rootScope",function(b,d,c){function e(a){var b=null;Array.prototype.some.call(a,
function(a){if("a"===va(a))return b=a,!0});return b}function f(a){if(a){a.scrollIntoView();var c;c=g.yOffset;E(c)?c=c():Ob(c)?(c=c[0],c="fixed"!==b.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):Q(c)||(c=0);c&&(a=a.getBoundingClientRect().top,b.scrollBy(0,a-c))}else b.scrollTo(0,0)}function g(a){a=F(a)?a:d.hash();var b;a?(b=h.getElementById(a))?f(b):(b=e(h.getElementsByName(a)))?f(b):"top"===a&&f(null):f(null)}var h=b.document;a&&c.$watch(function(){return d.hash()},function(a,b){a===
b&&""===a||Of(function(){c.$evalAsync(g)})});return g}]}function fb(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;K(a)&&(a=a.join(" "));K(b)&&(b=b.join(" "));return a+" "+b}function Xf(a){F(a)&&(a=a.split(" "));var b=T();q(a,function(a){a.length&&(b[a]=!0)});return b}function Ha(a){return G(a)?a:{}}function Yf(a,b,d,c){function e(a){try{a.apply(null,za.call(arguments,1))}finally{if(M--,0===M)for(;w.length;)try{w.pop()()}catch(b){d.error(b)}}}function f(){u=null;g();h()}function g(){p=I();
p=y(p)?null:p;pa(p,L)&&(p=L);L=p}function h(){if(t!==k.url()||H!==p)t=k.url(),H=p,q(J,function(a){a(k.url(),p)})}var k=this,l=a.location,n=a.history,m=a.setTimeout,r=a.clearTimeout,N={};k.isMock=!1;var M=0,w=[];k.$$completeOutstandingRequest=e;k.$$incOutstandingRequestCount=function(){M++};k.notifyWhenNoOutstandingRequests=function(a){0===M?a():w.push(a)};var p,H,t=l.href,z=b.find("base"),u=null,I=c.history?function(){try{return n.state}catch(a){}}:C;g();H=p;k.url=function(b,d,e){y(e)&&(e=null);l!==
a.location&&(l=a.location);n!==a.history&&(n=a.history);if(b){var f=H===e;if(t===b&&(!c.history||f))return k;var h=t&&Ia(t)===Ia(b);t=b;H=e;if(!c.history||h&&f){if(!h||u)u=b;d?l.replace(b):h?(d=l,e=b.indexOf("#"),e=-1===e?"":b.substr(e),d.hash=e):l.href=b;l.href!==b&&(u=b)}else n[d?"replaceState":"pushState"](e,"",b),g(),H=p;return k}return u||l.href.replace(/%27/g,"'")};k.state=function(){return p};var J=[],D=!1,L=null;k.onUrlChange=function(b){if(!D){if(c.history)B(a).on("popstate",f);B(a).on("hashchange",
f);D=!0}J.push(b);return b};k.$$applicationDestroyed=function(){B(a).off("hashchange popstate",f)};k.$$checkUrlChange=h;k.baseHref=function(){var a=z.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};k.defer=function(a,b){var c;M++;c=m(function(){delete N[c];e(a)},b||0);N[c]=!0;return c};k.defer.cancel=function(a){return N[a]?(delete N[a],r(a),e(C),!0):!1}}function cf(){this.$get=["$window","$log","$sniffer","$document",function(a,b,d,c){return new Yf(a,c,b,d)}]}function df(){this.$get=
function(){function a(a,c){function e(a){a!=m&&(r?r==a&&(r=a.n):r=a,f(a.n,a.p),f(a,m),m=a,m.n=null)}function f(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(a in b)throw O("$cacheFactory")("iid",a);var g=0,h=R({},c,{id:a}),k=T(),l=c&&c.capacity||Number.MAX_VALUE,n=T(),m=null,r=null;return b[a]={put:function(a,b){if(!y(b)){if(l<Number.MAX_VALUE){var c=n[a]||(n[a]={key:a});e(c)}a in k||g++;k[a]=b;g>l&&this.remove(r.key);return b}},get:function(a){if(l<Number.MAX_VALUE){var b=n[a];if(!b)return;e(b)}return k[a]},
remove:function(a){if(l<Number.MAX_VALUE){var b=n[a];if(!b)return;b==m&&(m=b.p);b==r&&(r=b.n);f(b.n,b.p);delete n[a]}a in k&&(delete k[a],g--)},removeAll:function(){k=T();g=0;n=T();m=r=null},destroy:function(){n=h=k=null;delete b[a]},info:function(){return R({},h,{size:g})}}}var b={};a.info=function(){var a={};q(b,function(b,e){a[e]=b.info()});return a};a.get=function(a){return b[a]};return a}}function zf(){this.$get=["$cacheFactory",function(a){return a("templates")}]}function Cc(a,b){function d(a,
b,c){var d=/^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/,e=T();q(a,function(a,f){if(a in n)e[f]=n[a];else{var g=a.match(d);if(!g)throw ga("iscp",b,f,a,c?"controller bindings definition":"isolate scope definition");e[f]={mode:g[1][0],collection:"*"===g[2],optional:"?"===g[3],attrName:g[4]||f};g[4]&&(n[a]=e[f])}});return e}function c(a){var b=a.charAt(0);if(!b||b!==P(b))throw ga("baddir",a);if(a!==a.trim())throw ga("baddir",a);}var e={},f=/^\s*directive\:\s*([\w\-]+)\s+(.*)$/,g=/(([\w\-]+)(?:\:([^;]+))?;?)/,
h=ae("ngSrc,ngSrcset,src,srcset"),k=/^(?:(\^\^?)?(\?)?(\^\^?)?)?/,l=/^(on[a-z]+|formaction)$/,n=T();this.directive=function M(b,d){Qa(b,"directive");F(b)?(c(b),qb(d,"directiveFactory"),e.hasOwnProperty(b)||(e[b]=[],a.factory(b+"Directive",["$injector","$exceptionHandler",function(a,c){var d=[];q(e[b],function(e,f){try{var g=a.invoke(e);E(g)?g={compile:da(g)}:!g.compile&&g.link&&(g.compile=da(g.link));g.priority=g.priority||0;g.index=f;g.name=g.name||b;g.require=g.require||g.controller&&g.name;g.restrict=
g.restrict||"EA";g.$$moduleName=e.$$moduleName;d.push(g)}catch(h){c(h)}});return d}])),e[b].push(d)):q(b,qc(M));return this};this.component=function(a,b){function c(a){function e(b){return E(b)||K(b)?function(c,d){return a.invoke(b,this,{$element:c,$attrs:d})}:b}var f=b.template||b.templateUrl?b.template:"",g={controller:d,controllerAs:Uc(b.controller)||b.controllerAs||"$ctrl",template:e(f),templateUrl:e(b.templateUrl),transclude:b.transclude,scope:{},bindToController:b.bindings||{},restrict:"E",
require:b.require};q(b,function(a,b){"$"===b.charAt(0)&&(g[b]=a)});return g}var d=b.controller||function(){};q(b,function(a,b){"$"===b.charAt(0)&&(c[b]=a,E(d)&&(d[b]=a))});c.$inject=["$injector"];return this.directive(a,c)};this.aHrefSanitizationWhitelist=function(a){return x(a)?(b.aHrefSanitizationWhitelist(a),this):b.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(a){return x(a)?(b.imgSrcSanitizationWhitelist(a),this):b.imgSrcSanitizationWhitelist()};var m=!0;this.debugInfoEnabled=
function(a){return x(a)?(m=a,this):m};var r=10;this.onChangesTtl=function(a){return arguments.length?(r=a,this):r};this.$get=["$injector","$interpolate","$exceptionHandler","$templateRequest","$parse","$controller","$rootScope","$sce","$animate","$$sanitizeUri",function(a,b,c,n,t,z,u,I,J,D){function L(){try{if(!--qa)throw Z=void 0,ga("infchng",r);u.$apply(function(){for(var a=0,b=Z.length;a<b;++a)Z[a]();Z=void 0})}finally{qa++}}function S(a,b){if(b){var c=Object.keys(b),d,e,f;d=0;for(e=c.length;d<
e;d++)f=c[d],this[f]=b[f]}else this.$attr={};this.$$element=a}function $(a,b,c){na.innerHTML="<span "+b+">";b=na.firstChild.attributes;var d=b[0];b.removeNamedItem(d.name);d.value=c;a.attributes.setNamedItem(d)}function A(a,b){try{a.addClass(b)}catch(c){}}function ba(a,b,c,d,e){a instanceof B||(a=B(a));for(var f=/\S+/,g=0,h=a.length;g<h;g++){var k=a[g];k.nodeType===Ma&&k.nodeValue.match(f)&&Mc(k,a[g]=v.document.createElement("span"))}var l=s(a,b,a,c,d,e);ba.$$addScopeClass(a);var m=null;return function(b,
c,d){qb(b,"scope");e&&e.needsNewScope&&(b=b.$parent.$new());d=d||{};var f=d.parentBoundTranscludeFn,g=d.transcludeControllers;d=d.futureParentElement;f&&f.$$boundTransclude&&(f=f.$$boundTransclude);m||(m=(d=d&&d[0])?"foreignobject"!==va(d)&&ma.call(d).match(/SVG/)?"svg":"html":"html");d="html"!==m?B(ca(m,B("<div>").append(a).html())):c?Oa.clone.call(a):a;if(g)for(var h in g)d.data("$"+h+"Controller",g[h].instance);ba.$$addScopeInfo(d,b);c&&c(d,b);l&&l(b,d,d,f);return d}}function s(a,b,c,d,e,f){function g(a,
c,d,e){var f,k,l,m,n,t,p;if(r)for(p=Array(c.length),m=0;m<h.length;m+=3)f=h[m],p[f]=c[f];else p=c;m=0;for(n=h.length;m<n;)k=p[h[m++]],c=h[m++],f=h[m++],c?(c.scope?(l=a.$new(),ba.$$addScopeInfo(B(k),l)):l=a,t=c.transcludeOnThisElement?ka(a,c.transclude,e):!c.templateOnThisElement&&e?e:!e&&b?ka(a,b):null,c(f,l,k,d,t)):f&&f(a,k.childNodes,void 0,e)}for(var h=[],k,l,m,n,r,t=0;t<a.length;t++){k=new S;l=x(a[t],[],k,0===t?d:void 0,e);(f=l.length?Ba(l,a[t],k,b,c,null,[],[],f):null)&&f.scope&&ba.$$addScopeClass(k.$$element);
k=f&&f.terminal||!(m=a[t].childNodes)||!m.length?null:s(m,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b);if(f||k)h.push(t,f,k),n=!0,r=r||f;f=null}return n?g:null}function ka(a,b,c){function d(e,f,g,h,k){e||(e=a.$new(!1,k),e.$$transcluded=!0);return b(e,f,{parentBoundTranscludeFn:c,transcludeControllers:g,futureParentElement:h})}var e=d.$$slots=T(),f;for(f in b.$$slots)e[f]=b.$$slots[f]?ka(a,b.$$slots[f],c):null;return d}function x(a,b,c,d,e){var h=c.$attr,k;switch(a.nodeType){case 1:la(b,
xa(va(a)),"E",d,e);for(var l,m,n,t=a.attributes,r=0,p=t&&t.length;r<p;r++){var I=!1,D=!1;l=t[r];k=l.name;m=V(l.value);l=xa(k);if(n=ya.test(l))k=k.replace(Vc,"").substr(8).replace(/_(.)/g,function(a,b){return b.toUpperCase()});(l=l.match(Aa))&&Q(l[1])&&(I=k,D=k.substr(0,k.length-5)+"end",k=k.substr(0,k.length-6));l=xa(k.toLowerCase());h[l]=k;if(n||!c.hasOwnProperty(l))c[l]=m,Rc(a,l)&&(c[l]=!0);fa(a,b,m,l,n);la(b,l,"A",d,e,I,D)}a=a.className;G(a)&&(a=a.animVal);if(F(a)&&""!==a)for(;k=g.exec(a);)l=xa(k[2]),
la(b,l,"C",d,e)&&(c[l]=V(k[3])),a=a.substr(k.index+k[0].length);break;case Ma:if(11===Ca)for(;a.parentNode&&a.nextSibling&&a.nextSibling.nodeType===Ma;)a.nodeValue+=a.nextSibling.nodeValue,a.parentNode.removeChild(a.nextSibling);X(b,a.nodeValue);break;case 8:try{if(k=f.exec(a.nodeValue))l=xa(k[1]),la(b,l,"M",d,e)&&(c[l]=V(k[2]))}catch(J){}}b.sort(Y);return b}function Wc(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ga("uterdir",b,c);1==a.nodeType&&(a.hasAttribute(b)&&
e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return B(d)}function Xc(a,b,c){return function(d,e,f,g,h){e=Wc(e[0],b,c);return a(d,e,f,g,h)}}function Yb(a,b,c,d,e,f){var g;return a?ba(b,c,d,e,f):function(){g||(g=ba(b,c,d,e,f),b=c=f=null);return g.apply(this,arguments)}}function Ba(a,b,d,e,f,g,h,k,l){function m(a,b,c,d){if(a){c&&(a=Xc(a,c,d));a.require=A.require;a.directiveName=M;if(D===A||A.$$isolateScope)a=ha(a,{isolateScope:!0});h.push(a)}if(b){c&&(b=Xc(b,c,d));
b.require=A.require;b.directiveName=M;if(D===A||A.$$isolateScope)b=ha(b,{isolateScope:!0});k.push(b)}}function n(a,c,e,f,g){function l(a,b,c,d){var e;Ya(a)||(d=c,c=b,b=a,a=void 0);H&&(e=u);c||(c=H?z.parent():z);if(d){var f=g.$$slots[d];if(f)return f(a,b,e,c,$);if(y(f))throw ga("noslot",d,wa(z));}else return g(a,b,e,c,$)}var m,t,p,A,w,u,L,z;b===e?(f=d,z=d.$$element):(z=B(e),f=new S(z,d));w=c;D?A=c.$new(!0):r&&(w=c.$parent);g&&(L=l,L.$$boundTransclude=g,L.isSlotFilled=function(a){return!!g.$$slots[a]});
I&&(u=O(z,f,L,I,A,c,D));D&&(ba.$$addScopeInfo(z,A,!0,!(J&&(J===D||J===D.$$originalDirective))),ba.$$addScopeClass(z,!0),A.$$isolateBindings=D.$$isolateBindings,t=ia(c,f,A,A.$$isolateBindings,D),t.removeWatches&&A.$on("$destroy",t.removeWatches));for(m in u){t=I[m];p=u[m];var Xb=t.$$bindings.bindToController;p.bindingInfo=p.identifier&&Xb?ia(w,f,p.instance,Xb,t):{};var M=p();M!==p.instance&&(p.instance=M,z.data("$"+t.name+"Controller",M),p.bindingInfo.removeWatches&&p.bindingInfo.removeWatches(),p.bindingInfo=
ia(w,f,p.instance,Xb,t))}q(I,function(a,b){var c=a.require;a.bindToController&&!K(c)&&G(c)&&R(u[b].instance,gb(b,c,z,u))});q(u,function(a){var b=a.instance;E(b.$onChanges)&&b.$onChanges(a.bindingInfo.initialChanges);E(b.$onInit)&&b.$onInit();E(b.$onDestroy)&&w.$on("$destroy",function(){b.$onDestroy()})});m=0;for(t=h.length;m<t;m++)p=h[m],ja(p,p.isolateScope?A:c,z,f,p.require&&gb(p.directiveName,p.require,z,u),L);var $=c;D&&(D.template||null===D.templateUrl)&&($=A);a&&a($,e.childNodes,void 0,g);for(m=
k.length-1;0<=m;m--)p=k[m],ja(p,p.isolateScope?A:c,z,f,p.require&&gb(p.directiveName,p.require,z,u),L);q(u,function(a){a=a.instance;E(a.$postLink)&&a.$postLink()})}l=l||{};for(var t=-Number.MAX_VALUE,r=l.newScopeDirective,I=l.controllerDirectives,D=l.newIsolateScopeDirective,J=l.templateDirective,w=l.nonTlbTranscludeDirective,u=!1,L=!1,H=l.hasElementTranscludeDirective,z=d.$$element=B(b),A,M,$,s=e,Sa,ka=!1,C=!1,v,F=0,Ba=a.length;F<Ba;F++){A=a[F];var P=A.$$start,Q=A.$$end;P&&(z=Wc(b,P,Q));$=void 0;
if(t>A.priority)break;if(v=A.scope)A.templateUrl||(G(v)?(W("new/isolated scope",D||r,A,z),D=A):W("new/isolated scope",D,A,z)),r=r||A;M=A.name;if(!ka&&(A.replace&&(A.templateUrl||A.template)||A.transclude&&!A.$$tlb)){for(v=F+1;ka=a[v++];)if(ka.transclude&&!ka.$$tlb||ka.replace&&(ka.templateUrl||ka.template)){C=!0;break}ka=!0}!A.templateUrl&&A.controller&&(v=A.controller,I=I||T(),W("'"+M+"' controller",I[M],A,z),I[M]=A);if(v=A.transclude)if(u=!0,A.$$tlb||(W("transclusion",w,A,z),w=A),"element"==v)H=
!0,t=A.priority,$=z,z=d.$$element=B(ba.$$createComment(M,d[M])),b=z[0],da(f,za.call($,0),b),$[0].$$parentNode=$[0].parentNode,s=Yb(C,$,e,t,g&&g.name,{nonTlbTranscludeDirective:w});else{var la=T();$=B(Vb(b)).contents();if(G(v)){$=[];var Y=T(),X=T();q(v,function(a,b){var c="?"===a.charAt(0);a=c?a.substring(1):a;Y[a]=b;la[b]=null;X[b]=c});q(z.contents(),function(a){var b=Y[xa(va(a))];b?(X[b]=!0,la[b]=la[b]||[],la[b].push(a)):$.push(a)});q(X,function(a,b){if(!a)throw ga("reqslot",b);});for(var Z in la)la[Z]&&
(la[Z]=Yb(C,la[Z],e))}z.empty();s=Yb(C,$,e,void 0,void 0,{needsNewScope:A.$$isolateScope||A.$$newScope});s.$$slots=la}if(A.template)if(L=!0,W("template",J,A,z),J=A,v=E(A.template)?A.template(z,d):A.template,v=ta(v),A.replace){g=A;$=Tb.test(v)?Yc(ca(A.templateNamespace,V(v))):[];b=$[0];if(1!=$.length||1!==b.nodeType)throw ga("tplrt",M,"");da(f,z,b);Ba={$attr:{}};v=x(b,[],Ba);var ea=a.splice(F+1,a.length-(F+1));(D||r)&&Zc(v,D,r);a=a.concat(v).concat(ea);U(d,Ba);Ba=a.length}else z.html(v);if(A.templateUrl)L=
!0,W("template",J,A,z),J=A,A.replace&&(g=A),n=aa(a.splice(F,a.length-F),z,d,f,u&&s,h,k,{controllerDirectives:I,newScopeDirective:r!==A&&r,newIsolateScopeDirective:D,templateDirective:J,nonTlbTranscludeDirective:w}),Ba=a.length;else if(A.compile)try{Sa=A.compile(z,d,s),E(Sa)?m(null,Sa,P,Q):Sa&&m(Sa.pre,Sa.post,P,Q)}catch(fa){c(fa,wa(z))}A.terminal&&(n.terminal=!0,t=Math.max(t,A.priority))}n.scope=r&&!0===r.scope;n.transcludeOnThisElement=u;n.templateOnThisElement=L;n.transclude=s;l.hasElementTranscludeDirective=
H;return n}function gb(a,b,c,d){var e;if(F(b)){var f=b.match(k);b=b.substring(f[0].length);var g=f[1]||f[3],f="?"===f[2];"^^"===g?c=c.parent():e=(e=d&&d[b])&&e.instance;if(!e){var h="$"+b+"Controller";e=g?c.inheritedData(h):c.data(h)}if(!e&&!f)throw ga("ctreq",b,a);}else if(K(b))for(e=[],g=0,f=b.length;g<f;g++)e[g]=gb(a,b[g],c,d);else G(b)&&(e={},q(b,function(b,f){e[f]=gb(a,b,c,d)}));return e||null}function O(a,b,c,d,e,f,g){var h=T(),k;for(k in d){var l=d[k],m={$scope:l===g||l.$$isolateScope?e:f,
$element:a,$attrs:b,$transclude:c},n=l.controller;"@"==n&&(n=b[l.name]);m=z(n,m,!0,l.controllerAs);h[l.name]=m;a.data("$"+l.name+"Controller",m.instance)}return h}function Zc(a,b,c){for(var d=0,e=a.length;d<e;d++)a[d]=Pb(a[d],{$$isolateScope:b,$$newScope:c})}function la(b,f,g,h,k,l,m){if(f===k)return null;k=null;if(e.hasOwnProperty(f)){var n;f=a.get(f+"Directive");for(var t=0,r=f.length;t<r;t++)try{if(n=f[t],(y(h)||h>n.priority)&&-1!=n.restrict.indexOf(g)){l&&(n=Pb(n,{$$start:l,$$end:m}));if(!n.$$bindings){var I=
n,D=n,A=n.name,J={isolateScope:null,bindToController:null};G(D.scope)&&(!0===D.bindToController?(J.bindToController=d(D.scope,A,!0),J.isolateScope={}):J.isolateScope=d(D.scope,A,!1));G(D.bindToController)&&(J.bindToController=d(D.bindToController,A,!0));if(G(J.bindToController)){var w=D.controller,z=D.controllerAs;if(!w)throw ga("noctrl",A);if(!Uc(w,z))throw ga("noident",A);}var u=I.$$bindings=J;G(u.isolateScope)&&(n.$$isolateBindings=u.isolateScope)}b.push(n);k=n}}catch(L){c(L)}}return k}function Q(b){if(e.hasOwnProperty(b))for(var c=
a.get(b+"Directive"),d=0,f=c.length;d<f;d++)if(b=c[d],b.multiElement)return!0;return!1}function U(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;q(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});q(b,function(b,f){"class"==f?(A(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function aa(a,b,c,d,e,f,
g,h){var k=[],l,m,t=b[0],p=a.shift(),r=Pb(p,{templateUrl:null,transclude:null,replace:null,$$originalDirective:p}),I=E(p.templateUrl)?p.templateUrl(b,c):p.templateUrl,D=p.templateNamespace;b.empty();n(I).then(function(n){var J,w;n=ta(n);if(p.replace){n=Tb.test(n)?Yc(ca(D,V(n))):[];J=n[0];if(1!=n.length||1!==J.nodeType)throw ga("tplrt",p.name,I);n={$attr:{}};da(d,b,J);var z=x(J,[],n);G(p.scope)&&Zc(z,!0);a=z.concat(a);U(c,n)}else J=t,b.html(n);a.unshift(r);l=Ba(a,J,c,e,b,p,f,g,h);q(d,function(a,c){a==
J&&(d[c]=b[0])});for(m=s(b[0].childNodes,e);k.length;){n=k.shift();w=k.shift();var u=k.shift(),L=k.shift(),z=b[0];if(!n.$$destroyed){if(w!==t){var S=w.className;h.hasElementTranscludeDirective&&p.replace||(z=Vb(J));da(u,B(w),z);A(B(z),S)}w=l.transcludeOnThisElement?ka(n,l.transclude,L):L;l(m,n,z,d,w)}}k=null});return function(a,b,c,d,e){a=e;b.$$destroyed||(k?k.push(b,c,d,a):(l.transcludeOnThisElement&&(a=ka(b,l.transclude,e)),l(m,b,c,d,a)))}}function Y(a,b){var c=b.priority-a.priority;return 0!==
c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function W(a,b,c,d){function e(a){return a?" (module: "+a+")":""}if(b)throw ga("multidir",b.name,e(b.$$moduleName),c.name,e(c.$$moduleName),a,wa(d));}function X(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){a=a.parent();var b=!!a.length;b&&ba.$$addBindingClass(a);return function(a,c){var e=c.parent();b||ba.$$addBindingClass(e);ba.$$addBindingInfo(e,d.expressions);a.$watch(d,function(a){c[0].nodeValue=a})}}})}function ca(a,b){a=
P(a||"html");switch(a){case "svg":case "math":var c=v.document.createElement("div");c.innerHTML="<"+a+">"+b+"</"+a+">";return c.childNodes[0].childNodes;default:return b}}function ea(a,b){if("srcdoc"==b)return I.HTML;var c=va(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"ngSrc"==b))return I.RESOURCE_URL}function fa(a,c,d,e,f){var g=ea(a,e);f=h[e]||f;var k=b(d,!0,g,f);if(k){if("multiple"===e&&"select"===va(a))throw ga("selmulti",wa(a));c.push({priority:100,compile:function(){return{pre:function(a,
c,h){c=h.$$observers||(h.$$observers=T());if(l.test(e))throw ga("nodomevents");var m=h[e];m!==d&&(k=m&&b(m,!0,g,f),d=m);k&&(h[e]=k(a),(c[e]||(c[e]=[])).$$inter=!0,(h.$$observers&&h.$$observers[e].$$scope||a).$watch(k,function(a,b){"class"===e&&a!=b?h.$updateClass(a,b):h.$set(e,a)}))}}}})}}function da(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g<h;g++)if(a[g]==d){a[g++]=c;h=g+e-1;for(var k=a.length;g<k;g++,h++)h<k?a[g]=a[h]:delete a[g];a.length-=e-1;a.context===d&&(a.context=
c);break}f&&f.replaceChild(c,d);a=v.document.createDocumentFragment();for(g=0;g<e;g++)a.appendChild(b[g]);B.hasData(d)&&(B.data(c,B.data(d)),B(d).off("$destroy"));B.cleanData(a.querySelectorAll("*"));for(g=1;g<e;g++)delete b[g];b[0]=c;b.length=1}function ha(a,b){return R(function(){return a.apply(null,arguments)},a,b)}function ja(a,b,d,e,f,g){try{a(b,d,e,f,g)}catch(h){c(h,wa(d))}}function ia(a,c,d,e,f){function g(b,c,e){E(d.$onChanges)&&c!==e&&(Z||(a.$$postDigest(L),Z=[]),m||(m={},Z.push(h)),m[b]&&
(e=m[b].previousValue),m[b]=new Db(e,c))}function h(){d.$onChanges(m);m=void 0}var k=[],l={},m;q(e,function(e,h){var m=e.attrName,n=e.optional,p,r,I,D;switch(e.mode){case "@":n||ua.call(c,m)||(d[h]=c[m]=void 0);c.$observe(m,function(a){if(F(a)||Da(a))g(h,a,d[h]),d[h]=a});c.$$observers[m].$$scope=a;p=c[m];F(p)?d[h]=b(p)(a):Da(p)&&(d[h]=p);l[h]=new Db(Zb,d[h]);break;case "=":if(!ua.call(c,m)){if(n)break;c[m]=void 0}if(n&&!c[m])break;r=t(c[m]);D=r.literal?pa:function(a,b){return a===b||a!==a&&b!==b};
I=r.assign||function(){p=d[h]=r(a);throw ga("nonassign",c[m],m,f.name);};p=d[h]=r(a);n=function(b){D(b,d[h])||(D(b,p)?I(a,b=d[h]):d[h]=b);return p=b};n.$stateful=!0;n=e.collection?a.$watchCollection(c[m],n):a.$watch(t(c[m],n),null,r.literal);k.push(n);break;case "<":if(!ua.call(c,m)){if(n)break;c[m]=void 0}if(n&&!c[m])break;r=t(c[m]);d[h]=r(a);l[h]=new Db(Zb,d[h]);n=a.$watch(r,function(a,b){a===b&&(b=d[h]);g(h,a,b);d[h]=a},r.literal);k.push(n);break;case "&":r=c.hasOwnProperty(m)?t(c[m]):C;if(r===
C&&n)break;d[h]=function(b){return r(a,b)}}});return{initialChanges:l,removeWatches:k.length&&function(){for(var a=0,b=k.length;a<b;++a)k[a]()}}}var oa=/^\w/,na=v.document.createElement("div"),qa=r,Z;S.prototype={$normalize:xa,$addClass:function(a){a&&0<a.length&&J.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&J.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=$c(a,b);c&&c.length&&J.addClass(this.$$element,c);(c=$c(b,a))&&c.length&&J.removeClass(this.$$element,
c)},$set:function(a,b,d,e){var f=Rc(this.$$element[0],a),g=ad[a],h=a;f?(this.$$element.prop(a,b),e=f):g&&(this[g]=b,h=g);this[a]=b;e?this.$attr[a]=e:(e=this.$attr[a])||(this.$attr[a]=e=zc(a,"-"));f=va(this.$$element);if("a"===f&&("href"===a||"xlinkHref"===a)||"img"===f&&"src"===a)this[a]=b=D(b,"src"===a);else if("img"===f&&"srcset"===a){for(var f="",g=V(b),k=/(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/,k=/\s/.test(g)?k:/(,)/,g=g.split(k),k=Math.floor(g.length/2),l=0;l<k;l++)var m=2*l,f=f+D(V(g[m]),!0),f=
f+(" "+V(g[m+1]));g=V(g[2*l]).split(/\s/);f+=D(V(g[0]),!0);2===g.length&&(f+=" "+V(g[1]));this[a]=b=f}!1!==d&&(null===b||y(b)?this.$$element.removeAttr(e):oa.test(e)?this.$$element.attr(e,b):$(this.$$element[0],e,b));(a=this.$$observers)&&q(a[h],function(a){try{a(b)}catch(d){c(d)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers=T()),e=d[a]||(d[a]=[]);e.push(b);u.$evalAsync(function(){e.$$inter||!c.hasOwnProperty(a)||y(c[a])||b(c[a])});return function(){Za(e,b)}}};var ra=b.startSymbol(),
sa=b.endSymbol(),ta="{{"==ra&&"}}"==sa?Xa:function(a){return a.replace(/\{\{/g,ra).replace(/}}/g,sa)},ya=/^ngAttr[A-Z]/,Aa=/^(.+)Start$/;ba.$$addBindingInfo=m?function(a,b){var c=a.data("$binding")||[];K(b)?c=c.concat(b):c.push(b);a.data("$binding",c)}:C;ba.$$addBindingClass=m?function(a){A(a,"ng-binding")}:C;ba.$$addScopeInfo=m?function(a,b,c,d){a.data(c?d?"$isolateScopeNoTemplate":"$isolateScope":"$scope",b)}:C;ba.$$addScopeClass=m?function(a,b){A(a,b?"ng-isolate-scope":"ng-scope")}:C;ba.$$createComment=
function(a,b){var c="";m&&(c=" "+(a||"")+": "+(b||"")+" ");return v.document.createComment(c)};return ba}]}function Db(a,b){this.previousValue=a;this.currentValue=b}function xa(a){return cb(a.replace(Vc,""))}function $c(a,b){var d="",c=a.split(/\s+/),e=b.split(/\s+/),f=0;a:for(;f<c.length;f++){for(var g=c[f],h=0;h<e.length;h++)if(g==e[h])continue a;d+=(0<d.length?" ":"")+g}return d}function Yc(a){a=B(a);var b=a.length;if(1>=b)return a;for(;b--;)8===a[b].nodeType&&Zf.call(a,b,1);return a}function Uc(a,
b){if(b&&F(b))return b;if(F(a)){var d=bd.exec(a);if(d)return d[3]}}function ef(){var a={},b=!1;this.has=function(b){return a.hasOwnProperty(b)};this.register=function(b,c){Qa(b,"controller");G(b)?R(a,b):a[b]=c};this.allowGlobals=function(){b=!0};this.$get=["$injector","$window",function(d,c){function e(a,b,c,d){if(!a||!G(a.$scope))throw O("$controller")("noscp",d,b);a.$scope[b]=c}return function(f,g,h,k){var l,n,m;h=!0===h;k&&F(k)&&(m=k);if(F(f)){k=f.match(bd);if(!k)throw $f("ctrlfmt",f);n=k[1];m=
m||k[3];f=a.hasOwnProperty(n)?a[n]:Bc(g.$scope,n,!0)||(b?Bc(c,n,!0):void 0);Pa(f,n,!0)}if(h)return h=(K(f)?f[f.length-1]:f).prototype,l=Object.create(h||null),m&&e(g,m,l,n||f.name),R(function(){var a=d.invoke(f,l,g,n);a!==l&&(G(a)||E(a))&&(l=a,m&&e(g,m,l,n||f.name));return l},{instance:l,identifier:m});l=d.instantiate(f,g,n);m&&e(g,m,l,n||f.name);return l}}]}function ff(){this.$get=["$window",function(a){return B(a.document)}]}function gf(){this.$get=["$log",function(a){return function(b,d){a.error.apply(a,
arguments)}}]}function $b(a){return G(a)?fa(a)?a.toISOString():ab(a):a}function mf(){this.$get=function(){return function(a){if(!a)return"";var b=[];pc(a,function(a,c){null===a||y(a)||(K(a)?q(a,function(a){b.push(ja(c)+"="+ja($b(a)))}):b.push(ja(c)+"="+ja($b(a))))});return b.join("&")}}}function nf(){this.$get=function(){return function(a){function b(a,e,f){null===a||y(a)||(K(a)?q(a,function(a,c){b(a,e+"["+(G(a)?c:"")+"]")}):G(a)&&!fa(a)?pc(a,function(a,c){b(a,e+(f?"":"[")+c+(f?"":"]"))}):d.push(ja(e)+
"="+ja($b(a))))}if(!a)return"";var d=[];b(a,"",!0);return d.join("&")}}}function ac(a,b){if(F(a)){var d=a.replace(ag,"").trim();if(d){var c=b("Content-Type");(c=c&&0===c.indexOf(cd))||(c=(c=d.match(bg))&&cg[c[0]].test(d));c&&(a=uc(d))}}return a}function dd(a){var b=T(),d;F(a)?q(a.split("\n"),function(a){d=a.indexOf(":");var e=P(V(a.substr(0,d)));a=V(a.substr(d+1));e&&(b[e]=b[e]?b[e]+", "+a:a)}):G(a)&&q(a,function(a,d){var f=P(d),g=V(a);f&&(b[f]=b[f]?b[f]+", "+g:g)});return b}function ed(a){var b;
return function(d){b||(b=dd(a));return d?(d=b[P(d)],void 0===d&&(d=null),d):b}}function fd(a,b,d,c){if(E(c))return c(a,b,d);q(c,function(c){a=c(a,b,d)});return a}function lf(){var a=this.defaults={transformResponse:[ac],transformRequest:[function(a){return G(a)&&"[object File]"!==ma.call(a)&&"[object Blob]"!==ma.call(a)&&"[object FormData]"!==ma.call(a)?ab(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:ha(bc),put:ha(bc),patch:ha(bc)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",
paramSerializer:"$httpParamSerializer"},b=!1;this.useApplyAsync=function(a){return x(a)?(b=!!a,this):b};var d=!0;this.useLegacyPromiseExtensions=function(a){return x(a)?(d=!!a,this):d};var c=this.interceptors=[];this.$get=["$httpBackend","$$cookieReader","$cacheFactory","$rootScope","$q","$injector",function(e,f,g,h,k,l){function n(b){function c(a){var b=R({},a);b.data=fd(a.data,a.headers,a.status,f.transformResponse);a=a.status;return 200<=a&&300>a?b:k.reject(b)}function e(a,b){var c,d={};q(a,function(a,
e){E(a)?(c=a(b),null!=c&&(d[e]=c)):d[e]=a});return d}if(!G(b))throw O("$http")("badreq",b);if(!F(b.url))throw O("$http")("badreq",b.url);var f=R({method:"get",transformRequest:a.transformRequest,transformResponse:a.transformResponse,paramSerializer:a.paramSerializer},b);f.headers=function(b){var c=a.headers,d=R({},b.headers),f,g,h,c=R({},c.common,c[P(b.method)]);a:for(f in c){g=P(f);for(h in d)if(P(h)===g)continue a;d[f]=c[f]}return e(d,ha(b))}(b);f.method=sb(f.method);f.paramSerializer=F(f.paramSerializer)?
l.get(f.paramSerializer):f.paramSerializer;var g=[function(b){var d=b.headers,e=fd(b.data,ed(d),void 0,b.transformRequest);y(e)&&q(d,function(a,b){"content-type"===P(b)&&delete d[b]});y(b.withCredentials)&&!y(a.withCredentials)&&(b.withCredentials=a.withCredentials);return m(b,e).then(c,c)},void 0],h=k.when(f);for(q(M,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;){b=g.shift();var n=g.shift(),
h=h.then(b,n)}d?(h.success=function(a){Pa(a,"fn");h.then(function(b){a(b.data,b.status,b.headers,f)});return h},h.error=function(a){Pa(a,"fn");h.then(null,function(b){a(b.data,b.status,b.headers,f)});return h}):(h.success=gd("success"),h.error=gd("error"));return h}function m(c,d){function g(a){if(a){var c={};q(a,function(a,d){c[d]=function(c){function d(){a(c)}b?h.$applyAsync(d):h.$$phase?d():h.$apply(d)}});return c}}function l(a,c,d,e){function f(){m(c,a,d,e)}L&&(200<=a&&300>a?L.put(A,[a,c,dd(d),
e]):L.remove(A));b?h.$applyAsync(f):(f(),h.$$phase||h.$apply())}function m(a,b,d,e){b=-1<=b?b:0;(200<=b&&300>b?J.resolve:J.reject)({data:a,status:b,headers:ed(d),config:c,statusText:e})}function u(a){m(a.data,a.status,ha(a.headers()),a.statusText)}function I(){var a=n.pendingRequests.indexOf(c);-1!==a&&n.pendingRequests.splice(a,1)}var J=k.defer(),D=J.promise,L,S,M=c.headers,A=r(c.url,c.paramSerializer(c.params));n.pendingRequests.push(c);D.then(I,I);!c.cache&&!a.cache||!1===c.cache||"GET"!==c.method&&
"JSONP"!==c.method||(L=G(c.cache)?c.cache:G(a.cache)?a.cache:N);L&&(S=L.get(A),x(S)?S&&E(S.then)?S.then(u,u):K(S)?m(S[1],S[0],ha(S[2]),S[3]):m(S,200,{},"OK"):L.put(A,D));y(S)&&((S=hd(c.url)?f()[c.xsrfCookieName||a.xsrfCookieName]:void 0)&&(M[c.xsrfHeaderName||a.xsrfHeaderName]=S),e(c.method,A,d,l,M,c.timeout,c.withCredentials,c.responseType,g(c.eventHandlers),g(c.uploadEventHandlers)));return D}function r(a,b){0<b.length&&(a+=(-1==a.indexOf("?")?"?":"&")+b);return a}var N=g("$http");a.paramSerializer=
F(a.paramSerializer)?l.get(a.paramSerializer):a.paramSerializer;var M=[];q(c,function(a){M.unshift(F(a)?l.get(a):l.invoke(a))});n.pendingRequests=[];(function(a){q(arguments,function(a){n[a]=function(b,c){return n(R({},c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){q(arguments,function(a){n[a]=function(b,c,d){return n(R({},d||{},{method:a,url:b,data:c}))}})})("post","put","patch");n.defaults=a;return n}]}function pf(){this.$get=function(){return function(){return new v.XMLHttpRequest}}}
function of(){this.$get=["$browser","$window","$document","$xhrFactory",function(a,b,d,c){return dg(a,c,a.defer,b.angular.callbacks,d[0])}]}function dg(a,b,d,c,e){function f(a,b,d){var f=e.createElement("script"),n=null;f.type="text/javascript";f.src=a;f.async=!0;n=function(a){f.removeEventListener("load",n,!1);f.removeEventListener("error",n,!1);e.body.removeChild(f);f=null;var g=-1,N="unknown";a&&("load"!==a.type||c[b].called||(a={type:"error"}),N=a.type,g="error"===a.type?404:200);d&&d(g,N)};f.addEventListener("load",
n,!1);f.addEventListener("error",n,!1);e.body.appendChild(f);return n}return function(e,h,k,l,n,m,r,N,M,w){function p(){z&&z();u&&u.abort()}function H(b,c,e,f,g){x(J)&&d.cancel(J);z=u=null;b(c,e,f,g);a.$$completeOutstandingRequest(C)}a.$$incOutstandingRequestCount();h=h||a.url();if("jsonp"==P(e)){var t="_"+(c.counter++).toString(36);c[t]=function(a){c[t].data=a;c[t].called=!0};var z=f(h.replace("JSON_CALLBACK","angular.callbacks."+t),t,function(a,b){H(l,a,c[t].data,"",b);c[t]=C})}else{var u=b(e,h);
u.open(e,h,!0);q(n,function(a,b){x(a)&&u.setRequestHeader(b,a)});u.onload=function(){var a=u.statusText||"",b="response"in u?u.response:u.responseText,c=1223===u.status?204:u.status;0===c&&(c=b?200:"file"==ra(h).protocol?404:0);H(l,c,b,u.getAllResponseHeaders(),a)};e=function(){H(l,-1,null,null,"")};u.onerror=e;u.onabort=e;q(M,function(a,b){u.addEventListener(b,a)});q(w,function(a,b){u.upload.addEventListener(b,a)});r&&(u.withCredentials=!0);if(N)try{u.responseType=N}catch(I){if("json"!==N)throw I;
}u.send(y(k)?null:k)}if(0<m)var J=d(p,m);else m&&E(m.then)&&m.then(p)}}function jf(){var a="{{",b="}}";this.startSymbol=function(b){return b?(a=b,this):a};this.endSymbol=function(a){return a?(b=a,this):b};this.$get=["$parse","$exceptionHandler","$sce",function(d,c,e){function f(a){return"\\\\\\"+a}function g(c){return c.replace(m,a).replace(r,b)}function h(a,b,c,d){var e;return e=a.$watch(function(a){e();return d(a)},b,c)}function k(f,k,m,r){function H(a){try{var b=a;a=m?e.getTrusted(m,b):e.valueOf(b);
var d;if(r&&!x(a))d=a;else if(null==a)d="";else{switch(typeof a){case "string":break;case "number":a=""+a;break;default:a=ab(a)}d=a}return d}catch(g){c(Ja.interr(f,g))}}if(!f.length||-1===f.indexOf(a)){var t;k||(k=g(f),t=da(k),t.exp=f,t.expressions=[],t.$$watchDelegate=h);return t}r=!!r;var z,u,I=0,J=[],D=[];t=f.length;for(var L=[],S=[];I<t;)if(-1!=(z=f.indexOf(a,I))&&-1!=(u=f.indexOf(b,z+l)))I!==z&&L.push(g(f.substring(I,z))),I=f.substring(z+l,u),J.push(I),D.push(d(I,H)),I=u+n,S.push(L.length),L.push("");
else{I!==t&&L.push(g(f.substring(I)));break}m&&1<L.length&&Ja.throwNoconcat(f);if(!k||J.length){var q=function(a){for(var b=0,c=J.length;b<c;b++){if(r&&y(a[b]))return;L[S[b]]=a[b]}return L.join("")};return R(function(a){var b=0,d=J.length,e=Array(d);try{for(;b<d;b++)e[b]=D[b](a);return q(e)}catch(g){c(Ja.interr(f,g))}},{exp:f,expressions:J,$$watchDelegate:function(a,b){var c;return a.$watchGroup(D,function(d,e){var f=q(d);E(b)&&b.call(this,f,d!==e?c:f,a);c=f})}})}}var l=a.length,n=b.length,m=new RegExp(a.replace(/./g,
f),"g"),r=new RegExp(b.replace(/./g,f),"g");k.startSymbol=function(){return a};k.endSymbol=function(){return b};return k}]}function kf(){this.$get=["$rootScope","$window","$q","$$q","$browser",function(a,b,d,c,e){function f(f,k,l,n){function m(){r?f.apply(null,N):f(p)}var r=4<arguments.length,N=r?za.call(arguments,4):[],q=b.setInterval,w=b.clearInterval,p=0,H=x(n)&&!n,t=(H?c:d).defer(),z=t.promise;l=x(l)?l:0;z.$$intervalId=q(function(){H?e.defer(m):a.$evalAsync(m);t.notify(p++);0<l&&p>=l&&(t.resolve(p),
w(z.$$intervalId),delete g[z.$$intervalId]);H||a.$apply()},k);g[z.$$intervalId]=t;return z}var g={};f.cancel=function(a){return a&&a.$$intervalId in g?(g[a.$$intervalId].reject("canceled"),b.clearInterval(a.$$intervalId),delete g[a.$$intervalId],!0):!1};return f}]}function cc(a){a=a.split("/");for(var b=a.length;b--;)a[b]=ob(a[b]);return a.join("/")}function id(a,b){var d=ra(a);b.$$protocol=d.protocol;b.$$host=d.hostname;b.$$port=X(d.port)||eg[d.protocol]||null}function jd(a,b){var d="/"!==a.charAt(0);
d&&(a="/"+a);var c=ra(a);b.$$path=decodeURIComponent(d&&"/"===c.pathname.charAt(0)?c.pathname.substring(1):c.pathname);b.$$search=xc(c.search);b.$$hash=decodeURIComponent(c.hash);b.$$path&&"/"!=b.$$path.charAt(0)&&(b.$$path="/"+b.$$path)}function na(a,b){if(0===b.indexOf(a))return b.substr(a.length)}function Ia(a){var b=a.indexOf("#");return-1==b?a:a.substr(0,b)}function hb(a){return a.replace(/(#.+)|#$/,"$1")}function dc(a,b,d){this.$$html5=!0;d=d||"";id(a,this);this.$$parse=function(a){var d=na(b,
a);if(!F(d))throw Eb("ipthprfx",a,b);jd(d,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Rb(this.$$search),d=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=cc(this.$$path)+(a?"?"+a:"")+d;this.$$absUrl=b+this.$$url.substr(1)};this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;x(f=na(a,c))?(g=f,g=x(f=na(d,f))?b+(na("/",f)||f):a+g):x(f=na(b,c))?g=b+f:b==c+"/"&&(g=b);g&&this.$$parse(g);return!!g}}function ec(a,b,d){id(a,this);
this.$$parse=function(c){var e=na(a,c)||na(b,c),f;y(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",y(e)&&(a=c,this.replace())):(f=na(d,e),y(f)&&(f=e));jd(f,this);c=this.$$path;var e=a,g=/^\/[A-Z]:(\/.*)/;0===f.indexOf(e)&&(f=f.replace(e,""));g.exec(f)||(c=(f=g.exec(c))?f[1]:c);this.$$path=c;this.$$compose()};this.$$compose=function(){var b=Rb(this.$$search),e=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=cc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+(this.$$url?d+this.$$url:"")};this.$$parseLinkUrl=
function(b,d){return Ia(a)==Ia(b)?(this.$$parse(b),!0):!1}}function kd(a,b,d){this.$$html5=!0;ec.apply(this,arguments);this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;a==Ia(c)?f=c:(g=na(b,c))?f=a+d+g:b===c+"/"&&(f=b);f&&this.$$parse(f);return!!f};this.$$compose=function(){var b=Rb(this.$$search),e=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=cc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+d+this.$$url}}function Fb(a){return function(){return this[a]}}function ld(a,
b){return function(d){if(y(d))return this[a];this[a]=b(d);this.$$compose();return this}}function qf(){var a="",b={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(b){return x(b)?(a=b,this):a};this.html5Mode=function(a){return Da(a)?(b.enabled=a,this):G(a)?(Da(a.enabled)&&(b.enabled=a.enabled),Da(a.requireBase)&&(b.requireBase=a.requireBase),Da(a.rewriteLinks)&&(b.rewriteLinks=a.rewriteLinks),this):b};this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window",function(d,
c,e,f,g){function h(a,b,d){var e=l.url(),f=l.$$state;try{c.url(a,b,d),l.$$state=c.state()}catch(g){throw l.url(e),l.$$state=f,g;}}function k(a,b){d.$broadcast("$locationChangeSuccess",l.absUrl(),a,l.$$state,b)}var l,n;n=c.baseHref();var m=c.url(),r;if(b.enabled){if(!n&&b.requireBase)throw Eb("nobase");r=m.substring(0,m.indexOf("/",m.indexOf("//")+2))+(n||"/");n=e.history?dc:kd}else r=Ia(m),n=ec;var N=r.substr(0,Ia(r).lastIndexOf("/")+1);l=new n(r,N,"#"+a);l.$$parseLinkUrl(m,m);l.$$state=c.state();
var q=/^\s*(javascript|mailto):/i;f.on("click",function(a){if(b.rewriteLinks&&!a.ctrlKey&&!a.metaKey&&!a.shiftKey&&2!=a.which&&2!=a.button){for(var e=B(a.target);"a"!==va(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;var h=e.prop("href"),k=e.attr("href")||e.attr("xlink:href");G(h)&&"[object SVGAnimatedString]"===h.toString()&&(h=ra(h.animVal).href);q.test(h)||!h||e.attr("target")||a.isDefaultPrevented()||!l.$$parseLinkUrl(h,k)||(a.preventDefault(),l.absUrl()!=c.url()&&(d.$apply(),g.angular["ff-684208-preventDefault"]=
!0))}});hb(l.absUrl())!=hb(m)&&c.url(l.absUrl(),!0);var w=!0;c.onUrlChange(function(a,b){y(na(N,a))?g.location.href=a:(d.$evalAsync(function(){var c=l.absUrl(),e=l.$$state,f;a=hb(a);l.$$parse(a);l.$$state=b;f=d.$broadcast("$locationChangeStart",a,c,b,e).defaultPrevented;l.absUrl()===a&&(f?(l.$$parse(c),l.$$state=e,h(c,!1,e)):(w=!1,k(c,e)))}),d.$$phase||d.$digest())});d.$watch(function(){var a=hb(c.url()),b=hb(l.absUrl()),f=c.state(),g=l.$$replace,m=a!==b||l.$$html5&&e.history&&f!==l.$$state;if(w||
m)w=!1,d.$evalAsync(function(){var b=l.absUrl(),c=d.$broadcast("$locationChangeStart",b,a,l.$$state,f).defaultPrevented;l.absUrl()===b&&(c?(l.$$parse(a),l.$$state=f):(m&&h(b,g,f===l.$$state?null:l.$$state),k(a,f)))});l.$$replace=!1});return l}]}function rf(){var a=!0,b=this;this.debugEnabled=function(b){return x(b)?(a=b,this):a};this.$get=["$window",function(d){function c(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&
(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=d.console||{},e=b[a]||b.log||C;a=!1;try{a=!!e.apply}catch(k){}return a?function(){var a=[];q(arguments,function(b){a.push(c(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){a&&c.apply(b,arguments)}}()}}]}function Ta(a,b){if("__defineGetter__"===a||"__defineSetter__"===a||"__lookupGetter__"===a||"__lookupSetter__"===
a||"__proto__"===a)throw ca("isecfld",b);return a}function fg(a){return a+""}function sa(a,b){if(a){if(a.constructor===a)throw ca("isecfn",b);if(a.window===a)throw ca("isecwindow",b);if(a.children&&(a.nodeName||a.prop&&a.attr&&a.find))throw ca("isecdom",b);if(a===Object)throw ca("isecobj",b);}return a}function md(a,b){if(a){if(a.constructor===a)throw ca("isecfn",b);if(a===gg||a===hg||a===ig)throw ca("isecff",b);}}function Gb(a,b){if(a&&(a===(0).constructor||a===(!1).constructor||a==="".constructor||
a==={}.constructor||a===[].constructor||a===Function.constructor))throw ca("isecaf",b);}function jg(a,b){return"undefined"!==typeof a?a:b}function nd(a,b){return"undefined"===typeof a?b:"undefined"===typeof b?a:a+b}function aa(a,b){var d,c;switch(a.type){case s.Program:d=!0;q(a.body,function(a){aa(a.expression,b);d=d&&a.expression.constant});a.constant=d;break;case s.Literal:a.constant=!0;a.toWatch=[];break;case s.UnaryExpression:aa(a.argument,b);a.constant=a.argument.constant;a.toWatch=a.argument.toWatch;
break;case s.BinaryExpression:aa(a.left,b);aa(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.left.toWatch.concat(a.right.toWatch);break;case s.LogicalExpression:aa(a.left,b);aa(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.constant?[]:[a];break;case s.ConditionalExpression:aa(a.test,b);aa(a.alternate,b);aa(a.consequent,b);a.constant=a.test.constant&&a.alternate.constant&&a.consequent.constant;a.toWatch=a.constant?[]:[a];break;case s.Identifier:a.constant=
!1;a.toWatch=[a];break;case s.MemberExpression:aa(a.object,b);a.computed&&aa(a.property,b);a.constant=a.object.constant&&(!a.computed||a.property.constant);a.toWatch=[a];break;case s.CallExpression:d=a.filter?!b(a.callee.name).$stateful:!1;c=[];q(a.arguments,function(a){aa(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=a.filter&&!b(a.callee.name).$stateful?c:[a];break;case s.AssignmentExpression:aa(a.left,b);aa(a.right,b);a.constant=a.left.constant&&a.right.constant;
a.toWatch=[a];break;case s.ArrayExpression:d=!0;c=[];q(a.elements,function(a){aa(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=c;break;case s.ObjectExpression:d=!0;c=[];q(a.properties,function(a){aa(a.value,b);d=d&&a.value.constant;a.value.constant||c.push.apply(c,a.value.toWatch)});a.constant=d;a.toWatch=c;break;case s.ThisExpression:a.constant=!1;a.toWatch=[];break;case s.LocalsExpression:a.constant=!1,a.toWatch=[]}}function od(a){if(1==a.length){a=a[0].expression;
var b=a.toWatch;return 1!==b.length?b:b[0]!==a?b:void 0}}function pd(a){return a.type===s.Identifier||a.type===s.MemberExpression}function qd(a){if(1===a.body.length&&pd(a.body[0].expression))return{type:s.AssignmentExpression,left:a.body[0].expression,right:{type:s.NGValueParameter},operator:"="}}function rd(a){return 0===a.body.length||1===a.body.length&&(a.body[0].expression.type===s.Literal||a.body[0].expression.type===s.ArrayExpression||a.body[0].expression.type===s.ObjectExpression)}function sd(a,
b){this.astBuilder=a;this.$filter=b}function td(a,b){this.astBuilder=a;this.$filter=b}function Hb(a){return"constructor"==a}function fc(a){return E(a.valueOf)?a.valueOf():kg.call(a)}function sf(){var a=T(),b=T(),d={"true":!0,"false":!1,"null":null,undefined:void 0},c,e;this.addLiteral=function(a,b){d[a]=b};this.setIdentifierFns=function(a,b){c=a;e=b;return this};this.$get=["$filter",function(f){function g(c,d,e){var g,k,D;e=e||H;switch(typeof c){case "string":D=c=c.trim();var q=e?b:a;g=q[D];if(!g){":"===
c.charAt(0)&&":"===c.charAt(1)&&(k=!0,c=c.substring(2));g=e?p:w;var S=new gc(g);g=(new hc(S,f,g)).parse(c);g.constant?g.$$watchDelegate=r:k?g.$$watchDelegate=g.literal?m:n:g.inputs&&(g.$$watchDelegate=l);e&&(g=h(g));q[D]=g}return N(g,d);case "function":return N(c,d);default:return N(C,d)}}function h(a){function b(c,d,e,f){var g=H;H=!0;try{return a(c,d,e,f)}finally{H=g}}if(!a)return a;b.$$watchDelegate=a.$$watchDelegate;b.assign=h(a.assign);b.constant=a.constant;b.literal=a.literal;for(var c=0;a.inputs&&
c<a.inputs.length;++c)a.inputs[c]=h(a.inputs[c]);b.inputs=a.inputs;return b}function k(a,b){return null==a||null==b?a===b:"object"===typeof a&&(a=fc(a),"object"===typeof a)?!1:a===b||a!==a&&b!==b}function l(a,b,c,d,e){var f=d.inputs,g;if(1===f.length){var h=k,f=f[0];return a.$watch(function(a){var b=f(a);k(b,h)||(g=d(a,void 0,void 0,[b]),h=b&&fc(b));return g},b,c,e)}for(var l=[],m=[],n=0,r=f.length;n<r;n++)l[n]=k,m[n]=null;return a.$watch(function(a){for(var b=!1,c=0,e=f.length;c<e;c++){var h=f[c](a);
if(b||(b=!k(h,l[c])))m[c]=h,l[c]=h&&fc(h)}b&&(g=d(a,void 0,void 0,m));return g},b,c,e)}function n(a,b,c,d){var e,f;return e=a.$watch(function(a){return d(a)},function(a,c,d){f=a;E(b)&&b.apply(this,arguments);x(a)&&d.$$postDigest(function(){x(f)&&e()})},c)}function m(a,b,c,d){function e(a){var b=!0;q(a,function(a){x(a)||(b=!1)});return b}var f,g;return f=a.$watch(function(a){return d(a)},function(a,c,d){g=a;E(b)&&b.call(this,a,c,d);e(a)&&d.$$postDigest(function(){e(g)&&f()})},c)}function r(a,b,c,d){var e;
return e=a.$watch(function(a){e();return d(a)},b,c)}function N(a,b){if(!b)return a;var c=a.$$watchDelegate,d=!1,c=c!==m&&c!==n?function(c,e,f,g){f=d&&g?g[0]:a(c,e,f,g);return b(f,c,e)}:function(c,d,e,f){e=a(c,d,e,f);c=b(e,c,d);return x(e)?c:e};a.$$watchDelegate&&a.$$watchDelegate!==l?c.$$watchDelegate=a.$$watchDelegate:b.$stateful||(c.$$watchDelegate=l,d=!a.inputs,c.inputs=a.inputs?a.inputs:[a]);return c}var M=Ea().noUnsafeEval,w={csp:M,expensiveChecks:!1,literals:qa(d),isIdentifierStart:E(c)&&c,
isIdentifierContinue:E(e)&&e},p={csp:M,expensiveChecks:!0,literals:qa(d),isIdentifierStart:E(c)&&c,isIdentifierContinue:E(e)&&e},H=!1;g.$$runningExpensiveChecks=function(){return H};return g}]}function uf(){this.$get=["$rootScope","$exceptionHandler",function(a,b){return ud(function(b){a.$evalAsync(b)},b)}]}function vf(){this.$get=["$browser","$exceptionHandler",function(a,b){return ud(function(b){a.defer(b)},b)}]}function ud(a,b){function d(){this.$$state={status:0}}function c(a,b){return function(c){b.call(a,
c)}}function e(c){!c.processScheduled&&c.pending&&(c.processScheduled=!0,a(function(){var a,d,e;e=c.pending;c.processScheduled=!1;c.pending=void 0;for(var f=0,g=e.length;f<g;++f){d=e[f][0];a=e[f][c.status];try{E(a)?d.resolve(a(c.value)):1===c.status?d.resolve(c.value):d.reject(c.value)}catch(h){d.reject(h),b(h)}}}))}function f(){this.promise=new d}var g=O("$q",TypeError);R(d.prototype,{then:function(a,b,c){if(y(a)&&y(b)&&y(c))return this;var d=new f;this.$$state.pending=this.$$state.pending||[];this.$$state.pending.push([d,
a,b,c]);0<this.$$state.status&&e(this.$$state);return d.promise},"catch":function(a){return this.then(null,a)},"finally":function(a,b){return this.then(function(b){return k(b,!0,a)},function(b){return k(b,!1,a)},b)}});R(f.prototype,{resolve:function(a){this.promise.$$state.status||(a===this.promise?this.$$reject(g("qcycle",a)):this.$$resolve(a))},$$resolve:function(a){function d(a){k||(k=!0,h.$$resolve(a))}function f(a){k||(k=!0,h.$$reject(a))}var g,h=this,k=!1;try{if(G(a)||E(a))g=a&&a.then;E(g)?
(this.promise.$$state.status=-1,g.call(a,d,f,c(this,this.notify))):(this.promise.$$state.value=a,this.promise.$$state.status=1,e(this.promise.$$state))}catch(l){f(l),b(l)}},reject:function(a){this.promise.$$state.status||this.$$reject(a)},$$reject:function(a){this.promise.$$state.value=a;this.promise.$$state.status=2;e(this.promise.$$state)},notify:function(c){var d=this.promise.$$state.pending;0>=this.promise.$$state.status&&d&&d.length&&a(function(){for(var a,e,f=0,g=d.length;f<g;f++){e=d[f][0];
a=d[f][3];try{e.notify(E(a)?a(c):c)}catch(h){b(h)}}})}});var h=function(a,b){var c=new f;b?c.resolve(a):c.reject(a);return c.promise},k=function(a,b,c){var d=null;try{E(c)&&(d=c())}catch(e){return h(e,!1)}return d&&E(d.then)?d.then(function(){return h(a,b)},function(a){return h(a,!1)}):h(a,b)},l=function(a,b,c,d){var e=new f;e.resolve(a);return e.promise.then(b,c,d)},n=function(a){if(!E(a))throw g("norslvr",a);var b=new f;a(function(a){b.resolve(a)},function(a){b.reject(a)});return b.promise};n.prototype=
d.prototype;n.defer=function(){var a=new f;a.resolve=c(a,a.resolve);a.reject=c(a,a.reject);a.notify=c(a,a.notify);return a};n.reject=function(a){var b=new f;b.reject(a);return b.promise};n.when=l;n.resolve=l;n.all=function(a){var b=new f,c=0,d=K(a)?[]:{};q(a,function(a,e){c++;l(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise};return n}function Ef(){this.$get=["$window","$timeout",function(a,
b){var d=a.requestAnimationFrame||a.webkitRequestAnimationFrame,c=a.cancelAnimationFrame||a.webkitCancelAnimationFrame||a.webkitCancelRequestAnimationFrame,e=!!d,f=e?function(a){var b=d(a);return function(){c(b)}}:function(a){var c=b(a,16.66,!1);return function(){b.cancel(c)}};f.supported=e;return f}]}function tf(){function a(a){function b(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$id=++nb;this.$$ChildScope=
null}b.prototype=a;return b}var b=10,d=O("$rootScope"),c=null,e=null;this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=["$exceptionHandler","$parse","$browser",function(f,g,h){function k(a){a.currentScope.$$destroyed=!0}function l(a){9===Ca&&(a.$$childHead&&l(a.$$childHead),a.$$nextSibling&&l(a.$$nextSibling));a.$parent=a.$$nextSibling=a.$$prevSibling=a.$$childHead=a.$$childTail=a.$root=a.$$watchers=null}function n(){this.$id=++nb;this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=
this.$$prevSibling=this.$$childHead=this.$$childTail=null;this.$root=this;this.$$destroyed=!1;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$$isolateBindings=null}function m(a){if(H.$$phase)throw d("inprog",H.$$phase);H.$$phase=a}function r(a,b){do a.$$watchersCount+=b;while(a=a.$parent)}function N(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function s(){}function w(){for(;u.length;)try{u.shift()()}catch(a){f(a)}e=
null}function p(){null===e&&(e=h.defer(function(){H.$apply(w)}))}n.prototype={constructor:n,$new:function(b,c){var d;c=c||this;b?(d=new n,d.$root=this.$root):(this.$$ChildScope||(this.$$ChildScope=a(this)),d=new this.$$ChildScope);d.$parent=c;d.$$prevSibling=c.$$childTail;c.$$childHead?(c.$$childTail.$$nextSibling=d,c.$$childTail=d):c.$$childHead=c.$$childTail=d;(b||c!=this)&&d.$on("$destroy",k);return d},$watch:function(a,b,d,e){var f=g(a);if(f.$$watchDelegate)return f.$$watchDelegate(this,b,d,f,
a);var h=this,k=h.$$watchers,l={fn:b,last:s,get:f,exp:e||a,eq:!!d};c=null;E(b)||(l.fn=C);k||(k=h.$$watchers=[]);k.unshift(l);r(this,1);return function(){0<=Za(k,l)&&r(h,-1);c=null}},$watchGroup:function(a,b){function c(){h=!1;k?(k=!1,b(e,e,g)):b(e,d,g)}var d=Array(a.length),e=Array(a.length),f=[],g=this,h=!1,k=!0;if(!a.length){var l=!0;g.$evalAsync(function(){l&&b(e,e,g)});return function(){l=!1}}if(1===a.length)return this.$watch(a[0],function(a,c,f){e[0]=a;d[0]=c;b(e,a===c?e:d,f)});q(a,function(a,
b){var k=g.$watch(a,function(a,f){e[b]=a;d[b]=f;h||(h=!0,g.$evalAsync(c))});f.push(k)});return function(){for(;f.length;)f.shift()()}},$watchCollection:function(a,b){function c(a){e=a;var b,d,g,h;if(!y(e)){if(G(e))if(ya(e))for(f!==m&&(f=m,t=f.length=0,l++),a=e.length,t!==a&&(l++,f.length=t=a),b=0;b<a;b++)h=f[b],g=e[b],d=h!==h&&g!==g,d||h===g||(l++,f[b]=g);else{f!==r&&(f=r={},t=0,l++);a=0;for(b in e)ua.call(e,b)&&(a++,g=e[b],h=f[b],b in f?(d=h!==h&&g!==g,d||h===g||(l++,f[b]=g)):(t++,f[b]=g,l++));if(t>
a)for(b in l++,f)ua.call(e,b)||(t--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$stateful=!0;var d=this,e,f,h,k=1<b.length,l=0,n=g(a,c),m=[],r={},p=!0,t=0;return this.$watch(n,function(){p?(p=!1,b(e,e,d)):b(e,h,d);if(k)if(G(e))if(ya(e)){h=Array(e.length);for(var a=0;a<e.length;a++)h[a]=e[a]}else for(a in h={},e)ua.call(e,a)&&(h[a]=e[a]);else h=e})},$digest:function(){var a,g,k,l,n,r,p,q,N=b,u,x=[],y,v;m("$digest");h.$$checkUrlChange();this===H&&null!==e&&(h.defer.cancel(e),w());c=null;do{q=!1;
for(u=this;t.length;){try{v=t.shift(),v.scope.$eval(v.expression,v.locals)}catch(C){f(C)}c=null}a:do{if(r=u.$$watchers)for(p=r.length;p--;)try{if(a=r[p])if(n=a.get,(g=n(u))!==(k=a.last)&&!(a.eq?pa(g,k):"number"===typeof g&&"number"===typeof k&&isNaN(g)&&isNaN(k)))q=!0,c=a,a.last=a.eq?qa(g,null):g,l=a.fn,l(g,k===s?g:k,u),5>N&&(y=4-N,x[y]||(x[y]=[]),x[y].push({msg:E(a.exp)?"fn: "+(a.exp.name||a.exp.toString()):a.exp,newVal:g,oldVal:k}));else if(a===c){q=!1;break a}}catch(F){f(F)}if(!(r=u.$$watchersCount&&
u.$$childHead||u!==this&&u.$$nextSibling))for(;u!==this&&!(r=u.$$nextSibling);)u=u.$parent}while(u=r);if((q||t.length)&&!N--)throw H.$$phase=null,d("infdig",b,x);}while(q||t.length);for(H.$$phase=null;z.length;)try{z.shift()()}catch(B){f(B)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this===H&&h.$$applicationDestroyed();r(this,-this.$$watchersCount);for(var b in this.$$listenerCount)N(this,this.$$listenerCount[b],b);a&&a.$$childHead==
this&&(a.$$childHead=this.$$nextSibling);a&&a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$destroy=this.$digest=this.$apply=this.$evalAsync=this.$applyAsync=C;this.$on=this.$watch=this.$watchGroup=function(){return C};this.$$listeners={};this.$$nextSibling=null;l(this)}},$eval:function(a,b){return g(a)(this,b)},$evalAsync:function(a,b){H.$$phase||
t.length||h.defer(function(){t.length&&H.$digest()});t.push({scope:this,expression:g(a),locals:b})},$$postDigest:function(a){z.push(a)},$apply:function(a){try{m("$apply");try{return this.$eval(a)}finally{H.$$phase=null}}catch(b){f(b)}finally{try{H.$digest()}catch(c){throw f(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&u.push(b);a=g(a);p()},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=
0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){var d=c.indexOf(b);-1!==d&&(c[d]=null,N(e,1,a))}},$emit:function(a,b){var c=[],d,e=this,g=!1,h={name:a,targetScope:e,stopPropagation:function(){g=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=$a([h],arguments,1),l,n;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(n=d.length;l<n;l++)if(d[l])try{d[l].apply(null,k)}catch(m){f(m)}else d.splice(l,1),l--,n--;if(g)return h.currentScope=null,h;e=e.$parent}while(e);
h.currentScope=null;return h},$broadcast:function(a,b){var c=this,d=this,e={name:a,targetScope:this,preventDefault:function(){e.defaultPrevented=!0},defaultPrevented:!1};if(!this.$$listenerCount[a])return e;for(var g=$a([e],arguments,1),h,k;c=d;){e.currentScope=c;d=c.$$listeners[a]||[];h=0;for(k=d.length;h<k;h++)if(d[h])try{d[h].apply(null,g)}catch(l){f(l)}else d.splice(h,1),h--,k--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=c.$$nextSibling);)c=c.$parent}e.currentScope=
null;return e}};var H=new n,t=H.$$asyncQueue=[],z=H.$$postDigestQueue=[],u=H.$$applyAsyncQueue=[];return H}]}function me(){var a=/^\s*(https?|ftp|mailto|tel|file):/,b=/^\s*((https?|ftp|file|blob):|data:image\/)/;this.aHrefSanitizationWhitelist=function(b){return x(b)?(a=b,this):a};this.imgSrcSanitizationWhitelist=function(a){return x(a)?(b=a,this):b};this.$get=function(){return function(d,c){var e=c?b:a,f;f=ra(d).href;return""===f||f.match(e)?d:"unsafe:"+f}}}function lg(a){if("self"===a)return a;
if(F(a)){if(-1<a.indexOf("***"))throw ta("iwcard",a);a=vd(a).replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return new RegExp("^"+a+"$")}if(Wa(a))return new RegExp("^"+a.source+"$");throw ta("imatcher");}function wd(a){var b=[];x(a)&&q(a,function(a){b.push(lg(a))});return b}function xf(){this.SCE_CONTEXTS=oa;var a=["self"],b=[];this.resourceUrlWhitelist=function(b){arguments.length&&(a=wd(b));return a};this.resourceUrlBlacklist=function(a){arguments.length&&(b=wd(a));return b};this.$get=["$injector",
function(d){function c(a,b){return"self"===a?hd(b):!!a.exec(b.href)}function e(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var f=function(a){throw ta("unsafe");};d.has("$sanitize")&&(f=d.get("$sanitize"));var g=e(),h={};h[oa.HTML]=e(g);h[oa.CSS]=e(g);h[oa.URL]=e(g);h[oa.JS]=e(g);h[oa.RESOURCE_URL]=
e(h[oa.URL]);return{trustAs:function(a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw ta("icontext",a,b);if(null===b||y(b)||""===b)return b;if("string"!==typeof b)throw ta("itype",a);return new c(b)},getTrusted:function(d,e){if(null===e||y(e)||""===e)return e;var g=h.hasOwnProperty(d)?h[d]:null;if(g&&e instanceof g)return e.$$unwrapTrustedValue();if(d===oa.RESOURCE_URL){var g=ra(e.toString()),m,r,q=!1;m=0;for(r=a.length;m<r;m++)if(c(a[m],g)){q=!0;break}if(q)for(m=0,r=b.length;m<r;m++)if(c(b[m],
g)){q=!1;break}if(q)return e;throw ta("insecurl",e.toString());}if(d===oa.HTML)return f(e);throw ta("unsafe");},valueOf:function(a){return a instanceof g?a.$$unwrapTrustedValue():a}}}]}function wf(){var a=!0;this.enabled=function(b){arguments.length&&(a=!!b);return a};this.$get=["$parse","$sceDelegate",function(b,d){if(a&&8>Ca)throw ta("iequirks");var c=ha(oa);c.isEnabled=function(){return a};c.trustAs=d.trustAs;c.getTrusted=d.getTrusted;c.valueOf=d.valueOf;a||(c.trustAs=c.getTrusted=function(a,b){return b},
c.valueOf=Xa);c.parseAs=function(a,d){var e=b(d);return e.literal&&e.constant?e:b(d,function(b){return c.getTrusted(a,b)})};var e=c.parseAs,f=c.getTrusted,g=c.trustAs;q(oa,function(a,b){var d=P(b);c[cb("parse_as_"+d)]=function(b){return e(a,b)};c[cb("get_trusted_"+d)]=function(b){return f(a,b)};c[cb("trust_as_"+d)]=function(b){return g(a,b)}});return c}]}function yf(){this.$get=["$window","$document",function(a,b){var d={},c=!(a.chrome&&a.chrome.app&&a.chrome.app.runtime)&&a.history&&a.history.pushState,
e=X((/android (\d+)/.exec(P((a.navigator||{}).userAgent))||[])[1]),f=/Boxee/i.test((a.navigator||{}).userAgent),g=b[0]||{},h,k=/^(Moz|webkit|ms)(?=[A-Z])/,l=g.body&&g.body.style,n=!1,m=!1;if(l){for(var r in l)if(n=k.exec(r)){h=n[0];h=h.substr(0,1).toUpperCase()+h.substr(1);break}h||(h="WebkitOpacity"in l&&"webkit");n=!!("transition"in l||h+"Transition"in l);m=!!("animation"in l||h+"Animation"in l);!e||n&&m||(n=F(l.webkitTransition),m=F(l.webkitAnimation))}return{history:!(!c||4>e||f),hasEvent:function(a){if("input"===
a&&11>=Ca)return!1;if(y(d[a])){var b=g.createElement("div");d[a]="on"+a in b}return d[a]},csp:Ea(),vendorPrefix:h,transitions:n,animations:m,android:e}}]}function Af(){var a;this.httpOptions=function(b){return b?(a=b,this):a};this.$get=["$templateCache","$http","$q","$sce",function(b,d,c,e){function f(g,h){f.totalPendingRequests++;F(g)&&b.get(g)||(g=e.getTrustedResourceUrl(g));var k=d.defaults&&d.defaults.transformResponse;K(k)?k=k.filter(function(a){return a!==ac}):k===ac&&(k=null);return d.get(g,
R({cache:b,transformResponse:k},a))["finally"](function(){f.totalPendingRequests--}).then(function(a){b.put(g,a.data);return a.data},function(a){if(!h)throw mg("tpload",g,a.status,a.statusText);return c.reject(a)})}f.totalPendingRequests=0;return f}]}function Bf(){this.$get=["$rootScope","$browser","$location",function(a,b,d){return{findBindings:function(a,b,d){a=a.getElementsByClassName("ng-binding");var g=[];q(a,function(a){var c=ea.element(a).data("$binding");c&&q(c,function(c){d?(new RegExp("(^|\\s)"+
vd(b)+"(\\s|\\||$)")).test(c)&&g.push(a):-1!=c.indexOf(b)&&g.push(a)})});return g},findModels:function(a,b,d){for(var g=["ng-","data-ng-","ng\\:"],h=0;h<g.length;++h){var k=a.querySelectorAll("["+g[h]+"model"+(d?"=":"*=")+'"'+b+'"]');if(k.length)return k}},getLocation:function(){return d.url()},setLocation:function(b){b!==d.url()&&(d.url(b),a.$digest())},whenStable:function(a){b.notifyWhenNoOutstandingRequests(a)}}}]}function Cf(){this.$get=["$rootScope","$browser","$q","$$q","$exceptionHandler",
function(a,b,d,c,e){function f(f,k,l){E(f)||(l=k,k=f,f=C);var n=za.call(arguments,3),m=x(l)&&!l,r=(m?c:d).defer(),q=r.promise,s;s=b.defer(function(){try{r.resolve(f.apply(null,n))}catch(b){r.reject(b),e(b)}finally{delete g[q.$$timeoutId]}m||a.$apply()},k);q.$$timeoutId=s;g[s]=r;return q}var g={};f.cancel=function(a){return a&&a.$$timeoutId in g?(g[a.$$timeoutId].reject("canceled"),delete g[a.$$timeoutId],b.defer.cancel(a.$$timeoutId)):!1};return f}]}function ra(a){Ca&&(Y.setAttribute("href",a),a=
Y.href);Y.setAttribute("href",a);return{href:Y.href,protocol:Y.protocol?Y.protocol.replace(/:$/,""):"",host:Y.host,search:Y.search?Y.search.replace(/^\?/,""):"",hash:Y.hash?Y.hash.replace(/^#/,""):"",hostname:Y.hostname,port:Y.port,pathname:"/"===Y.pathname.charAt(0)?Y.pathname:"/"+Y.pathname}}function hd(a){a=F(a)?ra(a):a;return a.protocol===xd.protocol&&a.host===xd.host}function Df(){this.$get=da(v)}function yd(a){function b(a){try{return decodeURIComponent(a)}catch(b){return a}}var d=a[0]||{},
c={},e="";return function(){var a,g,h,k,l;a=d.cookie||"";if(a!==e)for(e=a,a=e.split("; "),c={},h=0;h<a.length;h++)g=a[h],k=g.indexOf("="),0<k&&(l=b(g.substring(0,k)),y(c[l])&&(c[l]=b(g.substring(k+1))));return c}}function Hf(){this.$get=yd}function Jc(a){function b(d,c){if(G(d)){var e={};q(d,function(a,c){e[c]=b(c,a)});return e}return a.factory(d+"Filter",c)}this.register=b;this.$get=["$injector",function(a){return function(b){return a.get(b+"Filter")}}];b("currency",zd);b("date",Ad);b("filter",ng);
b("json",og);b("limitTo",pg);b("lowercase",qg);b("number",Bd);b("orderBy",Cd);b("uppercase",rg)}function ng(){return function(a,b,d){if(!ya(a)){if(null==a)return a;throw O("filter")("notarray",a);}var c;switch(ic(b)){case "function":break;case "boolean":case "null":case "number":case "string":c=!0;case "object":b=sg(b,d,c);break;default:return a}return Array.prototype.filter.call(a,b)}}function sg(a,b,d){var c=G(a)&&"$"in a;!0===b?b=pa:E(b)||(b=function(a,b){if(y(a))return!1;if(null===a||null===b)return a===
b;if(G(b)||G(a)&&!rc(a))return!1;a=P(""+a);b=P(""+b);return-1!==a.indexOf(b)});return function(e){return c&&!G(e)?Ka(e,a.$,b,!1):Ka(e,a,b,d)}}function Ka(a,b,d,c,e){var f=ic(a),g=ic(b);if("string"===g&&"!"===b.charAt(0))return!Ka(a,b.substring(1),d,c);if(K(a))return a.some(function(a){return Ka(a,b,d,c)});switch(f){case "object":var h;if(c){for(h in a)if("$"!==h.charAt(0)&&Ka(a[h],b,d,!0))return!0;return e?!1:Ka(a,b,d,!1)}if("object"===g){for(h in b)if(e=b[h],!E(e)&&!y(e)&&(f="$"===h,!Ka(f?a:a[h],
e,d,f,f)))return!1;return!0}return d(a,b);case "function":return!1;default:return d(a,b)}}function ic(a){return null===a?"null":typeof a}function zd(a){var b=a.NUMBER_FORMATS;return function(a,c,e){y(c)&&(c=b.CURRENCY_SYM);y(e)&&(e=b.PATTERNS[1].maxFrac);return null==a?a:Dd(a,b.PATTERNS[1],b.GROUP_SEP,b.DECIMAL_SEP,e).replace(/\u00A4/g,c)}}function Bd(a){var b=a.NUMBER_FORMATS;return function(a,c){return null==a?a:Dd(a,b.PATTERNS[0],b.GROUP_SEP,b.DECIMAL_SEP,c)}}function tg(a){var b=0,d,c,e,f,g;-1<
(c=a.indexOf(Ed))&&(a=a.replace(Ed,""));0<(e=a.search(/e/i))?(0>c&&(c=e),c+=+a.slice(e+1),a=a.substring(0,e)):0>c&&(c=a.length);for(e=0;a.charAt(e)==jc;e++);if(e==(g=a.length))d=[0],c=1;else{for(g--;a.charAt(g)==jc;)g--;c-=e;d=[];for(f=0;e<=g;e++,f++)d[f]=+a.charAt(e)}c>Fd&&(d=d.splice(0,Fd-1),b=c-1,c=1);return{d:d,e:b,i:c}}function ug(a,b,d,c){var e=a.d,f=e.length-a.i;b=y(b)?Math.min(Math.max(d,f),c):+b;d=b+a.i;c=e[d];if(0<d){e.splice(Math.max(a.i,d));for(var g=d;g<e.length;g++)e[g]=0}else for(f=
Math.max(0,f),a.i=1,e.length=Math.max(1,d=b+1),e[0]=0,g=1;g<d;g++)e[g]=0;if(5<=c)if(0>d-1){for(c=0;c>d;c--)e.unshift(0),a.i++;e.unshift(1);a.i++}else e[d-1]++;for(;f<Math.max(0,b);f++)e.push(0);if(b=e.reduceRight(function(a,b,c,d){b+=a;d[c]=b%10;return Math.floor(b/10)},0))e.unshift(b),a.i++}function Dd(a,b,d,c,e){if(!F(a)&&!Q(a)||isNaN(a))return"";var f=!isFinite(a),g=!1,h=Math.abs(a)+"",k="";if(f)k="\u221e";else{g=tg(h);ug(g,e,b.minFrac,b.maxFrac);k=g.d;h=g.i;e=g.e;f=[];for(g=k.reduce(function(a,
b){return a&&!b},!0);0>h;)k.unshift(0),h++;0<h?f=k.splice(h):(f=k,k=[0]);h=[];for(k.length>=b.lgSize&&h.unshift(k.splice(-b.lgSize).join(""));k.length>b.gSize;)h.unshift(k.splice(-b.gSize).join(""));k.length&&h.unshift(k.join(""));k=h.join(d);f.length&&(k+=c+f.join(""));e&&(k+="e+"+e)}return 0>a&&!g?b.negPre+k+b.negSuf:b.posPre+k+b.posSuf}function Ib(a,b,d,c){var e="";if(0>a||c&&0>=a)c?a=-a+1:(a=-a,e="-");for(a=""+a;a.length<b;)a=jc+a;d&&(a=a.substr(a.length-b));return e+a}function W(a,b,d,c,e){d=
d||0;return function(f){f=f["get"+a]();if(0<d||f>-d)f+=d;0===f&&-12==d&&(f=12);return Ib(f,b,c,e)}}function ib(a,b,d){return function(c,e){var f=c["get"+a](),g=sb((d?"STANDALONE":"")+(b?"SHORT":"")+a);return e[g][f]}}function Gd(a){var b=(new Date(a,0,1)).getDay();return new Date(a,0,(4>=b?5:12)-b)}function Hd(a){return function(b){var d=Gd(b.getFullYear());b=+new Date(b.getFullYear(),b.getMonth(),b.getDate()+(4-b.getDay()))-+d;b=1+Math.round(b/6048E5);return Ib(b,a)}}function kc(a,b){return 0>=a.getFullYear()?
b.ERAS[0]:b.ERAS[1]}function Ad(a){function b(a){var b;if(b=a.match(d)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,k=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=X(b[9]+b[10]),g=X(b[9]+b[11]));h.call(a,X(b[1]),X(b[2])-1,X(b[3]));f=X(b[4]||0)-f;g=X(b[5]||0)-g;h=X(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));k.call(a,f,g,h,b)}return a}var d=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,d,f){var g="",h=
[],k,l;d=d||"mediumDate";d=a.DATETIME_FORMATS[d]||d;F(c)&&(c=vg.test(c)?X(c):b(c));Q(c)&&(c=new Date(c));if(!fa(c)||!isFinite(c.getTime()))return c;for(;d;)(l=wg.exec(d))?(h=$a(h,l,1),d=h.pop()):(h.push(d),d=null);var n=c.getTimezoneOffset();f&&(n=vc(f,n),c=Qb(c,f,!0));q(h,function(b){k=xg[b];g+=k?k(c,a.DATETIME_FORMATS,n):"''"===b?"'":b.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function og(){return function(a,b){y(b)&&(b=2);return ab(a,b)}}function pg(){return function(a,b,d){b=Infinity===
Math.abs(Number(b))?Number(b):X(b);if(isNaN(b))return a;Q(a)&&(a=a.toString());if(!K(a)&&!F(a))return a;d=!d||isNaN(d)?0:X(d);d=0>d?Math.max(0,a.length+d):d;return 0<=b?a.slice(d,d+b):0===d?a.slice(b,a.length):a.slice(Math.max(0,d+b),d)}}function Cd(a){function b(b,d){d=d?-1:1;return b.map(function(b){var c=1,h=Xa;if(E(b))h=b;else if(F(b)){if("+"==b.charAt(0)||"-"==b.charAt(0))c="-"==b.charAt(0)?-1:1,b=b.substring(1);if(""!==b&&(h=a(b),h.constant))var k=h(),h=function(a){return a[k]}}return{get:h,
descending:c*d}})}function d(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}return function(a,e,f){if(null==a)return a;if(!ya(a))throw O("orderBy")("notarray",a);K(e)||(e=[e]);0===e.length&&(e=["+"]);var g=b(e,f);g.push({get:function(){return{}},descending:f?-1:1});a=Array.prototype.map.call(a,function(a,b){return{value:a,predicateValues:g.map(function(c){var e=c.get(a);c=typeof e;if(null===e)c="string",e="null";else if("string"===c)e=e.toLowerCase();else if("object"===
c)a:{if("function"===typeof e.valueOf&&(e=e.valueOf(),d(e)))break a;if(rc(e)&&(e=e.toString(),d(e)))break a;e=b}return{value:e,type:c}})}});a.sort(function(a,b){for(var c=0,d=0,e=g.length;d<e;++d){var c=a.predicateValues[d],f=b.predicateValues[d],q=0;c.type===f.type?c.value!==f.value&&(q=c.value<f.value?-1:1):q=c.type<f.type?-1:1;if(c=q*g[d].descending)break}return c});return a=a.map(function(a){return a.value})}}function La(a){E(a)&&(a={link:a});a.restrict=a.restrict||"AC";return da(a)}function Id(a,
b,d,c,e){var f=this,g=[];f.$error={};f.$$success={};f.$pending=void 0;f.$name=e(b.name||b.ngForm||"")(d);f.$dirty=!1;f.$pristine=!0;f.$valid=!0;f.$invalid=!1;f.$submitted=!1;f.$$parentForm=Jb;f.$rollbackViewValue=function(){q(g,function(a){a.$rollbackViewValue()})};f.$commitViewValue=function(){q(g,function(a){a.$commitViewValue()})};f.$addControl=function(a){Qa(a.$name,"input");g.push(a);a.$name&&(f[a.$name]=a);a.$$parentForm=f};f.$$renameControl=function(a,b){var c=a.$name;f[c]===a&&delete f[c];
f[b]=a;a.$name=b};f.$removeControl=function(a){a.$name&&f[a.$name]===a&&delete f[a.$name];q(f.$pending,function(b,c){f.$setValidity(c,null,a)});q(f.$error,function(b,c){f.$setValidity(c,null,a)});q(f.$$success,function(b,c){f.$setValidity(c,null,a)});Za(g,a);a.$$parentForm=Jb};Jd({ctrl:this,$element:a,set:function(a,b,c){var d=a[b];d?-1===d.indexOf(c)&&d.push(c):a[b]=[c]},unset:function(a,b,c){var d=a[b];d&&(Za(d,c),0===d.length&&delete a[b])},$animate:c});f.$setDirty=function(){c.removeClass(a,Ua);
c.addClass(a,Kb);f.$dirty=!0;f.$pristine=!1;f.$$parentForm.$setDirty()};f.$setPristine=function(){c.setClass(a,Ua,Kb+" ng-submitted");f.$dirty=!1;f.$pristine=!0;f.$submitted=!1;q(g,function(a){a.$setPristine()})};f.$setUntouched=function(){q(g,function(a){a.$setUntouched()})};f.$setSubmitted=function(){c.addClass(a,"ng-submitted");f.$submitted=!0;f.$$parentForm.$setSubmitted()}}function lc(a){a.$formatters.push(function(b){return a.$isEmpty(b)?b:b.toString()})}function jb(a,b,d,c,e,f){var g=P(b[0].type);
if(!e.android){var h=!1;b.on("compositionstart",function(){h=!0});b.on("compositionend",function(){h=!1;l()})}var k,l=function(a){k&&(f.defer.cancel(k),k=null);if(!h){var e=b.val();a=a&&a.type;"password"===g||d.ngTrim&&"false"===d.ngTrim||(e=V(e));(c.$viewValue!==e||""===e&&c.$$hasNativeValidators)&&c.$setViewValue(e,a)}};if(e.hasEvent("input"))b.on("input",l);else{var n=function(a,b,c){k||(k=f.defer(function(){k=null;b&&b.value===c||l(a)}))};b.on("keydown",function(a){var b=a.keyCode;91===b||15<
b&&19>b||37<=b&&40>=b||n(a,this,this.value)});if(e.hasEvent("paste"))b.on("paste cut",n)}b.on("change",l);if(Kd[g]&&c.$$hasNativeValidators&&g===d.type)b.on("keydown wheel mousedown",function(a){if(!k){var b=this.validity,c=b.badInput,d=b.typeMismatch;k=f.defer(function(){k=null;b.badInput===c&&b.typeMismatch===d||l(a)})}});c.$render=function(){var a=c.$isEmpty(c.$viewValue)?"":c.$viewValue;b.val()!==a&&b.val(a)}}function Lb(a,b){return function(d,c){var e,f;if(fa(d))return d;if(F(d)){'"'==d.charAt(0)&&
'"'==d.charAt(d.length-1)&&(d=d.substring(1,d.length-1));if(yg.test(d))return new Date(d);a.lastIndex=0;if(e=a.exec(d))return e.shift(),f=c?{yyyy:c.getFullYear(),MM:c.getMonth()+1,dd:c.getDate(),HH:c.getHours(),mm:c.getMinutes(),ss:c.getSeconds(),sss:c.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0,sss:0},q(e,function(a,c){c<b.length&&(f[b[c]]=+a)}),new Date(f.yyyy,f.MM-1,f.dd,f.HH,f.mm,f.ss||0,1E3*f.sss||0)}return NaN}}function kb(a,b,d,c){return function(e,f,g,h,k,l,n){function m(a){return a&&
!(a.getTime&&a.getTime()!==a.getTime())}function r(a){return x(a)&&!fa(a)?d(a)||void 0:a}Ld(e,f,g,h);jb(e,f,g,h,k,l);var q=h&&h.$options&&h.$options.timezone,s;h.$$parserName=a;h.$parsers.push(function(a){if(h.$isEmpty(a))return null;if(b.test(a))return a=d(a,s),q&&(a=Qb(a,q)),a});h.$formatters.push(function(a){if(a&&!fa(a))throw lb("datefmt",a);if(m(a))return(s=a)&&q&&(s=Qb(s,q,!0)),n("date")(a,c,q);s=null;return""});if(x(g.min)||g.ngMin){var w;h.$validators.min=function(a){return!m(a)||y(w)||d(a)>=
w};g.$observe("min",function(a){w=r(a);h.$validate()})}if(x(g.max)||g.ngMax){var p;h.$validators.max=function(a){return!m(a)||y(p)||d(a)<=p};g.$observe("max",function(a){p=r(a);h.$validate()})}}}function Ld(a,b,d,c){(c.$$hasNativeValidators=G(b[0].validity))&&c.$parsers.push(function(a){var c=b.prop("validity")||{};return c.badInput||c.typeMismatch?void 0:a})}function Md(a,b,d,c,e){if(x(c)){a=a(c);if(!a.constant)throw lb("constexpr",d,c);return a(b)}return e}function mc(a,b){a="ngClass"+a;return["$animate",
function(d){function c(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],n=0;n<b.length;n++)if(e==b[n])continue a;c.push(e)}return c}function e(a){var b=[];return K(a)?(q(a,function(a){b=b.concat(e(a))}),b):F(a)?a.split(" "):G(a)?(q(a,function(a,c){a&&(b=b.concat(c.split(" ")))}),b):a}return{restrict:"AC",link:function(f,g,h){function k(a){a=l(a,1);h.$addClass(a)}function l(a,b){var c=g.data("$classCounts")||T(),d=[];q(a,function(a){if(0<b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});g.data("$classCounts",
c);return d.join(" ")}function n(a,b){var e=c(b,a),f=c(a,b),e=l(e,1),f=l(f,-1);e&&e.length&&d.addClass(g,e);f&&f.length&&d.removeClass(g,f)}function m(a){if(!0===b||f.$index%2===b){var c=e(a||[]);if(!r)k(c);else if(!pa(a,r)){var d=e(r);n(d,c)}}r=K(a)?a.map(function(a){return ha(a)}):ha(a)}var r;f.$watch(h[a],m,!0);h.$observe("class",function(b){m(f.$eval(h[a]))});"ngClass"!==a&&f.$watch("$index",function(c,d){var g=c&1;if(g!==(d&1)){var m=e(f.$eval(h[a]));g===b?k(m):(g=l(m,-1),h.$removeClass(g))}})}}}]}
function Jd(a){function b(a,b){b&&!f[a]?(k.addClass(e,a),f[a]=!0):!b&&f[a]&&(k.removeClass(e,a),f[a]=!1)}function d(a,c){a=a?"-"+zc(a,"-"):"";b(mb+a,!0===c);b(Nd+a,!1===c)}var c=a.ctrl,e=a.$element,f={},g=a.set,h=a.unset,k=a.$animate;f[Nd]=!(f[mb]=e.hasClass(mb));c.$setValidity=function(a,e,f){y(e)?(c.$pending||(c.$pending={}),g(c.$pending,a,f)):(c.$pending&&h(c.$pending,a,f),Od(c.$pending)&&(c.$pending=void 0));Da(e)?e?(h(c.$error,a,f),g(c.$$success,a,f)):(g(c.$error,a,f),h(c.$$success,a,f)):(h(c.$error,
a,f),h(c.$$success,a,f));c.$pending?(b(Pd,!0),c.$valid=c.$invalid=void 0,d("",null)):(b(Pd,!1),c.$valid=Od(c.$error),c.$invalid=!c.$valid,d("",c.$valid));e=c.$pending&&c.$pending[a]?void 0:c.$error[a]?!1:c.$$success[a]?!0:null;d(a,e);c.$$parentForm.$setValidity(a,e,c)}}function Od(a){if(a)for(var b in a)if(a.hasOwnProperty(b))return!1;return!0}var zg=/^\/(.+)\/([a-z]*)$/,ua=Object.prototype.hasOwnProperty,P=function(a){return F(a)?a.toLowerCase():a},sb=function(a){return F(a)?a.toUpperCase():a},Ca,
B,Z,za=[].slice,Zf=[].splice,Ag=[].push,ma=Object.prototype.toString,sc=Object.getPrototypeOf,Aa=O("ng"),ea=v.angular||(v.angular={}),Sb,nb=0;Ca=v.document.documentMode;C.$inject=[];Xa.$inject=[];var K=Array.isArray,$d=/^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/,V=function(a){return F(a)?a.trim():a},vd=function(a){return a.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08")},Ea=function(){if(!x(Ea.rules)){var a=v.document.querySelector("[ng-csp]")||
v.document.querySelector("[data-ng-csp]");if(a){var b=a.getAttribute("ng-csp")||a.getAttribute("data-ng-csp");Ea.rules={noUnsafeEval:!b||-1!==b.indexOf("no-unsafe-eval"),noInlineStyle:!b||-1!==b.indexOf("no-inline-style")}}else{a=Ea;try{new Function(""),b=!1}catch(d){b=!0}a.rules={noUnsafeEval:b,noInlineStyle:!1}}}return Ea.rules},pb=function(){if(x(pb.name_))return pb.name_;var a,b,d=Na.length,c,e;for(b=0;b<d;++b)if(c=Na[b],a=v.document.querySelector("["+c.replace(":","\\:")+"jq]")){e=a.getAttribute(c+
"jq");break}return pb.name_=e},ce=/:/g,Na=["ng-","data-ng-","ng:","x-ng-"],he=/[A-Z]/g,Ac=!1,Ma=3,le={full:"1.5.5",major:1,minor:5,dot:5,codeName:"material-conspiration"};U.expando="ng339";var eb=U.cache={},Nf=1;U._data=function(a){return this.cache[a[this.expando]]||{}};var If=/([\:\-\_]+(.))/g,Jf=/^moz([A-Z])/,wb={mouseleave:"mouseout",mouseenter:"mouseover"},Ub=O("jqLite"),Mf=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Tb=/<|&#?\w+;/,Kf=/<([\w:-]+)/,Lf=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,
ia={option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ia.optgroup=ia.option;ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead;ia.th=ia.td;var Sf=v.Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&16)},Oa=U.prototype={ready:function(a){function b(){d||(d=!0,a())}var d=!1;"complete"===
v.document.readyState?v.setTimeout(b):(this.on("DOMContentLoaded",b),U(v).on("load",b))},toString:function(){var a=[];q(this,function(b){a.push(""+b)});return"["+a.join(", ")+"]"},eq:function(a){return 0<=a?B(this[a]):B(this[this.length+a])},length:0,push:Ag,sort:[].sort,splice:[].splice},Cb={};q("multiple selected checked disabled readOnly required open".split(" "),function(a){Cb[P(a)]=a});var Sc={};q("input select option textarea button form details".split(" "),function(a){Sc[a]=!0});var ad={ngMinlength:"minlength",
ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};q({data:Wb,removeData:db,hasData:function(a){for(var b in eb[a.ng339])return!0;return!1},cleanData:function(a){for(var b=0,d=a.length;b<d;b++)db(a[b])}},function(a,b){U[b]=a});q({data:Wb,inheritedData:Ab,scope:function(a){return B.data(a,"$scope")||Ab(a.parentNode||a,["$isolateScope","$scope"])},isolateScope:function(a){return B.data(a,"$isolateScope")||B.data(a,"$isolateScopeNoTemplate")},controller:Pc,injector:function(a){return Ab(a,
"$injector")},removeAttr:function(a,b){a.removeAttribute(b)},hasClass:xb,css:function(a,b,d){b=cb(b);if(x(d))a.style[b]=d;else return a.style[b]},attr:function(a,b,d){var c=a.nodeType;if(c!==Ma&&2!==c&&8!==c)if(c=P(b),Cb[c])if(x(d))d?(a[b]=!0,a.setAttribute(b,c)):(a[b]=!1,a.removeAttribute(c));else return a[b]||(a.attributes.getNamedItem(b)||C).specified?c:void 0;else if(x(d))a.setAttribute(b,d);else if(a.getAttribute)return a=a.getAttribute(b,2),null===a?void 0:a},prop:function(a,b,d){if(x(d))a[b]=
d;else return a[b]},text:function(){function a(a,d){if(y(d)){var c=a.nodeType;return 1===c||c===Ma?a.textContent:""}a.textContent=d}a.$dv="";return a}(),val:function(a,b){if(y(b)){if(a.multiple&&"select"===va(a)){var d=[];q(a.options,function(a){a.selected&&d.push(a.value||a.text)});return 0===d.length?null:d}return a.value}a.value=b},html:function(a,b){if(y(b))return a.innerHTML;ub(a,!0);a.innerHTML=b},empty:Qc},function(a,b){U.prototype[b]=function(b,c){var e,f,g=this.length;if(a!==Qc&&y(2==a.length&&
a!==xb&&a!==Pc?b:c)){if(G(b)){for(e=0;e<g;e++)if(a===Wb)a(this[e],b);else for(f in b)a(this[e],f,b[f]);return this}e=a.$dv;g=y(e)?Math.min(g,1):g;for(f=0;f<g;f++){var h=a(this[f],b,c);e=e?e+h:h}return e}for(e=0;e<g;e++)a(this[e],b,c);return this}});q({removeData:db,on:function(a,b,d,c){if(x(c))throw Ub("onargs");if(Kc(a)){c=vb(a,!0);var e=c.events,f=c.handle;f||(f=c.handle=Pf(a,e));c=0<=b.indexOf(" ")?b.split(" "):[b];for(var g=c.length,h=function(b,c,g){var h=e[b];h||(h=e[b]=[],h.specialHandlerWrapper=
c,"$destroy"===b||g||a.addEventListener(b,f,!1));h.push(d)};g--;)b=c[g],wb[b]?(h(wb[b],Rf),h(b,void 0,!0)):h(b)}},off:Oc,one:function(a,b,d){a=B(a);a.on(b,function e(){a.off(b,d);a.off(b,e)});a.on(b,d)},replaceWith:function(a,b){var d,c=a.parentNode;ub(a);q(new U(b),function(b){d?c.insertBefore(b,d.nextSibling):c.replaceChild(b,a);d=b})},children:function(a){var b=[];q(a.childNodes,function(a){1===a.nodeType&&b.push(a)});return b},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,
b){var d=a.nodeType;if(1===d||11===d){b=new U(b);for(var d=0,c=b.length;d<c;d++)a.appendChild(b[d])}},prepend:function(a,b){if(1===a.nodeType){var d=a.firstChild;q(new U(b),function(b){a.insertBefore(b,d)})}},wrap:function(a,b){Mc(a,B(b).eq(0).clone()[0])},remove:Bb,detach:function(a){Bb(a,!0)},after:function(a,b){var d=a,c=a.parentNode;b=new U(b);for(var e=0,f=b.length;e<f;e++){var g=b[e];c.insertBefore(g,d.nextSibling);d=g}},addClass:zb,removeClass:yb,toggleClass:function(a,b,d){b&&q(b.split(" "),
function(b){var e=d;y(e)&&(e=!xb(a,b));(e?zb:yb)(a,b)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){return a.nextElementSibling},find:function(a,b){return a.getElementsByTagName?a.getElementsByTagName(b):[]},clone:Vb,triggerHandler:function(a,b,d){var c,e,f=b.type||b,g=vb(a);if(g=(g=g&&g.events)&&g[f])c={preventDefault:function(){this.defaultPrevented=!0},isDefaultPrevented:function(){return!0===this.defaultPrevented},stopImmediatePropagation:function(){this.immediatePropagationStopped=
!0},isImmediatePropagationStopped:function(){return!0===this.immediatePropagationStopped},stopPropagation:C,type:f,target:a},b.type&&(c=R(c,b)),b=ha(g),e=d?[c].concat(d):[c],q(b,function(b){c.isImmediatePropagationStopped()||b.apply(a,e)})}},function(a,b){U.prototype[b]=function(b,c,e){for(var f,g=0,h=this.length;g<h;g++)y(f)?(f=a(this[g],b,c,e),x(f)&&(f=B(f))):Nc(f,a(this[g],b,c,e));return x(f)?f:this};U.prototype.bind=U.prototype.on;U.prototype.unbind=U.prototype.off});Ra.prototype={put:function(a,
b){this[Fa(a,this.nextUid)]=b},get:function(a){return this[Fa(a,this.nextUid)]},remove:function(a){var b=this[a=Fa(a,this.nextUid)];delete this[a];return b}};var Gf=[function(){this.$get=[function(){return Ra}]}],Uf=/^([^\(]+?)=>/,Vf=/^[^\(]*\(\s*([^\)]*)\)/m,Bg=/,/,Cg=/^\s*(_?)(\S+?)\1\s*$/,Tf=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Ga=O("$injector");bb.$$annotate=function(a,b,d){var c;if("function"===typeof a){if(!(c=a.$inject)){c=[];if(a.length){if(b)throw F(d)&&d||(d=a.name||Wf(a)),Ga("strictdi",d);
b=Tc(a);q(b[1].split(Bg),function(a){a.replace(Cg,function(a,b,d){c.push(d)})})}a.$inject=c}}else K(a)?(b=a.length-1,Pa(a[b],"fn"),c=a.slice(0,b)):Pa(a,"fn",!0);return c};var Qd=O("$animate"),Ze=function(){this.$get=C},$e=function(){var a=new Ra,b=[];this.$get=["$$AnimateRunner","$rootScope",function(d,c){function e(a,b,c){var d=!1;b&&(b=F(b)?b.split(" "):K(b)?b:[],q(b,function(b){b&&(d=!0,a[b]=c)}));return d}function f(){q(b,function(b){var c=a.get(b);if(c){var d=Xf(b.attr("class")),e="",f="";q(c,
function(a,b){a!==!!d[b]&&(a?e+=(e.length?" ":"")+b:f+=(f.length?" ":"")+b)});q(b,function(a){e&&zb(a,e);f&&yb(a,f)});a.remove(b)}});b.length=0}return{enabled:C,on:C,off:C,pin:C,push:function(g,h,k,l){l&&l();k=k||{};k.from&&g.css(k.from);k.to&&g.css(k.to);if(k.addClass||k.removeClass)if(h=k.addClass,l=k.removeClass,k=a.get(g)||{},h=e(k,h,!0),l=e(k,l,!1),h||l)a.put(g,k),b.push(g),1===b.length&&c.$$postDigest(f);g=new d;g.complete();return g}}}]},Xe=["$provide",function(a){var b=this;this.$$registeredAnimations=
Object.create(null);this.register=function(d,c){if(d&&"."!==d.charAt(0))throw Qd("notcsel",d);var e=d+"-animation";b.$$registeredAnimations[d.substr(1)]=e;a.factory(e,c)};this.classNameFilter=function(a){if(1===arguments.length&&(this.$$classNameFilter=a instanceof RegExp?a:null)&&/(\s+|\/)ng-animate(\s+|\/)/.test(this.$$classNameFilter.toString()))throw Qd("nongcls","ng-animate");return this.$$classNameFilter};this.$get=["$$animateQueue",function(a){function b(a,c,d){if(d){var h;a:{for(h=0;h<d.length;h++){var k=
d[h];if(1===k.nodeType){h=k;break a}}h=void 0}!h||h.parentNode||h.previousElementSibling||(d=null)}d?d.after(a):c.prepend(a)}return{on:a.on,off:a.off,pin:a.pin,enabled:a.enabled,cancel:function(a){a.end&&a.end()},enter:function(e,f,g,h){f=f&&B(f);g=g&&B(g);f=f||g.parent();b(e,f,g);return a.push(e,"enter",Ha(h))},move:function(e,f,g,h){f=f&&B(f);g=g&&B(g);f=f||g.parent();b(e,f,g);return a.push(e,"move",Ha(h))},leave:function(b,c){return a.push(b,"leave",Ha(c),function(){b.remove()})},addClass:function(b,
c,g){g=Ha(g);g.addClass=fb(g.addclass,c);return a.push(b,"addClass",g)},removeClass:function(b,c,g){g=Ha(g);g.removeClass=fb(g.removeClass,c);return a.push(b,"removeClass",g)},setClass:function(b,c,g,h){h=Ha(h);h.addClass=fb(h.addClass,c);h.removeClass=fb(h.removeClass,g);return a.push(b,"setClass",h)},animate:function(b,c,g,h,k){k=Ha(k);k.from=k.from?R(k.from,c):c;k.to=k.to?R(k.to,g):g;k.tempClasses=fb(k.tempClasses,h||"ng-inline-animate");return a.push(b,"animate",k)}}}]}],bf=function(){this.$get=
["$$rAF",function(a){function b(b){d.push(b);1<d.length||a(function(){for(var a=0;a<d.length;a++)d[a]();d=[]})}var d=[];return function(){var a=!1;b(function(){a=!0});return function(d){a?d():b(d)}}}]},af=function(){this.$get=["$q","$sniffer","$$animateAsyncRun","$document","$timeout",function(a,b,d,c,e){function f(a){this.setHost(a);var b=d();this._doneCallbacks=[];this._tick=function(a){var d=c[0];d&&d.hidden?e(a,0,!1):b(a)};this._state=0}f.chain=function(a,b){function c(){if(d===a.length)b(!0);
else a[d](function(a){!1===a?b(!1):(d++,c())})}var d=0;c()};f.all=function(a,b){function c(f){e=e&&f;++d===a.length&&b(e)}var d=0,e=!0;q(a,function(a){a.done(c)})};f.prototype={setHost:function(a){this.host=a||{}},done:function(a){2===this._state?a():this._doneCallbacks.push(a)},progress:C,getPromise:function(){if(!this.promise){var b=this;this.promise=a(function(a,c){b.done(function(b){!1===b?c():a()})})}return this.promise},then:function(a,b){return this.getPromise().then(a,b)},"catch":function(a){return this.getPromise()["catch"](a)},
"finally":function(a){return this.getPromise()["finally"](a)},pause:function(){this.host.pause&&this.host.pause()},resume:function(){this.host.resume&&this.host.resume()},end:function(){this.host.end&&this.host.end();this._resolve(!0)},cancel:function(){this.host.cancel&&this.host.cancel();this._resolve(!1)},complete:function(a){var b=this;0===b._state&&(b._state=1,b._tick(function(){b._resolve(a)}))},_resolve:function(a){2!==this._state&&(q(this._doneCallbacks,function(b){b(a)}),this._doneCallbacks.length=
0,this._state=2)}};return f}]},Ye=function(){this.$get=["$$rAF","$q","$$AnimateRunner",function(a,b,d){return function(b,e){function f(){a(function(){g.addClass&&(b.addClass(g.addClass),g.addClass=null);g.removeClass&&(b.removeClass(g.removeClass),g.removeClass=null);g.to&&(b.css(g.to),g.to=null);h||k.complete();h=!0});return k}var g=e||{};g.$$prepared||(g=qa(g));g.cleanupStyles&&(g.from=g.to=null);g.from&&(b.css(g.from),g.from=null);var h,k=new d;return{start:f,end:f}}}]},ga=O("$compile"),Zb=new function(){};
Cc.$inject=["$provide","$$sanitizeUriProvider"];Db.prototype.isFirstChange=function(){return this.previousValue===Zb};var Vc=/^((?:x|data)[\:\-_])/i,$f=O("$controller"),bd=/^(\S+)(\s+as\s+([\w$]+))?$/,hf=function(){this.$get=["$document",function(a){return function(b){b?!b.nodeType&&b instanceof B&&(b=b[0]):b=a[0].body;return b.offsetWidth+1}}]},cd="application/json",bc={"Content-Type":cd+";charset=utf-8"},bg=/^\[|^\{(?!\{)/,cg={"[":/]$/,"{":/}$/},ag=/^\)\]\}',?\n/,Dg=O("$http"),gd=function(a){return function(){throw Dg("legacy",
a);}},Ja=ea.$interpolateMinErr=O("$interpolate");Ja.throwNoconcat=function(a){throw Ja("noconcat",a);};Ja.interr=function(a,b){return Ja("interr",a,b.toString())};var Eg=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,eg={http:80,https:443,ftp:21},Eb=O("$location"),Fg={$$html5:!1,$$replace:!1,absUrl:Fb("$$absUrl"),url:function(a){if(y(a))return this.$$url;var b=Eg.exec(a);(b[1]||""===a)&&this.path(decodeURIComponent(b[1]));(b[2]||b[1]||""===a)&&this.search(b[3]||"");this.hash(b[5]||"");return this},protocol:Fb("$$protocol"),
host:Fb("$$host"),port:Fb("$$port"),path:ld("$$path",function(a){a=null!==a?a.toString():"";return"/"==a.charAt(0)?a:"/"+a}),search:function(a,b){switch(arguments.length){case 0:return this.$$search;case 1:if(F(a)||Q(a))a=a.toString(),this.$$search=xc(a);else if(G(a))a=qa(a,{}),q(a,function(b,c){null==b&&delete a[c]}),this.$$search=a;else throw Eb("isrcharg");break;default:y(b)||null===b?delete this.$$search[a]:this.$$search[a]=b}this.$$compose();return this},hash:ld("$$hash",function(a){return null!==
a?a.toString():""}),replace:function(){this.$$replace=!0;return this}};q([kd,ec,dc],function(a){a.prototype=Object.create(Fg);a.prototype.state=function(b){if(!arguments.length)return this.$$state;if(a!==dc||!this.$$html5)throw Eb("nostate");this.$$state=y(b)?null:b;return this}});var ca=O("$parse"),gg=Function.prototype.call,hg=Function.prototype.apply,ig=Function.prototype.bind,Mb=T();q("+ - * / % === !== == != < > <= >= && || ! = |".split(" "),function(a){Mb[a]=!0});var Gg={n:"\n",f:"\f",r:"\r",
t:"\t",v:"\v","'":"'",'"':'"'},gc=function(a){this.options=a};gc.prototype={constructor:gc,lex:function(a){this.text=a;this.index=0;for(this.tokens=[];this.index<this.text.length;)if(a=this.text.charAt(this.index),'"'===a||"'"===a)this.readString(a);else if(this.isNumber(a)||"."===a&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdentifierStart(this.peekMultichar()))this.readIdent();else if(this.is(a,"(){}[].,;:?"))this.tokens.push({index:this.index,text:a}),this.index++;else if(this.isWhitespace(a))this.index++;
else{var b=a+this.peek(),d=b+this.peek(2),c=Mb[b],e=Mb[d];Mb[a]||c||e?(a=e?d:c?b:a,this.tokens.push({index:this.index,text:a,operator:!0}),this.index+=a.length):this.throwError("Unexpected next character ",this.index,this.index+1)}return this.tokens},is:function(a,b){return-1!==b.indexOf(a)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a&&"string"===typeof a},isWhitespace:function(a){return" "===a||"\r"===a||
"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdentifierStart:function(a){return this.options.isIdentifierStart?this.options.isIdentifierStart(a,this.codePointAt(a)):this.isValidIdentifierStart(a)},isValidIdentifierStart:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isIdentifierContinue:function(a){return this.options.isIdentifierContinue?this.options.isIdentifierContinue(a,this.codePointAt(a)):this.isValidIdentifierContinue(a)},isValidIdentifierContinue:function(a,b){return this.isValidIdentifierStart(a,
b)||this.isNumber(a)},codePointAt:function(a){return 1===a.length?a.charCodeAt(0):(a.charCodeAt(0)<<10)+a.charCodeAt(1)-56613888},peekMultichar:function(){var a=this.text.charAt(this.index),b=this.peek();if(!b)return a;var d=a.charCodeAt(0),c=b.charCodeAt(0);return 55296<=d&&56319>=d&&56320<=c&&57343>=c?a+b:a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,b,d){d=d||this.index;b=x(b)?"s "+b+"-"+this.index+" ["+this.text.substring(b,d)+"]":" "+d;throw ca("lexerr",
a,b,this.text);},readNumber:function(){for(var a="",b=this.index;this.index<this.text.length;){var d=P(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var c=this.peek();if("e"==d&&this.isExpOperator(c))a+=d;else if(this.isExpOperator(d)&&c&&this.isNumber(c)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||c&&this.isNumber(c)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}this.tokens.push({index:b,text:a,constant:!0,value:Number(a)})},
readIdent:function(){var a=this.index;for(this.index+=this.peekMultichar().length;this.index<this.text.length;){var b=this.peekMultichar();if(!this.isIdentifierContinue(b))break;this.index+=b.length}this.tokens.push({index:a,text:this.text.slice(a,this.index),identifier:!0})},readString:function(a){var b=this.index;this.index++;for(var d="",c=a,e=!1;this.index<this.text.length;){var f=this.text.charAt(this.index),c=c+f;if(e)"u"===f?(e=this.text.substring(this.index+1,this.index+5),e.match(/[\da-f]{4}/i)||
this.throwError("Invalid unicode escape [\\u"+e+"]"),this.index+=4,d+=String.fromCharCode(parseInt(e,16))):d+=Gg[f]||f,e=!1;else if("\\"===f)e=!0;else{if(f===a){this.index++;this.tokens.push({index:b,text:c,constant:!0,value:d});return}d+=f}this.index++}this.throwError("Unterminated quote",b)}};var s=function(a,b){this.lexer=a;this.options=b};s.Program="Program";s.ExpressionStatement="ExpressionStatement";s.AssignmentExpression="AssignmentExpression";s.ConditionalExpression="ConditionalExpression";
s.LogicalExpression="LogicalExpression";s.BinaryExpression="BinaryExpression";s.UnaryExpression="UnaryExpression";s.CallExpression="CallExpression";s.MemberExpression="MemberExpression";s.Identifier="Identifier";s.Literal="Literal";s.ArrayExpression="ArrayExpression";s.Property="Property";s.ObjectExpression="ObjectExpression";s.ThisExpression="ThisExpression";s.LocalsExpression="LocalsExpression";s.NGValueParameter="NGValueParameter";s.prototype={ast:function(a){this.text=a;this.tokens=this.lexer.lex(a);
a=this.program();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);return a},program:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.expressionStatement()),!this.expect(";"))return{type:s.Program,body:a}},expressionStatement:function(){return{type:s.ExpressionStatement,expression:this.filterChain()}},filterChain:function(){for(var a=this.expression();this.expect("|");)a=this.filter(a);return a},expression:function(){return this.assignment()},
assignment:function(){var a=this.ternary();this.expect("=")&&(a={type:s.AssignmentExpression,left:a,right:this.assignment(),operator:"="});return a},ternary:function(){var a=this.logicalOR(),b,d;return this.expect("?")&&(b=this.expression(),this.consume(":"))?(d=this.expression(),{type:s.ConditionalExpression,test:a,alternate:b,consequent:d}):a},logicalOR:function(){for(var a=this.logicalAND();this.expect("||");)a={type:s.LogicalExpression,operator:"||",left:a,right:this.logicalAND()};return a},logicalAND:function(){for(var a=
this.equality();this.expect("&&");)a={type:s.LogicalExpression,operator:"&&",left:a,right:this.equality()};return a},equality:function(){for(var a=this.relational(),b;b=this.expect("==","!=","===","!==");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.relational()};return a},relational:function(){for(var a=this.additive(),b;b=this.expect("<",">","<=",">=");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.additive()};return a},additive:function(){for(var a=this.multiplicative(),
b;b=this.expect("+","-");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),b;b=this.expect("*","/","%");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+","-","!"))?{type:s.UnaryExpression,operator:a.text,prefix:!0,argument:this.unary()}:this.primary()},primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")):
this.expect("[")?a=this.arrayDeclaration():this.expect("{")?a=this.object():this.selfReferential.hasOwnProperty(this.peek().text)?a=qa(this.selfReferential[this.consume().text]):this.options.literals.hasOwnProperty(this.peek().text)?a={type:s.Literal,value:this.options.literals[this.consume().text]}:this.peek().identifier?a=this.identifier():this.peek().constant?a=this.constant():this.throwError("not a primary expression",this.peek());for(var b;b=this.expect("(","[",".");)"("===b.text?(a={type:s.CallExpression,
callee:a,arguments:this.parseArguments()},this.consume(")")):"["===b.text?(a={type:s.MemberExpression,object:a,property:this.expression(),computed:!0},this.consume("]")):"."===b.text?a={type:s.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a=[a];for(var b={type:s.CallExpression,callee:this.identifier(),arguments:a,filter:!0};this.expect(":");)a.push(this.expression());return b},parseArguments:function(){var a=[];if(")"!==
this.peekToken().text){do a.push(this.expression());while(this.expect(","))}return a},identifier:function(){var a=this.consume();a.identifier||this.throwError("is not a valid identifier",a);return{type:s.Identifier,name:a.text}},constant:function(){return{type:s.Literal,value:this.consume().value}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;a.push(this.expression())}while(this.expect(","))}this.consume("]");return{type:s.ArrayExpression,elements:a}},
object:function(){var a=[],b;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;b={type:s.Property,kind:"init"};this.peek().constant?b.key=this.constant():this.peek().identifier?b.key=this.identifier():this.throwError("invalid key",this.peek());this.consume(":");b.value=this.expression();a.push(b)}while(this.expect(","))}this.consume("}");return{type:s.ObjectExpression,properties:a}},throwError:function(a,b){throw ca("syntax",b.text,a,b.index+1,this.text,this.text.substring(b.index));},consume:function(a){if(0===
this.tokens.length)throw ca("ueoe",this.text);var b=this.expect(a);b||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return b},peekToken:function(){if(0===this.tokens.length)throw ca("ueoe",this.text);return this.tokens[0]},peek:function(a,b,d,c){return this.peekAhead(0,a,b,d,c)},peekAhead:function(a,b,d,c,e){if(this.tokens.length>a){a=this.tokens[a];var f=a.text;if(f===b||f===d||f===c||f===e||!(b||d||c||e))return a}return!1},expect:function(a,b,d,c){return(a=this.peek(a,b,d,c))?
(this.tokens.shift(),a):!1},selfReferential:{"this":{type:s.ThisExpression},$locals:{type:s.LocalsExpression}}};sd.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.state={nextId:0,filters:{},expensiveChecks:b,fn:{vars:[],body:[],own:{}},assign:{vars:[],body:[],own:{}},inputs:[]};aa(c,d.$filter);var e="",f;this.stage="assign";if(f=qd(c))this.state.computing="assign",e=this.nextId(),this.recurse(f,e),this.return_(e),e="fn.assign="+this.generateFunction("assign","s,v,l");f=od(c.body);
d.stage="inputs";q(f,function(a,b){var c="fn"+b;d.state[c]={vars:[],body:[],own:{}};d.state.computing=c;var e=d.nextId();d.recurse(a,e);d.return_(e);d.state.inputs.push(c);a.watchId=b});this.state.computing="fn";this.stage="main";this.recurse(c);e='"'+this.USE+" "+this.STRICT+'";\n'+this.filterPrefix()+"var fn="+this.generateFunction("fn","s,l,a,i")+e+this.watchFns()+"return fn;";e=(new Function("$filter","ensureSafeMemberName","ensureSafeObject","ensureSafeFunction","getStringValue","ensureSafeAssignContext",
"ifDefined","plus","text",e))(this.$filter,Ta,sa,md,fg,Gb,jg,nd,a);this.state=this.stage=void 0;e.literal=rd(c);e.constant=c.constant;return e},USE:"use",STRICT:"strict",watchFns:function(){var a=[],b=this.state.inputs,d=this;q(b,function(b){a.push("var "+b+"="+d.generateFunction(b,"s"))});b.length&&a.push("fn.inputs=["+b.join(",")+"];");return a.join("")},generateFunction:function(a,b){return"function("+b+"){"+this.varsPrefix(a)+this.body(a)+"};"},filterPrefix:function(){var a=[],b=this;q(this.state.filters,
function(d,c){a.push(d+"=$filter("+b.escape(c)+")")});return a.length?"var "+a.join(",")+";":""},varsPrefix:function(a){return this.state[a].vars.length?"var "+this.state[a].vars.join(",")+";":""},body:function(a){return this.state[a].body.join("")},recurse:function(a,b,d,c,e,f){var g,h,k=this,l,n;c=c||C;if(!f&&x(a.watchId))b=b||this.nextId(),this.if_("i",this.lazyAssign(b,this.computedMember("i",a.watchId)),this.lazyRecurse(a,b,d,c,e,!0));else switch(a.type){case s.Program:q(a.body,function(b,c){k.recurse(b.expression,
void 0,void 0,function(a){h=a});c!==a.body.length-1?k.current().body.push(h,";"):k.return_(h)});break;case s.Literal:n=this.escape(a.value);this.assign(b,n);c(n);break;case s.UnaryExpression:this.recurse(a.argument,void 0,void 0,function(a){h=a});n=a.operator+"("+this.ifDefined(h,0)+")";this.assign(b,n);c(n);break;case s.BinaryExpression:this.recurse(a.left,void 0,void 0,function(a){g=a});this.recurse(a.right,void 0,void 0,function(a){h=a});n="+"===a.operator?this.plus(g,h):"-"===a.operator?this.ifDefined(g,
0)+a.operator+this.ifDefined(h,0):"("+g+")"+a.operator+"("+h+")";this.assign(b,n);c(n);break;case s.LogicalExpression:b=b||this.nextId();k.recurse(a.left,b);k.if_("&&"===a.operator?b:k.not(b),k.lazyRecurse(a.right,b));c(b);break;case s.ConditionalExpression:b=b||this.nextId();k.recurse(a.test,b);k.if_(b,k.lazyRecurse(a.alternate,b),k.lazyRecurse(a.consequent,b));c(b);break;case s.Identifier:b=b||this.nextId();d&&(d.context="inputs"===k.stage?"s":this.assign(this.nextId(),this.getHasOwnProperty("l",
a.name)+"?l:s"),d.computed=!1,d.name=a.name);Ta(a.name);k.if_("inputs"===k.stage||k.not(k.getHasOwnProperty("l",a.name)),function(){k.if_("inputs"===k.stage||"s",function(){e&&1!==e&&k.if_(k.not(k.nonComputedMember("s",a.name)),k.lazyAssign(k.nonComputedMember("s",a.name),"{}"));k.assign(b,k.nonComputedMember("s",a.name))})},b&&k.lazyAssign(b,k.nonComputedMember("l",a.name)));(k.state.expensiveChecks||Hb(a.name))&&k.addEnsureSafeObject(b);c(b);break;case s.MemberExpression:g=d&&(d.context=this.nextId())||
this.nextId();b=b||this.nextId();k.recurse(a.object,g,void 0,function(){k.if_(k.notNull(g),function(){e&&1!==e&&k.addEnsureSafeAssignContext(g);if(a.computed)h=k.nextId(),k.recurse(a.property,h),k.getStringValue(h),k.addEnsureSafeMemberName(h),e&&1!==e&&k.if_(k.not(k.computedMember(g,h)),k.lazyAssign(k.computedMember(g,h),"{}")),n=k.ensureSafeObject(k.computedMember(g,h)),k.assign(b,n),d&&(d.computed=!0,d.name=h);else{Ta(a.property.name);e&&1!==e&&k.if_(k.not(k.nonComputedMember(g,a.property.name)),
k.lazyAssign(k.nonComputedMember(g,a.property.name),"{}"));n=k.nonComputedMember(g,a.property.name);if(k.state.expensiveChecks||Hb(a.property.name))n=k.ensureSafeObject(n);k.assign(b,n);d&&(d.computed=!1,d.name=a.property.name)}},function(){k.assign(b,"undefined")});c(b)},!!e);break;case s.CallExpression:b=b||this.nextId();a.filter?(h=k.filter(a.callee.name),l=[],q(a.arguments,function(a){var b=k.nextId();k.recurse(a,b);l.push(b)}),n=h+"("+l.join(",")+")",k.assign(b,n),c(b)):(h=k.nextId(),g={},l=
[],k.recurse(a.callee,h,g,function(){k.if_(k.notNull(h),function(){k.addEnsureSafeFunction(h);q(a.arguments,function(a){k.recurse(a,k.nextId(),void 0,function(a){l.push(k.ensureSafeObject(a))})});g.name?(k.state.expensiveChecks||k.addEnsureSafeObject(g.context),n=k.member(g.context,g.name,g.computed)+"("+l.join(",")+")"):n=h+"("+l.join(",")+")";n=k.ensureSafeObject(n);k.assign(b,n)},function(){k.assign(b,"undefined")});c(b)}));break;case s.AssignmentExpression:h=this.nextId();g={};if(!pd(a.left))throw ca("lval");
this.recurse(a.left,void 0,g,function(){k.if_(k.notNull(g.context),function(){k.recurse(a.right,h);k.addEnsureSafeObject(k.member(g.context,g.name,g.computed));k.addEnsureSafeAssignContext(g.context);n=k.member(g.context,g.name,g.computed)+a.operator+h;k.assign(b,n);c(b||n)})},1);break;case s.ArrayExpression:l=[];q(a.elements,function(a){k.recurse(a,k.nextId(),void 0,function(a){l.push(a)})});n="["+l.join(",")+"]";this.assign(b,n);c(n);break;case s.ObjectExpression:l=[];q(a.properties,function(a){k.recurse(a.value,
k.nextId(),void 0,function(b){l.push(k.escape(a.key.type===s.Identifier?a.key.name:""+a.key.value)+":"+b)})});n="{"+l.join(",")+"}";this.assign(b,n);c(n);break;case s.ThisExpression:this.assign(b,"s");c("s");break;case s.LocalsExpression:this.assign(b,"l");c("l");break;case s.NGValueParameter:this.assign(b,"v"),c("v")}},getHasOwnProperty:function(a,b){var d=a+"."+b,c=this.current().own;c.hasOwnProperty(d)||(c[d]=this.nextId(!1,a+"&&("+this.escape(b)+" in "+a+")"));return c[d]},assign:function(a,b){if(a)return this.current().body.push(a,
"=",b,";"),a},filter:function(a){this.state.filters.hasOwnProperty(a)||(this.state.filters[a]=this.nextId(!0));return this.state.filters[a]},ifDefined:function(a,b){return"ifDefined("+a+","+this.escape(b)+")"},plus:function(a,b){return"plus("+a+","+b+")"},return_:function(a){this.current().body.push("return ",a,";")},if_:function(a,b,d){if(!0===a)b();else{var c=this.current().body;c.push("if(",a,"){");b();c.push("}");d&&(c.push("else{"),d(),c.push("}"))}},not:function(a){return"!("+a+")"},notNull:function(a){return a+
"!=null"},nonComputedMember:function(a,b){var d=/[^$_a-zA-Z0-9]/g;return/[$_a-zA-Z][$_a-zA-Z0-9]*/.test(b)?a+"."+b:a+'["'+b.replace(d,this.stringEscapeFn)+'"]'},computedMember:function(a,b){return a+"["+b+"]"},member:function(a,b,d){return d?this.computedMember(a,b):this.nonComputedMember(a,b)},addEnsureSafeObject:function(a){this.current().body.push(this.ensureSafeObject(a),";")},addEnsureSafeMemberName:function(a){this.current().body.push(this.ensureSafeMemberName(a),";")},addEnsureSafeFunction:function(a){this.current().body.push(this.ensureSafeFunction(a),
";")},addEnsureSafeAssignContext:function(a){this.current().body.push(this.ensureSafeAssignContext(a),";")},ensureSafeObject:function(a){return"ensureSafeObject("+a+",text)"},ensureSafeMemberName:function(a){return"ensureSafeMemberName("+a+",text)"},ensureSafeFunction:function(a){return"ensureSafeFunction("+a+",text)"},getStringValue:function(a){this.assign(a,"getStringValue("+a+")")},ensureSafeAssignContext:function(a){return"ensureSafeAssignContext("+a+",text)"},lazyRecurse:function(a,b,d,c,e,f){var g=
this;return function(){g.recurse(a,b,d,c,e,f)}},lazyAssign:function(a,b){var d=this;return function(){d.assign(a,b)}},stringEscapeRegex:/[^ a-zA-Z0-9]/g,stringEscapeFn:function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)},escape:function(a){if(F(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(Q(a))return a.toString();if(!0===a)return"true";if(!1===a)return"false";if(null===a)return"null";if("undefined"===typeof a)return"undefined";throw ca("esc");},nextId:function(a,
b){var d="v"+this.state.nextId++;a||this.current().vars.push(d+(b?"="+b:""));return d},current:function(){return this.state[this.state.computing]}};td.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.expression=a;this.expensiveChecks=b;aa(c,d.$filter);var e,f;if(e=qd(c))f=this.recurse(e);e=od(c.body);var g;e&&(g=[],q(e,function(a,b){var c=d.recurse(a);a.input=c;g.push(c);a.watchId=b}));var h=[];q(c.body,function(a){h.push(d.recurse(a.expression))});e=0===c.body.length?C:1===
c.body.length?h[0]:function(a,b){var c;q(h,function(d){c=d(a,b)});return c};f&&(e.assign=function(a,b,c){return f(a,c,b)});g&&(e.inputs=g);e.literal=rd(c);e.constant=c.constant;return e},recurse:function(a,b,d){var c,e,f=this,g;if(a.input)return this.inputs(a.input,a.watchId);switch(a.type){case s.Literal:return this.value(a.value,b);case s.UnaryExpression:return e=this.recurse(a.argument),this["unary"+a.operator](e,b);case s.BinaryExpression:return c=this.recurse(a.left),e=this.recurse(a.right),
this["binary"+a.operator](c,e,b);case s.LogicalExpression:return c=this.recurse(a.left),e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case s.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),b);case s.Identifier:return Ta(a.name,f.expression),f.identifier(a.name,f.expensiveChecks||Hb(a.name),b,d,f.expression);case s.MemberExpression:return c=this.recurse(a.object,!1,!!d),a.computed||(Ta(a.property.name,f.expression),
e=a.property.name),a.computed&&(e=this.recurse(a.property)),a.computed?this.computedMember(c,e,b,d,f.expression):this.nonComputedMember(c,e,f.expensiveChecks,b,d,f.expression);case s.CallExpression:return g=[],q(a.arguments,function(a){g.push(f.recurse(a))}),a.filter&&(e=this.$filter(a.callee.name)),a.filter||(e=this.recurse(a.callee,!0)),a.filter?function(a,c,d,f){for(var m=[],r=0;r<g.length;++r)m.push(g[r](a,c,d,f));a=e.apply(void 0,m,f);return b?{context:void 0,name:void 0,value:a}:a}:function(a,
c,d,n){var m=e(a,c,d,n),r;if(null!=m.value){sa(m.context,f.expression);md(m.value,f.expression);r=[];for(var q=0;q<g.length;++q)r.push(sa(g[q](a,c,d,n),f.expression));r=sa(m.value.apply(m.context,r),f.expression)}return b?{value:r}:r};case s.AssignmentExpression:return c=this.recurse(a.left,!0,1),e=this.recurse(a.right),function(a,d,g,n){var m=c(a,d,g,n);a=e(a,d,g,n);sa(m.value,f.expression);Gb(m.context);m.context[m.name]=a;return b?{value:a}:a};case s.ArrayExpression:return g=[],q(a.elements,function(a){g.push(f.recurse(a))}),
function(a,c,d,e){for(var f=[],r=0;r<g.length;++r)f.push(g[r](a,c,d,e));return b?{value:f}:f};case s.ObjectExpression:return g=[],q(a.properties,function(a){g.push({key:a.key.type===s.Identifier?a.key.name:""+a.key.value,value:f.recurse(a.value)})}),function(a,c,d,e){for(var f={},r=0;r<g.length;++r)f[g[r].key]=g[r].value(a,c,d,e);return b?{value:f}:f};case s.ThisExpression:return function(a){return b?{value:a}:a};case s.LocalsExpression:return function(a,c){return b?{value:c}:c};case s.NGValueParameter:return function(a,
c,d){return b?{value:d}:d}}},"unary+":function(a,b){return function(d,c,e,f){d=a(d,c,e,f);d=x(d)?+d:0;return b?{value:d}:d}},"unary-":function(a,b){return function(d,c,e,f){d=a(d,c,e,f);d=x(d)?-d:0;return b?{value:d}:d}},"unary!":function(a,b){return function(d,c,e,f){d=!a(d,c,e,f);return b?{value:d}:d}},"binary+":function(a,b,d){return function(c,e,f,g){var h=a(c,e,f,g);c=b(c,e,f,g);h=nd(h,c);return d?{value:h}:h}},"binary-":function(a,b,d){return function(c,e,f,g){var h=a(c,e,f,g);c=b(c,e,f,g);
h=(x(h)?h:0)-(x(c)?c:0);return d?{value:h}:h}},"binary*":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)*b(c,e,f,g);return d?{value:c}:c}},"binary/":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)/b(c,e,f,g);return d?{value:c}:c}},"binary%":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)%b(c,e,f,g);return d?{value:c}:c}},"binary===":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)===b(c,e,f,g);return d?{value:c}:c}},"binary!==":function(a,b,d){return function(c,e,f,g){c=a(c,
e,f,g)!==b(c,e,f,g);return d?{value:c}:c}},"binary==":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)==b(c,e,f,g);return d?{value:c}:c}},"binary!=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)!=b(c,e,f,g);return d?{value:c}:c}},"binary<":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)<b(c,e,f,g);return d?{value:c}:c}},"binary>":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>b(c,e,f,g);return d?{value:c}:c}},"binary<=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,
g)<=b(c,e,f,g);return d?{value:c}:c}},"binary>=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>=b(c,e,f,g);return d?{value:c}:c}},"binary&&":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)&&b(c,e,f,g);return d?{value:c}:c}},"binary||":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)||b(c,e,f,g);return d?{value:c}:c}},"ternary?:":function(a,b,d,c){return function(e,f,g,h){e=a(e,f,g,h)?b(e,f,g,h):d(e,f,g,h);return c?{value:e}:e}},value:function(a,b){return function(){return b?{context:void 0,
name:void 0,value:a}:a}},identifier:function(a,b,d,c,e){return function(f,g,h,k){f=g&&a in g?g:f;c&&1!==c&&f&&!f[a]&&(f[a]={});g=f?f[a]:void 0;b&&sa(g,e);return d?{context:f,name:a,value:g}:g}},computedMember:function(a,b,d,c,e){return function(f,g,h,k){var l=a(f,g,h,k),n,m;null!=l&&(n=b(f,g,h,k),n+="",Ta(n,e),c&&1!==c&&(Gb(l),l&&!l[n]&&(l[n]={})),m=l[n],sa(m,e));return d?{context:l,name:n,value:m}:m}},nonComputedMember:function(a,b,d,c,e,f){return function(g,h,k,l){g=a(g,h,k,l);e&&1!==e&&(Gb(g),
g&&!g[b]&&(g[b]={}));h=null!=g?g[b]:void 0;(d||Hb(b))&&sa(h,f);return c?{context:g,name:b,value:h}:h}},inputs:function(a,b){return function(d,c,e,f){return f?f[b]:a(d,c,e)}}};var hc=function(a,b,d){this.lexer=a;this.$filter=b;this.options=d;this.ast=new s(a,d);this.astCompiler=d.csp?new td(this.ast,b):new sd(this.ast,b)};hc.prototype={constructor:hc,parse:function(a){return this.astCompiler.compile(a,this.options.expensiveChecks)}};var kg=Object.prototype.valueOf,ta=O("$sce"),oa={HTML:"html",CSS:"css",
URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},mg=O("$compile"),Y=v.document.createElement("a"),xd=ra(v.location.href);yd.$inject=["$document"];Jc.$inject=["$provide"];var Fd=22,Ed=".",jc="0";zd.$inject=["$locale"];Bd.$inject=["$locale"];var xg={yyyy:W("FullYear",4,0,!1,!0),yy:W("FullYear",2,0,!0,!0),y:W("FullYear",1,0,!1,!0),MMMM:ib("Month"),MMM:ib("Month",!0),MM:W("Month",2,1),M:W("Month",1,1),LLLL:ib("Month",!1,!0),dd:W("Date",2),d:W("Date",1),HH:W("Hours",2),H:W("Hours",1),hh:W("Hours",2,-12),
h:W("Hours",1,-12),mm:W("Minutes",2),m:W("Minutes",1),ss:W("Seconds",2),s:W("Seconds",1),sss:W("Milliseconds",3),EEEE:ib("Day"),EEE:ib("Day",!0),a:function(a,b){return 12>a.getHours()?b.AMPMS[0]:b.AMPMS[1]},Z:function(a,b,d){a=-1*d;return a=(0<=a?"+":"")+(Ib(Math[0<a?"floor":"ceil"](a/60),2)+Ib(Math.abs(a%60),2))},ww:Hd(2),w:Hd(1),G:kc,GG:kc,GGG:kc,GGGG:function(a,b){return 0>=a.getFullYear()?b.ERANAMES[0]:b.ERANAMES[1]}},wg=/((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
vg=/^\-?\d+$/;Ad.$inject=["$locale"];var qg=da(P),rg=da(sb);Cd.$inject=["$parse"];var ne=da({restrict:"E",compile:function(a,b){if(!b.href&&!b.xlinkHref)return function(a,b){if("a"===b[0].nodeName.toLowerCase()){var e="[object SVGAnimatedString]"===ma.call(b.prop("href"))?"xlink:href":"href";b.on("click",function(a){b.attr(e)||a.preventDefault()})}}}}),tb={};q(Cb,function(a,b){function d(a,d,e){a.$watch(e[c],function(a){e.$set(b,!!a)})}if("multiple"!=a){var c=xa("ng-"+b),e=d;"checked"===a&&(e=function(a,
b,e){e.ngModel!==e[c]&&d(a,b,e)});tb[c]=function(){return{restrict:"A",priority:100,link:e}}}});q(ad,function(a,b){tb[b]=function(){return{priority:100,link:function(a,c,e){if("ngPattern"===b&&"/"==e.ngPattern.charAt(0)&&(c=e.ngPattern.match(zg))){e.$set("ngPattern",new RegExp(c[1],c[2]));return}a.$watch(e[b],function(a){e.$set(b,a)})}}}});q(["src","srcset","href"],function(a){var b=xa("ng-"+a);tb[b]=function(){return{priority:99,link:function(d,c,e){var f=a,g=a;"href"===a&&"[object SVGAnimatedString]"===
ma.call(c.prop("href"))&&(g="xlinkHref",e.$attr[g]="xlink:href",f=null);e.$observe(b,function(b){b?(e.$set(g,b),Ca&&f&&c.prop(f,e[g])):"href"===a&&e.$set(g,null)})}}}});var Jb={$addControl:C,$$renameControl:function(a,b){a.$name=b},$removeControl:C,$setValidity:C,$setDirty:C,$setPristine:C,$setSubmitted:C};Id.$inject=["$element","$attrs","$scope","$animate","$interpolate"];var Rd=function(a){return["$timeout","$parse",function(b,d){function c(a){return""===a?d('this[""]').assign:d(a).assign||C}return{name:"form",
restrict:a?"EAC":"E",require:["form","^^?form"],controller:Id,compile:function(d,f){d.addClass(Ua).addClass(mb);var g=f.name?"name":a&&f.ngForm?"ngForm":!1;return{pre:function(a,d,e,f){var m=f[0];if(!("action"in e)){var r=function(b){a.$apply(function(){m.$commitViewValue();m.$setSubmitted()});b.preventDefault()};d[0].addEventListener("submit",r,!1);d.on("$destroy",function(){b(function(){d[0].removeEventListener("submit",r,!1)},0,!1)})}(f[1]||m.$$parentForm).$addControl(m);var q=g?c(m.$name):C;g&&
(q(a,m),e.$observe(g,function(b){m.$name!==b&&(q(a,void 0),m.$$parentForm.$$renameControl(m,b),q=c(m.$name),q(a,m))}));d.on("$destroy",function(){m.$$parentForm.$removeControl(m);q(a,void 0);R(m,Jb)})}}}}}]},oe=Rd(),Be=Rd(!0),yg=/^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/,Hg=/^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i,Ig=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,
Jg=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/,Sd=/^(\d{4,})-(\d{2})-(\d{2})$/,Td=/^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,nc=/^(\d{4,})-W(\d\d)$/,Ud=/^(\d{4,})-(\d\d)$/,Vd=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Kd=T();q(["date","datetime-local","month","time","week"],function(a){Kd[a]=!0});var Wd={text:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);lc(c)},date:kb("date",Sd,Lb(Sd,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":kb("datetimelocal",Td,Lb(Td,"yyyy MM dd HH mm ss sss".split(" ")),
"yyyy-MM-ddTHH:mm:ss.sss"),time:kb("time",Vd,Lb(Vd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:kb("week",nc,function(a,b){if(fa(a))return a;if(F(a)){nc.lastIndex=0;var d=nc.exec(a);if(d){var c=+d[1],e=+d[2],f=d=0,g=0,h=0,k=Gd(c),e=7*(e-1);b&&(d=b.getHours(),f=b.getMinutes(),g=b.getSeconds(),h=b.getMilliseconds());return new Date(c,0,k.getDate()+e,d,f,g,h)}}return NaN},"yyyy-Www"),month:kb("month",Ud,Lb(Ud,["yyyy","MM"]),"yyyy-MM"),number:function(a,b,d,c,e,f){Ld(a,b,d,c);jb(a,b,d,c,e,f);c.$$parserName=
"number";c.$parsers.push(function(a){if(c.$isEmpty(a))return null;if(Jg.test(a))return parseFloat(a)});c.$formatters.push(function(a){if(!c.$isEmpty(a)){if(!Q(a))throw lb("numfmt",a);a=a.toString()}return a});if(x(d.min)||d.ngMin){var g;c.$validators.min=function(a){return c.$isEmpty(a)||y(g)||a>=g};d.$observe("min",function(a){x(a)&&!Q(a)&&(a=parseFloat(a,10));g=Q(a)&&!isNaN(a)?a:void 0;c.$validate()})}if(x(d.max)||d.ngMax){var h;c.$validators.max=function(a){return c.$isEmpty(a)||y(h)||a<=h};d.$observe("max",
function(a){x(a)&&!Q(a)&&(a=parseFloat(a,10));h=Q(a)&&!isNaN(a)?a:void 0;c.$validate()})}},url:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);lc(c);c.$$parserName="url";c.$validators.url=function(a,b){var d=a||b;return c.$isEmpty(d)||Hg.test(d)}},email:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);lc(c);c.$$parserName="email";c.$validators.email=function(a,b){var d=a||b;return c.$isEmpty(d)||Ig.test(d)}},radio:function(a,b,d,c){y(d.name)&&b.attr("name",++nb);b.on("click",function(a){b[0].checked&&c.$setViewValue(d.value,
a&&a.type)});c.$render=function(){b[0].checked=d.value==c.$viewValue};d.$observe("value",c.$render)},checkbox:function(a,b,d,c,e,f,g,h){var k=Md(h,a,"ngTrueValue",d.ngTrueValue,!0),l=Md(h,a,"ngFalseValue",d.ngFalseValue,!1);b.on("click",function(a){c.$setViewValue(b[0].checked,a&&a.type)});c.$render=function(){b[0].checked=c.$viewValue};c.$isEmpty=function(a){return!1===a};c.$formatters.push(function(a){return pa(a,k)});c.$parsers.push(function(a){return a?k:l})},hidden:C,button:C,submit:C,reset:C,
file:C},Dc=["$browser","$sniffer","$filter","$parse",function(a,b,d,c){return{restrict:"E",require:["?ngModel"],link:{pre:function(e,f,g,h){h[0]&&(Wd[P(g.type)]||Wd.text)(e,f,g,h[0],b,a,d,c)}}}}],Kg=/^(true|false|\d+)$/,Te=function(){return{restrict:"A",priority:100,compile:function(a,b){return Kg.test(b.ngValue)?function(a,b,e){e.$set("value",a.$eval(e.ngValue))}:function(a,b,e){a.$watch(e.ngValue,function(a){e.$set("value",a)})}}}},te=["$compile",function(a){return{restrict:"AC",compile:function(b){a.$$addBindingClass(b);
return function(b,c,e){a.$$addBindingInfo(c,e.ngBind);c=c[0];b.$watch(e.ngBind,function(a){c.textContent=y(a)?"":a})}}}}],ve=["$interpolate","$compile",function(a,b){return{compile:function(d){b.$$addBindingClass(d);return function(c,d,f){c=a(d.attr(f.$attr.ngBindTemplate));b.$$addBindingInfo(d,c.expressions);d=d[0];f.$observe("ngBindTemplate",function(a){d.textContent=y(a)?"":a})}}}}],ue=["$sce","$parse","$compile",function(a,b,d){return{restrict:"A",compile:function(c,e){var f=b(e.ngBindHtml),g=
b(e.ngBindHtml,function(a){return(a||"").toString()});d.$$addBindingClass(c);return function(b,c,e){d.$$addBindingInfo(c,e.ngBindHtml);b.$watch(g,function(){c.html(a.getTrustedHtml(f(b))||"")})}}}}],Se=da({restrict:"A",require:"ngModel",link:function(a,b,d,c){c.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),we=mc("",!0),ye=mc("Odd",0),xe=mc("Even",1),ze=La({compile:function(a,b){b.$set("ngCloak",void 0);a.removeClass("ng-cloak")}}),Ae=[function(){return{restrict:"A",scope:!0,controller:"@",
priority:500}}],Ic={},Lg={blur:!0,focus:!0};q("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var b=xa("ng-"+a);Ic[b]=["$parse","$rootScope",function(d,c){return{restrict:"A",compile:function(e,f){var g=d(f[b],null,!0);return function(b,d){d.on(a,function(d){var e=function(){g(b,{$event:d})};Lg[a]&&c.$$phase?b.$evalAsync(e):b.$apply(e)})}}}}]});var De=["$animate","$compile",function(a,
b){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(d,c,e,f,g){var h,k,l;d.$watch(e.ngIf,function(d){d?k||g(function(d,f){k=f;d[d.length++]=b.$$createComment("end ngIf",e.ngIf);h={clone:d};a.enter(d,c.parent(),c)}):(l&&(l.remove(),l=null),k&&(k.$destroy(),k=null),h&&(l=rb(h.clone),a.leave(l).then(function(){l=null}),h=null))})}}}],Ee=["$templateRequest","$anchorScroll","$animate",function(a,b,d){return{restrict:"ECA",priority:400,terminal:!0,
transclude:"element",controller:ea.noop,compile:function(c,e){var f=e.ngInclude||e.src,g=e.onload||"",h=e.autoscroll;return function(c,e,n,m,r){var q=0,s,w,p,y=function(){w&&(w.remove(),w=null);s&&(s.$destroy(),s=null);p&&(d.leave(p).then(function(){w=null}),w=p,p=null)};c.$watch(f,function(f){var n=function(){!x(h)||h&&!c.$eval(h)||b()},u=++q;f?(a(f,!0).then(function(a){if(!c.$$destroyed&&u===q){var b=c.$new();m.template=a;a=r(b,function(a){y();d.enter(a,null,e).then(n)});s=b;p=a;s.$emit("$includeContentLoaded",
f);c.$eval(g)}},function(){c.$$destroyed||u!==q||(y(),c.$emit("$includeContentError",f))}),c.$emit("$includeContentRequested",f)):(y(),m.template=null)})}}}}],Ve=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(b,d,c,e){ma.call(d[0]).match(/SVG/)?(d.empty(),a(Lc(e.template,v.document).childNodes)(b,function(a){d.append(a)},{futureParentElement:d})):(d.html(e.template),a(d.contents())(b))}}}],Fe=La({priority:450,compile:function(){return{pre:function(a,
b,d){a.$eval(d.ngInit)}}}}),Re=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,b,d,c){var e=b.attr(d.$attr.ngList)||", ",f="false"!==d.ngTrim,g=f?V(e):e;c.$parsers.push(function(a){if(!y(a)){var b=[];a&&q(a.split(g),function(a){a&&b.push(f?V(a):a)});return b}});c.$formatters.push(function(a){if(K(a))return a.join(e)});c.$isEmpty=function(a){return!a||!a.length}}}},mb="ng-valid",Nd="ng-invalid",Ua="ng-pristine",Kb="ng-dirty",Pd="ng-pending",lb=O("ngModel"),Mg=["$scope",
"$exceptionHandler","$attrs","$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,b,d,c,e,f,g,h,k,l){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=void 0;this.$validators={};this.$asyncValidators={};this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$untouched=!0;this.$touched=!1;this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$error={};this.$$success={};this.$pending=void 0;this.$name=l(d.name||"",!1)(a);
this.$$parentForm=Jb;var n=e(d.ngModel),m=n.assign,r=n,s=m,v=null,w,p=this;this.$$setOptions=function(a){if((p.$options=a)&&a.getterSetter){var b=e(d.ngModel+"()"),f=e(d.ngModel+"($$$p)");r=function(a){var c=n(a);E(c)&&(c=b(a));return c};s=function(a,b){E(n(a))?f(a,{$$$p:b}):m(a,b)}}else if(!n.assign)throw lb("nonassign",d.ngModel,wa(c));};this.$render=C;this.$isEmpty=function(a){return y(a)||""===a||null===a||a!==a};this.$$updateEmptyClasses=function(a){p.$isEmpty(a)?(f.removeClass(c,"ng-not-empty"),
f.addClass(c,"ng-empty")):(f.removeClass(c,"ng-empty"),f.addClass(c,"ng-not-empty"))};var H=0;Jd({ctrl:this,$element:c,set:function(a,b){a[b]=!0},unset:function(a,b){delete a[b]},$animate:f});this.$setPristine=function(){p.$dirty=!1;p.$pristine=!0;f.removeClass(c,Kb);f.addClass(c,Ua)};this.$setDirty=function(){p.$dirty=!0;p.$pristine=!1;f.removeClass(c,Ua);f.addClass(c,Kb);p.$$parentForm.$setDirty()};this.$setUntouched=function(){p.$touched=!1;p.$untouched=!0;f.setClass(c,"ng-untouched","ng-touched")};
this.$setTouched=function(){p.$touched=!0;p.$untouched=!1;f.setClass(c,"ng-touched","ng-untouched")};this.$rollbackViewValue=function(){g.cancel(v);p.$viewValue=p.$$lastCommittedViewValue;p.$render()};this.$validate=function(){if(!Q(p.$modelValue)||!isNaN(p.$modelValue)){var a=p.$$rawModelValue,b=p.$valid,c=p.$modelValue,d=p.$options&&p.$options.allowInvalid;p.$$runValidators(a,p.$$lastCommittedViewValue,function(e){d||b===e||(p.$modelValue=e?a:void 0,p.$modelValue!==c&&p.$$writeModelToScope())})}};
this.$$runValidators=function(a,b,c){function d(){var c=!0;q(p.$validators,function(d,e){var g=d(a,b);c=c&&g;f(e,g)});return c?!0:(q(p.$asyncValidators,function(a,b){f(b,null)}),!1)}function e(){var c=[],d=!0;q(p.$asyncValidators,function(e,g){var h=e(a,b);if(!h||!E(h.then))throw lb("nopromise",h);f(g,void 0);c.push(h.then(function(){f(g,!0)},function(){d=!1;f(g,!1)}))});c.length?k.all(c).then(function(){g(d)},C):g(!0)}function f(a,b){h===H&&p.$setValidity(a,b)}function g(a){h===H&&c(a)}H++;var h=
H;(function(){var a=p.$$parserName||"parse";if(y(w))f(a,null);else return w||(q(p.$validators,function(a,b){f(b,null)}),q(p.$asyncValidators,function(a,b){f(b,null)})),f(a,w),w;return!0})()?d()?e():g(!1):g(!1)};this.$commitViewValue=function(){var a=p.$viewValue;g.cancel(v);if(p.$$lastCommittedViewValue!==a||""===a&&p.$$hasNativeValidators)p.$$updateEmptyClasses(a),p.$$lastCommittedViewValue=a,p.$pristine&&this.$setDirty(),this.$$parseAndValidate()};this.$$parseAndValidate=function(){var b=p.$$lastCommittedViewValue;
if(w=y(b)?void 0:!0)for(var c=0;c<p.$parsers.length;c++)if(b=p.$parsers[c](b),y(b)){w=!1;break}Q(p.$modelValue)&&isNaN(p.$modelValue)&&(p.$modelValue=r(a));var d=p.$modelValue,e=p.$options&&p.$options.allowInvalid;p.$$rawModelValue=b;e&&(p.$modelValue=b,p.$modelValue!==d&&p.$$writeModelToScope());p.$$runValidators(b,p.$$lastCommittedViewValue,function(a){e||(p.$modelValue=a?b:void 0,p.$modelValue!==d&&p.$$writeModelToScope())})};this.$$writeModelToScope=function(){s(a,p.$modelValue);q(p.$viewChangeListeners,
function(a){try{a()}catch(c){b(c)}})};this.$setViewValue=function(a,b){p.$viewValue=a;p.$options&&!p.$options.updateOnDefault||p.$$debounceViewValueCommit(b)};this.$$debounceViewValueCommit=function(b){var c=0,d=p.$options;d&&x(d.debounce)&&(d=d.debounce,Q(d)?c=d:Q(d[b])?c=d[b]:Q(d["default"])&&(c=d["default"]));g.cancel(v);c?v=g(function(){p.$commitViewValue()},c):h.$$phase?p.$commitViewValue():a.$apply(function(){p.$commitViewValue()})};a.$watch(function(){var b=r(a);if(b!==p.$modelValue&&(p.$modelValue===
p.$modelValue||b===b)){p.$modelValue=p.$$rawModelValue=b;w=void 0;for(var c=p.$formatters,d=c.length,e=b;d--;)e=c[d](e);p.$viewValue!==e&&(p.$$updateEmptyClasses(e),p.$viewValue=p.$$lastCommittedViewValue=e,p.$render(),p.$$runValidators(b,e,C))}return b})}],Qe=["$rootScope",function(a){return{restrict:"A",require:["ngModel","^?form","^?ngModelOptions"],controller:Mg,priority:1,compile:function(b){b.addClass(Ua).addClass("ng-untouched").addClass(mb);return{pre:function(a,b,e,f){var g=f[0];b=f[1]||
g.$$parentForm;g.$$setOptions(f[2]&&f[2].$options);b.$addControl(g);e.$observe("name",function(a){g.$name!==a&&g.$$parentForm.$$renameControl(g,a)});a.$on("$destroy",function(){g.$$parentForm.$removeControl(g)})},post:function(b,c,e,f){var g=f[0];if(g.$options&&g.$options.updateOn)c.on(g.$options.updateOn,function(a){g.$$debounceViewValueCommit(a&&a.type)});c.on("blur",function(){g.$touched||(a.$$phase?b.$evalAsync(g.$setTouched):b.$apply(g.$setTouched))})}}}}}],Ng=/(\s+|^)default(\s+|$)/,Ue=function(){return{restrict:"A",
controller:["$scope","$attrs",function(a,b){var d=this;this.$options=qa(a.$eval(b.ngModelOptions));x(this.$options.updateOn)?(this.$options.updateOnDefault=!1,this.$options.updateOn=V(this.$options.updateOn.replace(Ng,function(){d.$options.updateOnDefault=!0;return" "}))):this.$options.updateOnDefault=!0}]}},Ge=La({terminal:!0,priority:1E3}),Og=O("ngOptions"),Pg=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,
Oe=["$compile","$document","$parse",function(a,b,d){function c(a,b,c){function e(a,b,c,d,f){this.selectValue=a;this.viewValue=b;this.label=c;this.group=d;this.disabled=f}function f(a){var b;if(!q&&ya(a))b=a;else{b=[];for(var c in a)a.hasOwnProperty(c)&&"$"!==c.charAt(0)&&b.push(c)}return b}var m=a.match(Pg);if(!m)throw Og("iexp",a,wa(b));var r=m[5]||m[7],q=m[6];a=/ as /.test(m[0])&&m[1];var s=m[9];b=d(m[2]?m[1]:r);var w=a&&d(a)||b,p=s&&d(s),v=s?function(a,b){return p(c,b)}:function(a){return Fa(a)},
t=function(a,b){return v(a,L(a,b))},z=d(m[2]||m[1]),u=d(m[3]||""),y=d(m[4]||""),x=d(m[8]),D={},L=q?function(a,b){D[q]=b;D[r]=a;return D}:function(a){D[r]=a;return D};return{trackBy:s,getTrackByValue:t,getWatchables:d(x,function(a){var b=[];a=a||[];for(var d=f(a),e=d.length,g=0;g<e;g++){var h=a===d?g:d[g],l=a[h],h=L(l,h),l=v(l,h);b.push(l);if(m[2]||m[1])l=z(c,h),b.push(l);m[4]&&(h=y(c,h),b.push(h))}return b}),getOptions:function(){for(var a=[],b={},d=x(c)||[],g=f(d),h=g.length,m=0;m<h;m++){var p=d===
g?m:g[m],q=L(d[p],p),r=w(c,q),p=v(r,q),D=z(c,q),N=u(c,q),q=y(c,q),r=new e(p,r,D,N,q);a.push(r);b[p]=r}return{items:a,selectValueMap:b,getOptionFromViewValue:function(a){return b[t(a)]},getViewValueFromOption:function(a){return s?ea.copy(a.viewValue):a.viewValue}}}}}var e=v.document.createElement("option"),f=v.document.createElement("optgroup");return{restrict:"A",terminal:!0,require:["select","ngModel"],link:{pre:function(a,b,c,d){d[0].registerOption=C},post:function(d,h,k,l){function n(a,b){a.element=
b;b.disabled=a.disabled;a.label!==b.label&&(b.label=a.label,b.textContent=a.label);a.value!==b.value&&(b.value=a.selectValue)}function m(){var a=u&&r.readValue();if(u)for(var b=u.items.length-1;0<=b;b--){var c=u.items[b];c.group?Bb(c.element.parentNode):Bb(c.element)}u=I.getOptions();var d={};t&&h.prepend(w);u.items.forEach(function(a){var b;if(x(a.group)){b=d[a.group];b||(b=f.cloneNode(!1),E.appendChild(b),b.label=a.group,d[a.group]=b);var c=e.cloneNode(!1)}else b=E,c=e.cloneNode(!1);b.appendChild(c);
n(a,c)});h[0].appendChild(E);s.$render();s.$isEmpty(a)||(b=r.readValue(),(I.trackBy||v?pa(a,b):a===b)||(s.$setViewValue(b),s.$render()))}var r=l[0],s=l[1],v=k.multiple,w;l=0;for(var p=h.children(),y=p.length;l<y;l++)if(""===p[l].value){w=p.eq(l);break}var t=!!w,z=B(e.cloneNode(!1));z.val("?");var u,I=c(k.ngOptions,h,d),E=b[0].createDocumentFragment();v?(s.$isEmpty=function(a){return!a||0===a.length},r.writeValue=function(a){u.items.forEach(function(a){a.element.selected=!1});a&&a.forEach(function(a){if(a=
u.getOptionFromViewValue(a))a.element.selected=!0})},r.readValue=function(){var a=h.val()||[],b=[];q(a,function(a){(a=u.selectValueMap[a])&&!a.disabled&&b.push(u.getViewValueFromOption(a))});return b},I.trackBy&&d.$watchCollection(function(){if(K(s.$viewValue))return s.$viewValue.map(function(a){return I.getTrackByValue(a)})},function(){s.$render()})):(r.writeValue=function(a){var b=u.getOptionFromViewValue(a);b?(h[0].value!==b.selectValue&&(z.remove(),t||w.remove(),h[0].value=b.selectValue,b.element.selected=
!0),b.element.setAttribute("selected","selected")):null===a||t?(z.remove(),t||h.prepend(w),h.val(""),w.prop("selected",!0),w.attr("selected",!0)):(t||w.remove(),h.prepend(z),h.val("?"),z.prop("selected",!0),z.attr("selected",!0))},r.readValue=function(){var a=u.selectValueMap[h.val()];return a&&!a.disabled?(t||w.remove(),z.remove(),u.getViewValueFromOption(a)):null},I.trackBy&&d.$watch(function(){return I.getTrackByValue(s.$viewValue)},function(){s.$render()}));t?(w.remove(),a(w)(d),w.removeClass("ng-scope")):
w=B(e.cloneNode(!1));h.empty();m();d.$watchCollection(I.getWatchables,m)}}}}],He=["$locale","$interpolate","$log",function(a,b,d){var c=/{}/g,e=/^when(Minus)?(.+)$/;return{link:function(f,g,h){function k(a){g.text(a||"")}var l=h.count,n=h.$attr.when&&g.attr(h.$attr.when),m=h.offset||0,r=f.$eval(n)||{},s={},v=b.startSymbol(),w=b.endSymbol(),p=v+l+"-"+m+w,x=ea.noop,t;q(h,function(a,b){var c=e.exec(b);c&&(c=(c[1]?"-":"")+P(c[2]),r[c]=g.attr(h.$attr[b]))});q(r,function(a,d){s[d]=b(a.replace(c,p))});f.$watch(l,
function(b){var c=parseFloat(b),e=isNaN(c);e||c in r||(c=a.pluralCat(c-m));c===t||e&&Q(t)&&isNaN(t)||(x(),e=s[c],y(e)?(null!=b&&d.debug("ngPluralize: no rule defined for '"+c+"' in "+n),x=C,k()):x=f.$watch(e,k),t=c)})}}}],Ie=["$parse","$animate","$compile",function(a,b,d){var c=O("ngRepeat"),e=function(a,b,c,d,e,n,m){a[c]=d;e&&(a[e]=n);a.$index=b;a.$first=0===b;a.$last=b===m-1;a.$middle=!(a.$first||a.$last);a.$odd=!(a.$even=0===(b&1))};return{restrict:"A",multiElement:!0,transclude:"element",priority:1E3,
terminal:!0,$$tlb:!0,compile:function(f,g){var h=g.ngRepeat,k=d.$$createComment("end ngRepeat",h),l=h.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);if(!l)throw c("iexp",h);var n=l[1],m=l[2],r=l[3],s=l[4],l=n.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);if(!l)throw c("iidexp",n);var v=l[3]||l[1],w=l[2];if(r&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(r)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(r)))throw c("badident",
r);var p,y,t,z,u={$id:Fa};s?p=a(s):(t=function(a,b){return Fa(b)},z=function(a){return a});return function(a,d,f,g,l){p&&(y=function(b,c,d){w&&(u[w]=b);u[v]=c;u.$index=d;return p(a,u)});var n=T();a.$watchCollection(m,function(f){var g,m,p=d[0],s,u=T(),x,D,E,C,F,B,G;r&&(a[r]=f);if(ya(f))F=f,m=y||t;else for(G in m=y||z,F=[],f)ua.call(f,G)&&"$"!==G.charAt(0)&&F.push(G);x=F.length;G=Array(x);for(g=0;g<x;g++)if(D=f===F?g:F[g],E=f[D],C=m(D,E,g),n[C])B=n[C],delete n[C],u[C]=B,G[g]=B;else{if(u[C])throw q(G,
function(a){a&&a.scope&&(n[a.id]=a)}),c("dupes",h,C,E);G[g]={id:C,scope:void 0,clone:void 0};u[C]=!0}for(s in n){B=n[s];C=rb(B.clone);b.leave(C);if(C[0].parentNode)for(g=0,m=C.length;g<m;g++)C[g].$$NG_REMOVED=!0;B.scope.$destroy()}for(g=0;g<x;g++)if(D=f===F?g:F[g],E=f[D],B=G[g],B.scope){s=p;do s=s.nextSibling;while(s&&s.$$NG_REMOVED);B.clone[0]!=s&&b.move(rb(B.clone),null,p);p=B.clone[B.clone.length-1];e(B.scope,g,v,E,w,D,x)}else l(function(a,c){B.scope=c;var d=k.cloneNode(!1);a[a.length++]=d;b.enter(a,
null,p);p=d;B.clone=a;u[B.id]=B;e(B.scope,g,v,E,w,D,x)});n=u})}}}}],Je=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,d,c){b.$watch(c.ngShow,function(b){a[b?"removeClass":"addClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],Ce=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,d,c){b.$watch(c.ngHide,function(b){a[b?"addClass":"removeClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],Ke=La(function(a,b,d){a.$watch(d.ngStyle,function(a,
d){d&&a!==d&&q(d,function(a,c){b.css(c,"")});a&&b.css(a)},!0)}),Le=["$animate","$compile",function(a,b){return{require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(d,c,e,f){var g=[],h=[],k=[],l=[],n=function(a,b){return function(){a.splice(b,1)}};d.$watch(e.ngSwitch||e.on,function(c){var d,e;d=0;for(e=k.length;d<e;++d)a.cancel(k[d]);d=k.length=0;for(e=l.length;d<e;++d){var s=rb(h[d].clone);l[d].$destroy();(k[d]=a.leave(s)).then(n(k,d))}h.length=0;l.length=0;(g=f.cases["!"+
c]||f.cases["?"])&&q(g,function(c){c.transclude(function(d,e){l.push(e);var f=c.element;d[d.length++]=b.$$createComment("end ngSwitchWhen");h.push({clone:d});a.enter(d,f.parent(),f)})})})}}}],Me=La({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,b,d,c,e){c.cases["!"+d.ngSwitchWhen]=c.cases["!"+d.ngSwitchWhen]||[];c.cases["!"+d.ngSwitchWhen].push({transclude:e,element:b})}}),Ne=La({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,
b,d,c,e){c.cases["?"]=c.cases["?"]||[];c.cases["?"].push({transclude:e,element:b})}}),Qg=O("ngTransclude"),Pe=La({restrict:"EAC",link:function(a,b,d,c,e){d.ngTransclude===d.$attr.ngTransclude&&(d.ngTransclude="");if(!e)throw Qg("orphan",wa(b));e(function(a){a.length&&(b.empty(),b.append(a))},null,d.ngTransclude||d.ngTranscludeSlot)}}),pe=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(b,d){"text/ng-template"==d.type&&a.put(d.id,b[0].text)}}}],Rg={$setViewValue:C,$render:C},
Sg=["$element","$scope",function(a,b){var d=this,c=new Ra;d.ngModelCtrl=Rg;d.unknownOption=B(v.document.createElement("option"));d.renderUnknownOption=function(b){b="? "+Fa(b)+" ?";d.unknownOption.val(b);a.prepend(d.unknownOption);a.val(b)};b.$on("$destroy",function(){d.renderUnknownOption=C});d.removeUnknownOption=function(){d.unknownOption.parent()&&d.unknownOption.remove()};d.readValue=function(){d.removeUnknownOption();return a.val()};d.writeValue=function(b){d.hasOption(b)?(d.removeUnknownOption(),
a.val(b),""===b&&d.emptyOption.prop("selected",!0)):null==b&&d.emptyOption?(d.removeUnknownOption(),a.val("")):d.renderUnknownOption(b)};d.addOption=function(a,b){if(8!==b[0].nodeType){Qa(a,'"option value"');""===a&&(d.emptyOption=b);var g=c.get(a)||0;c.put(a,g+1);d.ngModelCtrl.$render();b[0].hasAttribute("selected")&&(b[0].selected=!0)}};d.removeOption=function(a){var b=c.get(a);b&&(1===b?(c.remove(a),""===a&&(d.emptyOption=void 0)):c.put(a,b-1))};d.hasOption=function(a){return!!c.get(a)};d.registerOption=
function(a,b,c,h,k){if(h){var l;c.$observe("value",function(a){x(l)&&d.removeOption(l);l=a;d.addOption(a,b)})}else k?a.$watch(k,function(a,e){c.$set("value",a);e!==a&&d.removeOption(e);d.addOption(a,b)}):d.addOption(c.value,b);b.on("$destroy",function(){d.removeOption(c.value);d.ngModelCtrl.$render()})}}],qe=function(){return{restrict:"E",require:["select","?ngModel"],controller:Sg,priority:1,link:{pre:function(a,b,d,c){var e=c[1];if(e){var f=c[0];f.ngModelCtrl=e;b.on("change",function(){a.$apply(function(){e.$setViewValue(f.readValue())})});
if(d.multiple){f.readValue=function(){var a=[];q(b.find("option"),function(b){b.selected&&a.push(b.value)});return a};f.writeValue=function(a){var c=new Ra(a);q(b.find("option"),function(a){a.selected=x(c.get(a.value))})};var g,h=NaN;a.$watch(function(){h!==e.$viewValue||pa(g,e.$viewValue)||(g=ha(e.$viewValue),e.$render());h=e.$viewValue});e.$isEmpty=function(a){return!a||0===a.length}}}},post:function(a,b,d,c){var e=c[1];if(e){var f=c[0];e.$render=function(){f.writeValue(e.$viewValue)}}}}}},se=["$interpolate",
function(a){return{restrict:"E",priority:100,compile:function(b,d){if(x(d.value))var c=a(d.value,!0);else{var e=a(b.text(),!0);e||d.$set("value",b.text())}return function(a,b,d){var k=b.parent();(k=k.data("$selectController")||k.parent().data("$selectController"))&&k.registerOption(a,b,d,c,e)}}}}],re=da({restrict:"E",terminal:!1}),Fc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){c&&(d.required=!0,c.$validators.required=function(a,b){return!d.required||!c.$isEmpty(b)},d.$observe("required",
function(){c.$validate()}))}}},Ec=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e,f=d.ngPattern||d.pattern;d.$observe("pattern",function(a){F(a)&&0<a.length&&(a=new RegExp("^"+a+"$"));if(a&&!a.test)throw O("ngPattern")("noregexp",f,a,wa(b));e=a||void 0;c.$validate()});c.$validators.pattern=function(a,b){return c.$isEmpty(b)||y(e)||e.test(b)}}}}},Hc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=-1;d.$observe("maxlength",function(a){a=
X(a);e=isNaN(a)?-1:a;c.$validate()});c.$validators.maxlength=function(a,b){return 0>e||c.$isEmpty(b)||b.length<=e}}}}},Gc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=0;d.$observe("minlength",function(a){e=X(a)||0;c.$validate()});c.$validators.minlength=function(a,b){return c.$isEmpty(b)||b.length>=e}}}}};v.angular.bootstrap?v.console&&console.log("WARNING: Tried to load angular more than once."):(ie(),ke(ea),ea.module("ngLocale",[],["$provide",function(a){function b(a){a+=
"";var b=a.indexOf(".");return-1==b?0:a.length-b-1}a.value("$locale",{DATETIME_FORMATS:{AMPMS:["AM","PM"],DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"],FIRSTDAYOFWEEK:6,MONTH:"January February March April May June July August September October November December".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),STANDALONEMONTH:"January February March April May June July August September October November December".split(" "),
WEEKENDRANGE:[5,6],fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",medium:"MMM d, y h:mm:ss a",mediumDate:"MMM d, y",mediumTime:"h:mm:ss a","short":"M/d/yy h:mm a",shortDate:"M/d/yy",shortTime:"h:mm a"},NUMBER_FORMATS:{CURRENCY_SYM:"$",DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{gSize:3,lgSize:3,maxFrac:3,minFrac:0,minInt:1,negPre:"-",negSuf:"",posPre:"",posSuf:""},{gSize:3,lgSize:3,maxFrac:2,minFrac:2,minInt:1,negPre:"-\u00a4",negSuf:"",posPre:"\u00a4",posSuf:""}]},id:"en-us",localeID:"en_US",pluralCat:function(a,
c){var e=a|0,f=c;void 0===f&&(f=Math.min(b(a),3));Math.pow(10,f);return 1==e&&0==f?"one":"other"}})}]),B(v.document).ready(function(){ee(v.document,yc)}))})(window);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
//# sourceMappingURL=angular.min.js.map

View File

@ -0,0 +1,238 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>eu.dnetlib</groupId>
<artifactId>literatureBrokerService</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>literatureBrokerService</name>
<description>Literature Broker Service</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.M4</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<hibernate.version>5.2.0.Final</hibernate.version>
<prometheus.version>0.2.0</prometheus.version>
</properties>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<exclusions>
<exclusion>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- H2 DB -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<!-- Commons -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<!-- Mail -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
<!-- Gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<!-- Swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.4.0</version>
</dependency>
<!-- Openaire -->
<dependency>
<groupId>eu.dnetlib</groupId>
<artifactId>dnet-openaire-broker-common</artifactId>
<version>[1.0.0-SNAPSHOT, 2.0.0)</version>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>stringtemplate</artifactId>
<version>3.2.1</version>
</dependency>
<!-- for /metrics and /health controllers -->
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_spring_boot</artifactId>
<version>${prometheus.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_hotspot</artifactId>
<version>${prometheus.version}</version>
</dependency>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_servlet</artifactId>
<version>${prometheus.version}</version>
</dependency>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_spring_web</artifactId>
<version>0.3.0</version>
</dependency>
<!-- Only for tests -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>dnet-deps</id>
<name>dnet-dependencies</name>
<url>http://maven.research-infrastructures.eu/nexus/content/repositories/dnet-deps</url>
<layout>default</layout>
</repository>
<repository>
<id>dnet45-snapshots</id>
<name>D-Net 45 Snapshots</name>
<url>http://maven.research-infrastructures.eu/nexus/content/repositories/dnet45-snapshots</url>
<layout>default</layout>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>dnet45-releases</id>
<name>D-Net 45 Releases</name>
<url>http://maven.research-infrastructures.eu/nexus/content/repositories/dnet45-releases</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
<repository>
<id>spring-milestone</id>
<name>Spring Milestone Repository</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestone</id>
<name>Spring Milestone Repository</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
</project>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,209 @@
package eu.dnetlib.lbs.controllers;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.lbs.LiteratureBrokerServiceConfiguration;
import eu.dnetlib.lbs.cron.ScheduledActions;
import eu.dnetlib.lbs.elasticsearch.NotificationRepository;
import eu.dnetlib.lbs.matchers.SubscriptionEventMatcher;
import eu.dnetlib.lbs.subscriptions.MapCondition;
import eu.dnetlib.lbs.subscriptions.NotificationFrequency;
import eu.dnetlib.lbs.subscriptions.NotificationMode;
import eu.dnetlib.lbs.subscriptions.Subscription;
import eu.dnetlib.lbs.subscriptions.SubscriptionRepository;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@RestController
@RequestMapping("/api/subscriptions")
@Api(tags = LiteratureBrokerServiceConfiguration.TAG_SUBSCRIPTIONS)
public class SubscriptionsController extends AbstractLbsController {
@Autowired
private SubscriptionRepository subscriptionRepo;
@Autowired
private NotificationRepository notificationRepo;
@Autowired
private ScheduledActions schedulerActions;
@Autowired
private SubscriptionEventMatcher subscriptionEventMatcher;
public static final Predicate<String> verifyTopic = Pattern.compile("^[a-zA-Z0-9._-]+(\\/[a-zA-Z0-9._-]+)+$").asPredicate();
public static final Predicate<String> verifyEmail = email -> {
try {
new InternetAddress(email).validate();
return true;
} catch (final AddressException e) {
return false;
}
};
@ApiOperation("Return the list of subscriptions")
@GetMapping("")
public Iterable<Subscription> listSubscriptions() {
return subscriptionRepo.findAll();
}
@ApiOperation("Return a subscription by ID")
@GetMapping("/{id}")
public Subscription getSubscription(@PathVariable final String id) {
return subscriptionRepo.findById(id).get();
}
@ApiOperation("Delete a subscription by ID and its notifications")
@DeleteMapping("/{id}")
public void deleteSubscription(@PathVariable final String id) {
subscriptionRepo.deleteById(id);
notificationRepo.deleteBySubscriptionId(id);
}
@ApiOperation("Perform a new subscription")
@PostMapping("")
public Subscription registerSubscription(@RequestBody final InSubscription inSub) {
final Subscription sub = inSub.asSubscription();
subscriptionRepo.save(sub);
return sub;
}
@ApiOperation("Delete all subscriptions and notifications")
@DeleteMapping("")
public Map<String, Object> clearSubscriptions() {
final Map<String, Object> res = new HashMap<>();
subscriptionRepo.deleteAll();
notificationRepo.deleteAll();
res.put("deleted", "all");
return res;
}
@ApiOperation("Launch the thread that produces new notifications")
@GetMapping("/startMatching")
public List<String> startMatching() {
schedulerActions.startSubscriptionEventsMatcher();
return Arrays.asList("done");
}
@ApiOperation("Launch the thread that produces new notifications by subscriptuion id")
@GetMapping("/startMatching/{subscriptionId}")
public List<String> startMatching(@PathVariable final String subscriptionId) {
final Optional<Subscription> s = subscriptionRepo.findById(subscriptionId);
if (s.isPresent()) {
subscriptionEventMatcher.startMatching(s.get());
return Arrays.asList("done");
} else {
return Arrays.asList("subscription not present");
}
}
@ApiOperation("Reset the last notification date")
@DeleteMapping("/{id}/date")
public void deleteNotificationDate(@PathVariable final String id) {
final Subscription s = subscriptionRepo.findById(id).get();
s.setLastNotificationDate(null);
subscriptionRepo.save(s);
}
@ApiOperation("Reset all the last notification dates")
@GetMapping("/resetLastNotificationDates")
public void deleteAllNotificationDates() {
for (final Subscription s : subscriptionRepo.findAll()) {
s.setLastNotificationDate(null);
subscriptionRepo.save(s);
}
}
}
class InSubscription {
private String subscriber;
private String topic;
private NotificationFrequency frequency;
private NotificationMode mode;
private List<MapCondition> conditions;
public InSubscription() {}
public InSubscription(final String subscriber, final String topic, final NotificationFrequency frequency, final NotificationMode mode,
final List<MapCondition> conditions) {
this.subscriber = subscriber;
this.topic = topic;
this.frequency = frequency;
this.mode = mode;
this.conditions = conditions;
}
public String getSubscriber() {
return subscriber;
}
public void setSubscriber(final String subscriber) {
this.subscriber = subscriber;
}
public String getTopic() {
return topic;
}
public void setTopic(final String topic) {
this.topic = topic;
}
public NotificationFrequency getFrequency() {
return frequency;
}
public void setFrequency(final NotificationFrequency frequency) {
this.frequency = frequency;
}
public NotificationMode getMode() {
return mode;
}
public void setMode(final NotificationMode mode) {
this.mode = mode;
}
public List<MapCondition> getConditions() {
return conditions;
}
public void setConditions(final List<MapCondition> conditions) {
this.conditions = conditions;
}
public Subscription asSubscription() {
if (StringUtils.isBlank(subscriber)) { throw new IllegalArgumentException("subscriber is empty"); }
if (StringUtils.isBlank(topic)) { throw new IllegalArgumentException("topic is empty"); }
if (!SubscriptionsController.verifyEmail.test(subscriber)) { throw new IllegalArgumentException("Invalid email: " + subscriber); }
if (!SubscriptionsController.verifyTopic.test(topic)) { throw new IllegalArgumentException("Invalid topic: " + topic); }
final String subscriptionId = "sub-" + UUID.randomUUID();
return new Subscription(subscriptionId, subscriber, topic, frequency, mode, null, new Date(), conditions);
}
}

View File

@ -0,0 +1,7 @@
package eu.dnetlib.lbs.topics;
import org.springframework.data.repository.CrudRepository;
public interface TopicTypeRepository extends CrudRepository<TopicType, String> {
}

View File

@ -0,0 +1,39 @@
#!/usr/bin/perl
use strict;
use utf8;
use Net::RabbitMQ;
binmode STDOUT, ':utf8';
my $host = 'localhost';
my $user = 'admin';
my $passwd = 'driver';
my $queue = 'test_queue';
my @files = @ARGV;
die "Missing input file" unless (@files);
foreach my $inputFile (@files) {
die "File does not exist" unless (-e $inputFile);
open(my $fh, '<:encoding(UTF-8)', $inputFile) or die "Could not open file '$inputFile' $!";
print "Populating $queue from file $inputFile...\n";
my $mq = Net::RabbitMQ->new();
$mq->connect($host, { user => $user, password => $passwd });
$mq->channel_open(1);
foreach my $line (<$fh>) {
$mq->publish(1, $queue, $line) if ($line);
}
close($fh);
$mq->disconnect();
}
print "Done.\n\n";
1;

View File

@ -0,0 +1,34 @@
package eu.dnetlib.lbs.controllers.objects;
public class ThreadStatus implements Comparable<ThreadStatus> {
private final String name;
private final String state;
private final boolean alive;
public ThreadStatus(final String name, final String state, final boolean alive) {
this.name = name;
this.state = state;
this.alive = alive;
}
public String getName() {
return this.name;
}
public String getState() {
return this.state;
}
public boolean isAlive() {
return this.alive;
}
@Override
public int compareTo(final ThreadStatus t) {
if (this.name == null) { return -1; }
if (t.name == null) { return 1; }
return this.name.compareTo(t.name);
}
}

View File

@ -0,0 +1,5 @@
package eu.dnetlib.lbs.subscriptions;
public enum NotificationMode {
MOCK, EMAIL
}

View File

@ -0,0 +1,107 @@
package eu.dnetlib.lbs.subscriptions;
import java.util.Arrays;
import java.util.Date;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import eu.dnetlib.lbs.utils.DateParser;
public class ConditionParams {
private String value;
private String otherValue;
public ConditionParams() {}
public ConditionParams(final String value, final String otherValue) {
this.value = value;
this.otherValue = otherValue;
}
public String getValue() {
return this.value;
}
public void setValue(final String value) {
this.value = value;
}
public String getOtherValue() {
return this.otherValue;
}
public void setOtherValue(final String otherValue) {
this.otherValue = otherValue;
}
public boolean verify(final String s, final ConditionOperator operator) {
final Set<String> set = tokenize(s);
switch (operator) {
case EXACT:
return StringUtils.equalsIgnoreCase(s, this.value);
case MATCH_ANY:
return tokenize(this.value).stream().anyMatch(set::contains);
case MATCH_ALL:
return tokenize(this.value).stream().allMatch(set::contains);
case RANGE:
return (this.value == null || s.compareTo(this.value) >= 0) && (this.otherValue == null || s.compareTo(this.otherValue) <= 0);
default:
}
return true;
}
public boolean verify(final boolean b, final ConditionOperator operator) {
switch (operator) {
case EXACT:
case MATCH_ANY:
case MATCH_ALL:
return b == (this.value.equalsIgnoreCase("true"));
default:
return false;
}
}
public boolean verify(final double n, final ConditionOperator operator) {
switch (operator) {
case EXACT:
case MATCH_ANY:
case MATCH_ALL:
return n == NumberUtils.toDouble(this.value, 0);
case RANGE:
return (n >= NumberUtils.toDouble(this.value, Double.MIN_VALUE)
&& (n <= NumberUtils.toDouble(this.otherValue, Double.MAX_VALUE)));
default:
return false;
}
}
public boolean verify(final Date date, final ConditionOperator operator) {
if (date == null) { return false; }
switch (operator) {
case EXACT:
case MATCH_ANY:
case MATCH_ALL:
return date.getTime() == DateParser.parse(this.value).getTime();
case RANGE:
final Date min = DateParser.parse(this.value);
final Date max = DateParser.parse(this.otherValue);
final long t = date.getTime();
return (min == null || t >= min.getTime()) && (max == null || t <= max.getTime());
default:
return false;
}
}
private Set<String> tokenize(final String s) {
return Arrays.stream(s.replaceAll("[^a-zA-Z0-9]", " ").toLowerCase().split(" "))
.map(String::trim)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,50 @@
package eu.dnetlib.lbs.openaire;
import java.util.List;
import eu.dnetlib.broker.objects.OpenAireEventPayload;
public class EventsPage {
private final String datasource;
private final String topic;
private final long currPage;
private final long totalPages;
private final long total;
private final List<OpenAireEventPayload> values;
public EventsPage(final String datasource, final String topic, final long currPage, final long totalPages, final long total,
final List<OpenAireEventPayload> values) {
this.datasource = datasource;
this.topic = topic;
this.currPage = currPage;
this.totalPages = totalPages;
this.total = total;
this.values = values;
}
public String getDatasource() {
return this.datasource;
}
public String getTopic() {
return this.topic;
}
public long getCurrPage() {
return this.currPage;
}
public long getTotalPages() {
return this.totalPages;
}
public long getTotal() {
return this.total;
}
public List<OpenAireEventPayload> getValues() {
return this.values;
}
}

View File

@ -0,0 +1,71 @@
package eu.dnetlib.lbs.utils;
import java.util.Date;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.google.gson.Gson;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.events.input.EventMessage;
public class JsonMessageToEventFunction implements Function<String, Event> {
private final Gson gson = new Gson();
private static final Log log = LogFactory.getLog(JsonMessageToEventFunction.class);
@Override
public Event apply(final String json) {
try {
final EventMessage msg = gson.fromJson(json, EventMessage.class);
if (msg == null || msg.hasNulls()) {
log.warn("Message is null or some required field is null");
return null;
}
final long now = new Date().getTime();
final Event event = new Event();
event.setEventId("event-" + DigestUtils.md5Hex(msg.getTopic()).substring(0, 6) + "-" + calculatePayloadHash(msg.getPayload()));
event.setProducerId(msg.getProducerId());
event.setPayload(msg.getPayload());
event.setMap(msg.getMap()
.entrySet()
.stream()
.filter(e -> e.getValue().asObject() != null)
.collect(Collectors.toMap(
e -> e.getKey(),
e -> e.getValue().asObject())));
event.setTopic(msg.getTopic());
event.setCreationDate(now);
event.setExpiryDate(calculateExpiryDate(now, msg.getTthDays()));
event.setInstantMessage(msg.getTthDays() == 0);
return event;
} catch (final Throwable e) {
log.warn("Error converting json: " + json, e);
return null;
}
}
private Long calculateExpiryDate(final long now, final int ttlDays) {
if (ttlDays < 0) {
return null;
} else if (ttlDays == 0) {
return now;
} else {
final long duration = ttlDays * 24 * 60 * 60 * 1000;
return now + duration;
}
}
private static String calculatePayloadHash(final String payload) {
// TODO : the payload should be normalized before hashing it
return DigestUtils.md5Hex(payload);
}
}

View File

@ -0,0 +1,24 @@
package eu.dnetlib.lbs.utils;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Component;
@Component
public class ThreadManager {
private final List<Thread> threads = new ArrayList<>();
public void newThread(final String name, final Runnable r) {
final Thread thread = new Thread(r, name);
this.threads.add(thread);
thread.start();
}
public List<Thread> getThreads() {
return this.threads;
}
}

View File

@ -0,0 +1,2 @@
/*! angularjs-slider - v5.4.3 - (c) Rafal Zajac <rzajac@gmail.com>, Valentin Hervieu <valentin@hervieu.me>, Jussi Saarivirta <jusasi@gmail.com>, Angelin Sirbu <angelin.sirbu@gmail.com> - https://github.com/angular-slider/angularjs-slider - 2016-08-07 */
.rzslider{position:relative;display:inline-block;width:100%;height:4px;margin:35px 0 15px 0;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.rzslider.with-legend{margin-bottom:40px}.rzslider[disabled]{cursor:not-allowed}.rzslider[disabled] .rz-pointer{cursor:not-allowed;background-color:#d8e0f3}.rzslider span{position:absolute;display:inline-block;white-space:nowrap}.rzslider .rz-base{width:100%;height:100%;padding:0}.rzslider .rz-bar-wrapper{left:0;z-index:1;width:100%;height:32px;padding-top:16px;margin-top:-16px;box-sizing:border-box}.rzslider .rz-bar-wrapper.rz-draggable{cursor:move}.rzslider .rz-bar{left:0;z-index:1;width:100%;height:4px;background:#d8e0f3;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.rzslider .rz-bar.rz-selection{z-index:2;background:#0db9f0;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.rzslider .rz-pointer{top:-14px;z-index:3;width:32px;height:32px;cursor:pointer;background-color:#0db9f0;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}.rzslider .rz-pointer:after{position:absolute;top:12px;left:12px;width:8px;height:8px;background:#fff;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;content:''}.rzslider .rz-pointer:hover:after{background-color:#fff}.rzslider .rz-pointer.rz-active{z-index:4}.rzslider .rz-pointer.rz-active:after{background-color:#451aff}.rzslider .rz-bubble{bottom:16px;padding:1px 3px;color:#55637d;cursor:default}.rzslider .rz-bubble.rz-selection{top:16px}.rzslider .rz-bubble.rz-limit{color:#55637d}.rzslider .rz-ticks{position:absolute;top:-3px;left:0;z-index:1;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;height:0;padding:0 11px;margin:0;list-style:none;box-sizing:border-box;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.rzslider .rz-ticks .rz-tick{width:10px;height:10px;text-align:center;cursor:pointer;background:#d8e0f3;border-radius:50%}.rzslider .rz-ticks .rz-tick.rz-selected{background:#0db9f0}.rzslider .rz-ticks .rz-tick .rz-tick-value{position:absolute;top:-30px;transform:translate(-50%,0)}.rzslider .rz-ticks .rz-tick .rz-tick-legend{position:absolute;top:24px;max-width:50px;white-space:normal;transform:translate(-50%,0)}.rzslider .rz-ticks.rz-ticks-values-under .rz-tick-value{top:initial;bottom:-40px}.rzslider.rz-vertical{position:relative;width:4px;height:100%;padding:0;margin:0 20px;vertical-align:baseline}.rzslider.rz-vertical .rz-base{width:100%;height:100%;padding:0}.rzslider.rz-vertical .rz-bar-wrapper{top:auto;left:0;width:32px;height:100%;padding:0 0 0 16px;margin:0 0 0 -16px}.rzslider.rz-vertical .rz-bar{bottom:0;left:auto;width:4px;height:100%}.rzslider.rz-vertical .rz-pointer{top:auto;bottom:0;left:-14px!important}.rzslider.rz-vertical .rz-bubble{bottom:0;left:16px!important;margin-left:3px}.rzslider.rz-vertical .rz-bubble.rz-selection{top:auto;left:16px!important}.rzslider.rz-vertical .rz-ticks{top:0;left:-3px;z-index:1;width:0;height:100%;padding:11px 0;-webkit-flex-direction:column-reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}.rzslider.rz-vertical .rz-ticks .rz-tick{vertical-align:middle}.rzslider.rz-vertical .rz-ticks .rz-tick .rz-tick-value{top:initial;left:22px;transform:translate(0,-28%)}.rzslider.rz-vertical .rz-ticks .rz-tick .rz-tick-legend{top:initial;right:24px;max-width:none;white-space:nowrap;transform:translate(0,-28%)}.rzslider.rz-vertical .rz-ticks.rz-ticks-values-under .rz-tick-value{right:12px;bottom:initial;left:initial}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
package eu.dnetlib.lbs.events.input;
public enum MapValueType {
STRING, INTEGER, FLOAT, DATE, BOOLEAN, LIST_STRING, LIST_INTEGER, LIST_FLOAT, LIST_DATE, LIST_BOOLEAN;
}

View File

@ -0,0 +1,399 @@
var lbsModule = angular.module('lbs', ['ngRoute', 'lbsControllers']);
lbsModule.config([
'$routeProvider',
function($routeProvider) {
$routeProvider
.when('/summary', { templateUrl: 'html/summary.html', controller: 'lbsSummaryCtrl' })
.when('/topics', { templateUrl: 'html/topics.html', controller: 'lbsTopicsCtrl' })
.when('/subscriptions', { templateUrl: 'html/subscriptions.html', controller: 'lbsSubscriptionsCtrl' })
.when('/events/:condition', { templateUrl: 'html/filterEvents.html', controller: 'lbsFilterEventsCtrl' })
.when('/events/:page/:size/:topic*', { templateUrl: 'html/eventsByTopic.html', controller: 'lbsEventsByTopicCtrl' })
.otherwise({ redirectTo: '/summary' });
}
]);
lbsModule.controller('moduleMenuCtrl', function ($scope, $http, $location) {
$scope.tools = [];
$http.get('ajax/externalTools').success(function(data) {
$scope.tools = data;
}).error(function() {
alert("error");
});
$scope.isActive = function(regex) {
var re = new RegExp(regex);
return re.test($location.path());
}
});
lbsModule.directive('twoStepButton', function($timeout) {
return {
restrict: 'E',
scope: {
'label' : '@',
'action' : '&'
},
templateUrl: 'html/twoStepButton.template.html',
link: function(scope, iElement, iAttrs, ctrl) {
scope.status = 0;
scope.doAction = function() {
if (scope.promise) {
$timeout.cancel(scope.promise);
}
if (scope.status == 0) {
scope.status = 1;
scope.promise = $timeout(function() { scope.status = 0 }, 4000);
} else {
scope.status = 0;
scope.action();
}
}
}
}
});
lbsModule.directive('adminOperation', function($timeout) {
return {
restrict: 'A',
replace: true,
scope: {
'label' : '@',
'description' : '@',
'alert' : '@',
'action' : '&'
},
templateUrl: 'html/adminOperation.template.html',
link: function(scope, iElement, iAttrs, ctrl) {
scope.status = 0;
scope.doAction = function() {
if (scope.promise) {
$timeout.cancel(scope.promise);
}
if (scope.status == 0) {
scope.status = 1;
scope.promise = $timeout(function() { scope.status = 0 }, 4000);
} else {
scope.status = 0;
$('.modal').modal('hide');
scope.action();
}
}
}
}
});
//---------------------------------------------------------------------------------------------------------------------//
var lbsControllers = angular.module('lbsControllers', []);
lbsControllers.controller('lbsSummaryCtrl', function ($scope, $http, $sce, $location, $routeParams) {
$scope.summary = {};
$scope.refresh = function() {
$http.get('ajax/summary').success(function(data) {
$scope.summary = data;
}).error(function() {
alert("error");
});
};
$scope.resetCounters = function() {
$http.get('ajax/resetCounters').success(function(data) {
$scope.summary = data;
}).error(function() {
alert("error");
});
};
$scope.deleteAllSubscriptions = function() {
$http.delete('api/subscriptions').success(function(data) {
$scope.summary.totals.subscriptions = 0;
}).error(function() {
alert("error");
});
};
$scope.deleteAllEvents = function() {
$http.delete('api/events/all').success(function(data) {
$scope.summary.totals.events = 0;
}).error(function() {
alert("error");
});
};
$scope.deleteExpiredEvents = function() {
$http.delete('api/events/expired').success(function(data) {
$scope.summary.totals.events = $scope.summary.totals.events - data.deleted;
}).error(function() {
alert("error");
});
};
$scope.deleteAllTopicTypes = function() {
$http.delete('api/topic-types').success(function(data) {
$scope.summary.totals.topics = 0;
}).error(function() {
alert("error");
});
};
$scope.deleteAllNotifications = function() {
$http.delete('api/notifications').success(function(data) {
$scope.summary.totals.notifications = 0;
}).error(function() {
alert("error");
});
};
$scope. startSubscriptionEventsMatcher = function() {
$http.get('api/subscriptions/startMatching').success(function(data) {
alert("matching is running");
}).error(function() {
alert("error");
});
};
$scope.refresh();
});
//---------------------------------------------------------------------------------------------------------------------//
lbsControllers.controller('lbsSubscriptionsCtrl', function ($scope, $http, $sce, $location, $routeParams) {
$scope.list = [];
$scope.availableTopics = [];
$scope.subscriber = '';
$scope.topic = '';
$scope.frequency = '';
$scope.topicType = '';
$scope.topicParts = [];
$scope.topicConditions = [];
$scope.topicRegex = '';
$http.get('api/topic-types').success(function(data) {
$scope.availableTopics = data;
}).error(function() {
alert("error");
});
$scope.refresh = function() {
$http.get('api/subscriptions').success(function(data) {
$scope.list = data;
}).error(function() {
alert("error");
});
};
$scope.resetFormFields = function() {
$scope.subscriber = '';
$scope.topic = '';
$scope.frequency = '';
$scope.topicType = '';
$scope.topicParts = [];
$scope.topicConditions = [];
$scope.topiceRegex = '';
}
$scope.onSelectTopicType = function(topicType) {
$scope.topicParts = [];
$scope.topicRegex = topicType.regex;
angular.forEach(topicType.expression.split('/'), function(val) {
if (val.length > 2 && val.charAt(0) == '<' && val.charAt(val.length - 1) == '>') {
$scope.topicParts.push({ 'name' : val, variable : true, 'value' : '' });
} else {
$scope.topicParts.push({ 'name' : val, variable : false, 'value' : val });
}
});
$scope.updateTopic();
}
$scope.updateTopic = function() {
t = '';
for (var i=0; i < $scope.topicParts.length; i++) {
if ($scope.topicParts[i].value) {
if (i == 0) {
t += $scope.topicParts[i].value
} else {
t += '/' + $scope.topicParts[i].value;
}
} else {
$scope.topic = '';
return;
}
}
$scope.topic = t;
}
$scope.addCondition = function() {
$scope.topicConditions.push({
'field' : '',
'fieldType' : '',
'operator' : '',
'listParams' : [ { 'value' : '', 'otherValue': '' } ]
});
}
$scope.removeCondition = function(idx) {
$scope.topicConditions.splice(idx, 1);
}
$scope.add = function(subscriber, topic, frequency, mode, conditions) {
$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
$http.post('api/subscriptions', {
'subscriber' : subscriber,
'topic' : topic,
'frequency' : frequency,
'mode' : mode,
'conditions' : conditions
}).success(function(data) {
$scope.list.push(data);
}).error(function() {
alert("error");
});
};
$scope.startMatching = function(id) {
$http.get('api/subscriptions/startMatching/' + id).success(function(data) {
alert("Matching in queue");
}).error(function() {
alert("error");
});
};
$scope.resetNotificationDate = function(id) {
if (confirm("Are you sure ?")) {
$http.delete('api/subscriptions/' + id + '/date').success(function(data) {
$scope.refresh();
}).error(function() {
alert("error");
});
}
};
$scope.remove = function(id) {
if (confirm("Are you sure ?")) {
$http.delete('api/subscriptions/' + id).success(function(data) {
$scope.refresh();
}).error(function() {
alert("error");
});
}
};
$scope.refresh();
});
//---------------------------------------------------------------------------------------------------------------------//
lbsControllers.controller('lbsTopicsCtrl', function ($scope, $http, $sce, $location, $routeParams) {
$scope.list = [];
$scope.segments = [];
$scope.refresh = function() {
$http.get('api/topic-types').success(function(data) {
$scope.list = data;
}).error(function() {
alert("error");
});
};
$scope.resetFormFields = function() {
$scope.segments = [{},{}];
$scope.topicName = '';
$scope.topicProducerId = '';
$scope.topicExpression = '';
$scope.topicMapKeys = [];
}
$scope.updateTopicExpression = function() {
var s = ""
for (var i=0; i<$scope.segments.length; i++) {
if ($scope.segments[i].type && $scope.segments[i].value) {
if (i > 0) { s += '/'; }
if ($scope.segments[i].type == 'Fixed') {
s += $scope.segments[i].value;
} else {
s += '<' + $scope.segments[i].value + '>';
}
} else {
$scope.topicExpression = '';
return;
}
}
$scope.topicExpression = s;
}
$scope.add = function(name, producerId, expression, mapKeys) {
$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8";
$http.post('api/topic-types/add', $.param({
'name' : name,
'expression' : expression,
'producerId' : producerId,
'mapKeys' : mapKeys.join()
})).success(function(data) {
$scope.list.push(data);
}).error(function() {
alert("error");
});
};
$scope.refresh();
});
//---------------------------------------------------------------------------------------------------------------------//
lbsControllers.controller('lbsFilterEventsCtrl', function ($scope, $http, $sce, $location, $routeParams) {
$scope.list = [];
$scope.cond = $routeParams.condition;
$scope.onlyWithSubscriptions = ($scope.cond != 'all');
$scope.reloadWithCondition = function() {
$location.url($scope.onlyWithSubscriptions ? '/events/withSubscriptions' : '/events/all');
}
$scope.refresh = function() {
var url = $scope.onlyWithSubscriptions ? "api/events/topics/withSubscriptions" : "api/events/topics/all";
$http.get(url).success(function(data) {
$scope.list = data;
}).error(function() {
alert("error");
});
};
$scope.refresh();
});
//---------------------------------------------------------------------------------------------------------------------//
lbsControllers.controller('lbsEventsByTopicCtrl', function ($scope, $http, $sce, $location, $routeParams) {
$scope.list = [];
$scope.page = $routeParams.page;
$scope.pageSize = $routeParams.size;
$scope.topic = $routeParams.topic;
$scope.refresh = function() {
$http.get('api/events/byTopic/' + $scope.page + '/' + $scope.pageSize + '?topic=' + $scope.topic).success(function(data) {
$scope.list = data;
}).error(function() {
alert("error");
});
};
$scope.refresh();
});

View File

@ -0,0 +1,12 @@
package eu.dnetlib.lbs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class LiteratureBrokerServiceApplication {
public static void main(final String[] args) {
SpringApplication.run(LiteratureBrokerServiceApplication.class, args);
}
}

View File

@ -0,0 +1,56 @@
package eu.dnetlib.lbs.utils;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.topics.TopicType;
import eu.dnetlib.lbs.topics.TopicTypeRepository;
public class EventVerifier implements Predicate<Event> {
private final TopicTypeRepository topicTypeRepo;
private final long MAX_DELAY = 5000; // 10 seconds
private Instant last = Instant.EPOCH;
private List<Predicate<Event>> validators = new ArrayList<>();
private static final Log log = LogFactory.getLog(EventVerifier.class);
public EventVerifier(final TopicTypeRepository topicTypeRepo) {
this.topicTypeRepo = topicTypeRepo;
}
@Override
public boolean test(final Event event) {
if (event == null || StringUtils.isBlank(event.getProducerId())) { return false; }
// TODO: La lista delle topicTypes deve essere in funzione del producer
final Instant now = Instant.now();
if (Duration.between(this.last, now).toMillis() > this.MAX_DELAY) {
log.info("Updating the list of topic regex");
final Iterable<TopicType> iter = this.topicTypeRepo.findAll();
this.validators = StreamSupport.stream(iter.spliterator(), false)
.map(TopicType::asValidator)
.collect(Collectors.toList());
}
try {
return this.validators.stream().anyMatch(p -> p.test(event));
} catch (final Throwable e) {
return false;
} finally {
this.last = now;
}
}
}

View File

@ -0,0 +1,136 @@
package eu.dnetlib.lbs.elasticsearch;
import java.util.Date;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import eu.dnetlib.lbs.subscriptions.Subscription;
@Document(indexName = "#{elasticSearchProperties.notificationsIndexName}", type = "#{elasticSearchProperties.notificationsIndexType}")
public class Notification {
@Id
private String notificationId;
@Field(type = FieldType.Keyword)
private String subscriptionId;
@Field(type = FieldType.Keyword)
private String producerId;
@Field(type = FieldType.Keyword)
private String eventId;
@Field(type = FieldType.Keyword)
private String topic;
@Field(type = FieldType.Text)
private String payload;
@Field(type = FieldType.Nested)
private Map<String, Object> map;
@Field(type = FieldType.Long)
private Long date;
public Notification() {}
public Notification(final String notificationId, final String subscriptionId, final String producerId, final String eventId, final String topic,
final String payload,
final Map<String, Object> map, final Long date) {
super();
this.notificationId = notificationId;
this.subscriptionId = subscriptionId;
this.producerId = producerId;
this.eventId = eventId;
this.topic = topic;
this.payload = payload;
this.map = map;
this.date = date;
}
public Notification(final Subscription s, final Event e) {
this(Notification.calculateId(s, e),
s.getSubscriptionId(),
e.getProducerId(),
e.getEventId(),
e.getTopic(),
e.getPayload(),
e.getMap(),
new Date().getTime());
}
public String getNotificationId() {
return notificationId;
}
public void setNotificationId(final String notificationId) {
this.notificationId = notificationId;
}
public String getSubscriptionId() {
return subscriptionId;
}
public void setSubscriptionId(final String subscriptionId) {
this.subscriptionId = subscriptionId;
}
public String getProducerId() {
return producerId;
}
public void setProducerId(final String producerId) {
this.producerId = producerId;
}
public String getEventId() {
return eventId;
}
public void setEventId(final String eventId) {
this.eventId = eventId;
}
public Long getDate() {
return date;
}
public void setDate(final Long date) {
this.date = date;
}
public String getTopic() {
return topic;
}
public void setTopic(final String topic) {
this.topic = topic;
}
public String getPayload() {
return payload;
}
public void setPayload(final String payload) {
this.payload = payload;
}
public Map<String, Object> getMap() {
return map;
}
public void setMap(final Map<String, Object> map) {
this.map = map;
}
public static String calculateId(final Subscription s, final Event e) {
return "ntf-" + DigestUtils.md5Hex(s.getSubscriptionId() + "@@@" + e.getEventId());
}
}

View File

@ -0,0 +1,104 @@
package eu.dnetlib.lbs.events.input;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import eu.dnetlib.lbs.utils.DateParser;
public class MapValue {
private MapValueType type = MapValueType.STRING;
private Object value = "";
private static final Log log = LogFactory.getLog(MapValue.class);
public MapValue() {}
public MapValue(final MapValueType type, final Object value) {
this.type = type;
this.value = value;
}
public MapValueType getType() {
return this.type;
}
public void setType(final MapValueType type) {
this.type = type;
}
public Object getValue() {
return this.value;
}
public void setValue(final Object value) {
this.value = value;
}
public Object asObject() {
if (this.value == null) {
log.warn("Object is NULL");
return null;
}
try {
switch (this.type) {
case STRING:
case INTEGER:
case FLOAT:
case BOOLEAN:
case DATE:
return asSimpleObject(getType(), getValue().toString());
case LIST_STRING:
return ((List<?>) getValue()).stream().map(o -> asSimpleObject(MapValueType.STRING, o.toString())).collect(Collectors.toList());
case LIST_INTEGER:
return ((List<?>) getValue()).stream().map(o -> asSimpleObject(MapValueType.INTEGER, o.toString())).collect(Collectors.toList());
case LIST_FLOAT:
return ((List<?>) getValue()).stream().map(o -> asSimpleObject(MapValueType.FLOAT, o.toString())).collect(Collectors.toList());
case LIST_BOOLEAN:
return ((List<?>) getValue()).stream().map(o -> asSimpleObject(MapValueType.BOOLEAN, o.toString())).collect(Collectors.toList());
case LIST_DATE:
return ((List<?>) getValue()).stream().map(o -> asSimpleObject(MapValueType.DATE, o.toString())).collect(Collectors.toList());
default:
log.warn("Invalid type: " + this.type);
return null;
}
} catch (final Exception e) {
log.warn("Error parsing value: " + this);
return null;
}
}
private Object asSimpleObject(final MapValueType type, final String s) {
try {
switch (type) {
case STRING:
return s.toString();
case INTEGER:
return s.contains(".") ? NumberUtils.toLong(StringUtils.substringBefore(s, "."), 0) : NumberUtils.toLong(s, 0);
case FLOAT:
return NumberUtils.toDouble(s, 0);
case BOOLEAN:
return BooleanUtils.toBoolean(s);
case DATE:
return DateParser.parse(s);
default:
log.warn("Unmamaged type: " + type);
return null;
}
} catch (final Exception e) {
log.warn("Error parsing value: " + s + " - type: " + type);
return null;
}
}
@Override
public String toString() {
return String.format("[ type; %s, value: %s ]", getType(), getValue());
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,75 @@
package eu.dnetlib.lbs.elasticsearch;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Component;
import eu.dnetlib.lbs.properties.ElasticSearchProperties;
@Component
public class EventStatsManager {
@Autowired
private ElasticsearchOperations esOperations;
@Autowired
private ElasticSearchProperties elasticSearchProperties;
private static final Log log = LogFactory.getLog(EventStatsManager.class);
public class BrowseEntry {
private final String value;
private final long count;
public BrowseEntry(final String value, final long count) {
this.value = value;
this.count = count;
}
public String getValue() {
return value;
}
public long getCount() {
return count;
}
}
public List<BrowseEntry> browseTopics() {
final String term = "topic";
final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchAllQuery())
.withSearchType(SearchType.DEFAULT)
.addAggregation(AggregationBuilders.terms(term).field(term).size(1000).minDocCount(1))
.build();
final SearchHits<Event> hits = esOperations.search(searchQuery, Event.class, IndexCoordinates.of(elasticSearchProperties.getEventsIndexName()));
final Aggregations aggregations = hits.getAggregations();
return ((ParsedStringTerms) aggregations.asMap().get(term)).getBuckets()
.stream()
.map(b -> new BrowseEntry(b.getKeyAsString(), b.getDocCount()))
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,145 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
set MAVEN_CMD_LINE_ARGS=%*
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar""
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

View File

@ -0,0 +1,26 @@
package eu.dnetlib.lbs.openaire;
public class BrowseEntry implements Comparable<BrowseEntry> {
public final String value;
public final long size;
public BrowseEntry(final String value, final long size) {
this.value = value;
this.size = size;
}
public String getValue() {
return this.value;
}
public long getSize() {
return this.size;
}
@Override
public int compareTo(final BrowseEntry bv) {
return Long.compare(getSize(), bv.getSize());
}
}

View File

@ -0,0 +1,54 @@
<div class="well col-xs-12 col-md-8 col-lg-6" ng-hide="eventsPage">
<input type="text" class="form-control" placeholder="filter..." ng-model="filterDatasource" />
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>Datasource</th>
<th class="text-right" style="white-space: nowrap;"># events</th>
</tr>
</thead>
<tr ng-repeat="ds in datasources | filter: filterDatasource">
<td><a href="javascript:void(0)" ng-click="browseTopics(ds.value)" data-target="#showTopicsModal" data-toggle="modal">{{ds.value}}</a></td>
<td class="text-right">{{ds.size}}</td>
</tr>
</table>
</div>
<div id="showTopicsModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">{{dsName}}</h4>
</div>
<div class="modal-body">
<table class="table">
<thead>
<tr>
<th>Topic</th>
<th class="text-right" style="white-space: nowrap;">#</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="t in topics">
<td><a href="javascript:void(0)" ng-click="gotoEventsPage(dsName, t.value)" data-dismiss="modal">{{t.value}}</a></td>
<td class="text-right">{{t.size}}</td>
</tr>
<tr>
<th>Total</th>
<th class="text-right">{{calculateTotal(topics)}}</th>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<back-top><span class="glyphicon glyphicon-chevron-up"></span> top</back-top>

View File

@ -0,0 +1,60 @@
#!/bin/bash
echo
echo "Fixing events index..."
curl -XPUT -H 'Content-Type: application/json' 'http://localhost:9200/events/_mapping' -d '
{
"properties" : {
"map": {
"type": "nested",
"properties": {
"target_datasource_id": {
"type": "keyword",
"index": true
},
"target_datasource_name": {
"type": "keyword",
"index": true
}
}
}
}
}'
echo
echo
echo "Fixing notifications index..."
curl -XPUT -H 'Content-Type: application/json' 'http://localhost:9200/notifications/_mapping' -d '
{
"properties" : {
"map": {
"type": "nested",
"properties": {
"target_datasource_id": {
"type": "keyword",
"index": true
},
"target_datasource_name": {
"type": "keyword",
"index": true
}
}
}
}
}'
echo
echo
echo "Registering topic type: ENRICH/<cond>/<field>..."
curl "http://localhost:8080/api/topic-types/add" -d "name=ENRICH&expression=ENRICH%2F%3Ccond%3E%2F%3Cfield%3E&producerId=OpenAIRE&mapKeys=target_datasource_name"
echo
echo
echo "Registering topic type: ENRICH/<cond>/<field>/<subfield>..."
curl "http://localhost:8080/api/topic-types/add" -d "name=ENRICH_WITH_SUBFIELD&expression=ENRICH%2F%3Ccond%3E%2F%3Cfield%3E%2F%3Csubfield%3E&producerId=OpenAIRE&mapKeys=target_datasource_name"
echo
echo
echo "Registering topic type: ADD/<field>..."
curl "http://localhost:8080/api/topic-types/add" -d "name=ADD&expression=ADD%2F%3Cfield%3E&producerId=OpenAIRE&mapKeys=target_datasource_name"
echo
echo
echo

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,135 @@
package eu.dnetlib.lbs.events.output;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Date;
import java.util.Properties;
import java.util.stream.Collectors;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.antlr.stringtemplate.StringTemplate;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import com.google.common.base.Splitter;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.properties.EmailProperties;
import eu.dnetlib.lbs.subscriptions.NotificationMode;
import eu.dnetlib.lbs.subscriptions.Subscription;
@Component
public class EmailDispatcher extends AbstractNotificationDispatcher<Message> {
@Autowired
private EmailProperties props;
@Value("${lbs.mail.message.template}")
private Resource emailTemplate;
public static final int MAX_NUMBER_OF_EVENTS = 20;
private static final Log log = LogFactory.getLog(EmailDispatcher.class);
@Override
protected Message prepareAction(final Subscription subscription, final Event... events) throws Exception {
if (subscription == null || StringUtils.isBlank(subscription.getSubscriber())) {
log.warn("Invalid subscription");
throw new IllegalArgumentException("Invalid subscription");
}
if (events.length == 0) {
log.warn("Event list is empty");
throw new IllegalArgumentException("Event list is empty");
}
final String topics = Arrays.stream(events).map(e -> e.getTopic()).distinct().collect(Collectors.joining(", "));
final Session session = Session.getInstance(obtainProperties(), obtainAuthenticator());
final MimeMessage mimeMessage = new MimeMessage(session);
mimeMessage.setFrom(new InternetAddress(props.getFrom(), props.getFromName()));
mimeMessage.setSubject("Notification for topic(s): " + topics);
mimeMessage.setContent(generateMailContent(subscription, events), "text/html; charset=utf-8");
mimeMessage.setSentDate(new Date());
mimeMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(subscription.getSubscriber()));
if (StringUtils.isNotBlank(props.getCc())) {
for (final String aCC : Splitter.on(",").omitEmptyStrings().trimResults().split(props.getCc())) {
mimeMessage.addRecipient(Message.RecipientType.CC, new InternetAddress(aCC));
}
}
log.info("Created mail, to: " + subscription.getSubscriber());
return mimeMessage;
}
private String generateMailContent(final Subscription subscription, final Event... events) throws IOException {
final StringTemplate st = new StringTemplate(IOUtils.toString(emailTemplate.getInputStream(), StandardCharsets.UTF_8));
st.setAttribute("sub", subscription);
st.setAttribute("total", events.length);
st.setAttribute("max", MAX_NUMBER_OF_EVENTS);
if (events.length > MAX_NUMBER_OF_EVENTS) {
st.setAttribute("events", Arrays.copyOfRange(events, 0, MAX_NUMBER_OF_EVENTS));
} else {
st.setAttribute("events", events);
}
return st.toString();
}
@Override
protected void performAction(final Message message) throws Exception {
log.info("Sending mail to " + Arrays.toString(message.getAllRecipients()) + "...");
Transport.send(message);
log.info("...sent");
}
private Properties obtainProperties() {
final Properties p = new Properties();
p.put("mail.transport.protocol", "smtp");
p.put("mail.smtp.host", props.getSmtpHost());
p.put("mail.smtp.port", props.getSmtpPort());
p.put("mail.smtp.auth", Boolean.toString(StringUtils.isNotBlank(props.getSmtpUser())));
return p;
}
private Authenticator obtainAuthenticator() {
if (StringUtils.isBlank(props.getSmtpUser())) { return null; }
return new Authenticator() {
private final PasswordAuthentication authentication =
new PasswordAuthentication(props.getSmtpUser(), props.getSmtpPassword());
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return authentication;
}
};
}
@Override
public NotificationMode getMode() {
return NotificationMode.EMAIL;
}
}

View File

@ -0,0 +1,143 @@
package eu.dnetlib.lbs.matchers;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import javax.annotation.PostConstruct;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.util.CloseableIterator;
import org.springframework.stereotype.Component;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.elasticsearch.Notification;
import eu.dnetlib.lbs.elasticsearch.NotificationRepository;
import eu.dnetlib.lbs.events.output.DispatcherManager;
import eu.dnetlib.lbs.properties.ElasticSearchProperties;
import eu.dnetlib.lbs.subscriptions.MapCondition;
import eu.dnetlib.lbs.subscriptions.Subscription;
import eu.dnetlib.lbs.subscriptions.SubscriptionRepository;
import eu.dnetlib.lbs.utils.LbsQueue;
import eu.dnetlib.lbs.utils.QueueManager;
import eu.dnetlib.lbs.utils.ThreadManager;
@Component
public class SubscriptionEventMatcher implements Runnable {
@Autowired
private QueueManager queueManager;
@Autowired
private DispatcherManager dispatcherManager;
@Autowired
private SubscriptionRepository subscriptionRepo;
@Autowired
private ElasticSearchProperties elasticSearchProperties;
private LbsQueue<Subscription, Subscription> queue;
@Autowired
private ElasticsearchOperations esOperations;
@Autowired
private NotificationRepository notificationRepository;
@Autowired
private ThreadManager threadManager;
private static final Log log = LogFactory.getLog(SubscriptionEventMatcher.class);
@PostConstruct
public void init() {
queue = queueManager.newQueue("subscr-events-matcher-queue", Subscription::isReady);
threadManager.newThread("subscr-events-matcher", this);
}
public void startMatching(final Subscription s) {
if (queue.offer(s)) {
log.info("Matching of subscription " + s.getSubscriptionId() + " in queue");
} else {
log.info("Subscription " + s.getSubscriptionId() + " not queued");
}
}
@Override
public void run() {
log.info("SubscriptionEventMatcher started: " + Thread.currentThread().getName());
while (true) {
try {
final Subscription s = queue.takeOne();
if (s != null && Subscription.isReady(s)) {
startSubscriptionEventsMatcher(s);
s.setLastNotificationDate(new Date());
subscriptionRepo.save(s);
}
} catch (final Throwable e) {
log.error("Error iterating matching queue", e);
}
}
}
private void startSubscriptionEventsMatcher(final Subscription s) {
log.info("Start matching subscription: " + s);
try {
// TODO: eseguire una query simile a quella dell'api di openaire, includendo "> subscription.lastNotificationDate"
final BoolQueryBuilder mapQuery = QueryBuilders.boolQuery();
s.getConditionsAsList()
.stream()
.map(MapCondition::asQueryBuilder)
.filter(Objects::nonNull)
.forEach(mapQuery::must);
final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("topic", s.getTopic()))
.must(QueryBuilders.rangeQuery("creationDate").from(s.getLastNotificationDate().getTime()))
.must(QueryBuilders.nestedQuery("map", mapQuery, ScoreMode.None)))
.withSearchType(SearchType.DEFAULT)
// .withIndices(elasticSearchProperties.getEventsIndexName())
// .withTypes(elasticSearchProperties.getEventsIndexType())
.withPageable(PageRequest.of(0, 10))
.build();
final List<Event> events = new ArrayList<>();
final CloseableIterator<SearchHit<Event>> it =
esOperations.searchForStream(searchQuery, Event.class, IndexCoordinates.of(elasticSearchProperties.getEventsIndexName()));
while (it.hasNext()) {
final Event e = it.next().getContent();
final Notification n = new Notification(s, e);
if (isNotAlreadyNotified(n)) {
notificationRepository.save(n);
events.add(e);
}
}
log.info("End matching subscription: " + s.getSubscriptionId());
dispatcherManager.dispatch(s, events.toArray(new Event[events.size()]));
} catch (final Throwable e) {
log.error("Error matching subscription: " + s.getSubscriptionId(), e);
}
}
private boolean isNotAlreadyNotified(final Notification n) {
return !notificationRepository.findById(n.getNotificationId()).isPresent();
}
}

View File

@ -0,0 +1,189 @@
package eu.dnetlib.lbs.subscriptions;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.search.MatchQuery.ZeroTermsQuery;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.events.input.MapValueType;
import eu.dnetlib.lbs.utils.DateParser;
public class MapCondition {
private static final Log log = LogFactory.getLog(MapCondition.class);
private String field;
private MapValueType fieldType;
private ConditionOperator operator;
private List<ConditionParams> listParams = new ArrayList<>();
public MapCondition() {}
public MapCondition(final String field, final MapValueType fieldType, final ConditionOperator operator, final List<ConditionParams> listParams) {
this.field = field;
this.fieldType = fieldType;
this.operator = operator;
this.listParams = listParams;
}
public String getField() {
return field;
}
public void setField(final String field) {
this.field = field;
}
public MapValueType getFieldType() {
return fieldType;
}
public void setFieldType(final MapValueType fieldType) {
this.fieldType = fieldType;
}
public ConditionOperator getOperator() {
return operator;
}
public void setOperator(final ConditionOperator operator) {
this.operator = operator;
}
public List<ConditionParams> getListParams() {
return listParams;
}
public void setListParams(final List<ConditionParams> listParams) {
this.listParams = listParams;
}
public boolean verifyEvent(final Event event) {
final Object val = event.getMap().get(field);
return getListParams().stream().anyMatch(cp -> {
try {
switch (fieldType) {
case STRING:
return cp.verify(val.toString(), operator);
case INTEGER:
return cp.verify(val instanceof Long ? (long) val : NumberUtils.toInt(val.toString()), operator);
case FLOAT:
return cp.verify(val instanceof Double ? (double) val : NumberUtils.toDouble(val.toString()), operator);
case BOOLEAN:
return cp.verify(val instanceof Boolean ? (boolean) val : val.toString().equalsIgnoreCase("true"), operator);
case DATE:
return cp.verify(val instanceof Date ? (Date) val : DateParser.parse(val.toString()), operator);
case LIST_STRING:
return ((List<?>) val).stream().map(Object::toString).anyMatch(s -> cp.verify(s, operator));
case LIST_DATE:
return ((List<?>) val).stream().map(o -> o instanceof Date ? (Date) o : DateParser.parse(o.toString()))
.anyMatch(s -> cp.verify(s, operator));
case LIST_INTEGER:
return ((List<?>) val).stream().map(Object::toString).map(NumberUtils::toInt).anyMatch(n -> cp.verify(n, operator));
case LIST_FLOAT:
return ((List<?>) val).stream().map(Object::toString).map(NumberUtils::toDouble).anyMatch(n -> cp.verify(n, operator));
default:
break;
}
} catch (final Throwable e) {
log.error("Error verifying condition " + this + " on event: " + event, e);
}
return false;
});
}
public QueryBuilder asQueryBuilder() {
if (listParams == null || listParams.isEmpty()) { return null; }
if (operator == ConditionOperator.RANGE) {
if (listParams.size() == 1) {
return createSimpleRangeOperator(listParams.get(0));
} else {
return createListRangeOperator(listParams);
}
} else {
if (listParams.size() == 1) {
return createSimpleOperator(listParams.get(0));
} else {
return createListOperator(listParams);
}
}
}
private QueryBuilder createSimpleOperator(final ConditionParams p) {
if (StringUtils.isNotBlank(p.getValue())) {
return QueryBuilders.matchQuery("map." + field, convertToType(p.getValue())).operator(Operator.AND)
.zeroTermsQuery(ZeroTermsQuery.ALL);
} else {
return null;
}
}
private QueryBuilder createListOperator(final List<ConditionParams> list) {
final BoolQueryBuilder query = QueryBuilders.boolQuery();
for (final ConditionParams p : list) {
query.should(QueryBuilders.matchQuery("map." + field, convertToType(p.getValue())).operator(Operator.AND)
.zeroTermsQuery(ZeroTermsQuery.ALL));
}
return query;
}
private QueryBuilder createSimpleRangeOperator(final ConditionParams p) {
if (StringUtils.isNotBlank(p.getValue()) || StringUtils.isNotBlank(p.getOtherValue())) {
return QueryBuilders.rangeQuery("map." + field)
.from(convertToType(p.getValue()))
.to(convertToType(p.getOtherValue()));
} else {
return null;
}
}
private QueryBuilder createListRangeOperator(final List<ConditionParams> list) {
final BoolQueryBuilder query = QueryBuilders.boolQuery();
for (final ConditionParams p : list) {
query.should(QueryBuilders
.rangeQuery("map." + field)
.from(convertToType(p.getValue()))
.to(convertToType(p.getOtherValue())));
}
return query;
}
private Object convertToType(final String s) {
switch (fieldType) {
case STRING:
case LIST_STRING:
return s;
case INTEGER:
case LIST_INTEGER:
return NumberUtils.toLong(s, 0);
case FLOAT:
case LIST_FLOAT:
return NumberUtils.toDouble(s, 0);
case DATE:
case LIST_DATE:
return DateParser.parse(s).getTime();
case BOOLEAN:
case LIST_BOOLEAN:
return "true".equalsIgnoreCase(s);
}
return null;
}
@Override
public String toString() {
return String.format("[ %s %s %s ]", field, operator, listParams);
}
}

View File

@ -0,0 +1,125 @@
package eu.dnetlib.lbs.controllers;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.google.common.collect.Lists;
import eu.dnetlib.lbs.LiteratureBrokerServiceConfiguration;
import eu.dnetlib.lbs.cron.ScheduledActions;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.elasticsearch.EventRepository;
import eu.dnetlib.lbs.elasticsearch.EventStatsManager;
import eu.dnetlib.lbs.elasticsearch.EventStatsManager.BrowseEntry;
import eu.dnetlib.lbs.subscriptions.Subscription;
import eu.dnetlib.lbs.subscriptions.SubscriptionRepository;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@RestController
@RequestMapping("/api/events")
@Api(tags = LiteratureBrokerServiceConfiguration.TAG_EVENTS)
public class EventsController extends AbstractLbsController {
@Autowired
private EventRepository eventRepository;
@Autowired
private SubscriptionRepository subscriptionRepo;
@Autowired
private EventStatsManager eventStatsManager;
@Autowired
private ScheduledActions scheduledActions;
@ApiOperation("Return an event by ID")
@GetMapping("/{id}")
public Event getEvent(@PathVariable final String id) {
return eventRepository.findById(id).get();
}
@ApiOperation("Delete an event by ID")
@DeleteMapping("/{id}")
public void deleteEvent(@PathVariable final String id) {
eventRepository.deleteById(id);
}
@ApiOperation("Save an event by ID")
@PostMapping("/{id}")
public Event saveEvent(@RequestBody final Event event) {
return eventRepository.save(event);
}
@ApiOperation("Return a page of events")
@GetMapping("/list/{page}/{pageSize}")
public List<Event> events(
@PathVariable final int page,
@PathVariable final int pageSize) {
return Lists.newArrayList(eventRepository.findAll(PageRequest.of(page, pageSize)));
}
@ApiOperation("Return a page of events by topic")
@GetMapping("/byTopic/{page}/{pageSize}")
public List<Event> eventsByTopic(
@PathVariable final int page,
@PathVariable final int pageSize,
@RequestParam final String topic) {
return Lists.newArrayList(eventRepository.findByTopic(topic, PageRequest.of(page, pageSize)));
}
@ApiOperation("Delete all the events")
@DeleteMapping("/all")
public Map<String, Object> clearEvents() {
eventRepository.deleteAll();
final Map<String, Object> res = new HashMap<>();
res.put("deleted", true);
return res;
}
@ApiOperation("Delete the expired events")
@DeleteMapping("/expired")
public Map<String, Long> deleteExpiredEvents() {
final Map<String, Long> res = new HashMap<>();
res.put("deleted", scheduledActions.deleteExpiredEvents());
return res;
}
@ApiOperation("Return the topics of the indexed events (all)")
@GetMapping("/topics/all")
public List<BrowseEntry> browseTopics() {
return eventStatsManager.browseTopics();
}
@ApiOperation("Return the topics of the indexed events (only with subscriptions)")
@GetMapping("/topics/withSubscriptions")
public List<BrowseEntry> browseTopicsWithSubscriptions() {
final Iterable<Subscription> iter = subscriptionRepo.findAll();
final Set<String> validTopics = StreamSupport.stream(iter.spliterator(), false)
.map(Subscription::getTopic)
.collect(Collectors.toSet());
return this.browseTopics()
.stream()
.filter(e -> validTopics.contains(e.getValue()))
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,65 @@
package eu.dnetlib.lbs.utils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import eu.dnetlib.lbs.elasticsearch.Event;
@Ignore
public class JsonMessageToEventFunctionTest {
private JsonMessageToEventFunction f;
@Before
public void setUp() throws Exception {
f = new JsonMessageToEventFunction();
}
@Test
public void test() throws IOException {
final String json = IOUtils.toString(getClass().getResourceAsStream("/test-event-1.json"), Charset.defaultCharset());
System.out.println("Original record:\n\n" + json + "\n\n");
final Event event = f.apply(json);
assertNotNull(event);
assertEquals("openaire", event.getProducerId());
assertEquals("INSERT/123", event.getTopic());
assertEquals("Hello World", event.getPayload());
assertEquals(10 * 24 * 60 * 60 * 1000, event.getExpiryDate() - event.getCreationDate());
assertEquals("id_123", event.getMap().get("id"));
assertEquals("Hello", event.getMap().get("title"));
assertEquals(0.7d, event.getMap().get("trust"));
assertEquals(4l, event.getMap().get("count"));
assertEquals(true, event.getMap().get("open"));
assertNotNull(event.getMap().get("date"));
assertTrue(event.getMap().get("date") instanceof Date);
assertNotNull(event.getMap().get("creators"));
assertTrue(event.getMap().get("creators") instanceof List);
assertEquals(3, ((List<?>) event.getMap().get("creators")).size());
assertEquals("michele", ((List<?>) event.getMap().get("creators")).get(0));
assertEquals("claudio", ((List<?>) event.getMap().get("creators")).get(1));
assertEquals("alessia", ((List<?>) event.getMap().get("creators")).get(2));
assertNotNull(event.getMap().get("years"));
assertTrue(event.getMap().get("years") instanceof List);
assertEquals(2, ((List<?>) event.getMap().get("years")).size());
assertEquals(2011L, ((List<?>) event.getMap().get("years")).get(0));
assertEquals(2013L, ((List<?>) event.getMap().get("years")).get(1));
}
}

View File

@ -0,0 +1,41 @@
package eu.dnetlib.lbs.controllers;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
public abstract class AbstractLbsController {
@ExceptionHandler(Exception.class)
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public @ResponseBody ErrorMessage handleException(final Exception e) {
return new ErrorMessage(e);
}
public class ErrorMessage {
private final String message;
private final String stacktrace;
public ErrorMessage(final Exception e) {
this(e.getMessage(), ExceptionUtils.getStackTrace(e));
}
public ErrorMessage(final String message, final String stacktrace) {
this.message = message;
this.stacktrace = stacktrace;
}
public String getMessage() {
return this.message;
}
public String getStacktrace() {
return this.stacktrace;
}
}
}

View File

@ -0,0 +1,38 @@
package eu.dnetlib.lbs.openaire;
import org.apache.commons.lang3.StringUtils;
public class Range {
private String min;
private String max;
public Range() {}
public Range(final String min, final String max) {
super();
this.min = min;
this.max = max;
}
public String getMin() {
return this.min;
}
public void setMin(final String min) {
this.min = min;
}
public String getMax() {
return this.max;
}
public void setMax(final String max) {
this.max = max;
}
public boolean isValid() {
return StringUtils.isNotBlank(this.min) || StringUtils.isNotBlank(this.max);
}
}

View File

@ -0,0 +1,46 @@
{
"producerId": "openaire",
"topic": "INSERT/123",
"payload": "Hello World",
"tthDays": 10,
"map": {
"id": {
"type": "STRING",
"value": "id_123"
},
"title": {
"type": "STRING",
"value": "Hello"
},
"date": {
"type": "DATE",
"value": "2016-11-25"
},
"count": {
"type": "INTEGER",
"value": 4
},
"trust": {
"type": "FLOAT",
"value": 0.7
},
"open": {
"type": "BOOLEAN",
"value": true
},
"creators": {
"type": "LIST_STRING",
"value": [
"michele",
"claudio",
"alessia"
]
},
"years": {
"type": [
2011,
2013
]
}
}
}

View File

@ -0,0 +1,377 @@
<div class="col-xs-12" ng-show="eventsPage">
<p>
<b>Datasource:</b> {{eventsPage.datasource}}<br />
<b>Topic:</b> {{eventsPage.topic}}<br />
<b>Total:</b> {{eventsPage.total}}<br />
<b>Page:</b> {{eventsPage.currPage + 1}} / {{eventsPage.totalPages}}<br />
<button class="btn btn-sm btn-primary" data-target="#showAdvancedSearchModal" data-toggle="modal" ng-click="prepareAdvancedSearchForm()">advanced search</button>
<button class="btn btn-sm btn-primary" data-target="#showCreateSubscriptionModal" data-toggle="modal" ng-click="prepareSubscriptionForm()">create subscription</button>
</p>
<p ng-if="eventsPage.values.length == 0">
<b>No records</b>
</p>
<div ng-if="eventsPage.values.length > 0">
<ul class="pager">
<li class="disabled" ng-if="eventsPage.currPage <= 0"><a href="javascript:void(0)">&larr; Prev</a></li>
<li ng-if="eventsPage.currPage > 0"><a href="javascript:void(0)" ng-click="gotoPage(eventsPage.currPage - 1)">&larr; Prev</a></li>
<li class="disabled" ng-if="eventsPage.totalPages <= eventsPage.currPage + 1"><a href="javascript:void(0)">Next &rarr;</a></li>
<li ng-if="eventsPage.currPage + 1 < eventsPage.totalPages"><a href="javascript:void(0)" ng-click="gotoPage(eventsPage.currPage + 1)">Next &rarr;</a></li>
</ul>
<div class="well" ng-repeat="val in eventsPage.values">
<p class="text-right"><span class="label label-primary"><b>Trust:</b> {{val.trust}}</span></p>
<table class="table table-striped">
<tr>
<th class="col-xs-4 col-md-3 col-lg-2">ID</th>
<td class="col-xs-8 col-md-9 col-lg-10">
{{val.publication.originalId}}
</td>
</tr>
<tr>
<th>Title(s)</th>
<td>
<span ng-repeat="t in val.publication.titles track by $index">
<br ng-if="$index > 0"/>
{{t}}
</span>
</td>
</tr>
<tr>
<th>Author(s)</th>
<td>
<span ng-repeat="a in val.publication.creators track by $index">
<br ng-if="$index > 0"/>
{{a}}
</span>
</td>
</tr>
<tr ng-class="{'success' : val.highlight.pids && val.highlight.pids.length > 0}">
<th>PID(s)</th>
<td>
<span ng-repeat="pid in val.publication.pids">
<br ng-if="$index > 0"/>
{{pid.value}} <b>({{pid.type}})</b>
<span ng-if="val.highlight.pids && isPidHighlighted(pid, val.highlight.pids)">
<span class="label label-success">added</span>
<span class="pull-right">
<b>from: </b>
<a href="{{val.provenance.url}}" target="_blank" ng-if="val.provenance.url">{{val.provenance.repositoryName}}</a>
<span ng-if="!val.provenance.url">{{val.provenance.repositoryName}}</span>
<span ng-if="val.provenance.id">(ID: {{val.provenance.id}})</span>
</span>
</span>
</span>
</td>
</tr>
<tr ng-class="{'success' : val.highlight.abstracts && val.highlight.abstracts.length > 0}">
<th>Abstract(s)</th>
<td>
<span ng-repeat="d in val.publication.abstracts track by $index">
<br ng-if="$index > 0"/>
{{d}}
</span>
<span class="pull-right" ng-if="val.highlight.abstracts && val.highlight.abstracts.length > 0">
<b>from: </b>
<a href="{{val.provenance.url}}" target="_blank" ng-if="val.provenance.url">{{val.provenance.repositoryName}}</a>
<span ng-if="!val.provenance.url">{{val.provenance.repositoryName}}</span>
<span ng-if="val.provenance.id">(ID: {{val.provenance.id}})</span>
</span>
</td>
</tr>
<tr ng-class="{'success' : val.highlight.subjects && val.highlight.subjects.length > 0}">
<th>Subject(s)</th>
<td>
<span ng-repeat="s in val.publication.subjects track by $index">
<br ng-if="$index > 0"/>
{{s}}
<span ng-if="val.highlight.subjects && isSubjectHighlighted(s, val.highlight.subjects)">
<span class="label label-success">added</span>
<span class="pull-right">
<b>from: </b>
<a href="{{val.provenance.url}}" target="_blank" ng-if="val.provenance.url">{{val.provenance.repositoryName}}</a>
<span ng-if="!val.provenance.url">{{val.provenance.repositoryName}}</span>
<span ng-if="val.provenance.id">(ID: {{val.provenance.id}})</span>
</span>
</span>
</span>
</td>
</tr>
<tr ng-class="{'success' : val.highlight.publicationdate }">
<th>Publication date</th>
<td>
{{val.publication.publicationdate}}
<span class="pull-right" ng-if="val.highlight.publicationdate">
<b>from: </b>
<a href="{{val.provenance.url}}" target="_blank" ng-if="val.provenance.url">{{val.provenance.repositoryName}}</a>
<span ng-if="!val.provenance.url">{{val.provenance.repositoryName}}</span>
<span ng-if="val.provenance.id">(ID: {{val.provenance.id}})</span>
</span>
</td>
</tr>
<tr ng-class="{'success' : val.highlight.instances && val.highlight.instances.length > 0}">
<th>Rights</th>
<td>
<span ng-repeat="i in val.publication.instances">
<br ng-if="$index > 0"/>
{{i.license}} <b>({{i.hostedby}})</b>
<span ng-if="val.highlight.instances && isInstanceHighlighted(i, val.highlight.instances)">
<span class="label label-success">added</span>
<span class="pull-right">
<b>from: </b>
<a href="{{val.provenance.url}}" target="_blank" ng-if="val.provenance.url">{{val.provenance.repositoryName}}</a>
<span ng-if="!val.provenance.url">{{val.provenance.repositoryName}}</span>
<span ng-if="val.provenance.id">(ID: {{val.provenance.id}})</span>
</span>
</span>
<br />
<a href="{{i.url}}" target="_blank">{{i.url}}</a>
</span>
</td>
</tr>
<tr ng-class="{'success' : val.highlight.projects && val.highlight.projects.length > 0}">
<th>Project(s)</th>
<td>
<span ng-repeat="p in val.publication.projects">
<br ng-if="$index > 0"/>
{{p.title}}
<span ng-if="val.highlight.projects && isProjectHighlighted(p, val.highlight.projects)">
<span class="label label-success">added</span>
<span class="pull-right">
<b>from: </b>
<a href="{{val.provenance.url}}" target="_blank" ng-if="val.provenance.url">{{val.provenance.repositoryName}}</a>
<span ng-if="!val.provenance.url">{{val.provenance.repositoryName}}</span>
<span ng-if="val.provenance.id">(ID: {{val.provenance.id}})</span>
</span>
</span>
<ul>
<li ng-if="p.acronym">acronym: <i>{{p.acronym}}</i></li>
<li ng-if="p.code">code: <i>{{p.code}}</i></li>
<li ng-if="p.funder">funder: <i>{{p.funder}}</i></li>
<li ng-if="p.fundingProgram">fundingProgram: <i>{{p.fundingProgram}}</i></li>
<li ng-if="p.jurisdiction">jurisdiction: <i>{{p.jurisdiction}}</i></li>
</ul>
</span>
</td>
</tr>
<!-- DATASETS AND PLUBLICATIONS -->
<tr ng-class="{'success' : val.highlight.datasets && val.highlight.datasets.length > 0}">
<th>Dataset(s)</th>
<td>
<span ng-repeat="d in val.publication.datasets">
<br ng-if="$index > 0"/>
{{d.titles[0]}}
<span ng-if="val.highlight.datasets && isDatasetHighlighted(d, val.highlight.datasets)">
<span class="label label-success">added</span>
<span class="pull-right">
<b>from: </b>
<a href="{{val.provenance.url}}" target="_blank" ng-if="val.provenance.url">{{val.provenance.repositoryName}}</a>
<span ng-if="!val.provenance.url">{{val.provenance.repositoryName}}</span>
<span ng-if="val.provenance.id">(ID: {{val.provenance.id}})</span>
</span>
</span>
<ul class="small">
<li ng-repeat="pid in d.pids"><b>{{pid.type}}:</b> {{pid.value}}</li>
</ul>
</span>
</td>
</tr>
<tr ng-class="{'success' : val.highlight.publications && val.highlight.publications.length > 0}">
<th>Related publications(s)</th>
<td>
<span ng-repeat="p in val.publication.publications">
<br ng-if="$index > 0"/>
{{p.titles[0]}}
<span ng-if="val.highlight.publications && isRelatedPublicationHighlighted(p, val.highlight.publications)">
<span class="label label-success">added</span>
<span class="pull-right">
<b>from: </b>
<a href="{{val.provenance.url}}" target="_blank" ng-if="val.provenance.url">{{val.provenance.repositoryName}}</a>
<span ng-if="!val.provenance.url">{{val.provenance.repositoryName}}</span>
<span ng-if="val.provenance.id">(ID: {{val.provenance.id}})</span>
</span>
</span>
<ul class="small">
<li ng-repeat="pid in p.pids"><b>{{pid.type}}:</b> {{pid.value}}</li>
</ul>
</span>
</td>
</tr>
</table>
</div>
<ul class="pager">
<li class="disabled" ng-if="eventsPage.currPage <= 0"><a href="javascript:void(0)">&larr; Prev</a></li>
<li ng-if="eventsPage.currPage > 0"><a href="javascript:void(0)" ng-click="gotoPage(eventsPage.currPage - 1)">&larr; Prev</a></li>
<li class="disabled" ng-if="eventsPage.totalPages <= eventsPage.currPage + 1"><a href="javascript:void(0)">Next &rarr;</a></li>
<li ng-if="eventsPage.currPage + 1 < eventsPage.totalPages"><a href="javascript:void(0)" ng-click="gotoPage(eventsPage.currPage + 1)">Next &rarr;</a></li>
</ul>
</div>
</div>
<div id="showAdvancedSearchModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">Advanced search</h4>
</div>
<div class="modal-body">
<form class="form-horizontal">
<div class="form-group">
<label class="control-label col-sm-4">Datasource</label>
<div class="col-sm-8">
<p class="form-control-static">{{eventsPage.datasource}}</p>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-4">Topic</label>
<div class="col-sm-8">
<p class="form-control-static">{{eventsPage.topic}}</p>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-4">Trust</label>
<div class="col-sm-8">
<rzslider
rz-slider-model="tempQuery.trust.min"
rz-slider-high="tempQuery.trust.max"
rz-slider-options="{floor: 0, ceil: 1, step: 0.01, precision: 2, hideLimitLabels: true}"></rzslider>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-4">Title(s)</label>
<div class="col-sm-8">
<div class="input-group" ng-repeat="t in tempQuery.titles track by $index" style="margin-bottom: 4px;">
<input type="text" class="form-control input-sm" ng-model="tempQuery.titles[$index]" />
<span class="input-group-btn">
<button class="btn btn-sm btn-danger" ng-click="tempQuery.titles.splice($index, 1)">
<span class="glyphicon glyphicon-minus"></span>
</button>
</span>
</div>
<button class="btn btn-sm btn-primary" ng-click="tempQuery.titles.push('')">add</button>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-4">Author(s)</label>
<div class="col-sm-8">
<div class="input-group" ng-repeat="aut in tempQuery.authors track by $index" style="margin-bottom: 4px;">
<input type="text" class="form-control input-sm" ng-model="tempQuery.authors[$index]" />
<span class="input-group-btn">
<button class="btn btn-sm btn-danger" ng-click="tempQuery.authors.splice($index, 1)">
<span class="glyphicon glyphicon-minus"></span>
</button>
</span>
</div>
<button class="btn btn-sm btn-primary" ng-click="tempQuery.authors.push('')">add</button>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-4">Subject(s)</label>
<div class="col-sm-8">
<div class="input-group" ng-repeat="s in tempQuery.subjects track by $index" style="margin-bottom: 4px;">
<input type="text" class="form-control input-sm" ng-model="tempQuery.subjects[$index]" />
<span class="input-group-btn">
<button class="btn btn-sm btn-danger" ng-click="tempQuery.subjects.splice($index, 1)">
<span class="glyphicon glyphicon-minus"></span>
</button>
</span>
</div>
<button class="btn btn-sm btn-primary" ng-click="tempQuery.subjects.push('')">add</button>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-4">Date(s)</label>
<div class="col-sm-8">
<div class="input-group" ng-repeat="s in tempQuery.dates track by $index" style="margin-bottom: 4px;">
<span class="input-group-addon">from</span>
<input type="text" class="form-control input-sm" ng-model="tempQuery.dates[$index].min" placeholder="yyyy-MM-dd" />
<span class="input-group-addon">to</span>
<input type="text" class="form-control input-sm" ng-model="tempQuery.dates[$index].max" placeholder="yyyy-MM-dd" />
<span class="input-group-btn">
<button class="btn btn-sm btn-danger" ng-click="tempQuery.dates.splice($index, 1)">
<span class="glyphicon glyphicon-minus"></span>
</button>
</span>
</div>
<button class="btn btn-sm btn-primary" ng-click="tempQuery.dates.push({from: '', to: ''})">add</button>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" data-dismiss="modal" ng-click="gotoEventsPageAdv(tempQuery)">Update</button>
</div>
</div>
</div>
</div>
<div id="showCreateSubscriptionModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">Create subscription</h4>
</div>
<div class="modal-body">
<form name="newSubscriptionForm" class="form-horizontal">
<div class="form-group" ng-class="{'has-error' : !subscription.subscriber, 'has-success' : subscription.subscriber }">
<label class="control-label col-sm-3">Email</label>
<div class="col-sm-9">
<p class="form-control-static">{{subscription.subscriber}}</p>
</div>
</div>
<div class="form-group" ng-class="{'has-error' : !subscription.frequency, 'has-success' : subscription.frequency }">
<label class="control-label col-sm-3">Frequency</label>
<div class="col-sm-9">
<select class="form-control" ng-model="subscription.frequency" required="required">
<option>never</option>
<option>daily</option>
<option>weekly</option>
<option>monthly</option>
<option>realtime</option>
</select>
</div>
</div>
<div class="form-group" ng-class="{'has-error' : !subscription.mode, 'has-success' : subscription.mode }">
<label class="control-label col-sm-3">Mode</label>
<div class="col-sm-9">
<select class="form-control" ng-model="subscription.mode" required="required">
<option>MOCK</option>
<option>EMAIL</option>
</select>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-3">Query</label>
<div class="col-sm-9">
<pre>{{subscription.query | json: 4}}</pre>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" data-dismiss="modal" ng-disabled="newSubscriptionForm.$invalid" ng-click="createSubscription()">Save</button>
</div>
</div>
</div>
</div>
<back-top><span class="glyphicon glyphicon-chevron-up"></span> top</back-top>

View File

@ -0,0 +1,355 @@
var openaireBroker = angular.module('openaireBroker', ['ngRoute', 'angular.backtop', 'openaireBrokerControllers', 'rzModule', 'LocalStorageModule', 'base64']);
openaireBroker.config([
'$routeProvider',
function($routeProvider) {
$routeProvider
.when('/login', { templateUrl: 'login.html', controller: 'loginCtrl' })
.when('/datasources', { templateUrl: 'datasources.html', controller: 'datasourcesCtrl' })
.when('/q/:page/:encodedJson', { templateUrl: 'query.html', controller: 'queryCtrl' })
.when('/subscriptions', { templateUrl: 'subscriptions.html', controller: 'subscriptionsCtrl' })
.when('/ntf/:page/:subscrId', { templateUrl: 'notifications.html', controller: 'notificationsCtrl' })
.otherwise({ redirectTo: '/login' });
}
]);
openaireBroker.factory('loginService', function(localStorageService) {
var user;
var tmpPath;
var setUser = function(aUser) {
user = aUser;
if (user) {
localStorageService.set('openaire_broker_user', user);
} else {
localStorageService.clearAll();
}
};
var getUser = function() {
if (!user) {
user = localStorageService.get('openaire_broker_user');
}
return user;
};
return {
setUser : setUser,
getUser : getUser,
isLogged : function() { return getUser() ? true : false },
getTmpPath : function() { return tmpPath },
setTmpPath : function(p) { tmpPath = p },
}
});
openaireBroker.run(function ($rootScope, $location, loginService) {
$rootScope.$on('$routeChangeStart', function (event) {
if (!loginService.isLogged()) {
if ($location.path() != '/login') {
loginService.setTmpPath($location.path());
}
$location.path('/login');
} else {
loginService.setTmpPath('');
}
});
});
openaireBroker.controller('moduleMenuCtrl', function ($scope, $http, $location, loginService) {
$scope.isActive = function(regex) {
var re = new RegExp(regex);
return re.test($location.path());
}
$scope.$watch(loginService.isLogged, function (value, oldValue) {
if(!value && oldValue) {
$location.path('/login');
}
}, true);
$scope.getUser = function() {
return loginService.getUser()
}
$scope.isLogged = function() {
return loginService.isLogged();
}
$scope.logout = function() {
return loginService.setUser('');
}
});
//---------------------------------------------------------------------------------------------------------------------//
var openaireBrokerControllers = angular.module('openaireBrokerControllers', []);
openaireBrokerControllers.controller('loginCtrl', function ($scope, $location, loginService) {
$scope.isLogged = function() {
return loginService.isLogged();
}
$scope.performLogin = function(email) {
loginService.setUser(email);
$location.path(loginService.getTmpPath() ? loginService.getTmpPath() : "/datasources");
}
});
openaireBrokerControllers.controller('datasourcesCtrl', function ($scope, $http, $sce, $location, $routeParams, $timeout, $base64, loginService) {
$scope.datasources = [];
$scope.topics = [];
$scope.dsName = "";
$scope.refresh = function() {
$scope.datasources = [];
$http.get('../api/openaireBroker/datasources').success(function(data) {
$scope.datasources = data;
}).error(function() {
alert("error");
});
};
$scope.browseTopics = function(dsName) {
$scope.topics = [];
$scope.dsName = dsName;
$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8";
$http.get('../api/openaireBroker/topicsForDatasource?ds=' + encodeURIComponent(dsName)).success(function(data) {
$scope.topics = data;
}).error(function() {
alert("error");
});
}
$scope.gotoEventsPage = function(ds, topic) {
var query = {
'datasource' : ds,
'topic' : topic,
'trust' : {'min': 0, 'max': 1},
'titles' : [],
'authors' : [],
'subjects' : [],
'dates' : []
};
var path = '/q/0/' + encodeURIComponent($base64.encode(angular.toJson(query)))
$timeout(function() { $location.path(path); }, 500);
}
$scope.calculateTotal = function(list) {
var total = 0;
angular.forEach(list, function(i) {
total += i.size;
});
return total;
}
$scope.refresh();
});
openaireBrokerControllers.controller('queryCtrl', function ($scope, $http, $sce, $location, $routeParams, $timeout, $base64, loginService) {
$scope.eventsPage = null;
$scope.page = $routeParams.page;
$scope.encodedQuery = $routeParams.encodedJson;
$scope.query = angular.fromJson($base64.decode(decodeURIComponent($scope.encodedQuery)));
$scope.refresh = function() {
$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
$http.post('../api/openaireBroker/events/' + $scope.page + '/20', $scope.query).success(function(data) {
$scope.eventsPage = data;
}).error(function() {
alert("error");
});
}
$scope.gotoPage = function(page) {
$location.path('/q/' + page + '/' + $scope.encodedQuery);
}
$scope.gotoEventsPageAdv = function(q) {
var query = {
'datasource' : q.datasource,
'topic' : q.topic,
'trust' : q.trust,
'titles' : q.titles.filter($scope.isValidString),
'authors' : q.authors.filter($scope.isValidString),
'subjects' : q.subjects.filter($scope.isValidString),
'dates' : q.dates.filter($scope.isValidRange)
};
var path = '/q/0/' + encodeURIComponent($base64.encode(angular.toJson(query)))
$timeout(function() { $location.path(path); }, 500);
}
$scope.isValidString = function(s) {
return (s && s.trim().length > 0);
}
$scope.isValidRange = function(s) {
return (s && ((s.min && s.min.trim().length > 0) || (s.max && s.max.trim().length > 0)));
}
$scope.isSubjectHighlighted = function(subject, list) {
if (list) {
for (var i=0; i<list.length; i++) {
if (subject == list[i]) {
return true;
}
}
}
return false;
}
$scope.isInstanceHighlighted = function(instance, list) {
if (list) {
for (var i=0; i<list.length; i++) {
if (instance.url == list[i].url && instance.license == list[i].license && instance.hostedby == list[i].hostedby) {
return true;
}
}
}
return false;
}
$scope.isProjectHighlighted= function(project, list) {
if (list) {
for (var i=0; i<list.length; i++) {
if (project.code == list[i].code) {
return true;
}
}
}
return false;
}
$scope.isPidHighlighted = function(pid, list) {
if (list) {
for (var i=0; i<list.length; i++) {
if (pid.value == list[i].value && pid.type == list[i].type) {
return true;
}
}
}
return false;
}
$scope.isDatasetHighlighted= function(dataset, list) {
if (list) {
for (var i=0; i<list.length; i++) {
if (dataset.originalId == list[i].originalId) {
return true;
}
}
}
return false;
}
$scope.isRelatedPublicationHighlighted= function(pub, list) {
if (list) {
for (var i=0; i<list.length; i++) {
if (pub.originalId == list[i].originalId) {
return true;
}
}
}
return false;
}
$scope.prepareAdvancedSearchForm = function() {
$scope.tempQuery = angular.copy($scope.query);
if ($scope.tempQuery.titles.length == 0) { $scope.tempQuery.titles.push(''); }
if ($scope.tempQuery.authors.length == 0) { $scope.tempQuery.authors.push(''); }
if ($scope.tempQuery.subjects.length == 0) { $scope.tempQuery.subjects.push(''); }
if ($scope.tempQuery.dates.length == 0) { $scope.tempQuery.dates.push({'min':'','max':''}); }
$timeout(function() {
$scope.$broadcast('rzSliderForceRender'); //Force refresh sliders on render. Otherwise bullets are aligned at left side.
}, 1000);
}
$scope.prepareSubscriptionForm = function() {
$scope.subscription = {
'subscriber' : loginService.getUser(),
'frequency' : '',
'mode' : '',
'query' : $scope.query
};
}
$scope.createSubscription = function() {
$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
$http.post('../api/openaireBroker/subscribe', $scope.subscription).success(function(data) {
alert("New registered subscription, ID: " + data.subscriptionId);
}).error(function() {
alert("error");
});
}
$scope.refresh();
});
openaireBrokerControllers.controller('subscriptionsCtrl', function ($scope, $http, $sce, $location, $routeParams, $timeout, $base64, loginService) {
$scope.subscriptions = {};
$scope.isEmpty = function() {
return angular.equals({}, $scope.subscriptions);
}
$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8";
$http.get('../api/openaireBroker/subscriptions?email=' + encodeURIComponent(loginService.getUser())).success(function(data) {
$scope.subscriptions = data;
}).error(function() {
alert("error");
});
});
openaireBrokerControllers.controller('notificationsCtrl', function ($scope, $http, $sce, $location, $routeParams, $timeout, $base64, loginService) {
$scope.page = null;
$scope.nPage = $routeParams.page;
$scope.subscrId = $routeParams.subscrId;
$scope.refresh = function() {
$http.get('../api/openaireBroker/notifications/' + $scope.subscrId + "/" + $scope.nPage + '/20').success(function(data) {
$scope.page = data;
}).error(function() {
alert("error");
});
}
$scope.gotoPage = function(n) {
$location.path('/ntf/' + n + "/" + $scope.subscrId);
}
$scope.isInstanceHighlighted = function(instance, list) {
if (list) {
for (var i=0; i<list.length; i++) {
if (instance.url == list[i].url && instance.license == list[i].license && instance.hostedby == list[i].hostedby) {
return true;
}
}
}
return false;
}
$scope.isPidHighlighted = function(pid, list) {
if (list) {
for (var i=0; i<list.length; i++) {
if (pid.value == list[i].value && pid.type == list[i].type) {
return true;
}
}
}
return false;
}
$scope.refresh();
});

View File

@ -0,0 +1,47 @@
package eu.dnetlib.lbs.openaire;
import java.util.List;
import eu.dnetlib.broker.objects.OpenAireEventPayload;
public class ScrollPage {
private String id;
private boolean completed;
private List<OpenAireEventPayload> values;
public ScrollPage() {}
public ScrollPage(final String id, final boolean completed, final List<OpenAireEventPayload> values) {
this.id = id;
this.completed = completed;
this.values = values;
}
public String getId() {
return id;
}
public void setId(final String id) {
this.id = id;
}
public boolean isCompleted() {
return completed;
}
public void setCompleted(final boolean completed) {
this.completed = completed;
}
public List<OpenAireEventPayload> getValues() {
return values;
}
public void setValues(final List<OpenAireEventPayload> values) {
this.values = values;
}
}

View File

@ -0,0 +1,38 @@
package eu.dnetlib.lbs.utils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class QueueManager {
final List<LbsQueue<?, ?>> lbsQueues = new ArrayList<>();
@Value("${lbs.queues.maxReturnedValues}")
private int maxReturnedValues;
public <T, K> LbsQueue<T, K> newQueue(final String name, final Function<T, K> func, final Predicate<K> predicate) {
final LbsQueue<T, K> queue = new LbsQueue<>(name, func, predicate, this.maxReturnedValues);
this.lbsQueues.add(queue);
return queue;
}
public <K> LbsQueue<K, K> newQueue(final String name, final Predicate<K> predicate) {
return newQueue(name, s -> s, predicate);
}
public <K> LbsQueue<K, K> newQueue(final String name) {
return newQueue(name, s -> s, Objects::nonNull);
}
public List<LbsQueue<?, ?>> getLbsQueues() {
return this.lbsQueues;
}
}

View File

@ -0,0 +1,24 @@
package eu.dnetlib.lbs.openaire;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/openaire")
public String index_1() {
return "redirect:/openaire/index.html";
}
@GetMapping("/openaireBroker")
public String index_2() {
return "redirect:/openaire/index.html";
}
@GetMapping("/openaireBroker/index.html")
public String index_3() {
return "redirect:/openaire/index.html";
}
}

View File

@ -0,0 +1,27 @@
package eu.dnetlib.lbs.events.input;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.properties.RabbitMQProperties;
import eu.dnetlib.lbs.utils.LbsQueue;
@Component
@ConditionalOnProperty(prefix = "lbs.rabbit", name = "enabled")
public class RabbitMQConsumerFactory {
@Autowired
private RabbitMQProperties props;
public RabbitMQConsumer newConsumer(final LbsQueue<String, Event> localQueue) {
final String queue = this.props.getQueue();
final String host = this.props.getHost();
final int port = this.props.getPort();
final String username = this.props.getUsername();
final String password = this.props.getPassword();
return new RabbitMQConsumer(queue, host, port, username, password, localQueue);
}
}

View File

@ -0,0 +1,83 @@
package eu.dnetlib.lbs.properties;
import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "lbs.elastic")
public class ElasticSearchProperties {
@NotNull
private String clusterNodes;
@NotNull
private String clusterName;
@NotNull
private String homepage;
@NotNull
private String eventsIndexName;
@NotNull
private String eventsIndexType;
@NotNull
private String notificationsIndexName;
@NotNull
private String notificationsIndexType;
public String getClusterNodes() {
return this.clusterNodes;
}
public void setClusterNodes(final String clusterNodes) {
this.clusterNodes = clusterNodes;
}
public String getClusterName() {
return this.clusterName;
}
public void setClusterName(final String clusterName) {
this.clusterName = clusterName;
}
public String getHomepage() {
return this.homepage;
}
public void setHomepage(final String homepage) {
this.homepage = homepage;
}
public String getEventsIndexName() {
return this.eventsIndexName;
}
public void setEventsIndexName(final String eventsIndexName) {
this.eventsIndexName = eventsIndexName;
}
public String getEventsIndexType() {
return this.eventsIndexType;
}
public void setEventsIndexType(final String eventsIndexType) {
this.eventsIndexType = eventsIndexType;
}
public String getNotificationsIndexName() {
return this.notificationsIndexName;
}
public void setNotificationsIndexName(final String notificationsIndexName) {
this.notificationsIndexName = notificationsIndexName;
}
public String getNotificationsIndexType() {
return this.notificationsIndexType;
}
public void setNotificationsIndexType(final String notificationsIndexType) {
this.notificationsIndexType = notificationsIndexType;
}
}

View File

@ -0,0 +1,11 @@
package eu.dnetlib.lbs.subscriptions;
import org.springframework.data.repository.CrudRepository;
public interface SubscriptionRepository extends CrudRepository<Subscription, String> {
Iterable<Subscription> findByTopic(String topic);
Iterable<Subscription> findBySubscriber(String email);
}

View File

@ -0,0 +1,33 @@
package eu.dnetlib.lbs.properties;
import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "lbs.database")
public class DatabaseProperties {
@NotNull
private String driverName;
@NotNull
private String url;
public String getDriverName() {
return this.driverName;
}
public void setDriverName(final String driverName) {
this.driverName = driverName;
}
public String getUrl() {
return this.url;
}
public void setUrl(final String url) {
this.url = url;
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,40 @@
package eu.dnetlib.lbs.events.output;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.subscriptions.Subscription;
@Component
public class DispatcherManager {
@Autowired(required = false)
private final List<NotificationDispatcher> dispatchers = new ArrayList<>();
private static final Log log = LogFactory.getLog(DispatcherManager.class);
public void dispatch(final Subscription s, final Event... events) {
final Optional<NotificationDispatcher> dispatcher = this.dispatchers
.stream()
.filter(d -> d.getMode() == s.getMode())
.findFirst();
if (dispatcher.isPresent()) {
dispatcher.get().sendNotification(s, events);
} else {
log.error("Notification dispatcher not found, mode=" + s.getMode());
}
}
public List<NotificationDispatcher> getDispatchers() {
return this.dispatchers;
}
}

View File

@ -0,0 +1,91 @@
package eu.dnetlib.lbs;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.elasticsearch.Notification;
import eu.dnetlib.lbs.properties.ElasticSearchProperties;
import io.prometheus.client.exporter.MetricsServlet;
import io.prometheus.client.hotspot.DefaultExports;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Tag;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
@EnableScheduling
@EnableTransactionManagement
@EnableElasticsearchRepositories(basePackageClasses = { Event.class, Notification.class })
public class LiteratureBrokerServiceConfiguration extends AbstractElasticsearchConfiguration {
private static final Log log = LogFactory.getLog(LiteratureBrokerServiceConfiguration.class);
@Autowired
private ElasticSearchProperties elasticSearchProperties;
public static final String TAG_EVENTS = "Events";
public static final String TAG_SUBSCRIPTIONS = "Subscriptions";
public static final String TAG_NOTIFICATIONS = "Notifications";
public static final String TAG_TOPIC_TYPES = "Topic Types";
public static final String TAG_OPENAIRE = "OpenAIRE";
@Override
@Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo(elasticSearchProperties.getClusterNodes())
.build();
return RestClients.create(clientConfiguration).rest();
}
@Bean
public static Docket newSwaggerDocket() {
log.info("Initializing SWAGGER...");
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(p -> p.startsWith("/api/"))
.build()
.tags(
new Tag(TAG_EVENTS, "Events management"),
new Tag(TAG_SUBSCRIPTIONS, "Subscriptions management"),
new Tag(TAG_NOTIFICATIONS, "Notifications management"),
new Tag(TAG_TOPIC_TYPES, "Topic types management"),
new Tag(TAG_OPENAIRE, "OpenAIRE use case"))
.apiInfo(new ApiInfo(
"Literature Broker Service",
"APIs documentation",
"1.1",
"urn:tos",
ApiInfo.DEFAULT_CONTACT,
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0"));
}
@Bean
@ConditionalOnMissingBean(name = "prometheusMetricsServletRegistrationBean")
ServletRegistrationBean<MetricsServlet> prometheusMetricsServletRegistrationBean(@Value("${prometheus.metrics.path:/metrics}") final String metricsPath) {
DefaultExports.initialize();
return new ServletRegistrationBean<>(new MetricsServlet(), metricsPath);
}
}

View File

@ -0,0 +1,26 @@
package eu.dnetlib.lbs.utils;
import java.text.ParseException;
import java.util.Date;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class DateParser {
private static final Log log = LogFactory.getLog(DateParser.class);
public static Date parse(final String s) {
if (s == null) {
log.warn("Date is NULL");
return null;
}
try {
return DateUtils.parseDate(s, "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss");
} catch (final ParseException e) {
log.warn("Invalid Date: " + s);
return null;
}
}
}

View File

@ -0,0 +1,70 @@
package eu.dnetlib.lbs.events.manager;
import java.util.List;
import java.util.stream.StreamSupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.elasticsearch.EventRepository;
import eu.dnetlib.lbs.elasticsearch.Notification;
import eu.dnetlib.lbs.elasticsearch.NotificationRepository;
import eu.dnetlib.lbs.events.output.DispatcherManager;
import eu.dnetlib.lbs.subscriptions.NotificationFrequency;
import eu.dnetlib.lbs.subscriptions.Subscription;
import eu.dnetlib.lbs.subscriptions.SubscriptionRepository;
import eu.dnetlib.lbs.utils.LbsQueue;
public class EventManager implements Runnable {
private final EventRepository eventRepository;
private final NotificationRepository notificationRepository;
private final SubscriptionRepository subscriptionRepo;
private final DispatcherManager dispatcherManager;
private final LbsQueue<String, Event> queue;
private static final Log log = LogFactory.getLog(EventManager.class);
public EventManager(final EventRepository eventRepository, final NotificationRepository notificationRepository,
final SubscriptionRepository subscriptionRepo,
final DispatcherManager dispatcherManager,
final LbsQueue<String, Event> queue) {
this.eventRepository = eventRepository;
this.notificationRepository = notificationRepository;
this.subscriptionRepo = subscriptionRepo;
this.dispatcherManager = dispatcherManager;
this.queue = queue;
}
public boolean add(final String s) {
return this.queue.offer(s);
}
@Override
public void run() {
log.info("Event indexer started: " + Thread.currentThread().getName());
while (true) {
final List<Event> list = this.queue.takeList();
this.eventRepository.saveAll(list);
// TODO: cache of Subscription with NotificationFrequency.realtime
list.stream().filter(Event::isInstantMessage).forEach(e -> {
final Iterable<Subscription> iter = this.subscriptionRepo.findByTopic(e.getTopic());
StreamSupport.stream(iter.spliterator(), false)
.filter(s -> s.verifyEventConditions(e))
.filter(s -> s.getFrequency() == NotificationFrequency.realtime)
.forEach(s -> {
final Notification n = new Notification(s, e);
this.notificationRepository.save(n);
this.dispatcherManager.dispatch(s, e);
});
});
}
}
public LbsQueue<String, Event> getQueue() {
return this.queue;
}
}

View File

@ -0,0 +1,19 @@
package eu.dnetlib.lbs.elasticsearch;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface EventRepository extends ElasticsearchRepository<Event, String> {
// TODO: use the @Query annotation if necessary
// See: http://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/
Iterable<Event> findByTopic(String topic);
Page<Event> findByTopic(String topic, Pageable pageable);
long deleteByExpiryDateBefore(long l);
}

View File

@ -0,0 +1,7 @@
<button class="btn btn-xs"
ng-class="{'btn-danger' : status == 0, 'btn-success' : status != 0 }"
style="width: 80px; margin-left: 10px;"
ng-click="doAction()">
<span ng-show="status == 0">{{label}}</span>
<span ng-show="status == 1">confirm ?</span>
</button>

View File

@ -0,0 +1,41 @@
package eu.dnetlib.lbs.events.output;
import java.io.StringWriter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.subscriptions.NotificationMode;
import eu.dnetlib.lbs.subscriptions.Subscription;
@Component
@Profile("dev")
public class MockDispatcher extends AbstractNotificationDispatcher<String> {
private static final Log log = LogFactory.getLog(MockDispatcher.class);
@Override
protected String prepareAction(final Subscription subscription, final Event... events) throws Exception {
final StringWriter sw = new StringWriter();
sw.write("\n********************************");
sw.write("\n* New notification");
sw.write("\n* - Subscription : " + subscription);
sw.write("\n* - N. events : " + events.length);
sw.write("\n********************************");
return sw.toString();
}
@Override
protected void performAction(final String message) throws Exception {
log.info(message);
}
@Override
public NotificationMode getMode() {
return NotificationMode.MOCK;
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,69 @@
package eu.dnetlib.lbs.events.input;
import java.util.Map;
public class EventMessage {
public static final int TTH_INFINITE = -1;
private String producerId;
private String topic;
private String payload;
private int tthDays = TTH_INFINITE;
private Map<String, MapValue> map;
public EventMessage() {}
public EventMessage(final String producerId, final String topic, final String payload, final int tthDays, final Map<String, MapValue> map) {
this.setProducerId(producerId);
this.topic = topic;
this.payload = payload;
this.tthDays = tthDays;
this.map = map;
}
public String getProducerId() {
return this.producerId;
}
public void setProducerId(final String producerId) {
this.producerId = producerId;
}
public String getTopic() {
return this.topic;
}
public void setTopic(final String topic) {
this.topic = topic;
}
public String getPayload() {
return this.payload;
}
public void setPayload(final String payload) {
this.payload = payload;
}
public Map<String, MapValue> getMap() {
return this.map;
}
public void setMap(final Map<String, MapValue> map) {
this.map = map;
}
public boolean hasNulls() {
return this.producerId == null || this.topic == null || this.payload == null || this.map == null;
}
public int getTthDays() {
return this.tthDays;
}
public void setTthDays(final int tthDays) {
this.tthDays = tthDays;
}
}

View File

@ -0,0 +1,56 @@
package eu.dnetlib.lbs.events.input;
import javax.annotation.PostConstruct;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.events.manager.EventManager;
import eu.dnetlib.lbs.events.manager.EventManagerFactory;
import eu.dnetlib.lbs.properties.RabbitMQProperties;
import eu.dnetlib.lbs.topics.TopicTypeRepository;
import eu.dnetlib.lbs.utils.EventVerifier;
import eu.dnetlib.lbs.utils.JsonMessageToEventFunction;
import eu.dnetlib.lbs.utils.LbsQueue;
import eu.dnetlib.lbs.utils.QueueManager;
import eu.dnetlib.lbs.utils.ThreadManager;
@Component
@ConditionalOnProperty(prefix = "lbs.rabbit", name = "enabled")
public class RabbitMQConsumerLauncher {
@Autowired
private EventManagerFactory eventManagerFactory;
@Autowired
private RabbitMQConsumerFactory rabbitMQConsumerFactory;
@Autowired
private QueueManager queueManager;
@Autowired
private TopicTypeRepository topicTypeRepo;
@Autowired
private RabbitMQProperties props;
@Autowired
private ThreadManager threadManager;
private static final Log log = LogFactory.getLog(RabbitMQConsumerLauncher.class);
@PostConstruct
public void init() {
for (int i = 0; i < this.props.getNumberOfConsumers(); i++) {
final LbsQueue<String, Event> queue =
this.queueManager.newQueue("event-manager-" + i, new JsonMessageToEventFunction(), new EventVerifier(this.topicTypeRepo));
final EventManager manager = this.eventManagerFactory.newEventManager(queue);
final RabbitMQConsumer consumer = this.rabbitMQConsumerFactory.newConsumer(queue);
log.info("Launching indexer and consumer threads: " + i);
this.threadManager.newThread("rabbit-manager-" + i, manager);
this.threadManager.newThread("rabbit-consumer-" + i, consumer);
}
}
}

View File

@ -0,0 +1,71 @@
package eu.dnetlib.lbs.openaire;
import java.util.Date;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import eu.dnetlib.lbs.subscriptions.NotificationFrequency;
import eu.dnetlib.lbs.subscriptions.NotificationMode;
import eu.dnetlib.lbs.subscriptions.Subscription;
public class OpenaireSubscription {
private String subscriber;
private NotificationFrequency frequency;
private NotificationMode mode;
private AdvQueryObject query;
public OpenaireSubscription() {}
public OpenaireSubscription(final String subscriber, final NotificationFrequency frequency, final NotificationMode mode,
final AdvQueryObject query) {
this.subscriber = subscriber;
this.frequency = frequency;
this.mode = mode;
this.query = query;
}
public String getSubscriber() {
return this.subscriber;
}
public void setSubscriber(final String subscriber) {
this.subscriber = subscriber;
}
public NotificationFrequency getFrequency() {
return this.frequency;
}
public void setFrequency(final NotificationFrequency frequency) {
this.frequency = frequency;
}
public NotificationMode getMode() {
return this.mode;
}
public void setMode(final NotificationMode mode) {
this.mode = mode;
}
public AdvQueryObject getQuery() {
return this.query;
}
public void setQuery(final AdvQueryObject query) {
this.query = query;
}
public Subscription asSubscription() {
if (StringUtils.isBlank(this.subscriber)) { throw new IllegalArgumentException("subscriber is empty"); }
if (StringUtils.isBlank(this.query.getTopic())) { throw new IllegalArgumentException("topic is empty"); }
final String subscriptionId = "sub-" + UUID.randomUUID();
return new Subscription(subscriptionId, this.subscriber, this.query.getTopic(), this.frequency, this.mode, null, new Date(),
this.query.asListOfConditions());
}
}

View File

@ -0,0 +1,44 @@
<h3>Registered events (elasticsearch)</h3>
<div class="row" style="margin-top:24px;">
<div class="col-xs-12 col-md-6 col-lg-3">
<input type="text" class="form-control" ng-model="searchFilter.value" placeholder="Filter..."/>
</div>
<div class="col-xs-13 col-md-6 col-lg-9 text-right">
<form class="form-inline">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="onlyWithSubscriptions" ng-change="reloadWithCondition()" /> only with registered subscriptions
</label>
</div>
<button class="btn btn-default" ng-click="refresh()">
<span class="glyphicon glyphicon-refresh"></span>
refresh
</button>
</form>
</div>
</div>
<p>
<table class="table table-striped table-condensed" style="font-size: 12px">
<thead>
<tr>
<th>Topic</th>
<th class="text-right">Size</th>
</tr>
</thead>
<tbody>
<tr ng-if="list.length == 0">
<td class="text-center text-muted" colspan="2">
No events
</td>
</tr>
<tr ng-repeat="s in list | filter: searchFilter">
<td style="font-family: monospace">
<a href="#/events/0/50/{{s.value}}">{{s.value}}</a>
</td>
<td class="text-right">{{s.count}}</td>
</tr>
</tbody>
</table>
</p>

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<head>
<title>Literature Broker Service</title>
<link rel="stylesheet" href="./css/bootstrap.min.css" />
<link rel="stylesheet" href="./css/bootstrap-theme.min.css" />
<script src="./js/jquery-1.12.3.min.js"></script>
<script src="./js/bootstrap.min.js"></script>
<script src="./js/angular.min.js"></script>
<script src="./js/angular-route.min.js"></script>
<script src="./js/lbs-ui.js"></script>
</head>
<body ng-app="lbs">
<nav class="navbar navbar-default" ng-controller="moduleMenuCtrl">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">Literature Broker Service</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li ng-class="{active : isActive('^\/summary')}"><a href="#/summary">Summary</a></li>
<li ng-class="{active : isActive('^\/topics')}"><a href="#/topics">Topics</a></li>
<li ng-class="{active : isActive('^\/subscriptions')}"><a href="#/subscriptions">Subscriptions</a></li>
<li ng-class="{active : isActive('^\/events')}"><a href="#/events/all">Events</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Tools <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{{t.url}}" target="_blank" ng-repeat="t in tools">{{t.name}}</a></li>
<li><a href="/swagger-ui.html" target="_blank">API documentation</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<div ng-view style="margin: 20px;"></div>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,166 @@
package eu.dnetlib.lbs.subscriptions;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Table;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import eu.dnetlib.lbs.elasticsearch.Event;
@Entity(name = "subscriptions")
@Table(name = "subscriptions")
public class Subscription {
@Id
@Column(name = "subscriptionid")
private String subscriptionId;
@Column(name = "subscriber", length = 4096, nullable = false)
private String subscriber;
@Column(name = "topic", nullable = false)
private String topic;
@Column(name = "frequency", nullable = false)
@Enumerated(EnumType.STRING)
private NotificationFrequency frequency;
@Column(name = "mode", nullable = false)
@Enumerated(EnumType.STRING)
private NotificationMode mode;
@Column(name = "lastNotificationDate", nullable = true)
private Date lastNotificationDate;
@Column(name = "creationDate", nullable = false, columnDefinition = "timestamp default '2018-01-01 00:00:00'")
private Date creationDate;
@Column(name = "conditions", length = 4096, nullable = true)
private String conditions;
public Subscription() {}
public Subscription(final String subscriptionId, final String subscriber, final String topic, final NotificationFrequency frequency,
final NotificationMode mode,
final Date lastNotificationDate, final Date creationDate, final List<MapCondition> conditions) {
this(subscriptionId, subscriber, topic, frequency, mode, lastNotificationDate, creationDate, new Gson().toJson(conditions));
}
public Subscription(final String subscriptionId, final String subscriber, final String topic, final NotificationFrequency frequency,
final NotificationMode mode, final Date lastNotificationDate, final Date creationDate, final String conditions) {
this.subscriptionId = subscriptionId;
this.subscriber = subscriber;
this.topic = topic;
this.frequency = frequency;
this.mode = mode;
this.lastNotificationDate = lastNotificationDate;
this.creationDate = creationDate;
this.conditions = conditions;
}
public String getSubscriptionId() {
return this.subscriptionId;
}
public void setSubscriptionId(final String subscriptionId) {
this.subscriptionId = subscriptionId;
}
public String getSubscriber() {
return this.subscriber;
}
public void setSubscriber(final String subscriber) {
this.subscriber = subscriber;
}
public String getTopic() {
return this.topic;
}
public void setTopic(final String topic) {
this.topic = topic;
}
public NotificationFrequency getFrequency() {
return this.frequency;
}
public void setFrequency(final NotificationFrequency frequency) {
this.frequency = frequency;
}
public NotificationMode getMode() {
return this.mode;
}
public void setMode(final NotificationMode mode) {
this.mode = mode;
}
public Date getLastNotificationDate() {
return this.lastNotificationDate;
}
public void setLastNotificationDate(final Date lastNotificationDate) {
this.lastNotificationDate = lastNotificationDate;
}
public Date getCreationDate() {
return this.creationDate;
}
public void setCreationDate(final Date creationDate) {
this.creationDate = creationDate;
}
public String getConditions() {
return this.conditions;
}
public List<MapCondition> getConditionsAsList() {
return new Gson().fromJson(this.conditions, new TypeToken<List<MapCondition>>() {}.getType());
}
public void setConditions(final String conditions) {
this.conditions = conditions;
}
public static boolean isReady(final Subscription s) {
if (s == null || s.getFrequency() == NotificationFrequency.never) {
return false;
} else if (s.getFrequency() == NotificationFrequency.realtime) {
return true;
} else if (s.getLastNotificationDate() == null) {
return true;
} else {
final long diff = new Date().getTime() - s.getLastNotificationDate().getTime();
if (((s.getFrequency() == NotificationFrequency.daily) && (diff >= TimeUnit.DAYS.toMillis(1))) ||
((s.getFrequency() == NotificationFrequency.weekly) && (diff >= TimeUnit.DAYS.toMillis(7))) ||
((s.getFrequency() == NotificationFrequency.monthly) && (diff >= TimeUnit.DAYS.toMillis(30)))) { return true; }
}
return false;
}
@Override
public String toString() {
return new Gson().toJson(this);
}
public boolean verifyEventConditions(final Event event) {
for (final MapCondition cond : getConditionsAsList()) {
if (!cond.verifyEvent(event)) { return false; }
}
return true;
}
}

View File

@ -0,0 +1,28 @@
package org.springframework.data.elasticsearch.core;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Query;
public class ElasticSearchScrollUtils {
private static final long SCROLL_TIMEOUT_IN_MILLIS = 5 * 60 * 1000;
public static <T> SearchScrollHits<T> startScroll(final AbstractElasticsearchTemplate esTemplate,
final IndexCoordinates index,
final Query searchQuery,
final Class<T> clazz) {
return esTemplate.searchScrollStart(SCROLL_TIMEOUT_IN_MILLIS, searchQuery, clazz, index);
}
public static <T> SearchScrollHits<T> continueScroll(final AbstractElasticsearchTemplate esTemplate,
final String scrollId,
final Class<T> clazz) {
return esTemplate.searchScrollContinue(scrollId, SCROLL_TIMEOUT_IN_MILLIS, clazz);
}
public static void clearScroll(final AbstractElasticsearchTemplate esTemplate, final String scrollId) {
esTemplate.searchScrollClear(scrollId);
}
}

View File

@ -0,0 +1,18 @@
package eu.dnetlib.lbs.events.output;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.subscriptions.NotificationMode;
import eu.dnetlib.lbs.subscriptions.Subscription;
public interface NotificationDispatcher {
void sendNotification(Subscription subscription, Event... events);
void resetCount();
String getDispatcherName();
long count();
NotificationMode getMode();
}

View File

@ -0,0 +1,131 @@
#backtop.light {
bottom: 30px;
right: 30px
}
#backtop.light.full-width {
background: white;
bottom: 0;
left: 0;
width: 100%
}
#backtop.light.full-width button {
width: 100%;
margin: 0 auto;
text-align: center;
padding: 15px 0;
border-radius: 0;
border-left: none;
border-right: none
}
#backtop.light button {
color: black;
background: white;
padding: 10px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
border: 1px solid #9f9f9f
}
#backtop.light button:hover, #backtop.light button:focus {
background: #f1f1f1
}
#backtop.light button:active {
-webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
-moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
background: #fafafa
}
#backtop.dark {
bottom: 30px;
right: 30px
}
#backtop.dark.full-width {
background: black;
bottom: 0;
left: 0;
width: 100%
}
#backtop.dark.full-width button {
width: 100%;
margin: 0 auto;
text-align: center;
padding: 15px 0;
border-radius: 0;
border-left: none;
border-right: none
}
#backtop.dark button {
color: white;
background: black;
padding: 10px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px
}
#backtop.dark button:hover, #backtop.dark button:focus {
background: #444
}
#backtop.dark button:active {
-webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
-moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
background: #666
}
#backtop.bootstrap button {
padding: 10px 15px;
background: #4479BA;
color: #FFF;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
border: none;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px
rgba(0, 0, 0, 0.2);
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px
rgba(0, 0, 0, 0.2);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px
rgba(0, 0, 0, 0.2)
}
#backtop.bootstrap button:hover, #backtop.bootstrap button:focus {
background: #356094
}
#backtop.bootstrap button:active {
-webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
-moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
background: #2E5481
}
#backtop {
opacity: 0;
transition: all .3s ease-out;
position: fixed
}
#backtop button {
outline: none;
border: 0
}
#backtop button:hover {
cursor: pointer
}
.show {
opacity: 1 !important
}

View File

@ -0,0 +1,310 @@
package eu.dnetlib.lbs.openaire;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.ElasticSearchScrollUtils;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchScrollHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.google.gson.Gson;
import eu.dnetlib.broker.objects.OpenAireEventPayload;
import eu.dnetlib.lbs.LiteratureBrokerServiceConfiguration;
import eu.dnetlib.lbs.controllers.AbstractLbsController;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.elasticsearch.Notification;
import eu.dnetlib.lbs.elasticsearch.NotificationRepository;
import eu.dnetlib.lbs.properties.ElasticSearchProperties;
import eu.dnetlib.lbs.subscriptions.MapCondition;
import eu.dnetlib.lbs.subscriptions.Subscription;
import eu.dnetlib.lbs.subscriptions.SubscriptionRepository;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@RestController
@RequestMapping("/api/openaireBroker")
@Api(tags = LiteratureBrokerServiceConfiguration.TAG_OPENAIRE)
public class OpenaireBrokerController extends AbstractLbsController {
@Autowired
private ElasticsearchOperations esOperations;
@Autowired
private NotificationRepository notificationRepository;
@Autowired
private SubscriptionRepository subscriptionRepo;
@Autowired
private ElasticSearchProperties props;
private static final Log log = LogFactory.getLog(OpenaireBrokerController.class);
@ApiOperation("Return the datasources having events")
@GetMapping("/datasources")
public List<BrowseEntry> findDatasourcesWithEvents() {
final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchAllQuery())
.withSearchType(SearchType.DEFAULT)
.addAggregation(AggregationBuilders.nested("nested", "map")
// .path("map")
.subAggregation(AggregationBuilders.terms("by_map").field("map.target_datasource_name").size(1000).minDocCount(1)))
.build();
final SearchHits<Event> hits = esOperations.search(searchQuery, Event.class, IndexCoordinates.of(props.getEventsIndexName()));
final Aggregations aggregations = hits.getAggregations();
final Aggregation aggByMap = ((ParsedNested) aggregations.asMap().get("nested")).getAggregations().asMap().get("by_map");
return ((ParsedStringTerms) aggByMap).getBuckets()
.stream()
.map(b -> new BrowseEntry(b.getKeyAsString(), b.getDocCount()))
.collect(Collectors.toList());
}
@ApiOperation("Return the topics of the events of a datasource")
@GetMapping("/topicsForDatasource")
public List<BrowseEntry> findTopicsForDatasource(@RequestParam final String ds) {
final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.nestedQuery("map", QueryBuilders.matchQuery("map.target_datasource_name", ds), ScoreMode.None))
.withSearchType(SearchType.DEFAULT)
.addAggregation(AggregationBuilders.terms("topic").field("topic").size(1000).minDocCount(1))
.build();
final SearchHits<Event> hits = esOperations.search(searchQuery, Event.class, IndexCoordinates.of(props.getEventsIndexName()));
final Aggregations aggregations = hits.getAggregations();
return ((ParsedStringTerms) aggregations.asMap().get("topic")).getBuckets()
.stream()
.map(b -> new BrowseEntry(b.getKeyAsString(), b.getDocCount()))
.collect(Collectors.toList());
}
@ApiOperation("Return a page of events of a datasource (by topic)")
@GetMapping("/events/{nPage}/{size}")
public EventsPage showEvents(@RequestParam final String ds, @RequestParam final String topic, @PathVariable final int nPage, @PathVariable final int size) {
final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("topic", topic))
.must(QueryBuilders.nestedQuery("map", QueryBuilders.matchQuery("map.target_datasource_name", ds), ScoreMode.None)))
.withSearchType(SearchType.DEFAULT)
.withFields("payload")
.withPageable(PageRequest.of(nPage, size))
.build();
final SearchHits<Event> page = esOperations.search(searchQuery, Event.class, IndexCoordinates.of(props.getEventsIndexName()));
final List<OpenAireEventPayload> list = page.stream()
.map(SearchHit::getContent)
.map(Event::getPayload)
.map(OpenAireEventPayload::fromJSON)
.collect(Collectors.toList());
return new EventsPage(ds, topic, nPage, overrideGetTotalPage(page, size), page.getTotalHits(), list);
}
@ApiOperation("Return a page of events of a datasource (by query)")
@PostMapping("/events/{nPage}/{size}")
public EventsPage advancedShowEvents(@PathVariable final int nPage, @PathVariable final int size, @RequestBody final AdvQueryObject qObj) {
final BoolQueryBuilder mapQuery = QueryBuilders.boolQuery();
ElasticSearchQueryUtils.addMapCondition(mapQuery, "map.target_datasource_name", qObj.getDatasource());
ElasticSearchQueryUtils.addMapCondition(mapQuery, "map.target_publication_title", qObj.getTitles());
ElasticSearchQueryUtils.addMapCondition(mapQuery, "map.target_publication_author_list", qObj.getAuthors());
ElasticSearchQueryUtils.addMapCondition(mapQuery, "map.target_publication_subject_list", qObj.getSubjects());
ElasticSearchQueryUtils.addMapConditionForTrust(mapQuery, "map.trust", qObj.getTrust());
ElasticSearchQueryUtils.addMapConditionForDates(mapQuery, "map.target_dateofacceptance", qObj.getDates());
final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("topic", qObj.getTopic()))
.must(QueryBuilders.nestedQuery("map", mapQuery, ScoreMode.None)))
.withSearchType(SearchType.DEFAULT)
.withFields("payload")
.withPageable(PageRequest.of(nPage, size))
.build();
final SearchHits<Event> page = esOperations.search(searchQuery, Event.class, IndexCoordinates.of(props.getEventsIndexName()));
final List<OpenAireEventPayload> list = page.stream()
.map(SearchHit::getContent)
.map(Event::getPayload)
.map(OpenAireEventPayload::fromJSON)
.collect(Collectors.toList());
return new EventsPage(qObj.getDatasource(), qObj.getTopic(), nPage, overrideGetTotalPage(page, size), page.getTotalHits(), list);
}
@ApiOperation("Perform a subscription")
@PostMapping("/subscribe")
public Subscription registerSubscription(@RequestBody final OpenaireSubscription oSub) {
final Subscription sub = oSub.asSubscription();
subscriptionRepo.save(sub);
return sub;
}
@ApiOperation("Return the subscriptions of an user (by email)")
@GetMapping("/subscriptions")
public Map<String, List<SimpleSubscriptionDesc>> subscriptions(@RequestParam final String email) {
final Iterable<Subscription> iter = subscriptionRepo.findBySubscriber(email);
return StreamSupport.stream(iter.spliterator(), false)
.map(this::subscriptionDesc)
.collect(Collectors.groupingBy(SimpleSubscriptionDesc::getDatasource));
}
@ApiOperation("Return a page of notifications")
@GetMapping("/notifications/{subscrId}/{nPage}/{size}")
public EventsPage notifications(@PathVariable final String subscrId, @PathVariable final int nPage, @PathVariable final int size) {
final Optional<Subscription> optSub = subscriptionRepo.findById(subscrId);
if (optSub.isPresent()) {
final Subscription sub = optSub.get();
final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchQuery("subscriptionId", subscrId))
.withSearchType(SearchType.DEFAULT)
.withFields("payload")
.withPageable(PageRequest.of(nPage, size))
.build();
final SearchHits<Notification> page = esOperations.search(searchQuery, Notification.class, IndexCoordinates.of(props.getNotificationsIndexName()));
final List<OpenAireEventPayload> list = page.stream()
.map(SearchHit::getContent)
.map(Notification::getPayload)
.map(OpenAireEventPayload::fromJSON)
.collect(Collectors.toList());
return new EventsPage(extractDatasource(sub), sub.getTopic(), nPage, overrideGetTotalPage(page, size), page.getTotalHits(), list);
} else {
log.warn("Invalid subscription: " + subscrId);
return new EventsPage("", "", nPage, 0, 0, new ArrayList<>());
}
}
@ApiOperation("Returns notifications using scrolls (first page)")
@GetMapping("/scroll/notifications/start/{subscrId}")
public ScrollPage prepareScrollNotifications(@PathVariable final String subscrId) {
final Optional<Subscription> optSub = subscriptionRepo.findById(subscrId);
if (optSub.isPresent()) {
final AbstractElasticsearchTemplate esTemplate = (AbstractElasticsearchTemplate) esOperations;
final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchQuery("subscriptionId", subscrId))
.withSearchType(SearchType.DEFAULT)
.withFields("payload")
.build();
final SearchScrollHits<Notification> scroll =
ElasticSearchScrollUtils.startScroll(esTemplate, IndexCoordinates.of(props.getNotificationsIndexName()), searchQuery, Notification.class);
if (scroll.hasSearchHits()) {
final List<OpenAireEventPayload> values = calculateEventPayloads(scroll);
return new ScrollPage(scroll.getScrollId(), values.isEmpty() || scroll.getScrollId() == null, values);
} else {
ElasticSearchScrollUtils.clearScroll(esTemplate, scroll.getScrollId());
return new ScrollPage(null, true, new ArrayList<>());
}
} else {
log.warn("Invalid subscription: " + subscrId);
return new ScrollPage();
}
}
@ApiOperation("Returns notifications using scrolls (other pages)")
@GetMapping("/scroll/notifications/{scrollId}")
public ScrollPage scrollNotifications(@PathVariable final String scrollId) {
final AbstractElasticsearchTemplate esTemplate = (AbstractElasticsearchTemplate) esOperations;
final SearchScrollHits<Notification> scroll = ElasticSearchScrollUtils.continueScroll(esTemplate, scrollId, Notification.class);
if (scroll.hasSearchHits()) {
final List<OpenAireEventPayload> values = calculateEventPayloads(scroll);
return new ScrollPage(scroll.getScrollId(), values.isEmpty() || scroll.getScrollId() == null, values);
} else {
ElasticSearchScrollUtils.clearScroll(esTemplate, scroll.getScrollId());
return new ScrollPage(null, true, new ArrayList<>());
}
}
private List<OpenAireEventPayload> calculateEventPayloads(final SearchScrollHits<Notification> scroll) {
if (scroll.getSearchHits().size() > 0) {
final Gson gson = new Gson();
return scroll.stream()
.map(SearchHit::getContent)
.map(Notification::getPayload)
.map(s -> gson.fromJson(s, OpenAireEventPayload.class))
.collect(Collectors.toList());
} else {
return new ArrayList<>();
}
}
private SimpleSubscriptionDesc subscriptionDesc(final Subscription s) {
return new SimpleSubscriptionDesc(s.getSubscriptionId(), extractDatasource(s), s.getTopic(), s.getCreationDate(), s.getLastNotificationDate(),
OpenaireBrokerController.this.notificationRepository.countBySubscriptionId(s.getSubscriptionId()));
}
private String extractDatasource(final Subscription sub) {
return sub.getConditionsAsList().stream()
.filter(c -> c.getField().equals("target_datasource_name"))
.map(MapCondition::getListParams)
.filter(l -> !l.isEmpty())
.map(l -> l.get(0).getValue())
.findFirst()
.get();
}
private long overrideGetTotalPage(final SearchHits<?> page, final int size) {
return (page.getTotalHits() + size - 1) / size;
}
}

View File

@ -0,0 +1,20 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/elasticsearch http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch.xsd">
<elasticsearch:transport-client id="elasticSearchClient"
cluster-nodes="localhost:9300"
cluster-name="elasticsearch_michele" />
<elasticsearch:repositories
base-package="eu.dnetlib.lbs.elasticsearch"
elasticsearch-template-ref="elasticsearchTemplate" />
<bean id="elasticsearchTemplate" class="org.springframework.data.elasticsearch.core.ElasticsearchTemplate">
<constructor-arg name="client" ref="elasticSearchClient" />
</bean>
</beans>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,32 @@
package eu.dnetlib.lbs.events.manager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.elasticsearch.EventRepository;
import eu.dnetlib.lbs.elasticsearch.NotificationRepository;
import eu.dnetlib.lbs.events.output.DispatcherManager;
import eu.dnetlib.lbs.subscriptions.SubscriptionRepository;
import eu.dnetlib.lbs.utils.LbsQueue;
@Component
public class EventManagerFactory {
@Autowired
private EventRepository eventRepository;
@Autowired
private NotificationRepository notificationRepository;
@Autowired
private SubscriptionRepository subscriptionRepo;
@Autowired
private DispatcherManager dispatcherManager;
public EventManager newEventManager(final LbsQueue<String, Event> queue) {
return new EventManager(this.eventRepository, this.notificationRepository, this.subscriptionRepo, this.dispatcherManager, queue);
}
}

View File

@ -0,0 +1,13 @@
package eu.dnetlib.lbs.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ApiDocController {
@GetMapping({ "/apidoc", "/api-doc", "/doc", "/swagger" })
public String apiDoc() {
return "redirect:swagger-ui.html";
}
}

View File

@ -0,0 +1,55 @@
package eu.dnetlib.lbs.cron;
import java.util.Date;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import eu.dnetlib.lbs.elasticsearch.EventRepository;
import eu.dnetlib.lbs.elasticsearch.NotificationRepository;
import eu.dnetlib.lbs.matchers.SubscriptionEventMatcher;
import eu.dnetlib.lbs.subscriptions.SubscriptionRepository;
@Component
public class ScheduledActions {
@Autowired
private EventRepository eventRepository;
@Autowired
private NotificationRepository notificationRepository;
@Autowired
private SubscriptionRepository subscriptionRepo;
@Autowired
private SubscriptionEventMatcher subscriptionEventMatcher;
private static final Log log = LogFactory.getLog(ScheduledActions.class);
@Scheduled(cron = "${lbs.task.deleteOldEvents.cron}")
public long deleteExpiredEvents() {
// TODO : the date used in the query should be the min among the dates of the subscriptions (considering only the subscriptions with
// creation_date > events.creationDate)
final long n = this.eventRepository.deleteByExpiryDateBefore(new Date().getTime());
log.info("Number of deleted expired events: " + n);
return n;
}
@Scheduled(cron = "${lbs.task.deleteOldNotifications.cron}")
public long deleteExpiredNotifications() {
// TODO
return 0;
}
@Scheduled(cron = "${lbs.task.subscriptionEventsMatcher.cron}")
public void startSubscriptionEventsMatcher() {
this.subscriptionRepo.findAll().forEach(this.subscriptionEventMatcher::startMatching);
}
}

View File

@ -0,0 +1,23 @@
{
"query": {
"nested": {
"path": "map",
"query": {
"bool": {
"must": [
{
"match": {
"map.id": "id_1_33"
}
},
{
"match": {
"map.year": 2016
}
}
]
}
}
}
}
}

View File

@ -0,0 +1,83 @@
package eu.dnetlib.lbs.properties;
import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "lbs.mail")
public class EmailProperties {
@NotNull
private String from;
@NotNull
private String fromName;
private String cc;
@NotNull
private String smtpHost;
@NotNull
private int smtpPort;
private String smtpUser;
private String smtpPassword;
public String getFrom() {
return this.from;
}
public void setFrom(final String from) {
this.from = from;
}
public String getFromName() {
return this.fromName;
}
public void setFromName(final String fromName) {
this.fromName = fromName;
}
public String getCc() {
return this.cc;
}
public void setCc(final String cc) {
this.cc = cc;
}
public String getSmtpHost() {
return this.smtpHost;
}
public void setSmtpHost(final String smtpHost) {
this.smtpHost = smtpHost;
}
public int getSmtpPort() {
return this.smtpPort;
}
public void setSmtpPort(final int smtpPort) {
this.smtpPort = smtpPort;
}
public String getSmtpUser() {
return this.smtpUser;
}
public void setSmtpUser(final String smtpUser) {
this.smtpUser = smtpUser;
}
public String getSmtpPassword() {
return this.smtpPassword;
}
public void setSmtpPassword(final String smtpPassword) {
this.smtpPassword = smtpPassword;
}
}

View File

@ -0,0 +1,83 @@
<h3>Registered Events - topic <span style="font-family: monospace;">{{topic}}</span></h3>
<form style="margin-top:40px;">
<div class="row">
<div class="col-xs-12 col-md-4">
<input type="text" class="form-control" ng-model="searchFilter" placeholder="Filter..."/>
</div>
<div class="col-xs-12 col-md-4">
<ul class="pager">
<li class="disabled" ng-if="page <= 0">
<a href="javascript:void(0)">&larr; Prev</a>
</li>
<li ng-if="page > 0">
<a href="#/events/{{(page*1) - 1}}/{{pageSize}}/{{topic}}">&larr; Prev</a>
</li>
<li class="disabled" ng-if="list.length < pageSize">
<a href="javascript:void(0)">Next &rarr;</a>
</li>
<li ng-if="list.length == pageSize">
<a href="#/events/{{(page*1) + 1}}/{{pageSize}}/{{topic}}">Next &rarr;</a>
</li>
</ul>
</div>
<div class="col-xs-12 col-md-4 text-right">
<a href="#/events" class="btn btn-primary">
<span class="glyphicon glyphicon-chevron-left"></span>
back
</a>
<button class="btn btn-default" ng-click="refresh()">
<span class="glyphicon glyphicon-refresh"></span>
refresh
</button>
</div>
</div>
</form>
<p>
<table class="table table-striped table-condensed" style="font-size: 12px">
<thead>
<tr>
<th>ID</th>
<th>Producer</th>
<th class="text-center">Creation date</th>
<th class="text-center">Expiry date</th>
<th>Payload</th>
<th>Map</th>
</tr>
</thead>
<tbody>
<tr ng-if="list.length == 0">
<td colspan="6" class="text-center text-muted">No events</td>
</tr>
<tr ng-repeat="s in list | filter: searchFilter">
<th style="font-family: monospace;">{{s.eventId}}</th>
<td style="font-family: monospace;">{{s.producerId}}</td>
<td class="text-center">{{s.creationDate | date:'yyyy-MM-dd HH:mm'}}</td>
<td class="text-center">{{s.expiryDate ? (s.expiryDate | date:'yyyy-MM-dd HH:mm') : 'never'}}</td>
<td>{{s.payload}}</td>
<td style="white-space: pre; font-size: 12px; font-family: monospace;">{{s.map | json}}</td>
</tr>
</tbody>
</table>
</p>
<nav>
<ul class="pager">
<li class="disabled" ng-if="page <= 0">
<a href="javascript:void(0)">&larr; Prev</a>
</li>
<li ng-if="page > 0">
<a href="#/events/{{(page*1) - 1}}/{{pageSize}}/{{topic}}">&larr; Prev</a>
</li>
<li class="disabled" ng-if="list.length < pageSize">
<a href="javascript:void(0)">Next &rarr;</a>
</li>
<li ng-if="list.length == pageSize">
<a href="#/events/{{(page*1) + 1}}/{{pageSize}}/{{topic}}">Next &rarr;</a>
</li>
</ul>
</nav>

View File

@ -0,0 +1,33 @@
You have received this mail because you are owner of the following subscription:
<ul>
<li><b>ID: </b>$sub.subscriptionId$</li>
<li><b>Subscriber: </b>$sub.subscriber$</li>
<li><b>Topic: </b>$sub.topic$</li>
<li><b>Frequency: </b>$sub.frequency$</li>
<li><b>Mode: </b>$sub.mode$</li>
<li><b>Conditions: </b>$sub.conditions$</li>
</ul>
<hr />
<p>There are $total$ event(s) that may be of interest to you (The following list is limited to $max$ events):</p>
<ol>
$events:{e|
<li>
<ul>
<li><b>ID: </b>$e.eventId$</li>
<li><b>Producer: </b>$e.producerId$</li>
<li><b>Topic: </b>$e.topic$</li>
<li><b>Payload: </b>$e.payload$</li>
<li><b>Event Creation Date: </b>$e.creationDate$</li>
<li><b>Event Expiry Date: </b>$e.expiryDate$</li>
<li><b>isInstantMessage: </b>$e.instantMessage$</li>
<li><b>Indexed fields: </b><ul>$e.map.keys:{k|<li><b>$k$: </b>$e.map.(k)$</li>}$</ul></li>
</ul>
</li>
}$
</ol>
<hr />
This email message was auto-generated. Please do not respond.

View File

@ -0,0 +1,219 @@
<h3>Subscriptions (H2 database)</h3>
<form class="form-inline text-right" style="margin-top:40px; margin-bottom:20px">
<button class="btn btn-primary" data-target="#addSubscriptionModal" data-toggle="modal" ng-click="resetFormFields()">
<span class="glyphicon glyphicon-plus"></span>
add
</button>
<button class="btn btn-default" ng-click="refresh()">
<span class="glyphicon glyphicon-refresh"></span>
refresh
</button>
</form>
<table class="table table-striped" style="margin-top:20px; font-size: 12px">
<thead>
<tr>
<th class="col-xs-2">ID</th>
<th class="col-xs-1">Subscriber</th>
<th class="col-xs-1">Creation Date</th>
<th class="col-xs-1">Topic</th>
<th class="col-xs-3">Conditions</th>
<th class="col-xs-1 text-center">Notification frequency</th>
<th class="col-xs-1 text-center">Notification mode</th>
<th class="col-xs-1 text-right">Last notification</th>
<th class="col-xs-1"></th>
</tr>
</thead>
<tbody>
<tr ng-if="list.length == 0">
<td colspan="8" class="text-center text-muted">No subscriptions</td>
</tr>
<tr ng-repeat="s in list">
<th style="font-family: monospace;">{{s.subscriptionId}}</th>
<td>{{s.subscriber}}</td>
<td>{{s.creationDate | date:'yyyy-MM-dd HH:mm'}}</td>
<td>{{s.topic}}</td>
<td style="font-size: 12px; font-family: monospace;">
<ul>
<li ng-repeat="c in s.conditionsAsList">
<b>{{c.field}}</b>
<ul>
<li ng-repeat="p in c.listParams">
<b>{{c.operator}}</b>
<span ng-if="c.operator != 'RANGE'">"{{p.value}}"</span>
<span ng-if="c.operator == 'RANGE'">[{{p.value ? '"' + p.value + '"': 'NULL'}}, {{p.otherValue ? '"' + p.otherValue + '"' : 'NULL'}}]</span>
</li>
</ul>
</li>
</ul>
</td>
<td class="text-center">{{s.frequency}}</td>
<td class="text-center">{{s.mode}}</td>
<td class="text-right">{{s.lastNotificationDate ? (s.lastNotificationDate | date:'yyyy-MM-dd HH:mm') : 'not yet'}}</td>
<td class="text-right">
<button type="button" class="btn btn-sm btn-primary" ng-click="resetNotificationDate(s.subscriptionId)">
reset date
</button>
<button type="button" class="btn btn-sm btn-primary" ng-click="startMatching(s.subscriptionId)">
match
</button>
<button type="button" class="btn btn-sm btn-danger" ng-click="remove(s.subscriptionId)">
<span class="glyphicon glyphicon-minus"></span>
</button>
</td>
</tr>
</tbody>
</table>
<div id="addSubscriptionModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg"">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">Add subscription</h4>
</div>
<div class="modal-body">
<form name="newSubscriptionForm" class="form-horizontal">
<div class="form-group" ng-class="{'has-error' : !subscriber, 'has-success' : subscriber }">
<label class="control-label col-sm-4">Subscriber</label>
<div class="col-sm-8">
<input type="email" class="form-control" placeholder="email" required="required" ng-model="subscriber"/>
</div>
</div>
<div class="form-group" ng-class="{'has-error' : !frequency, 'has-success' : frequency }">
<label class="control-label col-sm-4">Notification frequency</label>
<div class="col-sm-8">
<select class="form-control" ng-model="frequency" required="required">
<option>never</option>
<option>daily</option>
<option>weekly</option>
<option>monthly</option>
<option>realtime</option>
</select>
</div>
</div>
<div class="form-group" ng-class="{'has-error' : !mode, 'has-success' : mode }">
<label class="control-label col-sm-4">Notification mode</label>
<div class="col-sm-8">
<select class="form-control" ng-model="mode" required="required">
<option>MOCK</option>
<option>EMAIL</option>
</select>
</div>
</div>
<div class="form-group" ng-class="{'has-error' : !topicType, 'has-success' : topicType }">
<label class="control-label col-sm-4">Topic name</label>
<div class="col-sm-8">
<select class="form-control" ng-options="topic.name for topic in availableTopics" ng-model="topicType" ng-change="onSelectTopicType(topicType)" required="required"></select>
</div>
</div>
<div class="form-group" ng-repeat="t in topicParts" ng-if="t.variable" ng-class="{'has-error' : !t.value, 'has-success' : t.value}">
<label class="control-label col-sm-4" style="font-family: monospace;">{{t.name}}</label>
<div class="col-sm-8">
<input type="text" class="form-control" required="required" ng-model="t.value" ng-pattern="/^[a-zA-Z0-9._-]+$/" ng-change="updateTopic()"/>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-4">Topic expression</label>
<p class="form-control-static col-sm-8">{{topic ? topic : 'missing'}}</p>
</div>
<div class="form-group">
<label class="control-label col-sm-4">Conditions (on map values)</label>
<div class="col-sm-8">
<table class="table table-condensed">
<tbody ng-repeat="tc in topicConditions" style="border: 2px solid #ddd;">
<tr ng-class="{'has-error' : !tc.field, 'has-success' : tc.field }">
<td class="col-xs-1" rowspan="{{tc.listParams.length + 4}}">
<button type="button" class="btn btn-sm btn-danger" ng-click="removeCondition($index)">
<span class="glyphicon glyphicon-minus"></span>
</button>
</td>
<th class="col-xs-2 control-label">Field</th>
<td class="col-sm-9" colspan="3">
<input type="text" class="form-control" required="required" ng-model="tc.field" required="required" />
</td>
</tr>
<tr ng-class="{'has-error' : !tc.fieldType, 'has-success' : tc.fieldType }">
<th class="control-label">Field type</th>
<td colspan="3">
<select class="form-control" ng-model="tc.fieldType" required="required">
<option>STRING</option>
<option>INTEGER</option>
<option>FLOAT</option>
<option>DATE</option>
<option>BOOLEAN</option>
<option>LIST_STRING</option>
<option>LIST_INTEGER</option>
<option>LIST_FLOAT</option>
<option>LIST_DATE</option>
<option>LIST_BOOLEAN</option>
</select>
</td>
</tr>
<tr ng-class="{'has-error' : !tc.operator, 'has-success' : tc.operator }">
<th class="control-label">Operator</th>
<td colspan="3">
<select class="form-control" ng-model="tc.operator" required="required">
<option>EXACT</option>
<option>MATCH_ANY</option>
<option>MATCH_ALL</option>
<option>RANGE</option>
</select>
</td>
</tr>
<tr ng-class="{'has-error' : !(p.value || p.otherValue), 'has-success' : p.value || p.otherValue}" ng-if="$parent.tc.operator == 'RANGE'" ng-repeat="p in tc.listParams">
<th class="control-label" rowspan="{{$parent.tc.listParams.length}}" ng-if="$first">Range</th>
<td><input type="text" class="form-control" ng-model="p.value" /></td>
<td><input type="text" class="form-control" ng-model="p.otherValue" /></td>
<td>
<button type="button" class="btn btn-sm btn-danger" ng-click="$parent.tc.listParams.splice($index, 1)">
<span class="glyphicon glyphicon-minus"></span>
</button>
</td>
</tr>
<tr ng-class="{'has-error' : !p.value, 'has-success' : p.value }" ng-if="$parent.tc.operator != 'RANGE'" ng-repeat="p in tc.listParams">
<th class="control-label" rowspan="{{$parent.tc.listParams.length + 1}}" ng-if="$first">Values</th>
<td colspan="2"><input type="text" class="form-control" ng-model="p.value" /></td>
<td>
<button type="button" class="btn btn-sm btn-danger" ng-click="$parent.tc.listParams.splice($index, 1)">
<span class="glyphicon glyphicon-minus"></span>
</button>
</td>
</tr>
<tr>
<td colspan="3">
<button type="button" class="btn btn-sm btn-success" ng-click="tc.listParams.push({ 'value' : '', 'otherValue': '' })">
<span class="glyphicon glyphicon-plus"></span> add
</button>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">
<button type="button" class="btn btn-sm btn-success" ng-click="addCondition()">
<span class="glyphicon glyphicon-plus"></span>
add condition
</button>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary" data-dismiss="modal" ng-disabled="newSubscriptionForm.$invalid" ng-click="add(subscriber, topic, frequency, mode, topicConditions)">Add</button>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,5 @@
package eu.dnetlib.lbs.metrics;
public interface MetricInterceptor {
}

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