mirror of
https://github.com/sismics/docs.git
synced 2025-12-13 17:56:20 +00:00
Initial commit
This commit is contained in:
16
docs-web/src/dev/main/webapp/web-override.xml
Normal file
16
docs-web/src/dev/main/webapp/web-override.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app id="docs"
|
||||
xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||
version="3.0">
|
||||
|
||||
<!-- Override init parameter to avoid nasty file locking issue on windows. -->
|
||||
<servlet>
|
||||
<servlet-name>default</servlet-name>
|
||||
<init-param>
|
||||
<param-name>useFileMappedBuffer</param-name>
|
||||
<param-value>false</param-value>
|
||||
</init-param>
|
||||
</servlet>
|
||||
</web-app>
|
||||
3
docs-web/src/dev/resources/config.properties
Normal file
3
docs-web/src/dev/resources/config.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
api.current_version=${project.version}
|
||||
api.min_version=1.0
|
||||
db.version=1
|
||||
10
docs-web/src/dev/resources/hibernate.properties
Normal file
10
docs-web/src/dev/resources/hibernate.properties
Normal file
@@ -0,0 +1,10 @@
|
||||
hibernate.connection.driver_class=org.hsqldb.jdbcDriver
|
||||
hibernate.connection.url=jdbc:hsqldb:mem:docs
|
||||
hibernate.connection.username=sa
|
||||
hibernate.connection.password=
|
||||
hibernate.hbm2ddl.auto=none
|
||||
hibernate.dialect=org.hibernate.dialect.HSQLDialect
|
||||
hibernate.show_sql=false
|
||||
hibernate.format_sql=false
|
||||
hibernate.max_fetch_depth=5
|
||||
hibernate.cache.use_second_level_cache=false
|
||||
8
docs-web/src/dev/resources/log4j.properties
Normal file
8
docs-web/src/dev/resources/log4j.properties
Normal file
@@ -0,0 +1,8 @@
|
||||
log4j.rootCategory=WARN, CONSOLE, MEMORY
|
||||
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.CONSOLE.layout.ConversionPattern=%d{DATE} %p %l %m %n
|
||||
log4j.appender.MEMORY=com.sismics.util.log4j.MemoryAppender
|
||||
log4j.appender.MEMORY.size=1000
|
||||
|
||||
log4j.logger.com.sismics=DEBUG
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.sismics.docs.rest.constant;
|
||||
|
||||
/**
|
||||
* Base functions.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
public enum BaseFunction {
|
||||
|
||||
/**
|
||||
* Allows the user to use the admin fonctions.
|
||||
*/
|
||||
ADMIN,
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.sismics.docs.rest.resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.log4j.Appender;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
|
||||
import com.sismics.docs.core.util.ConfigUtil;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
||||
import com.sismics.docs.rest.constant.BaseFunction;
|
||||
import com.sismics.rest.exception.ForbiddenClientException;
|
||||
import com.sismics.rest.exception.ServerException;
|
||||
import com.sismics.util.log4j.LogCriteria;
|
||||
import com.sismics.util.log4j.LogEntry;
|
||||
import com.sismics.util.log4j.MemoryAppender;
|
||||
|
||||
/**
|
||||
* General app REST resource.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
@Path("/app")
|
||||
public class AppResource extends BaseResource {
|
||||
/**
|
||||
* Return the information about the application.
|
||||
*
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response version() throws JSONException {
|
||||
ResourceBundle configBundle = ConfigUtil.getConfigBundle();
|
||||
String currentVersion = configBundle.getString("api.current_version");
|
||||
String minVersion = configBundle.getString("api.min_version");
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("current_version", currentVersion.replace("-SNAPSHOT", ""));
|
||||
response.put("min_version", minVersion);
|
||||
response.put("total_memory", Runtime.getRuntime().totalMemory());
|
||||
response.put("free_memory", Runtime.getRuntime().freeMemory());
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the application logs.
|
||||
*
|
||||
* @param level Filter on logging level
|
||||
* @param tag Filter on logger name / tag
|
||||
* @param message Filter on message
|
||||
* @param limit Page limit
|
||||
* @param offset Page offset
|
||||
* @return
|
||||
* @throws JSONException
|
||||
*/
|
||||
@GET
|
||||
@Path("log")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response log(
|
||||
@QueryParam("level") String level,
|
||||
@QueryParam("tag") String tag,
|
||||
@QueryParam("message") String message,
|
||||
@QueryParam("limit") Integer limit,
|
||||
@QueryParam("offset") Integer offset) throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
// Get the memory appender
|
||||
Logger logger = Logger.getRootLogger();
|
||||
Appender appender = logger.getAppender("MEMORY");
|
||||
if (appender == null || !(appender instanceof MemoryAppender)) {
|
||||
throw new ServerException("ServerError", "MEMORY appender not configured");
|
||||
}
|
||||
MemoryAppender memoryAppender = (MemoryAppender) appender;
|
||||
|
||||
// Find the logs
|
||||
LogCriteria logCriteria = new LogCriteria();
|
||||
logCriteria.setLevel(StringUtils.stripToNull(level));
|
||||
logCriteria.setTag(StringUtils.stripToNull(tag));
|
||||
logCriteria.setMessage(StringUtils.stripToNull(message));
|
||||
|
||||
PaginatedList<LogEntry> paginatedList = PaginatedLists.create(limit, offset);
|
||||
memoryAppender.find(logCriteria, paginatedList);
|
||||
JSONObject response = new JSONObject();
|
||||
List<JSONObject> logs = new ArrayList<JSONObject>();
|
||||
for (LogEntry logEntry : paginatedList.getResultList()) {
|
||||
JSONObject log = new JSONObject();
|
||||
log.put("date", logEntry.getTimestamp());
|
||||
log.put("level", logEntry.getLevel());
|
||||
log.put("tag", logEntry.getTag());
|
||||
log.put("message", logEntry.getMessage());
|
||||
logs.add(log);
|
||||
}
|
||||
response.put("total", paginatedList.getResultCount());
|
||||
response.put("logs", logs);
|
||||
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.sismics.docs.rest.resource;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
|
||||
import com.sismics.docs.rest.constant.BaseFunction;
|
||||
import com.sismics.rest.exception.ForbiddenClientException;
|
||||
import com.sismics.security.IPrincipal;
|
||||
import com.sismics.security.UserPrincipal;
|
||||
import com.sismics.util.filter.TokenBasedSecurityFilter;
|
||||
|
||||
/**
|
||||
* Base class of REST resources.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
public abstract class BaseResource {
|
||||
/**
|
||||
* Injects the HTTP request.
|
||||
*/
|
||||
@Context
|
||||
protected HttpServletRequest request;
|
||||
|
||||
/**
|
||||
* Application key.
|
||||
*/
|
||||
@QueryParam("app_key")
|
||||
protected String appKey;
|
||||
|
||||
/**
|
||||
* Principal of the authenticated user.
|
||||
*/
|
||||
protected IPrincipal principal;
|
||||
|
||||
/**
|
||||
* This method is used to check if the user is authenticated.
|
||||
*
|
||||
* @return True if the user is authenticated and not anonymous
|
||||
*/
|
||||
protected boolean authenticate() {
|
||||
Principal principal = (Principal) request.getAttribute(TokenBasedSecurityFilter.PRINCIPAL_ATTRIBUTE);
|
||||
if (principal != null && principal instanceof IPrincipal) {
|
||||
this.principal = (IPrincipal) principal;
|
||||
return !this.principal.isAnonymous();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user has a base function. Throw an exception if the check fails.
|
||||
*
|
||||
* @param baseFunction Base function to check
|
||||
* @throws JSONException
|
||||
*/
|
||||
protected void checkBaseFunction(BaseFunction baseFunction) throws JSONException {
|
||||
if (!hasBaseFunction(baseFunction)) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user has a base function.
|
||||
*
|
||||
* @param baseFunction Base function to check
|
||||
* @return True if the user has the base function
|
||||
* @throws JSONException
|
||||
*/
|
||||
protected boolean hasBaseFunction(BaseFunction baseFunction) throws JSONException {
|
||||
if (principal == null || !(principal instanceof UserPrincipal)) {
|
||||
return false;
|
||||
}
|
||||
Set<String> baseFunctionSet = ((UserPrincipal) principal).getBaseFunctionSet();
|
||||
return baseFunctionSet != null && baseFunctionSet.contains(baseFunction.name());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package com.sismics.docs.rest.resource;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.FormParam;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
|
||||
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
||||
import com.sismics.docs.core.dao.jpa.criteria.DocumentCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
import com.sismics.rest.exception.ClientException;
|
||||
import com.sismics.rest.exception.ForbiddenClientException;
|
||||
import com.sismics.rest.util.ValidationUtil;
|
||||
|
||||
/**
|
||||
* Document REST resources.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
@Path("/document")
|
||||
public class DocumentResource extends BaseResource {
|
||||
/**
|
||||
* Returns a document.
|
||||
*
|
||||
* @param id Document ID
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response get(
|
||||
@QueryParam("id") String id) throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
Document documentDb = null;
|
||||
try {
|
||||
documentDb = documentDao.getDocument(id, principal.getId());
|
||||
} catch (NoResultException e) {
|
||||
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", id));
|
||||
}
|
||||
|
||||
JSONObject document = new JSONObject();
|
||||
document.put("id", documentDb.getId());
|
||||
document.put("title", documentDb.getTitle());
|
||||
document.put("description", documentDb.getDescription());
|
||||
document.put("create_date", documentDb.getCreateDate().getTime());
|
||||
|
||||
return Response.ok().entity(document).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all documents.
|
||||
*
|
||||
* @param limit Page limit
|
||||
* @param offset Page offset
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@GET
|
||||
@Path("list")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response list(
|
||||
@QueryParam("limit") Integer limit,
|
||||
@QueryParam("offset") Integer offset,
|
||||
@QueryParam("sort_column") Integer sortColumn,
|
||||
@QueryParam("asc") Boolean asc) throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
List<JSONObject> documents = new ArrayList<JSONObject>();
|
||||
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
PaginatedList<DocumentDto> paginatedList = PaginatedLists.create(limit, offset);
|
||||
SortCriteria sortCriteria = new SortCriteria(sortColumn, asc);
|
||||
DocumentCriteria documentCriteria = new DocumentCriteria();
|
||||
documentCriteria.setUserId(principal.getId());
|
||||
documentDao.findByCriteria(paginatedList, documentCriteria, sortCriteria);
|
||||
|
||||
for (DocumentDto documentDto : paginatedList.getResultList()) {
|
||||
JSONObject document = new JSONObject();
|
||||
document.put("id", documentDto.getId());
|
||||
document.put("title", documentDto.getTitle());
|
||||
document.put("description", documentDto.getDescription());
|
||||
document.put("create_date", documentDto.getCreateTimestamp());
|
||||
documents.add(document);
|
||||
}
|
||||
response.put("total", paginatedList.getResultCount());
|
||||
response.put("documents", documents);
|
||||
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new document.
|
||||
*
|
||||
* @param title Title
|
||||
* @param description Description
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@PUT
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response add(
|
||||
@FormParam("title") String title,
|
||||
@FormParam("description") String description) throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Validate input data
|
||||
title = ValidationUtil.validateLength(title, "title", 1, 100, false);
|
||||
description = ValidationUtil.validateLength(description, "description", 0, 4000, true);
|
||||
|
||||
// Create the document
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
Document document = new Document();
|
||||
document.setUserId(principal.getId());
|
||||
document.setTitle(title);
|
||||
document.setDescription(description);
|
||||
String documentId = documentDao.create(document);
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("id", documentId);
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the document.
|
||||
*
|
||||
* @param title Title
|
||||
* @param description Description
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@POST
|
||||
@Path("{id: [a-z0-9\\-]+}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response update(
|
||||
@PathParam("id") String id,
|
||||
@FormParam("title") String title,
|
||||
@FormParam("description") String description) throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Validate input data
|
||||
title = ValidationUtil.validateLength(title, "title", 1, 100, false);
|
||||
description = ValidationUtil.validateLength(description, "description", 0, 4000, true);
|
||||
|
||||
// Get the document
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
Document document = null;
|
||||
try {
|
||||
document = documentDao.getDocument(id, principal.getId());
|
||||
} catch (NoResultException e) {
|
||||
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", id));
|
||||
}
|
||||
|
||||
// Update the document
|
||||
if (title != null) {
|
||||
document.setTitle(title);
|
||||
}
|
||||
if (description != null) {
|
||||
document.setDescription(description);
|
||||
}
|
||||
|
||||
// Always return ok
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("status", "ok");
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a document.
|
||||
*
|
||||
* @param id Document ID
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@DELETE
|
||||
@Path("{id: [a-z0-9\\-]+}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response delete(
|
||||
@PathParam("id") String id) throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Get the document
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
Document document = null;
|
||||
try {
|
||||
document = documentDao.getDocument(id, principal.getId());
|
||||
} catch (NoResultException e) {
|
||||
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", id));
|
||||
}
|
||||
|
||||
// Delete the document
|
||||
documentDao.delete(document.getId());
|
||||
|
||||
// Always return ok
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("status", "ok");
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package com.sismics.docs.rest.resource;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
|
||||
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
||||
import com.sismics.docs.core.dao.jpa.FileDao;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
import com.sismics.docs.core.util.DirectoryUtil;
|
||||
import com.sismics.rest.exception.ClientException;
|
||||
import com.sismics.rest.exception.ForbiddenClientException;
|
||||
import com.sismics.rest.exception.ServerException;
|
||||
import com.sismics.rest.util.ValidationUtil;
|
||||
import com.sismics.util.mime.MimeTypeUtil;
|
||||
import com.sun.jersey.multipart.FormDataBodyPart;
|
||||
import com.sun.jersey.multipart.FormDataParam;
|
||||
|
||||
/**
|
||||
* File REST resources.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
@Path("/file")
|
||||
public class FileResource extends BaseResource {
|
||||
/**
|
||||
* Add a file to a document.
|
||||
*
|
||||
* @param id Document ID
|
||||
* @param fileBodyPart File to add
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@PUT
|
||||
@Consumes("multipart/form-data")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response add(
|
||||
@FormDataParam("id") String documentId,
|
||||
@FormDataParam("file") FormDataBodyPart fileBodyPart) throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Validate input data
|
||||
ValidationUtil.validateRequired(documentId, "id");
|
||||
ValidationUtil.validateRequired(fileBodyPart, "file");
|
||||
|
||||
// Get the document
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
Document document = null;
|
||||
try {
|
||||
document = documentDao.getDocument(documentId, principal.getId());
|
||||
} catch (NoResultException e) {
|
||||
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", documentId));
|
||||
}
|
||||
|
||||
|
||||
FileDao fileDao = new FileDao();
|
||||
|
||||
InputStream is = fileBodyPart.getValueAs(InputStream.class);
|
||||
try {
|
||||
// Create the file
|
||||
File file = new File();
|
||||
file.setDocumentId(document.getId());
|
||||
file.setMimeType(MimeTypeUtil.guessMimeType(is));
|
||||
String fileId = fileDao.create(file);
|
||||
|
||||
// Copy the incoming stream content into the storage directory
|
||||
Files.copy(is, Paths.get(DirectoryUtil.getStorageDirectory().getPath(), fileId));
|
||||
|
||||
// Always return ok
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("status", "ok");
|
||||
response.put("id", fileId);
|
||||
return Response.ok().entity(response).build();
|
||||
} catch (Exception e) {
|
||||
throw new ServerException("FileError", "Error adding a file", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file.
|
||||
*
|
||||
* @param id Document ID
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response get(
|
||||
@QueryParam("id") String id) throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
FileDao fileDao = new FileDao();
|
||||
File fileDb = null;
|
||||
try {
|
||||
fileDb = fileDao.getFile(id);
|
||||
} catch (NoResultException e) {
|
||||
throw new ClientException("FileNotFound", MessageFormat.format("File not found: {0}", id));
|
||||
}
|
||||
|
||||
JSONObject file = new JSONObject();
|
||||
file.put("id", fileDb.getId());
|
||||
file.put("mimetype", fileDb.getMimeType());
|
||||
file.put("document_id", fileDb.getDocumentId());
|
||||
file.put("create_date", fileDb.getCreateDate().getTime());
|
||||
|
||||
return Response.ok().entity(file).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns files linked to a document.
|
||||
*
|
||||
* @param id Document ID
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@GET
|
||||
@Path("list")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response list(
|
||||
@QueryParam("id") String documentId) throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
FileDao fileDao = new FileDao();
|
||||
List<File> fileList = fileDao.getByDocumentId(documentId);
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
List<JSONObject> files = new ArrayList<JSONObject>();
|
||||
|
||||
for (File fileDb : fileList) {
|
||||
JSONObject file = new JSONObject();
|
||||
file.put("id", fileDb.getId());
|
||||
file.put("mimetype", fileDb.getMimeType());
|
||||
file.put("document_id", fileDb.getDocumentId());
|
||||
file.put("create_date", fileDb.getCreateDate().getTime());
|
||||
files.add(file);
|
||||
}
|
||||
|
||||
response.put("files", files);
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file.
|
||||
*
|
||||
* @param id File ID
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@DELETE
|
||||
@Path("{id: [a-z0-9\\-]+}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response delete(
|
||||
@PathParam("id") String id) throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Get the file
|
||||
FileDao fileDao = new FileDao();
|
||||
File file = null;
|
||||
try {
|
||||
file = fileDao.getFile(id);
|
||||
} catch (NoResultException e) {
|
||||
throw new ClientException("FileNotFound", MessageFormat.format("File not found: {0}", id));
|
||||
}
|
||||
|
||||
// Delete the document
|
||||
fileDao.delete(file.getId());
|
||||
|
||||
// Always return ok
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("status", "ok");
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.sismics.docs.rest.resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
|
||||
import com.sismics.docs.core.dao.jpa.LocaleDao;
|
||||
import com.sismics.docs.core.model.jpa.Locale;
|
||||
|
||||
/**
|
||||
* Locale REST resources.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
@Path("/locale")
|
||||
public class LocaleResource extends BaseResource {
|
||||
/**
|
||||
* Returns the list of all locales.
|
||||
*
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response list() throws JSONException {
|
||||
LocaleDao localeDao = new LocaleDao();
|
||||
List<Locale> localeList = localeDao.findAll();
|
||||
JSONObject response = new JSONObject();
|
||||
List<JSONObject> items = new ArrayList<JSONObject>();
|
||||
for (Locale locale : localeList) {
|
||||
JSONObject item = new JSONObject();
|
||||
item.put("id", locale.getId());
|
||||
items.add(item);
|
||||
}
|
||||
response.put("locales", items);
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.sismics.docs.rest.resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.ext.MessageBodyWriter;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
|
||||
import com.sun.jersey.core.util.ReaderWriter;
|
||||
|
||||
/**
|
||||
* MessageBodyWriter personalized to write JSON despite the text/plain MIME type.
|
||||
* Used in particuler in return of a posted form, since IE doesn't knw how to read the application/json MIME type.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
@Provider
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public class TextPlainMessageBodyWriter implements
|
||||
MessageBodyWriter<JSONObject> {
|
||||
@Override
|
||||
public boolean isWriteable(Class<?> type, Type genericType,
|
||||
Annotation[] annotations, MediaType mediaType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize(JSONObject array, Class<?> type, Type genericType,
|
||||
Annotation[] annotations, MediaType mediaType) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(JSONObject jsonObject, Class<?> type, Type genericType,
|
||||
Annotation[] annotations, MediaType mediaType,
|
||||
MultivaluedMap<String, Object> httpHeaders,
|
||||
OutputStream entityStream) throws IOException,
|
||||
WebApplicationException {
|
||||
try {
|
||||
OutputStreamWriter writer = new OutputStreamWriter(entityStream, ReaderWriter.getCharset(mediaType));
|
||||
jsonObject.write(writer);
|
||||
writer.flush();
|
||||
} catch (JSONException e) {
|
||||
throw new WebApplicationException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.sismics.docs.rest.resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
|
||||
import com.sismics.docs.core.dao.file.theme.ThemeDao;
|
||||
|
||||
/**
|
||||
* Theme REST resources.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
@Path("/theme")
|
||||
public class ThemeResource extends BaseResource {
|
||||
/**
|
||||
* Returns the list of all themes.
|
||||
*
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response list() throws JSONException {
|
||||
ThemeDao themeDao = new ThemeDao();
|
||||
List<String> themeList = themeDao.findAll();
|
||||
JSONObject response = new JSONObject();
|
||||
List<JSONObject> items = new ArrayList<JSONObject>();
|
||||
for (String theme : themeList) {
|
||||
JSONObject item = new JSONObject();
|
||||
item.put("id", theme);
|
||||
items.add(item);
|
||||
}
|
||||
response.put("themes", items);
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,535 @@
|
||||
package com.sismics.docs.rest.resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.FormParam;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.NewCookie;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.codehaus.jettison.json.JSONArray;
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
|
||||
import com.sismics.docs.core.constant.Constants;
|
||||
import com.sismics.docs.core.dao.jpa.AuthenticationTokenDao;
|
||||
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||
import com.sismics.docs.core.model.jpa.AuthenticationToken;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
import com.sismics.docs.rest.constant.BaseFunction;
|
||||
import com.sismics.rest.exception.ClientException;
|
||||
import com.sismics.rest.exception.ForbiddenClientException;
|
||||
import com.sismics.rest.exception.ServerException;
|
||||
import com.sismics.rest.util.ValidationUtil;
|
||||
import com.sismics.security.UserPrincipal;
|
||||
import com.sismics.util.LocaleUtil;
|
||||
import com.sismics.util.filter.TokenBasedSecurityFilter;
|
||||
|
||||
/**
|
||||
* User REST resources.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
@Path("/user")
|
||||
public class UserResource extends BaseResource {
|
||||
/**
|
||||
* Creates a new user.
|
||||
*
|
||||
* @param username User's username
|
||||
* @param password Password
|
||||
* @param email E-Mail
|
||||
* @param localeId Locale ID
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@PUT
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response register(
|
||||
@FormParam("username") String username,
|
||||
@FormParam("password") String password,
|
||||
@FormParam("locale") String localeId,
|
||||
@FormParam("email") String email) throws JSONException {
|
||||
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
// Validate the input data
|
||||
username = ValidationUtil.validateLength(username, "username", 3, 50);
|
||||
ValidationUtil.validateAlphanumeric(username, "username");
|
||||
password = ValidationUtil.validateLength(password, "password", 8, 50);
|
||||
email = ValidationUtil.validateLength(email, "email", 3, 50);
|
||||
ValidationUtil.validateEmail(email, "email");
|
||||
|
||||
// Create the user
|
||||
User user = new User();
|
||||
user.setRoleId(Constants.DEFAULT_USER_ROLE);
|
||||
user.setUsername(username);
|
||||
user.setPassword(password);
|
||||
user.setEmail(email);
|
||||
user.setCreateDate(new Date());
|
||||
|
||||
if (localeId == null) {
|
||||
// Set the locale from the HTTP headers
|
||||
localeId = LocaleUtil.getLocaleIdFromAcceptLanguage(request.getHeader("Accept-Language"));
|
||||
}
|
||||
user.setLocaleId(localeId);
|
||||
|
||||
// Create the user
|
||||
UserDao userDao = new UserDao();
|
||||
try {
|
||||
userDao.create(user);
|
||||
} catch (Exception e) {
|
||||
if ("AlreadyExistingUsername".equals(e.getMessage())) {
|
||||
throw new ServerException("AlreadyExistingUsername", "Login already used", e);
|
||||
} else {
|
||||
throw new ServerException("UnknownError", "Unknown Server Error", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Always return OK
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("status", "ok");
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates user informations.
|
||||
*
|
||||
* @param password Password
|
||||
* @param email E-Mail
|
||||
* @param themeId Theme
|
||||
* @param localeId Locale ID
|
||||
* @param displayTitleWeb Display only article titles (web application).
|
||||
* @param displayTitleMobile Display only article titles (mobile application).
|
||||
* @param displayUnreadWeb Display only unread titles (web application).
|
||||
* @param displayUnreadMobile Display only unread titles (mobile application).
|
||||
* @param firstConnection True if the user hasn't acknowledged the first connection wizard yet.
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@POST
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response update(
|
||||
@FormParam("password") String password,
|
||||
@FormParam("email") String email,
|
||||
@FormParam("theme") String themeId,
|
||||
@FormParam("locale") String localeId,
|
||||
@FormParam("first_connection") Boolean firstConnection) throws JSONException {
|
||||
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Validate the input data
|
||||
password = ValidationUtil.validateLength(password, "password", 8, 50, true);
|
||||
email = ValidationUtil.validateLength(email, "email", null, 100, true);
|
||||
localeId = ValidationUtil.validateLocale(localeId, "locale", true);
|
||||
themeId = ValidationUtil.validateTheme(themeId, "theme", true);
|
||||
|
||||
// Update the user
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(principal.getName());
|
||||
if (email != null) {
|
||||
user.setEmail(email);
|
||||
}
|
||||
if (themeId != null) {
|
||||
user.setTheme(themeId);
|
||||
}
|
||||
if (localeId != null) {
|
||||
user.setLocaleId(localeId);
|
||||
}
|
||||
if (firstConnection != null && hasBaseFunction(BaseFunction.ADMIN)) {
|
||||
user.setFirstConnection(firstConnection);
|
||||
}
|
||||
|
||||
user = userDao.update(user);
|
||||
|
||||
if (StringUtils.isNotBlank(password)) {
|
||||
user.setPassword(password);
|
||||
user = userDao.updatePassword(user);
|
||||
}
|
||||
|
||||
// Always return "ok"
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("status", "ok");
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates user informations.
|
||||
*
|
||||
* @param username Username
|
||||
* @param password Password
|
||||
* @param email E-Mail
|
||||
* @param themeId Theme
|
||||
* @param localeId Locale ID
|
||||
* @param displayTitleWeb Display only article titles (web application).
|
||||
* @param displayTitleMobile Display only article titles (mobile application).
|
||||
* @param displayUnreadWeb Display only unread titles (web application).
|
||||
* @param displayUnreadMobile Display only unread titles (mobile application).
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@POST
|
||||
@Path("{username: [a-zA-Z0-9_]+}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response update(
|
||||
@PathParam("username") String username,
|
||||
@FormParam("password") String password,
|
||||
@FormParam("email") String email,
|
||||
@FormParam("theme") String themeId,
|
||||
@FormParam("locale") String localeId) throws JSONException {
|
||||
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
// Validate the input data
|
||||
password = ValidationUtil.validateLength(password, "password", 8, 50, true);
|
||||
email = ValidationUtil.validateLength(email, "email", null, 100, true);
|
||||
localeId = ValidationUtil.validateLocale(localeId, "locale", true);
|
||||
themeId = ValidationUtil.validateTheme(themeId, "theme", true);
|
||||
|
||||
// Check if the user exists
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(username);
|
||||
if (user == null) {
|
||||
throw new ClientException("UserNotFound", "The user doesn't exist");
|
||||
}
|
||||
|
||||
// Update the user
|
||||
if (email != null) {
|
||||
user.setEmail(email);
|
||||
}
|
||||
if (themeId != null) {
|
||||
user.setTheme(themeId);
|
||||
}
|
||||
if (localeId != null) {
|
||||
user.setLocaleId(localeId);
|
||||
}
|
||||
|
||||
user = userDao.update(user);
|
||||
|
||||
if (StringUtils.isNotBlank(password)) {
|
||||
// Change the password
|
||||
user.setPassword(password);
|
||||
user = userDao.updatePassword(user);
|
||||
}
|
||||
|
||||
// Always return "ok"
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("status", "ok");
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a username is available. Search only on active accounts.
|
||||
*
|
||||
* @param username Username to check
|
||||
* @return Response
|
||||
*/
|
||||
@GET
|
||||
@Path("check_username")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response checkUsername(
|
||||
@QueryParam("username") String username) throws JSONException {
|
||||
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(username);
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
if (user != null) {
|
||||
response.put("status", "ko");
|
||||
response.put("message", "Username already registered");
|
||||
} else {
|
||||
response.put("status", "ok");
|
||||
}
|
||||
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* This resource is used to authenticate the user and create a user ession.
|
||||
* The "session" is only used to identify the user, no other data is stored in the session.
|
||||
*
|
||||
* @param username Username
|
||||
* @param password Password
|
||||
* @param longLasted Remember the user next time, create a long lasted session.
|
||||
* @return Response
|
||||
*/
|
||||
@POST
|
||||
@Path("login")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response login(
|
||||
@FormParam("username") String username,
|
||||
@FormParam("password") String password,
|
||||
@FormParam("remember") boolean longLasted) throws JSONException {
|
||||
|
||||
// Validate the input data
|
||||
username = StringUtils.strip(username);
|
||||
password = StringUtils.strip(password);
|
||||
|
||||
// Get the user
|
||||
UserDao userDao = new UserDao();
|
||||
String userId = userDao.authenticate(username, password);
|
||||
if (userId == null) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Create a new session token
|
||||
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
|
||||
AuthenticationToken authenticationToken = new AuthenticationToken();
|
||||
authenticationToken.setUserId(userId);
|
||||
authenticationToken.setLongLasted(longLasted);
|
||||
String token = authenticationTokenDao.create(authenticationToken);
|
||||
|
||||
// Cleanup old session tokens
|
||||
authenticationTokenDao.deleteOldSessionToken(userId);
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
int maxAge = longLasted ? TokenBasedSecurityFilter.TOKEN_LONG_LIFETIME : -1;
|
||||
NewCookie cookie = new NewCookie(TokenBasedSecurityFilter.COOKIE_NAME, token, "/", null, null, maxAge, false);
|
||||
return Response.ok().entity(response).cookie(cookie).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs out the user and deletes the active session.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
@POST
|
||||
@Path("logout")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response logout() throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Get the value of the session token
|
||||
String authToken = null;
|
||||
if (request.getCookies() != null) {
|
||||
for (Cookie cookie : request.getCookies()) {
|
||||
if (TokenBasedSecurityFilter.COOKIE_NAME.equals(cookie.getName())) {
|
||||
authToken = cookie.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
|
||||
AuthenticationToken authenticationToken = null;
|
||||
if (authToken != null) {
|
||||
authenticationToken = authenticationTokenDao.get(authToken);
|
||||
}
|
||||
|
||||
// No token : nothing to do
|
||||
if (authenticationToken == null) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Deletes the server token
|
||||
try {
|
||||
authenticationTokenDao.delete(authToken);
|
||||
} catch (Exception e) {
|
||||
throw new ServerException("AuthenticationTokenError", "Error deleting authentication token: " + authToken, e);
|
||||
}
|
||||
|
||||
// Deletes the client token in the HTTP response
|
||||
JSONObject response = new JSONObject();
|
||||
NewCookie cookie = new NewCookie(TokenBasedSecurityFilter.COOKIE_NAME, null);
|
||||
return Response.ok().entity(response).cookie(cookie).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a user.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
@DELETE
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response delete() throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Ensure that the admin user is not deleted
|
||||
// TODO Ensure it exists at least one user left
|
||||
|
||||
// Delete the user
|
||||
UserDao userDao = new UserDao();
|
||||
userDao.delete(principal.getName());
|
||||
|
||||
// Always return ok
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("status", "ok");
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a user.
|
||||
*
|
||||
* @param username Username
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@DELETE
|
||||
@Path("{username: [a-zA-Z0-9_]+}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response delete(@PathParam("username") String username) throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
// Check if the user exists
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(username);
|
||||
if (user == null) {
|
||||
throw new ClientException("UserNotFound", "The user doesn't exist");
|
||||
}
|
||||
|
||||
// Ensure that the admin user is not deleted
|
||||
// TODO Ensure it exists at least one user left
|
||||
|
||||
// Delete the user
|
||||
userDao.delete(user.getUsername());
|
||||
|
||||
// Always return ok
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("status", "ok");
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
/**
|
||||
* Returns the information about the connected user.
|
||||
*
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response info() throws JSONException {
|
||||
JSONObject response = new JSONObject();
|
||||
if (!authenticate()) {
|
||||
response.put("anonymous", true);
|
||||
|
||||
String localeId = LocaleUtil.getLocaleIdFromAcceptLanguage(request.getHeader("Accept-Language"));
|
||||
response.put("locale", localeId);
|
||||
|
||||
// Check if admin has the default password
|
||||
UserDao userDao = new UserDao();
|
||||
User adminUser = userDao.getById("admin");
|
||||
if (adminUser != null && adminUser.getDeleteDate() == null) {
|
||||
response.put("is_default_password", Constants.DEFAULT_ADMIN_PASSWORD.equals(adminUser.getPassword()));
|
||||
}
|
||||
} else {
|
||||
response.put("anonymous", false);
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getById(principal.getId());
|
||||
response.put("username", user.getUsername());
|
||||
response.put("email", user.getEmail());
|
||||
response.put("theme", user.getTheme());
|
||||
response.put("locale", user.getLocaleId());
|
||||
response.put("first_connection", user.isFirstConnection());
|
||||
JSONArray baseFunctions = new JSONArray(((UserPrincipal) principal).getBaseFunctionSet());
|
||||
response.put("base_functions", baseFunctions);
|
||||
response.put("is_default_password", hasBaseFunction(BaseFunction.ADMIN) && Constants.DEFAULT_ADMIN_PASSWORD.equals(user.getPassword()));
|
||||
}
|
||||
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the information about a user.
|
||||
*
|
||||
* @param username Username
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@GET
|
||||
@Path("{username: [a-zA-Z0-9_]+}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response view(@PathParam("username") String username) throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(username);
|
||||
if (user == null) {
|
||||
throw new ClientException("UserNotFound", "The user doesn't exist");
|
||||
}
|
||||
|
||||
response.put("username", user.getUsername());
|
||||
response.put("email", user.getEmail());
|
||||
response.put("theme", user.getTheme());
|
||||
response.put("locale", user.getLocaleId());
|
||||
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all active users.
|
||||
*
|
||||
* @param limit Page limit
|
||||
* @param offset Page offset
|
||||
* @param sortColumn Sort index
|
||||
* @param asc If true, ascending sorting, else descending
|
||||
* @return Response
|
||||
* @throws JSONException
|
||||
*/
|
||||
@GET
|
||||
@Path("list")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response list(
|
||||
@QueryParam("limit") Integer limit,
|
||||
@QueryParam("offset") Integer offset,
|
||||
@QueryParam("sort_column") Integer sortColumn,
|
||||
@QueryParam("asc") Boolean asc) throws JSONException {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
List<JSONObject> users = new ArrayList<JSONObject>();
|
||||
|
||||
PaginatedList<UserDto> paginatedList = PaginatedLists.create(limit, offset);
|
||||
SortCriteria sortCriteria = new SortCriteria(sortColumn, asc);
|
||||
|
||||
UserDao userDao = new UserDao();
|
||||
userDao.findAll(paginatedList, sortCriteria);
|
||||
for (UserDto userDto : paginatedList.getResultList()) {
|
||||
JSONObject user = new JSONObject();
|
||||
user.put("id", userDto.getId());
|
||||
user.put("username", userDto.getUsername());
|
||||
user.put("email", userDto.getEmail());
|
||||
user.put("create_date", userDto.getCreateTimestamp());
|
||||
users.add(user);
|
||||
}
|
||||
response.put("total", paginatedList.getResultCount());
|
||||
response.put("users", users);
|
||||
|
||||
return Response.ok().entity(response).build();
|
||||
}
|
||||
}
|
||||
51
docs-web/src/main/webapp/WEB-INF/web.xml
Normal file
51
docs-web/src/main/webapp/WEB-INF/web.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app id="docs"
|
||||
xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||
version="3.0">
|
||||
<display-name>Docs</display-name>
|
||||
|
||||
<!-- This filter is used to secure URLs -->
|
||||
<filter>
|
||||
<filter-name>requestContextFilter</filter-name>
|
||||
<filter-class>com.sismics.util.filter.RequestContextFilter</filter-class>
|
||||
</filter>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>requestContextFilter</filter-name>
|
||||
<url-pattern>/api/*</url-pattern>
|
||||
<url-pattern>*.jsp</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<!-- This filter is used to secure URLs -->
|
||||
<filter>
|
||||
<filter-name>tokenBasedSecurityFilter</filter-name>
|
||||
<filter-class>com.sismics.util.filter.TokenBasedSecurityFilter</filter-class>
|
||||
</filter>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>tokenBasedSecurityFilter</filter-name>
|
||||
<url-pattern>/api/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<!-- Welcome files -->
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.html</welcome-file>
|
||||
</welcome-file-list>
|
||||
|
||||
<!-- Jersey -->
|
||||
<servlet>
|
||||
<servlet-name>Jersey REST Service</servlet-name>
|
||||
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
|
||||
<init-param>
|
||||
<param-name>com.sun.jersey.config.property.packages</param-name>
|
||||
<param-value>com.sismics.docs.rest.resource</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>Jersey REST Service</servlet-name>
|
||||
<url-pattern>/api/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
</web-app>
|
||||
BIN
docs-web/src/main/webapp/img/glyphicons-halflings-white.png
Normal file
BIN
docs-web/src/main/webapp/img/glyphicons-halflings-white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
BIN
docs-web/src/main/webapp/img/glyphicons-halflings.png
Normal file
BIN
docs-web/src/main/webapp/img/glyphicons-halflings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
42
docs-web/src/main/webapp/index.html
Normal file
42
docs-web/src/main/webapp/index.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<!doctype html>
|
||||
<html ng-app="docs">
|
||||
<head>
|
||||
<title>Sismics Docs</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<link rel="stylesheet" href="style/bootstrap.css" type="text/css" />
|
||||
<link rel="stylesheet/less" href="style/main.less" type="text/css" />
|
||||
<script>
|
||||
less = {
|
||||
env: 'development', // or "production"
|
||||
async: false, // load imports async
|
||||
fileAsync: false, // load imports async when in a page under a file protocol
|
||||
poll: 1000, // when in watch mode, time in ms between polls
|
||||
dumpLineNumbers: 'all', // or "mediaQuery" or "comments"
|
||||
};
|
||||
</script>
|
||||
<script src="lib/jquery.js" type="text/javascript"></script>
|
||||
<script src="lib/less.js" type="text/javascript"></script>
|
||||
<script src="lib/underscore.js" type="text/javascript"></script>
|
||||
<script src="lib/angular/angular.js" type="text/javascript"></script>
|
||||
<script src="lib/angular.ui-router.js" type="text/javascript"></script>
|
||||
<script src="lib/angular.ui-bootstrap.js" type="text/javascript"></script>
|
||||
<script src="lib/angular.restangular.js" type="text/javascript"></script>
|
||||
<script src="js/app.js" type="text/javascript"></script>
|
||||
<script src="js/controller/Main.js" type="text/javascript"></script>
|
||||
<script src="js/controller/Document.js" type="text/javascript"></script>
|
||||
<script src="js/controller/DocumentEdit.js" type="text/javascript"></script>
|
||||
<script src="js/controller/DocumentView.js" type="text/javascript"></script>
|
||||
<script src="js/controller/Login.js" type="text/javascript"></script>
|
||||
<script src="js/service/User.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar">
|
||||
<div class="navbar-inner">
|
||||
<a class="brand" href="#">Sismics Docs</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main ui-view="page">
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
117
docs-web/src/main/webapp/js/app.js
Normal file
117
docs-web/src/main/webapp/js/app.js
Normal file
@@ -0,0 +1,117 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Trackino application.
|
||||
*/
|
||||
var App = angular.module('docs', ['ui.state', 'ui.bootstrap', 'restangular'])
|
||||
|
||||
/**
|
||||
* Configuring modules.
|
||||
*/
|
||||
.config(function($stateProvider, $httpProvider, $routeProvider, RestangularProvider) {
|
||||
// Configuring UI Router
|
||||
$stateProvider
|
||||
.state('main', {
|
||||
url: '',
|
||||
views: {
|
||||
'page': {
|
||||
templateUrl: 'partial/main.html',
|
||||
controller: 'Main'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('document', {
|
||||
url: '/document',
|
||||
views: {
|
||||
'page': {
|
||||
templateUrl: 'partial/document.html',
|
||||
controller: 'Document'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('document.add', {
|
||||
url: '/add',
|
||||
views: {
|
||||
'document': {
|
||||
templateUrl: 'partial/document.edit.html',
|
||||
controller: 'DocumentEdit'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('document.edit', {
|
||||
url: '/edit/:id',
|
||||
views: {
|
||||
'document': {
|
||||
templateUrl: 'partial/document.edit.html',
|
||||
controller: 'DocumentEdit'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('document.view', {
|
||||
url: '/view/:id',
|
||||
views: {
|
||||
'document': {
|
||||
templateUrl: 'partial/document.view.html',
|
||||
controller: 'DocumentView'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('login', {
|
||||
url: '/login',
|
||||
views: {
|
||||
'page': {
|
||||
templateUrl: 'partial/login.html',
|
||||
controller: 'Login'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Configuring Restangular
|
||||
RestangularProvider.setBaseUrl('api');
|
||||
|
||||
// Configuring $http
|
||||
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
|
||||
$httpProvider.defaults.transformRequest = [function(data) {
|
||||
var param = function(obj) {
|
||||
var query = '';
|
||||
var name, value, fullSubName, subName, subValue, innerObj, i;
|
||||
|
||||
for(name in obj) {
|
||||
value = obj[name];
|
||||
|
||||
if(value instanceof Array) {
|
||||
for(i=0; i<value.length; ++i) {
|
||||
subValue = value[i];
|
||||
fullSubName = name + '[' + i + ']';
|
||||
innerObj = {};
|
||||
innerObj[fullSubName] = subValue;
|
||||
query += param(innerObj) + '&';
|
||||
}
|
||||
} else if(value instanceof Object) {
|
||||
for(subName in value) {
|
||||
subValue = value[subName];
|
||||
fullSubName = name + '[' + subName + ']';
|
||||
innerObj = {};
|
||||
innerObj[fullSubName] = subValue;
|
||||
query += param(innerObj) + '&';
|
||||
}
|
||||
}
|
||||
else if(value !== undefined && value !== null) {
|
||||
query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
|
||||
}
|
||||
}
|
||||
|
||||
return query.length ? query.substr(0, query.length - 1) : query;
|
||||
};
|
||||
|
||||
return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
|
||||
}];
|
||||
})
|
||||
|
||||
/**
|
||||
* Application initialization.
|
||||
*/
|
||||
.run(function($rootScope, $state, $stateParams) {
|
||||
$rootScope.$state = $state;
|
||||
$rootScope.$stateParams = $stateParams;
|
||||
});
|
||||
34
docs-web/src/main/webapp/js/controller/Document.js
Normal file
34
docs-web/src/main/webapp/js/controller/Document.js
Normal file
@@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Document controller.
|
||||
*/
|
||||
App.controller('Document', function($scope, $state, Restangular) {
|
||||
/**
|
||||
* Load documents.
|
||||
*/
|
||||
$scope.loadDocuments = function() {
|
||||
Restangular.one('document')
|
||||
.getList('list', {
|
||||
offset: 0,
|
||||
limit: 30
|
||||
})
|
||||
.then(function(data) {
|
||||
$scope.documents = data.documents;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Go to add document form.
|
||||
*/
|
||||
$scope.addDocument = function() {
|
||||
$state.transitionTo('document.add');
|
||||
};
|
||||
|
||||
$scope.viewDocument = function(id) {
|
||||
$state.transitionTo('document.view', { id: id });
|
||||
};
|
||||
|
||||
// Initial documents loading
|
||||
$scope.loadDocuments();
|
||||
});
|
||||
37
docs-web/src/main/webapp/js/controller/DocumentEdit.js
Normal file
37
docs-web/src/main/webapp/js/controller/DocumentEdit.js
Normal file
@@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Document edition controller.
|
||||
*/
|
||||
App.controller('DocumentEdit', function($scope, $state, $stateParams, Restangular) {
|
||||
/**
|
||||
* Returns true if in edit mode (false in add mode).
|
||||
*/
|
||||
$scope.isEdit = function() {
|
||||
return $stateParams.id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Edit a document.
|
||||
*/
|
||||
$scope.edit = function() {
|
||||
if ($scope.isEdit()) {
|
||||
// TODO
|
||||
} else {
|
||||
Restangular
|
||||
.one('document')
|
||||
.put($scope.document)
|
||||
.then(function() {
|
||||
$scope.document = {};
|
||||
$scope.loadDocuments();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel edition.
|
||||
*/
|
||||
$scope.cancel = function() {
|
||||
$state.transitionTo('document');
|
||||
};
|
||||
});
|
||||
7
docs-web/src/main/webapp/js/controller/DocumentView.js
Normal file
7
docs-web/src/main/webapp/js/controller/DocumentView.js
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Document view controller.
|
||||
*/
|
||||
App.controller('DocumentView', function($scope, Restangular) {
|
||||
});
|
||||
18
docs-web/src/main/webapp/js/controller/Login.js
Normal file
18
docs-web/src/main/webapp/js/controller/Login.js
Normal file
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Login controller.
|
||||
*/
|
||||
App.controller('Login', function($scope, $state, $dialog, User) {
|
||||
$scope.login = function() {
|
||||
User.login($scope.user).then(function() {
|
||||
$state.transitionTo('browse');
|
||||
}, function() {
|
||||
var title = 'Login failed';
|
||||
var msg = 'Username or password invalid';
|
||||
var btns = [{result:'ok', label: 'OK', cssClass: 'btn-primary'}];
|
||||
|
||||
$dialog.messageBox(title, msg, btns).open();
|
||||
});
|
||||
};
|
||||
});
|
||||
14
docs-web/src/main/webapp/js/controller/Main.js
Normal file
14
docs-web/src/main/webapp/js/controller/Main.js
Normal file
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Main controller.
|
||||
*/
|
||||
App.controller('Main', function($scope, $state, User) {
|
||||
User.userInfo(true).then(function(data) {
|
||||
if (data.anonymous) {
|
||||
$state.transitionTo('login');
|
||||
} else {
|
||||
$state.transitionTo('document');
|
||||
}
|
||||
});
|
||||
});
|
||||
28
docs-web/src/main/webapp/js/service/User.js
Normal file
28
docs-web/src/main/webapp/js/service/User.js
Normal file
@@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* User service.
|
||||
*/
|
||||
App.factory('User', function(Restangular) {
|
||||
var userInfo = null;
|
||||
|
||||
return {
|
||||
/**
|
||||
* Returns user info.
|
||||
* @param force If true, force reloading data
|
||||
*/
|
||||
userInfo: function(force) {
|
||||
if (userInfo == null || force) {
|
||||
userInfo = Restangular.one('user').get();
|
||||
}
|
||||
return userInfo;
|
||||
},
|
||||
|
||||
/**
|
||||
* Login an user.
|
||||
*/
|
||||
login: function(user) {
|
||||
return Restangular.one('user').post('login', user);
|
||||
}
|
||||
}
|
||||
});
|
||||
782
docs-web/src/main/webapp/lib/angular.restangular.js
Normal file
782
docs-web/src/main/webapp/lib/angular.restangular.js
Normal file
@@ -0,0 +1,782 @@
|
||||
'use strict';
|
||||
|
||||
(function() {
|
||||
|
||||
var module = angular.module('restangular', []);
|
||||
|
||||
module.provider('Restangular', function() {
|
||||
// Configuration
|
||||
var Configurer = {};
|
||||
Configurer.init = function(object, config) {
|
||||
/**
|
||||
* Those are HTTP safe methods for which there is no need to pass any data with the request.
|
||||
*/
|
||||
var safeMethods= ["get", "head", "options", "trace"];
|
||||
config.isSafe = function(operation) {
|
||||
return _.contains(safeMethods, operation.toLowerCase());
|
||||
};
|
||||
/**
|
||||
* This is the BaseURL to be used with Restangular
|
||||
*/
|
||||
config.baseUrl = _.isUndefined(config.baseUrl) ? "" : config.baseUrl;
|
||||
object.setBaseUrl = function(newBaseUrl) {
|
||||
config.baseUrl = _.last(newBaseUrl) === "/"
|
||||
? _.initial(newBaseUrl).join("")
|
||||
: newBaseUrl;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the extra fields to keep from the parents
|
||||
*/
|
||||
config.extraFields = config.extraFields || [];
|
||||
object.setExtraFields = function(newExtraFields) {
|
||||
config.extraFields = newExtraFields;
|
||||
};
|
||||
|
||||
/**
|
||||
* Some default $http parameter to be used in EVERY call
|
||||
**/
|
||||
config.defaultHttpFields = config.defaultHttpFields || {};
|
||||
object.setDefaultHttpFields = function(values) {
|
||||
config.defaultHttpFields = values;
|
||||
};
|
||||
|
||||
config.withHttpDefaults = function(obj) {
|
||||
return _.defaults(obj, config.defaultHttpFields);
|
||||
};
|
||||
|
||||
config.defaultRequestParams = config.defaultRequestParams || {};
|
||||
object.setDefaultRequestParams = function(values) {
|
||||
config.defaultRequestParams = values;
|
||||
};
|
||||
|
||||
config.defaultHeaders = config.defaultHeaders || {};
|
||||
object.setDefaultHeaders = function(headers) {
|
||||
config.defaultHeaders = headers;
|
||||
};
|
||||
|
||||
/**
|
||||
* Method overriders will set which methods are sent via POST with an X-HTTP-Method-Override
|
||||
**/
|
||||
config.methodOverriders = config.methodOverriders || [];
|
||||
object.setMethodOverriders = function(values) {
|
||||
var overriders = _.extend([], values);
|
||||
if (config.isOverridenMethod('delete', overriders)) {
|
||||
overriders.push("remove");
|
||||
}
|
||||
config.methodOverriders = overriders;
|
||||
};
|
||||
|
||||
config.isOverridenMethod = function(method, values) {
|
||||
var search = values || config.methodOverriders;
|
||||
return !_.isUndefined(_.find(search, function(one) {
|
||||
return one.toLowerCase() === method.toLowerCase();
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the URL creator type. For now, only Path is created. In the future we'll have queryParams
|
||||
**/
|
||||
config.urlCreator = config.urlCreator || "path";
|
||||
object.setUrlCreator = function(name) {
|
||||
if (!_.has(config.urlCreatorFactory, name)) {
|
||||
throw new Error("URL Path selected isn't valid");
|
||||
}
|
||||
|
||||
config.urlCreator = name;
|
||||
};
|
||||
|
||||
/**
|
||||
* You can set the restangular fields here. The 3 required fields for Restangular are:
|
||||
*
|
||||
* id: Id of the element
|
||||
* route: name of the route of this element
|
||||
* parentResource: the reference to the parent resource
|
||||
*
|
||||
* All of this fields except for id, are handled (and created) by Restangular. By default,
|
||||
* the field values will be id, route and parentResource respectively
|
||||
*/
|
||||
config.restangularFields = config.restangularFields || {
|
||||
id: "id",
|
||||
route: "route",
|
||||
parentResource: "parentResource",
|
||||
restangularCollection: "restangularCollection"
|
||||
};
|
||||
object.setRestangularFields = function(resFields) {
|
||||
config.restangularFields =
|
||||
_.extend(config.restangularFields, resFields);
|
||||
};
|
||||
|
||||
config.setIdToElem = function(elem, id) {
|
||||
var properties = config.restangularFields.id.split('.');
|
||||
var idValue = elem;
|
||||
_.each(_.initial(properties), function(prop) {
|
||||
idValue[prop] = {};
|
||||
idValue = idValue[prop];
|
||||
});
|
||||
idValue[_.last(properties)] = id;
|
||||
};
|
||||
|
||||
config.getIdFromElem = function(elem) {
|
||||
var properties = config.restangularFields.id.split('.');
|
||||
var idValue = angular.copy(elem);
|
||||
_.each(properties, function(prop) {
|
||||
idValue = idValue[prop];
|
||||
});
|
||||
return idValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the Response parser. This is used in case your response isn't directly the data.
|
||||
* For example if you have a response like {meta: {'meta'}, data: {name: 'Gonto'}}
|
||||
* you can extract this data which is the one that needs wrapping
|
||||
*
|
||||
* The ResponseExtractor is a function that receives the response and the method executed.
|
||||
*/
|
||||
config.responseExtractor = config.responseExtractor || function(response) {
|
||||
return response;
|
||||
};
|
||||
|
||||
object.setResponseExtractor = function(extractor) {
|
||||
config.responseExtractor = extractor;
|
||||
};
|
||||
|
||||
object.setResponseInterceptor = object.setResponseExtractor;
|
||||
|
||||
/**
|
||||
* Request interceptor is called before sending an object to the server.
|
||||
*/
|
||||
config.fullRequestInterceptor = config.fullRequestInterceptor || function(element, operation,
|
||||
path, url, headers, params) {
|
||||
return {
|
||||
element: element,
|
||||
headers: headers,
|
||||
params: params
|
||||
};
|
||||
};
|
||||
|
||||
object.setRequestInterceptor = function(interceptor) {
|
||||
config.fullRequestInterceptor = function(elem, operation, path, url, headers, params) {
|
||||
return {
|
||||
headers: headers,
|
||||
params: params,
|
||||
element: interceptor(elem, operation, path, url)
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
object.setFullRequestInterceptor = function(interceptor) {
|
||||
config.fullRequestInterceptor = interceptor;
|
||||
};
|
||||
|
||||
|
||||
|
||||
config.errorInterceptor = config.errorInterceptor || function() {};
|
||||
|
||||
object.setErrorInterceptor = function(interceptor) {
|
||||
config.errorInterceptor = interceptor;
|
||||
};
|
||||
|
||||
config.onBeforeElemRestangularized = config.onBeforeElemRestangularized || function(elem) {
|
||||
return elem;
|
||||
}
|
||||
object.setOnBeforeElemRestangularized = function(post) {
|
||||
config.onBeforeElemRestangularized = post;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method is called after an element has been "Restangularized".
|
||||
*
|
||||
* It receives the element, a boolean indicating if it's an element or a collection
|
||||
* and the name of the model
|
||||
*
|
||||
*/
|
||||
config.onElemRestangularized = config.onElemRestangularized || function(elem) {
|
||||
return elem;
|
||||
};
|
||||
object.setOnElemRestangularized = function(post) {
|
||||
config.onElemRestangularized = post;
|
||||
};
|
||||
|
||||
/**
|
||||
* Depracated. Don't use this!!
|
||||
*/
|
||||
object.setListTypeIsArray = function(val) {
|
||||
|
||||
};
|
||||
|
||||
config.shouldSaveParent = config.shouldSaveParent || function() {
|
||||
return true;
|
||||
};
|
||||
object.setParentless = function(values) {
|
||||
if (_.isArray(values)) {
|
||||
config.shouldSaveParent = function(route) {
|
||||
return !_.contains(values, route);
|
||||
}
|
||||
} else if (_.isBoolean(values)) {
|
||||
config.shouldSaveParent = function() {
|
||||
return !values;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This lets you set a suffix to every request.
|
||||
*
|
||||
* For example, if your api requires that for JSon requests you do /users/123.json, you can set that
|
||||
* in here.
|
||||
*
|
||||
*
|
||||
* By default, the suffix is null
|
||||
*/
|
||||
config.suffix = _.isUndefined(config.suffix) ? null : config.suffix;
|
||||
object.setRequestSuffix = function(newSuffix) {
|
||||
config.suffix = newSuffix;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add element transformers for certain routes.
|
||||
*/
|
||||
config.transformers = config.transformers || {};
|
||||
object.addElementTransformer = function(type, secondArg, thirdArg) {
|
||||
var isCollection = null;
|
||||
var transformer = null;
|
||||
if (arguments.length === 2) {
|
||||
transformer = secondArg;
|
||||
} else {
|
||||
transformer = thirdArg;
|
||||
isCollection = secondArg;
|
||||
}
|
||||
|
||||
var typeTransformers = config.transformers[type];
|
||||
if (!typeTransformers) {
|
||||
typeTransformers = config.transformers[type] = [];
|
||||
}
|
||||
|
||||
typeTransformers.push(function(coll, elem) {
|
||||
if (_.isNull(isCollection) || (coll == isCollection)) {
|
||||
return transformer(elem);
|
||||
}
|
||||
return elem;
|
||||
});
|
||||
};
|
||||
|
||||
object.extendCollection = function(route, fn) {
|
||||
return object.addElementTransformer(route, true, fn);
|
||||
};
|
||||
|
||||
object.extendModel = function(route, fn) {
|
||||
return object.addElementTransformer(route, false, fn);
|
||||
};
|
||||
|
||||
config.transformElem = function(elem, isCollection, route, Restangular) {
|
||||
var typeTransformers = config.transformers[route];
|
||||
var changedElem = elem;
|
||||
if (typeTransformers) {
|
||||
_.each(typeTransformers, function(transformer) {
|
||||
changedElem = transformer(isCollection, changedElem);
|
||||
});
|
||||
}
|
||||
return config.onElemRestangularized(changedElem,
|
||||
isCollection, route, Restangular);
|
||||
};
|
||||
|
||||
config.fullResponse = _.isUndefined(config.fullResponse) ? false : config.fullResponse;
|
||||
object.setFullResponse = function(full) {
|
||||
config.fullResponse = full;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//Internal values and functions
|
||||
config.urlCreatorFactory = {};
|
||||
|
||||
/**
|
||||
* Base URL Creator. Base prototype for everything related to it
|
||||
**/
|
||||
|
||||
var BaseCreator = function() {
|
||||
};
|
||||
|
||||
BaseCreator.prototype.setConfig = function(config) {
|
||||
this.config = config;
|
||||
};
|
||||
|
||||
BaseCreator.prototype.parentsArray = function(current) {
|
||||
var parents = [];
|
||||
while(current) {
|
||||
parents.push(current);
|
||||
current = current[this.config.restangularFields.parentResource];
|
||||
}
|
||||
return parents.reverse();
|
||||
};
|
||||
|
||||
function RestangularResource($http, url, configurer) {
|
||||
var resource = {};
|
||||
_.each(_.keys(configurer), function(key) {
|
||||
var value = configurer[key];
|
||||
|
||||
// We don't want the ? if no params are there
|
||||
if (_.isEmpty(value.params)) {
|
||||
delete value.params;
|
||||
}
|
||||
|
||||
if (config.isSafe(value.method)) {
|
||||
|
||||
resource[key] = function() {
|
||||
return $http(_.extend(value, {
|
||||
url: url
|
||||
}));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
resource[key] = function(data) {
|
||||
return $http(_.extend(value, {
|
||||
url: url,
|
||||
data: data
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
BaseCreator.prototype.resource = function(current, $http, callHeaders, callParams, what) {
|
||||
|
||||
var params = _.defaults(callParams || {}, this.config.defaultRequestParams);
|
||||
var headers = _.defaults(callHeaders || {}, this.config.defaultHeaders);
|
||||
|
||||
var url = this.base(current);
|
||||
url += what ? ("/" + what): '';
|
||||
url += (this.config.suffix || '');
|
||||
|
||||
return RestangularResource($http, url, {
|
||||
getList: this.config.withHttpDefaults({method: 'GET',
|
||||
params: params,
|
||||
headers: headers || {}}),
|
||||
|
||||
get: this.config.withHttpDefaults({method: 'GET',
|
||||
params: params,
|
||||
headers: headers || {}}),
|
||||
|
||||
put: this.config.withHttpDefaults({method: 'PUT',
|
||||
params: params,
|
||||
headers: headers || {}}),
|
||||
|
||||
post: this.config.withHttpDefaults({method: 'POST',
|
||||
params: params,
|
||||
headers: headers || {}}),
|
||||
|
||||
remove: this.config.withHttpDefaults({method: 'DELETE',
|
||||
params: params,
|
||||
headers: headers || {}}),
|
||||
|
||||
head: this.config.withHttpDefaults({method: 'HEAD',
|
||||
params: params,
|
||||
headers: headers || {}}),
|
||||
|
||||
trace: this.config.withHttpDefaults({method: 'TRACE',
|
||||
params: params,
|
||||
headers: headers || {}}),
|
||||
|
||||
options: this.config.withHttpDefaults({method: 'OPTIONS',
|
||||
params: params,
|
||||
headers: headers || {}}),
|
||||
|
||||
patch: this.config.withHttpDefaults({method: 'PATCH',
|
||||
params: params,
|
||||
headers: headers || {}})
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the Path URL creator. It uses Path to show Hierarchy in the Rest API.
|
||||
* This means that if you have an Account that then has a set of Buildings, a URL to a building
|
||||
* would be /accounts/123/buildings/456
|
||||
**/
|
||||
var Path = function() {
|
||||
};
|
||||
|
||||
Path.prototype = new BaseCreator();
|
||||
|
||||
Path.prototype.base = function(current) {
|
||||
var __this = this;
|
||||
return this.config.baseUrl + _.reduce(this.parentsArray(current), function(acum, elem) {
|
||||
var currUrl = acum + "/" + elem[__this.config.restangularFields.route];
|
||||
|
||||
if (!elem[__this.config.restangularFields.restangularCollection]) {
|
||||
var elemId = __this.config.getIdFromElem(elem);
|
||||
if ("" !== elemId && !_.isUndefined(elemId) && !_.isNull(elemId)) {
|
||||
currUrl += "/" + elemId;
|
||||
}
|
||||
}
|
||||
|
||||
return currUrl;
|
||||
}, '');
|
||||
};
|
||||
|
||||
|
||||
|
||||
Path.prototype.fetchUrl = function(current, what) {
|
||||
var baseUrl = this.base(current);
|
||||
if (what) {
|
||||
baseUrl += "/" + what;
|
||||
}
|
||||
return baseUrl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
config.urlCreatorFactory.path = Path;
|
||||
|
||||
}
|
||||
|
||||
var globalConfiguration = {};
|
||||
|
||||
Configurer.init(this, globalConfiguration);
|
||||
|
||||
|
||||
|
||||
|
||||
this.$get = ['$http', '$q', function($http, $q) {
|
||||
|
||||
function createServiceForConfiguration(config) {
|
||||
var service = {};
|
||||
|
||||
var urlHandler = new config.urlCreatorFactory[config.urlCreator]();
|
||||
urlHandler.setConfig(config);
|
||||
|
||||
function restangularizeBase(parent, elem, route) {
|
||||
elem[config.restangularFields.route] = route;
|
||||
elem.getRestangularUrl = _.bind(urlHandler.fetchUrl, urlHandler, elem);
|
||||
elem.addRestangularMethod = _.bind(addRestangularMethodFunction, elem);
|
||||
|
||||
// RequestLess connection
|
||||
elem.one = _.bind(one, elem, elem);
|
||||
elem.all = _.bind(all, elem, elem);
|
||||
if (parent && config.shouldSaveParent(route)) {
|
||||
var restangularFieldsForParent = _.union(
|
||||
_.values( _.pick(config.restangularFields, ['id', 'route', 'parentResource']) ),
|
||||
config.extraFields
|
||||
);
|
||||
elem[config.restangularFields.parentResource] = _.pick(parent, restangularFieldsForParent);
|
||||
} else {
|
||||
elem[config.restangularFields.parentResource] = null;
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function one(parent, route, id) {
|
||||
var elem = {};
|
||||
config.setIdToElem(elem, id);
|
||||
return restangularizeElem(parent, elem , route);
|
||||
}
|
||||
|
||||
|
||||
function all(parent, route) {
|
||||
return restangularizeCollection(parent, {} , route, true);
|
||||
}
|
||||
// Promises
|
||||
function restangularizePromise(promise, isCollection) {
|
||||
promise.call = _.bind(promiseCall, promise);
|
||||
promise.get = _.bind(promiseGet, promise);
|
||||
promise[config.restangularFields.restangularCollection] = isCollection;
|
||||
if (isCollection) {
|
||||
promise.push = _.bind(promiseCall, promise, "push");
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
function promiseCall(method) {
|
||||
var deferred = $q.defer();
|
||||
var callArgs = arguments;
|
||||
this.then(function(val) {
|
||||
var params = Array.prototype.slice.call(callArgs, 1);
|
||||
var func = val[method];
|
||||
func.apply(val, params);
|
||||
deferred.resolve(val);
|
||||
});
|
||||
return restangularizePromise(deferred.promise, this[config.restangularFields.restangularCollection]);
|
||||
}
|
||||
|
||||
function promiseGet(what) {
|
||||
var deferred = $q.defer();
|
||||
this.then(function(val) {
|
||||
deferred.resolve(val[what]);
|
||||
});
|
||||
return restangularizePromise(deferred.promise, this[config.restangularFields.restangularCollection]);
|
||||
}
|
||||
|
||||
function resolvePromise(deferred, response, data) {
|
||||
if (config.fullResponse) {
|
||||
return deferred.resolve(_.extend(response, {
|
||||
data: data
|
||||
}));
|
||||
} else {
|
||||
deferred.resolve(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Elements
|
||||
|
||||
function stripRestangular(elem) {
|
||||
return _.omit(elem, _.values(_.omit(config.restangularFields, 'id')));
|
||||
}
|
||||
|
||||
function addCustomOperation(elem) {
|
||||
elem.customOperation = _.bind(customFunction, elem);
|
||||
_.each(["put", "post", "get", "delete"], function(oper) {
|
||||
_.each(["do", "custom"], function(alias) {
|
||||
var callOperation = oper === 'delete' ? 'remove' : oper;
|
||||
var name = alias + oper.toUpperCase();
|
||||
elem[name] = _.bind(customFunction, elem, callOperation);
|
||||
});
|
||||
});
|
||||
elem.customGETLIST = _.bind(fetchFunction, elem);
|
||||
elem.doGETLIST = elem.customGETLIST;
|
||||
}
|
||||
|
||||
function copyRestangularizedElement(fromElement) {
|
||||
var copiedElement = angular.copy(fromElement);
|
||||
return restangularizeElem(copiedElement[config.restangularFields.parentResource],
|
||||
copiedElement, copiedElement[config.restangularFields.route]);
|
||||
}
|
||||
|
||||
function restangularizeElem(parent, element, route) {
|
||||
var elem = config.onBeforeElemRestangularized(element, false, route);
|
||||
|
||||
var localElem = restangularizeBase(parent, elem, route);
|
||||
localElem[config.restangularFields.restangularCollection] = false;
|
||||
localElem.get = _.bind(getFunction, localElem);
|
||||
localElem.getList = _.bind(fetchFunction, localElem);
|
||||
localElem.put = _.bind(putFunction, localElem);
|
||||
localElem.post = _.bind(postFunction, localElem);
|
||||
localElem.remove = _.bind(deleteFunction, localElem);
|
||||
localElem.head = _.bind(headFunction, localElem);
|
||||
localElem.trace = _.bind(traceFunction, localElem);
|
||||
localElem.options = _.bind(optionsFunction, localElem);
|
||||
localElem.patch = _.bind(patchFunction, localElem);
|
||||
|
||||
addCustomOperation(localElem);
|
||||
return config.transformElem(localElem, false, route, service);
|
||||
}
|
||||
|
||||
function restangularizeCollection(parent, element, route) {
|
||||
var elem = config.onBeforeElemRestangularized(element, true, route);
|
||||
|
||||
var localElem = restangularizeBase(parent, elem, route);
|
||||
localElem[config.restangularFields.restangularCollection] = true;
|
||||
localElem.post = _.bind(postFunction, localElem, null);
|
||||
localElem.head = _.bind(headFunction, localElem);
|
||||
localElem.trace = _.bind(traceFunction, localElem);
|
||||
localElem.putElement = _.bind(putElementFunction, localElem);
|
||||
localElem.options = _.bind(optionsFunction, localElem);
|
||||
localElem.patch = _.bind(patchFunction, localElem);
|
||||
localElem.getList = _.bind(fetchFunction, localElem, null);
|
||||
|
||||
addCustomOperation(localElem);
|
||||
return config.transformElem(localElem, true, route, service);
|
||||
}
|
||||
|
||||
function putElementFunction(idx, params, headers) {
|
||||
var __this = this;
|
||||
var elemToPut = this[idx];
|
||||
var deferred = $q.defer();
|
||||
elemToPut.put(params, headers).then(function(serverElem) {
|
||||
var newArray = copyRestangularizedElement(__this);
|
||||
newArray[idx] = serverElem;
|
||||
deferred.resolve(newArray);
|
||||
}, function(response) {
|
||||
deferred.reject(response);
|
||||
});
|
||||
|
||||
return restangularizePromise(deferred.promise, true)
|
||||
}
|
||||
|
||||
|
||||
function fetchFunction(what, reqParams, headers) {
|
||||
var __this = this;
|
||||
var deferred = $q.defer();
|
||||
var operation = 'getList';
|
||||
var url = urlHandler.fetchUrl(this, what);
|
||||
var whatFetched = what || __this[config.restangularFields.route];
|
||||
|
||||
|
||||
var request = config.fullRequestInterceptor(null, operation,
|
||||
whatFetched, url, headers || {}, reqParams || {});
|
||||
|
||||
urlHandler.resource(this, $http, request.headers, request.params, what).getList().then(function(response) {
|
||||
var resData = response.data;
|
||||
var data = config.responseExtractor(resData, operation, whatFetched, url);
|
||||
var processedData = _.map(data, function(elem) {
|
||||
if (!__this[config.restangularFields.restangularCollection]) {
|
||||
return restangularizeElem(__this, elem, what);
|
||||
} else {
|
||||
return restangularizeElem(__this[config.restangularFields.parentResource],
|
||||
elem, __this[config.restangularFields.route]);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
processedData = _.extend(data, processedData);
|
||||
if (!__this[config.restangularFields.restangularCollection]) {
|
||||
resolvePromise(deferred, response, restangularizeCollection(__this, processedData, what));
|
||||
} else {
|
||||
resolvePromise(deferred, response, restangularizeCollection(null, processedData, __this[config.restangularFields.route]));
|
||||
}
|
||||
}, function error(response) {
|
||||
config.errorInterceptor(response);
|
||||
deferred.reject(response);
|
||||
});
|
||||
|
||||
return restangularizePromise(deferred.promise, true);
|
||||
}
|
||||
|
||||
function elemFunction(operation, what, params, obj, headers) {
|
||||
var __this = this;
|
||||
var deferred = $q.defer();
|
||||
var resParams = params || {};
|
||||
var resObj = obj || this;
|
||||
var route = what || this[config.restangularFields.route];
|
||||
var fetchUrl = urlHandler.fetchUrl(this, what);
|
||||
|
||||
var callObj = obj || stripRestangular(this);
|
||||
var request = config.fullRequestInterceptor(callObj, operation, route, fetchUrl,
|
||||
headers || {}, resParams || {});
|
||||
|
||||
var okCallback = function(response) {
|
||||
var resData = response.data;
|
||||
var elem = config.responseExtractor(resData, operation, route, fetchUrl) || resObj;
|
||||
if (operation === "post" && !__this[config.restangularFields.restangularCollection]) {
|
||||
resolvePromise(deferred, response, restangularizeElem(__this, elem, what));
|
||||
} else {
|
||||
resolvePromise(deferred, response, restangularizeElem(__this[config.restangularFields.parentResource], elem, __this[config.restangularFields.route]));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var errorCallback = function(response) {
|
||||
config.errorInterceptor(response);
|
||||
deferred.reject(response);
|
||||
};
|
||||
// Overring HTTP Method
|
||||
var callOperation = operation;
|
||||
var callHeaders = _.extend({}, request.headers);
|
||||
var isOverrideOperation = config.isOverridenMethod(operation);
|
||||
if (isOverrideOperation) {
|
||||
callOperation = 'post';
|
||||
callHeaders = _.extend(callHeaders, {'X-HTTP-Method-Override': operation});
|
||||
}
|
||||
|
||||
if (config.isSafe(operation)) {
|
||||
if (isOverrideOperation) {
|
||||
urlHandler.resource(this, $http, callHeaders, request.params,
|
||||
what)[callOperation]({}).then(okCallback, errorCallback);
|
||||
} else {
|
||||
urlHandler.resource(this, $http, callHeaders, request.params,
|
||||
what)[callOperation]().then(okCallback, errorCallback);
|
||||
}
|
||||
} else {
|
||||
urlHandler.resource(this, $http, callHeaders, request.params,
|
||||
what)[callOperation](request.element).then(okCallback, errorCallback);
|
||||
}
|
||||
|
||||
return restangularizePromise(deferred.promise);
|
||||
}
|
||||
|
||||
function getFunction(params, headers) {
|
||||
return _.bind(elemFunction, this)("get", undefined, params, undefined, headers);
|
||||
}
|
||||
|
||||
function deleteFunction(params, headers) {
|
||||
return _.bind(elemFunction, this)("remove", undefined, params, undefined, headers);
|
||||
}
|
||||
|
||||
function putFunction(params, headers) {
|
||||
return _.bind(elemFunction, this)("put", undefined, params, undefined, headers);
|
||||
}
|
||||
|
||||
function postFunction(what, elem, params, headers) {
|
||||
return _.bind(elemFunction, this)("post", what, params, elem, headers);
|
||||
}
|
||||
|
||||
function headFunction(params, headers) {
|
||||
return _.bind(elemFunction, this)("head", undefined, params, undefined, headers);
|
||||
}
|
||||
|
||||
function traceFunction(params, headers) {
|
||||
return _.bind(elemFunction, this)("trace", undefined, params, undefined, headers);
|
||||
}
|
||||
|
||||
function optionsFunction(params, headers) {
|
||||
return _.bind(elemFunction, this)("options", undefined, params, undefined, headers);
|
||||
}
|
||||
|
||||
function patchFunction(elem, params, headers) {
|
||||
return _.bind(elemFunction, this)("patch", undefined, params, elem, headers);
|
||||
}
|
||||
|
||||
function customFunction(operation, path, params, headers, elem) {
|
||||
return _.bind(elemFunction, this)(operation, path, params, elem, headers);
|
||||
}
|
||||
|
||||
function addRestangularMethodFunction(name, operation, path, defaultParams, defaultHeaders, defaultElem) {
|
||||
var bindedFunction;
|
||||
if (operation === 'getList') {
|
||||
bindedFunction = _.bind(fetchFunction, this, path);
|
||||
} else {
|
||||
bindedFunction = _.bind(customFunction, this, operation, path);
|
||||
}
|
||||
|
||||
this[name] = function(params, headers, elem) {
|
||||
var callParams = _.defaults({
|
||||
params: params,
|
||||
headers: headers,
|
||||
elem: elem
|
||||
}, {
|
||||
params: defaultParams,
|
||||
headers: defaultHeaders,
|
||||
elem: defaultElem
|
||||
});
|
||||
return bindedFunction(callParams.params, callParams.headers, callParams.elem);
|
||||
}
|
||||
}
|
||||
|
||||
function withConfigurationFunction(configurer) {
|
||||
var newConfig = angular.copy(globalConfiguration);
|
||||
Configurer.init(newConfig, newConfig);
|
||||
configurer(newConfig);
|
||||
return createServiceForConfiguration(newConfig);
|
||||
}
|
||||
|
||||
|
||||
Configurer.init(service, config);
|
||||
|
||||
service.copy = _.bind(copyRestangularizedElement, service);
|
||||
|
||||
service.withConfig = _.bind(withConfigurationFunction, service);
|
||||
|
||||
service.one = _.bind(one, service, null);
|
||||
|
||||
service.all = _.bind(all, service, null);
|
||||
|
||||
service.restangularizeElement = _.bind(restangularizeElem, service);
|
||||
|
||||
service.restangularizeCollection = _.bind(restangularizeCollection, service);
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
return createServiceForConfiguration(globalConfiguration);
|
||||
|
||||
}];
|
||||
}
|
||||
);
|
||||
|
||||
})();
|
||||
3165
docs-web/src/main/webapp/lib/angular.ui-bootstrap.js
Normal file
3165
docs-web/src/main/webapp/lib/angular.ui-bootstrap.js
Normal file
File diff suppressed because it is too large
Load Diff
1037
docs-web/src/main/webapp/lib/angular.ui-router.js
Normal file
1037
docs-web/src/main/webapp/lib/angular.ui-router.js
Normal file
File diff suppressed because it is too large
Load Diff
185
docs-web/src/main/webapp/lib/angular/angular-cookies.js
vendored
Normal file
185
docs-web/src/main/webapp/lib/angular/angular-cookies.js
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* @license AngularJS v1.1.5
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ngCookies
|
||||
*/
|
||||
|
||||
|
||||
angular.module('ngCookies', ['ng']).
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ngCookies.$cookies
|
||||
* @requires $browser
|
||||
*
|
||||
* @description
|
||||
* Provides read/write access to browser's cookies.
|
||||
*
|
||||
* Only a simple Object is exposed and by adding or removing properties to/from
|
||||
* this object, new cookies are created/deleted at the end of current $eval.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function ExampleController($cookies) {
|
||||
// Retrieving a cookie
|
||||
var favoriteCookie = $cookies.myFavorite;
|
||||
// Setting a cookie
|
||||
$cookies.myFavorite = 'oatmeal';
|
||||
}
|
||||
</script>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) {
|
||||
var cookies = {},
|
||||
lastCookies = {},
|
||||
lastBrowserCookies,
|
||||
runEval = false,
|
||||
copy = angular.copy,
|
||||
isUndefined = angular.isUndefined;
|
||||
|
||||
//creates a poller fn that copies all cookies from the $browser to service & inits the service
|
||||
$browser.addPollFn(function() {
|
||||
var currentCookies = $browser.cookies();
|
||||
if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
|
||||
lastBrowserCookies = currentCookies;
|
||||
copy(currentCookies, lastCookies);
|
||||
copy(currentCookies, cookies);
|
||||
if (runEval) $rootScope.$apply();
|
||||
}
|
||||
})();
|
||||
|
||||
runEval = true;
|
||||
|
||||
//at the end of each eval, push cookies
|
||||
//TODO: this should happen before the "delayed" watches fire, because if some cookies are not
|
||||
// strings or browser refuses to store some cookies, we update the model in the push fn.
|
||||
$rootScope.$watch(push);
|
||||
|
||||
return cookies;
|
||||
|
||||
|
||||
/**
|
||||
* Pushes all the cookies from the service to the browser and verifies if all cookies were stored.
|
||||
*/
|
||||
function push() {
|
||||
var name,
|
||||
value,
|
||||
browserCookies,
|
||||
updated;
|
||||
|
||||
//delete any cookies deleted in $cookies
|
||||
for (name in lastCookies) {
|
||||
if (isUndefined(cookies[name])) {
|
||||
$browser.cookies(name, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
//update all cookies updated in $cookies
|
||||
for(name in cookies) {
|
||||
value = cookies[name];
|
||||
if (!angular.isString(value)) {
|
||||
if (angular.isDefined(lastCookies[name])) {
|
||||
cookies[name] = lastCookies[name];
|
||||
} else {
|
||||
delete cookies[name];
|
||||
}
|
||||
} else if (value !== lastCookies[name]) {
|
||||
$browser.cookies(name, value);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
//verify what was actually stored
|
||||
if (updated){
|
||||
updated = false;
|
||||
browserCookies = $browser.cookies();
|
||||
|
||||
for (name in cookies) {
|
||||
if (cookies[name] !== browserCookies[name]) {
|
||||
//delete or reset all cookies that the browser dropped from $cookies
|
||||
if (isUndefined(browserCookies[name])) {
|
||||
delete cookies[name];
|
||||
} else {
|
||||
cookies[name] = browserCookies[name];
|
||||
}
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}]).
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ngCookies.$cookieStore
|
||||
* @requires $cookies
|
||||
*
|
||||
* @description
|
||||
* Provides a key-value (string-object) storage, that is backed by session cookies.
|
||||
* Objects put or retrieved from this storage are automatically serialized or
|
||||
* deserialized by angular's toJson/fromJson.
|
||||
* @example
|
||||
*/
|
||||
factory('$cookieStore', ['$cookies', function($cookies) {
|
||||
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngCookies.$cookieStore#get
|
||||
* @methodOf ngCookies.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Returns the value of given cookie key
|
||||
*
|
||||
* @param {string} key Id to use for lookup.
|
||||
* @returns {Object} Deserialized cookie value.
|
||||
*/
|
||||
get: function(key) {
|
||||
var value = $cookies[key];
|
||||
return value ? angular.fromJson(value) : value;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngCookies.$cookieStore#put
|
||||
* @methodOf ngCookies.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Sets a value for given cookie key
|
||||
*
|
||||
* @param {string} key Id for the `value`.
|
||||
* @param {Object} value Value to be stored.
|
||||
*/
|
||||
put: function(key, value) {
|
||||
$cookies[key] = angular.toJson(value);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngCookies.$cookieStore#remove
|
||||
* @methodOf ngCookies.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Remove given cookie
|
||||
*
|
||||
* @param {string} key Id of the key-value pair to delete.
|
||||
*/
|
||||
remove: function(key) {
|
||||
delete $cookies[key];
|
||||
}
|
||||
};
|
||||
|
||||
}]);
|
||||
|
||||
|
||||
})(window, window.angular);
|
||||
7
docs-web/src/main/webapp/lib/angular/angular-cookies.min.js
vendored
Normal file
7
docs-web/src/main/webapp/lib/angular/angular-cookies.min.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
AngularJS v1.1.5
|
||||
(c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(m,f,l){'use strict';f.module("ngCookies",["ng"]).factory("$cookies",["$rootScope","$browser",function(d,b){var c={},g={},h,i=!1,j=f.copy,k=f.isUndefined;b.addPollFn(function(){var a=b.cookies();h!=a&&(h=a,j(a,g),j(a,c),i&&d.$apply())})();i=!0;d.$watch(function(){var a,e,d;for(a in g)k(c[a])&&b.cookies(a,l);for(a in c)e=c[a],f.isString(e)?e!==g[a]&&(b.cookies(a,e),d=!0):f.isDefined(g[a])?c[a]=g[a]:delete c[a];if(d)for(a in e=b.cookies(),c)c[a]!==e[a]&&(k(e[a])?delete c[a]:c[a]=e[a])});return c}]).factory("$cookieStore",
|
||||
["$cookies",function(d){return{get:function(b){return(b=d[b])?f.fromJson(b):b},put:function(b,c){d[b]=f.toJson(c)},remove:function(b){delete d[b]}}}])})(window,window.angular);
|
||||
304
docs-web/src/main/webapp/lib/angular/angular-loader.js
vendored
Normal file
304
docs-web/src/main/webapp/lib/angular/angular-loader.js
vendored
Normal file
@@ -0,0 +1,304 @@
|
||||
/**
|
||||
* @license AngularJS v1.1.5
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
(
|
||||
|
||||
/**
|
||||
* @ngdoc interface
|
||||
* @name angular.Module
|
||||
* @description
|
||||
*
|
||||
* Interface for configuring angular {@link angular.module modules}.
|
||||
*/
|
||||
|
||||
function setupModuleLoader(window) {
|
||||
|
||||
function ensure(obj, name, factory) {
|
||||
return obj[name] || (obj[name] = factory());
|
||||
}
|
||||
|
||||
return ensure(ensure(window, 'angular', Object), 'module', function() {
|
||||
/** @type {Object.<string, angular.Module>} */
|
||||
var modules = {};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.module
|
||||
* @description
|
||||
*
|
||||
* The `angular.module` is a global place for creating and registering Angular modules. All
|
||||
* modules (angular core or 3rd party) that should be available to an application must be
|
||||
* registered using this mechanism.
|
||||
*
|
||||
*
|
||||
* # Module
|
||||
*
|
||||
* A module is a collocation of services, directives, filters, and configuration information. Module
|
||||
* is used to configure the {@link AUTO.$injector $injector}.
|
||||
*
|
||||
* <pre>
|
||||
* // Create a new module
|
||||
* var myModule = angular.module('myModule', []);
|
||||
*
|
||||
* // register a new service
|
||||
* myModule.value('appName', 'MyCoolApp');
|
||||
*
|
||||
* // configure existing services inside initialization blocks.
|
||||
* myModule.config(function($locationProvider) {
|
||||
'use strict';
|
||||
* // Configure existing providers
|
||||
* $locationProvider.hashPrefix('!');
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* Then you can create an injector and load your modules like this:
|
||||
*
|
||||
* <pre>
|
||||
* var injector = angular.injector(['ng', 'MyModule'])
|
||||
* </pre>
|
||||
*
|
||||
* However it's more likely that you'll just use
|
||||
* {@link ng.directive:ngApp ngApp} or
|
||||
* {@link angular.bootstrap} to simplify this process for you.
|
||||
*
|
||||
* @param {!string} name The name of the module to create or retrieve.
|
||||
* @param {Array.<string>=} requires If specified then new module is being created. If unspecified then the
|
||||
* the module is being retrieved for further configuration.
|
||||
* @param {Function} configFn Optional configuration function for the module. Same as
|
||||
* {@link angular.Module#config Module#config()}.
|
||||
* @returns {module} new module with the {@link angular.Module} api.
|
||||
*/
|
||||
return function module(name, requires, configFn) {
|
||||
if (requires && modules.hasOwnProperty(name)) {
|
||||
modules[name] = null;
|
||||
}
|
||||
return ensure(modules, name, function() {
|
||||
if (!requires) {
|
||||
throw Error('No module: ' + name);
|
||||
}
|
||||
|
||||
/** @type {!Array.<Array.<*>>} */
|
||||
var invokeQueue = [];
|
||||
|
||||
/** @type {!Array.<Function>} */
|
||||
var runBlocks = [];
|
||||
|
||||
var config = invokeLater('$injector', 'invoke');
|
||||
|
||||
/** @type {angular.Module} */
|
||||
var moduleInstance = {
|
||||
// Private state
|
||||
_invokeQueue: invokeQueue,
|
||||
_runBlocks: runBlocks,
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name angular.Module#requires
|
||||
* @propertyOf angular.Module
|
||||
* @returns {Array.<string>} List of module names which must be loaded before this module.
|
||||
* @description
|
||||
* Holds the list of modules which the injector will load before the current module is loaded.
|
||||
*/
|
||||
requires: requires,
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name angular.Module#name
|
||||
* @propertyOf angular.Module
|
||||
* @returns {string} Name of the module.
|
||||
* @description
|
||||
*/
|
||||
name: name,
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#provider
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name service name
|
||||
* @param {Function} providerType Construction function for creating new instance of the service.
|
||||
* @description
|
||||
* See {@link AUTO.$provide#provider $provide.provider()}.
|
||||
*/
|
||||
provider: invokeLater('$provide', 'provider'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#factory
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name service name
|
||||
* @param {Function} providerFunction Function for creating new instance of the service.
|
||||
* @description
|
||||
* See {@link AUTO.$provide#factory $provide.factory()}.
|
||||
*/
|
||||
factory: invokeLater('$provide', 'factory'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#service
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name service name
|
||||
* @param {Function} constructor A constructor function that will be instantiated.
|
||||
* @description
|
||||
* See {@link AUTO.$provide#service $provide.service()}.
|
||||
*/
|
||||
service: invokeLater('$provide', 'service'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#value
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name service name
|
||||
* @param {*} object Service instance object.
|
||||
* @description
|
||||
* See {@link AUTO.$provide#value $provide.value()}.
|
||||
*/
|
||||
value: invokeLater('$provide', 'value'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#constant
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name constant name
|
||||
* @param {*} object Constant value.
|
||||
* @description
|
||||
* Because the constant are fixed, they get applied before other provide methods.
|
||||
* See {@link AUTO.$provide#constant $provide.constant()}.
|
||||
*/
|
||||
constant: invokeLater('$provide', 'constant', 'unshift'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#animation
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name animation name
|
||||
* @param {Function} animationFactory Factory function for creating new instance of an animation.
|
||||
* @description
|
||||
*
|
||||
* Defines an animation hook that can be later used with {@link ng.directive:ngAnimate ngAnimate}
|
||||
* alongside {@link ng.directive:ngAnimate#Description common ng directives} as well as custom directives.
|
||||
* <pre>
|
||||
* module.animation('animation-name', function($inject1, $inject2) {
|
||||
* return {
|
||||
* //this gets called in preparation to setup an animation
|
||||
* setup : function(element) { ... },
|
||||
*
|
||||
* //this gets called once the animation is run
|
||||
* start : function(element, done, memo) { ... }
|
||||
* }
|
||||
* })
|
||||
* </pre>
|
||||
*
|
||||
* See {@link ng.$animationProvider#register $animationProvider.register()} and
|
||||
* {@link ng.directive:ngAnimate ngAnimate} for more information.
|
||||
*/
|
||||
animation: invokeLater('$animationProvider', 'register'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#filter
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name Filter name.
|
||||
* @param {Function} filterFactory Factory function for creating new instance of filter.
|
||||
* @description
|
||||
* See {@link ng.$filterProvider#register $filterProvider.register()}.
|
||||
*/
|
||||
filter: invokeLater('$filterProvider', 'register'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#controller
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name Controller name.
|
||||
* @param {Function} constructor Controller constructor function.
|
||||
* @description
|
||||
* See {@link ng.$controllerProvider#register $controllerProvider.register()}.
|
||||
*/
|
||||
controller: invokeLater('$controllerProvider', 'register'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#directive
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name directive name
|
||||
* @param {Function} directiveFactory Factory function for creating new instance of
|
||||
* directives.
|
||||
* @description
|
||||
* See {@link ng.$compileProvider#directive $compileProvider.directive()}.
|
||||
*/
|
||||
directive: invokeLater('$compileProvider', 'directive'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#config
|
||||
* @methodOf angular.Module
|
||||
* @param {Function} configFn Execute this function on module load. Useful for service
|
||||
* configuration.
|
||||
* @description
|
||||
* Use this method to register work which needs to be performed on module loading.
|
||||
*/
|
||||
config: config,
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#run
|
||||
* @methodOf angular.Module
|
||||
* @param {Function} initializationFn Execute this function after injector creation.
|
||||
* Useful for application initialization.
|
||||
* @description
|
||||
* Use this method to register work which should be performed when the injector is done
|
||||
* loading all modules.
|
||||
*/
|
||||
run: function(block) {
|
||||
runBlocks.push(block);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
if (configFn) {
|
||||
config(configFn);
|
||||
}
|
||||
|
||||
return moduleInstance;
|
||||
|
||||
/**
|
||||
* @param {string} provider
|
||||
* @param {string} method
|
||||
* @param {String=} insertMethod
|
||||
* @returns {angular.Module}
|
||||
*/
|
||||
function invokeLater(provider, method, insertMethod) {
|
||||
return function() {
|
||||
invokeQueue[insertMethod || 'push']([provider, method, arguments]);
|
||||
return moduleInstance;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
)(window);
|
||||
|
||||
/**
|
||||
* Closure compiler type information
|
||||
*
|
||||
* @typedef { {
|
||||
* requires: !Array.<string>,
|
||||
* invokeQueue: !Array.<Array.<*>>,
|
||||
*
|
||||
* service: function(string, Function):angular.Module,
|
||||
* factory: function(string, Function):angular.Module,
|
||||
* value: function(string, *):angular.Module,
|
||||
*
|
||||
* filter: function(string, Function):angular.Module,
|
||||
*
|
||||
* init: function(Function):angular.Module
|
||||
* } }
|
||||
*/
|
||||
angular.Module;
|
||||
|
||||
7
docs-web/src/main/webapp/lib/angular/angular-loader.min.js
vendored
Normal file
7
docs-web/src/main/webapp/lib/angular/angular-loader.min.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
AngularJS v1.1.5
|
||||
(c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(i){'use strict';function d(c,b,e){return c[b]||(c[b]=e())}return d(d(i,"angular",Object),"module",function(){var c={};return function(b,e,f){e&&c.hasOwnProperty(b)&&(c[b]=null);return d(c,b,function(){function a(a,b,d){return function(){c[d||"push"]([a,b,arguments]);return g}}if(!e)throw Error("No module: "+b);var c=[],d=[],h=a("$injector","invoke"),g={_invokeQueue:c,_runBlocks:d,requires:e,name:b,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),
|
||||
value:a("$provide","value"),constant:a("$provide","constant","unshift"),animation:a("$animationProvider","register"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:h,run:function(a){d.push(a);return this}};f&&h(f);return g})}})})(window);
|
||||
460
docs-web/src/main/webapp/lib/angular/angular-mobile.js
vendored
Normal file
460
docs-web/src/main/webapp/lib/angular/angular-mobile.js
vendored
Normal file
@@ -0,0 +1,460 @@
|
||||
/**
|
||||
* @license AngularJS v1.1.5
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ngMobile
|
||||
* @description
|
||||
* Touch events and other mobile helpers.
|
||||
* Based on jQuery Mobile touch event handling (jquerymobile.com)
|
||||
*/
|
||||
|
||||
// define ngMobile module
|
||||
var ngMobile = angular.module('ngMobile', []);
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMobile.directive:ngClick
|
||||
*
|
||||
* @description
|
||||
* A more powerful replacement for the default ngClick designed to be used on touchscreen
|
||||
* devices. Most mobile browsers wait about 300ms after a tap-and-release before sending
|
||||
* the click event. This version handles them immediately, and then prevents the
|
||||
* following click event from propagating.
|
||||
*
|
||||
* This directive can fall back to using an ordinary click event, and so works on desktop
|
||||
* browsers as well as mobile.
|
||||
*
|
||||
* This directive also sets the CSS class `ng-click-active` while the element is being held
|
||||
* down (by a mouse click or touch) so you can restyle the depressed element if you wish.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngClick {@link guide/expression Expression} to evaluate
|
||||
* upon tap. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<button ng-click="count = count + 1" ng-init="count=0">
|
||||
Increment
|
||||
</button>
|
||||
count: {{ count }}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
ngMobile.config(['$provide', function($provide) {
|
||||
$provide.decorator('ngClickDirective', ['$delegate', function($delegate) {
|
||||
// drop the default ngClick directive
|
||||
$delegate.shift();
|
||||
return $delegate;
|
||||
}]);
|
||||
}]);
|
||||
|
||||
ngMobile.directive('ngClick', ['$parse', '$timeout', '$rootElement',
|
||||
function($parse, $timeout, $rootElement) {
|
||||
var TAP_DURATION = 750; // Shorter than 750ms is a tap, longer is a taphold or drag.
|
||||
var MOVE_TOLERANCE = 12; // 12px seems to work in most mobile browsers.
|
||||
var PREVENT_DURATION = 2500; // 2.5 seconds maximum from preventGhostClick call to click
|
||||
var CLICKBUSTER_THRESHOLD = 25; // 25 pixels in any dimension is the limit for busting clicks.
|
||||
|
||||
var ACTIVE_CLASS_NAME = 'ng-click-active';
|
||||
var lastPreventedTime;
|
||||
var touchCoordinates;
|
||||
|
||||
|
||||
// TAP EVENTS AND GHOST CLICKS
|
||||
//
|
||||
// Why tap events?
|
||||
// Mobile browsers detect a tap, then wait a moment (usually ~300ms) to see if you're
|
||||
// double-tapping, and then fire a click event.
|
||||
//
|
||||
// This delay sucks and makes mobile apps feel unresponsive.
|
||||
// So we detect touchstart, touchmove, touchcancel and touchend ourselves and determine when
|
||||
// the user has tapped on something.
|
||||
//
|
||||
// What happens when the browser then generates a click event?
|
||||
// The browser, of course, also detects the tap and fires a click after a delay. This results in
|
||||
// tapping/clicking twice. So we do "clickbusting" to prevent it.
|
||||
//
|
||||
// How does it work?
|
||||
// We attach global touchstart and click handlers, that run during the capture (early) phase.
|
||||
// So the sequence for a tap is:
|
||||
// - global touchstart: Sets an "allowable region" at the point touched.
|
||||
// - element's touchstart: Starts a touch
|
||||
// (- touchmove or touchcancel ends the touch, no click follows)
|
||||
// - element's touchend: Determines if the tap is valid (didn't move too far away, didn't hold
|
||||
// too long) and fires the user's tap handler. The touchend also calls preventGhostClick().
|
||||
// - preventGhostClick() removes the allowable region the global touchstart created.
|
||||
// - The browser generates a click event.
|
||||
// - The global click handler catches the click, and checks whether it was in an allowable region.
|
||||
// - If preventGhostClick was called, the region will have been removed, the click is busted.
|
||||
// - If the region is still there, the click proceeds normally. Therefore clicks on links and
|
||||
// other elements without ngTap on them work normally.
|
||||
//
|
||||
// This is an ugly, terrible hack!
|
||||
// Yeah, tell me about it. The alternatives are using the slow click events, or making our users
|
||||
// deal with the ghost clicks, so I consider this the least of evils. Fortunately Angular
|
||||
// encapsulates this ugly logic away from the user.
|
||||
//
|
||||
// Why not just put click handlers on the element?
|
||||
// We do that too, just to be sure. The problem is that the tap event might have caused the DOM
|
||||
// to change, so that the click fires in the same position but something else is there now. So
|
||||
// the handlers are global and care only about coordinates and not elements.
|
||||
|
||||
// Checks if the coordinates are close enough to be within the region.
|
||||
function hit(x1, y1, x2, y2) {
|
||||
return Math.abs(x1 - x2) < CLICKBUSTER_THRESHOLD && Math.abs(y1 - y2) < CLICKBUSTER_THRESHOLD;
|
||||
}
|
||||
|
||||
// Checks a list of allowable regions against a click location.
|
||||
// Returns true if the click should be allowed.
|
||||
// Splices out the allowable region from the list after it has been used.
|
||||
function checkAllowableRegions(touchCoordinates, x, y) {
|
||||
for (var i = 0; i < touchCoordinates.length; i += 2) {
|
||||
if (hit(touchCoordinates[i], touchCoordinates[i+1], x, y)) {
|
||||
touchCoordinates.splice(i, i + 2);
|
||||
return true; // allowable region
|
||||
}
|
||||
}
|
||||
return false; // No allowable region; bust it.
|
||||
}
|
||||
|
||||
// Global click handler that prevents the click if it's in a bustable zone and preventGhostClick
|
||||
// was called recently.
|
||||
function onClick(event) {
|
||||
if (Date.now() - lastPreventedTime > PREVENT_DURATION) {
|
||||
return; // Too old.
|
||||
}
|
||||
|
||||
var touches = event.touches && event.touches.length ? event.touches : [event];
|
||||
var x = touches[0].clientX;
|
||||
var y = touches[0].clientY;
|
||||
// Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label
|
||||
// and on the input element). Depending on the exact browser, this second click we don't want
|
||||
// to bust has either (0,0) or negative coordinates.
|
||||
if (x < 1 && y < 1) {
|
||||
return; // offscreen
|
||||
}
|
||||
|
||||
// Look for an allowable region containing this click.
|
||||
// If we find one, that means it was created by touchstart and not removed by
|
||||
// preventGhostClick, so we don't bust it.
|
||||
if (checkAllowableRegions(touchCoordinates, x, y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we didn't find an allowable region, bust the click.
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
|
||||
// Global touchstart handler that creates an allowable region for a click event.
|
||||
// This allowable region can be removed by preventGhostClick if we want to bust it.
|
||||
function onTouchStart(event) {
|
||||
var touches = event.touches && event.touches.length ? event.touches : [event];
|
||||
var x = touches[0].clientX;
|
||||
var y = touches[0].clientY;
|
||||
touchCoordinates.push(x, y);
|
||||
|
||||
$timeout(function() {
|
||||
// Remove the allowable region.
|
||||
for (var i = 0; i < touchCoordinates.length; i += 2) {
|
||||
if (touchCoordinates[i] == x && touchCoordinates[i+1] == y) {
|
||||
touchCoordinates.splice(i, i + 2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}, PREVENT_DURATION, false);
|
||||
}
|
||||
|
||||
// On the first call, attaches some event handlers. Then whenever it gets called, it creates a
|
||||
// zone around the touchstart where clicks will get busted.
|
||||
function preventGhostClick(x, y) {
|
||||
if (!touchCoordinates) {
|
||||
$rootElement[0].addEventListener('click', onClick, true);
|
||||
$rootElement[0].addEventListener('touchstart', onTouchStart, true);
|
||||
touchCoordinates = [];
|
||||
}
|
||||
|
||||
lastPreventedTime = Date.now();
|
||||
|
||||
checkAllowableRegions(touchCoordinates, x, y);
|
||||
}
|
||||
|
||||
// Actual linking function.
|
||||
return function(scope, element, attr) {
|
||||
var clickHandler = $parse(attr.ngClick),
|
||||
tapping = false,
|
||||
tapElement, // Used to blur the element after a tap.
|
||||
startTime, // Used to check if the tap was held too long.
|
||||
touchStartX,
|
||||
touchStartY;
|
||||
|
||||
function resetState() {
|
||||
tapping = false;
|
||||
element.removeClass(ACTIVE_CLASS_NAME);
|
||||
}
|
||||
|
||||
element.bind('touchstart', function(event) {
|
||||
tapping = true;
|
||||
tapElement = event.target ? event.target : event.srcElement; // IE uses srcElement.
|
||||
// Hack for Safari, which can target text nodes instead of containers.
|
||||
if(tapElement.nodeType == 3) {
|
||||
tapElement = tapElement.parentNode;
|
||||
}
|
||||
|
||||
element.addClass(ACTIVE_CLASS_NAME);
|
||||
|
||||
startTime = Date.now();
|
||||
|
||||
var touches = event.touches && event.touches.length ? event.touches : [event];
|
||||
var e = touches[0].originalEvent || touches[0];
|
||||
touchStartX = e.clientX;
|
||||
touchStartY = e.clientY;
|
||||
});
|
||||
|
||||
element.bind('touchmove', function(event) {
|
||||
resetState();
|
||||
});
|
||||
|
||||
element.bind('touchcancel', function(event) {
|
||||
resetState();
|
||||
});
|
||||
|
||||
element.bind('touchend', function(event) {
|
||||
var diff = Date.now() - startTime;
|
||||
|
||||
var touches = (event.changedTouches && event.changedTouches.length) ? event.changedTouches :
|
||||
((event.touches && event.touches.length) ? event.touches : [event]);
|
||||
var e = touches[0].originalEvent || touches[0];
|
||||
var x = e.clientX;
|
||||
var y = e.clientY;
|
||||
var dist = Math.sqrt( Math.pow(x - touchStartX, 2) + Math.pow(y - touchStartY, 2) );
|
||||
|
||||
if (tapping && diff < TAP_DURATION && dist < MOVE_TOLERANCE) {
|
||||
// Call preventGhostClick so the clickbuster will catch the corresponding click.
|
||||
preventGhostClick(x, y);
|
||||
|
||||
// Blur the focused element (the button, probably) before firing the callback.
|
||||
// This doesn't work perfectly on Android Chrome, but seems to work elsewhere.
|
||||
// I couldn't get anything to work reliably on Android Chrome.
|
||||
if (tapElement) {
|
||||
tapElement.blur();
|
||||
}
|
||||
|
||||
scope.$apply(function() {
|
||||
// TODO(braden): This is sending the touchend, not a tap or click. Is that kosher?
|
||||
clickHandler(scope, {$event: event});
|
||||
});
|
||||
}
|
||||
|
||||
resetState();
|
||||
});
|
||||
|
||||
// Hack for iOS Safari's benefit. It goes searching for onclick handlers and is liable to click
|
||||
// something else nearby.
|
||||
element.onclick = function(event) { };
|
||||
|
||||
// Fallback click handler.
|
||||
// Busted clicks don't get this far, and adding this handler allows ng-tap to be used on
|
||||
// desktop as well, to allow more portable sites.
|
||||
element.bind('click', function(event) {
|
||||
scope.$apply(function() {
|
||||
clickHandler(scope, {$event: event});
|
||||
});
|
||||
});
|
||||
|
||||
element.bind('mousedown', function(event) {
|
||||
element.addClass(ACTIVE_CLASS_NAME);
|
||||
});
|
||||
|
||||
element.bind('mousemove mouseup', function(event) {
|
||||
element.removeClass(ACTIVE_CLASS_NAME);
|
||||
});
|
||||
|
||||
};
|
||||
}]);
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMobile.directive:ngSwipeLeft
|
||||
*
|
||||
* @description
|
||||
* Specify custom behavior when an element is swiped to the left on a touchscreen device.
|
||||
* A leftward swipe is a quick, right-to-left slide of the finger.
|
||||
* Though ngSwipeLeft is designed for touch-based devices, it will work with a mouse click and drag too.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngSwipeLeft {@link guide/expression Expression} to evaluate
|
||||
* upon left swipe. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div ng-show="!showActions" ng-swipe-left="showActions = true">
|
||||
Some list content, like an email in the inbox
|
||||
</div>
|
||||
<div ng-show="showActions" ng-swipe-right="showActions = false">
|
||||
<button ng-click="reply()">Reply</button>
|
||||
<button ng-click="delete()">Delete</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMobile.directive:ngSwipeRight
|
||||
*
|
||||
* @description
|
||||
* Specify custom behavior when an element is swiped to the right on a touchscreen device.
|
||||
* A rightward swipe is a quick, left-to-right slide of the finger.
|
||||
* Though ngSwipeRight is designed for touch-based devices, it will work with a mouse click and drag too.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngSwipeRight {@link guide/expression Expression} to evaluate
|
||||
* upon right swipe. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div ng-show="!showActions" ng-swipe-left="showActions = true">
|
||||
Some list content, like an email in the inbox
|
||||
</div>
|
||||
<div ng-show="showActions" ng-swipe-right="showActions = false">
|
||||
<button ng-click="reply()">Reply</button>
|
||||
<button ng-click="delete()">Delete</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
function makeSwipeDirective(directiveName, direction) {
|
||||
ngMobile.directive(directiveName, ['$parse', function($parse) {
|
||||
// The maximum vertical delta for a swipe should be less than 75px.
|
||||
var MAX_VERTICAL_DISTANCE = 75;
|
||||
// Vertical distance should not be more than a fraction of the horizontal distance.
|
||||
var MAX_VERTICAL_RATIO = 0.3;
|
||||
// At least a 30px lateral motion is necessary for a swipe.
|
||||
var MIN_HORIZONTAL_DISTANCE = 30;
|
||||
// The total distance in any direction before we make the call on swipe vs. scroll.
|
||||
var MOVE_BUFFER_RADIUS = 10;
|
||||
|
||||
function getCoordinates(event) {
|
||||
var touches = event.touches && event.touches.length ? event.touches : [event];
|
||||
var e = (event.changedTouches && event.changedTouches[0]) ||
|
||||
(event.originalEvent && event.originalEvent.changedTouches &&
|
||||
event.originalEvent.changedTouches[0]) ||
|
||||
touches[0].originalEvent || touches[0];
|
||||
|
||||
return {
|
||||
x: e.clientX,
|
||||
y: e.clientY
|
||||
};
|
||||
}
|
||||
|
||||
return function(scope, element, attr) {
|
||||
var swipeHandler = $parse(attr[directiveName]);
|
||||
var startCoords, valid;
|
||||
var totalX, totalY;
|
||||
var lastX, lastY;
|
||||
|
||||
function validSwipe(event) {
|
||||
// Check that it's within the coordinates.
|
||||
// Absolute vertical distance must be within tolerances.
|
||||
// Horizontal distance, we take the current X - the starting X.
|
||||
// This is negative for leftward swipes and positive for rightward swipes.
|
||||
// After multiplying by the direction (-1 for left, +1 for right), legal swipes
|
||||
// (ie. same direction as the directive wants) will have a positive delta and
|
||||
// illegal ones a negative delta.
|
||||
// Therefore this delta must be positive, and larger than the minimum.
|
||||
if (!startCoords) return false;
|
||||
var coords = getCoordinates(event);
|
||||
var deltaY = Math.abs(coords.y - startCoords.y);
|
||||
var deltaX = (coords.x - startCoords.x) * direction;
|
||||
return valid && // Short circuit for already-invalidated swipes.
|
||||
deltaY < MAX_VERTICAL_DISTANCE &&
|
||||
deltaX > 0 &&
|
||||
deltaX > MIN_HORIZONTAL_DISTANCE &&
|
||||
deltaY / deltaX < MAX_VERTICAL_RATIO;
|
||||
}
|
||||
|
||||
element.bind('touchstart mousedown', function(event) {
|
||||
startCoords = getCoordinates(event);
|
||||
valid = true;
|
||||
totalX = 0;
|
||||
totalY = 0;
|
||||
lastX = startCoords.x;
|
||||
lastY = startCoords.y;
|
||||
});
|
||||
|
||||
element.bind('touchcancel', function(event) {
|
||||
valid = false;
|
||||
});
|
||||
|
||||
element.bind('touchmove mousemove', function(event) {
|
||||
if (!valid) return;
|
||||
|
||||
// Android will send a touchcancel if it thinks we're starting to scroll.
|
||||
// So when the total distance (+ or - or both) exceeds 10px in either direction,
|
||||
// we either:
|
||||
// - On totalX > totalY, we send preventDefault() and treat this as a swipe.
|
||||
// - On totalY > totalX, we let the browser handle it as a scroll.
|
||||
|
||||
// Invalidate a touch while it's in progress if it strays too far away vertically.
|
||||
// We don't want a scroll down and back up while drifting sideways to be a swipe just
|
||||
// because you happened to end up vertically close in the end.
|
||||
if (!startCoords) return;
|
||||
var coords = getCoordinates(event);
|
||||
|
||||
if (Math.abs(coords.y - startCoords.y) > MAX_VERTICAL_DISTANCE) {
|
||||
valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
totalX += Math.abs(coords.x - lastX);
|
||||
totalY += Math.abs(coords.y - lastY);
|
||||
|
||||
lastX = coords.x;
|
||||
lastY = coords.y;
|
||||
|
||||
if (totalX < MOVE_BUFFER_RADIUS && totalY < MOVE_BUFFER_RADIUS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// One of totalX or totalY has exceeded the buffer, so decide on swipe vs. scroll.
|
||||
if (totalY > totalX) {
|
||||
valid = false;
|
||||
return;
|
||||
} else {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
element.bind('touchend mouseup', function(event) {
|
||||
if (validSwipe(event)) {
|
||||
// Prevent this swipe from bubbling up to any other elements with ngSwipes.
|
||||
event.stopPropagation();
|
||||
scope.$apply(function() {
|
||||
swipeHandler(scope, {$event:event});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
}]);
|
||||
}
|
||||
|
||||
// Left is negative X-coordinate, right is positive.
|
||||
makeSwipeDirective('ngSwipeLeft', -1);
|
||||
makeSwipeDirective('ngSwipeRight', 1);
|
||||
|
||||
|
||||
|
||||
})(window, window.angular);
|
||||
11
docs-web/src/main/webapp/lib/angular/angular-mobile.min.js
vendored
Normal file
11
docs-web/src/main/webapp/lib/angular/angular-mobile.min.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
AngularJS v1.1.5
|
||||
(c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(u,s){'use strict';function k(i,t){j.directive(i,["$parse",function(l){function g(b){var h=b.touches&&b.touches.length?b.touches:[b],b=b.changedTouches&&b.changedTouches[0]||b.originalEvent&&b.originalEvent.changedTouches&&b.originalEvent.changedTouches[0]||h[0].originalEvent||h[0];return{x:b.clientX,y:b.clientY}}var m=75,j=0.3,p=30;return function(b,h,n){function o(e){if(!a)return!1;var b=g(e),e=Math.abs(b.y-a.y),b=(b.x-a.x)*t;return c&&e<m&&b>0&&b>p&&e/b<j}var d=l(n[i]),a,c,f,e,q,r;h.bind("touchstart mousedown",
|
||||
function(b){a=g(b);c=!0;e=f=0;q=a.x;r=a.y});h.bind("touchcancel",function(){c=!1});h.bind("touchmove mousemove",function(b){if(c&&a){var d=g(b);Math.abs(d.y-a.y)>m?c=!1:(f+=Math.abs(d.x-q),e+=Math.abs(d.y-r),q=d.x,r=d.y,f<10&&e<10||(e>f?c=!1:b.preventDefault()))}});h.bind("touchend mouseup",function(a){o(a)&&(a.stopPropagation(),b.$apply(function(){d(b,{$event:a})}))})}}])}var j=s.module("ngMobile",[]);j.config(["$provide",function(i){i.decorator("ngClickDirective",["$delegate",function(i){i.shift();
|
||||
return i}])}]);j.directive("ngClick",["$parse","$timeout","$rootElement",function(i,j,l){function g(a,c,b){for(var e=0;e<a.length;e+=2)if(Math.abs(a[e]-c)<h&&Math.abs(a[e+1]-b)<h)return a.splice(e,e+2),!0;return!1}function m(a){if(!(Date.now()-o>b)){var c=a.touches&&a.touches.length?a.touches:[a],f=c[0].clientX,c=c[0].clientY;!(f<1&&c<1)&&!g(d,f,c)&&(a.stopPropagation(),a.preventDefault())}}function k(a){var a=a.touches&&a.touches.length?a.touches:[a],c=a[0].clientX,f=a[0].clientY;d.push(c,f);j(function(){for(var a=
|
||||
0;a<d.length;a+=2)if(d[a]==c&&d[a+1]==f){d.splice(a,a+2);break}},b,!1)}function p(a,b){d||(l[0].addEventListener("click",m,!0),l[0].addEventListener("touchstart",k,!0),d=[]);o=Date.now();g(d,a,b)}var b=2500,h=25,n="ng-click-active",o,d;return function(a,b,d){function e(){j=!1;b.removeClass(n)}var h=i(d.ngClick),j=!1,g,k,l,m;b.bind("touchstart",function(a){j=!0;g=a.target?a.target:a.srcElement;if(g.nodeType==3)g=g.parentNode;b.addClass(n);k=Date.now();a=a.touches&&a.touches.length?a.touches:[a];a=
|
||||
a[0].originalEvent||a[0];l=a.clientX;m=a.clientY});b.bind("touchmove",function(){e()});b.bind("touchcancel",function(){e()});b.bind("touchend",function(b){var d=Date.now()-k,c=b.changedTouches&&b.changedTouches.length?b.changedTouches:b.touches&&b.touches.length?b.touches:[b],f=c[0].originalEvent||c[0],c=f.clientX,f=f.clientY,i=Math.sqrt(Math.pow(c-l,2)+Math.pow(f-m,2));j&&d<750&&i<12&&(p(c,f),g&&g.blur(),a.$apply(function(){h(a,{$event:b})}));e()});b.onclick=function(){};b.bind("click",function(b){a.$apply(function(){h(a,
|
||||
{$event:b})})});b.bind("mousedown",function(){b.addClass(n)});b.bind("mousemove mouseup",function(){b.removeClass(n)})}}]);k("ngSwipeLeft",-1);k("ngSwipeRight",1)})(window,window.angular);
|
||||
1886
docs-web/src/main/webapp/lib/angular/angular-mocks.js
vendored
Normal file
1886
docs-web/src/main/webapp/lib/angular/angular-mocks.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
537
docs-web/src/main/webapp/lib/angular/angular-resource.js
vendored
Normal file
537
docs-web/src/main/webapp/lib/angular/angular-resource.js
vendored
Normal file
@@ -0,0 +1,537 @@
|
||||
/**
|
||||
* @license AngularJS v1.1.5
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ngResource
|
||||
* @description
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ngResource.$resource
|
||||
* @requires $http
|
||||
*
|
||||
* @description
|
||||
* A factory which creates a resource object that lets you interact with
|
||||
* [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
|
||||
*
|
||||
* The returned resource object has action methods which provide high-level behaviors without
|
||||
* the need to interact with the low level {@link ng.$http $http} service.
|
||||
*
|
||||
* # Installation
|
||||
* To use $resource make sure you have included the `angular-resource.js` that comes in Angular
|
||||
* package. You can also find this file on Google CDN, bower as well as at
|
||||
* {@link http://code.angularjs.org/ code.angularjs.org}.
|
||||
*
|
||||
* Finally load the module in your application:
|
||||
*
|
||||
* angular.module('app', ['ngResource']);
|
||||
*
|
||||
* and you are ready to get started!
|
||||
*
|
||||
* @param {string} url A parametrized URL template with parameters prefixed by `:` as in
|
||||
* `/user/:username`. If you are using a URL with a port number (e.g.
|
||||
* `http://example.com:8080/api`), you'll need to escape the colon character before the port
|
||||
* number, like this: `$resource('http://example.com\\:8080/api')`.
|
||||
*
|
||||
* If you are using a url with a suffix, just add the suffix, like this:
|
||||
* `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')
|
||||
* or even `$resource('http://example.com/resource/:resource_id.:format')`
|
||||
* If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
|
||||
* collapsed down to a single `.`. If you need this sequence to appear and not collapse then you
|
||||
* can escape it with `/\.`.
|
||||
*
|
||||
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
|
||||
* `actions` methods. If any of the parameter value is a function, it will be executed every time
|
||||
* when a param value needs to be obtained for a request (unless the param was overridden).
|
||||
*
|
||||
* Each key value in the parameter object is first bound to url template if present and then any
|
||||
* excess keys are appended to the url search query after the `?`.
|
||||
*
|
||||
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
|
||||
* URL `/path/greet?salutation=Hello`.
|
||||
*
|
||||
* If the parameter value is prefixed with `@` then the value of that parameter is extracted from
|
||||
* the data object (useful for non-GET operations).
|
||||
*
|
||||
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend the
|
||||
* default set of resource actions. The declaration should be created in the format of {@link
|
||||
* ng.$http#Parameters $http.config}:
|
||||
*
|
||||
* {action1: {method:?, params:?, isArray:?, headers:?, ...},
|
||||
* action2: {method:?, params:?, isArray:?, headers:?, ...},
|
||||
* ...}
|
||||
*
|
||||
* Where:
|
||||
*
|
||||
* - **`action`** – {string} – The name of action. This name becomes the name of the method on your
|
||||
* resource object.
|
||||
* - **`method`** – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`,
|
||||
* and `JSONP`.
|
||||
* - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of the
|
||||
* parameter value is a function, it will be executed every time when a param value needs to be
|
||||
* obtained for a request (unless the param was overridden).
|
||||
* - **`url`** – {string} – action specific `url` override. The url templating is supported just like
|
||||
* for the resource-level urls.
|
||||
* - **`isArray`** – {boolean=} – If true then the returned object for this action is an array, see
|
||||
* `returns` section.
|
||||
* - **`transformRequest`** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
|
||||
* transform function or an array of such functions. The transform function takes the http
|
||||
* request body and headers and returns its transformed (typically serialized) version.
|
||||
* - **`transformResponse`** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
|
||||
* transform function or an array of such functions. The transform function takes the http
|
||||
* response body and headers and returns its transformed (typically deserialized) version.
|
||||
* - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
|
||||
* GET request, otherwise if a cache instance built with
|
||||
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
|
||||
* caching.
|
||||
* - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that
|
||||
* should abort the request when resolved.
|
||||
* - **`withCredentials`** - `{boolean}` - whether to to set the `withCredentials` flag on the
|
||||
* XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
|
||||
* requests with credentials} for more information.
|
||||
* - **`responseType`** - `{string}` - see {@link
|
||||
* https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}.
|
||||
*
|
||||
* @returns {Object} A resource "class" object with methods for the default set of resource actions
|
||||
* optionally extended with custom `actions`. The default set contains these actions:
|
||||
*
|
||||
* { 'get': {method:'GET'},
|
||||
* 'save': {method:'POST'},
|
||||
* 'query': {method:'GET', isArray:true},
|
||||
* 'remove': {method:'DELETE'},
|
||||
* 'delete': {method:'DELETE'} };
|
||||
*
|
||||
* Calling these methods invoke an {@link ng.$http} with the specified http method,
|
||||
* destination and parameters. When the data is returned from the server then the object is an
|
||||
* instance of the resource class. The actions `save`, `remove` and `delete` are available on it
|
||||
* as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
|
||||
* read, update, delete) on server-side data like this:
|
||||
* <pre>
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
var user = User.get({userId:123}, function() {
|
||||
user.abc = true;
|
||||
user.$save();
|
||||
});
|
||||
</pre>
|
||||
*
|
||||
* It is important to realize that invoking a $resource object method immediately returns an
|
||||
* empty reference (object or array depending on `isArray`). Once the data is returned from the
|
||||
* server the existing reference is populated with the actual data. This is a useful trick since
|
||||
* usually the resource is assigned to a model which is then rendered by the view. Having an empty
|
||||
* object results in no rendering, once the data arrives from the server then the object is
|
||||
* populated with the data and the view automatically re-renders itself showing the new data. This
|
||||
* means that in most case one never has to write a callback function for the action methods.
|
||||
*
|
||||
* The action methods on the class object or instance object can be invoked with the following
|
||||
* parameters:
|
||||
*
|
||||
* - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
|
||||
* - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
|
||||
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
|
||||
*
|
||||
*
|
||||
* The Resource instances and collection have these additional properties:
|
||||
*
|
||||
* - `$then`: the `then` method of a {@link ng.$q promise} derived from the underlying
|
||||
* {@link ng.$http $http} call.
|
||||
*
|
||||
* The success callback for the `$then` method will be resolved if the underlying `$http` requests
|
||||
* succeeds.
|
||||
*
|
||||
* The success callback is called with a single object which is the {@link ng.$http http response}
|
||||
* object extended with a new property `resource`. This `resource` property is a reference to the
|
||||
* result of the resource action — resource object or array of resources.
|
||||
*
|
||||
* The error callback is called with the {@link ng.$http http response} object when an http
|
||||
* error occurs.
|
||||
*
|
||||
* - `$resolved`: true if the promise has been resolved (either with success or rejection);
|
||||
* Knowing if the Resource has been resolved is useful in data-binding.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* # Credit card resource
|
||||
*
|
||||
* <pre>
|
||||
// Define CreditCard class
|
||||
var CreditCard = $resource('/user/:userId/card/:cardId',
|
||||
{userId:123, cardId:'@id'}, {
|
||||
charge: {method:'POST', params:{charge:true}}
|
||||
});
|
||||
|
||||
// We can retrieve a collection from the server
|
||||
var cards = CreditCard.query(function() {
|
||||
// GET: /user/123/card
|
||||
// server returns: [ {id:456, number:'1234', name:'Smith'} ];
|
||||
|
||||
var card = cards[0];
|
||||
// each item is an instance of CreditCard
|
||||
expect(card instanceof CreditCard).toEqual(true);
|
||||
card.name = "J. Smith";
|
||||
// non GET methods are mapped onto the instances
|
||||
card.$save();
|
||||
// POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
|
||||
// server returns: {id:456, number:'1234', name: 'J. Smith'};
|
||||
|
||||
// our custom method is mapped as well.
|
||||
card.$charge({amount:9.99});
|
||||
// POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
|
||||
});
|
||||
|
||||
// we can create an instance as well
|
||||
var newCard = new CreditCard({number:'0123'});
|
||||
newCard.name = "Mike Smith";
|
||||
newCard.$save();
|
||||
// POST: /user/123/card {number:'0123', name:'Mike Smith'}
|
||||
// server returns: {id:789, number:'01234', name: 'Mike Smith'};
|
||||
expect(newCard.id).toEqual(789);
|
||||
* </pre>
|
||||
*
|
||||
* The object returned from this function execution is a resource "class" which has "static" method
|
||||
* for each action in the definition.
|
||||
*
|
||||
* Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and `headers`.
|
||||
* When the data is returned from the server then the object is an instance of the resource type and
|
||||
* all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
|
||||
* operations (create, read, update, delete) on server-side data.
|
||||
|
||||
<pre>
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
var user = User.get({userId:123}, function() {
|
||||
user.abc = true;
|
||||
user.$save();
|
||||
});
|
||||
</pre>
|
||||
*
|
||||
* It's worth noting that the success callback for `get`, `query` and other method gets passed
|
||||
* in the response that came from the server as well as $http header getter function, so one
|
||||
* could rewrite the above example and get access to http headers as:
|
||||
*
|
||||
<pre>
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
User.get({userId:123}, function(u, getResponseHeaders){
|
||||
u.abc = true;
|
||||
u.$save(function(u, putResponseHeaders) {
|
||||
//u => saved user object
|
||||
//putResponseHeaders => $http header getter
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
* # Buzz client
|
||||
|
||||
Let's look at what a buzz client created with the `$resource` service looks like:
|
||||
<doc:example>
|
||||
<doc:source jsfiddle="false">
|
||||
<script>
|
||||
function BuzzController($resource) {
|
||||
this.userId = 'googlebuzz';
|
||||
this.Activity = $resource(
|
||||
'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments',
|
||||
{alt:'json', callback:'JSON_CALLBACK'},
|
||||
{get:{method:'JSONP', params:{visibility:'@self'}}, replies: {method:'JSONP', params:{visibility:'@self', comments:'@comments'}}}
|
||||
);
|
||||
}
|
||||
|
||||
BuzzController.prototype = {
|
||||
fetch: function() {
|
||||
this.activities = this.Activity.get({userId:this.userId});
|
||||
},
|
||||
expandReplies: function(activity) {
|
||||
activity.replies = this.Activity.replies({userId:this.userId, activityId:activity.id});
|
||||
}
|
||||
};
|
||||
BuzzController.$inject = ['$resource'];
|
||||
</script>
|
||||
|
||||
<div ng-controller="BuzzController">
|
||||
<input ng-model="userId"/>
|
||||
<button ng-click="fetch()">fetch</button>
|
||||
<hr/>
|
||||
<div ng-repeat="item in activities.data.items">
|
||||
<h1 style="font-size: 15px;">
|
||||
<img src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
||||
<a href="{{item.actor.profileUrl}}">{{item.actor.name}}</a>
|
||||
<a href ng-click="expandReplies(item)" style="float: right;">Expand replies: {{item.links.replies[0].count}}</a>
|
||||
</h1>
|
||||
{{item.object.content | html}}
|
||||
<div ng-repeat="reply in item.replies.data.items" style="margin-left: 20px;">
|
||||
<img src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
||||
<a href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>: {{reply.content | html}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angular.module('ngResource', ['ng']).
|
||||
factory('$resource', ['$http', '$parse', function($http, $parse) {
|
||||
var DEFAULT_ACTIONS = {
|
||||
'get': {method:'GET'},
|
||||
'save': {method:'POST'},
|
||||
'query': {method:'GET', isArray:true},
|
||||
'remove': {method:'DELETE'},
|
||||
'delete': {method:'DELETE'}
|
||||
};
|
||||
var noop = angular.noop,
|
||||
forEach = angular.forEach,
|
||||
extend = angular.extend,
|
||||
copy = angular.copy,
|
||||
isFunction = angular.isFunction,
|
||||
getter = function(obj, path) {
|
||||
return $parse(path)(obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* We need our custom method because encodeURIComponent is too aggressive and doesn't follow
|
||||
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
|
||||
* segments:
|
||||
* segment = *pchar
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
* pct-encoded = "%" HEXDIG HEXDIG
|
||||
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
* / "*" / "+" / "," / ";" / "="
|
||||
*/
|
||||
function encodeUriSegment(val) {
|
||||
return encodeUriQuery(val, true).
|
||||
replace(/%26/gi, '&').
|
||||
replace(/%3D/gi, '=').
|
||||
replace(/%2B/gi, '+');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is intended for encoding *key* or *value* parts of query component. We need a custom
|
||||
* method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
|
||||
* encoded per http://tools.ietf.org/html/rfc3986:
|
||||
* query = *( pchar / "/" / "?" )
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
* pct-encoded = "%" HEXDIG HEXDIG
|
||||
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
* / "*" / "+" / "," / ";" / "="
|
||||
*/
|
||||
function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
return encodeURIComponent(val).
|
||||
replace(/%40/gi, '@').
|
||||
replace(/%3A/gi, ':').
|
||||
replace(/%24/g, '$').
|
||||
replace(/%2C/gi, ',').
|
||||
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
|
||||
}
|
||||
|
||||
function Route(template, defaults) {
|
||||
this.template = template;
|
||||
this.defaults = defaults || {};
|
||||
this.urlParams = {};
|
||||
}
|
||||
|
||||
Route.prototype = {
|
||||
setUrlParams: function(config, params, actionUrl) {
|
||||
var self = this,
|
||||
url = actionUrl || self.template,
|
||||
val,
|
||||
encodedVal;
|
||||
|
||||
var urlParams = self.urlParams = {};
|
||||
forEach(url.split(/\W/), function(param){
|
||||
if (param && (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
|
||||
urlParams[param] = true;
|
||||
}
|
||||
});
|
||||
url = url.replace(/\\:/g, ':');
|
||||
|
||||
params = params || {};
|
||||
forEach(self.urlParams, function(_, urlParam){
|
||||
val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
|
||||
if (angular.isDefined(val) && val !== null) {
|
||||
encodedVal = encodeUriSegment(val);
|
||||
url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), encodedVal + "$1");
|
||||
} else {
|
||||
url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
|
||||
leadingSlashes, tail) {
|
||||
if (tail.charAt(0) == '/') {
|
||||
return tail;
|
||||
} else {
|
||||
return leadingSlashes + tail;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// strip trailing slashes and set the url
|
||||
url = url.replace(/\/+$/, '');
|
||||
// then replace collapse `/.` if found in the last URL path segment before the query
|
||||
// E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
|
||||
url = url.replace(/\/\.(?=\w+($|\?))/, '.');
|
||||
// replace escaped `/\.` with `/.`
|
||||
config.url = url.replace(/\/\\\./, '/.');
|
||||
|
||||
|
||||
// set params - delegate param encoding to $http
|
||||
forEach(params, function(value, key){
|
||||
if (!self.urlParams[key]) {
|
||||
config.params = config.params || {};
|
||||
config.params[key] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function ResourceFactory(url, paramDefaults, actions) {
|
||||
var route = new Route(url);
|
||||
|
||||
actions = extend({}, DEFAULT_ACTIONS, actions);
|
||||
|
||||
function extractParams(data, actionParams){
|
||||
var ids = {};
|
||||
actionParams = extend({}, paramDefaults, actionParams);
|
||||
forEach(actionParams, function(value, key){
|
||||
if (isFunction(value)) { value = value(); }
|
||||
ids[key] = value && value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value;
|
||||
});
|
||||
return ids;
|
||||
}
|
||||
|
||||
function Resource(value){
|
||||
copy(value || {}, this);
|
||||
}
|
||||
|
||||
forEach(actions, function(action, name) {
|
||||
action.method = angular.uppercase(action.method);
|
||||
var hasBody = action.method == 'POST' || action.method == 'PUT' || action.method == 'PATCH';
|
||||
Resource[name] = function(a1, a2, a3, a4) {
|
||||
var params = {};
|
||||
var data;
|
||||
var success = noop;
|
||||
var error = null;
|
||||
var promise;
|
||||
|
||||
switch(arguments.length) {
|
||||
case 4:
|
||||
error = a4;
|
||||
success = a3;
|
||||
//fallthrough
|
||||
case 3:
|
||||
case 2:
|
||||
if (isFunction(a2)) {
|
||||
if (isFunction(a1)) {
|
||||
success = a1;
|
||||
error = a2;
|
||||
break;
|
||||
}
|
||||
|
||||
success = a2;
|
||||
error = a3;
|
||||
//fallthrough
|
||||
} else {
|
||||
params = a1;
|
||||
data = a2;
|
||||
success = a3;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
if (isFunction(a1)) success = a1;
|
||||
else if (hasBody) data = a1;
|
||||
else params = a1;
|
||||
break;
|
||||
case 0: break;
|
||||
default:
|
||||
throw "Expected between 0-4 arguments [params, data, success, error], got " +
|
||||
arguments.length + " arguments.";
|
||||
}
|
||||
|
||||
var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
|
||||
var httpConfig = {},
|
||||
promise;
|
||||
|
||||
forEach(action, function(value, key) {
|
||||
if (key != 'params' && key != 'isArray' ) {
|
||||
httpConfig[key] = copy(value);
|
||||
}
|
||||
});
|
||||
httpConfig.data = data;
|
||||
route.setUrlParams(httpConfig, extend({}, extractParams(data, action.params || {}), params), action.url);
|
||||
|
||||
function markResolved() { value.$resolved = true; }
|
||||
|
||||
promise = $http(httpConfig);
|
||||
value.$resolved = false;
|
||||
|
||||
promise.then(markResolved, markResolved);
|
||||
value.$then = promise.then(function(response) {
|
||||
var data = response.data;
|
||||
var then = value.$then, resolved = value.$resolved;
|
||||
|
||||
if (data) {
|
||||
if (action.isArray) {
|
||||
value.length = 0;
|
||||
forEach(data, function(item) {
|
||||
value.push(new Resource(item));
|
||||
});
|
||||
} else {
|
||||
copy(data, value);
|
||||
value.$then = then;
|
||||
value.$resolved = resolved;
|
||||
}
|
||||
}
|
||||
|
||||
(success||noop)(value, response.headers);
|
||||
|
||||
response.resource = value;
|
||||
return response;
|
||||
}, error).then;
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
|
||||
Resource.prototype['$' + name] = function(a1, a2, a3) {
|
||||
var params = extractParams(this),
|
||||
success = noop,
|
||||
error;
|
||||
|
||||
switch(arguments.length) {
|
||||
case 3: params = a1; success = a2; error = a3; break;
|
||||
case 2:
|
||||
case 1:
|
||||
if (isFunction(a1)) {
|
||||
success = a1;
|
||||
error = a2;
|
||||
} else {
|
||||
params = a1;
|
||||
success = a2 || noop;
|
||||
}
|
||||
case 0: break;
|
||||
default:
|
||||
throw "Expected between 1-3 arguments [params, success, error], got " +
|
||||
arguments.length + " arguments.";
|
||||
}
|
||||
var data = hasBody ? this : undefined;
|
||||
Resource[name].call(this, params, data, success, error);
|
||||
};
|
||||
});
|
||||
|
||||
Resource.bind = function(additionalParamDefaults){
|
||||
return ResourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
|
||||
};
|
||||
|
||||
return Resource;
|
||||
}
|
||||
|
||||
return ResourceFactory;
|
||||
}]);
|
||||
|
||||
|
||||
})(window, window.angular);
|
||||
11
docs-web/src/main/webapp/lib/angular/angular-resource.min.js
vendored
Normal file
11
docs-web/src/main/webapp/lib/angular/angular-resource.min.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
AngularJS v1.1.5
|
||||
(c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(B,f,w){'use strict';f.module("ngResource",["ng"]).factory("$resource",["$http","$parse",function(x,y){function u(b,d){this.template=b;this.defaults=d||{};this.urlParams={}}function v(b,d,e){function j(c,b){var p={},b=m({},d,b);l(b,function(a,b){k(a)&&(a=a());var g;a&&a.charAt&&a.charAt(0)=="@"?(g=a.substr(1),g=y(g)(c)):g=a;p[b]=g});return p}function c(c){t(c||{},this)}var n=new u(b),e=m({},z,e);l(e,function(b,d){b.method=f.uppercase(b.method);var p=b.method=="POST"||b.method=="PUT"||b.method==
|
||||
"PATCH";c[d]=function(a,d,g,A){function f(){h.$resolved=!0}var i={},e,o=q,r=null;switch(arguments.length){case 4:r=A,o=g;case 3:case 2:if(k(d)){if(k(a)){o=a;r=d;break}o=d;r=g}else{i=a;e=d;o=g;break}case 1:k(a)?o=a:p?e=a:i=a;break;case 0:break;default:throw"Expected between 0-4 arguments [params, data, success, error], got "+arguments.length+" arguments.";}var h=this instanceof c?this:b.isArray?[]:new c(e),s={};l(b,function(a,b){b!="params"&&b!="isArray"&&(s[b]=t(a))});s.data=e;n.setUrlParams(s,m({},
|
||||
j(e,b.params||{}),i),b.url);i=x(s);h.$resolved=!1;i.then(f,f);h.$then=i.then(function(a){var d=a.data,g=h.$then,e=h.$resolved;if(d)b.isArray?(h.length=0,l(d,function(a){h.push(new c(a))})):(t(d,h),h.$then=g,h.$resolved=e);(o||q)(h,a.headers);a.resource=h;return a},r).then;return h};c.prototype["$"+d]=function(a,b,g){var e=j(this),f=q,i;switch(arguments.length){case 3:e=a;f=b;i=g;break;case 2:case 1:k(a)?(f=a,i=b):(e=a,f=b||q);case 0:break;default:throw"Expected between 1-3 arguments [params, success, error], got "+
|
||||
arguments.length+" arguments.";}c[d].call(this,e,p?this:w,f,i)}});c.bind=function(c){return v(b,m({},d,c),e)};return c}var z={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},q=f.noop,l=f.forEach,m=f.extend,t=f.copy,k=f.isFunction;u.prototype={setUrlParams:function(b,d,e){var j=this,c=e||j.template,n,k,m=j.urlParams={};l(c.split(/\W/),function(b){b&&RegExp("(^|[^\\\\]):"+b+"(\\W|$)").test(c)&&(m[b]=!0)});c=c.replace(/\\:/g,
|
||||
":");d=d||{};l(j.urlParams,function(b,a){n=d.hasOwnProperty(a)?d[a]:j.defaults[a];f.isDefined(n)&&n!==null?(k=encodeURIComponent(n).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"),c=c.replace(RegExp(":"+a+"(\\W|$)","g"),k+"$1")):c=c.replace(RegExp("(/?):"+a+"(\\W|$)","g"),function(b,a,c){return c.charAt(0)=="/"?c:a+c})});c=c.replace(/\/+$/,"");c=c.replace(/\/\.(?=\w+($|\?))/,".");
|
||||
b.url=c.replace(/\/\\\./,"/.");l(d,function(c,a){if(!j.urlParams[a])b.params=b.params||{},b.params[a]=c})}};return v}])})(window,window.angular);
|
||||
558
docs-web/src/main/webapp/lib/angular/angular-sanitize.js
vendored
Normal file
558
docs-web/src/main/webapp/lib/angular/angular-sanitize.js
vendored
Normal file
@@ -0,0 +1,558 @@
|
||||
/**
|
||||
* @license AngularJS v1.1.5
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ngSanitize
|
||||
* @description
|
||||
*/
|
||||
|
||||
/*
|
||||
* HTML Parser By Misko Hevery (misko@hevery.com)
|
||||
* based on: HTML Parser By John Resig (ejohn.org)
|
||||
* Original code by Erik Arvidsson, Mozilla Public License
|
||||
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
|
||||
*
|
||||
* // Use like so:
|
||||
* htmlParser(htmlString, {
|
||||
* start: function(tag, attrs, unary) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* });
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name ngSanitize.$sanitize
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are
|
||||
* then serialized back to properly escaped html string. This means that no unsafe input can make
|
||||
* it into the returned string, however, since our parser is more strict than a typical browser
|
||||
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a
|
||||
* browser, won't make it through the sanitizer.
|
||||
*
|
||||
* @param {string} html Html input.
|
||||
* @returns {string} Sanitized html.
|
||||
*
|
||||
* @example
|
||||
<doc:example module="ngSanitize">
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.snippet =
|
||||
'<p style="color:blue">an html\n' +
|
||||
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
|
||||
'snippet</p>';
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Filter</td>
|
||||
<td>Source</td>
|
||||
<td>Rendered</td>
|
||||
</tr>
|
||||
<tr id="html-filter">
|
||||
<td>html filter</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="snippet"><br/></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-bind-html="snippet"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="escaped-html">
|
||||
<td>no filter</td>
|
||||
<td><pre><div ng-bind="snippet"><br/></div></pre></td>
|
||||
<td><div ng-bind="snippet"></div></td>
|
||||
</tr>
|
||||
<tr id="html-unsafe-filter">
|
||||
<td>unsafe html filter</td>
|
||||
<td><pre><div ng-bind-html-unsafe="snippet"><br/></div></pre></td>
|
||||
<td><div ng-bind-html-unsafe="snippet"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should sanitize the html snippet ', function() {
|
||||
expect(using('#html-filter').element('div').html()).
|
||||
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
|
||||
});
|
||||
|
||||
it('should escape snippet without any filter', function() {
|
||||
expect(using('#escaped-html').element('div').html()).
|
||||
toBe("<p style=\"color:blue\">an html\n" +
|
||||
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
||||
"snippet</p>");
|
||||
});
|
||||
|
||||
it('should inline raw snippet if filtered as unsafe', function() {
|
||||
expect(using('#html-unsafe-filter').element("div").html()).
|
||||
toBe("<p style=\"color:blue\">an html\n" +
|
||||
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
||||
"snippet</p>");
|
||||
});
|
||||
|
||||
it('should update', function() {
|
||||
input('snippet').enter('new <b>text</b>');
|
||||
expect(using('#html-filter').binding('snippet')).toBe('new <b>text</b>');
|
||||
expect(using('#escaped-html').element('div').html()).toBe("new <b>text</b>");
|
||||
expect(using('#html-unsafe-filter').binding("snippet")).toBe('new <b>text</b>');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var $sanitize = function(html) {
|
||||
var buf = [];
|
||||
htmlParser(html, htmlSanitizeWriter(buf));
|
||||
return buf.join('');
|
||||
};
|
||||
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,
|
||||
END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/,
|
||||
ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
|
||||
BEGIN_TAG_REGEXP = /^</,
|
||||
BEGING_END_TAGE_REGEXP = /^<\s*\//,
|
||||
COMMENT_REGEXP = /<!--(.*?)-->/g,
|
||||
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
|
||||
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/,
|
||||
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character)
|
||||
|
||||
|
||||
// Good source of info about elements and attributes
|
||||
// http://dev.w3.org/html5/spec/Overview.html#semantics
|
||||
// http://simon.html5.org/html-elements
|
||||
|
||||
// Safe Void Elements - HTML5
|
||||
// http://dev.w3.org/html5/spec/Overview.html#void-elements
|
||||
var voidElements = makeMap("area,br,col,hr,img,wbr");
|
||||
|
||||
// Elements that you can, intentionally, leave open (and which close themselves)
|
||||
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
|
||||
var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
|
||||
optionalEndTagInlineElements = makeMap("rp,rt"),
|
||||
optionalEndTagElements = angular.extend({}, optionalEndTagInlineElements, optionalEndTagBlockElements);
|
||||
|
||||
// Safe Block Elements - HTML5
|
||||
var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article,aside," +
|
||||
"blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6," +
|
||||
"header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul"));
|
||||
|
||||
// Inline Elements - HTML5
|
||||
var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b,bdi,bdo," +
|
||||
"big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small," +
|
||||
"span,strike,strong,sub,sup,time,tt,u,var"));
|
||||
|
||||
|
||||
// Special Elements (can contain anything)
|
||||
var specialElements = makeMap("script,style");
|
||||
|
||||
var validElements = angular.extend({}, voidElements, blockElements, inlineElements, optionalEndTagElements);
|
||||
|
||||
//Attributes that have href and hence need to be sanitized
|
||||
var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap");
|
||||
var validAttrs = angular.extend({}, uriAttrs, makeMap(
|
||||
'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+
|
||||
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+
|
||||
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+
|
||||
'scope,scrolling,shape,span,start,summary,target,title,type,'+
|
||||
'valign,value,vspace,width'));
|
||||
|
||||
function makeMap(str) {
|
||||
var obj = {}, items = str.split(','), i;
|
||||
for (i = 0; i < items.length; i++) obj[items[i]] = true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @example
|
||||
* htmlParser(htmlString, {
|
||||
* start: function(tag, attrs, unary) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* });
|
||||
*
|
||||
* @param {string} html string
|
||||
* @param {object} handler
|
||||
*/
|
||||
function htmlParser( html, handler ) {
|
||||
var index, chars, match, stack = [], last = html;
|
||||
stack.last = function() { return stack[ stack.length - 1 ]; };
|
||||
|
||||
while ( html ) {
|
||||
chars = true;
|
||||
|
||||
// Make sure we're not in a script or style element
|
||||
if ( !stack.last() || !specialElements[ stack.last() ] ) {
|
||||
|
||||
// Comment
|
||||
if ( html.indexOf("<!--") === 0 ) {
|
||||
index = html.indexOf("-->");
|
||||
|
||||
if ( index >= 0 ) {
|
||||
if (handler.comment) handler.comment( html.substring( 4, index ) );
|
||||
html = html.substring( index + 3 );
|
||||
chars = false;
|
||||
}
|
||||
|
||||
// end tag
|
||||
} else if ( BEGING_END_TAGE_REGEXP.test(html) ) {
|
||||
match = html.match( END_TAG_REGEXP );
|
||||
|
||||
if ( match ) {
|
||||
html = html.substring( match[0].length );
|
||||
match[0].replace( END_TAG_REGEXP, parseEndTag );
|
||||
chars = false;
|
||||
}
|
||||
|
||||
// start tag
|
||||
} else if ( BEGIN_TAG_REGEXP.test(html) ) {
|
||||
match = html.match( START_TAG_REGEXP );
|
||||
|
||||
if ( match ) {
|
||||
html = html.substring( match[0].length );
|
||||
match[0].replace( START_TAG_REGEXP, parseStartTag );
|
||||
chars = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( chars ) {
|
||||
index = html.indexOf("<");
|
||||
|
||||
var text = index < 0 ? html : html.substring( 0, index );
|
||||
html = index < 0 ? "" : html.substring( index );
|
||||
|
||||
if (handler.chars) handler.chars( decodeEntities(text) );
|
||||
}
|
||||
|
||||
} else {
|
||||
html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), function(all, text){
|
||||
text = text.
|
||||
replace(COMMENT_REGEXP, "$1").
|
||||
replace(CDATA_REGEXP, "$1");
|
||||
|
||||
if (handler.chars) handler.chars( decodeEntities(text) );
|
||||
|
||||
return "";
|
||||
});
|
||||
|
||||
parseEndTag( "", stack.last() );
|
||||
}
|
||||
|
||||
if ( html == last ) {
|
||||
throw "Parse Error: " + html;
|
||||
}
|
||||
last = html;
|
||||
}
|
||||
|
||||
// Clean up any remaining tags
|
||||
parseEndTag();
|
||||
|
||||
function parseStartTag( tag, tagName, rest, unary ) {
|
||||
tagName = angular.lowercase(tagName);
|
||||
if ( blockElements[ tagName ] ) {
|
||||
while ( stack.last() && inlineElements[ stack.last() ] ) {
|
||||
parseEndTag( "", stack.last() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( optionalEndTagElements[ tagName ] && stack.last() == tagName ) {
|
||||
parseEndTag( "", tagName );
|
||||
}
|
||||
|
||||
unary = voidElements[ tagName ] || !!unary;
|
||||
|
||||
if ( !unary )
|
||||
stack.push( tagName );
|
||||
|
||||
var attrs = {};
|
||||
|
||||
rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQoutedValue, unqoutedValue) {
|
||||
var value = doubleQuotedValue
|
||||
|| singleQoutedValue
|
||||
|| unqoutedValue
|
||||
|| '';
|
||||
|
||||
attrs[name] = decodeEntities(value);
|
||||
});
|
||||
if (handler.start) handler.start( tagName, attrs, unary );
|
||||
}
|
||||
|
||||
function parseEndTag( tag, tagName ) {
|
||||
var pos = 0, i;
|
||||
tagName = angular.lowercase(tagName);
|
||||
if ( tagName )
|
||||
// Find the closest opened tag of the same type
|
||||
for ( pos = stack.length - 1; pos >= 0; pos-- )
|
||||
if ( stack[ pos ] == tagName )
|
||||
break;
|
||||
|
||||
if ( pos >= 0 ) {
|
||||
// Close all the open elements, up the stack
|
||||
for ( i = stack.length - 1; i >= pos; i-- )
|
||||
if (handler.end) handler.end( stack[ i ] );
|
||||
|
||||
// Remove the open elements from the stack
|
||||
stack.length = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* decodes all entities into regular string
|
||||
* @param value
|
||||
* @returns {string} A string with decoded entities.
|
||||
*/
|
||||
var hiddenPre=document.createElement("pre");
|
||||
function decodeEntities(value) {
|
||||
hiddenPre.innerHTML=value.replace(/</g,"<");
|
||||
return hiddenPre.innerText || hiddenPre.textContent || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes all potentially dangerous characters, so that the
|
||||
* resulting string can be safely inserted into attribute or
|
||||
* element text.
|
||||
* @param value
|
||||
* @returns escaped text
|
||||
*/
|
||||
function encodeEntities(value) {
|
||||
return value.
|
||||
replace(/&/g, '&').
|
||||
replace(NON_ALPHANUMERIC_REGEXP, function(value){
|
||||
return '&#' + value.charCodeAt(0) + ';';
|
||||
}).
|
||||
replace(/</g, '<').
|
||||
replace(/>/g, '>');
|
||||
}
|
||||
|
||||
/**
|
||||
* create an HTML/XML writer which writes to buffer
|
||||
* @param {Array} buf use buf.jain('') to get out sanitized html string
|
||||
* @returns {object} in the form of {
|
||||
* start: function(tag, attrs, unary) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* }
|
||||
*/
|
||||
function htmlSanitizeWriter(buf){
|
||||
var ignore = false;
|
||||
var out = angular.bind(buf, buf.push);
|
||||
return {
|
||||
start: function(tag, attrs, unary){
|
||||
tag = angular.lowercase(tag);
|
||||
if (!ignore && specialElements[tag]) {
|
||||
ignore = tag;
|
||||
}
|
||||
if (!ignore && validElements[tag] == true) {
|
||||
out('<');
|
||||
out(tag);
|
||||
angular.forEach(attrs, function(value, key){
|
||||
var lkey=angular.lowercase(key);
|
||||
if (validAttrs[lkey]==true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) {
|
||||
out(' ');
|
||||
out(key);
|
||||
out('="');
|
||||
out(encodeEntities(value));
|
||||
out('"');
|
||||
}
|
||||
});
|
||||
out(unary ? '/>' : '>');
|
||||
}
|
||||
},
|
||||
end: function(tag){
|
||||
tag = angular.lowercase(tag);
|
||||
if (!ignore && validElements[tag] == true) {
|
||||
out('</');
|
||||
out(tag);
|
||||
out('>');
|
||||
}
|
||||
if (tag == ignore) {
|
||||
ignore = false;
|
||||
}
|
||||
},
|
||||
chars: function(chars){
|
||||
if (!ignore) {
|
||||
out(encodeEntities(chars));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// define ngSanitize module and register $sanitize service
|
||||
angular.module('ngSanitize', []).value('$sanitize', $sanitize);
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngSanitize.directive:ngBindHtml
|
||||
*
|
||||
* @description
|
||||
* Creates a binding that will sanitize the result of evaluating the `expression` with the
|
||||
* {@link ngSanitize.$sanitize $sanitize} service and innerHTML the result into the current element.
|
||||
*
|
||||
* See {@link ngSanitize.$sanitize $sanitize} docs for examples.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
|
||||
*/
|
||||
angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($sanitize) {
|
||||
return function(scope, element, attr) {
|
||||
element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
|
||||
scope.$watch(attr.ngBindHtml, function ngBindHtmlWatchAction(value) {
|
||||
value = $sanitize(value);
|
||||
element.html(value || '');
|
||||
});
|
||||
};
|
||||
}]);
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name ngSanitize.filter:linky
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
|
||||
* plain email address links.
|
||||
*
|
||||
* @param {string} text Input text.
|
||||
* @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
|
||||
* @returns {string} Html-linkified text.
|
||||
*
|
||||
* @usage
|
||||
<span ng-bind-html="linky_expression | linky"></span>
|
||||
*
|
||||
* @example
|
||||
<doc:example module="ngSanitize">
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.snippet =
|
||||
'Pretty text with some links:\n'+
|
||||
'http://angularjs.org/,\n'+
|
||||
'mailto:us@somewhere.org,\n'+
|
||||
'another@somewhere.org,\n'+
|
||||
'and one more: ftp://127.0.0.1/.';
|
||||
$scope.snippetWithTarget = 'http://angularjs.org/';
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Filter</td>
|
||||
<td>Source</td>
|
||||
<td>Rendered</td>
|
||||
</tr>
|
||||
<tr id="linky-filter">
|
||||
<td>linky filter</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="snippet | linky"><br></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-bind-html="snippet | linky"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="linky-target">
|
||||
<td>linky target</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="snippetWithTarget | linky:'_blank'"><br></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="escaped-html">
|
||||
<td>no filter</td>
|
||||
<td><pre><div ng-bind="snippet"><br></div></pre></td>
|
||||
<td><div ng-bind="snippet"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should linkify the snippet with urls', function() {
|
||||
expect(using('#linky-filter').binding('snippet | linky')).
|
||||
toBe('Pretty text with some links: ' +
|
||||
'<a href="http://angularjs.org/">http://angularjs.org/</a>, ' +
|
||||
'<a href="mailto:us@somewhere.org">us@somewhere.org</a>, ' +
|
||||
'<a href="mailto:another@somewhere.org">another@somewhere.org</a>, ' +
|
||||
'and one more: <a href="ftp://127.0.0.1/">ftp://127.0.0.1/</a>.');
|
||||
});
|
||||
|
||||
it ('should not linkify snippet without the linky filter', function() {
|
||||
expect(using('#escaped-html').binding('snippet')).
|
||||
toBe("Pretty text with some links:\n" +
|
||||
"http://angularjs.org/,\n" +
|
||||
"mailto:us@somewhere.org,\n" +
|
||||
"another@somewhere.org,\n" +
|
||||
"and one more: ftp://127.0.0.1/.");
|
||||
});
|
||||
|
||||
it('should update', function() {
|
||||
input('snippet').enter('new http://link.');
|
||||
expect(using('#linky-filter').binding('snippet | linky')).
|
||||
toBe('new <a href="http://link">http://link</a>.');
|
||||
expect(using('#escaped-html').binding('snippet')).toBe('new http://link.');
|
||||
});
|
||||
|
||||
it('should work with the target property', function() {
|
||||
expect(using('#linky-target').binding("snippetWithTarget | linky:'_blank'")).
|
||||
toBe('<a target="_blank" href="http://angularjs.org/">http://angularjs.org/</a>');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angular.module('ngSanitize').filter('linky', function() {
|
||||
var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,
|
||||
MAILTO_REGEXP = /^mailto:/;
|
||||
|
||||
return function(text, target) {
|
||||
if (!text) return text;
|
||||
var match;
|
||||
var raw = text;
|
||||
var html = [];
|
||||
// TODO(vojta): use $sanitize instead
|
||||
var writer = htmlSanitizeWriter(html);
|
||||
var url;
|
||||
var i;
|
||||
var properties = {};
|
||||
if (angular.isDefined(target)) {
|
||||
properties.target = target;
|
||||
}
|
||||
while ((match = raw.match(LINKY_URL_REGEXP))) {
|
||||
// We can not end in these as they are sometimes found at the end of the sentence
|
||||
url = match[0];
|
||||
// if we did not match ftp/http/mailto then assume mailto
|
||||
if (match[2] == match[3]) url = 'mailto:' + url;
|
||||
i = match.index;
|
||||
writer.chars(raw.substr(0, i));
|
||||
properties.href = url;
|
||||
writer.start('a', properties);
|
||||
writer.chars(match[0].replace(MAILTO_REGEXP, ''));
|
||||
writer.end('a');
|
||||
raw = raw.substring(i + match[0].length);
|
||||
}
|
||||
writer.chars(raw);
|
||||
return html.join('');
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
})(window, window.angular);
|
||||
13
docs-web/src/main/webapp/lib/angular/angular-sanitize.min.js
vendored
Normal file
13
docs-web/src/main/webapp/lib/angular/angular-sanitize.min.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
AngularJS v1.1.5
|
||||
(c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(I,h){'use strict';function i(a){var d={},a=a.split(","),c;for(c=0;c<a.length;c++)d[a[c]]=!0;return d}function z(a,d){function c(a,b,c,f){b=h.lowercase(b);if(m[b])for(;e.last()&&n[e.last()];)g("",e.last());o[b]&&e.last()==b&&g("",b);(f=p[b]||!!f)||e.push(b);var j={};c.replace(A,function(a,b,d,c,g){j[b]=k(d||c||g||"")});d.start&&d.start(b,j,f)}function g(a,b){var c=0,g;if(b=h.lowercase(b))for(c=e.length-1;c>=0;c--)if(e[c]==b)break;if(c>=0){for(g=e.length-1;g>=c;g--)d.end&&d.end(e[g]);e.length=
|
||||
c}}var b,f,e=[],j=a;for(e.last=function(){return e[e.length-1]};a;){f=!0;if(!e.last()||!q[e.last()]){if(a.indexOf("<\!--")===0)b=a.indexOf("--\>"),b>=0&&(d.comment&&d.comment(a.substring(4,b)),a=a.substring(b+3),f=!1);else if(B.test(a)){if(b=a.match(r))a=a.substring(b[0].length),b[0].replace(r,g),f=!1}else if(C.test(a)&&(b=a.match(s)))a=a.substring(b[0].length),b[0].replace(s,c),f=!1;f&&(b=a.indexOf("<"),f=b<0?a:a.substring(0,b),a=b<0?"":a.substring(b),d.chars&&d.chars(k(f)))}else a=a.replace(RegExp("(.*)<\\s*\\/\\s*"+
|
||||
e.last()+"[^>]*>","i"),function(a,b){b=b.replace(D,"$1").replace(E,"$1");d.chars&&d.chars(k(b));return""}),g("",e.last());if(a==j)throw"Parse Error: "+a;j=a}g()}function k(a){l.innerHTML=a.replace(/</g,"<");return l.innerText||l.textContent||""}function t(a){return a.replace(/&/g,"&").replace(F,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"<").replace(/>/g,">")}function u(a){var d=!1,c=h.bind(a,a.push);return{start:function(a,b,f){a=h.lowercase(a);!d&&q[a]&&(d=a);!d&&v[a]==
|
||||
!0&&(c("<"),c(a),h.forEach(b,function(a,b){var d=h.lowercase(b);if(G[d]==!0&&(w[d]!==!0||a.match(H)))c(" "),c(b),c('="'),c(t(a)),c('"')}),c(f?"/>":">"))},end:function(a){a=h.lowercase(a);!d&&v[a]==!0&&(c("</"),c(a),c(">"));a==d&&(d=!1)},chars:function(a){d||c(t(a))}}}var s=/^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,r=/^<\s*\/\s*([\w:-]+)[^>]*>/,A=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,C=/^</,B=/^<\s*\//,D=/<\!--(.*?)--\>/g,
|
||||
E=/<!\[CDATA\[(.*?)]]\>/g,H=/^((ftp|https?):\/\/|mailto:|tel:|#)/,F=/([^\#-~| |!])/g,p=i("area,br,col,hr,img,wbr"),x=i("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),y=i("rp,rt"),o=h.extend({},y,x),m=h.extend({},x,i("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),n=h.extend({},y,i("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")),
|
||||
q=i("script,style"),v=h.extend({},p,m,n,o),w=i("background,cite,href,longdesc,src,usemap"),G=h.extend({},w,i("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,span,start,summary,target,title,type,valign,value,vspace,width")),l=document.createElement("pre");h.module("ngSanitize",[]).value("$sanitize",function(a){var d=[];
|
||||
z(a,u(d));return d.join("")});h.module("ngSanitize").directive("ngBindHtml",["$sanitize",function(a){return function(d,c,g){c.addClass("ng-binding").data("$binding",g.ngBindHtml);d.$watch(g.ngBindHtml,function(b){b=a(b);c.html(b||"")})}}]);h.module("ngSanitize").filter("linky",function(){var a=/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,d=/^mailto:/;return function(c,g){if(!c)return c;var b,f=c,e=[],j=u(e),i,k,l={};if(h.isDefined(g))l.target=g;for(;b=f.match(a);)i=
|
||||
b[0],b[2]==b[3]&&(i="mailto:"+i),k=b.index,j.chars(f.substr(0,k)),l.href=i,j.start("a",l),j.chars(b[0].replace(d,"")),j.end("a"),f=f.substring(k+b[0].length);j.chars(f);return e.join("")}})})(window,window.angular);
|
||||
28463
docs-web/src/main/webapp/lib/angular/angular-scenario.js
vendored
Normal file
28463
docs-web/src/main/webapp/lib/angular/angular-scenario.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
16884
docs-web/src/main/webapp/lib/angular/angular.js
vendored
Normal file
16884
docs-web/src/main/webapp/lib/angular/angular.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
178
docs-web/src/main/webapp/lib/angular/angular.min.js
vendored
Normal file
178
docs-web/src/main/webapp/lib/angular/angular.min.js
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
AngularJS v1.1.5
|
||||
(c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(M,T,p){'use strict';function lc(){var b=M.angular;M.angular=mc;return b}function Xa(b){return!b||typeof b.length!=="number"?!1:typeof b.hasOwnProperty!="function"&&typeof b.constructor!="function"?!0:b instanceof R||ga&&b instanceof ga||Ea.call(b)!=="[object Object]"||typeof b.callee==="function"}function n(b,a,c){var d;if(b)if(H(b))for(d in b)d!="prototype"&&d!="length"&&d!="name"&&b.hasOwnProperty(d)&&a.call(c,b[d],d);else if(b.forEach&&b.forEach!==n)b.forEach(a,c);else if(Xa(b))for(d=
|
||||
0;d<b.length;d++)a.call(c,b[d],d);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d],d);return b}function qb(b){var a=[],c;for(c in b)b.hasOwnProperty(c)&&a.push(c);return a.sort()}function nc(b,a,c){for(var d=qb(b),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d}function rb(b){return function(a,c){b(c,a)}}function Fa(){for(var b=ba.length,a;b;){b--;a=ba[b].charCodeAt(0);if(a==57)return ba[b]="A",ba.join("");if(a==90)ba[b]="0";else return ba[b]=String.fromCharCode(a+1),ba.join("")}ba.unshift("0");
|
||||
return ba.join("")}function sb(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function t(b){var a=b.$$hashKey;n(arguments,function(a){a!==b&&n(a,function(a,c){b[c]=a})});sb(b,a);return b}function N(b){return parseInt(b,10)}function tb(b,a){return t(new (t(function(){},{prototype:b})),a)}function q(){}function qa(b){return b}function S(b){return function(){return b}}function C(b){return typeof b=="undefined"}function B(b){return typeof b!="undefined"}function L(b){return b!=null&&typeof b=="object"}function E(b){return typeof b==
|
||||
"string"}function Ya(b){return typeof b=="number"}function ra(b){return Ea.apply(b)=="[object Date]"}function F(b){return Ea.apply(b)=="[object Array]"}function H(b){return typeof b=="function"}function sa(b){return b&&b.document&&b.location&&b.alert&&b.setInterval}function U(b){return E(b)?b.replace(/^\s*/,"").replace(/\s*$/,""):b}function oc(b){return b&&(b.nodeName||b.bind&&b.find)}function Za(b,a,c){var d=[];n(b,function(b,g,i){d.push(a.call(c,b,g,i))});return d}function Ga(b,a){if(b.indexOf)return b.indexOf(a);
|
||||
for(var c=0;c<b.length;c++)if(a===b[c])return c;return-1}function ta(b,a){var c=Ga(b,a);c>=0&&b.splice(c,1);return a}function V(b,a){if(sa(b)||b&&b.$evalAsync&&b.$watch)throw Error("Can't copy Window or Scope");if(a){if(b===a)throw Error("Can't copy equivalent objects or arrays");if(F(b))for(var c=a.length=0;c<b.length;c++)a.push(V(b[c]));else{c=a.$$hashKey;n(a,function(b,c){delete a[c]});for(var d in b)a[d]=V(b[d]);sb(a,c)}}else(a=b)&&(F(b)?a=V(b,[]):ra(b)?a=new Date(b.getTime()):L(b)&&(a=V(b,{})));
|
||||
return a}function pc(b,a){var a=a||{},c;for(c in b)b.hasOwnProperty(c)&&c.substr(0,2)!=="$$"&&(a[c]=b[c]);return a}function ia(b,a){if(b===a)return!0;if(b===null||a===null)return!1;if(b!==b&&a!==a)return!0;var c=typeof b,d;if(c==typeof a&&c=="object")if(F(b)){if((c=b.length)==a.length){for(d=0;d<c;d++)if(!ia(b[d],a[d]))return!1;return!0}}else if(ra(b))return ra(a)&&b.getTime()==a.getTime();else{if(b&&b.$evalAsync&&b.$watch||a&&a.$evalAsync&&a.$watch||sa(b)||sa(a))return!1;c={};for(d in b)if(!(d.charAt(0)===
|
||||
"$"||H(b[d]))){if(!ia(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c[d]&&d.charAt(0)!=="$"&&a[d]!==p&&!H(a[d]))return!1;return!0}return!1}function $a(b,a){var c=arguments.length>2?ka.call(arguments,2):[];return H(a)&&!(a instanceof RegExp)?c.length?function(){return arguments.length?a.apply(b,c.concat(ka.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}:a}function qc(b,a){var c=a;/^\$+/.test(b)?c=p:sa(a)?c="$WINDOW":a&&T===a?c="$DOCUMENT":a&&a.$evalAsync&&
|
||||
a.$watch&&(c="$SCOPE");return c}function ha(b,a){return JSON.stringify(b,qc,a?" ":null)}function ub(b){return E(b)?JSON.parse(b):b}function ua(b){b&&b.length!==0?(b=I(""+b),b=!(b=="f"||b=="0"||b=="false"||b=="no"||b=="n"||b=="[]")):b=!1;return b}function va(b){b=w(b).clone();try{b.html("")}catch(a){}var c=w("<div>").append(b).html();try{return b[0].nodeType===3?I(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+I(b)})}catch(d){return I(c)}}function vb(b){var a={},c,d;n((b||
|
||||
"").split("&"),function(b){b&&(c=b.split("="),d=decodeURIComponent(c[0]),a[d]=B(c[1])?decodeURIComponent(c[1]):!0)});return a}function wb(b){var a=[];n(b,function(b,d){a.push(wa(d,!0)+(b===!0?"":"="+wa(b,!0)))});return a.length?a.join("&"):""}function ab(b){return wa(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function wa(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,a?"%20":"+")}function rc(b,
|
||||
a){function c(a){a&&d.push(a)}var d=[b],e,g,i=["ng:app","ng-app","x-ng-app","data-ng-app"],f=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;n(i,function(a){i[a]=!0;c(T.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(n(b.querySelectorAll("."+a),c),n(b.querySelectorAll("."+a+"\\:"),c),n(b.querySelectorAll("["+a+"]"),c))});n(d,function(a){if(!e){var b=f.exec(" "+a.className+" ");b?(e=a,g=(b[2]||"").replace(/\s+/g,",")):n(a.attributes,function(b){if(!e&&i[b.name])e=a,g=b.value})}});e&&a(e,g?[g]:[])}
|
||||
function xb(b,a){var c=function(){b=w(b);a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");var c=yb(a);c.invoke(["$rootScope","$rootElement","$compile","$injector","$animator",function(a,b,c,d,e){a.$apply(function(){b.data("$injector",d);c(b)(a)});e.enabled(!0)}]);return c},d=/^NG_DEFER_BOOTSTRAP!/;if(M&&!d.test(M.name))return c();M.name=M.name.replace(d,"");Ha.resumeBootstrap=function(b){n(b,function(b){a.push(b)});c()}}function bb(b,a){a=a||"_";return b.replace(sc,
|
||||
function(b,d){return(d?a:"")+b.toLowerCase()})}function cb(b,a,c){if(!b)throw Error("Argument '"+(a||"?")+"' is "+(c||"required"));return b}function xa(b,a,c){c&&F(b)&&(b=b[b.length-1]);cb(H(b),a,"not a function, got "+(b&&typeof b=="object"?b.constructor.name||"Object":typeof b));return b}function tc(b){function a(a,b,e){return a[b]||(a[b]=e())}return a(a(b,"angular",Object),"module",function(){var b={};return function(d,e,g){e&&b.hasOwnProperty(d)&&(b[d]=null);return a(b,d,function(){function a(c,
|
||||
d,e){return function(){b[e||"push"]([c,d,arguments]);return m}}if(!e)throw Error("No module: "+d);var b=[],c=[],j=a("$injector","invoke"),m={_invokeQueue:b,_runBlocks:c,requires:e,name:d,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),animation:a("$animationProvider","register"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider",
|
||||
"directive"),config:j,run:function(a){c.push(a);return this}};g&&j(g);return m})}})}function Ia(b){return b.replace(uc,function(a,b,d,e){return e?d.toUpperCase():d}).replace(vc,"Moz$1")}function db(b,a){function c(){var e;for(var b=[this],c=a,i,f,h,j,m,k;b.length;){i=b.shift();f=0;for(h=i.length;f<h;f++){j=w(i[f]);c?j.triggerHandler("$destroy"):c=!c;m=0;for(e=(k=j.children()).length,j=e;m<j;m++)b.push(ga(k[m]))}}return d.apply(this,arguments)}var d=ga.fn[b],d=d.$original||d;c.$original=d;ga.fn[b]=
|
||||
c}function R(b){if(b instanceof R)return b;if(!(this instanceof R)){if(E(b)&&b.charAt(0)!="<")throw Error("selectors not implemented");return new R(b)}if(E(b)){var a=T.createElement("div");a.innerHTML="<div> </div>"+b;a.removeChild(a.firstChild);eb(this,a.childNodes);this.remove()}else eb(this,b)}function fb(b){return b.cloneNode(!0)}function ya(b){zb(b);for(var a=0,b=b.childNodes||[];a<b.length;a++)ya(b[a])}function Ab(b,a,c){var d=ca(b,"events");ca(b,"handle")&&(C(a)?n(d,function(a,c){gb(b,
|
||||
c,a);delete d[c]}):C(c)?(gb(b,a,d[a]),delete d[a]):ta(d[a],c))}function zb(b){var a=b[Ja],c=Ka[a];c&&(c.handle&&(c.events.$destroy&&c.handle({},"$destroy"),Ab(b)),delete Ka[a],b[Ja]=p)}function ca(b,a,c){var d=b[Ja],d=Ka[d||-1];if(B(c))d||(b[Ja]=d=++wc,d=Ka[d]={}),d[a]=c;else return d&&d[a]}function Bb(b,a,c){var d=ca(b,"data"),e=B(c),g=!e&&B(a),i=g&&!L(a);!d&&!i&&ca(b,"data",d={});if(e)d[a]=c;else if(g)if(i)return d&&d[a];else t(d,a);else return d}function La(b,a){return(" "+b.className+" ").replace(/[\n\t]/g,
|
||||
" ").indexOf(" "+a+" ")>-1}function Cb(b,a){a&&n(a.split(" "),function(a){b.className=U((" "+b.className+" ").replace(/[\n\t]/g," ").replace(" "+U(a)+" "," "))})}function Db(b,a){a&&n(a.split(" "),function(a){if(!La(b,a))b.className=U(b.className+" "+U(a))})}function eb(b,a){if(a)for(var a=!a.nodeName&&B(a.length)&&!sa(a)?a:[a],c=0;c<a.length;c++)b.push(a[c])}function Eb(b,a){return Ma(b,"$"+(a||"ngController")+"Controller")}function Ma(b,a,c){b=w(b);for(b[0].nodeType==9&&(b=b.find("html"));b.length;){if(c=
|
||||
b.data(a))return c;b=b.parent()}}function Fb(b,a){var c=Na[a.toLowerCase()];return c&&Gb[b.nodeName]&&c}function xc(b,a){var c=function(c,e){if(!c.preventDefault)c.preventDefault=function(){c.returnValue=!1};if(!c.stopPropagation)c.stopPropagation=function(){c.cancelBubble=!0};if(!c.target)c.target=c.srcElement||T;if(C(c.defaultPrevented)){var g=c.preventDefault;c.preventDefault=function(){c.defaultPrevented=!0;g.call(c)};c.defaultPrevented=!1}c.isDefaultPrevented=function(){return c.defaultPrevented||
|
||||
c.returnValue==!1};n(a[e||c.type],function(a){a.call(b,c)});Z<=8?(c.preventDefault=null,c.stopPropagation=null,c.isDefaultPrevented=null):(delete c.preventDefault,delete c.stopPropagation,delete c.isDefaultPrevented)};c.elem=b;return c}function la(b){var a=typeof b,c;if(a=="object"&&b!==null)if(typeof(c=b.$$hashKey)=="function")c=b.$$hashKey();else{if(c===p)c=b.$$hashKey=Fa()}else c=b;return a+":"+c}function za(b){n(b,this.put,this)}function Hb(b){var a,c;if(typeof b=="function"){if(!(a=b.$inject))a=
|
||||
[],c=b.toString().replace(yc,""),c=c.match(zc),n(c[1].split(Ac),function(b){b.replace(Bc,function(b,c,d){a.push(d)})}),b.$inject=a}else F(b)?(c=b.length-1,xa(b[c],"fn"),a=b.slice(0,c)):xa(b,"fn",!0);return a}function yb(b){function a(a){return function(b,c){if(L(b))n(b,rb(a));else return a(b,c)}}function c(a,b){if(H(b)||F(b))b=k.instantiate(b);if(!b.$get)throw Error("Provider "+a+" must define $get factory method.");return m[a+f]=b}function d(a,b){return c(a,{$get:b})}function e(a){var b=[];n(a,function(a){if(!j.get(a))if(j.put(a,
|
||||
!0),E(a)){var c=Aa(a);b=b.concat(e(c.requires)).concat(c._runBlocks);try{for(var d=c._invokeQueue,c=0,f=d.length;c<f;c++){var g=d[c],o=k.get(g[0]);o[g[1]].apply(o,g[2])}}catch(h){throw h.message&&(h.message+=" from "+a),h;}}else if(H(a))try{b.push(k.invoke(a))}catch(l){throw l.message&&(l.message+=" from "+a),l;}else if(F(a))try{b.push(k.invoke(a))}catch(i){throw i.message&&(i.message+=" from "+String(a[a.length-1])),i;}else xa(a,"module")});return b}function g(a,b){function c(d){if(typeof d!=="string")throw Error("Service name expected");
|
||||
if(a.hasOwnProperty(d)){if(a[d]===i)throw Error("Circular dependency: "+h.join(" <- "));return a[d]}else try{return h.unshift(d),a[d]=i,a[d]=b(d)}finally{h.shift()}}function d(a,b,e){var f=[],j=Hb(a),g,o,h;o=0;for(g=j.length;o<g;o++)h=j[o],f.push(e&&e.hasOwnProperty(h)?e[h]:c(h));a.$inject||(a=a[g]);switch(b?-1:f.length){case 0:return a();case 1:return a(f[0]);case 2:return a(f[0],f[1]);case 3:return a(f[0],f[1],f[2]);case 4:return a(f[0],f[1],f[2],f[3]);case 5:return a(f[0],f[1],f[2],f[3],f[4]);
|
||||
case 6:return a(f[0],f[1],f[2],f[3],f[4],f[5]);case 7:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6]);case 8:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7]);case 9:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8]);case 10:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8],f[9]);default:return a.apply(b,f)}}return{invoke:d,instantiate:function(a,b){var c=function(){},e;c.prototype=(F(a)?a[a.length-1]:a).prototype;c=new c;e=d(a,c,b);return L(e)?e:c},get:c,annotate:Hb,has:function(b){return m.hasOwnProperty(b+
|
||||
f)||a.hasOwnProperty(b)}}}var i={},f="Provider",h=[],j=new za,m={$provide:{provider:a(c),factory:a(d),service:a(function(a,b){return d(a,["$injector",function(a){return a.instantiate(b)}])}),value:a(function(a,b){return d(a,S(b))}),constant:a(function(a,b){m[a]=b;l[a]=b}),decorator:function(a,b){var c=k.get(a+f),d=c.$get;c.$get=function(){var a=u.invoke(d,c);return u.invoke(b,null,{$delegate:a})}}}},k=m.$injector=g(m,function(){throw Error("Unknown provider: "+h.join(" <- "));}),l={},u=l.$injector=
|
||||
g(l,function(a){a=k.get(a+f);return u.invoke(a.$get,a)});n(e(b),function(a){u.invoke(a||q)});return u}function Cc(){var b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;n(a,function(a){!b&&I(a.nodeName)==="a"&&(b=a)});return b}function g(){var b=c.hash(),d;b?(d=i.getElementById(b))?d.scrollIntoView():(d=e(i.getElementsByName(b)))?d.scrollIntoView():b==="top"&&a.scrollTo(0,0):a.scrollTo(0,0)}var i=a.document;b&&d.$watch(function(){return c.hash()},
|
||||
function(){d.$evalAsync(g)});return g}]}function Ib(b){this.register=function(a,c){b.factory(Ia(a)+"Animation",c)};this.$get=["$injector",function(a){return function(b){if(b&&(b=Ia(b)+"Animation",a.has(b)))return a.get(b)}}]}function Dc(b,a,c,d){function e(a){try{a.apply(null,ka.call(arguments,1))}finally{if(o--,o===0)for(;z.length;)try{z.pop()()}catch(b){c.error(b)}}}function g(a,b){(function s(){n(r,function(a){a()});y=b(s,a)})()}function i(){x!=f.url()&&(x=f.url(),n(v,function(a){a(f.url())}))}
|
||||
var f=this,h=a[0],j=b.location,m=b.history,k=b.setTimeout,l=b.clearTimeout,u={};f.isMock=!1;var o=0,z=[];f.$$completeOutstandingRequest=e;f.$$incOutstandingRequestCount=function(){o++};f.notifyWhenNoOutstandingRequests=function(a){n(r,function(a){a()});o===0?a():z.push(a)};var r=[],y;f.addPollFn=function(a){C(y)&&g(100,k);r.push(a);return a};var x=j.href,W=a.find("base");f.url=function(a,b){if(a){if(x!=a)return x=a,d.history?b?m.replaceState(null,"",a):(m.pushState(null,"",a),W.attr("href",W.attr("href"))):
|
||||
b?j.replace(a):j.href=a,f}else return j.href.replace(/%27/g,"'")};var v=[],A=!1;f.onUrlChange=function(a){A||(d.history&&w(b).bind("popstate",i),d.hashchange?w(b).bind("hashchange",i):f.addPollFn(i),A=!0);v.push(a);return a};f.baseHref=function(){var a=W.attr("href");return a?a.replace(/^https?\:\/\/[^\/]*/,""):""};var G={},D="",$=f.baseHref();f.cookies=function(a,b){var d,e,f,j;if(a)if(b===p)h.cookie=escape(a)+"=;path="+$+";expires=Thu, 01 Jan 1970 00:00:00 GMT";else{if(E(b))d=(h.cookie=escape(a)+
|
||||
"="+escape(b)+";path="+$).length+1,d>4096&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!")}else{if(h.cookie!==D){D=h.cookie;d=D.split("; ");G={};for(f=0;f<d.length;f++)e=d[f],j=e.indexOf("="),j>0&&(a=unescape(e.substring(0,j)),G[a]===p&&(G[a]=unescape(e.substring(j+1))))}return G}};f.defer=function(a,b){var c;o++;c=k(function(){delete u[c];e(a)},b||0);u[c]=!0;return c};f.defer.cancel=function(a){return u[a]?(delete u[a],l(a),e(q),!0):!1}}function Ec(){this.$get=
|
||||
["$window","$log","$sniffer","$document",function(b,a,c,d){return new Dc(b,d,a,c)}]}function Fc(){this.$get=function(){function b(b,d){function e(a){if(a!=k){if(l){if(l==a)l=a.n}else l=a;g(a.n,a.p);g(a,k);k=a;k.n=null}}function g(a,b){if(a!=b){if(a)a.p=b;if(b)b.n=a}}if(b in a)throw Error("cacheId "+b+" taken");var i=0,f=t({},d,{id:b}),h={},j=d&&d.capacity||Number.MAX_VALUE,m={},k=null,l=null;return a[b]={put:function(a,b){var c=m[a]||(m[a]={key:a});e(c);if(!C(b))return a in h||i++,h[a]=b,i>j&&this.remove(l.key),
|
||||
b},get:function(a){var b=m[a];if(b)return e(b),h[a]},remove:function(a){var b=m[a];if(b){if(b==k)k=b.p;if(b==l)l=b.n;g(b.n,b.p);delete m[a];delete h[a];i--}},removeAll:function(){h={};i=0;m={};k=l=null},destroy:function(){m=f=h=null;delete a[b]},info:function(){return t({},f,{size:i})}}}var a={};b.info=function(){var b={};n(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function Gc(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function Jb(b){var a=
|
||||
{},c="Directive",d=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,e=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,g="Template must have exactly one root element. was: ",i=/^\s*(https?|ftp|mailto|file):/;this.directive=function h(d,e){E(d)?(cb(e,"directive"),a.hasOwnProperty(d)||(a[d]=[],b.factory(d+c,["$injector","$exceptionHandler",function(b,c){var e=[];n(a[d],function(a){try{var g=b.invoke(a);if(H(g))g={compile:S(g)};else if(!g.compile&&g.link)g.compile=S(g.link);g.priority=g.priority||0;g.name=g.name||d;g.require=
|
||||
g.require||g.controller&&g.name;g.restrict=g.restrict||"A";e.push(g)}catch(h){c(h)}});return e}])),a[d].push(e)):n(d,rb(h));return this};this.urlSanitizationWhitelist=function(a){return B(a)?(i=a,this):i};this.$get=["$injector","$interpolate","$exceptionHandler","$http","$templateCache","$parse","$controller","$rootScope","$document",function(b,j,m,k,l,u,o,z,r){function y(a,b,c){a instanceof w||(a=w(a));n(a,function(b,c){b.nodeType==3&&b.nodeValue.match(/\S+/)&&(a[c]=w(b).wrap("<span></span>").parent()[0])});
|
||||
var d=W(a,b,a,c);return function(b,c){cb(b,"scope");for(var e=c?Ba.clone.call(a):a,j=0,g=e.length;j<g;j++){var h=e[j];(h.nodeType==1||h.nodeType==9)&&e.eq(j).data("$scope",b)}x(e,"ng-scope");c&&c(e,b);d&&d(b,e,e);return e}}function x(a,b){try{a.addClass(b)}catch(c){}}function W(a,b,c,d){function e(a,c,d,g){var h,i,k,l,o,m,u,z=[];o=0;for(m=c.length;o<m;o++)z.push(c[o]);u=o=0;for(m=j.length;o<m;u++)i=z[u],c=j[o++],h=j[o++],c?(c.scope?(k=a.$new(L(c.scope)),w(i).data("$scope",k)):k=a,(l=c.transclude)||
|
||||
!g&&b?c(h,k,i,d,function(b){return function(c){var d=a.$new();d.$$transcluded=!0;return b(d,c).bind("$destroy",$a(d,d.$destroy))}}(l||b)):c(h,k,i,p,g)):h&&h(a,i.childNodes,p,g)}for(var j=[],g,h,k,i=0;i<a.length;i++)h=new ma,g=v(a[i],[],h,d),h=(g=g.length?A(g,a[i],h,b,c):null)&&g.terminal||!a[i].childNodes||!a[i].childNodes.length?null:W(a[i].childNodes,g?g.transclude:b),j.push(g),j.push(h),k=k||g||h;return k?e:null}function v(a,b,c,j){var g=c.$attr,h;switch(a.nodeType){case 1:G(b,da(hb(a).toLowerCase()),
|
||||
"E",j);var i,k,l;h=a.attributes;for(var o=0,m=h&&h.length;o<m;o++)if(i=h[o],i.specified)k=i.name,l=da(k),Y.test(l)&&(k=l.substr(6).toLowerCase()),l=da(k.toLowerCase()),g[l]=k,c[l]=i=U(Z&&k=="href"?decodeURIComponent(a.getAttribute(k,2)):i.value),Fb(a,l)&&(c[l]=!0),s(a,b,i,l),G(b,l,"A",j);a=a.className;if(E(a)&&a!=="")for(;h=e.exec(a);)l=da(h[2]),G(b,l,"C",j)&&(c[l]=U(h[3])),a=a.substr(h.index+h[0].length);break;case 3:P(b,a.nodeValue);break;case 8:try{if(h=d.exec(a.nodeValue))l=da(h[1]),G(b,l,"M",
|
||||
j)&&(c[l]=U(h[2]))}catch(u){}}b.sort(K);return b}function A(a,b,c,d,e){function h(a,b){if(a)a.require=s.require,z.push(a);if(b)b.require=s.require,ea.push(b)}function i(a,b){var c,d="data",e=!1;if(E(a)){for(;(c=a.charAt(0))=="^"||c=="?";)a=a.substr(1),c=="^"&&(d="inheritedData"),e=e||c=="?";c=b[d]("$"+a+"Controller");if(!c&&!e)throw Error("No controller: "+a);}else F(a)&&(c=[],n(a,function(a){c.push(i(a,b))}));return c}function k(a,d,e,g,h){var l,v,r,D,x;l=b===e?c:pc(c,new ma(w(e),c.$attr));v=l.$$element;
|
||||
if(K){var y=/^\s*([@=&])(\??)\s*(\w*)\s*$/,s=d.$parent||d;n(K.scope,function(a,b){var c=a.match(y)||[],e=c[3]||b,g=c[2]=="?",c=c[1],h,k,i;d.$$isolateBindings[b]=c+e;switch(c){case "@":l.$observe(e,function(a){d[b]=a});l.$$observers[e].$$scope=s;l[e]&&(d[b]=j(l[e])(s));break;case "=":if(g&&!l[e])break;k=u(l[e]);i=k.assign||function(){h=d[b]=k(s);throw Error(Kb+l[e]+" (directive: "+K.name+")");};h=d[b]=k(s);d.$watch(function(){var a=k(s);a!==d[b]&&(a!==h?h=d[b]=a:i(s,a=h=d[b]));return a});break;case "&":k=
|
||||
u(l[e]);d[b]=function(a){return k(s,a)};break;default:throw Error("Invalid isolate scope definition for directive "+K.name+": "+a);}})}q&&n(q,function(a){var b={$scope:d,$element:v,$attrs:l,$transclude:h};x=a.controller;x=="@"&&(x=l[a.name]);v.data("$"+a.name+"Controller",o(x,b))});g=0;for(r=z.length;g<r;g++)try{D=z[g],D(d,v,l,D.require&&i(D.require,v))}catch(Hc){m(Hc,va(v))}a&&a(d,e.childNodes,p,h);g=0;for(r=ea.length;g<r;g++)try{D=ea[g],D(d,v,l,D.require&&i(D.require,v))}catch(J){m(J,va(v))}}for(var l=
|
||||
-Number.MAX_VALUE,z=[],ea=[],r=null,K=null,W=null,J=c.$$element=w(b),s,A,Y,G,P=d,q,na,t,B=0,C=a.length;B<C;B++){s=a[B];Y=p;if(l>s.priority)break;if(t=s.scope)O("isolated scope",K,s,J),L(t)&&(x(J,"ng-isolate-scope"),K=s),x(J,"ng-scope"),r=r||s;A=s.name;if(t=s.controller)q=q||{},O("'"+A+"' controller",q[A],s,J),q[A]=s;if(t=s.transclude)O("transclusion",G,s,J),G=s,l=s.priority,t=="element"?(Y=w(b),J=c.$$element=w(T.createComment(" "+A+": "+c[A]+" ")),b=J[0],ja(e,w(Y[0]),b),P=y(Y,d,l)):(Y=w(fb(b)).contents(),
|
||||
J.html(""),P=y(Y,d));if(s.template)if(O("template",W,s,J),W=s,t=H(s.template)?s.template(J,c):s.template,t=Lb(t),s.replace){Y=w("<div>"+U(t)+"</div>").contents();b=Y[0];if(Y.length!=1||b.nodeType!==1)throw Error(g+t);ja(e,J,b);A={$attr:{}};a=a.concat(v(b,a.splice(B+1,a.length-(B+1)),A));D(c,A);C=a.length}else J.html(t);if(s.templateUrl)O("template",W,s,J),W=s,k=$(a.splice(B,a.length-B),k,J,c,e,s.replace,P),C=a.length;else if(s.compile)try{na=s.compile(J,c,P),H(na)?h(null,na):na&&h(na.pre,na.post)}catch(I){m(I,
|
||||
va(J))}if(s.terminal)k.terminal=!0,l=Math.max(l,s.priority)}k.scope=r&&r.scope;k.transclude=G&&P;return k}function G(d,e,j,g){var l=!1;if(a.hasOwnProperty(e))for(var k,e=b.get(e+c),i=0,o=e.length;i<o;i++)try{if(k=e[i],(g===p||g>k.priority)&&k.restrict.indexOf(j)!=-1)d.push(k),l=!0}catch(u){m(u)}return l}function D(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;n(a,function(d,e){e.charAt(0)!="$"&&(b[e]&&(d+=(e==="style"?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});n(b,function(b,j){j=="class"?(x(e,b),a["class"]=
|
||||
(a["class"]?a["class"]+" ":"")+b):j=="style"?e.attr("style",e.attr("style")+";"+b):j.charAt(0)!="$"&&!a.hasOwnProperty(j)&&(a[j]=b,d[j]=c[j])})}function $(a,b,c,d,e,j,h){var i=[],o,m,u=c[0],z=a.shift(),r=t({},z,{controller:null,templateUrl:null,transclude:null,scope:null}),z=H(z.templateUrl)?z.templateUrl(c,d):z.templateUrl;c.html("");k.get(z,{cache:l}).success(function(l){var k,z,l=Lb(l);if(j){z=w("<div>"+U(l)+"</div>").contents();k=z[0];if(z.length!=1||k.nodeType!==1)throw Error(g+l);l={$attr:{}};
|
||||
ja(e,c,k);v(k,a,l);D(d,l)}else k=u,c.html(l);a.unshift(r);o=A(a,k,d,h);for(m=W(c[0].childNodes,h);i.length;){var ea=i.shift(),l=i.shift();z=i.shift();var x=i.shift(),y=k;l!==u&&(y=fb(k),ja(z,w(l),y));o(function(){b(m,ea,y,e,x)},ea,y,e,x)}i=null}).error(function(a,b,c,d){throw Error("Failed to load template: "+d.url);});return function(a,c,d,e,j){i?(i.push(c),i.push(d),i.push(e),i.push(j)):o(function(){b(m,c,d,e,j)},c,d,e,j)}}function K(a,b){return b.priority-a.priority}function O(a,b,c,d){if(b)throw Error("Multiple directives ["+
|
||||
b.name+", "+c.name+"] asking for "+a+" on: "+va(d));}function P(a,b){var c=j(b,!0);c&&a.push({priority:0,compile:S(function(a,b){var d=b.parent(),e=d.data("$binding")||[];e.push(c);x(d.data("$binding",e),"ng-binding");a.$watch(c,function(a){b[0].nodeValue=a})})})}function s(a,b,c,d){var e=j(c,!0);e&&b.push({priority:100,compile:S(function(a,b,c){b=c.$$observers||(c.$$observers={});if(e=j(c[d],!0))c[d]=e(a),(b[d]||(b[d]=[])).$$inter=!0,(c.$$observers&&c.$$observers[d].$$scope||a).$watch(e,function(a){c.$set(d,
|
||||
a)})})})}function ja(a,b,c){var d=b[0],e=d.parentNode,j,g;if(a){j=0;for(g=a.length;j<g;j++)if(a[j]==d){a[j]=c;break}}e&&e.replaceChild(c,d);c[w.expando]=d[w.expando];b[0]=c}var ma=function(a,b){this.$$element=a;this.$attr=b||{}};ma.prototype={$normalize:da,$set:function(a,b,c,d){var e=Fb(this.$$element[0],a),j=this.$$observers;e&&(this.$$element.prop(a,b),d=e);this[a]=b;d?this.$attr[a]=d:(d=this.$attr[a])||(this.$attr[a]=d=bb(a,"-"));if(hb(this.$$element[0])==="A"&&a==="href")q.setAttribute("href",
|
||||
b),e=q.href,e.match(i)||(this[a]=b="unsafe:"+e);c!==!1&&(b===null||b===p?this.$$element.removeAttr(d):this.$$element.attr(d,b));j&&n(j[a],function(a){try{a(b)}catch(c){m(c)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers={}),e=d[a]||(d[a]=[]);e.push(b);z.$evalAsync(function(){e.$$inter||b(c[a])});return b}};var q=r[0].createElement("a"),ea=j.startSymbol(),J=j.endSymbol(),Lb=ea=="{{"||J=="}}"?qa:function(a){return a.replace(/\{\{/g,ea).replace(/}}/g,J)},Y=/^ngAttr[A-Z]/;return y}]}
|
||||
function da(b){return Ia(b.replace(Ic,""))}function Jc(){var b={},a=/^(\S+)(\s+as\s+(\w+))?$/;this.register=function(a,d){L(a)?t(b,a):b[a]=d};this.$get=["$injector","$window",function(c,d){return function(e,g){var i,f;E(e)&&(f=e.match(a),i=f[1],f=f[3],e=b.hasOwnProperty(i)?b[i]:ib(g.$scope,i,!0)||ib(d,i,!0),xa(e,i,!0));i=c.instantiate(e,g);if(f){if(typeof g.$scope!=="object")throw Error('Can not export controller as "'+f+'". No scope object provided!');g.$scope[f]=i}return i}}]}function Kc(){this.$get=
|
||||
["$window",function(b){return w(b.document)}]}function Lc(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function Mc(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse","$exceptionHandler",function(c,d){function e(e,h){for(var j,m,k=0,l=[],u=e.length,o=!1,z=[];k<u;)(j=e.indexOf(b,k))!=-1&&(m=e.indexOf(a,j+g))!=-1?(k!=j&&l.push(e.substring(k,j)),l.push(k=c(o=e.substring(j+g,
|
||||
m))),k.exp=o,k=m+i,o=!0):(k!=u&&l.push(e.substring(k)),k=u);if(!(u=l.length))l.push(""),u=1;if(!h||o)return z.length=u,k=function(a){try{for(var b=0,c=u,j;b<c;b++){if(typeof(j=l[b])=="function")j=j(a),j==null||j==p?j="":typeof j!="string"&&(j=ha(j));z[b]=j}return z.join("")}catch(g){d(Error("Error while interpolating: "+e+"\n"+g.toString()))}},k.exp=e,k.parts=l,k}var g=b.length,i=a.length;e.startSymbol=function(){return b};e.endSymbol=function(){return a};return e}]}function Mb(b){for(var b=b.split("/"),
|
||||
a=b.length;a--;)b[a]=ab(b[a]);return b.join("/")}function Nb(b,a){var c=jb.exec(b);a.$$protocol=c[1];a.$$host=c[3];a.$$port=N(c[5])||Oa[c[1]]||null}function Ob(b,a){var c=Pb.exec(b);a.$$path=decodeURIComponent(c[1]);a.$$search=vb(c[3]);a.$$hash=decodeURIComponent(c[5]||"");if(a.$$path&&a.$$path.charAt(0)!="/")a.$$path="/"+a.$$path}function fa(b,a,c){return a.indexOf(b)==0?a.substr(b.length):c}function Ca(b){var a=b.indexOf("#");return a==-1?b:b.substr(0,a)}function kb(b){return b.substr(0,Ca(b).lastIndexOf("/")+
|
||||
1)}function Qb(b,a){var a=a||"",c=kb(b);this.$$parse=function(a){var b={};Nb(a,b);var g=fa(c,a);if(!E(g))throw Error('Invalid url "'+a+'", missing path prefix "'+c+'".');Ob(g,b);t(this,b);if(!this.$$path)this.$$path="/";this.$$compose()};this.$$compose=function(){var a=wb(this.$$search),b=this.$$hash?"#"+ab(this.$$hash):"";this.$$url=Mb(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$rewrite=function(d){var e;if((e=fa(b,d))!==p)return d=e,(e=fa(a,e))!==p?c+(fa("/",e)||e):
|
||||
b+d;else if((e=fa(c,d))!==p)return c+e;else if(c==d+"/")return c}}function lb(b,a){var c=kb(b);this.$$parse=function(d){Nb(d,this);var e=fa(b,d)||fa(c,d);if(!E(e))throw Error('Invalid url "'+d+'", does not start with "'+b+'".');e=e.charAt(0)=="#"?fa(a,e):e;if(!E(e))throw Error('Invalid url "'+d+'", missing hash prefix "'+a+'".');Ob(e,this);this.$$compose()};this.$$compose=function(){var c=wb(this.$$search),e=this.$$hash?"#"+ab(this.$$hash):"";this.$$url=Mb(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=
|
||||
b+(this.$$url?a+this.$$url:"")};this.$$rewrite=function(a){if(Ca(b)==Ca(a))return a}}function Rb(b,a){lb.apply(this,arguments);var c=kb(b);this.$$rewrite=function(d){var e;if(b==Ca(d))return d;else if(e=fa(c,d))return b+a+e;else if(c===d+"/")return c}}function Pa(b){return function(){return this[b]}}function Sb(b,a){return function(c){if(C(c))return this[b];this[b]=a(c);this.$$compose();return this}}function Nc(){var b="",a=!1;this.hashPrefix=function(a){return B(a)?(b=a,this):b};this.html5Mode=function(b){return B(b)?
|
||||
(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,g){function i(a){c.$broadcast("$locationChangeSuccess",f.absUrl(),a)}var f,h=d.baseHref(),j=d.url();a?(h=h?j.substring(0,j.indexOf("/",j.indexOf("//")+2))+h:j,e=e.history?Qb:Rb):(h=Ca(j),e=lb);f=new e(h,"#"+b);f.$$parse(f.$$rewrite(j));g.bind("click",function(a){if(!a.ctrlKey&&!(a.metaKey||a.which==2)){for(var b=w(a.target);I(b[0].nodeName)!=="a";)if(b[0]===g[0]||!(b=b.parent())[0])return;var e=b.prop("href"),
|
||||
j=f.$$rewrite(e);e&&!b.attr("target")&&j&&!a.isDefaultPrevented()&&(a.preventDefault(),j!=d.url()&&(f.$$parse(j),c.$apply(),M.angular["ff-684208-preventDefault"]=!0))}});f.absUrl()!=j&&d.url(f.absUrl(),!0);d.onUrlChange(function(a){f.absUrl()!=a&&(c.$broadcast("$locationChangeStart",a,f.absUrl()).defaultPrevented?d.url(f.absUrl()):(c.$evalAsync(function(){var b=f.absUrl();f.$$parse(a);i(b)}),c.$$phase||c.$digest()))});var m=0;c.$watch(function(){var a=d.url(),b=f.$$replace;if(!m||a!=f.absUrl())m++,
|
||||
c.$evalAsync(function(){c.$broadcast("$locationChangeStart",f.absUrl(),a).defaultPrevented?f.$$parse(a):(d.url(f.absUrl(),b),i(a))});f.$$replace=!1;return m});return f}]}function Oc(){var b=!0,a=this;this.debugEnabled=function(a){return B(a)?(b=a,this):b};this.$get=["$window",function(c){function d(a){a instanceof Error&&(a.stack?a=a.message&&a.stack.indexOf(a.message)===-1?"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=
|
||||
c.console||{},e=b[a]||b.log||q;return e.apply?function(){var a=[];n(arguments,function(b){a.push(d(b))});return e.apply(b,a)}:function(a,b){e(a,b)}}return{log:e("log"),warn:e("warn"),info:e("info"),error:e("error"),debug:function(){var c=e("debug");return function(){b&&c.apply(a,arguments)}}()}}]}function Pc(b,a){function c(a){return a.indexOf(r)!=-1}function d(a){a=a||1;return o+a<b.length?b.charAt(o+a):!1}function e(a){return"0"<=a&&a<="9"}function g(a){return a==" "||a=="\r"||a=="\t"||a=="\n"||
|
||||
a=="\u000b"||a=="\u00a0"}function i(a){return"a"<=a&&a<="z"||"A"<=a&&a<="Z"||"_"==a||a=="$"}function f(a){return a=="-"||a=="+"||e(a)}function h(a,c,d){d=d||o;throw Error("Lexer Error: "+a+" at column"+(B(c)?"s "+c+"-"+o+" ["+b.substring(c,d)+"]":" "+d)+" in expression ["+b+"].");}function j(){for(var a="",c=o;o<b.length;){var j=I(b.charAt(o));if(j=="."||e(j))a+=j;else{var g=d();if(j=="e"&&f(g))a+=j;else if(f(j)&&g&&e(g)&&a.charAt(a.length-1)=="e")a+=j;else if(f(j)&&(!g||!e(g))&&a.charAt(a.length-
|
||||
1)=="e")h("Invalid exponent");else break}o++}a*=1;l.push({index:c,text:a,json:!0,fn:function(){return a}})}function m(){for(var c="",d=o,f,j,h,k;o<b.length;){k=b.charAt(o);if(k=="."||i(k)||e(k))k=="."&&(f=o),c+=k;else break;o++}if(f)for(j=o;j<b.length;){k=b.charAt(j);if(k=="("){h=c.substr(f-d+1);c=c.substr(0,f-d);o=j;break}if(g(k))j++;else break}d={index:d,text:c};if(Da.hasOwnProperty(c))d.fn=d.json=Da[c];else{var m=Tb(c,a);d.fn=t(function(a,b){return m(a,b)},{assign:function(a,b){return Ub(a,c,b)}})}l.push(d);
|
||||
h&&(l.push({index:f,text:".",json:!1}),l.push({index:f+1,text:h,json:!1}))}function k(a){var c=o;o++;for(var d="",e=a,f=!1;o<b.length;){var j=b.charAt(o);e+=j;if(f)j=="u"?(j=b.substring(o+1,o+5),j.match(/[\da-f]{4}/i)||h("Invalid unicode escape [\\u"+j+"]"),o+=4,d+=String.fromCharCode(parseInt(j,16))):(f=Qc[j],d+=f?f:j),f=!1;else if(j=="\\")f=!0;else if(j==a){o++;l.push({index:c,text:e,string:d,json:!0,fn:function(){return d}});return}else d+=j;o++}h("Unterminated quote",c)}for(var l=[],u,o=0,z=[],
|
||||
r,y=":";o<b.length;){r=b.charAt(o);if(c("\"'"))k(r);else if(e(r)||c(".")&&e(d()))j();else if(i(r)){if(m(),"{,".indexOf(y)!=-1&&z[0]=="{"&&(u=l[l.length-1]))u.json=u.text.indexOf(".")==-1}else if(c("(){}[].,;:?"))l.push({index:o,text:r,json:":[,".indexOf(y)!=-1&&c("{[")||c("}]:,")}),c("{[")&&z.unshift(r),c("}]")&&z.shift(),o++;else if(g(r)){o++;continue}else{var x=r+d(),n=x+d(2),v=Da[r],A=Da[x],G=Da[n];G?(l.push({index:o,text:n,fn:G}),o+=3):A?(l.push({index:o,text:x,fn:A}),o+=2):v?(l.push({index:o,
|
||||
text:r,fn:v,json:"[,:".indexOf(y)!=-1&&c("+-")}),o+=1):h("Unexpected next character ",o,o+1)}y=r}return l}function Rc(b,a,c,d){function e(a,c){throw Error("Syntax Error: Token '"+c.text+"' "+a+" at column "+(c.index+1)+" of the expression ["+b+"] starting at ["+b.substring(c.index)+"].");}function g(){if(O.length===0)throw Error("Unexpected end of expression: "+b);return O[0]}function i(a,b,c,d){if(O.length>0){var e=O[0],f=e.text;if(f==a||f==b||f==c||f==d||!a&&!b&&!c&&!d)return e}return!1}function f(b,
|
||||
c,d,f){return(b=i(b,c,d,f))?(a&&!b.json&&e("is not valid json",b),O.shift(),b):!1}function h(a){f(a)||e("is unexpected, expecting ["+a+"]",i())}function j(a,b){return t(function(c,d){return a(c,d,b)},{constant:b.constant})}function m(a,b,c){return t(function(d,e){return a(d,e)?b(d,e):c(d,e)},{constant:a.constant&&b.constant&&c.constant})}function k(a,b,c){return t(function(d,e){return b(d,e,a,c)},{constant:a.constant&&c.constant})}function l(){for(var a=[];;)if(O.length>0&&!i("}",")",";","]")&&a.push(w()),
|
||||
!f(";"))return a.length==1?a[0]:function(b,c){for(var d,e=0;e<a.length;e++){var f=a[e];f&&(d=f(b,c))}return d}}function u(){for(var a=f(),b=c(a.text),d=[];;)if(a=f(":"))d.push(P());else{var e=function(a,c,e){for(var e=[e],f=0;f<d.length;f++)e.push(d[f](a,c));return b.apply(a,e)};return function(){return e}}}function o(){var a=z(),b,c;if(f("?"))if(b=o(),c=f(":"))return m(a,b,o());else e("expected :",c);else return a}function z(){for(var a=r(),b;;)if(b=f("||"))a=k(a,b.fn,r());else return a}function r(){var a=
|
||||
y(),b;if(b=f("&&"))a=k(a,b.fn,r());return a}function y(){var a=x(),b;if(b=f("==","!=","===","!=="))a=k(a,b.fn,y());return a}function x(){var a;a=n();for(var b;b=f("+","-");)a=k(a,b.fn,n());if(b=f("<",">","<=",">="))a=k(a,b.fn,x());return a}function n(){for(var a=v(),b;b=f("*","/","%");)a=k(a,b.fn,v());return a}function v(){var a;return f("+")?A():(a=f("-"))?k($,a.fn,v()):(a=f("!"))?j(a.fn,v()):A()}function A(){var a;if(f("("))a=w(),h(")");else if(f("["))a=G();else if(f("{"))a=D();else{var b=f();(a=
|
||||
b.fn)||e("not a primary expression",b);if(b.json)a.constant=a.literal=!0}for(var c;b=f("(","[",".");)b.text==="("?(a=s(a,c),c=null):b.text==="["?(c=a,a=ma(a)):b.text==="."?(c=a,a=ja(a)):e("IMPOSSIBLE");return a}function G(){var a=[],b=!0;if(g().text!="]"){do{var c=P();a.push(c);c.constant||(b=!1)}while(f(","))}h("]");return t(function(b,c){for(var d=[],e=0;e<a.length;e++)d.push(a[e](b,c));return d},{literal:!0,constant:b})}function D(){var a=[],b=!0;if(g().text!="}"){do{var c=f(),c=c.string||c.text;
|
||||
h(":");var d=P();a.push({key:c,value:d});d.constant||(b=!1)}while(f(","))}h("}");return t(function(b,c){for(var d={},e=0;e<a.length;e++){var f=a[e];d[f.key]=f.value(b,c)}return d},{literal:!0,constant:b})}var $=S(0),K,O=Pc(b,d),P=function(){var a=o(),c,d;return(d=f("="))?(a.assign||e("implies assignment but ["+b.substring(0,d.index)+"] can not be assigned to",d),c=o(),function(b,d){return a.assign(b,c(b,d),d)}):a},s=function(a,b){var c=[];if(g().text!=")"){do c.push(P());while(f(","))}h(")");return function(d,
|
||||
e){for(var f=[],j=b?b(d,e):d,g=0;g<c.length;g++)f.push(c[g](d,e));g=a(d,e,j)||q;return g.apply?g.apply(j,f):g(f[0],f[1],f[2],f[3],f[4])}},ja=function(a){var b=f().text,c=Tb(b,d);return t(function(b,d,e){return c(e||a(b,d),d)},{assign:function(c,d,e){return Ub(a(c,e),b,d)}})},ma=function(a){var b=P();h("]");return t(function(c,d){var e=a(c,d),f=b(c,d),j;if(!e)return p;if((e=e[f])&&e.then){j=e;if(!("$$v"in e))j.$$v=p,j.then(function(a){j.$$v=a});e=e.$$v}return e},{assign:function(c,d,e){return a(c,
|
||||
e)[b(c,e)]=d}})},w=function(){for(var a=P(),b;;)if(b=f("|"))a=k(a,b.fn,u());else return a};a?(P=z,s=ja=ma=w=function(){e("is not valid json",{text:b,index:0})},K=A()):K=l();O.length!==0&&e("is an unexpected token",O[0]);K.literal=!!K.literal;K.constant=!!K.constant;return K}function Ub(b,a,c){for(var a=a.split("."),d=0;a.length>1;d++){var e=a.shift(),g=b[e];g||(g={},b[e]=g);b=g}return b[a.shift()]=c}function ib(b,a,c){if(!a)return b;for(var a=a.split("."),d,e=b,g=a.length,i=0;i<g;i++)d=a[i],b&&(b=
|
||||
(e=b)[d]);return!c&&H(b)?$a(e,b):b}function Vb(b,a,c,d,e){return function(g,i){var f=i&&i.hasOwnProperty(b)?i:g,h;if(f===null||f===p)return f;if((f=f[b])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}if(!a||f===null||f===p)return f;if((f=f[a])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}if(!c||f===null||f===p)return f;if((f=f[c])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}if(!d||f===null||f===p)return f;if((f=
|
||||
f[d])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}if(!e||f===null||f===p)return f;if((f=f[e])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}return f}}function Tb(b,a){if(mb.hasOwnProperty(b))return mb[b];var c=b.split("."),d=c.length,e;if(a)e=d<6?Vb(c[0],c[1],c[2],c[3],c[4]):function(a,b){var e=0,j;do j=Vb(c[e++],c[e++],c[e++],c[e++],c[e++])(a,b),b=p,a=j;while(e<d);return j};else{var g="var l, fn, p;\n";n(c,function(a,b){g+="if(s === null || s === undefined) return s;\nl=s;\ns="+
|
||||
(b?"s":'((k&&k.hasOwnProperty("'+a+'"))?k:s)')+'["'+a+'"];\nif (s && s.then) {\n if (!("$$v" in s)) {\n p=s;\n p.$$v = undefined;\n p.then(function(v) {p.$$v=v;});\n}\n s=s.$$v\n}\n'});g+="return s;";e=Function("s","k",g);e.toString=function(){return g}}return mb[b]=e}function Sc(){var b={};this.$get=["$filter","$sniffer",function(a,c){return function(d){switch(typeof d){case "string":return b.hasOwnProperty(d)?b[d]:b[d]=Rc(d,!1,a,c.csp);case "function":return d;default:return q}}}]}function Tc(){this.$get=
|
||||
["$rootScope","$exceptionHandler",function(b,a){return Uc(function(a){b.$evalAsync(a)},a)}]}function Uc(b,a){function c(a){return a}function d(a){return i(a)}var e=function(){var f=[],h,j;return j={resolve:function(a){if(f){var c=f;f=p;h=g(a);c.length&&b(function(){for(var a,b=0,d=c.length;b<d;b++)a=c[b],h.then(a[0],a[1])})}},reject:function(a){j.resolve(i(a))},promise:{then:function(b,j){var g=e(),i=function(d){try{g.resolve((b||c)(d))}catch(e){a(e),g.reject(e)}},o=function(b){try{g.resolve((j||
|
||||
d)(b))}catch(c){a(c),g.reject(c)}};f?f.push([i,o]):h.then(i,o);return g.promise},always:function(a){function b(a,c){var d=e();c?d.resolve(a):d.reject(a);return d.promise}function d(e,f){var j=null;try{j=(a||c)()}catch(g){return b(g,!1)}return j&&j.then?j.then(function(){return b(e,f)},function(a){return b(a,!1)}):b(e,f)}return this.then(function(a){return d(a,!0)},function(a){return d(a,!1)})}}}},g=function(a){return a&&a.then?a:{then:function(c){var d=e();b(function(){d.resolve(c(a))});return d.promise}}},
|
||||
i=function(a){return{then:function(c,j){var g=e();b(function(){g.resolve((j||d)(a))});return g.promise}}};return{defer:e,reject:i,when:function(f,h,j){var m=e(),k,l=function(b){try{return(h||c)(b)}catch(d){return a(d),i(d)}},u=function(b){try{return(j||d)(b)}catch(c){return a(c),i(c)}};b(function(){g(f).then(function(a){k||(k=!0,m.resolve(g(a).then(l,u)))},function(a){k||(k=!0,m.resolve(u(a)))})});return m.promise},all:function(a){var b=e(),c=0,d=F(a)?[]:{};n(a,function(a,e){c++;g(a).then(function(a){d.hasOwnProperty(e)||
|
||||
(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});c===0&&b.resolve(d);return b.promise}}}function Vc(){var b={};this.when=function(a,c){b[a]=t({reloadOnSearch:!0,caseInsensitiveMatch:!1},c);if(a){var d=a[a.length-1]=="/"?a.substr(0,a.length-1):a+"/";b[d]={redirectTo:a}}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache",function(a,c,d,e,g,i,f){function h(a,b,c){for(var b=
|
||||
"^"+b.replace(/[-\/\\^$:*+?.()|[\]{}]/g,"\\$&")+"$",d="",e=[],f={},j=/\\([:*])(\w+)/g,g,i=0;(g=j.exec(b))!==null;){d+=b.slice(i,g.index);switch(g[1]){case ":":d+="([^\\/]*)";break;case "*":d+="(.*)"}e.push(g[2]);i=j.lastIndex}d+=b.substr(i);var h=a.match(RegExp(d,c.caseInsensitiveMatch?"i":""));h&&n(e,function(a,b){f[a]=h[b+1]});return h?f:null}function j(){var b=m(),j=u.current;if(b&&j&&b.$$route===j.$$route&&ia(b.pathParams,j.pathParams)&&!b.reloadOnSearch&&!l)j.params=b.params,V(j.params,d),a.$broadcast("$routeUpdate",
|
||||
j);else if(b||j)l=!1,a.$broadcast("$routeChangeStart",b,j),(u.current=b)&&b.redirectTo&&(E(b.redirectTo)?c.path(k(b.redirectTo,b.params)).search(b.params).replace():c.url(b.redirectTo(b.pathParams,c.path(),c.search())).replace()),e.when(b).then(function(){if(b){var a=t({},b.resolve),c;n(a,function(b,c){a[c]=E(b)?g.get(b):g.invoke(b)});if(B(c=b.template))H(c)&&(c=c(b.params));else if(B(c=b.templateUrl))if(H(c)&&(c=c(b.params)),B(c))b.loadedTemplateUrl=c,c=i.get(c,{cache:f}).then(function(a){return a.data});
|
||||
B(c)&&(a.$template=c);return e.all(a)}}).then(function(c){if(b==u.current){if(b)b.locals=c,V(b.params,d);a.$broadcast("$routeChangeSuccess",b,j)}},function(c){b==u.current&&a.$broadcast("$routeChangeError",b,j,c)})}function m(){var a,d;n(b,function(b,e){if(!d&&(a=h(c.path(),e,b)))d=tb(b,{params:t({},c.search(),a),pathParams:a}),d.$$route=b});return d||b[null]&&tb(b[null],{params:{},pathParams:{}})}function k(a,b){var c=[];n((a||"").split(":"),function(a,d){if(d==0)c.push(a);else{var e=a.match(/(\w+)(.*)/),
|
||||
f=e[1];c.push(b[f]);c.push(e[2]||"");delete b[f]}});return c.join("")}var l=!1,u={routes:b,reload:function(){l=!0;a.$evalAsync(j)}};a.$on("$locationChangeSuccess",j);return u}]}function Wc(){this.$get=S({})}function Xc(){var b=10;this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=["$injector","$exceptionHandler","$parse",function(a,c,d){function e(){this.$id=Fa();this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;
|
||||
this["this"]=this.$root=this;this.$$destroyed=!1;this.$$asyncQueue=[];this.$$listeners={};this.$$isolateBindings={}}function g(a){if(h.$$phase)throw Error(h.$$phase+" already in progress");h.$$phase=a}function i(a,b){var c=d(a);xa(c,b);return c}function f(){}e.prototype={$new:function(a){if(H(a))throw Error("API-CHANGE: Use $controller to instantiate controllers.");a?(a=new e,a.$root=this.$root):(a=function(){},a.prototype=this,a=new a,a.$id=Fa());a["this"]=a;a.$$listeners={};a.$parent=this;a.$$watchers=
|
||||
a.$$nextSibling=a.$$childHead=a.$$childTail=null;a.$$prevSibling=this.$$childTail;this.$$childHead?this.$$childTail=this.$$childTail.$$nextSibling=a:this.$$childHead=this.$$childTail=a;return a},$watch:function(a,b,c){var d=i(a,"watch"),e=this.$$watchers,g={fn:b,last:f,get:d,exp:a,eq:!!c};if(!H(b)){var h=i(b||q,"listener");g.fn=function(a,b,c){h(c)}}if(typeof a=="string"&&d.constant){var r=g.fn;g.fn=function(a,b,c){r.call(this,a,b,c);ta(e,g)}}if(!e)e=this.$$watchers=[];e.unshift(g);return function(){ta(e,
|
||||
g)}},$watchCollection:function(a,b){var c=this,e,f,g=0,i=d(a),h=[],n={},x=0;return this.$watch(function(){f=i(c);var a,b;if(L(f))if(Xa(f)){if(e!==h)e=h,x=e.length=0,g++;a=f.length;if(x!==a)g++,e.length=x=a;for(b=0;b<a;b++)e[b]!==f[b]&&(g++,e[b]=f[b])}else{e!==n&&(e=n={},x=0,g++);a=0;for(b in f)f.hasOwnProperty(b)&&(a++,e.hasOwnProperty(b)?e[b]!==f[b]&&(g++,e[b]=f[b]):(x++,e[b]=f[b],g++));if(x>a)for(b in g++,e)e.hasOwnProperty(b)&&!f.hasOwnProperty(b)&&(x--,delete e[b])}else e!==f&&(e=f,g++);return g},
|
||||
function(){b(f,e,c)})},$digest:function(){var a,d,e,i,u=this.$$asyncQueue,o,z,r=b,n,x=[],p,v;g("$digest");do{z=!1;for(n=this;u.length;)try{n.$eval(u.shift())}catch(A){c(A)}do{if(i=n.$$watchers)for(o=i.length;o--;)try{if(a=i[o],(d=a.get(n))!==(e=a.last)&&!(a.eq?ia(d,e):typeof d=="number"&&typeof e=="number"&&isNaN(d)&&isNaN(e)))z=!0,a.last=a.eq?V(d):d,a.fn(d,e===f?d:e,n),r<5&&(p=4-r,x[p]||(x[p]=[]),v=H(a.exp)?"fn: "+(a.exp.name||a.exp.toString()):a.exp,v+="; newVal: "+ha(d)+"; oldVal: "+ha(e),x[p].push(v))}catch(G){c(G)}if(!(i=
|
||||
n.$$childHead||n!==this&&n.$$nextSibling))for(;n!==this&&!(i=n.$$nextSibling);)n=n.$parent}while(n=i);if(z&&!r--)throw h.$$phase=null,Error(b+" $digest() iterations reached. Aborting!\nWatchers fired in the last 5 iterations: "+ha(x));}while(z||u.length);h.$$phase=null},$destroy:function(){if(!(h==this||this.$$destroyed)){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;if(a.$$childHead==this)a.$$childHead=this.$$nextSibling;if(a.$$childTail==this)a.$$childTail=this.$$prevSibling;
|
||||
if(this.$$prevSibling)this.$$prevSibling.$$nextSibling=this.$$nextSibling;if(this.$$nextSibling)this.$$nextSibling.$$prevSibling=this.$$prevSibling;this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null}},$eval:function(a,b){return d(a)(this,b)},$evalAsync:function(a){this.$$asyncQueue.push(a)},$apply:function(a){try{return g("$apply"),this.$eval(a)}catch(b){c(b)}finally{h.$$phase=null;try{h.$digest()}catch(d){throw c(d),d;}}},$on:function(a,b){var c=this.$$listeners[a];
|
||||
c||(this.$$listeners[a]=c=[]);c.push(b);return function(){c[Ga(c,b)]=null}},$emit:function(a,b){var d=[],e,f=this,g=!1,i={name:a,targetScope:f,stopPropagation:function(){g=!0},preventDefault:function(){i.defaultPrevented=!0},defaultPrevented:!1},h=[i].concat(ka.call(arguments,1)),n,x;do{e=f.$$listeners[a]||d;i.currentScope=f;n=0;for(x=e.length;n<x;n++)if(e[n])try{if(e[n].apply(null,h),g)return i}catch(p){c(p)}else e.splice(n,1),n--,x--;f=f.$parent}while(f);return i},$broadcast:function(a,b){var d=
|
||||
this,e=this,f={name:a,targetScope:this,preventDefault:function(){f.defaultPrevented=!0},defaultPrevented:!1},g=[f].concat(ka.call(arguments,1)),i,h;do{d=e;f.currentScope=d;e=d.$$listeners[a]||[];i=0;for(h=e.length;i<h;i++)if(e[i])try{e[i].apply(null,g)}catch(n){c(n)}else e.splice(i,1),i--,h--;if(!(e=d.$$childHead||d!==this&&d.$$nextSibling))for(;d!==this&&!(e=d.$$nextSibling);)d=d.$parent}while(d=e);return f}};var h=new e;return h}]}function Yc(){this.$get=["$window","$document",function(b,a){var c=
|
||||
{},d=N((/android (\d+)/.exec(I((b.navigator||{}).userAgent))||[])[1]),e=a[0]||{},g,i=/^(Moz|webkit|O|ms)(?=[A-Z])/,f=e.body&&e.body.style,h=!1,j=!1;if(f){for(var m in f)if(h=i.exec(m)){g=h[0];g=g.substr(0,1).toUpperCase()+g.substr(1);break}h=!!("transition"in f||g+"Transition"in f);j=!!("animation"in f||g+"Animation"in f)}return{history:!(!b.history||!b.history.pushState||d<4),hashchange:"onhashchange"in b&&(!e.documentMode||e.documentMode>7),hasEvent:function(a){if(a=="input"&&Z==9)return!1;if(C(c[a])){var b=
|
||||
e.createElement("div");c[a]="on"+a in b}return c[a]},csp:e.securityPolicy?e.securityPolicy.isActive:!1,vendorPrefix:g,transitions:h,animations:j}}]}function Zc(){this.$get=S(M)}function Wb(b){var a={},c,d,e;if(!b)return a;n(b.split("\n"),function(b){e=b.indexOf(":");c=I(U(b.substr(0,e)));d=U(b.substr(e+1));c&&(a[c]?a[c]+=", "+d:a[c]=d)});return a}function $c(b,a){var c=ad.exec(b);if(c==null)return!0;var d={protocol:c[2],host:c[4],port:N(c[6])||Oa[c[2]]||null,relativeProtocol:c[2]===p||c[2]===""},
|
||||
c=jb.exec(a),c={protocol:c[1],host:c[3],port:N(c[5])||Oa[c[1]]||null};return(d.protocol==c.protocol||d.relativeProtocol)&&d.host==c.host&&(d.port==c.port||d.relativeProtocol&&c.port==Oa[c.protocol])}function Xb(b){var a=L(b)?b:p;return function(c){a||(a=Wb(b));return c?a[I(c)]||null:a}}function Yb(b,a,c){if(H(c))return c(b,a);n(c,function(c){b=c(b,a)});return b}function bd(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d={"Content-Type":"application/json;charset=utf-8"},e=this.defaults=
|
||||
{transformResponse:[function(d){E(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=ub(d,!0)));return d}],transformRequest:[function(a){return L(a)&&Ea.apply(a)!=="[object File]"?ha(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:d,put:d,patch:d},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN"},g=this.interceptors=[],i=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,d,k,l){function u(a){function c(a){var b=
|
||||
t({},a,{data:Yb(a.data,a.headers,d.transformResponse)});return 200<=a.status&&a.status<300?b:k.reject(b)}var d={transformRequest:e.transformRequest,transformResponse:e.transformResponse},f={};t(d,a);d.headers=f;d.method=oa(d.method);t(f,e.headers.common,e.headers[I(d.method)],a.headers);(a=$c(d.url,b.url())?b.cookies()[d.xsrfCookieName||e.xsrfCookieName]:p)&&(f[d.xsrfHeaderName||e.xsrfHeaderName]=a);var g=[function(a){var b=Yb(a.data,Xb(f),a.transformRequest);C(a.data)&&delete f["Content-Type"];if(C(a.withCredentials)&&
|
||||
!C(e.withCredentials))a.withCredentials=e.withCredentials;return o(a,b,f).then(c,c)},p],j=k.when(d);for(n(y,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;)var a=g.shift(),i=g.shift(),j=j.then(a,i);j.success=function(a){j.then(function(b){a(b.data,b.status,b.headers,d)});return j};j.error=function(a){j.then(null,function(b){a(b.data,b.status,b.headers,d)});return j};return j}function o(b,c,g){function j(a,
|
||||
b,c){n&&(200<=a&&a<300?n.put(s,[a,b,Wb(c)]):n.remove(s));i(b,a,c);d.$$phase||d.$apply()}function i(a,c,d){c=Math.max(c,0);(200<=c&&c<300?l.resolve:l.reject)({data:a,status:c,headers:Xb(d),config:b})}function h(){var a=Ga(u.pendingRequests,b);a!==-1&&u.pendingRequests.splice(a,1)}var l=k.defer(),o=l.promise,n,p,s=z(b.url,b.params);u.pendingRequests.push(b);o.then(h,h);if((b.cache||e.cache)&&b.cache!==!1&&b.method=="GET")n=L(b.cache)?b.cache:L(e.cache)?e.cache:r;if(n)if(p=n.get(s))if(p.then)return p.then(h,
|
||||
h),p;else F(p)?i(p[1],p[0],V(p[2])):i(p,200,{});else n.put(s,o);p||a(b.method,s,c,j,g,b.timeout,b.withCredentials,b.responseType);return o}function z(a,b){if(!b)return a;var c=[];nc(b,function(a,b){a==null||a==p||(F(a)||(a=[a]),n(a,function(a){L(a)&&(a=ha(a));c.push(wa(b)+"="+wa(a))}))});return a+(a.indexOf("?")==-1?"?":"&")+c.join("&")}var r=c("$http"),y=[];n(g,function(a){y.unshift(E(a)?l.get(a):l.invoke(a))});n(i,function(a,b){var c=E(a)?l.get(a):l.invoke(a);y.splice(b,0,{response:function(a){return c(k.when(a))},
|
||||
responseError:function(a){return c(k.reject(a))}})});u.pendingRequests=[];(function(a){n(arguments,function(a){u[a]=function(b,c){return u(t(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){n(arguments,function(a){u[a]=function(b,c,d){return u(t(d||{},{method:a,url:b,data:c}))}})})("post","put");u.defaults=e;return u}]}function cd(){this.$get=["$browser","$window","$document",function(b,a,c){return dd(b,ed,b.defer,a.angular.callbacks,c[0],a.location.protocol.replace(":",""))}]}
|
||||
function dd(b,a,c,d,e,g){function i(a,b){var c=e.createElement("script"),d=function(){e.body.removeChild(c);b&&b()};c.type="text/javascript";c.src=a;Z?c.onreadystatechange=function(){/loaded|complete/.test(c.readyState)&&d()}:c.onload=c.onerror=d;e.body.appendChild(c);return d}return function(e,h,j,m,k,l,u,o){function z(){p=-1;t&&t();v&&v.abort()}function r(a,d,e,f){var j=(h.match(jb)||["",g])[1];A&&c.cancel(A);t=v=null;d=j=="file"?e?200:404:d;a(d==1223?204:d,e,f);b.$$completeOutstandingRequest(q)}
|
||||
var p;b.$$incOutstandingRequestCount();h=h||b.url();if(I(e)=="jsonp"){var x="_"+(d.counter++).toString(36);d[x]=function(a){d[x].data=a};var t=i(h.replace("JSON_CALLBACK","angular.callbacks."+x),function(){d[x].data?r(m,200,d[x].data):r(m,p||-2);delete d[x]})}else{var v=new a;v.open(e,h,!0);n(k,function(a,b){a&&v.setRequestHeader(b,a)});v.onreadystatechange=function(){if(v.readyState==4){var a=v.getAllResponseHeaders(),b=["Cache-Control","Content-Language","Content-Type","Expires","Last-Modified",
|
||||
"Pragma"];a||(a="",n(b,function(b){var c=v.getResponseHeader(b);c&&(a+=b+": "+c+"\n")}));r(m,p||v.status,v.responseType?v.response:v.responseText,a)}};if(u)v.withCredentials=!0;if(o)v.responseType=o;v.send(j||"")}if(l>0)var A=c(z,l);else l&&l.then&&l.then(z)}}function fd(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",
|
||||
posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),SHORTMONTH:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),DAY:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),SHORTDAY:"Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(","),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",
|
||||
mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return b===1?"one":"other"}}}}function gd(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,f,h){var j=c.defer(),m=j.promise,k=B(h)&&!h,f=a.defer(function(){try{j.resolve(e())}catch(a){j.reject(a),d(a)}k||b.$apply()},f),h=function(){delete g[m.$$timeoutId]};m.$$timeoutId=f;g[f]=j;m.then(h,h);return m}var g={};e.cancel=function(b){return b&&b.$$timeoutId in
|
||||
g?(g[b.$$timeoutId].reject("canceled"),a.defer.cancel(b.$$timeoutId)):!1};return e}]}function Zb(b){function a(a,e){return b.factory(a+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency",$b);a("date",ac);a("filter",hd);a("json",id);a("limitTo",jd);a("lowercase",kd);a("number",bc);a("orderBy",cc);a("uppercase",ld)}function hd(){return function(b,a,c){if(!F(b))return b;var d=[];d.check=function(a){for(var b=0;b<d.length;b++)if(!d[b](a))return!1;
|
||||
return!0};switch(typeof c){case "function":break;case "boolean":if(c==!0){c=function(a,b){return Ha.equals(a,b)};break}default:c=function(a,b){b=(""+b).toLowerCase();return(""+a).toLowerCase().indexOf(b)>-1}}var e=function(a,b){if(typeof b=="string"&&b.charAt(0)==="!")return!e(a,b.substr(1));switch(typeof a){case "boolean":case "number":case "string":return c(a,b);case "object":switch(typeof b){case "object":return c(a,b);default:for(var d in a)if(d.charAt(0)!=="$"&&e(a[d],b))return!0}return!1;case "array":for(d=
|
||||
0;d<a.length;d++)if(e(a[d],b))return!0;return!1;default:return!1}};switch(typeof a){case "boolean":case "number":case "string":a={$:a};case "object":for(var g in a)g=="$"?function(){if(a[g]){var b=g;d.push(function(c){return e(c,a[b])})}}():function(){if(a[g]){var b=g;d.push(function(c){return e(ib(c,b),a[b])})}}();break;case "function":d.push(a);break;default:return b}for(var i=[],f=0;f<b.length;f++){var h=b[f];d.check(h)&&i.push(h)}return i}}function $b(b){var a=b.NUMBER_FORMATS;return function(b,
|
||||
d){if(C(d))d=a.CURRENCY_SYM;return dc(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,2).replace(/\u00A4/g,d)}}function bc(b){var a=b.NUMBER_FORMATS;return function(b,d){return dc(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function dc(b,a,c,d,e){if(isNaN(b)||!isFinite(b))return"";var g=b<0,b=Math.abs(b),i=b+"",f="",h=[],j=!1;if(i.indexOf("e")!==-1){var m=i.match(/([\d\.]+)e(-?)(\d+)/);m&&m[2]=="-"&&m[3]>e+1?i="0":(f=i,j=!0)}if(!j){i=(i.split(ec)[1]||"").length;C(e)&&(e=Math.min(Math.max(a.minFrac,i),
|
||||
a.maxFrac));var i=Math.pow(10,e),b=Math.round(b*i)/i,b=(""+b).split(ec),i=b[0],b=b[1]||"",j=0,m=a.lgSize,k=a.gSize;if(i.length>=m+k)for(var j=i.length-m,l=0;l<j;l++)(j-l)%k===0&&l!==0&&(f+=c),f+=i.charAt(l);for(l=j;l<i.length;l++)(i.length-l)%m===0&&l!==0&&(f+=c),f+=i.charAt(l);for(;b.length<e;)b+="0";e&&e!=="0"&&(f+=d+b.substr(0,e))}h.push(g?a.negPre:a.posPre);h.push(f);h.push(g?a.negSuf:a.posSuf);return h.join("")}function nb(b,a,c){var d="";b<0&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=
|
||||
b.substr(b.length-a));return d+b}function Q(b,a,c,d){c=c||0;return function(e){e=e["get"+b]();if(c>0||e>-c)e+=c;e===0&&c==-12&&(e=12);return nb(e,a,d)}}function Qa(b,a){return function(c,d){var e=c["get"+b](),g=oa(a?"SHORT"+b:b);return d[g][e]}}function ac(b){function a(a){var b;if(b=a.match(c)){var a=new Date(0),g=0,i=0,f=b[8]?a.setUTCFullYear:a.setFullYear,h=b[8]?a.setUTCHours:a.setHours;b[9]&&(g=N(b[9]+b[10]),i=N(b[9]+b[11]));f.call(a,N(b[1]),N(b[2])-1,N(b[3]));g=N(b[4]||0)-g;i=N(b[5]||0)-i;f=
|
||||
N(b[6]||0);b=Math.round(parseFloat("0."+(b[7]||0))*1E3);h.call(a,g,i,f,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e){var g="",i=[],f,h,e=e||"mediumDate",e=b.DATETIME_FORMATS[e]||e;E(c)&&(c=md.test(c)?N(c):a(c));Ya(c)&&(c=new Date(c));if(!ra(c))return c;for(;e;)(h=nd.exec(e))?(i=i.concat(ka.call(h,1)),e=i.pop()):(i.push(e),e=null);n(i,function(a){f=od[a];g+=f?f(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,
|
||||
"").replace(/''/g,"'")});return g}}function id(){return function(b){return ha(b,!0)}}function jd(){return function(b,a){if(!F(b)&&!E(b))return b;a=N(a);if(E(b))return a?a>=0?b.slice(0,a):b.slice(a,b.length):"";var c=[],d,e;a>b.length?a=b.length:a<-b.length&&(a=-b.length);a>0?(d=0,e=a):(d=b.length+a,e=b.length);for(;d<e;d++)c.push(b[d]);return c}}function cc(b){return function(a,c,d){function e(a,b){return ua(b)?function(b,c){return a(c,b)}:a}if(!F(a))return a;if(!c)return a;for(var c=F(c)?c:[c],c=
|
||||
Za(c,function(a){var c=!1,d=a||qa;if(E(a)){if(a.charAt(0)=="+"||a.charAt(0)=="-")c=a.charAt(0)=="-",a=a.substring(1);d=b(a)}return e(function(a,b){var c;c=d(a);var e=d(b),f=typeof c,g=typeof e;f==g?(f=="string"&&(c=c.toLowerCase()),f=="string"&&(e=e.toLowerCase()),c=c===e?0:c<e?-1:1):c=f<g?-1:1;return c},c)}),g=[],i=0;i<a.length;i++)g.push(a[i]);return g.sort(e(function(a,b){for(var d=0;d<c.length;d++){var e=c[d](a,b);if(e!==0)return e}return 0},d))}}function aa(b){H(b)&&(b={link:b});b.restrict=b.restrict||
|
||||
"AC";return S(b)}function fc(b,a){function c(a,c){c=c?"-"+bb(c,"-"):"";b.removeClass((a?Ra:Sa)+c).addClass((a?Sa:Ra)+c)}var d=this,e=b.parent().controller("form")||Ta,g=0,i=d.$error={},f=[];d.$name=a.name;d.$dirty=!1;d.$pristine=!0;d.$valid=!0;d.$invalid=!1;e.$addControl(d);b.addClass(pa);c(!0);d.$addControl=function(a){f.push(a);a.$name&&!d.hasOwnProperty(a.$name)&&(d[a.$name]=a)};d.$removeControl=function(a){a.$name&&d[a.$name]===a&&delete d[a.$name];n(i,function(b,c){d.$setValidity(c,!0,a)});ta(f,
|
||||
a)};d.$setValidity=function(a,b,f){var k=i[a];if(b){if(k&&(ta(k,f),!k.length)){g--;if(!g)c(b),d.$valid=!0,d.$invalid=!1;i[a]=!1;c(!0,a);e.$setValidity(a,!0,d)}}else{g||c(b);if(k){if(Ga(k,f)!=-1)return}else i[a]=k=[],g++,c(!1,a),e.$setValidity(a,!1,d);k.push(f);d.$valid=!1;d.$invalid=!0}};d.$setDirty=function(){b.removeClass(pa).addClass(Ua);d.$dirty=!0;d.$pristine=!1;e.$setDirty()};d.$setPristine=function(){b.removeClass(Ua).addClass(pa);d.$dirty=!1;d.$pristine=!0;n(f,function(a){a.$setPristine()})}}
|
||||
function X(b){return C(b)||b===""||b===null||b!==b}function Va(b,a,c,d,e,g){var i=function(){var e=a.val();if(ua(c.ngTrim||"T"))e=U(e);d.$viewValue!==e&&b.$apply(function(){d.$setViewValue(e)})};if(e.hasEvent("input"))a.bind("input",i);else{var f,h=function(){f||(f=g.defer(function(){i();f=null}))};a.bind("keydown",function(a){a=a.keyCode;a===91||15<a&&a<19||37<=a&&a<=40||h()});a.bind("change",i);e.hasEvent("paste")&&a.bind("paste cut",h)}d.$render=function(){a.val(X(d.$viewValue)?"":d.$viewValue)};
|
||||
var j=c.ngPattern,m=function(a,b){return X(b)||a.test(b)?(d.$setValidity("pattern",!0),b):(d.$setValidity("pattern",!1),p)};j&&((e=j.match(/^\/(.*)\/([gim]*)$/))?(j=RegExp(e[1],e[2]),e=function(a){return m(j,a)}):e=function(a){var c=b.$eval(j);if(!c||!c.test)throw Error("Expected "+j+" to be a RegExp but was "+c);return m(c,a)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){var k=N(c.ngMinlength),e=function(a){return!X(a)&&a.length<k?(d.$setValidity("minlength",!1),p):(d.$setValidity("minlength",
|
||||
!0),a)};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){var l=N(c.ngMaxlength),e=function(a){return!X(a)&&a.length>l?(d.$setValidity("maxlength",!1),p):(d.$setValidity("maxlength",!0),a)};d.$parsers.push(e);d.$formatters.push(e)}}function ob(b,a){b="ngClass"+b;return aa(function(c,d,e){function g(b){if(a===!0||c.$index%2===a)h&&!ia(b,h)&&i(h),f(b);h=V(b)}function i(a){L(a)&&!F(a)&&(a=Za(a,function(a,b){if(a)return b}));d.removeClass(F(a)?a.join(" "):a)}function f(a){L(a)&&!F(a)&&(a=Za(a,
|
||||
function(a,b){if(a)return b}));a&&d.addClass(F(a)?a.join(" "):a)}var h=p;c.$watch(e[b],g,!0);e.$observe("class",function(){var a=c.$eval(e[b]);g(a,a)});b!=="ngClass"&&c.$watch("$index",function(d,g){var h=d&1;h!==g&1&&(h===a?f(c.$eval(e[b])):i(c.$eval(e[b])))})})}var I=function(b){return E(b)?b.toLowerCase():b},oa=function(b){return E(b)?b.toUpperCase():b},Z=N((/msie (\d+)/.exec(I(navigator.userAgent))||[])[1]),w,ga,ka=[].slice,Wa=[].push,Ea=Object.prototype.toString,mc=M.angular,Ha=M.angular||(M.angular=
|
||||
{}),Aa,hb,ba=["0","0","0"];q.$inject=[];qa.$inject=[];hb=Z<9?function(b){b=b.nodeName?b:b[0];return b.scopeName&&b.scopeName!="HTML"?oa(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var sc=/[A-Z]/g,pd={full:"1.1.5",major:1,minor:1,dot:5,codeName:"triangle-squarification"},Ka=R.cache={},Ja=R.expando="ng-"+(new Date).getTime(),wc=1,gc=M.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},gb=
|
||||
M.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)},uc=/([\:\-\_]+(.))/g,vc=/^moz([A-Z])/,Ba=R.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;T.readyState==="complete"?setTimeout(a):(this.bind("DOMContentLoaded",a),R(M).bind("load",a))},toString:function(){var b=[];n(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return b>=0?w(this[b]):w(this[this.length+b])},length:0,push:Wa,sort:[].sort,
|
||||
splice:[].splice},Na={};n("multiple,selected,checked,disabled,readOnly,required,open".split(","),function(b){Na[I(b)]=b});var Gb={};n("input,select,option,textarea,button,form,details".split(","),function(b){Gb[oa(b)]=!0});n({data:Bb,inheritedData:Ma,scope:function(b){return Ma(b,"$scope")},controller:Eb,injector:function(b){return Ma(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:La,css:function(b,a,c){a=Ia(a);if(B(c))b.style[a]=c;else{var d;Z<=8&&(d=b.currentStyle&&b.currentStyle[a],
|
||||
d===""&&(d="auto"));d=d||b.style[a];Z<=8&&(d=d===""?p:d);return d}},attr:function(b,a,c){var d=I(a);if(Na[d])if(B(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||q).specified?d:p;else if(B(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),b===null?p:b},prop:function(b,a,c){if(B(c))b[a]=c;else return b[a]},text:t(Z<9?function(b,a){if(b.nodeType==1){if(C(a))return b.innerText;b.innerText=a}else{if(C(a))return b.nodeValue;
|
||||
b.nodeValue=a}}:function(b,a){if(C(a))return b.textContent;b.textContent=a},{$dv:""}),val:function(b,a){if(C(a))return b.value;b.value=a},html:function(b,a){if(C(a))return b.innerHTML;for(var c=0,d=b.childNodes;c<d.length;c++)ya(d[c]);b.innerHTML=a}},function(b,a){R.prototype[a]=function(a,d){var e,g;if((b.length==2&&b!==La&&b!==Eb?a:d)===p)if(L(a)){for(e=0;e<this.length;e++)if(b===Bb)b(this[e],a);else for(g in a)b(this[e],g,a[g]);return this}else{if(this.length)return b(this[0],a,d)}else{for(e=0;e<
|
||||
this.length;e++)b(this[e],a,d);return this}return b.$dv}});n({removeData:zb,dealoc:ya,bind:function a(c,d,e){var g=ca(c,"events"),i=ca(c,"handle");g||ca(c,"events",g={});i||ca(c,"handle",i=xc(c,g));n(d.split(" "),function(d){var h=g[d];if(!h){if(d=="mouseenter"||d=="mouseleave"){var j=T.body.contains||T.body.compareDocumentPosition?function(a,c){var d=a.nodeType===9?a.documentElement:a,e=c&&c.parentNode;return a===e||!(!e||!(e.nodeType===1&&(d.contains?d.contains(e):a.compareDocumentPosition&&a.compareDocumentPosition(e)&
|
||||
16)))}:function(a,c){if(c)for(;c=c.parentNode;)if(c===a)return!0;return!1};g[d]=[];a(c,{mouseleave:"mouseout",mouseenter:"mouseover"}[d],function(a){var c=a.relatedTarget;(!c||c!==this&&!j(this,c))&&i(a,d)})}else gc(c,d,i),g[d]=[];h=g[d]}h.push(e)})},unbind:Ab,replaceWith:function(a,c){var d,e=a.parentNode;ya(a);n(new R(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c,a);d=c})},children:function(a){var c=[];n(a.childNodes,function(a){a.nodeType===1&&c.push(a)});return c},contents:function(a){return a.childNodes||
|
||||
[]},append:function(a,c){n(new R(c),function(c){(a.nodeType===1||a.nodeType===11)&&a.appendChild(c)})},prepend:function(a,c){if(a.nodeType===1){var d=a.firstChild;n(new R(c),function(c){d?a.insertBefore(c,d):(a.appendChild(c),d=c)})}},wrap:function(a,c){var c=w(c)[0],d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:function(a){ya(a);var c=a.parentNode;c&&c.removeChild(a)},after:function(a,c){var d=a,e=a.parentNode;n(new R(c),function(a){e.insertBefore(a,d.nextSibling);d=a})},addClass:Db,
|
||||
removeClass:Cb,toggleClass:function(a,c,d){C(d)&&(d=!La(a,c));(d?Db:Cb)(a,c)},parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},next:function(a){if(a.nextElementSibling)return a.nextElementSibling;for(a=a.nextSibling;a!=null&&a.nodeType!==1;)a=a.nextSibling;return a},find:function(a,c){return a.getElementsByTagName(c)},clone:fb,triggerHandler:function(a,c){var d=(ca(a,"events")||{})[c];n(d,function(c){c.call(a,{preventDefault:q})})}},function(a,c){R.prototype[c]=function(c,e){for(var g,
|
||||
i=0;i<this.length;i++)g==p?(g=a(this[i],c,e),g!==p&&(g=w(g))):eb(g,a(this[i],c,e));return g==p?this:g}});za.prototype={put:function(a,c){this[la(a)]=c},get:function(a){return this[la(a)]},remove:function(a){var c=this[a=la(a)];delete this[a];return c}};var zc=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,Ac=/,/,Bc=/^\s*(_?)(\S+?)\1\s*$/,yc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;Ib.$inject=["$provide"];var qd=function(){var a="$ngAnimateController",c={running:!0};this.$get=["$animation","$window","$sniffer","$rootElement",
|
||||
"$rootScope",function(d,e,g,i){i.data(a,c);i=function(c,i){function j(j,k,o){return function(m,r,p){function x(a){var c=0,a=E(a)?a.split(/\s*,\s*/):[];n(a,function(a){c=Math.max(parseFloat(a)||0,c)});return c}function t(){m.addClass(K);if($)$(m,v,P);else if(H(e.getComputedStyle)){var a=g.vendorPrefix+"Animation",c=g.vendorPrefix+"Transition",d=0;n(m,function(f){if(f.nodeType==1){var g="transition",i=c,j=1,h=e.getComputedStyle(f)||{};if(parseFloat(h.animationDuration)>0||parseFloat(h[a+"Duration"])>
|
||||
0)g="animation",i=a,j=Math.max(parseInt(h[g+"IterationCount"])||0,parseInt(h[i+"IterationCount"])||0,j);f=Math.max(x(h[g+"Delay"]),x(h[i+"Delay"]));g=Math.max(x(h[g+"Duration"]),x(h[i+"Duration"]));d=Math.max(f+j*g,d)}});e.setTimeout(v,d*1E3)}else v()}function v(){if(!v.run)v.run=!0,o(m,r,p),m.removeClass(w),m.removeClass(K),m.removeData(a)}var A=c.$eval(i.ngAnimate),w=A?L(A)?A[j]:A+"-"+j:"",D=d(w),A=D&&D.setup,$=D&&D.start,D=D&&D.cancel;if(w){var K=w+"-active";r||(r=p?p.parent():m.parent());if(!g.transitions&&
|
||||
!A&&!$||(r.inheritedData(a)||q).running)k(m,r,p),o(m,r,p);else{var O=m.data(a)||{};O.running&&((D||q)(m),O.done());m.data(a,{running:!0,done:v});m.addClass(w);k(m,r,p);if(m.length==0)return v();var P=(A||q)(m);e.setTimeout(t,1)}}else k(m,r,p),o(m,r,p)}}function m(a,c,d){d?d.after(a):c.append(a)}var k={};k.enter=j("enter",m,q);k.leave=j("leave",q,function(a){a.remove()});k.move=j("move",function(a,c,d){m(a,c,d)},q);k.show=j("show",function(a){a.css("display","")},q);k.hide=j("hide",q,function(a){a.css("display",
|
||||
"none")});k.animate=function(a,c){j(a,q,q)(c)};return k};i.enabled=function(a){if(arguments.length)c.running=!a;return!c.running};return i}]},Kb="Non-assignable model expression: ";Jb.$inject=["$provide"];var Ic=/^(x[\:\-_]|data[\:\-_])/i,jb=/^([^:]+):\/\/(\w+:{0,1}\w*@)?(\{?[\w\.-]*\}?)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,Pb=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,Oa={http:80,https:443,ftp:21};Rb.prototype=lb.prototype=Qb.prototype={$$replace:!1,absUrl:Pa("$$absUrl"),url:function(a,c){if(C(a))return this.$$url;
|
||||
var d=Pb.exec(a);d[1]&&this.path(decodeURIComponent(d[1]));if(d[2]||d[1])this.search(d[3]||"");this.hash(d[5]||"",c);return this},protocol:Pa("$$protocol"),host:Pa("$$host"),port:Pa("$$port"),path:Sb("$$path",function(a){return a.charAt(0)=="/"?a:"/"+a}),search:function(a,c){if(C(a))return this.$$search;B(c)?c===null?delete this.$$search[a]:this.$$search[a]=c:this.$$search=E(a)?vb(a):a;this.$$compose();return this},hash:Sb("$$hash",qa),replace:function(){this.$$replace=!0;return this}};var Da={"null":function(){return null},
|
||||
"true":function(){return!0},"false":function(){return!1},undefined:q,"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return B(d)?B(e)?d+e:d:B(e)?e:p},"-":function(a,c,d,e){d=d(a,c);e=e(a,c);return(B(d)?d:0)-(B(e)?e:0)},"*":function(a,c,d,e){return d(a,c)*e(a,c)},"/":function(a,c,d,e){return d(a,c)/e(a,c)},"%":function(a,c,d,e){return d(a,c)%e(a,c)},"^":function(a,c,d,e){return d(a,c)^e(a,c)},"=":q,"===":function(a,c,d,e){return d(a,c)===e(a,c)},"!==":function(a,c,d,e){return d(a,c)!==e(a,c)},"==":function(a,
|
||||
c,d,e){return d(a,c)==e(a,c)},"!=":function(a,c,d,e){return d(a,c)!=e(a,c)},"<":function(a,c,d,e){return d(a,c)<e(a,c)},">":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Qc={n:"\n",f:"\u000c",r:"\r",
|
||||
t:"\t",v:"\u000b","'":"'",'"':'"'},mb={},ad=/^(([^:]+):)?\/\/(\w+:{0,1}\w*@)?([\w\.-]*)?(:([0-9]+))?(.*)$/,ed=M.XMLHttpRequest||function(){try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(c){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(d){}throw Error("This browser does not support XMLHttpRequest.");};Zb.$inject=["$provide"];$b.$inject=["$locale"];bc.$inject=["$locale"];var ec=".",od={yyyy:Q("FullYear",4),yy:Q("FullYear",
|
||||
2,0,!0),y:Q("FullYear",1),MMMM:Qa("Month"),MMM:Qa("Month",!0),MM:Q("Month",2,1),M:Q("Month",1,1),dd:Q("Date",2),d:Q("Date",1),HH:Q("Hours",2),H:Q("Hours",1),hh:Q("Hours",2,-12),h:Q("Hours",1,-12),mm:Q("Minutes",2),m:Q("Minutes",1),ss:Q("Seconds",2),s:Q("Seconds",1),sss:Q("Milliseconds",3),EEEE:Qa("Day"),EEE:Qa("Day",!0),a:function(a,c){return a.getHours()<12?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){var a=-1*a.getTimezoneOffset(),c=a>=0?"+":"";c+=nb(Math[a>0?"floor":"ceil"](a/60),2)+nb(Math.abs(a%60),
|
||||
2);return c}},nd=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,md=/^\d+$/;ac.$inject=["$locale"];var kd=S(I),ld=S(oa);cc.$inject=["$parse"];var rd=S({restrict:"E",compile:function(a,c){Z<=8&&(!c.href&&!c.name&&c.$set("href",""),a.append(T.createComment("IE fix")));return function(a,c){c.bind("click",function(a){c.attr("href")||a.preventDefault()})}}}),pb={};n(Na,function(a,c){var d=da("ng-"+c);pb[d]=function(){return{priority:100,compile:function(){return function(a,
|
||||
g,i){a.$watch(i[d],function(a){i.$set(c,!!a)})}}}}});n(["src","srcset","href"],function(a){var c=da("ng-"+a);pb[c]=function(){return{priority:99,link:function(d,e,g){g.$observe(c,function(c){c&&(g.$set(a,c),Z&&e.prop(a,g[a]))})}}}});var Ta={$addControl:q,$removeControl:q,$setValidity:q,$setDirty:q,$setPristine:q};fc.$inject=["$element","$attrs","$scope"];var Wa=function(a){return["$timeout",function(c){var d={name:"form",restrict:"E",controller:fc,compile:function(){return{pre:function(a,d,i,f){if(!i.action){var h=
|
||||
function(a){a.preventDefault?a.preventDefault():a.returnValue=!1};gc(d[0],"submit",h);d.bind("$destroy",function(){c(function(){gb(d[0],"submit",h)},0,!1)})}var j=d.parent().controller("form"),m=i.name||i.ngForm;m&&(a[m]=f);j&&d.bind("$destroy",function(){j.$removeControl(f);m&&(a[m]=p);t(f,Ta)})}}}};return a?t(V(d),{restrict:"EAC"}):d}]},sd=Wa(),td=Wa(!0),ud=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,vd=/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/,
|
||||
wd=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,hc={text:Va,number:function(a,c,d,e,g,i){Va(a,c,d,e,g,i);e.$parsers.push(function(a){var c=X(a);return c||wd.test(a)?(e.$setValidity("number",!0),a===""?null:c?a:parseFloat(a)):(e.$setValidity("number",!1),p)});e.$formatters.push(function(a){return X(a)?"":""+a});if(d.min){var f=parseFloat(d.min),a=function(a){return!X(a)&&a<f?(e.$setValidity("min",!1),p):(e.$setValidity("min",!0),a)};e.$parsers.push(a);e.$formatters.push(a)}if(d.max){var h=parseFloat(d.max),
|
||||
d=function(a){return!X(a)&&a>h?(e.$setValidity("max",!1),p):(e.$setValidity("max",!0),a)};e.$parsers.push(d);e.$formatters.push(d)}e.$formatters.push(function(a){return X(a)||Ya(a)?(e.$setValidity("number",!0),a):(e.$setValidity("number",!1),p)})},url:function(a,c,d,e,g,i){Va(a,c,d,e,g,i);a=function(a){return X(a)||ud.test(a)?(e.$setValidity("url",!0),a):(e.$setValidity("url",!1),p)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,g,i){Va(a,c,d,e,g,i);a=function(a){return X(a)||vd.test(a)?
|
||||
(e.$setValidity("email",!0),a):(e.$setValidity("email",!1),p)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){C(d.name)&&c.attr("name",Fa());c.bind("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var g=d.ngTrueValue,i=d.ngFalseValue;E(g)||(g=!0);E(i)||(i=!1);c.bind("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});
|
||||
e.$render=function(){c[0].checked=e.$viewValue};e.$formatters.push(function(a){return a===g});e.$parsers.push(function(a){return a?g:i})},hidden:q,button:q,submit:q,reset:q},ic=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",link:function(d,e,g,i){i&&(hc[I(g.type)]||hc.text)(d,e,g,i,c,a)}}}],Sa="ng-valid",Ra="ng-invalid",pa="ng-pristine",Ua="ng-dirty",xd=["$scope","$exceptionHandler","$attrs","$element","$parse",function(a,c,d,e,g){function i(a,c){c=c?"-"+bb(c,"-"):"";
|
||||
e.removeClass((a?Ra:Sa)+c).addClass((a?Sa:Ra)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=d.name;var f=g(d.ngModel),h=f.assign;if(!h)throw Error(Kb+d.ngModel+" ("+va(e)+")");this.$render=q;var j=e.inheritedData("$formController")||Ta,m=0,k=this.$error={};e.addClass(pa);i(!0);this.$setValidity=function(a,c){if(k[a]!==!c){if(c){if(k[a]&&m--,!m)i(!0),this.$valid=
|
||||
!0,this.$invalid=!1}else i(!1),this.$invalid=!0,this.$valid=!1,m++;k[a]=!c;i(c,a);j.$setValidity(a,c,this)}};this.$setPristine=function(){this.$dirty=!1;this.$pristine=!0;e.removeClass(Ua).addClass(pa)};this.$setViewValue=function(d){this.$viewValue=d;if(this.$pristine)this.$dirty=!0,this.$pristine=!1,e.removeClass(pa).addClass(Ua),j.$setDirty();n(this.$parsers,function(a){d=a(d)});if(this.$modelValue!==d)this.$modelValue=d,h(a,d),n(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})};
|
||||
var l=this;a.$watch(function(){var c=f(a);if(l.$modelValue!==c){var d=l.$formatters,e=d.length;for(l.$modelValue=c;e--;)c=d[e](c);if(l.$viewValue!==c)l.$viewValue=c,l.$render()}})}],yd=function(){return{require:["ngModel","^?form"],controller:xd,link:function(a,c,d,e){var g=e[0],i=e[1]||Ta;i.$addControl(g);c.bind("$destroy",function(){i.$removeControl(g)})}}},zd=S({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),jc=function(){return{require:"?ngModel",
|
||||
link:function(a,c,d,e){if(e){d.required=!0;var g=function(a){if(d.required&&(X(a)||a===!1))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(g);e.$parsers.unshift(g);d.$observe("required",function(){g(e.$viewValue)})}}}},Ad=function(){return{require:"ngModel",link:function(a,c,d,e){var g=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){var c=[];a&&n(a.split(g),function(a){a&&c.push(U(a))});return c});e.$formatters.push(function(a){return F(a)?
|
||||
a.join(", "):p})}}},Bd=/^(true|false|\d+)$/,Cd=function(){return{priority:100,compile:function(a,c){return Bd.test(c.ngValue)?function(a,c,g){g.$set("value",a.$eval(g.ngValue))}:function(a,c,g){a.$watch(g.ngValue,function(a){g.$set("value",a,!1)})}}}},Dd=aa(function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==p?"":a)})}),Ed=["$interpolate",function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",
|
||||
c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],Fd=[function(){return function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBindHtmlUnsafe);a.$watch(d.ngBindHtmlUnsafe,function(a){c.html(a||"")})}}],Gd=ob("",!0),Hd=ob("Odd",0),Id=ob("Even",1),Jd=aa({compile:function(a,c){c.$set("ngCloak",p);a.removeClass("ng-cloak")}}),Kd=[function(){return{scope:!0,controller:"@"}}],Ld=["$sniffer",function(a){return{priority:1E3,compile:function(){a.csp=!0}}}],kc={};n("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress".split(" "),
|
||||
function(a){var c=da("ng-"+a);kc[c]=["$parse",function(d){return function(e,g,i){var f=d(i[c]);g.bind(I(a),function(a){e.$apply(function(){f(e,{$event:a})})})}}]});var Md=aa(function(a,c,d){c.bind("submit",function(){a.$apply(d.ngSubmit)})}),Nd=["$animator",function(a){return{transclude:"element",priority:1E3,terminal:!0,restrict:"A",compile:function(c,d,e){return function(c,d,f){var h=a(c,f),j,m;c.$watch(f.ngIf,function(a){j&&(h.leave(j),j=p);m&&(m.$destroy(),m=p);ua(a)&&(m=c.$new(),e(m,function(a){j=
|
||||
a;h.enter(a,d.parent(),d)}))})}}}}],Od=["$http","$templateCache","$anchorScroll","$compile","$animator",function(a,c,d,e,g){return{restrict:"ECA",terminal:!0,compile:function(i,f){var h=f.ngInclude||f.src,j=f.onload||"",m=f.autoscroll;return function(f,i,n){var o=g(f,n),p=0,r,t=function(){r&&(r.$destroy(),r=null);o.leave(i.contents(),i)};f.$watch(h,function(g){var h=++p;g?(a.get(g,{cache:c}).success(function(a){h===p&&(r&&r.$destroy(),r=f.$new(),o.leave(i.contents(),i),a=w("<div/>").html(a).contents(),
|
||||
o.enter(a,i),e(a)(r),B(m)&&(!m||f.$eval(m))&&d(),r.$emit("$includeContentLoaded"),f.$eval(j))}).error(function(){h===p&&t()}),f.$emit("$includeContentRequested")):t()})}}}}],Pd=aa({compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Qd=aa({terminal:!0,priority:1E3}),Rd=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,g,i){var f=i.count,h=g.attr(i.$attr.when),j=i.offset||0,m=e.$eval(h),k={},l=c.startSymbol(),p=c.endSymbol();n(m,function(a,e){k[e]=
|
||||
c(a.replace(d,l+f+"-"+j+p))});e.$watch(function(){var c=parseFloat(e.$eval(f));return isNaN(c)?"":(c in m||(c=a.pluralCat(c-j)),k[c](e,g,!0))},function(a){g.text(a)})}}}],Sd=["$parse","$animator",function(a,c){return{transclude:"element",priority:1E3,terminal:!0,compile:function(d,e,g){return function(d,e,h){var j=c(d,h),m=h.ngRepeat,k=m.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),l,p,o,z,r,t={$id:la};if(!k)throw Error("Expected ngRepeat in form of '_item_ in _collection_[ track by _id_]' but got '"+
|
||||
m+"'.");h=k[1];o=k[2];(k=k[4])?(l=a(k),p=function(a,c,e){r&&(t[r]=a);t[z]=c;t.$index=e;return l(d,t)}):p=function(a,c){return la(c)};k=h.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!k)throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '"+h+"'.");z=k[3]||k[1];r=k[2];var x={};d.$watchCollection(o,function(a){var c,h,k=e,l,o={},t,q,w,s,B,y,C=[];if(Xa(a))B=a;else{B=[];for(w in a)a.hasOwnProperty(w)&&w.charAt(0)!="$"&&B.push(w);B.sort()}t=B.length;h=
|
||||
C.length=B.length;for(c=0;c<h;c++)if(w=a===B?c:B[c],s=a[w],l=p(w,s,c),x.hasOwnProperty(l))y=x[l],delete x[l],o[l]=y,C[c]=y;else if(o.hasOwnProperty(l))throw n(C,function(a){a&&a.element&&(x[a.id]=a)}),Error("Duplicates in a repeater are not allowed. Repeater: "+m+" key: "+l);else C[c]={id:l},o[l]=!1;for(w in x)if(x.hasOwnProperty(w))y=x[w],j.leave(y.element),y.element[0].$$NG_REMOVED=!0,y.scope.$destroy();c=0;for(h=B.length;c<h;c++){w=a===B?c:B[c];s=a[w];y=C[c];if(y.element){q=y.scope;l=k[0];do l=
|
||||
l.nextSibling;while(l&&l.$$NG_REMOVED);y.element[0]!=l&&j.move(y.element,null,k);k=y.element}else q=d.$new();q[z]=s;r&&(q[r]=w);q.$index=c;q.$first=c===0;q.$last=c===t-1;q.$middle=!(q.$first||q.$last);y.element||g(q,function(a){j.enter(a,null,k);k=a;y.scope=q;y.element=a;o[y.id]=y})}x=o})}}}}],Td=["$animator",function(a){return function(c,d,e){var g=a(c,e);c.$watch(e.ngShow,function(a){g[ua(a)?"show":"hide"](d)})}}],Ud=["$animator",function(a){return function(c,d,e){var g=a(c,e);c.$watch(e.ngHide,
|
||||
function(a){g[ua(a)?"hide":"show"](d)})}}],Vd=aa(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&n(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),Wd=["$animator",function(a){return{restrict:"EA",require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,d,e,g){var i=a(c,e),f,h,j=[];c.$watch(e.ngSwitch||e.on,function(a){for(var d=0,l=j.length;d<l;d++)j[d].$destroy(),i.leave(h[d]);h=[];j=[];if(f=g.cases["!"+a]||g.cases["?"])c.$eval(e.change),n(f,function(a){var d=
|
||||
c.$new();j.push(d);a.transclude(d,function(c){var d=a.element;h.push(c);i.enter(c,d.parent(),d)})})})}}}],Xd=aa({transclude:"element",priority:500,require:"^ngSwitch",compile:function(a,c,d){return function(a,g,i,f){f.cases["!"+c.ngSwitchWhen]=f.cases["!"+c.ngSwitchWhen]||[];f.cases["!"+c.ngSwitchWhen].push({transclude:d,element:g})}}}),Yd=aa({transclude:"element",priority:500,require:"^ngSwitch",compile:function(a,c,d){return function(a,c,i,f){f.cases["?"]=f.cases["?"]||[];f.cases["?"].push({transclude:d,
|
||||
element:c})}}}),Zd=aa({controller:["$transclude","$element",function(a,c){a(function(a){c.append(a)})}]}),$d=["$http","$templateCache","$route","$anchorScroll","$compile","$controller","$animator",function(a,c,d,e,g,i,f){return{restrict:"ECA",terminal:!0,link:function(a,c,m){function k(){var f=d.current&&d.current.locals,k=f&&f.$template;if(k){o.leave(c.contents(),c);l&&(l.$destroy(),l=null);k=w("<div></div>").html(k).contents();o.enter(k,c);var k=g(k),m=d.current;l=m.scope=a.$new();if(m.controller)f.$scope=
|
||||
l,f=i(m.controller,f),m.controllerAs&&(l[m.controllerAs]=f),c.children().data("$ngControllerController",f);k(l);l.$emit("$viewContentLoaded");l.$eval(n);e()}else o.leave(c.contents(),c),l&&(l.$destroy(),l=null)}var l,n=m.onload||"",o=f(a,m);a.$on("$routeChangeSuccess",k);k()}}}],ae=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){d.type=="text/ng-template"&&a.put(d.id,c[0].text)}}}],be=S({terminal:!0}),ce=["$compile","$parse",function(a,c){var d=/^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/,
|
||||
e={$setViewValue:q};return{restrict:"E",require:["select","?ngModel"],controller:["$element","$scope","$attrs",function(a,c,d){var h=this,j={},m=e,k;h.databound=d.ngModel;h.init=function(a,c,d){m=a;k=d};h.addOption=function(c){j[c]=!0;m.$viewValue==c&&(a.val(c),k.parent()&&k.remove())};h.removeOption=function(a){this.hasOption(a)&&(delete j[a],m.$viewValue==a&&this.renderUnknownOption(a))};h.renderUnknownOption=function(c){c="? "+la(c)+" ?";k.val(c);a.prepend(k);a.val(c);k.prop("selected",!0)};h.hasOption=
|
||||
function(a){return j.hasOwnProperty(a)};c.$on("$destroy",function(){h.renderUnknownOption=q})}],link:function(e,i,f,h){function j(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(v.parent()&&v.remove(),c.val(a),a===""&&t.prop("selected",!0)):C(a)&&t?c.val(""):e.renderUnknownOption(a)};c.bind("change",function(){a.$apply(function(){v.parent()&&v.remove();d.$setViewValue(c.val())})})}function m(a,c,d){var e;d.$render=function(){var a=new za(d.$viewValue);n(c.find("option"),function(c){c.selected=
|
||||
B(a.get(c.value))})};a.$watch(function(){ia(e,d.$viewValue)||(e=V(d.$viewValue),d.$render())});c.bind("change",function(){a.$apply(function(){var a=[];n(c.find("option"),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function k(e,f,g){function i(){var a={"":[]},c=[""],d,h,q,v,s;q=g.$modelValue;v=u(e)||[];var z=l?qb(v):v,B,y,A;y={};s=!1;var C,D;if(o)if(t&&F(q)){s=new za([]);for(h=0;h<q.length;h++)y[k]=q[h],s.put(t(e,y),q[h])}else s=new za(q);for(A=0;B=z.length,A<B;A++){y[k]=v[l?y[l]=
|
||||
z[A]:A];d=m(e,y)||"";if(!(h=a[d]))h=a[d]=[],c.push(d);o?d=s.remove(t?t(e,y):n(e,y))!=p:(t?(d={},d[k]=q,d=t(e,d)===t(e,y)):d=q===n(e,y),s=s||d);C=j(e,y);C=C===p?"":C;h.push({id:t?t(e,y):l?z[A]:A,label:C,selected:d})}o||(r||q===null?a[""].unshift({id:"",label:"",selected:!s}):s||a[""].unshift({id:"?",label:"",selected:!0}));y=0;for(z=c.length;y<z;y++){d=c[y];h=a[d];if(w.length<=y)q={element:E.clone().attr("label",d),label:h.label},v=[q],w.push(v),f.append(q.element);else if(v=w[y],q=v[0],q.label!=d)q.element.attr("label",
|
||||
q.label=d);C=null;A=0;for(B=h.length;A<B;A++)if(d=h[A],s=v[A+1]){C=s.element;if(s.label!==d.label)C.text(s.label=d.label);if(s.id!==d.id)C.val(s.id=d.id);if(C[0].selected!==d.selected)C.prop("selected",s.selected=d.selected)}else d.id===""&&r?D=r:(D=x.clone()).val(d.id).attr("selected",d.selected).text(d.label),v.push({element:D,label:d.label,id:d.id,selected:d.selected}),C?C.after(D):q.element.append(D),C=D;for(A++;v.length>A;)v.pop().element.remove()}for(;w.length>y;)w.pop()[0].element.remove()}
|
||||
var h;if(!(h=q.match(d)))throw Error("Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_ (track by _expr_)?' but got '"+q+"'.");var j=c(h[2]||h[1]),k=h[4]||h[6],l=h[5],m=c(h[3]||""),n=c(h[2]?h[1]:k),u=c(h[7]),t=h[8]?c(h[8]):null,w=[[{element:f,label:""}]];r&&(a(r)(e),r.removeClass("ng-scope"),r.remove());f.html("");f.bind("change",function(){e.$apply(function(){var a,c=u(e)||[],d={},h,i,j,m,q,r;if(o){i=[];m=0;for(r=w.length;m<r;m++){a=w[m];j=1;for(q=a.length;j<
|
||||
q;j++)if((h=a[j].element)[0].selected){h=h.val();l&&(d[l]=h);if(t)for(var s=0;s<c.length;s++){if(d[k]=c[s],t(e,d)==h)break}else d[k]=c[h];i.push(n(e,d))}}}else if(h=f.val(),h=="?")i=p;else if(h=="")i=null;else if(t)for(s=0;s<c.length;s++){if(d[k]=c[s],t(e,d)==h){i=n(e,d);break}}else d[k]=c[h],l&&(d[l]=h),i=n(e,d);g.$setViewValue(i)})});g.$render=i;e.$watch(i)}if(h[1]){for(var l=h[0],u=h[1],o=f.multiple,q=f.ngOptions,r=!1,t,x=w(T.createElement("option")),E=w(T.createElement("optgroup")),v=x.clone(),
|
||||
h=0,A=i.children(),G=A.length;h<G;h++)if(A[h].value==""){t=r=A.eq(h);break}l.init(u,r,v);if(o&&(f.required||f.ngRequired)){var D=function(a){u.$setValidity("required",!f.required||a&&a.length);return a};u.$parsers.push(D);u.$formatters.unshift(D);f.$observe("required",function(){D(u.$viewValue)})}q?k(e,i,u):o?m(e,i,u):j(e,i,u,l)}}}}],de=["$interpolate",function(a){var c={addOption:q,removeOption:q};return{restrict:"E",priority:100,compile:function(d,e){if(C(e.value)){var g=a(d.text(),!0);g||e.$set("value",
|
||||
d.text())}return function(a,d,e){var j=d.parent(),m=j.data("$selectController")||j.parent().data("$selectController");m&&m.databound?d.prop("selected",!1):m=c;g?a.$watch(g,function(a,c){e.$set("value",a);a!==c&&m.removeOption(c);m.addOption(a)}):m.addOption(e.value);d.bind("$destroy",function(){m.removeOption(e.value)})}}}}],ee=S({restrict:"E",terminal:!0});(ga=M.jQuery)?(w=ga,t(ga.fn,{scope:Ba.scope,controller:Ba.controller,injector:Ba.injector,inheritedData:Ba.inheritedData}),db("remove",!0),db("empty"),
|
||||
db("html")):w=R;Ha.element=w;(function(a){t(a,{bootstrap:xb,copy:V,extend:t,equals:ia,element:w,forEach:n,injector:yb,noop:q,bind:$a,toJson:ha,fromJson:ub,identity:qa,isUndefined:C,isDefined:B,isString:E,isFunction:H,isObject:L,isNumber:Ya,isElement:oc,isArray:F,version:pd,isDate:ra,lowercase:I,uppercase:oa,callbacks:{counter:0},noConflict:lc});Aa=tc(M);try{Aa("ngLocale")}catch(c){Aa("ngLocale",[]).provider("$locale",fd)}Aa("ng",["ngLocale"],["$provide",function(a){a.provider("$compile",Jb).directive({a:rd,
|
||||
input:ic,textarea:ic,form:sd,script:ae,select:ce,style:ee,option:de,ngBind:Dd,ngBindHtmlUnsafe:Fd,ngBindTemplate:Ed,ngClass:Gd,ngClassEven:Id,ngClassOdd:Hd,ngCsp:Ld,ngCloak:Jd,ngController:Kd,ngForm:td,ngHide:Ud,ngIf:Nd,ngInclude:Od,ngInit:Pd,ngNonBindable:Qd,ngPluralize:Rd,ngRepeat:Sd,ngShow:Td,ngSubmit:Md,ngStyle:Vd,ngSwitch:Wd,ngSwitchWhen:Xd,ngSwitchDefault:Yd,ngOptions:be,ngView:$d,ngTransclude:Zd,ngModel:yd,ngList:Ad,ngChange:zd,required:jc,ngRequired:jc,ngValue:Cd}).directive(pb).directive(kc);
|
||||
a.provider({$anchorScroll:Cc,$animation:Ib,$animator:qd,$browser:Ec,$cacheFactory:Fc,$controller:Jc,$document:Kc,$exceptionHandler:Lc,$filter:Zb,$interpolate:Mc,$http:bd,$httpBackend:cd,$location:Nc,$log:Oc,$parse:Sc,$route:Vc,$routeParams:Wc,$rootScope:Xc,$q:Tc,$sniffer:Yc,$templateCache:Gc,$timeout:gd,$window:Zc})}])})(Ha);w(T).ready(function(){rc(T,xb)})})(window,document);angular.element(document).find("head").append('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}</style>');
|
||||
6
docs-web/src/main/webapp/lib/jquery.js
vendored
Normal file
6
docs-web/src/main/webapp/lib/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
9
docs-web/src/main/webapp/lib/less.js
Normal file
9
docs-web/src/main/webapp/lib/less.js
Normal file
File diff suppressed because one or more lines are too long
1246
docs-web/src/main/webapp/lib/underscore.js
Normal file
1246
docs-web/src/main/webapp/lib/underscore.js
Normal file
File diff suppressed because it is too large
Load Diff
3
docs-web/src/main/webapp/locale/messages.en.js
Normal file
3
docs-web/src/main/webapp/locale/messages.en.js
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"app": "Sismics Docs"
|
||||
}
|
||||
18
docs-web/src/main/webapp/partial/document.edit.html
Normal file
18
docs-web/src/main/webapp/partial/document.edit.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<form class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputTitle">Title</label>
|
||||
<div class="controls">
|
||||
<input class="input-block-level" type="text" id="inputTitle" placeholder="Title" ng-model="document.title" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputDescription">Description</label>
|
||||
<div class="controls">
|
||||
<textarea class="input-block-level" rows="5" id="inputDescription" ng-model="document.description"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary" ng-click="edit()">{{ isEdit() ? 'Edit' : 'Add' }}</button>
|
||||
<button type="submit" class="btn" ng-click="cancel()">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
26
docs-web/src/main/webapp/partial/document.html
Normal file
26
docs-web/src/main/webapp/partial/document.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<div class="span5 well">
|
||||
<p class="text-center">
|
||||
<button class="btn btn-primary" type="button" ng-click="addDocument()">Add a document</button>
|
||||
</p>
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Creation date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-click="viewDocument(document.id)" ng-repeat="document in documents">
|
||||
<td>{{ document.title }}</td>
|
||||
<td>{{ document.create_date | date: 'short' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="span7 well">
|
||||
<div ui-view="document"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
1
docs-web/src/main/webapp/partial/document.view.html
Normal file
1
docs-web/src/main/webapp/partial/document.view.html
Normal file
@@ -0,0 +1 @@
|
||||
View doc
|
||||
27
docs-web/src/main/webapp/partial/login.html
Normal file
27
docs-web/src/main/webapp/partial/login.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<div class="row-fluid">
|
||||
<div class="span4 offset4">
|
||||
<h1 class="text-center">Sismics Docs</h1>
|
||||
<form class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputUsername">Username</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="inputUsername" placeholder="Username" ng-model="user.username" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputPassword">Password</label>
|
||||
<div class="controls">
|
||||
<input type="password" id="inputPassword" placeholder="Password" ng-model="user.password" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" ng-model="user.remember" /> Remember me
|
||||
</label>
|
||||
<button type="submit" class="btn" ng-click="login()">Sign in</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
5
docs-web/src/main/webapp/partial/main.html
Normal file
5
docs-web/src/main/webapp/partial/main.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<h4 class="text-center">Loading...</h4>
|
||||
</div>
|
||||
</div>
|
||||
310
docs-web/src/main/webapp/sismicsdms/log/dms.log
Normal file
310
docs-web/src/main/webapp/sismicsdms/log/dms.log
Normal file
@@ -0,0 +1,310 @@
|
||||
27 Jul 2013 13:37:48,704 INFO com.sismics.util.jpa.EMF.getEntityManagerProperties(EMF.java:69) Configuring EntityManager from hibernate.properties
|
||||
27 Jul 2013 13:37:49,155 INFO com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:67) Opening database and executing incremental updates
|
||||
27 Jul 2013 13:37:49,157 INFO com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:95) Unable to get database version: Table T_CONFIG not found
|
||||
27 Jul 2013 13:37:49,157 INFO com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:109) Executing initial schema creation script
|
||||
27 Jul 2013 13:37:49,159 ERROR com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:122) Unable to complete schema update
|
||||
java.lang.UnsupportedOperationException: Cannot list files for URL file:/C:/Users/Ben/workspace/bgd/docs/trunk/docs-core/target/classes/com/sismics/util/jpa/EMF$1.class
|
||||
at com.sismics.util.ResourceUtil.list(ResourceUtil.java:86)
|
||||
at com.sismics.util.jpa.DbOpenHelper.executeAllScript(DbOpenHelper.java:152)
|
||||
at com.sismics.util.jpa.EMF$1.onCreate(EMF.java:45)
|
||||
at com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:110)
|
||||
at com.sismics.util.jpa.EMF.<clinit>(EMF.java:55)
|
||||
at com.sismics.docs.core.util.TransactionUtil.handle(TransactionUtil.java:38)
|
||||
at com.sismics.util.filter.RequestContextFilter.init(RequestContextFilter.java:76)
|
||||
at org.eclipse.jetty.servlet.FilterHolder.doStart(FilterHolder.java:114)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:754)
|
||||
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:258)
|
||||
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1221)
|
||||
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:699)
|
||||
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:454)
|
||||
at org.mortbay.jetty.plugin.JettyWebAppContext.doStart(JettyWebAppContext.java:256)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
|
||||
at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:167)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerWrapper.doStart(HandlerWrapper.java:90)
|
||||
at org.eclipse.jetty.server.Server.doStart(Server.java:263)
|
||||
at org.mortbay.jetty.plugin.JettyServer.doStart(JettyServer.java:65)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.mortbay.jetty.plugin.AbstractJettyMojo.startJetty(AbstractJettyMojo.java:511)
|
||||
at org.mortbay.jetty.plugin.AbstractJettyMojo.execute(AbstractJettyMojo.java:364)
|
||||
at org.mortbay.jetty.plugin.JettyRunMojo.execute(JettyRunMojo.java:516)
|
||||
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
|
||||
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
|
||||
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
|
||||
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
|
||||
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
|
||||
at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
|
||||
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
|
||||
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
|
||||
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
|
||||
at java.lang.reflect.Method.invoke(Method.java:601)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
|
||||
27 Jul 2013 13:37:49,162 INFO com.sismics.util.jpa.EMF.getEntityManagerProperties(EMF.java:69) Configuring EntityManager from hibernate.properties
|
||||
27 Jul 2013 13:37:49,244 ERROR com.sismics.util.jpa.EMF.<clinit>(EMF.java:60) Error creating EMF
|
||||
javax.persistence.PersistenceException: No Persistence provider for EntityManager named transactions-optional
|
||||
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:69)
|
||||
at com.sismics.util.jpa.EMF.<clinit>(EMF.java:57)
|
||||
at com.sismics.docs.core.util.TransactionUtil.handle(TransactionUtil.java:38)
|
||||
at com.sismics.util.filter.RequestContextFilter.init(RequestContextFilter.java:76)
|
||||
at org.eclipse.jetty.servlet.FilterHolder.doStart(FilterHolder.java:114)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:754)
|
||||
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:258)
|
||||
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1221)
|
||||
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:699)
|
||||
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:454)
|
||||
at org.mortbay.jetty.plugin.JettyWebAppContext.doStart(JettyWebAppContext.java:256)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
|
||||
at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:167)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerWrapper.doStart(HandlerWrapper.java:90)
|
||||
at org.eclipse.jetty.server.Server.doStart(Server.java:263)
|
||||
at org.mortbay.jetty.plugin.JettyServer.doStart(JettyServer.java:65)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.mortbay.jetty.plugin.AbstractJettyMojo.startJetty(AbstractJettyMojo.java:511)
|
||||
at org.mortbay.jetty.plugin.AbstractJettyMojo.execute(AbstractJettyMojo.java:364)
|
||||
at org.mortbay.jetty.plugin.JettyRunMojo.execute(JettyRunMojo.java:516)
|
||||
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
|
||||
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
|
||||
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
|
||||
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
|
||||
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
|
||||
at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
|
||||
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
|
||||
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
|
||||
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
|
||||
at java.lang.reflect.Method.invoke(Method.java:601)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
|
||||
27 Jul 2013 13:37:49,245 ERROR com.sismics.docs.core.util.TransactionUtil.handle(TransactionUtil.java:40) Cannot create entity manager
|
||||
java.lang.NullPointerException
|
||||
at com.sismics.docs.core.util.TransactionUtil.handle(TransactionUtil.java:38)
|
||||
at com.sismics.util.filter.RequestContextFilter.init(RequestContextFilter.java:76)
|
||||
at org.eclipse.jetty.servlet.FilterHolder.doStart(FilterHolder.java:114)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:754)
|
||||
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:258)
|
||||
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1221)
|
||||
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:699)
|
||||
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:454)
|
||||
at org.mortbay.jetty.plugin.JettyWebAppContext.doStart(JettyWebAppContext.java:256)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
|
||||
at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:167)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerWrapper.doStart(HandlerWrapper.java:90)
|
||||
at org.eclipse.jetty.server.Server.doStart(Server.java:263)
|
||||
at org.mortbay.jetty.plugin.JettyServer.doStart(JettyServer.java:65)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.mortbay.jetty.plugin.AbstractJettyMojo.startJetty(AbstractJettyMojo.java:511)
|
||||
at org.mortbay.jetty.plugin.AbstractJettyMojo.execute(AbstractJettyMojo.java:364)
|
||||
at org.mortbay.jetty.plugin.JettyRunMojo.execute(JettyRunMojo.java:516)
|
||||
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
|
||||
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
|
||||
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
|
||||
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
|
||||
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
|
||||
at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
|
||||
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
|
||||
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
|
||||
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
|
||||
at java.lang.reflect.Method.invoke(Method.java:601)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
|
||||
27 Jul 2013 13:43:02,167 INFO com.sismics.util.jpa.EMF.getEntityManagerProperties(EMF.java:69) Configuring EntityManager from hibernate.properties
|
||||
27 Jul 2013 13:43:02,621 INFO com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:67) Opening database and executing incremental updates
|
||||
27 Jul 2013 13:43:02,623 INFO com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:95) Unable to get database version: Table T_CONFIG not found
|
||||
27 Jul 2013 13:43:02,624 INFO com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:109) Executing initial schema creation script
|
||||
27 Jul 2013 13:44:01,672 ERROR com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:122) Unable to complete schema update
|
||||
java.lang.UnsupportedOperationException: Cannot list files for URL file:/C:/Users/Ben/workspace/bgd/docs/trunk/docs-core/target/classes/com/sismics/util/jpa/EMF$1.class
|
||||
at com.sismics.util.ResourceUtil.list(ResourceUtil.java:86)
|
||||
at com.sismics.util.jpa.DbOpenHelper.executeAllScript(DbOpenHelper.java:152)
|
||||
at com.sismics.util.jpa.EMF$1.onCreate(EMF.java:45)
|
||||
at com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:110)
|
||||
at com.sismics.util.jpa.EMF.<clinit>(EMF.java:55)
|
||||
at com.sismics.docs.core.util.TransactionUtil.handle(TransactionUtil.java:38)
|
||||
at com.sismics.util.filter.RequestContextFilter.init(RequestContextFilter.java:76)
|
||||
at org.eclipse.jetty.servlet.FilterHolder.doStart(FilterHolder.java:114)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:754)
|
||||
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:258)
|
||||
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1221)
|
||||
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:699)
|
||||
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:454)
|
||||
at org.mortbay.jetty.plugin.JettyWebAppContext.doStart(JettyWebAppContext.java:256)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
|
||||
at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:167)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerWrapper.doStart(HandlerWrapper.java:90)
|
||||
at org.eclipse.jetty.server.Server.doStart(Server.java:263)
|
||||
at org.mortbay.jetty.plugin.JettyServer.doStart(JettyServer.java:65)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.mortbay.jetty.plugin.AbstractJettyMojo.startJetty(AbstractJettyMojo.java:511)
|
||||
at org.mortbay.jetty.plugin.AbstractJettyMojo.execute(AbstractJettyMojo.java:364)
|
||||
at org.mortbay.jetty.plugin.JettyRunMojo.execute(JettyRunMojo.java:516)
|
||||
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
|
||||
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
|
||||
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
|
||||
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
|
||||
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
|
||||
at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
|
||||
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
|
||||
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
|
||||
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
|
||||
at java.lang.reflect.Method.invoke(Method.java:601)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
|
||||
27 Jul 2013 13:44:01,677 INFO com.sismics.util.jpa.EMF.getEntityManagerProperties(EMF.java:69) Configuring EntityManager from hibernate.properties
|
||||
27 Jul 2013 13:44:01,769 ERROR com.sismics.util.jpa.EMF.<clinit>(EMF.java:60) Error creating EMF
|
||||
javax.persistence.PersistenceException: No Persistence provider for EntityManager named transactions-optional
|
||||
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:69)
|
||||
at com.sismics.util.jpa.EMF.<clinit>(EMF.java:57)
|
||||
at com.sismics.docs.core.util.TransactionUtil.handle(TransactionUtil.java:38)
|
||||
at com.sismics.util.filter.RequestContextFilter.init(RequestContextFilter.java:76)
|
||||
at org.eclipse.jetty.servlet.FilterHolder.doStart(FilterHolder.java:114)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:754)
|
||||
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:258)
|
||||
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1221)
|
||||
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:699)
|
||||
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:454)
|
||||
at org.mortbay.jetty.plugin.JettyWebAppContext.doStart(JettyWebAppContext.java:256)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
|
||||
at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:167)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerWrapper.doStart(HandlerWrapper.java:90)
|
||||
at org.eclipse.jetty.server.Server.doStart(Server.java:263)
|
||||
at org.mortbay.jetty.plugin.JettyServer.doStart(JettyServer.java:65)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.mortbay.jetty.plugin.AbstractJettyMojo.startJetty(AbstractJettyMojo.java:511)
|
||||
at org.mortbay.jetty.plugin.AbstractJettyMojo.execute(AbstractJettyMojo.java:364)
|
||||
at org.mortbay.jetty.plugin.JettyRunMojo.execute(JettyRunMojo.java:516)
|
||||
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
|
||||
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
|
||||
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
|
||||
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
|
||||
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
|
||||
at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
|
||||
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
|
||||
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
|
||||
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
|
||||
at java.lang.reflect.Method.invoke(Method.java:601)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
|
||||
27 Jul 2013 13:44:01,770 ERROR com.sismics.docs.core.util.TransactionUtil.handle(TransactionUtil.java:40) Cannot create entity manager
|
||||
java.lang.NullPointerException
|
||||
at com.sismics.docs.core.util.TransactionUtil.handle(TransactionUtil.java:38)
|
||||
at com.sismics.util.filter.RequestContextFilter.init(RequestContextFilter.java:76)
|
||||
at org.eclipse.jetty.servlet.FilterHolder.doStart(FilterHolder.java:114)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:754)
|
||||
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:258)
|
||||
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1221)
|
||||
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:699)
|
||||
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:454)
|
||||
at org.mortbay.jetty.plugin.JettyWebAppContext.doStart(JettyWebAppContext.java:256)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
|
||||
at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:167)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.eclipse.jetty.server.handler.HandlerWrapper.doStart(HandlerWrapper.java:90)
|
||||
at org.eclipse.jetty.server.Server.doStart(Server.java:263)
|
||||
at org.mortbay.jetty.plugin.JettyServer.doStart(JettyServer.java:65)
|
||||
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
|
||||
at org.mortbay.jetty.plugin.AbstractJettyMojo.startJetty(AbstractJettyMojo.java:511)
|
||||
at org.mortbay.jetty.plugin.AbstractJettyMojo.execute(AbstractJettyMojo.java:364)
|
||||
at org.mortbay.jetty.plugin.JettyRunMojo.execute(JettyRunMojo.java:516)
|
||||
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
|
||||
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
|
||||
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
|
||||
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
|
||||
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
|
||||
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
|
||||
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
|
||||
at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
|
||||
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
|
||||
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
|
||||
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
|
||||
at java.lang.reflect.Method.invoke(Method.java:601)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
|
||||
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
|
||||
27 Jul 2013 13:51:34,136 INFO com.sismics.util.jpa.EMF.getEntityManagerProperties(EMF.java:69) Configuring EntityManager from hibernate.properties
|
||||
27 Jul 2013 13:51:34,600 INFO com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:67) Opening database and executing incremental updates
|
||||
27 Jul 2013 13:51:34,602 INFO com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:95) Unable to get database version: Table T_CONFIG not found
|
||||
27 Jul 2013 13:51:34,602 INFO com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:109) Executing initial schema creation script
|
||||
27 Jul 2013 13:51:48,941 INFO com.sismics.util.jpa.DbOpenHelper.executeAllScript(DbOpenHelper.java:163) Executing script: dbupdate-000-0.sql
|
||||
27 Jul 2013 13:51:48,984 INFO com.sismics.util.jpa.DbOpenHelper.executeAllScript(DbOpenHelper.java:163) Executing script: dbupdate-000-1.sql
|
||||
27 Jul 2013 13:51:48,987 INFO com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:117) Found database version 0, new version is 1, executing database incremental update scripts
|
||||
27 Jul 2013 13:51:50,951 INFO com.sismics.util.jpa.DbOpenHelper.open(DbOpenHelper.java:119) Database upgrade complete
|
||||
27 Jul 2013 13:51:50,951 INFO com.sismics.util.jpa.EMF.getEntityManagerProperties(EMF.java:69) Configuring EntityManager from hibernate.properties
|
||||
27 Jul 2013 13:51:51,824 INFO com.sismics.docs.core.service.IndexingService.startUp(IndexingService.java:53) Using file Lucene storage: C:\Users\Ben\workspace\bgd\docs\trunk\docs-web\src\main\webapp\sismicsdocs\lucene
|
||||
6315
docs-web/src/main/webapp/style/bootstrap.css
vendored
Normal file
6315
docs-web/src/main/webapp/style/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
0
docs-web/src/main/webapp/style/main.less
Normal file
0
docs-web/src/main/webapp/style/main.less
Normal file
0
docs-web/src/main/webapp/style/theme/default.less
Normal file
0
docs-web/src/main/webapp/style/theme/default.less
Normal file
3
docs-web/src/prod/resources/config.properties
Normal file
3
docs-web/src/prod/resources/config.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
api.current_version=${project.version}
|
||||
api.min_version=1.0
|
||||
db.version=1
|
||||
5
docs-web/src/prod/resources/log4j.properties
Normal file
5
docs-web/src/prod/resources/log4j.properties
Normal file
@@ -0,0 +1,5 @@
|
||||
log4j.rootCategory=WARN, MEMORY
|
||||
log4j.appender.MEMORY=com.sismics.util.log4j.MemoryAppender
|
||||
log4j.appender.MEMORY.size=1000
|
||||
|
||||
log4j.logger.com.sismics=INFO
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.sismics.docs.rest;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.codehaus.jettison.json.JSONArray;
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sismics.docs.rest.BaseJerseyTest;
|
||||
import com.sismics.docs.rest.filter.CookieAuthenticationFilter;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.ClientResponse.Status;
|
||||
import com.sun.jersey.api.client.WebResource;
|
||||
|
||||
/**
|
||||
* Test the app resource.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
public class TestAppResource extends BaseJerseyTest {
|
||||
/**
|
||||
* Test the API resource.
|
||||
*
|
||||
* @throws JSONException
|
||||
*/
|
||||
@Test
|
||||
public void testAppResource() throws JSONException {
|
||||
// Check the application info
|
||||
WebResource appResource = resource().path("/app");
|
||||
ClientResponse response = appResource.get(ClientResponse.class);
|
||||
response = appResource.get(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
JSONObject json = response.getEntity(JSONObject.class);
|
||||
String currentVersion = json.getString("current_version");
|
||||
Assert.assertNotNull(currentVersion);
|
||||
String minVersion = json.getString("min_version");
|
||||
Assert.assertNotNull(minVersion);
|
||||
Long freeMemory = json.getLong("free_memory");
|
||||
Assert.assertTrue(freeMemory > 0);
|
||||
Long totalMemory = json.getLong("total_memory");
|
||||
Assert.assertTrue(totalMemory > 0 && totalMemory > freeMemory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the log resource.
|
||||
*
|
||||
* @throws JSONException
|
||||
*/
|
||||
@Test
|
||||
public void testLogResource() throws JSONException {
|
||||
// Login admin
|
||||
String adminAuthenticationToken = clientUtil.login("admin", "admin", false);
|
||||
|
||||
// Check the logs (page 1)
|
||||
WebResource appResource = resource()
|
||||
.path("/app/log")
|
||||
.queryParam("level", "DEBUG");
|
||||
ClientResponse response = appResource.get(ClientResponse.class);
|
||||
appResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
response = appResource.get(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
JSONObject json = response.getEntity(JSONObject.class);
|
||||
JSONArray logs = json.getJSONArray("logs");
|
||||
Assert.assertTrue(logs.length() == 10);
|
||||
Long date1 = logs.optJSONObject(0).optLong("date");
|
||||
Long date2 = logs.optJSONObject(9).optLong("date");
|
||||
Assert.assertTrue(date1 > date2);
|
||||
|
||||
// Check the logs (page 2)
|
||||
appResource = resource()
|
||||
.path("/app/log")
|
||||
.queryParam("offset", "10")
|
||||
.queryParam("level", "DEBUG");
|
||||
response = appResource.get(ClientResponse.class);
|
||||
appResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
response = appResource.get(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
logs = json.getJSONArray("logs");
|
||||
Assert.assertTrue(logs.length() == 10);
|
||||
Long date3 = logs.optJSONObject(0).optLong("date");
|
||||
Long date4 = logs.optJSONObject(9).optLong("date");
|
||||
Assert.assertTrue(date3 > date4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.sismics.docs.rest;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.codehaus.jettison.json.JSONArray;
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sismics.docs.rest.filter.CookieAuthenticationFilter;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.ClientResponse.Status;
|
||||
import com.sun.jersey.api.client.WebResource;
|
||||
import com.sun.jersey.core.util.MultivaluedMapImpl;
|
||||
|
||||
/**
|
||||
* Exhaustive test of the document resource.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class TestDocumentResource extends BaseJerseyTest {
|
||||
/**
|
||||
* Test the document resource.
|
||||
*
|
||||
* @throws JSONException
|
||||
*/
|
||||
@Test
|
||||
public void testDocumentResource() throws JSONException {
|
||||
// Login admin
|
||||
String adminAuthenticationToken = clientUtil.login("admin", "admin", false);
|
||||
|
||||
// Create a document
|
||||
WebResource documentResource = resource().path("/document");
|
||||
documentResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
MultivaluedMapImpl postParams = new MultivaluedMapImpl();
|
||||
postParams.add("title", "My super document 1");
|
||||
postParams.add("description", "My super description for document 1");
|
||||
ClientResponse response = documentResource.put(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
JSONObject json = response.getEntity(JSONObject.class);
|
||||
String document1Id = json.optString("id");
|
||||
Assert.assertNotNull(document1Id);
|
||||
|
||||
// List all documents
|
||||
documentResource = resource().path("/document/list");
|
||||
documentResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
MultivaluedMapImpl getParams = new MultivaluedMapImpl();
|
||||
getParams.putSingle("sort_column", 3);
|
||||
getParams.putSingle("asc", false);
|
||||
response = documentResource.queryParams(getParams).get(ClientResponse.class);
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
JSONArray documents = json.getJSONArray("documents");
|
||||
Assert.assertTrue(documents.length() == 1);
|
||||
Assert.assertEquals(document1Id, documents.getJSONObject(0).getString("id"));
|
||||
|
||||
// Get a document
|
||||
documentResource = resource().path("/document");
|
||||
documentResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
getParams = new MultivaluedMapImpl();
|
||||
getParams.putSingle("id", document1Id);
|
||||
response = documentResource.queryParams(getParams).get(ClientResponse.class);
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
Assert.assertEquals(document1Id, json.getString("id"));
|
||||
|
||||
// Update a document
|
||||
documentResource = resource().path("/document/" + document1Id);
|
||||
documentResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
postParams = new MultivaluedMapImpl();
|
||||
postParams.add("title", "My new super document 1");
|
||||
postParams.add("description", "My new super description for document 1");
|
||||
response = documentResource.post(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("ok", json.getString("status"));
|
||||
|
||||
// Get a document
|
||||
documentResource = resource().path("/document");
|
||||
documentResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
getParams = new MultivaluedMapImpl();
|
||||
getParams.putSingle("id", document1Id);
|
||||
response = documentResource.queryParams(getParams).get(ClientResponse.class);
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
Assert.assertTrue(json.getString("title").contains("new"));
|
||||
Assert.assertTrue(json.getString("description").contains("new"));
|
||||
|
||||
// Deletes a document
|
||||
documentResource = resource().path("/document/" + document1Id);
|
||||
documentResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
response = documentResource.delete(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("ok", json.getString("status"));
|
||||
|
||||
// Get a document (KO)
|
||||
documentResource = resource().path("/document");
|
||||
documentResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
getParams = new MultivaluedMapImpl();
|
||||
getParams.putSingle("id", document1Id);
|
||||
response = documentResource.queryParams(getParams).get(ClientResponse.class);
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.sismics.docs.rest;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.codehaus.jettison.json.JSONArray;
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sismics.docs.rest.filter.CookieAuthenticationFilter;
|
||||
import com.sismics.util.mime.MimeType;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.ClientResponse.Status;
|
||||
import com.sun.jersey.api.client.WebResource;
|
||||
import com.sun.jersey.core.util.MultivaluedMapImpl;
|
||||
import com.sun.jersey.multipart.FormDataBodyPart;
|
||||
import com.sun.jersey.multipart.FormDataMultiPart;
|
||||
|
||||
/**
|
||||
* Exhaustive test of the document resource.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class TestFileResource extends BaseJerseyTest {
|
||||
/**
|
||||
* Test the document resource.
|
||||
*
|
||||
* @throws JSONException
|
||||
*/
|
||||
@Test
|
||||
public void testFileResource() throws JSONException {
|
||||
// Login admin
|
||||
String adminAuthenticationToken = clientUtil.login("admin", "admin", false);
|
||||
|
||||
// Create a document
|
||||
WebResource documentResource = resource().path("/document");
|
||||
documentResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
MultivaluedMapImpl postParams = new MultivaluedMapImpl();
|
||||
postParams.add("title", "File test document 1");
|
||||
ClientResponse response = documentResource.put(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
JSONObject json = response.getEntity(JSONObject.class);
|
||||
String document1Id = json.optString("id");
|
||||
Assert.assertNotNull(document1Id);
|
||||
|
||||
// Add a file
|
||||
WebResource fileResource = resource().path("/file");
|
||||
fileResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
FormDataMultiPart form = new FormDataMultiPart();
|
||||
InputStream file = this.getClass().getResourceAsStream("/file/PIA00452.jpg");
|
||||
FormDataBodyPart fdp = new FormDataBodyPart("file",
|
||||
new BufferedInputStream(file),
|
||||
MediaType.APPLICATION_OCTET_STREAM_TYPE);
|
||||
form.bodyPart(fdp);
|
||||
form.field("id", document1Id);
|
||||
response = fileResource.type(MediaType.MULTIPART_FORM_DATA).put(ClientResponse.class, form);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
String file1Id = json.getString("id");
|
||||
|
||||
// Get a file
|
||||
fileResource = resource().path("/file");
|
||||
fileResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
MultivaluedMapImpl getParams = new MultivaluedMapImpl();
|
||||
getParams.putSingle("id", file1Id);
|
||||
response = fileResource.queryParams(getParams).get(ClientResponse.class);
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
Assert.assertEquals(MimeType.IMAGE_JPEG, json.getString("mimetype"));
|
||||
Assert.assertEquals(file1Id, json.getString("id"));
|
||||
|
||||
// Get all files from a document
|
||||
fileResource = resource().path("/file/list");
|
||||
fileResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
getParams = new MultivaluedMapImpl();
|
||||
getParams.putSingle("id", document1Id);
|
||||
response = fileResource.queryParams(getParams).get(ClientResponse.class);
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
JSONArray files = json.getJSONArray("files");
|
||||
Assert.assertEquals(1, files.length());
|
||||
|
||||
// Deletes a file
|
||||
documentResource = resource().path("/file/" + file1Id);
|
||||
documentResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
response = documentResource.delete(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("ok", json.getString("status"));
|
||||
|
||||
// Get a file (KO)
|
||||
documentResource = resource().path("/file");
|
||||
documentResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
getParams = new MultivaluedMapImpl();
|
||||
getParams.putSingle("id", file1Id);
|
||||
response = documentResource.queryParams(getParams).get(ClientResponse.class);
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.sismics.docs.rest;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.codehaus.jettison.json.JSONArray;
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sismics.docs.rest.BaseJerseyTest;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.ClientResponse.Status;
|
||||
import com.sun.jersey.api.client.WebResource;
|
||||
|
||||
/**
|
||||
* Test the locale resource.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
public class TestLocaleResource extends BaseJerseyTest {
|
||||
/**
|
||||
* Test the locale resource.
|
||||
*
|
||||
* @throws JSONException
|
||||
*/
|
||||
@Test
|
||||
public void testLocaleResource() throws JSONException {
|
||||
WebResource localeResource = resource().path("/locale");
|
||||
ClientResponse response = localeResource.get(ClientResponse.class);
|
||||
response = localeResource.get(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
JSONObject json = response.getEntity(JSONObject.class);
|
||||
JSONArray locale = json.getJSONArray("locales");
|
||||
Assert.assertTrue(locale.length() > 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.sismics.docs.rest;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sismics.docs.rest.BaseJerseyTest;
|
||||
import com.sismics.docs.rest.filter.CookieAuthenticationFilter;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.ClientResponse.Status;
|
||||
import com.sun.jersey.api.client.WebResource;
|
||||
import com.sun.jersey.core.util.MultivaluedMapImpl;
|
||||
|
||||
/**
|
||||
* Test of the security layer.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
public class TestSecurity extends BaseJerseyTest {
|
||||
/**
|
||||
* Test of the security layer.
|
||||
*
|
||||
* @throws JSONException
|
||||
*/
|
||||
@Test
|
||||
public void testSecurity() throws JSONException {
|
||||
// Create a user
|
||||
clientUtil.createUser("testsecurity");
|
||||
|
||||
// Changes a user's email KO : the user is not connected
|
||||
WebResource userResource = resource().path("/user/update");
|
||||
MultivaluedMapImpl postParams = new MultivaluedMapImpl();
|
||||
postParams.add("email", "testsecurity2@docs.com");
|
||||
ClientResponse response = userResource.post(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.FORBIDDEN, Status.fromStatusCode(response.getStatus()));
|
||||
JSONObject json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("ForbiddenError", json.getString("type"));
|
||||
Assert.assertEquals("You don't have access to this resource", json.getString("message"));
|
||||
|
||||
// User testsecurity logs in
|
||||
String testSecurityAuthenticationToken = clientUtil.login("testsecurity");
|
||||
|
||||
// User testsecurity changes his email OK
|
||||
userResource = resource().path("/user");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(testSecurityAuthenticationToken));
|
||||
postParams = new MultivaluedMapImpl();
|
||||
postParams.add("email", "testsecurity2@docs.com");
|
||||
postParams.add("locale", "en");
|
||||
response = userResource.post(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("ok", json.getString("status"));
|
||||
|
||||
// User testsecurity logs out
|
||||
userResource = resource().path("/user/logout");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(testSecurityAuthenticationToken));
|
||||
postParams = new MultivaluedMapImpl();
|
||||
response = userResource.post(ClientResponse.class, postParams);
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
testSecurityAuthenticationToken = clientUtil.getAuthenticationCookie(response);
|
||||
Assert.assertTrue(StringUtils.isEmpty(testSecurityAuthenticationToken));
|
||||
|
||||
// User testsecurity logs out KO : he is not connected anymore
|
||||
userResource = resource().path("/user/logout");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(testSecurityAuthenticationToken));
|
||||
postParams = new MultivaluedMapImpl();
|
||||
response = userResource.post(ClientResponse.class, postParams);
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals(Status.FORBIDDEN, Status.fromStatusCode(response.getStatus()));
|
||||
|
||||
// User testsecurity logs in with a long lived session
|
||||
testSecurityAuthenticationToken = clientUtil.login("testsecurity", "12345678", true);
|
||||
|
||||
// User testsecurity logs out
|
||||
clientUtil.logout(testSecurityAuthenticationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.sismics.docs.rest;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.codehaus.jettison.json.JSONArray;
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sismics.docs.rest.BaseJerseyTest;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.ClientResponse.Status;
|
||||
import com.sun.jersey.api.client.WebResource;
|
||||
|
||||
/**
|
||||
* Test the theme resource.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
public class TestThemeResource extends BaseJerseyTest {
|
||||
/**
|
||||
* Test the theme resource.
|
||||
*
|
||||
* @throws JSONException
|
||||
*/
|
||||
@Test
|
||||
public void testThemeResource() throws JSONException {
|
||||
WebResource themeResource = resource().path("/theme");
|
||||
ClientResponse response = themeResource.get(ClientResponse.class);
|
||||
response = themeResource.get(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
JSONObject json = response.getEntity(JSONObject.class);
|
||||
JSONArray theme = json.getJSONArray("themes");
|
||||
Assert.assertTrue(theme.length() > 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
package com.sismics.docs.rest;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.codehaus.jettison.json.JSONArray;
|
||||
import org.codehaus.jettison.json.JSONException;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sismics.docs.rest.BaseJerseyTest;
|
||||
import com.sismics.docs.rest.filter.CookieAuthenticationFilter;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.ClientResponse.Status;
|
||||
import com.sun.jersey.api.client.WebResource;
|
||||
import com.sun.jersey.core.util.MultivaluedMapImpl;
|
||||
|
||||
/**
|
||||
* Exhaustive test of the user resource.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
public class TestUserResource extends BaseJerseyTest {
|
||||
/**
|
||||
* Test the user resource.
|
||||
*
|
||||
* @throws JSONException
|
||||
*/
|
||||
@Test
|
||||
public void testUserResource() throws JSONException {
|
||||
// Check anonymous user information
|
||||
WebResource userResource = resource().path("/user");
|
||||
ClientResponse response = userResource.acceptLanguage(Locale.US).get(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
JSONObject json = response.getEntity(JSONObject.class);
|
||||
Assert.assertTrue(json.getBoolean("is_default_password"));
|
||||
|
||||
// Create alice user
|
||||
clientUtil.createUser("alice");
|
||||
|
||||
// Login admin
|
||||
String adminAuthenticationToken = clientUtil.login("admin", "admin", false);
|
||||
|
||||
// List all users
|
||||
userResource = resource().path("/user/list");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
MultivaluedMapImpl getParams = new MultivaluedMapImpl();
|
||||
getParams.putSingle("sort_column", 2);
|
||||
getParams.putSingle("asc", false);
|
||||
response = userResource.queryParams(getParams).get(ClientResponse.class);
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
JSONArray users = json.getJSONArray("users");
|
||||
Assert.assertTrue(users.length() > 0);
|
||||
|
||||
// Create a user KO (login length validation)
|
||||
userResource = resource().path("/user");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
MultivaluedMapImpl postParams = new MultivaluedMapImpl();
|
||||
postParams.putSingle("username", " bb ");
|
||||
postParams.putSingle("email", "bob@docs.com");
|
||||
postParams.putSingle("password", "12345678");
|
||||
response = userResource.put(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("ValidationError", json.getString("type"));
|
||||
Assert.assertTrue(json.getString("message"), json.getString("message").contains("more than 3"));
|
||||
|
||||
// Create a user KO (login format validation)
|
||||
userResource = resource().path("/user");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
postParams = new MultivaluedMapImpl();
|
||||
postParams.putSingle("username", "bob-");
|
||||
postParams.putSingle("email", " bob@docs.com ");
|
||||
postParams.putSingle("password", "12345678");
|
||||
response = userResource.put(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("ValidationError", json.getString("type"));
|
||||
Assert.assertTrue(json.getString("message"), json.getString("message").contains("alphanumeric"));
|
||||
|
||||
// Create a user KO (email format validation)
|
||||
userResource = resource().path("/user");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
postParams = new MultivaluedMapImpl();
|
||||
postParams.putSingle("username", "bob");
|
||||
postParams.putSingle("email", " bobdocs.com ");
|
||||
postParams.putSingle("password", "12345678");
|
||||
response = userResource.put(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("ValidationError", json.getString("type"));
|
||||
Assert.assertTrue(json.getString("message"), json.getString("message").contains("must be an email"));
|
||||
|
||||
// Create a user bob OK
|
||||
userResource = resource().path("/user");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
postParams = new MultivaluedMapImpl();
|
||||
postParams.putSingle("username", " bob ");
|
||||
postParams.putSingle("email", " bob@docs.com ");
|
||||
postParams.putSingle("password", " 12345678 ");
|
||||
postParams.putSingle("locale", "fr");
|
||||
response = userResource.put(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
|
||||
// Create a user bob KO : duplicate username
|
||||
response = userResource.put(ClientResponse.class, postParams);
|
||||
Assert.assertNotSame(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("AlreadyExistingUsername", json.getString("type"));
|
||||
|
||||
// Check if a username is free : OK
|
||||
userResource = resource().path("/user/check_username");
|
||||
MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
|
||||
queryParams.add("username", "carol");
|
||||
response = userResource.queryParams(queryParams).get(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
|
||||
// Check if a username is free : KO
|
||||
userResource = resource().path("/user/check_username");
|
||||
queryParams = new MultivaluedMapImpl();
|
||||
queryParams.add("username", "alice");
|
||||
response = userResource.queryParams(queryParams).get(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("ko", json.getString("status"));
|
||||
|
||||
// Login alice with extra whitespaces
|
||||
userResource = resource().path("/user/login");
|
||||
postParams = new MultivaluedMapImpl();
|
||||
postParams.putSingle("username", " alice ");
|
||||
postParams.putSingle("password", " 12345678 ");
|
||||
response = userResource.post(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
String aliceAuthToken = clientUtil.getAuthenticationCookie(response);
|
||||
|
||||
// Login user bob
|
||||
String bobAuthToken = clientUtil.login("bob");
|
||||
|
||||
// Check alice user information
|
||||
userResource = resource().path("/user");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(aliceAuthToken));
|
||||
response = userResource.get(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("alice@docs.com", json.getString("email"));
|
||||
Assert.assertEquals("default.less", json.getString("theme"));
|
||||
Assert.assertFalse(json.getBoolean("first_connection"));
|
||||
Assert.assertFalse(json.getBoolean("is_default_password"));
|
||||
|
||||
// Check bob user information
|
||||
userResource = resource().path("/user");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(bobAuthToken));
|
||||
response = userResource.get(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("bob@docs.com", json.getString("email"));
|
||||
Assert.assertEquals("fr", json.getString("locale"));
|
||||
|
||||
// Test login KO (user not found)
|
||||
userResource = resource().path("/user/login");
|
||||
postParams.putSingle("username", "intruder");
|
||||
postParams.putSingle("password", "12345678");
|
||||
response = userResource.post(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.FORBIDDEN, Status.fromStatusCode(response.getStatus()));
|
||||
|
||||
// Test login KO (wrong password)
|
||||
userResource = resource().path("/user/login");
|
||||
postParams.putSingle("username", "alice");
|
||||
postParams.putSingle("password", "error");
|
||||
response = userResource.post(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.FORBIDDEN, Status.fromStatusCode(response.getStatus()));
|
||||
|
||||
// User alice updates her information + changes her email
|
||||
userResource = resource().path("/user");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(aliceAuthToken));
|
||||
postParams = new MultivaluedMapImpl();
|
||||
postParams.add("email", " alice2@docs.com ");
|
||||
postParams.add("theme", " default.less ");
|
||||
postParams.add("locale", " en ");
|
||||
response = userResource.post(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("ok", json.getString("status"));
|
||||
|
||||
// Check the update
|
||||
userResource = resource().path("/user");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(aliceAuthToken));
|
||||
response = userResource.get(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("alice2@docs.com", json.getString("email"));
|
||||
|
||||
// Delete user alice
|
||||
userResource = resource().path("/user");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(aliceAuthToken));
|
||||
response = userResource.delete(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
|
||||
// Check the deletion
|
||||
userResource = resource().path("/user/login");
|
||||
postParams.putSingle("username", "alice");
|
||||
postParams.putSingle("password", "12345678");
|
||||
response = userResource.post(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.FORBIDDEN, Status.fromStatusCode(response.getStatus()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the user resource admin functions.
|
||||
*
|
||||
* @throws JSONException
|
||||
*/
|
||||
@Test
|
||||
public void testUserResourceAdmin() throws JSONException {
|
||||
// Create admin_user1 user
|
||||
clientUtil.createUser("admin_user1");
|
||||
|
||||
// Login admin
|
||||
String adminAuthenticationToken = clientUtil.login("admin", "admin", false);
|
||||
|
||||
// Check admin information
|
||||
WebResource userResource = resource().path("/user");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
ClientResponse response = userResource.get(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
JSONObject json = response.getEntity(JSONObject.class);
|
||||
Assert.assertTrue(json.getBoolean("first_connection"));
|
||||
Assert.assertTrue(json.getBoolean("is_default_password"));
|
||||
|
||||
// User admin updates his information
|
||||
userResource = resource().path("/user");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
MultivaluedMapImpl postParams = new MultivaluedMapImpl();
|
||||
postParams.add("first_connection", false);
|
||||
response = userResource.post(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("ok", json.getString("status"));
|
||||
|
||||
// Check admin information update
|
||||
userResource = resource().path("/user");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
response = userResource.get(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertFalse(json.getBoolean("first_connection"));
|
||||
|
||||
// User admin update admin_user1 information
|
||||
userResource = resource().path("/user");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
postParams = new MultivaluedMapImpl();
|
||||
postParams.add("email", " alice2@docs.com ");
|
||||
postParams.add("theme", " default.less");
|
||||
postParams.add("locale", " en ");
|
||||
response = userResource.post(ClientResponse.class, postParams);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("ok", json.getString("status"));
|
||||
|
||||
// User admin deletes user admin_user1
|
||||
userResource = resource().path("/user/admin_user1");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
response = userResource.delete(ClientResponse.class);
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("ok", json.getString("status"));
|
||||
|
||||
// User admin deletes user admin_user1 : KO (user doesn't exist)
|
||||
userResource = resource().path("/user/admin_user1");
|
||||
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||
response = userResource.delete(ClientResponse.class);
|
||||
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.getEntity(JSONObject.class);
|
||||
Assert.assertEquals("UserNotFound", json.getString("type"));
|
||||
}
|
||||
}
|
||||
BIN
docs-web/src/test/resources/file/PIA00452.jpg
Normal file
BIN
docs-web/src/test/resources/file/PIA00452.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 160 KiB |
10
docs-web/src/test/resources/hibernate.properties
Normal file
10
docs-web/src/test/resources/hibernate.properties
Normal file
@@ -0,0 +1,10 @@
|
||||
hibernate.connection.driver_class=org.hsqldb.jdbcDriver
|
||||
hibernate.connection.url=jdbc:hsqldb:mem:docs;get_column_name=false
|
||||
hibernate.connection.username=sa
|
||||
hibernate.connection.password=
|
||||
hibernate.hbm2ddl.auto=none
|
||||
hibernate.dialect=org.hibernate.dialect.HSQLDialect
|
||||
hibernate.show_sql=true
|
||||
hibernate.format_sql=false
|
||||
hibernate.max_fetch_depth=5
|
||||
hibernate.cache.use_second_level_cache=false
|
||||
9
docs-web/src/test/resources/log4j.properties
Normal file
9
docs-web/src/test/resources/log4j.properties
Normal file
@@ -0,0 +1,9 @@
|
||||
log4j.rootCategory=DEBUG, CONSOLE, MEMORY
|
||||
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.CONSOLE.layout.ConversionPattern=%d{DATE} %p %l %m %n
|
||||
log4j.appender.MEMORY=com.sismics.util.log4j.MemoryAppender
|
||||
log4j.appender.MEMORY.size=1000
|
||||
|
||||
log4j.logger.com.sismics=DEBUG
|
||||
log4j.logger.org.hibernate.internal.util.EntityPrinter=INFO
|
||||
Reference in New Issue
Block a user