Wao-commits
Threads by month
- ----- 2026 -----
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
March 2014
- 3 participants
- 90 discussions
20 Mar '14
Author: bleny
Date: 2014-03-20 17:53:39 +0100 (Thu, 20 Mar 2014)
New Revision: 1733
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1733
Log:
fixes #4496 user login and logout
Added:
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/InactiveWaoUserException.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/NoRoleAttributedException.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WrongCredentialsException.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/IndexAction.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/authentication/LogoutAction.java
Modified:
trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/ObsProgram.java
trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/UserProfileImpl.java
trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/UserRole.java
trunk/wao-persistence/src/main/xmi/wao-model.zargo
trunk/wao-services/src/main/java/fr/ifremer/wao/services/AuthenticatedWaoUser.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoInterceptor.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/authentication/LoginAction.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/SamplingPlanAction.java
trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties
trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties
trunk/wao-web/src/main/resources/struts.xml
trunk/wao-web/src/main/webapp/WEB-INF/content/authentication/login.jsp
trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/sampling-plan.jsp
trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout-login.jsp
trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout.jsp
trunk/wao-web/src/main/webapp/css/wao.css
Modified: trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/ObsProgram.java
===================================================================
--- trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/ObsProgram.java 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/ObsProgram.java 2014-03-20 16:53:39 UTC (rev 1733)
@@ -62,4 +62,7 @@
return OBSDEB == this;
}
+ public String getI18nKey() {
+ return i18nKey;
+ }
}
Modified: trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/UserProfileImpl.java
===================================================================
--- trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/UserProfileImpl.java 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/UserProfileImpl.java 2014-03-20 16:53:39 UTC (rev 1733)
@@ -20,22 +20,10 @@
*/
package fr.ifremer.wao.entity;
-import java.util.UUID;
+import com.google.common.base.Objects;
public class UserProfileImpl extends UserProfileAbstract {
- public UserProfileImpl() {
- String newToken = UUID.randomUUID().toString();
- setToken(newToken);
- }
-
- public UserProfileImpl(ObsProgram obsProgram, UserRole userRole, boolean canWrite) {
- this();
- setObsProgram(obsProgram);
- setUserRole(userRole);
- setCanWrite(canWrite);
- }
-
@Override
public UserRole getUserRole() {
return UserRole.valueOf(getUserRoleOrdinal());
@@ -105,36 +93,11 @@
return getObsProgram() == ObsProgram.OBSVENTE;
}
- @Override
- public String getDescription() {
- String role;
- if (getUserRoleOrdinal() == null) {
- role = "<no user role>";
- } else {
- role = getUserRole().getTranslation();
- }
- String program;
- if (getObsProgramOrdinal() == null) {
- program = "<no program>";
- } else {
- program = getObsProgram().toString();
- }
- String readOnly = "";
- if (getCanWrite() == null) {
- program = "<no write access specified>";
- } else if (isReadOnly()) {
- // readOnly = WaoUtils._("wao.business.readOnly");
- throw new UnsupportedOperationException();
- }
-
-// String result = WaoUtils._("wao.business.entity.UserProfile.description",
-// role, program, readOnly);
- throw new UnsupportedOperationException();
-
-// return result;
- }
-
public String toString() {
- return getDescription() + " " + getTopiaId();
+ String toString = Objects.toStringHelper(this)
+ .add("obsProgram", getObsProgram())
+ .add("userRole", getUserRole())
+ .add("readOnly", isReadOnly()).toString();
+ return toString;
}
}
\ No newline at end of file
Modified: trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/UserRole.java
===================================================================
--- trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/UserRole.java 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/UserRole.java 2014-03-20 16:53:39 UTC (rev 1733)
@@ -66,13 +66,8 @@
throw new IllegalArgumentException("ordinal " + userRoleOrdinal);
}
- // bleny 2011-01-26 calling this method toString make UI unstable.
- // In Administration form, when choosing a role, changing the form value lead to
- // an Enum.valueOf exception. It seems that Tapestry use toString()
- // instead of name() to know witch value is chosen
- public String getTranslation() {
- throw new UnsupportedOperationException();
- // return WaoUtils.t(i18nKey);
+ public String getI18nKey() {
+ return i18nKey;
}
/** for a given program, return the roles that can be assigned by someone
Modified: trunk/wao-persistence/src/main/xmi/wao-model.zargo
===================================================================
(Binary files differ)
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/AuthenticatedWaoUser.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/AuthenticatedWaoUser.java 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/AuthenticatedWaoUser.java 2014-03-20 16:53:39 UTC (rev 1733)
@@ -44,7 +44,7 @@
}
public void logout() {
- getCurrentUser().logout();
+ // TODO brendan 20/03/14 getCurrentUser().logout();
}
public boolean isAdmin() {
Added: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/InactiveWaoUserException.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/InactiveWaoUserException.java (rev 0)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/InactiveWaoUserException.java 2014-03-20 16:53:39 UTC (rev 1733)
@@ -0,0 +1,17 @@
+package fr.ifremer.wao.services.service.administration;
+
+import fr.ifremer.wao.WaoException;
+import fr.ifremer.wao.entity.WaoUser;
+
+public class InactiveWaoUserException extends WaoException {
+
+ protected WaoUser waoUser;
+
+ public InactiveWaoUserException(WaoUser waoUser) {
+ this.waoUser = waoUser;
+ }
+
+ public WaoUser getWaoUser() {
+ return waoUser;
+ }
+}
Added: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/NoRoleAttributedException.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/NoRoleAttributedException.java (rev 0)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/NoRoleAttributedException.java 2014-03-20 16:53:39 UTC (rev 1733)
@@ -0,0 +1,17 @@
+package fr.ifremer.wao.services.service.administration;
+
+import fr.ifremer.wao.WaoException;
+import fr.ifremer.wao.entity.WaoUser;
+
+public class NoRoleAttributedException extends WaoException {
+
+ protected WaoUser waoUser;
+
+ public NoRoleAttributedException(WaoUser waoUser) {
+ this.waoUser = waoUser;
+ }
+
+ public WaoUser getWaoUser() {
+ return waoUser;
+ }
+}
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java 2014-03-20 16:53:39 UTC (rev 1733)
@@ -18,6 +18,8 @@
import fr.ifremer.wao.services.service.mail.UserCredentialsEmail;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.nuiton.topia.persistence.TopiaEntities;
import org.nuiton.topia.persistence.TopiaEntity;
import org.nuiton.topia.persistence.TopiaQueryBuilderAddCriteriaOrRunQueryStep;
@@ -28,6 +30,8 @@
public class WaoUsersService extends WaoServiceSupport {
+ private static final Log log = LogFactory.getLog(WaoUsersService.class);
+
public List<WaoUser> getWaoUsers(Optional<String> optionalCompanyId) {
WaoUserTopiaDao dao = getWaoUserDao();
@@ -207,4 +211,53 @@
}
+ public WaoUser authenticate(String login, String clearPassword)
+ throws WrongCredentialsException, NoRoleAttributedException, InactiveWaoUserException {
+
+ WaoUserTopiaDao dao = getWaoUserDao();
+
+ Optional<WaoUser> optionalWaoUser = dao.forLoginEquals(login).tryFindUnique();
+
+ WaoUser waoUser;
+
+ if ( ! optionalWaoUser.isPresent()) {
+ if (log.isInfoEnabled()) {
+ log.info("no such user " + login);
+ }
+ throw new WrongCredentialsException();
+ }
+
+ waoUser = optionalWaoUser.get();
+ String hashedPassword = StringUtil.encodeMD5(clearPassword);
+
+ if ( ! waoUser.getPassword().equals(hashedPassword)) {
+ if (log.isWarnEnabled()) {
+ log.warn("wrong credentials provided for " + login);
+ }
+ throw new WrongCredentialsException();
+ }
+
+ if ( ! waoUser.isActive()) {
+ throw new InactiveWaoUserException(waoUser);
+ }
+
+ if (waoUser.isUserProfileEmpty()) {
+ throw new NoRoleAttributedException(waoUser);
+ }
+
+ waoUser.sizeUserProfile();
+ waoUser.sizeCanReadBoats();
+ waoUser.getCompany().getTopiaId();
+
+ return waoUser;
+
+ }
+
+ public void acceptCgu(WaoUser waoUser) {
+
+ waoUser.setCguAccepted(serviceContext.getNow());
+
+ commit();
+
+ }
}
Added: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WrongCredentialsException.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WrongCredentialsException.java (rev 0)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WrongCredentialsException.java 2014-03-20 16:53:39 UTC (rev 1733)
@@ -0,0 +1,7 @@
+package fr.ifremer.wao.services.service.administration;
+
+import fr.ifremer.wao.WaoException;
+
+public class WrongCredentialsException extends WaoException {
+
+}
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoInterceptor.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoInterceptor.java 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoInterceptor.java 2014-03-20 16:53:39 UTC (rev 1733)
@@ -1,6 +1,7 @@
package fr.ifremer.wao.web;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
import fr.ifremer.wao.WaoApplicationConfig;
@@ -13,6 +14,7 @@
import fr.ifremer.wao.services.AuthenticatedWaoUser;
import fr.ifremer.wao.services.WaoService;
import fr.ifremer.wao.services.WaoServiceContext;
+import fr.ifremer.wao.web.action.authentication.LoginAction;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -26,6 +28,9 @@
private static final Log log = LogFactory.getLog(WaoInterceptor.class);
+ protected static final ImmutableSet<Class<LoginAction>> ACTIONS_ACCESSIBLE_WITHOUT_LOGIN =
+ ImmutableSet.of(LoginAction.class);
+
public static final String SESSION_LAST_LOCATION = "lastLocation";
@Override
@@ -53,17 +58,26 @@
WaoSession waoSession = getWaoSession(invocation);
if (waoSession.getAuthenticatedWaoUser() == null) {
- // login à l'arrache
- WaoUser admin = serviceContext.getPersistenceContext().getWaoUserDao().forLoginEquals("admin").findUnique();
- admin.sizeUserProfile();
- admin.sizeCanReadBoats();
- admin.getCompany().getTopiaId();
- UserProfile userProfile = new UserProfileImpl();
- userProfile.setUserRole(UserRole.ADMIN);
- userProfile.setObsProgram(ObsProgram.OBSMER);
- userProfile.setCanWrite(true);
- AuthenticatedWaoUser authenticatedWaoUser = new AuthenticatedWaoUser(admin, userProfile);
- waoSession.setAuthenticatedWaoUser(authenticatedWaoUser);
+
+ boolean loginCheat = false;
+ if (loginCheat) {
+ // login à l'arrache
+ WaoUser admin = serviceContext.getPersistenceContext().getWaoUserDao().forLoginEquals("admin").findUnique();
+ admin.sizeUserProfile();
+ admin.sizeCanReadBoats();
+ admin.getCompany().getTopiaId();
+ UserProfile userProfile = new UserProfileImpl();
+ userProfile.setUserRole(UserRole.ADMIN);
+ userProfile.setObsProgram(ObsProgram.OBSMER);
+ userProfile.setCanWrite(true);
+ AuthenticatedWaoUser authenticatedWaoUser = new AuthenticatedWaoUser(admin, userProfile);
+ waoSession.setAuthenticatedWaoUser(authenticatedWaoUser);
+ } else {
+ if ( ! ACTIONS_ACCESSIBLE_WITHOUT_LOGIN.contains(waoAction.getClass())) {
+ return "redirect-to-login";
+ }
+ }
+
}
if (CollectionUtils.isNotEmpty(waoSession.getMessages())) {
Added: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/IndexAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/IndexAction.java (rev 0)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/IndexAction.java 2014-03-20 16:53:39 UTC (rev 1733)
@@ -0,0 +1,31 @@
+package fr.ifremer.wao.web.action;
+
+import fr.ifremer.wao.entity.ObsProgram;
+import fr.ifremer.wao.web.WaoJspActionSupport;
+import org.apache.struts2.convention.annotation.Result;
+import org.apache.struts2.convention.annotation.Results;
+
+@Results({
+ @Result(name="error", type="redirectAction", params = { "namespace", "/authentication", "actionName", "login!input" }),
+ @Result(name="success", type="redirectAction", params = { "namespace", "/${obsProgram.name().toLowerCase()}", "actionName", "news" })
+})
+public class IndexAction extends WaoJspActionSupport {
+
+ protected ObsProgram obsProgram;
+
+ @Override
+ public String execute() {
+
+ if (getSession().getAuthenticatedWaoUser() == null) {
+ return ERROR;
+ } else {
+ obsProgram = getSession().getAuthenticatedWaoUser().getObsProgram();
+ return SUCCESS;
+ }
+
+ }
+
+ public ObsProgram getObsProgram() {
+ return obsProgram;
+ }
+}
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/authentication/LoginAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/authentication/LoginAction.java 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/authentication/LoginAction.java 2014-03-20 16:53:39 UTC (rev 1733)
@@ -1,12 +1,157 @@
package fr.ifremer.wao.web.action.authentication;
+import com.google.common.collect.Iterables;
+import fr.ifremer.wao.entity.ObsProgram;
+import fr.ifremer.wao.entity.UserProfile;
+import fr.ifremer.wao.entity.WaoUser;
+import fr.ifremer.wao.services.AuthenticatedWaoUser;
+import fr.ifremer.wao.services.service.administration.InactiveWaoUserException;
+import fr.ifremer.wao.services.service.administration.NoRoleAttributedException;
+import fr.ifremer.wao.services.service.administration.WaoUsersService;
+import fr.ifremer.wao.services.service.administration.WrongCredentialsException;
import fr.ifremer.wao.web.WaoJspActionSupport;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.struts2.convention.annotation.Result;
+import org.apache.struts2.convention.annotation.Results;
+import java.util.HashMap;
+import java.util.Map;
+
+@Results({
+ @Result(name="success", type="redirectAction", params = { "namespace", "/${obsProgram.name().toLowerCase()}", "actionName", "news" })
+})
public class LoginAction extends WaoJspActionSupport {
+ protected WaoUsersService service;
+
+ protected String login;
+
+ protected String password;
+
+ protected String userProfileId;
+
+ protected boolean userProfileAsked;
+
+ protected Map<String, String> userProfiles;
+
+ protected boolean acceptCguAsked;
+
+ protected boolean cguAccepted;
+
+ protected ObsProgram obsProgram;
+
+ protected WaoUser waoUser;
+
+ public void setLogin(String login) {
+ this.login = login;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public void setService(WaoUsersService service) {
+ this.service = service;
+ }
+
+ public void setUserProfileId(String userProfileId) {
+ this.userProfileId = userProfileId;
+ }
+
+ public void setCguAccepted(boolean cguAccepted) {
+ this.cguAccepted = cguAccepted;
+ }
+
@Override
+ public void validate() {
+
+ try {
+
+ waoUser = service.authenticate(login, password);
+
+ if (cguAccepted) {
+ service.acceptCgu(waoUser);
+ }
+
+ if (waoUser.getCguAccepted() == null) {
+ acceptCguAsked = true;
+ addFieldError("cguAccepted", t("wao.ui.form.authentication.error.userMustAcceptCgu"));
+ }
+
+ if (waoUser.getUserProfile().size() == 1) {
+ userProfileId = Iterables.getOnlyElement(waoUser.getUserProfile()).getTopiaId();
+ } else {
+ userProfileAsked = true;
+ userProfiles = new HashMap<>();
+ for (UserProfile userProfile : waoUser.getUserProfile()) {
+ String label = t(userProfile.getUserRole().getI18nKey());
+ label += " " + t(userProfile.getObsProgram().getI18nKey());
+ if (BooleanUtils.isTrue(userProfile.isReadOnly())) {
+ label += " (" + t("wao.ui.form.readOnly") + ")";
+ }
+ userProfiles.put(userProfile.getTopiaId(), label);
+ }
+ }
+
+ } catch (WrongCredentialsException e) {
+ addFieldError("login", t("wao.ui.form.authentication.error.wrongCredentials"));
+ } catch (NoRoleAttributedException e) {
+ addFieldError("login", t("wao.ui.form.authentication.error.noRoleAttributed"));
+ } catch (InactiveWaoUserException e) {
+ addFieldError("login", t("wao.ui.form.authentication.error.inactiveWaoUser"));
+ }
+
+ }
+
+ @Override
public String execute() {
+
+ if (StringUtils.isNotBlank(userProfileId)) {
+
+ UserProfile userProfile = waoUser.getUserProfileByTopiaId(userProfileId);
+ AuthenticatedWaoUser authenticatedWaoUser = new AuthenticatedWaoUser(waoUser, userProfile);
+ session.setAuthenticatedWaoUser(authenticatedWaoUser);
+
+ obsProgram = userProfile.getObsProgram();
+
+ return SUCCESS;
+
+ }
+
return INPUT;
+
}
+ public String getPassword() {
+ return password;
+ }
+
+ public String getLogin() {
+ return login;
+ }
+
+ public String getUserProfileId() {
+ return userProfileId;
+ }
+
+ public boolean isUserProfileAsked() {
+ return userProfileAsked;
+ }
+
+ public ObsProgram getObsProgram() {
+ return obsProgram;
+ }
+
+ public boolean isAcceptCguAsked() {
+ return acceptCguAsked;
+ }
+
+ public boolean isCguAccepted() {
+ return cguAccepted;
+ }
+
+ public Map<String, String> getUserProfiles() {
+ return userProfiles;
+ }
}
Added: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/authentication/LogoutAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/authentication/LogoutAction.java (rev 0)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/authentication/LogoutAction.java 2014-03-20 16:53:39 UTC (rev 1733)
@@ -0,0 +1,20 @@
+package fr.ifremer.wao.web.action.authentication;
+
+import fr.ifremer.wao.web.WaoJspActionSupport;
+import org.apache.struts2.convention.annotation.Result;
+import org.apache.struts2.convention.annotation.Results;
+
+@Results({
+ @Result(name="success", type="redirectAction", params = { "actionName", "login!input" })
+})
+public class LogoutAction extends WaoJspActionSupport {
+
+ @Override
+ public String execute() {
+
+ session.logout();
+
+ return SUCCESS;
+
+ }
+}
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/SamplingPlanAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/SamplingPlanAction.java 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/SamplingPlanAction.java 2014-03-20 16:53:39 UTC (rev 1733)
@@ -22,9 +22,6 @@
SampleRowsFilter filter = service.newSampleRowsFilter(getAuthenticatedWaoUser());
- filter.setPeriodFrom(null);
- filter.setPeriodTo(null);
-
samplingPlan = service.getSamplingPlan(filter);
return SUCCESS;
Modified: trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties
===================================================================
--- trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties 2014-03-20 16:53:39 UTC (rev 1733)
@@ -234,6 +234,14 @@
wao.ui.form.addComment=Add a comment
wao.ui.form.addComment.success=Comment saved
wao.ui.form.addRole=Add this ru00F4le
+wao.ui.form.authentication.acceptCgu=
+wao.ui.form.authentication.action.submit=Login
+wao.ui.form.authentication.chooseUserProfile=
+wao.ui.form.authentication.error.inactiveWaoUser=This wao user is not active. Please, contact an administrator
+wao.ui.form.authentication.error.noRoleAttributed=This wao user has no role affected. Please, contact an administrator
+wao.ui.form.authentication.error.userMustAcceptCgu=You must accept the end-user agreement
+wao.ui.form.authentication.error.wrongCredentials=Incorrect credentials
+wao.ui.form.authentication.title=WAO authentication
wao.ui.form.boardingFrom=Boardings since
wao.ui.form.contactsFile=Contacts file
wao.ui.form.definePasswordManually=Define password manually
Modified: trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties
===================================================================
--- trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties 2014-03-20 16:53:39 UTC (rev 1733)
@@ -234,6 +234,14 @@
wao.ui.form.addComment=Ajouter un commentaire
wao.ui.form.addComment.success=Commentaire enregistré
wao.ui.form.addRole=Ajouter ce rôle
+wao.ui.form.authentication.acceptCgu=Vous acceptez les CGU
+wao.ui.form.authentication.action.submit=S'identifier
+wao.ui.form.authentication.chooseUserProfile=Rôle
+wao.ui.form.authentication.error.inactiveWaoUser=Ce compte est inactif, veuillez contacter un responsable
+wao.ui.form.authentication.error.noRoleAttributed=Aucun rôle n'est attribué à ce compte, veuillez contacter un responsable
+wao.ui.form.authentication.error.userMustAcceptCgu=Vous devez accepter les conditions générales d'utilisation
+wao.ui.form.authentication.error.wrongCredentials=Les informations d'authenfication ne sont pas correctes
+wao.ui.form.authentication.title=Identification WAO
wao.ui.form.boardingFrom=Sollicitations du navire depuis le
wao.ui.form.contactsFile=Fichier des contacts
wao.ui.form.definePasswordManually=définir manuellement
Modified: trunk/wao-web/src/main/resources/struts.xml
===================================================================
--- trunk/wao-web/src/main/resources/struts.xml 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-web/src/main/resources/struts.xml 2014-03-20 16:53:39 UTC (rev 1733)
@@ -52,6 +52,12 @@
<default-interceptor-ref name="waoStack"/>
+ <global-results>
+ <result name="redirect-to-login" type="redirect">
+ /authentication/login!input
+ </result>
+ </global-results>
+
</package>
Modified: trunk/wao-web/src/main/webapp/WEB-INF/content/authentication/login.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/authentication/login.jsp 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/authentication/login.jsp 2014-03-20 16:53:39 UTC (rev 1733)
@@ -1,28 +1,90 @@
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
-<!DOCTYPE html>
-<%@taglib uri="/struts-tags" prefix="s" %>
-<%@taglib prefix="sj" uri="/struts-jquery-tags" %>
+<%@taglib prefix="s" uri="/struts-tags" %>
+<%@taglib prefix="sj" uri="/struts-jquery-tags" %>
+
<html>
<head>
- <title>Identification</title>
+ <title>
+ <s:text name="wao.ui.form.authentication.title" />
+ </title>
</head>
<body>
- <p>Veuillez entrer vos identifiants pour accéder à votre espace</p>
+ <h1>
+ <s:text name="wao.ui.form.authentication.title" />
+ </h1>
- <s:form action="login" namespace="/authentication" method="post" cssClass="form-horizontal">
+ <s:form action="login" namespace="/authentication" method="POST" cssClass="form-horizontal">
- <s:textfield name="email"
- label="Votre adresse de courrier électronique :"/>
+ <fieldset>
- <s:password name="password"
- label="Votre mot de passe :"/>
+ <s:textfield name="login"
+ label="%{getText('wao.ui.field.WaoUser.login')}" />
- <sj:a openDialog="forgottenPasswordForm" cssClass="controls">J'ai oublié mon mot de passe</sj:a>
+ <s:password name="password"
+ label="%{getText('wao.ui.field.WaoUser.password')}" />
- <s:submit value="S'identifier" type='button' cssClass="btn btn-primary"/>
+ <s:if test="userProfileAsked">
+ <s:radio name="userProfileId" list="userProfiles" label="%{getText('wao.ui.form.authentication.chooseUserProfile')}" />
+
+ </s:if>
+
+ <s:if test="acceptCguAsked">
+
+ <s:checkbox name="cguAccepted" label="%{getText('wao.ui.form.authentication.acceptCgu')}" />
+
+ </s:if>
+
+ </fieldset>
+
+ <s:submit type="button" cssClass="btn btn-primary">
+ <s:text name="wao.ui.form.authentication.action.submit" />
+ </s:submit>
+
</s:form>
+ <s:if test="acceptCguAsked">
+
+ <article id="cgu">
+ <h2>CONDITIONS D’UTILISATION DU PORTAIL WAO (web applicatif obsmer)</h2>
+ <p>La base de données WAO est accessible via l’adresse internet suivante : http://www.ifremer.fr/wao/ (le « Portail »).</p>
+ <p>Les données et informations qui composent le Portail, quelle que soit leur forme, et notamment mais non exclusivement, les données nominatives et techniques, (ci-après communément et indistinctement désignées sous le terme « Données»), sont soumises aux présentes conditions d’utilisation.</p>
+ <p>Le titulaire (le « Titulaire ») du log in et du mot de passe accepte expressément et sans réserve les présentes conditions d’utilisation par le simple fait d’utiliser le log in et le mot de passe et d’accéder aux Données.</p>
+ <p>Le log in et le mot de passe sont strictement personnels et confidentiels. Ils ne peuvent être communiqués à un tiers sans l’accord préalable, exprès et écrit de la direction des pêches maritimes et de l’aquaculture (DPMA) et de l’Institut français de recherche pour l’exploitation de la mer (IFREMER). Sauf accord préalable et exprès de la DPMA et de l’IFREMER, seul le Titulaire est fondé à accéder aux Données, à l’exclusion de toute autre personne..</p>
+ <h3>ARTICLE 1 – PROPRIETE DES DONNEES</h3>
+ <p>Les Données sont la propriété exclusive de la DPMA et de l’IFREMER.</p>
+ <p>La divulgation des Données au Titulaire ne saurait, en aucun cas, être interprétée comme lui conférant, de manière expresse ou implicite un droit quelconque sur ces Donnéesappartenant conjointement à la DPMA et/ou à l’IFREMER.</p>
+ <h3>ARTICLE 2 – CONDITION D’UTILISATION DES DONNEES</h3>
+ <p>Les Données ne pourront être consultées et utilisées que dans le cadre du programme Obsmer (le « Programme Obsmer ») et dans les strictes limites suivantes :</p>
+ <p>Appui des observateurs des bureaux d’études mandatés par le MAAPRAT auprès des professionnels susceptibles d’embarquer un observateur scientifique</p>
+ <p>Appui auprès des professionnels pour la mise en conformité vis-à-vis des obligations administratives nécessaires pour l’embarquement d’un observateur scientifique (note de service DPMA/DAM 2307 du 20 octobre 2008)</p>
+ <p>Les Données consultables par le Titulaire sont strictement dédiées au Programme Obsmer de sorte qu’en dehors de la DPMA, de l’IFREMER, des bureaux d’étude en charge de la réalisation du Programme Obsmer et du Titulaire, aucune autre personne n’a accès aux Données.</p>
+ <p>Par conséquent, en cas de constat d’une utilisation des Données dans un domaine autre que le Programme Obsmer, la DPMA et l’IFREMER se réservent le droit de suspendre, sans formalité préalable, le log in et le mot de passe du Titulaire.</p>
+ <p>Cette suspension est non exclusive d’éventuelles actions destinées à réparer le préjudice subi par la DPMA et l’IFREMER.</p>
+ <p>Les Parties conviennent d’ores et déjà qu’en cas de contestation ou de litige, les données de connexion feront foi.</p>
+ <h3>ARTICLE 3 - INTERDICTION D’EXTRACTION OU DE DUPPLICATION DES DONNEES EN DEHORS DU PROGRAMME OBSMER</h3>
+ <p>Sauf dans le cadre du Programme Obsmer et dans les conditions de l’article 2 des présentes conditions d’utilisation des Données, le Titulaire ne pourra en aucun cas procéder à une extraction ou duplication des Données sans autorisation écrite et préalable de la DPMA et de l’IFREMER.</p>
+ <p>Le Titulaire s’interdit également de communiquer, revendre tout ou partie des Données auxquelles il a accès via le Portail.</p>
+ <p>A ce titre, le Titulaire ne procédera pas, notamment, à des copier / coller et / ou toute autre action ayant pour effet de permettre l’extraction ou la duplication de tout ou partie des Données.</p>
+ <h3>ARTICLE 4 – PERTE DU LOG IN ET DU MOT DE PASSE</h3>
+ <p>En cas de perte du log in et / ou du mot de passe, le Titulaire s’engage à prévenir la DPMA et l’IFREMER dans les plus brefs délais afin que ceux-ci soient désactivés. Il sera attribué au Titulaire un nouveau log in et un nouveau mot de passe.</p>
+ <h3>ARTICLE 5 - INFORMATIQUES ET LIBERTES</h3>
+ <p>Certaines des Données sont des données à caractère personnel ayant fait l’objet d’une déclaration auprès de la Commission National Informatique et Libertés.</p>
+ <p>Le Titulaire consulte et utilise les Données dans le respect de la loi n°78-17 du 6 janvier 1978 modifiée dite loi Informatique et Libertés.</p>
+ <h3>ARTICLE 6 - CONFIDENTIALITE</h3>
+ <p>Le Titulaire gardera confidentiel les Données qu’il consultera.</p>
+ <p>Le Titulaire traitera les Données avec le même degré de précaution et de protection que celui qu’il accorde à ses propres informations confidentielles de même importance.</p>
+ <p>Le Titulaire conservera de manière confidentielle, pour une durée de trente (30) ans à compter de la dernière consultation du Portail, les Données disponibles depuis la base de données WAO et s'abstiendra de les divulguer sauf à des employés ou à des préposés responsables qui, astreints au secret professionnel, en auront besoin pour l’accomplissement de leur mission au titre du Programme Obsmer.</p>
+ <p>Le Titulaire informera par écrit la DPMA et l’IFREMER de toute perte de document ou d’objet contenant des Données.</p>
+ <p>La DPMA et l’IFREMER pourront exiger, à tout moment et sous quelque forme que ce soit, que tous les documents portant des Données soient impérativement restitués ou détruits par le Titulaire.</p>
+ <h3>ARTICLE 7 - LIMITATION DE RESPONSABILITE</h3>
+ <p>La DPMA et l’IFREMER ne garantissent en rien que les Données seront exempts d’erreurs. Par conséquent, la DPMA et l’IFREMER déclinent toute responsabilité quant à l’utilisation qui en est faite et qui s’effectue sous la seule, pleine et entière responsabilité du Titulaire.</p>
+ <p>Par conséquent, le Titulaire ne pourra en aucun cas mettre en cause la responsabilité de la DPMA et de l’IFREMER en raison d’un préjudice subi par le Titulaire du fait de l’utilisation des Données, quel qu’il soit et quel qu’en soit la cause, que le préjudice ait été prévisible ou non.</p>
+ <p>En outre, la DPMA et l’IFREMER déclinent toute responsabilité sur l’utilisation du site internet et ne garantissent en rien que le site soit exempt de virus, bugs…</p>
+ </article>
+
+ </s:if>
+
</body>
</html>
Modified: trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/sampling-plan.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/sampling-plan.jsp 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/sampling-plan.jsp 2014-03-20 16:53:39 UTC (rev 1733)
@@ -18,6 +18,13 @@
<i class="icon-download"></i> <s:text name="wao.ui.action.csvExport" />
</s:a>
+ <s:if test="authenticatedWaoUser.authorizedToEditSamplingPlan">
+ <s:url action="edit-sample-row!input" id="createSampleRow" />
+ <s:a href="%{createSampleRow}" cssClass="btn">
+ <i class="icon-plus"></i> <s:text name="wao.ui.action.createSampleRow" />
+ </s:a>
+ </s:if>
+
<table class="large-table table-hover">
<thead>
<th>
@@ -102,11 +109,4 @@
</tfoot>
</table>
- <s:if test="authenticatedWaoUser.authorizedToEditSamplingPlan">
- <s:url action="edit-sample-row!input" id="createSampleRow" />
- <s:a href="%{createSampleRow}">
- <i class="icon-plus"></i> <s:text name="wao.ui.action.createSampleRow" />
- </s:a>
- </s:if>
-
</html>
Modified: trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout-login.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout-login.jsp 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout-login.jsp 2014-03-20 16:53:39 UTC (rev 1733)
@@ -21,66 +21,12 @@
<body>
- <content>
+ <main>
<s:actionerror theme="bootstrap" />
<s:actionmessage theme="bootstrap" />
<s:fielderror theme="bootstrap" />
<decorator:body />
- </content>
+ </main>
- <footer class="hidden-print<s:if test="%{instanceDisclaimer != null}"> disclaimer-displayed</s:if>" >
- <ul>
- <li>
- <a href="http://maven-site.forge.codelutin.com/wao/" target="_blank">
- WAO
- </a>
- </li>
- <li>
- <a href="http://forge.codelutin.com/projects/wao/news" target="_blank">
- <%@include file="version.jsp" %>
- </a>
- </li>
- <t:if test="connectedUser.obsMer">
- <li>
- <a href="mailto:harmonie@ifremer.fr" title="Contacter un responsable Obsmer">
- Obsmer
- </a>
- </li>
- </t:if>
- <li>
- <a href="http://sih.ifremer.fr/" title="Page d'accueil du SIH" target="_blank">
- Ifremer SIH
- </a>
- </li>
- <li>
- <a href="http://www.gnu.org/licenses/agpl.html" title="License AGPL v3" target="_blank">
- AGPLv3
- </a>
- </li>
- <li>
- Copyright 2009-${currentYear}
- <a href="http://www.ifremer.fr" title="Ifremer" target="_blank">
- Ifremer
- </a>,
- <a href="http://www.codelutin.com" title="Code Lutin" target="_blank">
- Code Lutin
- </a>
- </li>
- </ul>
- <s:if test="obsMer">
- <p class="cnil">
- ${message:wao.ui.disclaimer}
- </p>
- </s:if>
- </footer>
-
- <s:if test="%{instanceDisclaimer != null}">
- <div id="instance-disclaimer">
- <div>
- <s:property value="instanceDisclaimer" />
- </div>
- </div>
- </s:if>
-
</body>
</html>
Modified: trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout.jsp 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout.jsp 2014-03-20 16:53:39 UTC (rev 1733)
@@ -132,7 +132,10 @@
</s:a>
</li>
<li>
- <a href="#"><i class="icon-off"></i> <s:text name="wao.ui.action.logout" /></a>
+ <s:url namespace="/authentication" action="logout" id="logoutUrl" />
+ <s:a href="%{logoutUrl}">
+ <i class="icon-off"></i> <s:text name="wao.ui.action.logout" />
+ </s:a>
</li>
</ul>
</li>
Modified: trunk/wao-web/src/main/webapp/css/wao.css
===================================================================
--- trunk/wao-web/src/main/webapp/css/wao.css 2014-03-20 10:21:05 UTC (rev 1732)
+++ trunk/wao-web/src/main/webapp/css/wao.css 2014-03-20 16:53:39 UTC (rev 1733)
@@ -74,7 +74,7 @@
list-style-type: none;
}
-.large-table tr:hover {
+.large-table tbody tr:hover {
background-color: #f5f5f5;
}
@@ -98,3 +98,15 @@
text-align: right;
}
+form#login button[type="submit"] {
+ margin-left: 180px;
+}
+
+#cgu {
+ overflow-y: auto;
+ max-height: 300px;
+ padding: 5px;
+ border: solid 5px #f2f2f2;
+ margin-bottom: 20px;
+}
+
1
0
r1732 - in trunk: wao-services/src/main/java/fr/ifremer/wao/services/service/csv/operations wao-web/src/main/java/fr/ifremer/wao/web/action wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer wao-web/src/main/resources/i18n wao-web/src/main/webapp/WEB-INF/content/obsmer
by bleny@users.forge.codelutin.com 20 Mar '14
by bleny@users.forge.codelutin.com 20 Mar '14
20 Mar '14
Author: bleny
Date: 2014-03-20 11:21:05 +0100 (Thu, 20 Mar 2014)
New Revision: 1732
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1732
Log:
refs #4483 add export sampling plan ui
Added:
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/AbstractDownloadCsvAction.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/ExportSamplingPlanAction.java
Modified:
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/operations/DCF5CodesParserFormatter.java
trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties
trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties
trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/sampling-plan.jsp
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/operations/DCF5CodesParserFormatter.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/operations/DCF5CodesParserFormatter.java 2014-03-20 09:06:44 UTC (rev 1731)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/operations/DCF5CodesParserFormatter.java 2014-03-20 10:21:05 UTC (rev 1732)
@@ -57,6 +57,7 @@
@Override
public List<DCF5Code> parse(String codes) {
+ // FIXME brendan 20/03/14 la gestion d'exception fait qu'on a pas le message d'erreur dans l'UI
try {
Collection<DCF5Code> dcf5Codes = referentialService.getDcf5Codes(codes);
List<DCF5Code> value = new ArrayList<>(dcf5Codes);
Added: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/AbstractDownloadCsvAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/AbstractDownloadCsvAction.java (rev 0)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/AbstractDownloadCsvAction.java 2014-03-20 10:21:05 UTC (rev 1732)
@@ -0,0 +1,26 @@
+package fr.ifremer.wao.web.action;
+
+import fr.ifremer.wao.web.WaoJspActionSupport;
+import org.apache.struts2.convention.annotation.Result;
+
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+
+@Result(
+ name="success",
+ type="stream",
+ params = {
+ "contentType", "text/csv",
+ "inputName", "inputStream",
+ "contentDisposition", "attachment;filename=${fileName}"
+ }
+)
+public abstract class AbstractDownloadCsvAction extends WaoJspActionSupport {
+
+ protected SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-dd-MM_HHhmm");
+
+ public abstract String getFileName();
+
+ public abstract InputStream getInputStream();
+
+}
Added: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/ExportSamplingPlanAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/ExportSamplingPlanAction.java (rev 0)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/ExportSamplingPlanAction.java 2014-03-20 10:21:05 UTC (rev 1732)
@@ -0,0 +1,49 @@
+package fr.ifremer.wao.web.action.obsmer;
+
+import com.opensymphony.xwork2.Preparable;
+import fr.ifremer.wao.SampleRowsFilter;
+import fr.ifremer.wao.services.service.ObsMerSamplingPlanService;
+import fr.ifremer.wao.web.action.AbstractDownloadCsvAction;
+
+import java.io.InputStream;
+import java.util.Date;
+
+public class ExportSamplingPlanAction extends AbstractDownloadCsvAction implements Preparable {
+
+ protected ObsMerSamplingPlanService service;
+
+ protected SampleRowsFilter filter;
+
+ public void setService(ObsMerSamplingPlanService service) {
+ this.service = service;
+ }
+
+ public SampleRowsFilter getFilter() {
+ if (filter == null) {
+ prepare();
+ }
+ return filter;
+ }
+
+ @Override
+ public void prepare() {
+
+ filter = service.newSampleRowsFilter(getAuthenticatedWaoUser());
+
+ }
+
+ @Override
+ public String getFileName() {
+
+ return "plan_" + getObsProgram().name() + "_" + dateFormat.format(new Date()) + ".csv";
+
+ }
+
+ @Override
+ public InputStream getInputStream() {
+
+ return service.exportSamplingPlan(filter);
+
+ }
+
+}
Modified: trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties
===================================================================
--- trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties 2014-03-20 09:06:44 UTC (rev 1731)
+++ trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties 2014-03-20 10:21:05 UTC (rev 1732)
@@ -31,6 +31,7 @@
wao.ui.action.createNews.success=News created
wao.ui.action.createSampleRow=Add a sample row to the plan
wao.ui.action.createWaoUser=Create a user
+wao.ui.action.csvExport=Export as CSV
wao.ui.action.delete=Delete
wao.ui.action.deleteCompany=Delete company
wao.ui.action.deleteCompany.confirm=Are you sure you want to delete this company ?
Modified: trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties
===================================================================
--- trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties 2014-03-20 09:06:44 UTC (rev 1731)
+++ trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties 2014-03-20 10:21:05 UTC (rev 1732)
@@ -31,6 +31,7 @@
wao.ui.action.createNews.success=Actualité enregistrée avec succès
wao.ui.action.createSampleRow=Ajouter une ligne au plan
wao.ui.action.createWaoUser=Créer un utilisateur
+wao.ui.action.csvExport=Exporter au format CSV
wao.ui.action.delete=Supprimer
wao.ui.action.deleteCompany=Supprimer la société
wao.ui.action.deleteCompany.confirm=Êtes-vous sûr de vouloir supprimer la société ?
Modified: trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/sampling-plan.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/sampling-plan.jsp 2014-03-20 09:06:44 UTC (rev 1731)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/sampling-plan.jsp 2014-03-20 10:21:05 UTC (rev 1732)
@@ -13,6 +13,11 @@
<s:text name="wao.ui.page.SamplingPlan.title" />
</h1>
+ <s:url action="export-sampling-plan" id="exportSamplingPlanUrl" />
+ <s:a href="%{exportSamplingPlanUrl}" cssClass="btn">
+ <i class="icon-download"></i> <s:text name="wao.ui.action.csvExport" />
+ </s:a>
+
<table class="large-table table-hover">
<thead>
<th>
1
0
r1731 - in trunk: wao-persistence/src/main/java/fr/ifremer/wao/entity wao-services/src/main/java/fr/ifremer/wao/services/service wao-services/src/main/java/fr/ifremer/wao/services/service/csv wao-services/src/main/java/fr/ifremer/wao/services/service/csv/operations wao-services/src/test/java/fr/ifremer/wao/services/service
by bleny@users.forge.codelutin.com 20 Mar '14
by bleny@users.forge.codelutin.com 20 Mar '14
20 Mar '14
Author: bleny
Date: 2014-03-20 10:06:44 +0100 (Thu, 20 Mar 2014)
New Revision: 1731
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1731
Log:
refs #4483 add export sampling plan service
Added:
trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/DCF5Codes.java
Modified:
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/ObsMerObsVenteSamplingPlanImportExportModel.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/operations/DCF5CodesParserFormatter.java
trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanServiceTest.java
Added: trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/DCF5Codes.java
===================================================================
--- trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/DCF5Codes.java (rev 0)
+++ trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/DCF5Codes.java 2014-03-20 09:06:44 UTC (rev 1731)
@@ -0,0 +1,18 @@
+package fr.ifremer.wao.entity;
+
+import com.google.common.base.Function;
+
+public class DCF5Codes {
+
+ public static Function<DCF5Code, String> getCode() {
+ return new GetCode();
+ }
+
+ protected static class GetCode implements Function<DCF5Code, String> {
+
+ @Override
+ public String apply(DCF5Code input) {
+ return input.getCode();
+ }
+ }
+}
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java 2014-03-19 16:53:56 UTC (rev 1730)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java 2014-03-20 09:06:44 UTC (rev 1731)
@@ -3,10 +3,12 @@
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import fr.ifremer.wao.SampleRowsFilter;
+import fr.ifremer.wao.WaoTechnicalException;
import fr.ifremer.wao.entity.Boat;
import fr.ifremer.wao.entity.Boats;
import fr.ifremer.wao.entity.Company;
@@ -37,11 +39,14 @@
import fr.ifremer.wao.services.service.administration.UnknownFishingGearDcfCodeException;
import fr.ifremer.wao.services.service.administration.UnknownTargetSpeciesDcfCodeException;
import fr.ifremer.wao.services.service.csv.ObsMerObsVenteSamplingPlanImportExportModel;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.nuiton.csv.Export;
+import org.nuiton.csv.ExportModel;
import org.nuiton.csv.Import;
import org.nuiton.csv.ImportModel;
import org.nuiton.csv.ImportRuntimeException;
@@ -49,6 +54,7 @@
import org.nuiton.util.DateUtil;
import java.io.InputStream;
+import java.nio.charset.Charset;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
@@ -483,4 +489,39 @@
return samplingPlanImportModel;
}
+
+ public InputStream exportSamplingPlan(SampleRowsFilter filter) {
+
+ Preconditions.checkArgument(filter.getPeriodFrom() != null);
+ Preconditions.checkArgument(filter.getPeriodTo() != null);
+
+ // ignore pagination when exporting
+ filter.setOrderByArguments(ImmutableSet.of(SampleRow.PROPERTY_CODE));
+
+ SampleRowTopiaDao dao = getSampleRowDao();
+
+ List<SampleRow> sampleRows = dao.findAll(filter);
+
+ ExportModel<SampleRow> exportModel =
+ new ObsMerObsVenteSamplingPlanImportExportModel(
+ filter.getPeriodFrom(),
+ filter.getPeriodTo(),
+ ObsProgram.OBSMER);
+
+ Export<SampleRow> export = Export.newExport(exportModel, sampleRows);
+
+ try {
+
+ String csvContent = export.toString(Charset.forName("UTF-8"));
+
+ InputStream csvInputStream = IOUtils.toInputStream(csvContent, "UTF-8");
+
+ return csvInputStream;
+
+ } catch (Exception e) {
+ throw new WaoTechnicalException(e);
+ }
+
+ }
+
}
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/ObsMerObsVenteSamplingPlanImportExportModel.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/ObsMerObsVenteSamplingPlanImportExportModel.java 2014-03-19 16:53:56 UTC (rev 1730)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/ObsMerObsVenteSamplingPlanImportExportModel.java 2014-03-20 09:06:44 UTC (rev 1731)
@@ -124,8 +124,8 @@
protected PeriodDates periodDates;
/** Pour l'export */
- public ObsMerObsVenteSamplingPlanImportExportModel(PeriodDates periodDates, ObsProgram obsProgram) {
- this.periodDates = periodDates;
+ public ObsMerObsVenteSamplingPlanImportExportModel(Date fromMonth, Date toMonth, ObsProgram obsProgram) {
+ this.periodDates = new PeriodDates(fromMonth, toMonth);
this.obsProgram = obsProgram;
}
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/operations/DCF5CodesParserFormatter.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/operations/DCF5CodesParserFormatter.java 2014-03-19 16:53:56 UTC (rev 1730)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/operations/DCF5CodesParserFormatter.java 2014-03-20 09:06:44 UTC (rev 1731)
@@ -23,7 +23,9 @@
*/
package fr.ifremer.wao.services.service.csv.operations;
+import com.google.common.collect.Iterables;
import fr.ifremer.wao.entity.DCF5Code;
+import fr.ifremer.wao.entity.DCF5Codes;
import fr.ifremer.wao.services.service.administration.ReferentialService;
import fr.ifremer.wao.services.service.administration.UnknownFishingGearDcfCodeException;
import fr.ifremer.wao.services.service.administration.UnknownTargetSpeciesDcfCodeException;
@@ -49,7 +51,8 @@
@Override
public String format(List<DCF5Code> dcf5Codes) {
- return StringUtil.join(dcf5Codes, DCF5_CODES_SEPARATOR, true);
+ String join = StringUtil.join(Iterables.transform(dcf5Codes, DCF5Codes.getCode()), DCF5_CODES_SEPARATOR, true);
+ return join;
}
@Override
Modified: trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanServiceTest.java
===================================================================
--- trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanServiceTest.java 2014-03-19 16:53:56 UTC (rev 1730)
+++ trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanServiceTest.java 2014-03-20 09:06:44 UTC (rev 1731)
@@ -13,6 +13,7 @@
import org.junit.Test;
import org.nuiton.util.DateUtil;
+import java.io.IOException;
import java.io.InputStream;
import java.util.List;
@@ -82,4 +83,23 @@
}
+ @Test
+ public void testExportSamplingPlan() throws IOException {
+
+ fixtures.samplingPlan();
+
+ serviceContext.setDate(DateUtil.createDate(15, 5, 2010));
+
+ SampleRowsFilter filter = service.newSampleRowsFilter(fixtures.admin());
+
+ InputStream inputStream = service.exportSamplingPlan(filter);
+
+ String csvContent = IOUtils.toString(inputStream);
+
+ if (log.isDebugEnabled()) {
+ log.debug("csvContent = \n" + csvContent);
+ }
+
+ }
+
}
1
0
r1730 - in trunk/wao-services/src/main/java/fr/ifremer/wao/services/service: . administration
by bleny@users.forge.codelutin.com 19 Mar '14
by bleny@users.forge.codelutin.com 19 Mar '14
19 Mar '14
Author: bleny
Date: 2014-03-19 17:53:56 +0100 (Wed, 19 Mar 2014)
New Revision: 1730
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1730
Log:
refs #4560 enable to delete user
Modified:
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/IllegalDeletionException.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/CompaniesService.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/IllegalDeletionException.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/IllegalDeletionException.java 2014-03-19 15:15:10 UTC (rev 1729)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/IllegalDeletionException.java 2014-03-19 16:53:56 UTC (rev 1730)
@@ -3,7 +3,8 @@
import fr.ifremer.wao.WaoException;
import org.nuiton.topia.persistence.TopiaEntity;
-import java.util.Set;
+import java.util.List;
+import java.util.Map;
/**
* L'utilisateur a voulu supprimer une entité mais on doit refuser sa demande car
@@ -12,14 +13,14 @@
public class IllegalDeletionException extends WaoException {
/** Les types d'entités qui utilisent l'entité qu'on ne peut pas supprimer. */
- protected Set<Class<? extends TopiaEntity>> classes;
+ private Map<Class<? extends TopiaEntity>, List<? extends TopiaEntity>> allUsages;
- public IllegalDeletionException(Set<Class<? extends TopiaEntity>> classes) {
- this.classes = classes;
+ public IllegalDeletionException(Map<Class<? extends TopiaEntity>, List<? extends TopiaEntity>> allUsages) {
+ this.allUsages = allUsages;
}
- public Set<Class<? extends TopiaEntity>> getClasses() {
- return classes;
+ public Map<Class<? extends TopiaEntity>, List<? extends TopiaEntity>> getAllUsages() {
+ return allUsages;
}
}
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/CompaniesService.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/CompaniesService.java 2014-03-19 15:15:10 UTC (rev 1729)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/CompaniesService.java 2014-03-19 16:53:56 UTC (rev 1730)
@@ -68,7 +68,7 @@
if (allUsages.isEmpty()) {
dao.delete(company);
} else {
- throw new IllegalDeletionException(allUsages.keySet());
+ throw new IllegalDeletionException(allUsages);
}
commit();
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java 2014-03-19 15:15:10 UTC (rev 1729)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java 2014-03-19 16:53:56 UTC (rev 1730)
@@ -193,12 +193,14 @@
WaoUser waoUser = dao.findByTopiaId(waoUserId);
+ waoUser.setCompany(null);
+
Map<Class<? extends TopiaEntity>, List<? extends TopiaEntity>> allUsages = dao.findAllUsages(waoUser);
if (allUsages.isEmpty()) {
dao.delete(waoUser);
} else {
- throw new IllegalDeletionException(allUsages.keySet());
+ throw new IllegalDeletionException(allUsages);
}
commit();
1
0
r1729 - in trunk: wao-services/src/main/java/fr/ifremer/wao/services wao-web/src/main/java/fr/ifremer/wao/web wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer wao-web/src/main/resources/i18n wao-web/src/main/webapp/WEB-INF/content/obsmer wao-web/src/main/webapp/WEB-INF/decorators wao-web/src/main/webapp/css wao-web/src/main/webapp/js
by bleny@users.forge.codelutin.com 19 Mar '14
by bleny@users.forge.codelutin.com 19 Mar '14
19 Mar '14
Author: bleny
Date: 2014-03-19 16:15:10 +0100 (Wed, 19 Mar 2014)
New Revision: 1729
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1729
Log:
refs #4483 start sampling plan table
Added:
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/SamplingPlanAction.java
trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/sampling-plan.jsp
trunk/wao-web/src/main/webapp/js/wao.js
Modified:
trunk/wao-services/src/main/java/fr/ifremer/wao/services/AuthenticatedWaoUser.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoSession.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditSampleRowAction.java
trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties
trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties
trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/edit-sample-row-input.jsp
trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout.jsp
trunk/wao-web/src/main/webapp/css/wao.css
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/AuthenticatedWaoUser.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/AuthenticatedWaoUser.java 2014-03-19 15:11:43 UTC (rev 1728)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/AuthenticatedWaoUser.java 2014-03-19 15:15:10 UTC (rev 1729)
@@ -178,4 +178,13 @@
public boolean isAuthorizedToImportSamplingPlan() {
return userProfile.isAdmin();
}
+
+ public boolean isAuthorizedToEditSamplingPlan() {
+ return userProfile.isAdmin();
+ }
+
+ public boolean isAuthorizedToCreateContact() {
+ return userProfile.isCoordinatorOrObserver();
+ }
+
}
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoSession.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoSession.java 2014-03-19 15:11:43 UTC (rev 1728)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/WaoSession.java 2014-03-19 15:15:10 UTC (rev 1729)
@@ -16,6 +16,8 @@
protected AuthenticatedWaoUser authenticatedWaoUser;
+ protected String sampleRowToHighlightId;
+
public Collection<String> getMessages() {
if (messages == null) {
messages = Lists.newLinkedList();
@@ -51,4 +53,11 @@
this.authenticatedWaoUser = authenticatedWaoUser;
}
+ public String getSampleRowToHighlightId() {
+ return sampleRowToHighlightId;
+ }
+
+ public void setSampleRowToHighlightId(String sampleRowToHighlightId) {
+ this.sampleRowToHighlightId = sampleRowToHighlightId;
+ }
}
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditSampleRowAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditSampleRowAction.java 2014-03-19 15:11:43 UTC (rev 1728)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditSampleRowAction.java 2014-03-19 15:15:10 UTC (rev 1729)
@@ -118,6 +118,8 @@
session.addMessage(t("wao.ui.form.updateSampleRowCommand.success", updateSampleRowCommand.getSampleRow().getCode()));
+ session.setSampleRowToHighlightId(updateSampleRowCommand.getSampleRow().getTopiaId());
+
return SUCCESS;
}
Added: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/SamplingPlanAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/SamplingPlanAction.java (rev 0)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/SamplingPlanAction.java 2014-03-19 15:15:10 UTC (rev 1729)
@@ -0,0 +1,41 @@
+package fr.ifremer.wao.web.action.obsmer;
+
+import fr.ifremer.wao.SampleRowsFilter;
+import fr.ifremer.wao.entity.SampleRow;
+import fr.ifremer.wao.services.service.ObsMerSamplingPlanService;
+import fr.ifremer.wao.web.WaoJspActionSupport;
+
+import java.util.List;
+
+public class SamplingPlanAction extends WaoJspActionSupport {
+
+ protected ObsMerSamplingPlanService service;
+
+ protected List<SampleRow> samplingPlan;
+
+ public void setService(ObsMerSamplingPlanService service) {
+ this.service = service;
+ }
+
+ @Override
+ public String execute() {
+
+ SampleRowsFilter filter = service.newSampleRowsFilter(getAuthenticatedWaoUser());
+
+ filter.setPeriodFrom(null);
+ filter.setPeriodTo(null);
+
+ samplingPlan = service.getSamplingPlan(filter);
+
+ return SUCCESS;
+
+ }
+
+ public List<SampleRow> getSamplingPlan() {
+ return samplingPlan;
+ }
+
+ public String getSampleRowToHighlightId() {
+ return getSession().getSampleRowToHighlightId();
+ }
+}
Modified: trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties
===================================================================
--- trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties 2014-03-19 15:11:43 UTC (rev 1728)
+++ trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties 2014-03-19 15:15:10 UTC (rev 1729)
@@ -25,9 +25,11 @@
wao.ui.action.changeLocale=Change language
wao.ui.action.contactAdmin=Contact an admin
wao.ui.action.create=Create
+wao.ui.action.createAssociatedContact=Create a new contact for this sample row
wao.ui.action.createCompany=Create a company
wao.ui.action.createNews=Create news
wao.ui.action.createNews.success=News created
+wao.ui.action.createSampleRow=Add a sample row to the plan
wao.ui.action.createWaoUser=Create a user
wao.ui.action.delete=Delete
wao.ui.action.deleteCompany=Delete company
@@ -66,9 +68,12 @@
wao.ui.action.showDetails=Show details
wao.ui.action.showFilters=Show filters
wao.ui.action.unvalidateContact=Unvalidate contact
+wao.ui.action.viewAssociatedContacts=View sample row contacts
wao.ui.action.viewBoatsForRow=View the boats for this sample row
wao.ui.action.viewCompanyWaoUsers=View users for this company
+wao.ui.action.viewElligibleBoats=View eligible boats
wao.ui.action.viewIndicatorsHistory=View indicators historic
+wao.ui.action.viewSampleRowLog=View sample row log
wao.ui.actions=Actions
wao.ui.boatList=List of %s boats
wao.ui.boatinfo.title=Infos about %s
Modified: trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties
===================================================================
--- trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties 2014-03-19 15:11:43 UTC (rev 1728)
+++ trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties 2014-03-19 15:15:10 UTC (rev 1729)
@@ -25,9 +25,11 @@
wao.ui.action.changeLocale=Changer de langue
wao.ui.action.contactAdmin=Contacter un responsable ObsMer
wao.ui.action.create=Créer
+wao.ui.action.createAssociatedContact=Créer un contact pour cette ligne
wao.ui.action.createCompany=Créer une société
wao.ui.action.createNews=Créer une actualité
wao.ui.action.createNews.success=Actualité enregistrée avec succès
+wao.ui.action.createSampleRow=Ajouter une ligne au plan
wao.ui.action.createWaoUser=Créer un utilisateur
wao.ui.action.delete=Supprimer
wao.ui.action.deleteCompany=Supprimer la société
@@ -66,9 +68,12 @@
wao.ui.action.showDetails=Voir les détails
wao.ui.action.showFilters=Afficher les filtres
wao.ui.action.unvalidateContact=Invalider le contact
+wao.ui.action.viewAssociatedContacts=Voir les contacts associés à cette ligne
wao.ui.action.viewBoatsForRow=Voir les navires présentis pour cette ligne
wao.ui.action.viewCompanyWaoUsers=Voir les utilisateurs associés à cette société
+wao.ui.action.viewElligibleBoats=Consulter les navires éligibles
wao.ui.action.viewIndicatorsHistory=Voir l'historique des modifications des indicateurs
+wao.ui.action.viewSampleRowLog=Consulter l'historique de cette ligne
wao.ui.actions=Actions
wao.ui.boatList=Liste de %s navires
wao.ui.boatinfo.title=Informations sur %s
Modified: trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/edit-sample-row-input.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/edit-sample-row-input.jsp 2014-03-19 15:11:43 UTC (rev 1728)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/edit-sample-row-input.jsp 2014-03-19 15:15:10 UTC (rev 1729)
@@ -20,37 +20,43 @@
var $expectedObservationsByMonthsTable = $('#expectedObservationsByMonthsTable');
var $expectedObservationsByMonthsTableHead = $expectedObservationsByMonthsTable.find('thead');
var $expectedObservationsByMonthsTableBody = $expectedObservationsByMonthsTable.find('tbody');
- var $periodBeginInput = $('input[name="updateSampleRowCommand.sampleRow.periodBegin"');
- var $periodEndInput = $('input[name="updateSampleRowCommand.sampleRow.periodEnd"');
+ var $periodBeginInput = $('input[name="updateSampleRowCommand.sampleRow.periodBegin"]');
+ var $periodEndInput = $('input[name="updateSampleRowCommand.sampleRow.periodEnd"]');
var updateExpectedObservationsByMonthsTable = function() {
- var periodBegin = moment($periodBeginInput.val(), "MM/YYYY");
- var periodEnd = moment($periodEndInput.val(), "MM/YYYY");
- if (periodBegin > periodEnd) {
- $periodEndInput.val($periodBeginInput.val());
- periodEnd = moment($periodEndInput.val(), "MM/YYYY");
- }
- var months = [];
- do {
- months.push(periodBegin.format("YYYYMM"));
- periodBegin.add('months', 1);
- } while (periodBegin <= periodEnd);
$expectedObservationsByMonthsTableHead.empty();
$expectedObservationsByMonthsTableBody.empty();
- $expectedObservationsByMonthsTableHead.append('<tr></tr>');
- $expectedObservationsByMonthsTableBody.append('<tr></tr>');
- $(months).each(function (index, month) {
- var newInput = ' <td>'
- + ' ' + moment(month, "YYYYMM").format('MM/YYYY')
- + ' </td>';
- $expectedObservationsByMonthsTableHead.find('tr').append(newInput);
- });
- $(months).each(function (index, month) {
- var newInput = ' <td>'
- + ' <input type="number" name="expectedObservationsByMonths[\'' + month + '\']" value="' + expectedObservationsByMonths[month] + '" class="input-small" />'
- + ' </td>';
- $expectedObservationsByMonthsTableBody.find('tr').append(newInput);
- });
+ var periodBegin = moment($periodBeginInput.val(), 'MM/YYYY');
+ var periodEnd = moment($periodEndInput.val(), 'MM/YYYY');
+ if (periodBegin.isValid() && periodEnd.isValid()) {
+ if (periodBegin > periodEnd) {
+ $periodEndInput.val($periodBeginInput.val());
+ periodEnd = moment($periodEndInput.val(), 'MM/YYYY');
+ }
+ var months = [];
+ do {
+ months.push(periodBegin.format('YYYYMM'));
+ periodBegin.add('months', 1);
+ } while (periodBegin <= periodEnd);
+ $expectedObservationsByMonthsTableHead.append('<tr></tr>');
+ $expectedObservationsByMonthsTableBody.append('<tr></tr>');
+ $(months).each(function (index, month) {
+ var newInput = ' <td>'
+ + ' ' + moment(month, 'YYYYMM').format('MM/YYYY')
+ + ' </td>';
+ $expectedObservationsByMonthsTableHead.find('tr').append(newInput);
+ });
+ $(months).each(function (index, month) {
+ var value = "";
+ if (expectedObservationsByMonths[month]) {
+ value = expectedObservationsByMonths[month];
+ }
+ var newInput = ' <td>'
+ + ' <input type="number" name="expectedObservationsByMonths[\'' + month + '\']" value="' + value + '" class="input-small" />'
+ + ' </td>';
+ $expectedObservationsByMonthsTableBody.find('tr').append(newInput);
+ });
+ }
}
updateExpectedObservationsByMonthsTable();
@@ -145,8 +151,14 @@
<s:textfield name="updateSampleRowCommand.sampleRow.programName"
label="%{getText('wao.ui.field.SampleRow.programName')}"
disabled="updateSampleRowCommand.observationAlreadyStarted" />
- <s:textfield name="updateSampleRowCommand.sampleRow.periodBegin" label="%{getText('wao.ui.field.SampleRow.periodBegin')}" cssClass="input-small" />
- <s:textfield name="updateSampleRowCommand.sampleRow.periodEnd" label="%{getText('wao.ui.field.SampleRow.periodEnd')}" cssClass="input-small" />
+ <s:textfield name="updateSampleRowCommand.sampleRow.periodBegin"
+ label="%{getText('wao.ui.field.SampleRow.periodBegin')}"
+ placeholder="04/2014"
+ cssClass="input-small" />
+ <s:textfield name="updateSampleRowCommand.sampleRow.periodEnd"
+ label="%{getText('wao.ui.field.SampleRow.periodEnd')}"
+ placeholder="03/2015"
+ cssClass="input-small" />
<div class="control-group">
Added: trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/sampling-plan.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/sampling-plan.jsp (rev 0)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/sampling-plan.jsp 2014-03-19 15:15:10 UTC (rev 1729)
@@ -0,0 +1,107 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
+<%@taglib uri="/struts-tags" prefix="s" %>
+
+<html>
+
+ <head>
+ <title>
+ <s:text name="wao.ui.page.SamplingPlan.title" />
+ </title>
+ </head>
+
+ <h1>
+ <s:text name="wao.ui.page.SamplingPlan.title" />
+ </h1>
+
+ <table class="large-table table-hover">
+ <thead>
+ <th>
+ <s:text name="wao.ui.field.SampleRow.code" />
+ </th>
+ <th>
+ <s:text name="wao.ui.actions" />
+ </th>
+ </thead>
+ <tbody>
+ <s:iterator value="samplingPlan" var="sampleRow">
+ <tr<s:if test="sampleRowToHighlightId.equals(topiaId)"> class="highlight"</s:if>>
+ <th>
+ <s:property value="code" />
+ </th>
+ <td class="actions">
+ <%--
+ <td class="actions dropdown">
+ <a class="btn dropdown-toggle" data-toggle="dropdown" href="#"><s:text name="wao.ui.actions" /> <b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ --%>
+ <ul>
+ <s:if test="authenticatedWaoUser.authorizedToEditSamplingPlan">
+ <li>
+ <s:url action="edit-sample-row!input" id="editSampleRowUrl">
+ <s:param name="sampleRowId" value="topiaId" />
+ </s:url>
+ <s:a href="%{editSampleRowUrl}">
+ <i class="icon-edit"></i> <s:text name="wao.ui.action.edit" />
+ </s:a>
+ </li>
+ <li>
+ <s:url action="delete-sample-row" id="deleteSampleRowUrl">
+ <s:param name="companyId" value="topiaId" />
+ </s:url>
+ <s:a href="%{deleteSampleRowUrl}">
+ <i class="icon-trash"></i> <s:text name="wao.ui.action.delete" />
+ </s:a>
+ </li>
+ </s:if>
+ <li>
+ <s:url action="sample-row-log" id="sampleRowLogUrl">
+ <s:param name="sampleRowId" value="topiaId" />
+ </s:url>
+ <s:a href="%{sampleRowLogUrl}">
+ <i class="icon-time"></i> <s:text name="wao.ui.action.viewSampleRowLog" />
+ </s:a>
+ </li>
+ <li>
+ <s:url action="boats" id="viewElligibleBoatsUrl">
+ <s:param name="sampleRowIds" value="topiaId" />
+ </s:url>
+ <s:a href="%{viewElligibleBoatsUrl}">
+ <s:text name="wao.ui.action.viewElligibleBoats" />
+ </s:a>
+ </li>
+ <li>
+ <s:url action="contacts" id="viewAssociatedContactsUrl">
+ <s:param name="sampleRowIds" value="topiaId" />
+ </s:url>
+ <s:a href="%{viewAssociatedContactsUrl}">
+ <s:text name="wao.ui.action.viewAssociatedContacts" />
+ </s:a>
+ </li>
+ <s:if test="authenticatedWaoUser.authorizedToCreateContact">
+ <li>
+ <s:url action="contacts" id="createAssociatedContactUrl">
+ <s:param name="sampleRowIds" value="topiaId" />
+ </s:url>
+ <s:a href="%{createAssociatedContactUrl}">
+ <i class="icon-add"></i> <s:text name="wao.ui.action.createAssociatedContact" />
+ </s:a>
+ </li>
+ </s:if>
+ </ul>
+ </td>
+ </tr>
+ </s:iterator>
+ </todby>
+ <tfoot>
+
+ </tfoot>
+ </table>
+
+ <s:if test="authenticatedWaoUser.authorizedToEditSamplingPlan">
+ <s:url action="edit-sample-row!input" id="createSampleRow" />
+ <s:a href="%{createSampleRow}">
+ <i class="icon-plus"></i> <s:text name="wao.ui.action.createSampleRow" />
+ </s:a>
+ </s:if>
+
+</html>
Modified: trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout.jsp 2014-03-19 15:11:43 UTC (rev 1728)
+++ trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout.jsp 2014-03-19 15:15:10 UTC (rev 1729)
@@ -11,10 +11,11 @@
<sj:head locale="fr" jqueryui="true" loadAtOnce="true" jquerytheme="start" />
<sb:head />
<script type="text/javascript" src="<s:url value='/js/moment-js-2.5.1/moment-with-langs.js' />"></script>
- <script type="text/javascript" src="<s:url value='/js/select2/select2.min.js' />"></script>
- <script type="text/javascript" src="<s:url value='/js/select2/select2_locale_fr.js' />"></script>
+ <script type="text/javascript" src="<s:url value='/js/select2-3.4.5/select2.min.js' />"></script>
+ <script type="text/javascript" src="<s:url value='/js/select2-3.4.5/select2_locale_fr.js' />"></script>
<script type="text/javascript" src="<s:url value='/js/wao.js' />"></script>
- <link rel="stylesheet" type="text/css" href="<s:url value='/css/select2/select2.css' />" />
+ <link rel="stylesheet" type="text/css" href="<s:url value='/js/select2-3.4.5/select2.css' />" />
+ <link rel="stylesheet" type="text/css" href="<s:url value='/js/select2-3.4.5/select2-bootstrap.css' />" />
<link rel="stylesheet" type="text/css" href="<s:url value='/css/wao.css' />" media="all" />
<link rel="stylesheet" type="text/css" href="<s:url value='/css/wao-screen.css' />" media="screen">
<decorator:head />
Modified: trunk/wao-web/src/main/webapp/css/wao.css
===================================================================
--- trunk/wao-web/src/main/webapp/css/wao.css 2014-03-19 15:11:43 UTC (rev 1728)
+++ trunk/wao-web/src/main/webapp/css/wao.css 2014-03-19 15:15:10 UTC (rev 1729)
@@ -49,6 +49,10 @@
*/
/* pour représente qu'une entité (société, utilisateur) est inactive */
+.highlight {
+ background-color: #ffff99;
+}
+
.inactive {
text-decoration: line-through;
}
@@ -57,9 +61,34 @@
white-space: nowrap;
}
+.large-table, .large-table thead th {
+ border: solid 1px #aaaaaa;
+}
+
+.large-table * {
+ padding: 0px;
+ margin: 0px;
+}
+
+.large-table li {
+ list-style-type: none;
+}
+
+.large-table tr:hover {
+ background-color: #f5f5f5;
+}
+
+.large-table tbody th, .large-table td {
+ border: solid 1px #aaaaaa;
+ border-left-style: dashed;
+ border-right-style: dashed;
+ padding: 2px;
+}
+
/**
* Styles spécifiques pour certainse pages de l'appli
*/
+
#expectedObservationsByMonthsTable th {
white-space: nowrap;
}
Added: trunk/wao-web/src/main/webapp/js/wao.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/wao.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/wao.js 2014-03-19 15:15:10 UTC (rev 1729)
@@ -0,0 +1,5 @@
+$(document).ready(function () {
+
+ $('select').select2();
+
+});
\ No newline at end of file
1
0
r1728 - in trunk/wao-web/src/main/webapp/js: . select2-3.4.5
by bleny@users.forge.codelutin.com 19 Mar '14
by bleny@users.forge.codelutin.com 19 Mar '14
19 Mar '14
Author: bleny
Date: 2014-03-19 16:11:43 +0100 (Wed, 19 Mar 2014)
New Revision: 1728
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1728
Log:
add select2
Added:
trunk/wao-web/src/main/webapp/js/select2-3.4.5/
trunk/wao-web/src/main/webapp/js/select2-3.4.5/LICENSE
trunk/wao-web/src/main/webapp/js/select2-3.4.5/README.md
trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2-bootstrap.css
trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2-spinner.gif
trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.css
trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.js
trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.min.js
trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.png
trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2_locale_fr.js
trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2x2.png
Added: trunk/wao-web/src/main/webapp/js/select2-3.4.5/LICENSE
===================================================================
--- trunk/wao-web/src/main/webapp/js/select2-3.4.5/LICENSE (rev 0)
+++ trunk/wao-web/src/main/webapp/js/select2-3.4.5/LICENSE 2014-03-19 15:11:43 UTC (rev 1728)
@@ -0,0 +1,18 @@
+Copyright 2012 Igor Vaynberg
+
+Version: @@ver@@ Timestamp: @@timestamp@@
+
+This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
+General Public License version 2 (the "GPL License"). You may choose either license to govern your
+use of this software only upon the condition that you accept all of the terms of either the Apache
+License or the GPL License.
+
+You may obtain a copy of the Apache License and the GPL License at:
+
+http://www.apache.org/licenses/LICENSE-2.0
+http://www.gnu.org/licenses/gpl-2.0.html
+
+Unless required by applicable law or agreed to in writing, software distributed under the Apache License
+or the GPL Licesnse is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+either express or implied. See the Apache License and the GPL License for the specific language governing
+permissions and limitations under the Apache License and the GPL License.
\ No newline at end of file
Added: trunk/wao-web/src/main/webapp/js/select2-3.4.5/README.md
===================================================================
--- trunk/wao-web/src/main/webapp/js/select2-3.4.5/README.md (rev 0)
+++ trunk/wao-web/src/main/webapp/js/select2-3.4.5/README.md 2014-03-19 15:11:43 UTC (rev 1728)
@@ -0,0 +1,83 @@
+Select2
+=======
+
+Select2 is a jQuery-based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results.
+
+To get started, checkout examples and documentation at http://ivaynberg.github.com/select2
+
+Use cases
+---------
+
+* Enhancing native selects with search.
+* Enhancing native selects with a better multi-select interface.
+* Loading data from JavaScript: easily load items via ajax and have them searchable.
+* Nesting optgroups: native selects only support one level of nested. Select2 does not have this restriction.
+* Tagging: ability to add new items on the fly.
+* Working with large, remote datasets: ability to partially load a dataset based on the search term.
+* Paging of large datasets: easy support for loading more pages when the results are scrolled to the end.
+* Templating: support for custom rendering of results and selections.
+
+Browser compatibility
+---------------------
+* IE 8+
+* Chrome 8+
+* Firefox 10+
+* Safari 3+
+* Opera 10.6+
+
+Integrations
+------------
+
+* [Wicket-Select2](https://github.com/ivaynberg/wicket-select2) (Java / [Apache Wicket](http://wicket.apache.org))
+* [select2-rails](https://github.com/argerim/select2-rails) (Ruby on Rails)
+* [AngularUI](http://angular-ui.github.com/#directives-select2) ([AngularJS](angularjs.org))
+* [Django](https://github.com/applegrew/django-select2)
+* [Symfony](https://github.com/19Gerhard85/sfSelect2WidgetsPlugin)
+* [Bootstrap](https://github.com/t0m/select2-bootstrap-css) (CSS skin)
+* [Yii](https://github.com/tonybolzan/yii-select2)
+
+Internationalization (i18n)
+---------------------------
+
+Select2 supports multiple languages by simply including the right
+language JS file (`select2_locale_it.js`, `select2_locale_nl.js`, etc.).
+
+Missing a language? Just copy `select2_locale_en.js.template`, translate
+it, and make a pull request back to Select2 here on GitHub.
+
+Bug tracker
+-----------
+
+Have a bug? Please create an issue here on GitHub!
+
+https://github.com/ivaynberg/select2/issues
+
+Mailing list
+------------
+
+Have a question? Ask on our mailing list!
+
+select2(a)googlegroups.com
+
+https://groups.google.com/d/forum/select2
+
+
+Copyright and license
+---------------------
+
+Copyright 2012 Igor Vaynberg
+
+This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
+General Public License version 2 (the "GPL License"). You may choose either license to govern your
+use of this software only upon the condition that you accept all of the terms of either the Apache
+License or the GPL License.
+
+You may obtain a copy of the Apache License and the GPL License in the LICENSE file, or at:
+
+http://www.apache.org/licenses/LICENSE-2.0
+http://www.gnu.org/licenses/gpl-2.0.html
+
+Unless required by applicable law or agreed to in writing, software distributed under the Apache License
+or the GPL License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+either express or implied. See the Apache License and the GPL License for the specific language governing
+permissions and limitations under the Apache License and the GPL License.
Added: trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2-bootstrap.css
===================================================================
--- trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2-bootstrap.css (rev 0)
+++ trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2-bootstrap.css 2014-03-19 15:11:43 UTC (rev 1728)
@@ -0,0 +1,87 @@
+.form-control .select2-choice {
+ border: 0;
+ border-radius: 2px;
+}
+
+.form-control .select2-choice .select2-arrow {
+ border-radius: 0 2px 2px 0;
+}
+
+.form-control.select2-container {
+ height: auto !important;
+ padding: 0px;
+}
+
+.form-control.select2-container.select2-dropdown-open {
+ border-color: #5897FB;
+ border-radius: 3px 3px 0 0;
+}
+
+.form-control .select2-container.select2-dropdown-open .select2-choices {
+ border-radius: 3px 3px 0 0;
+}
+
+.form-control.select2-container .select2-choices {
+ border: 0 !important;
+ border-radius: 3px;
+}
+
+.control-group.warning .select2-container .select2-choice,
+.control-group.warning .select2-container .select2-choices,
+.control-group.warning .select2-container-active .select2-choice,
+.control-group.warning .select2-container-active .select2-choices,
+.control-group.warning .select2-dropdown-open.select2-drop-above .select2-choice,
+.control-group.warning .select2-dropdown-open.select2-drop-above .select2-choices,
+.control-group.warning .select2-container-multi.select2-container-active .select2-choices {
+ border: 1px solid #C09853 !important;
+}
+
+.control-group.warning .select2-container .select2-choice div {
+ border-left: 1px solid #C09853 !important;
+ background: #FCF8E3 !important;
+}
+
+.control-group.error .select2-container .select2-choice,
+.control-group.error .select2-container .select2-choices,
+.control-group.error .select2-container-active .select2-choice,
+.control-group.error .select2-container-active .select2-choices,
+.control-group.error .select2-dropdown-open.select2-drop-above .select2-choice,
+.control-group.error .select2-dropdown-open.select2-drop-above .select2-choices,
+.control-group.error .select2-container-multi.select2-container-active .select2-choices {
+ border: 1px solid #B94A48 !important;
+}
+
+.control-group.error .select2-container .select2-choice div {
+ border-left: 1px solid #B94A48 !important;
+ background: #F2DEDE !important;
+}
+
+.control-group.info .select2-container .select2-choice,
+.control-group.info .select2-container .select2-choices,
+.control-group.info .select2-container-active .select2-choice,
+.control-group.info .select2-container-active .select2-choices,
+.control-group.info .select2-dropdown-open.select2-drop-above .select2-choice,
+.control-group.info .select2-dropdown-open.select2-drop-above .select2-choices,
+.control-group.info .select2-container-multi.select2-container-active .select2-choices {
+ border: 1px solid #3A87AD !important;
+}
+
+.control-group.info .select2-container .select2-choice div {
+ border-left: 1px solid #3A87AD !important;
+ background: #D9EDF7 !important;
+}
+
+.control-group.success .select2-container .select2-choice,
+.control-group.success .select2-container .select2-choices,
+.control-group.success .select2-container-active .select2-choice,
+.control-group.success .select2-container-active .select2-choices,
+.control-group.success .select2-dropdown-open.select2-drop-above .select2-choice,
+.control-group.success .select2-dropdown-open.select2-drop-above .select2-choices,
+.control-group.success .select2-container-multi.select2-container-active .select2-choices {
+ border: 1px solid #468847 !important;
+}
+
+.control-group.success .select2-container .select2-choice div {
+ border-left: 1px solid #468847 !important;
+ background: #DFF0D8 !important;
+}
Added: trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2-spinner.gif
===================================================================
(Binary files differ)
Property changes on: trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2-spinner.gif
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.css
===================================================================
--- trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.css (rev 0)
+++ trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.css 2014-03-19 15:11:43 UTC (rev 1728)
@@ -0,0 +1,615 @@
+/*
+Version: 3.4.5 Timestamp: Mon Nov 4 08:22:42 PST 2013
+*/
+.select2-container {
+ margin: 0;
+ position: relative;
+ display: inline-block;
+ /* inline-block for ie7 */
+ zoom: 1;
+ *display: inline;
+ vertical-align: middle;
+}
+
+.select2-container,
+.select2-drop,
+.select2-search,
+.select2-search input {
+ /*
+ Force border-box so that % widths fit the parent
+ container without overlap because of margin/padding.
+
+ More Info : http://www.quirksmode.org/css/box.html
+ */
+ -webkit-box-sizing: border-box; /* webkit */
+ -moz-box-sizing: border-box; /* firefox */
+ box-sizing: border-box; /* css3 */
+}
+
+.select2-container .select2-choice {
+ display: block;
+ height: 26px;
+ padding: 0 0 0 8px;
+ overflow: hidden;
+ position: relative;
+
+ border: 1px solid #aaa;
+ white-space: nowrap;
+ line-height: 26px;
+ color: #444;
+ text-decoration: none;
+
+ border-radius: 4px;
+
+ background-clip: padding-box;
+
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+
+ background-color: #fff;
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.5, #fff));
+ background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 50%);
+ background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 50%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#ffffff', endColorstr = '#eeeeee', GradientType = 0);
+ background-image: linear-gradient(top, #fff 0%, #eee 50%);
+}
+
+.select2-container.select2-drop-above .select2-choice {
+ border-bottom-color: #aaa;
+
+ border-radius: 0 0 4px 4px;
+
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.9, #fff));
+ background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 90%);
+ background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 90%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);
+ background-image: linear-gradient(top, #eee 0%, #fff 90%);
+}
+
+.select2-container.select2-allowclear .select2-choice .select2-chosen {
+ margin-right: 42px;
+}
+
+.select2-container .select2-choice > .select2-chosen {
+ margin-right: 26px;
+ display: block;
+ overflow: hidden;
+
+ white-space: nowrap;
+
+ text-overflow: ellipsis;
+}
+
+.select2-container .select2-choice abbr {
+ display: none;
+ width: 12px;
+ height: 12px;
+ position: absolute;
+ right: 24px;
+ top: 8px;
+
+ font-size: 1px;
+ text-decoration: none;
+
+ border: 0;
+ background: url('select2.png') right top no-repeat;
+ cursor: pointer;
+ outline: 0;
+}
+
+.select2-container.select2-allowclear .select2-choice abbr {
+ display: inline-block;
+}
+
+.select2-container .select2-choice abbr:hover {
+ background-position: right -11px;
+ cursor: pointer;
+}
+
+.select2-drop-mask {
+ border: 0;
+ margin: 0;
+ padding: 0;
+ position: fixed;
+ left: 0;
+ top: 0;
+ min-height: 100%;
+ min-width: 100%;
+ height: auto;
+ width: auto;
+ opacity: 0;
+ z-index: 9998;
+ /* styles required for IE to work */
+ background-color: #fff;
+ filter: alpha(opacity=0);
+}
+
+.select2-drop {
+ width: 100%;
+ margin-top: -1px;
+ position: absolute;
+ z-index: 9999;
+ top: 100%;
+
+ background: #fff;
+ color: #000;
+ border: 1px solid #aaa;
+ border-top: 0;
+
+ border-radius: 0 0 4px 4px;
+
+ -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
+ box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
+}
+
+.select2-drop-auto-width {
+ border-top: 1px solid #aaa;
+ width: auto;
+}
+
+.select2-drop-auto-width .select2-search {
+ padding-top: 4px;
+}
+
+.select2-drop.select2-drop-above {
+ margin-top: 1px;
+ border-top: 1px solid #aaa;
+ border-bottom: 0;
+
+ border-radius: 4px 4px 0 0;
+
+ -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
+ box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
+}
+
+.select2-drop-active {
+ border: 1px solid #5897fb;
+ border-top: none;
+}
+
+.select2-drop.select2-drop-above.select2-drop-active {
+ border-top: 1px solid #5897fb;
+}
+
+.select2-container .select2-choice .select2-arrow {
+ display: inline-block;
+ width: 18px;
+ height: 100%;
+ position: absolute;
+ right: 0;
+ top: 0;
+
+ border-left: 1px solid #aaa;
+ border-radius: 0 4px 4px 0;
+
+ background-clip: padding-box;
+
+ background: #ccc;
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
+ background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
+ background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc', GradientType = 0);
+ background-image: linear-gradient(top, #ccc 0%, #eee 60%);
+}
+
+.select2-container .select2-choice .select2-arrow b {
+ display: block;
+ width: 100%;
+ height: 100%;
+ background: url('select2.png') no-repeat 0 1px;
+}
+
+.select2-search {
+ display: inline-block;
+ width: 100%;
+ min-height: 26px;
+ margin: 0;
+ padding-left: 4px;
+ padding-right: 4px;
+
+ position: relative;
+ z-index: 10000;
+
+ white-space: nowrap;
+}
+
+.select2-search input {
+ width: 100%;
+ height: auto !important;
+ min-height: 26px;
+ padding: 4px 20px 4px 5px;
+ margin: 0;
+
+ outline: 0;
+ font-family: sans-serif;
+ font-size: 1em;
+
+ border: 1px solid #aaa;
+ border-radius: 0;
+
+ -webkit-box-shadow: none;
+ box-shadow: none;
+
+ background: #fff url('select2.png') no-repeat 100% -22px;
+ background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
+ background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
+ background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
+ background: url('select2.png') no-repeat 100% -22px, linear-gradient(top, #fff 85%, #eee 99%);
+}
+
+.select2-drop.select2-drop-above .select2-search input {
+ margin-top: 4px;
+}
+
+.select2-search input.select2-active {
+ background: #fff url('select2-spinner.gif') no-repeat 100%;
+ background: url('select2-spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
+ background: url('select2-spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
+ background: url('select2-spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
+ background: url('select2-spinner.gif') no-repeat 100%, linear-gradient(top, #fff 85%, #eee 99%);
+}
+
+.select2-container-active .select2-choice,
+.select2-container-active .select2-choices {
+ border: 1px solid #5897fb;
+ outline: none;
+
+ -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3);
+ box-shadow: 0 0 5px rgba(0, 0, 0, .3);
+}
+
+.select2-dropdown-open .select2-choice {
+ border-bottom-color: transparent;
+ -webkit-box-shadow: 0 1px 0 #fff inset;
+ box-shadow: 0 1px 0 #fff inset;
+
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+
+ background-color: #eee;
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #fff), color-stop(0.5, #eee));
+ background-image: -webkit-linear-gradient(center bottom, #fff 0%, #eee 50%);
+ background-image: -moz-linear-gradient(center bottom, #fff 0%, #eee 50%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);
+ background-image: linear-gradient(top, #fff 0%, #eee 50%);
+}
+
+.select2-dropdown-open.select2-drop-above .select2-choice,
+.select2-dropdown-open.select2-drop-above .select2-choices {
+ border: 1px solid #5897fb;
+ border-top-color: transparent;
+
+ background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), color-stop(0.5, #eee));
+ background-image: -webkit-linear-gradient(center top, #fff 0%, #eee 50%);
+ background-image: -moz-linear-gradient(center top, #fff 0%, #eee 50%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);
+ background-image: linear-gradient(bottom, #fff 0%, #eee 50%);
+}
+
+.select2-dropdown-open .select2-choice .select2-arrow {
+ background: transparent;
+ border-left: none;
+ filter: none;
+}
+.select2-dropdown-open .select2-choice .select2-arrow b {
+ background-position: -18px 1px;
+}
+
+/* results */
+.select2-results {
+ max-height: 200px;
+ padding: 0 0 0 4px;
+ margin: 4px 4px 4px 0;
+ position: relative;
+ overflow-x: hidden;
+ overflow-y: auto;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+.select2-results ul.select2-result-sub {
+ margin: 0;
+ padding-left: 0;
+}
+
+.select2-results ul.select2-result-sub > li .select2-result-label { padding-left: 20px }
+.select2-results ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 40px }
+.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 60px }
+.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 80px }
+.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 100px }
+.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 110px }
+.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 120px }
+
+.select2-results li {
+ list-style: none;
+ display: list-item;
+ background-image: none;
+}
+
+.select2-results li.select2-result-with-children > .select2-result-label {
+ font-weight: bold;
+}
+
+.select2-results .select2-result-label {
+ padding: 3px 7px 4px;
+ margin: 0;
+ cursor: pointer;
+
+ min-height: 1em;
+
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.select2-results .select2-highlighted {
+ background: #3875d7;
+ color: #fff;
+}
+
+.select2-results li em {
+ background: #feffde;
+ font-style: normal;
+}
+
+.select2-results .select2-highlighted em {
+ background: transparent;
+}
+
+.select2-results .select2-highlighted ul {
+ background: #fff;
+ color: #000;
+}
+
+
+.select2-results .select2-no-results,
+.select2-results .select2-searching,
+.select2-results .select2-selection-limit {
+ background: #f4f4f4;
+ display: list-item;
+}
+
+/*
+disabled look for disabled choices in the results dropdown
+*/
+.select2-results .select2-disabled.select2-highlighted {
+ color: #666;
+ background: #f4f4f4;
+ display: list-item;
+ cursor: default;
+}
+.select2-results .select2-disabled {
+ background: #f4f4f4;
+ display: list-item;
+ cursor: default;
+}
+
+.select2-results .select2-selected {
+ display: none;
+}
+
+.select2-more-results.select2-active {
+ background: #f4f4f4 url('select2-spinner.gif') no-repeat 100%;
+}
+
+.select2-more-results {
+ background: #f4f4f4;
+ display: list-item;
+}
+
+/* disabled styles */
+
+.select2-container.select2-container-disabled .select2-choice {
+ background-color: #f4f4f4;
+ background-image: none;
+ border: 1px solid #ddd;
+ cursor: default;
+}
+
+.select2-container.select2-container-disabled .select2-choice .select2-arrow {
+ background-color: #f4f4f4;
+ background-image: none;
+ border-left: 0;
+}
+
+.select2-container.select2-container-disabled .select2-choice abbr {
+ display: none;
+}
+
+
+/* multiselect */
+
+.select2-container-multi .select2-choices {
+ height: auto !important;
+ height: 1%;
+ margin: 0;
+ padding: 0;
+ position: relative;
+
+ border: 1px solid #aaa;
+ cursor: text;
+ overflow: hidden;
+
+ background-color: #fff;
+ background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eee), color-stop(15%, #fff));
+ background-image: -webkit-linear-gradient(top, #eee 1%, #fff 15%);
+ background-image: -moz-linear-gradient(top, #eee 1%, #fff 15%);
+ background-image: linear-gradient(top, #eee 1%, #fff 15%);
+}
+
+.select2-locked {
+ padding: 3px 5px 3px 5px !important;
+}
+
+.select2-container-multi .select2-choices {
+ min-height: 26px;
+}
+
+.select2-container-multi.select2-container-active .select2-choices {
+ border: 1px solid #5897fb;
+ outline: none;
+
+ -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3);
+ box-shadow: 0 0 5px rgba(0, 0, 0, .3);
+}
+.select2-container-multi .select2-choices li {
+ float: left;
+ list-style: none;
+}
+.select2-container-multi .select2-choices .select2-search-field {
+ margin: 0;
+ padding: 0;
+ white-space: nowrap;
+}
+
+.select2-container-multi .select2-choices .select2-search-field input {
+ padding: 5px;
+ margin: 1px 0;
+
+ font-family: sans-serif;
+ font-size: 100%;
+ color: #666;
+ outline: 0;
+ border: 0;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ background: transparent !important;
+}
+
+.select2-container-multi .select2-choices .select2-search-field input.select2-active {
+ background: #fff url('select2-spinner.gif') no-repeat 100% !important;
+}
+
+.select2-default {
+ color: #999 !important;
+}
+
+.select2-container-multi .select2-choices .select2-search-choice {
+ padding: 3px 5px 3px 18px;
+ margin: 3px 0 3px 5px;
+ position: relative;
+
+ line-height: 13px;
+ color: #333;
+ cursor: default;
+ border: 1px solid #aaaaaa;
+
+ border-radius: 3px;
+
+ -webkit-box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05);
+ box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05);
+
+ background-clip: padding-box;
+
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+
+ background-color: #e4e4e4;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#f4f4f4', GradientType=0);
+ background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eee));
+ background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
+ background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
+ background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
+}
+.select2-container-multi .select2-choices .select2-search-choice .select2-chosen {
+ cursor: default;
+}
+.select2-container-multi .select2-choices .select2-search-choice-focus {
+ background: #d4d4d4;
+}
+
+.select2-search-choice-close {
+ display: block;
+ width: 12px;
+ height: 13px;
+ position: absolute;
+ right: 3px;
+ top: 4px;
+
+ font-size: 1px;
+ outline: none;
+ background: url('select2.png') right top no-repeat;
+}
+
+.select2-container-multi .select2-search-choice-close {
+ left: 3px;
+}
+
+.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover {
+ background-position: right -11px;
+}
+.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close {
+ background-position: right -11px;
+}
+
+/* disabled styles */
+.select2-container-multi.select2-container-disabled .select2-choices {
+ background-color: #f4f4f4;
+ background-image: none;
+ border: 1px solid #ddd;
+ cursor: default;
+}
+
+.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice {
+ padding: 3px 5px 3px 5px;
+ border: 1px solid #ddd;
+ background-image: none;
+ background-color: #f4f4f4;
+}
+
+.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { display: none;
+ background: none;
+}
+/* end multiselect */
+
+
+.select2-result-selectable .select2-match,
+.select2-result-unselectable .select2-match {
+ text-decoration: underline;
+}
+
+.select2-offscreen, .select2-offscreen:focus {
+ clip: rect(0 0 0 0) !important;
+ width: 1px !important;
+ height: 1px !important;
+ border: 0 !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ overflow: hidden !important;
+ position: absolute !important;
+ outline: 0 !important;
+ left: 0px !important;
+ top: 0px !important;
+}
+
+.select2-display-none {
+ display: none;
+}
+
+.select2-measure-scrollbar {
+ position: absolute;
+ top: -10000px;
+ left: -10000px;
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+}
+/* Retina-ize icons */
+
+@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 144dpi) {
+ .select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice .select2-arrow b {
+ background-image: url('select2x2.png') !important;
+ background-repeat: no-repeat !important;
+ background-size: 60px 40px !important;
+ }
+ .select2-search input {
+ background-position: 100% -21px !important;
+ }
+}
Added: trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.js 2014-03-19 15:11:43 UTC (rev 1728)
@@ -0,0 +1,3255 @@
+/*
+Copyright 2012 Igor Vaynberg
+
+Version: 3.4.5 Timestamp: Mon Nov 4 08:22:42 PST 2013
+
+This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
+General Public License version 2 (the "GPL License"). You may choose either license to govern your
+use of this software only upon the condition that you accept all of the terms of either the Apache
+License or the GPL License.
+
+You may obtain a copy of the Apache License and the GPL License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+ http://www.gnu.org/licenses/gpl-2.0.html
+
+Unless required by applicable law or agreed to in writing, software distributed under the
+Apache License or the GPL Licesnse is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied. See the Apache License and the GPL License for
+the specific language governing permissions and limitations under the Apache License and the GPL License.
+*/
+(function ($) {
+ if(typeof $.fn.each2 == "undefined") {
+ $.extend($.fn, {
+ /*
+ * 4-10 times faster .each replacement
+ * use it carefully, as it overrides jQuery context of element on each iteration
+ */
+ each2 : function (c) {
+ var j = $([0]), i = -1, l = this.length;
+ while (
+ ++i < l
+ && (j.context = j[0] = this[i])
+ && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
+ );
+ return this;
+ }
+ });
+ }
+})(jQuery);
+
+(function ($, undefined) {
+ "use strict";
+ /*global document, window, jQuery, console */
+
+ if (window.Select2 !== undefined) {
+ return;
+ }
+
+ var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer,
+ lastMousePosition={x:0,y:0}, $document, scrollBarDimensions,
+
+ KEY = {
+ TAB: 9,
+ ENTER: 13,
+ ESC: 27,
+ SPACE: 32,
+ LEFT: 37,
+ UP: 38,
+ RIGHT: 39,
+ DOWN: 40,
+ SHIFT: 16,
+ CTRL: 17,
+ ALT: 18,
+ PAGE_UP: 33,
+ PAGE_DOWN: 34,
+ HOME: 36,
+ END: 35,
+ BACKSPACE: 8,
+ DELETE: 46,
+ isArrow: function (k) {
+ k = k.which ? k.which : k;
+ switch (k) {
+ case KEY.LEFT:
+ case KEY.RIGHT:
+ case KEY.UP:
+ case KEY.DOWN:
+ return true;
+ }
+ return false;
+ },
+ isControl: function (e) {
+ var k = e.which;
+ switch (k) {
+ case KEY.SHIFT:
+ case KEY.CTRL:
+ case KEY.ALT:
+ return true;
+ }
+
+ if (e.metaKey) return true;
+
+ return false;
+ },
+ isFunctionKey: function (k) {
+ k = k.which ? k.which : k;
+ return k >= 112 && k <= 123;
+ }
+ },
+ MEASURE_SCROLLBAR_TEMPLATE = "<div class='select2-measure-scrollbar'></div>",
+
+ DIACRITICS = {"\u24B6":"A","\uFF21":"A","\u00C0":"A","\u00C1":"A","\u00C2":"A","\u1EA6":"A","\u1EA4":"A","\u1EAA":"A","\u1EA8":"A","\u00C3":"A","\u0100":"A","\u0102":"A","\u1EB0":"A","\u1EAE":"A","\u1EB4":"A","\u1EB2":"A","\u0226":"A","\u01E0":"A","\u00C4":"A","\u01DE":"A","\u1EA2":"A","\u00C5":"A","\u01FA":"A","\u01CD":"A","\u0200":"A","\u0202":"A","\u1EA0":"A","\u1EAC":"A","\u1EB6":"A","\u1E00":"A","\u0104":"A","\u023A":"A","\u2C6F":"A","\uA732":"AA","\u00C6":"AE","\u01FC":"AE","\u01E2":"AE","\uA734":"AO","\uA736":"AU","\uA738":"AV","\uA73A":"AV","\uA73C":"AY","\u24B7":"B","\uFF22":"B","\u1E02":"B","\u1E04":"B","\u1E06":"B","\u0243":"B","\u0182":"B","\u0181":"B","\u24B8":"C","\uFF23":"C","\u0106":"C","\u0108":"C","\u010A":"C","\u010C":"C","\u00C7":"C","\u1E08":"C","\u0187":"C","\u023B":"C","\uA73E":"C","\u24B9":"D","\uFF24":"D","\u1E0A":"D","\u010E":"D","\u1E0C":"D","\u1E10":"D","\u1E12":"D","\u1E0E":"D","\u0110":"D","\u018B":"D","\u018A":"D","\u0189":"D","\uA779":"D","\u01F1":"DZ","\u01C4":"DZ","\u01F2":"Dz","\u01C5":"Dz","\u24BA":"E","\uFF25":"E","\u00C8":"E","\u00C9":"E","\u00CA":"E","\u1EC0":"E","\u1EBE":"E","\u1EC4":"E","\u1EC2":"E","\u1EBC":"E","\u0112":"E","\u1E14":"E","\u1E16":"E","\u0114":"E","\u0116":"E","\u00CB":"E","\u1EBA":"E","\u011A":"E","\u0204":"E","\u0206":"E","\u1EB8":"E","\u1EC6":"E","\u0228":"E","\u1E1C":"E","\u0118":"E","\u1E18":"E","\u1E1A":"E","\u0190":"E","\u018E":"E","\u24BB":"F","\uFF26":"F","\u1E1E":"F","\u0191":"F","\uA77B":"F","\u24BC":"G","\uFF27":"G","\u01F4":"G","\u011C":"G","\u1E20":"G","\u011E":"G","\u0120":"G","\u01E6":"G","\u0122":"G","\u01E4":"G","\u0193":"G","\uA7A0":"G","\uA77D":"G","\uA77E":"G","\u24BD":"H","\uFF28":"H","\u0124":"H","\u1E22":"H","\u1E26":"H","\u021E":"H","\u1E24":"H","\u1E28":"H","\u1E2A":"H","\u0126":"H","\u2C67":"H","\u2C75":"H","\uA78D":"H","\u24BE":"I","\uFF29":"I","\u00CC":"I","\u00CD":"I","\u00CE":"I","\u0128":"I","\u012A":"I","\u012C":"I","\u0130":"I","\u00CF":"I","\u1E2E":"I","\u1EC8":"I","\u01CF":"I","\u0208":"I","\u020A":"I","\u1ECA":"I","\u012E":"I","\u1E2C":"I","\u0197":"I","\u24BF":"J","\uFF2A":"J","\u0134":"J","\u0248":"J","\u24C0":"K","\uFF2B":"K","\u1E30":"K","\u01E8":"K","\u1E32":"K","\u0136":"K","\u1E34":"K","\u0198":"K","\u2C69":"K","\uA740":"K","\uA742":"K","\uA744":"K","\uA7A2":"K","\u24C1":"L","\uFF2C":"L","\u013F":"L","\u0139":"L","\u013D":"L","\u1E36":"L","\u1E38":"L","\u013B":"L","\u1E3C":"L","\u1E3A":"L","\u0141":"L","\u023D":"L","\u2C62":"L","\u2C60":"L","\uA748":"L","\uA746":"L","\uA780":"L","\u01C7":"LJ","\u01C8":"Lj","\u24C2":"M","\uFF2D":"M","\u1E3E":"M","\u1E40":"M","\u1E42":"M","\u2C6E":"M","\u019C":"M","\u24C3":"N","\uFF2E":"N","\u01F8":"N","\u0143":"N","\u00D1":"N","\u1E44":"N","\u0147":"N","\u1E46":"N","\u0145":"N","\u1E4A":"N","\u1E48":"N","\u0220":"N","\u019D":"N","\uA790":"N","\uA7A4":"N","\u01CA":"NJ","\u01CB":"Nj","\u24C4":"O","\uFF2F":"O","\u00D2":"O","\u00D3":"O","\u00D4":"O","\u1ED2":"O","\u1ED0":"O","\u1ED6":"O","\u1ED4":"O","\u00D5":"O","\u1E4C":"O","\u022C":"O","\u1E4E":"O","\u014C":"O","\u1E50":"O","\u1E52":"O","\u014E":"O","\u022E":"O","\u0230":"O","\u00D6":"O","\u022A":"O","\u1ECE":"O","\u0150":"O","\u01D1":"O","\u020C":"O","\u020E":"O","\u01A0":"O","\u1EDC":"O","\u1EDA":"O","\u1EE0":"O","\u1EDE":"O","\u1EE2":"O","\u1ECC":"O","\u1ED8":"O","\u01EA":"O","\u01EC":"O","\u00D8":"O","\u01FE":"O","\u0186":"O","\u019F":"O","\uA74A":"O","\uA74C":"O","\u01A2":"OI","\uA74E":"OO","\u0222":"OU","\u24C5":"P","\uFF30":"P","\u1E54":"P","\u1E56":"P","\u01A4":"P","\u2C63":"P","\uA750":"P","\uA752":"P","\uA754":"P","\u24C6":"Q","\uFF31":"Q","\uA756":"Q","\uA758":"Q","\u024A":"Q","\u24C7":"R","\uFF32":"R","\u0154":"R","\u1E58":"R","\u0158":"R","\u0210":"R","\u0212":"R","\u1E5A":"R","\u1E5C":"R","\u0156":"R","\u1E5E":"R","\u024C":"R","\u2C64":"R","\uA75A":"R","\uA7A6":"R","\uA782":"R","\u24C8":"S","\uFF33":"S","\u1E9E":"S","\u015A":"S","\u1E64":"S","\u015C":"S","\u1E60":"S","\u0160":"S","\u1E66":"S","\u1E62":"S","\u1E68":"S","\u0218":"S","\u015E":"S","\u2C7E":"S","\uA7A8":"S","\uA784":"S","\u24C9":"T","\uFF34":"T","\u1E6A":"T","\u0164":"T","\u1E6C":"T","\u021A":"T","\u0162":"T","\u1E70":"T","\u1E6E":"T","\u0166":"T","\u01AC":"T","\u01AE":"T","\u023E":"T","\uA786":"T","\uA728":"TZ","\u24CA":"U","\uFF35":"U","\u00D9":"U","\u00DA":"U","\u00DB":"U","\u0168":"U","\u1E78":"U","\u016A":"U","\u1E7A":"U","\u016C":"U","\u00DC":"U","\u01DB":"U","\u01D7":"U","\u01D5":"U","\u01D9":"U","\u1EE6":"U","\u016E":"U","\u0170":"U","\u01D3":"U","\u0214":"U","\u0216":"U","\u01AF":"U","\u1EEA":"U","\u1EE8":"U","\u1EEE":"U","\u1EEC":"U","\u1EF0":"U","\u1EE4":"U","\u1E72":"U","\u0172":"U","\u1E76":"U","\u1E74":"U","\u0244":"U","\u24CB":"V","\uFF36":"V","\u1E7C":"V","\u1E7E":"V","\u01B2":"V","\uA75E":"V","\u0245":"V","\uA760":"VY","\u24CC":"W","\uFF37":"W","\u1E80":"W","\u1E82":"W","\u0174":"W","\u1E86":"W","\u1E84":"W","\u1E88":"W","\u2C72":"W","\u24CD":"X","\uFF38":"X","\u1E8A":"X","\u1E8C":"X","\u24CE":"Y","\uFF39":"Y","\u1EF2":"Y","\u00DD":"Y","\u0176":"Y","\u1EF8":"Y","\u0232":"Y","\u1E8E":"Y","\u0178":"Y","\u1EF6":"Y","\u1EF4":"Y","\u01B3":"Y","\u024E":"Y","\u1EFE":"Y","\u24CF":"Z","\uFF3A":"Z","\u0179":"Z","\u1E90":"Z","\u017B":"Z","\u017D":"Z","\u1E92":"Z","\u1E94":"Z","\u01B5":"Z","\u0224":"Z","\u2C7F":"Z","\u2C6B":"Z","\uA762":"Z","\u24D0":"a","\uFF41":"a","\u1E9A":"a","\u00E0":"a","\u00E1":"a","\u00E2":"a","\u1EA7":"a","\u1EA5":"a","\u1EAB":"a","\u1EA9":"a","\u00E3":"a","\u0101":"a","\u0103":"a","\u1EB1":"a","\u1EAF":"a","\u1EB5":"a","\u1EB3":"a","\u0227":"a","\u01E1":"a","\u00E4":"a","\u01DF":"a","\u1EA3":"a","\u00E5":"a","\u01FB":"a","\u01CE":"a","\u0201":"a","\u0203":"a","\u1EA1":"a","\u1EAD":"a","\u1EB7":"a","\u1E01":"a","\u0105":"a","\u2C65":"a","\u0250":"a","\uA733":"aa","\u00E6":"ae","\u01FD":"ae","\u01E3":"ae","\uA735":"ao","\uA737":"au","\uA739":"av","\uA73B":"av","\uA73D":"ay","\u24D1":"b","\uFF42":"b","\u1E03":"b","\u1E05":"b","\u1E07":"b","\u0180":"b","\u0183":"b","\u0253":"b","\u24D2":"c","\uFF43":"c","\u0107":"c","\u0109":"c","\u010B":"c","\u010D":"c","\u00E7":"c","\u1E09":"c","\u0188":"c","\u023C":"c","\uA73F":"c","\u2184":"c","\u24D3":"d","\uFF44":"d","\u1E0B":"d","\u010F":"d","\u1E0D":"d","\u1E11":"d","\u1E13":"d","\u1E0F":"d","\u0111":"d","\u018C":"d","\u0256":"d","\u0257":"d","\uA77A":"d","\u01F3":"dz","\u01C6":"dz","\u24D4":"e","\uFF45":"e","\u00E8":"e","\u00E9":"e","\u00EA":"e","\u1EC1":"e","\u1EBF":"e","\u1EC5":"e","\u1EC3":"e","\u1EBD":"e","\u0113":"e","\u1E15":"e","\u1E17":"e","\u0115":"e","\u0117":"e","\u00EB":"e","\u1EBB":"e","\u011B":"e","\u0205":"e","\u0207":"e","\u1EB9":"e","\u1EC7":"e","\u0229":"e","\u1E1D":"e","\u0119":"e","\u1E19":"e","\u1E1B":"e","\u0247":"e","\u025B":"e","\u01DD":"e","\u24D5":"f","\uFF46":"f","\u1E1F":"f","\u0192":"f","\uA77C":"f","\u24D6":"g","\uFF47":"g","\u01F5":"g","\u011D":"g","\u1E21":"g","\u011F":"g","\u0121":"g","\u01E7":"g","\u0123":"g","\u01E5":"g","\u0260":"g","\uA7A1":"g","\u1D79":"g","\uA77F":"g","\u24D7":"h","\uFF48":"h","\u0125":"h","\u1E23":"h","\u1E27":"h","\u021F":"h","\u1E25":"h","\u1E29":"h","\u1E2B":"h","\u1E96":"h","\u0127":"h","\u2C68":"h","\u2C76":"h","\u0265":"h","\u0195":"hv","\u24D8":"i","\uFF49":"i","\u00EC":"i","\u00ED":"i","\u00EE":"i","\u0129":"i","\u012B":"i","\u012D":"i","\u00EF":"i","\u1E2F":"i","\u1EC9":"i","\u01D0":"i","\u0209":"i","\u020B":"i","\u1ECB":"i","\u012F":"i","\u1E2D":"i","\u0268":"i","\u0131":"i","\u24D9":"j","\uFF4A":"j","\u0135":"j","\u01F0":"j","\u0249":"j","\u24DA":"k","\uFF4B":"k","\u1E31":"k","\u01E9":"k","\u1E33":"k","\u0137":"k","\u1E35":"k","\u0199":"k","\u2C6A":"k","\uA741":"k","\uA743":"k","\uA745":"k","\uA7A3":"k","\u24DB":"l","\uFF4C":"l","\u0140":"l","\u013A":"l","\u013E":"l","\u1E37":"l","\u1E39":"l","\u013C":"l","\u1E3D":"l","\u1E3B":"l","\u017F":"l","\u0142":"l","\u019A":"l","\u026B":"l","\u2C61":"l","\uA749":"l","\uA781":"l","\uA747":"l","\u01C9":"lj","\u24DC":"m","\uFF4D":"m","\u1E3F":"m","\u1E41":"m","\u1E43":"m","\u0271":"m","\u026F":"m","\u24DD":"n","\uFF4E":"n","\u01F9":"n","\u0144":"n","\u00F1":"n","\u1E45":"n","\u0148":"n","\u1E47":"n","\u0146":"n","\u1E4B":"n","\u1E49":"n","\u019E":"n","\u0272":"n","\u0149":"n","\uA791":"n","\uA7A5":"n","\u01CC":"nj","\u24DE":"o","\uFF4F":"o","\u00F2":"o","\u00F3":"o","\u00F4":"o","\u1ED3":"o","\u1ED1":"o","\u1ED7":"o","\u1ED5":"o","\u00F5":"o","\u1E4D":"o","\u022D":"o","\u1E4F":"o","\u014D":"o","\u1E51":"o","\u1E53":"o","\u014F":"o","\u022F":"o","\u0231":"o","\u00F6":"o","\u022B":"o","\u1ECF":"o","\u0151":"o","\u01D2":"o","\u020D":"o","\u020F":"o","\u01A1":"o","\u1EDD":"o","\u1EDB":"o","\u1EE1":"o","\u1EDF":"o","\u1EE3":"o","\u1ECD":"o","\u1ED9":"o","\u01EB":"o","\u01ED":"o","\u00F8":"o","\u01FF":"o","\u0254":"o","\uA74B":"o","\uA74D":"o","\u0275":"o","\u01A3":"oi","\u0223":"ou","\uA74F":"oo","\u24DF":"p","\uFF50":"p","\u1E55":"p","\u1E57":"p","\u01A5":"p","\u1D7D":"p","\uA751":"p","\uA753":"p","\uA755":"p","\u24E0":"q","\uFF51":"q","\u024B":"q","\uA757":"q","\uA759":"q","\u24E1":"r","\uFF52":"r","\u0155":"r","\u1E59":"r","\u0159":"r","\u0211":"r","\u0213":"r","\u1E5B":"r","\u1E5D":"r","\u0157":"r","\u1E5F":"r","\u024D":"r","\u027D":"r","\uA75B":"r","\uA7A7":"r","\uA783":"r","\u24E2":"s","\uFF53":"s","\u00DF":"s","\u015B":"s","\u1E65":"s","\u015D":"s","\u1E61":"s","\u0161":"s","\u1E67":"s","\u1E63":"s","\u1E69":"s","\u0219":"s","\u015F":"s","\u023F":"s","\uA7A9":"s","\uA785":"s","\u1E9B":"s","\u24E3":"t","\uFF54":"t","\u1E6B":"t","\u1E97":"t","\u0165":"t","\u1E6D":"t","\u021B":"t","\u0163":"t","\u1E71":"t","\u1E6F":"t","\u0167":"t","\u01AD":"t","\u0288":"t","\u2C66":"t","\uA787":"t","\uA729":"tz","\u24E4":"u","\uFF55":"u","\u00F9":"u","\u00FA":"u","\u00FB":"u","\u0169":"u","\u1E79":"u","\u016B":"u","\u1E7B":"u","\u016D":"u","\u00FC":"u","\u01DC":"u","\u01D8":"u","\u01D6":"u","\u01DA":"u","\u1EE7":"u","\u016F":"u","\u0171":"u","\u01D4":"u","\u0215":"u","\u0217":"u","\u01B0":"u","\u1EEB":"u","\u1EE9":"u","\u1EEF":"u","\u1EED":"u","\u1EF1":"u","\u1EE5":"u","\u1E73":"u","\u0173":"u","\u1E77":"u","\u1E75":"u","\u0289":"u","\u24E5":"v","\uFF56":"v","\u1E7D":"v","\u1E7F":"v","\u028B":"v","\uA75F":"v","\u028C":"v","\uA761":"vy","\u24E6":"w","\uFF57":"w","\u1E81":"w","\u1E83":"w","\u0175":"w","\u1E87":"w","\u1E85":"w","\u1E98":"w","\u1E89":"w","\u2C73":"w","\u24E7":"x","\uFF58":"x","\u1E8B":"x","\u1E8D":"x","\u24E8":"y","\uFF59":"y","\u1EF3":"y","\u00FD":"y","\u0177":"y","\u1EF9":"y","\u0233":"y","\u1E8F":"y","\u00FF":"y","\u1EF7":"y","\u1E99":"y","\u1EF5":"y","\u01B4":"y","\u024F":"y","\u1EFF":"y","\u24E9":"z","\uFF5A":"z","\u017A":"z","\u1E91":"z","\u017C":"z","\u017E":"z","\u1E93":"z","\u1E95":"z","\u01B6":"z","\u0225":"z","\u0240":"z","\u2C6C":"z","\uA763":"z"};
+
+ $document = $(document);
+
+ nextUid=(function() { var counter=1; return function() { return counter++; }; }());
+
+
+ function stripDiacritics(str) {
+ var ret, i, l, c;
+
+ if (!str || str.length < 1) return str;
+
+ ret = "";
+ for (i = 0, l = str.length; i < l; i++) {
+ c = str.charAt(i);
+ ret += DIACRITICS[c] || c;
+ }
+ return ret;
+ }
+
+ function indexOf(value, array) {
+ var i = 0, l = array.length;
+ for (; i < l; i = i + 1) {
+ if (equal(value, array[i])) return i;
+ }
+ return -1;
+ }
+
+ function measureScrollbar () {
+ var $template = $( MEASURE_SCROLLBAR_TEMPLATE );
+ $template.appendTo('body');
+
+ var dim = {
+ width: $template.width() - $template[0].clientWidth,
+ height: $template.height() - $template[0].clientHeight
+ };
+ $template.remove();
+
+ return dim;
+ }
+
+ /**
+ * Compares equality of a and b
+ * @param a
+ * @param b
+ */
+ function equal(a, b) {
+ if (a === b) return true;
+ if (a === undefined || b === undefined) return false;
+ if (a === null || b === null) return false;
+ // Check whether 'a' or 'b' is a string (primitive or object).
+ // The concatenation of an empty string (+'') converts its argument to a string's primitive.
+ if (a.constructor === String) return a+'' === b+''; // a+'' - in case 'a' is a String object
+ if (b.constructor === String) return b+'' === a+''; // b+'' - in case 'b' is a String object
+ return false;
+ }
+
+ /**
+ * Splits the string into an array of values, trimming each value. An empty array is returned for nulls or empty
+ * strings
+ * @param string
+ * @param separator
+ */
+ function splitVal(string, separator) {
+ var val, i, l;
+ if (string === null || string.length < 1) return [];
+ val = string.split(separator);
+ for (i = 0, l = val.length; i < l; i = i + 1) val[i] = $.trim(val[i]);
+ return val;
+ }
+
+ function getSideBorderPadding(element) {
+ return element.outerWidth(false) - element.width();
+ }
+
+ function installKeyUpChangeEvent(element) {
+ var key="keyup-change-value";
+ element.on("keydown", function () {
+ if ($.data(element, key) === undefined) {
+ $.data(element, key, element.val());
+ }
+ });
+ element.on("keyup", function () {
+ var val= $.data(element, key);
+ if (val !== undefined && element.val() !== val) {
+ $.removeData(element, key);
+ element.trigger("keyup-change");
+ }
+ });
+ }
+
+ $document.on("mousemove", function (e) {
+ lastMousePosition.x = e.pageX;
+ lastMousePosition.y = e.pageY;
+ });
+
+ /**
+ * filters mouse events so an event is fired only if the mouse moved.
+ *
+ * filters out mouse events that occur when mouse is stationary but
+ * the elements under the pointer are scrolled.
+ */
+ function installFilteredMouseMove(element) {
+ element.on("mousemove", function (e) {
+ var lastpos = lastMousePosition;
+ if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) {
+ $(e.target).trigger("mousemove-filtered", e);
+ }
+ });
+ }
+
+ /**
+ * Debounces a function. Returns a function that calls the original fn function only if no invocations have been made
+ * within the last quietMillis milliseconds.
+ *
+ * @param quietMillis number of milliseconds to wait before invoking fn
+ * @param fn function to be debounced
+ * @param ctx object to be used as this reference within fn
+ * @return debounced version of fn
+ */
+ function debounce(quietMillis, fn, ctx) {
+ ctx = ctx || undefined;
+ var timeout;
+ return function () {
+ var args = arguments;
+ window.clearTimeout(timeout);
+ timeout = window.setTimeout(function() {
+ fn.apply(ctx, args);
+ }, quietMillis);
+ };
+ }
+
+ /**
+ * A simple implementation of a thunk
+ * @param formula function used to lazily initialize the thunk
+ * @return {Function}
+ */
+ function thunk(formula) {
+ var evaluated = false,
+ value;
+ return function() {
+ if (evaluated === false) { value = formula(); evaluated = true; }
+ return value;
+ };
+ };
+
+ function installDebouncedScroll(threshold, element) {
+ var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);});
+ element.on("scroll", function (e) {
+ if (indexOf(e.target, element.get()) >= 0) notify(e);
+ });
+ }
+
+ function focus($el) {
+ if ($el[0] === document.activeElement) return;
+
+ /* set the focus in a 0 timeout - that way the focus is set after the processing
+ of the current event has finished - which seems like the only reliable way
+ to set focus */
+ window.setTimeout(function() {
+ var el=$el[0], pos=$el.val().length, range;
+
+ $el.focus();
+
+ /* make sure el received focus so we do not error out when trying to manipulate the caret.
+ sometimes modals or others listeners may steal it after its set */
+ if ($el.is(":visible") && el === document.activeElement) {
+
+ /* after the focus is set move the caret to the end, necessary when we val()
+ just before setting focus */
+ if(el.setSelectionRange)
+ {
+ el.setSelectionRange(pos, pos);
+ }
+ else if (el.createTextRange) {
+ range = el.createTextRange();
+ range.collapse(false);
+ range.select();
+ }
+ }
+ }, 0);
+ }
+
+ function getCursorInfo(el) {
+ el = $(el)[0];
+ var offset = 0;
+ var length = 0;
+ if ('selectionStart' in el) {
+ offset = el.selectionStart;
+ length = el.selectionEnd - offset;
+ } else if ('selection' in document) {
+ el.focus();
+ var sel = document.selection.createRange();
+ length = document.selection.createRange().text.length;
+ sel.moveStart('character', -el.value.length);
+ offset = sel.text.length - length;
+ }
+ return { offset: offset, length: length };
+ }
+
+ function killEvent(event) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ function killEventImmediately(event) {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ }
+
+ function measureTextWidth(e) {
+ if (!sizer){
+ var style = e[0].currentStyle || window.getComputedStyle(e[0], null);
+ sizer = $(document.createElement("div")).css({
+ position: "absolute",
+ left: "-10000px",
+ top: "-10000px",
+ display: "none",
+ fontSize: style.fontSize,
+ fontFamily: style.fontFamily,
+ fontStyle: style.fontStyle,
+ fontWeight: style.fontWeight,
+ letterSpacing: style.letterSpacing,
+ textTransform: style.textTransform,
+ whiteSpace: "nowrap"
+ });
+ sizer.attr("class","select2-sizer");
+ $("body").append(sizer);
+ }
+ sizer.text(e.val());
+ return sizer.width();
+ }
+
+ function syncCssClasses(dest, src, adapter) {
+ var classes, replacements = [], adapted;
+
+ classes = dest.attr("class");
+ if (classes) {
+ classes = '' + classes; // for IE which returns object
+ $(classes.split(" ")).each2(function() {
+ if (this.indexOf("select2-") === 0) {
+ replacements.push(this);
+ }
+ });
+ }
+ classes = src.attr("class");
+ if (classes) {
+ classes = '' + classes; // for IE which returns object
+ $(classes.split(" ")).each2(function() {
+ if (this.indexOf("select2-") !== 0) {
+ adapted = adapter(this);
+ if (adapted) {
+ replacements.push(adapted);
+ }
+ }
+ });
+ }
+ dest.attr("class", replacements.join(" "));
+ }
+
+
+ function markMatch(text, term, markup, escapeMarkup) {
+ var match=stripDiacritics(text.toUpperCase()).indexOf(stripDiacritics(term.toUpperCase())),
+ tl=term.length;
+
+ if (match<0) {
+ markup.push(escapeMarkup(text));
+ return;
+ }
+
+ markup.push(escapeMarkup(text.substring(0, match)));
+ markup.push("<span class='select2-match'>");
+ markup.push(escapeMarkup(text.substring(match, match + tl)));
+ markup.push("</span>");
+ markup.push(escapeMarkup(text.substring(match + tl, text.length)));
+ }
+
+ function defaultEscapeMarkup(markup) {
+ var replace_map = {
+ '\\': '\',
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
+ "/": '/'
+ };
+
+ return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
+ return replace_map[match];
+ });
+ }
+
+ /**
+ * Produces an ajax-based query function
+ *
+ * @param options object containing configuration paramters
+ * @param options.params parameter map for the transport ajax call, can contain such options as cache, jsonpCallback, etc. see $.ajax
+ * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax
+ * @param options.url url for the data
+ * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url.
+ * @param options.dataType request data type: ajax, jsonp, other datatatypes supported by jQuery's $.ajax function or the transport function if specified
+ * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often
+ * @param options.results a function(remoteData, pageNumber) that converts data returned form the remote request to the format expected by Select2.
+ * The expected format is an object containing the following keys:
+ * results array of objects that will be used as choices
+ * more (optional) boolean indicating whether there are more results available
+ * Example: {results:[{id:1, text:'Red'},{id:2, text:'Blue'}], more:true}
+ */
+ function ajax(options) {
+ var timeout, // current scheduled but not yet executed request
+ handler = null,
+ quietMillis = options.quietMillis || 100,
+ ajaxUrl = options.url,
+ self = this;
+
+ return function (query) {
+ window.clearTimeout(timeout);
+ timeout = window.setTimeout(function () {
+ var data = options.data, // ajax data function
+ url = ajaxUrl, // ajax url string or function
+ transport = options.transport || $.fn.select2.ajaxDefaults.transport,
+ // deprecated - to be removed in 4.0 - use params instead
+ deprecated = {
+ type: options.type || 'GET', // set type of request (GET or POST)
+ cache: options.cache || false,
+ jsonpCallback: options.jsonpCallback||undefined,
+ dataType: options.dataType||"json"
+ },
+ params = $.extend({}, $.fn.select2.ajaxDefaults.params, deprecated);
+
+ data = data ? data.call(self, query.term, query.page, query.context) : null;
+ url = (typeof url === 'function') ? url.call(self, query.term, query.page, query.context) : url;
+
+ if (handler) { handler.abort(); }
+
+ if (options.params) {
+ if ($.isFunction(options.params)) {
+ $.extend(params, options.params.call(self));
+ } else {
+ $.extend(params, options.params);
+ }
+ }
+
+ $.extend(params, {
+ url: url,
+ dataType: options.dataType,
+ data: data,
+ success: function (data) {
+ // TODO - replace query.page with query so users have access to term, page, etc.
+ var results = options.results(data, query.page);
+ query.callback(results);
+ }
+ });
+ handler = transport.call(self, params);
+ }, quietMillis);
+ };
+ }
+
+ /**
+ * Produces a query function that works with a local array
+ *
+ * @param options object containing configuration parameters. The options parameter can either be an array or an
+ * object.
+ *
+ * If the array form is used it is assumed that it contains objects with 'id' and 'text' keys.
+ *
+ * If the object form is used ti is assumed that it contains 'data' and 'text' keys. The 'data' key should contain
+ * an array of objects that will be used as choices. These objects must contain at least an 'id' key. The 'text'
+ * key can either be a String in which case it is expected that each element in the 'data' array has a key with the
+ * value of 'text' which will be used to match choices. Alternatively, text can be a function(item) that can extract
+ * the text.
+ */
+ function local(options) {
+ var data = options, // data elements
+ dataText,
+ tmp,
+ text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search
+
+ if ($.isArray(data)) {
+ tmp = data;
+ data = { results: tmp };
+ }
+
+ if ($.isFunction(data) === false) {
+ tmp = data;
+ data = function() { return tmp; };
+ }
+
+ var dataItem = data();
+ if (dataItem.text) {
+ text = dataItem.text;
+ // if text is not a function we assume it to be a key name
+ if (!$.isFunction(text)) {
+ dataText = dataItem.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available
+ text = function (item) { return item[dataText]; };
+ }
+ }
+
+ return function (query) {
+ var t = query.term, filtered = { results: [] }, process;
+ if (t === "") {
+ query.callback(data());
+ return;
+ }
+
+ process = function(datum, collection) {
+ var group, attr;
+ datum = datum[0];
+ if (datum.children) {
+ group = {};
+ for (attr in datum) {
+ if (datum.hasOwnProperty(attr)) group[attr]=datum[attr];
+ }
+ group.children=[];
+ $(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); });
+ if (group.children.length || query.matcher(t, text(group), datum)) {
+ collection.push(group);
+ }
+ } else {
+ if (query.matcher(t, text(datum), datum)) {
+ collection.push(datum);
+ }
+ }
+ };
+
+ $(data().results).each2(function(i, datum) { process(datum, filtered.results); });
+ query.callback(filtered);
+ };
+ }
+
+ // TODO javadoc
+ function tags(data) {
+ var isFunc = $.isFunction(data);
+ return function (query) {
+ var t = query.term, filtered = {results: []};
+ $(isFunc ? data() : data).each(function () {
+ var isObject = this.text !== undefined,
+ text = isObject ? this.text : this;
+ if (t === "" || query.matcher(t, text)) {
+ filtered.results.push(isObject ? this : {id: this, text: this});
+ }
+ });
+ query.callback(filtered);
+ };
+ }
+
+ /**
+ * Checks if the formatter function should be used.
+ *
+ * Throws an error if it is not a function. Returns true if it should be used,
+ * false if no formatting should be performed.
+ *
+ * @param formatter
+ */
+ function checkFormatter(formatter, formatterName) {
+ if ($.isFunction(formatter)) return true;
+ if (!formatter) return false;
+ throw new Error(formatterName +" must be a function or a falsy value");
+ }
+
+ function evaluate(val) {
+ return $.isFunction(val) ? val() : val;
+ }
+
+ function countResults(results) {
+ var count = 0;
+ $.each(results, function(i, item) {
+ if (item.children) {
+ count += countResults(item.children);
+ } else {
+ count++;
+ }
+ });
+ return count;
+ }
+
+ /**
+ * Default tokenizer. This function uses breaks the input on substring match of any string from the
+ * opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those
+ * two options have to be defined in order for the tokenizer to work.
+ *
+ * @param input text user has typed so far or pasted into the search field
+ * @param selection currently selected choices
+ * @param selectCallback function(choice) callback tho add the choice to selection
+ * @param opts select2's opts
+ * @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value
+ */
+ function defaultTokenizer(input, selection, selectCallback, opts) {
+ var original = input, // store the original so we can compare and know if we need to tell the search to update its text
+ dupe = false, // check for whether a token we extracted represents a duplicate selected choice
+ token, // token
+ index, // position at which the separator was found
+ i, l, // looping variables
+ separator; // the matched separator
+
+ if (!opts.createSearchChoice || !opts.tokenSeparators || opts.tokenSeparators.length < 1) return undefined;
+
+ while (true) {
+ index = -1;
+
+ for (i = 0, l = opts.tokenSeparators.length; i < l; i++) {
+ separator = opts.tokenSeparators[i];
+ index = input.indexOf(separator);
+ if (index >= 0) break;
+ }
+
+ if (index < 0) break; // did not find any token separator in the input string, bail
+
+ token = input.substring(0, index);
+ input = input.substring(index + separator.length);
+
+ if (token.length > 0) {
+ token = opts.createSearchChoice.call(this, token, selection);
+ if (token !== undefined && token !== null && opts.id(token) !== undefined && opts.id(token) !== null) {
+ dupe = false;
+ for (i = 0, l = selection.length; i < l; i++) {
+ if (equal(opts.id(token), opts.id(selection[i]))) {
+ dupe = true; break;
+ }
+ }
+
+ if (!dupe) selectCallback(token);
+ }
+ }
+ }
+
+ if (original!==input) return input;
+ }
+
+ /**
+ * Creates a new class
+ *
+ * @param superClass
+ * @param methods
+ */
+ function clazz(SuperClass, methods) {
+ var constructor = function () {};
+ constructor.prototype = new SuperClass;
+ constructor.prototype.constructor = constructor;
+ constructor.prototype.parent = SuperClass.prototype;
+ constructor.prototype = $.extend(constructor.prototype, methods);
+ return constructor;
+ }
+
+ AbstractSelect2 = clazz(Object, {
+
+ // abstract
+ bind: function (func) {
+ var self = this;
+ return function () {
+ func.apply(self, arguments);
+ };
+ },
+
+ // abstract
+ init: function (opts) {
+ var results, search, resultsSelector = ".select2-results";
+
+ // prepare options
+ this.opts = opts = this.prepareOpts(opts);
+
+ this.id=opts.id;
+
+ // destroy if called on an existing component
+ if (opts.element.data("select2") !== undefined &&
+ opts.element.data("select2") !== null) {
+ opts.element.data("select2").destroy();
+ }
+
+ this.container = this.createContainer();
+
+ this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid());
+ this.containerSelector="#"+this.containerId.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1');
+ this.container.attr("id", this.containerId);
+
+ // cache the body so future lookups are cheap
+ this.body = thunk(function() { return opts.element.closest("body"); });
+
+ syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
+
+ this.container.attr("style", opts.element.attr("style"));
+ this.container.css(evaluate(opts.containerCss));
+ this.container.addClass(evaluate(opts.containerCssClass));
+
+ this.elementTabIndex = this.opts.element.attr("tabindex");
+
+ // swap container for the element
+ this.opts.element
+ .data("select2", this)
+ .attr("tabindex", "-1")
+ .before(this.container)
+ .on("click.select2", killEvent); // do not leak click events
+
+ this.container.data("select2", this);
+
+ this.dropdown = this.container.find(".select2-drop");
+
+ syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass);
+
+ this.dropdown.addClass(evaluate(opts.dropdownCssClass));
+ this.dropdown.data("select2", this);
+ this.dropdown.on("click", killEvent);
+
+ this.results = results = this.container.find(resultsSelector);
+ this.search = search = this.container.find("input.select2-input");
+
+ this.queryCount = 0;
+ this.resultsPage = 0;
+ this.context = null;
+
+ // initialize the container
+ this.initContainer();
+
+ this.container.on("click", killEvent);
+
+ installFilteredMouseMove(this.results);
+ this.dropdown.on("mousemove-filtered touchstart touchmove touchend", resultsSelector, this.bind(this.highlightUnderEvent));
+
+ installDebouncedScroll(80, this.results);
+ this.dropdown.on("scroll-debounced", resultsSelector, this.bind(this.loadMoreIfNeeded));
+
+ // do not propagate change event from the search field out of the component
+ $(this.container).on("change", ".select2-input", function(e) {e.stopPropagation();});
+ $(this.dropdown).on("change", ".select2-input", function(e) {e.stopPropagation();});
+
+ // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel
+ if ($.fn.mousewheel) {
+ results.mousewheel(function (e, delta, deltaX, deltaY) {
+ var top = results.scrollTop();
+ if (deltaY > 0 && top - deltaY <= 0) {
+ results.scrollTop(0);
+ killEvent(e);
+ } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) {
+ results.scrollTop(results.get(0).scrollHeight - results.height());
+ killEvent(e);
+ }
+ });
+ }
+
+ installKeyUpChangeEvent(search);
+ search.on("keyup-change input paste", this.bind(this.updateResults));
+ search.on("focus", function () { search.addClass("select2-focused"); });
+ search.on("blur", function () { search.removeClass("select2-focused");});
+
+ this.dropdown.on("mouseup", resultsSelector, this.bind(function (e) {
+ if ($(e.target).closest(".select2-result-selectable").length > 0) {
+ this.highlightUnderEvent(e);
+ this.selectHighlighted(e);
+ }
+ }));
+
+ // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening
+ // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's
+ // dom it will trigger the popup close, which is not what we want
+ this.dropdown.on("click mouseup mousedown", function (e) { e.stopPropagation(); });
+
+ if ($.isFunction(this.opts.initSelection)) {
+ // initialize selection based on the current value of the source element
+ this.initSelection();
+
+ // if the user has provided a function that can set selection based on the value of the source element
+ // we monitor the change event on the element and trigger it, allowing for two way synchronization
+ this.monitorSource();
+ }
+
+ if (opts.maximumInputLength !== null) {
+ this.search.attr("maxlength", opts.maximumInputLength);
+ }
+
+ var disabled = opts.element.prop("disabled");
+ if (disabled === undefined) disabled = false;
+ this.enable(!disabled);
+
+ var readonly = opts.element.prop("readonly");
+ if (readonly === undefined) readonly = false;
+ this.readonly(readonly);
+
+ // Calculate size of scrollbar
+ scrollBarDimensions = scrollBarDimensions || measureScrollbar();
+
+ this.autofocus = opts.element.prop("autofocus");
+ opts.element.prop("autofocus", false);
+ if (this.autofocus) this.focus();
+
+ this.nextSearchTerm = undefined;
+ },
+
+ // abstract
+ destroy: function () {
+ var element=this.opts.element, select2 = element.data("select2");
+
+ this.close();
+
+ if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; }
+
+ if (select2 !== undefined) {
+ select2.container.remove();
+ select2.dropdown.remove();
+ element
+ .removeClass("select2-offscreen")
+ .removeData("select2")
+ .off(".select2")
+ .prop("autofocus", this.autofocus || false);
+ if (this.elementTabIndex) {
+ element.attr({tabindex: this.elementTabIndex});
+ } else {
+ element.removeAttr("tabindex");
+ }
+ element.show();
+ }
+ },
+
+ // abstract
+ optionToData: function(element) {
+ if (element.is("option")) {
+ return {
+ id:element.prop("value"),
+ text:element.text(),
+ element: element.get(),
+ css: element.attr("class"),
+ disabled: element.prop("disabled"),
+ locked: equal(element.attr("locked"), "locked") || equal(element.data("locked"), true)
+ };
+ } else if (element.is("optgroup")) {
+ return {
+ text:element.attr("label"),
+ children:[],
+ element: element.get(),
+ css: element.attr("class")
+ };
+ }
+ },
+
+ // abstract
+ prepareOpts: function (opts) {
+ var element, select, idKey, ajaxUrl, self = this;
+
+ element = opts.element;
+
+ if (element.get(0).tagName.toLowerCase() === "select") {
+ this.select = select = opts.element;
+ }
+
+ if (select) {
+ // these options are not allowed when attached to a select because they are picked up off the element itself
+ $.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () {
+ if (this in opts) {
+ throw new Error("Option '" + this + "' is not allowed for Select2 when attached to a <select> element.");
+ }
+ });
+ }
+
+ opts = $.extend({}, {
+ populateResults: function(container, results, query) {
+ var populate, id=this.opts.id;
+
+ populate=function(results, container, depth) {
+
+ var i, l, result, selectable, disabled, compound, node, label, innerContainer, formatted;
+
+ results = opts.sortResults(results, container, query);
+
+ for (i = 0, l = results.length; i < l; i = i + 1) {
+
+ result=results[i];
+
+ disabled = (result.disabled === true);
+ selectable = (!disabled) && (id(result) !== undefined);
+
+ compound=result.children && result.children.length > 0;
+
+ node=$("<li></li>");
+ node.addClass("select2-results-dept-"+depth);
+ node.addClass("select2-result");
+ node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable");
+ if (disabled) { node.addClass("select2-disabled"); }
+ if (compound) { node.addClass("select2-result-with-children"); }
+ node.addClass(self.opts.formatResultCssClass(result));
+
+ label=$(document.createElement("div"));
+ label.addClass("select2-result-label");
+
+ formatted=opts.formatResult(result, label, query, self.opts.escapeMarkup);
+ if (formatted!==undefined) {
+ label.html(formatted);
+ }
+
+ node.append(label);
+
+ if (compound) {
+
+ innerContainer=$("<ul></ul>");
+ innerContainer.addClass("select2-result-sub");
+ populate(result.children, innerContainer, depth+1);
+ node.append(innerContainer);
+ }
+
+ node.data("select2-data", result);
+ container.append(node);
+ }
+ };
+
+ populate(results, container, 0);
+ }
+ }, $.fn.select2.defaults, opts);
+
+ if (typeof(opts.id) !== "function") {
+ idKey = opts.id;
+ opts.id = function (e) { return e[idKey]; };
+ }
+
+ if ($.isArray(opts.element.data("select2Tags"))) {
+ if ("tags" in opts) {
+ throw "tags specified as both an attribute 'data-select2-tags' and in options of Select2 " + opts.element.attr("id");
+ }
+ opts.tags=opts.element.data("select2Tags");
+ }
+
+ if (select) {
+ opts.query = this.bind(function (query) {
+ var data = { results: [], more: false },
+ term = query.term,
+ children, placeholderOption, process;
+
+ process=function(element, collection) {
+ var group;
+ if (element.is("option")) {
+ if (query.matcher(term, element.text(), element)) {
+ collection.push(self.optionToData(element));
+ }
+ } else if (element.is("optgroup")) {
+ group=self.optionToData(element);
+ element.children().each2(function(i, elm) { process(elm, group.children); });
+ if (group.children.length>0) {
+ collection.push(group);
+ }
+ }
+ };
+
+ children=element.children();
+
+ // ignore the placeholder option if there is one
+ if (this.getPlaceholder() !== undefined && children.length > 0) {
+ placeholderOption = this.getPlaceholderOption();
+ if (placeholderOption) {
+ children=children.not(placeholderOption);
+ }
+ }
+
+ children.each2(function(i, elm) { process(elm, data.results); });
+
+ query.callback(data);
+ });
+ // this is needed because inside val() we construct choices from options and there id is hardcoded
+ opts.id=function(e) { return e.id; };
+ opts.formatResultCssClass = function(data) { return data.css; };
+ } else {
+ if (!("query" in opts)) {
+
+ if ("ajax" in opts) {
+ ajaxUrl = opts.element.data("ajax-url");
+ if (ajaxUrl && ajaxUrl.length > 0) {
+ opts.ajax.url = ajaxUrl;
+ }
+ opts.query = ajax.call(opts.element, opts.ajax);
+ } else if ("data" in opts) {
+ opts.query = local(opts.data);
+ } else if ("tags" in opts) {
+ opts.query = tags(opts.tags);
+ if (opts.createSearchChoice === undefined) {
+ opts.createSearchChoice = function (term) { return {id: $.trim(term), text: $.trim(term)}; };
+ }
+ if (opts.initSelection === undefined) {
+ opts.initSelection = function (element, callback) {
+ var data = [];
+ $(splitVal(element.val(), opts.separator)).each(function () {
+ var obj = { id: this, text: this },
+ tags = opts.tags;
+ if ($.isFunction(tags)) tags=tags();
+ $(tags).each(function() { if (equal(this.id, obj.id)) { obj = this; return false; } });
+ data.push(obj);
+ });
+
+ callback(data);
+ };
+ }
+ }
+ }
+ }
+ if (typeof(opts.query) !== "function") {
+ throw "query function not defined for Select2 " + opts.element.attr("id");
+ }
+
+ return opts;
+ },
+
+ /**
+ * Monitor the original element for changes and update select2 accordingly
+ */
+ // abstract
+ monitorSource: function () {
+ var el = this.opts.element, sync, observer;
+
+ el.on("change.select2", this.bind(function (e) {
+ if (this.opts.element.data("select2-change-triggered") !== true) {
+ this.initSelection();
+ }
+ }));
+
+ sync = this.bind(function () {
+
+ // sync enabled state
+ var disabled = el.prop("disabled");
+ if (disabled === undefined) disabled = false;
+ this.enable(!disabled);
+
+ var readonly = el.prop("readonly");
+ if (readonly === undefined) readonly = false;
+ this.readonly(readonly);
+
+ syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
+ this.container.addClass(evaluate(this.opts.containerCssClass));
+
+ syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass);
+ this.dropdown.addClass(evaluate(this.opts.dropdownCssClass));
+
+ });
+
+ // IE8-10
+ el.on("propertychange.select2", sync);
+
+ // hold onto a reference of the callback to work around a chromium bug
+ if (this.mutationCallback === undefined) {
+ this.mutationCallback = function (mutations) {
+ mutations.forEach(sync);
+ }
+ }
+
+ // safari, chrome, firefox, IE11
+ observer = window.MutationObserver || window.WebKitMutationObserver|| window.MozMutationObserver;
+ if (observer !== undefined) {
+ if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; }
+ this.propertyObserver = new observer(this.mutationCallback);
+ this.propertyObserver.observe(el.get(0), { attributes:true, subtree:false });
+ }
+ },
+
+ // abstract
+ triggerSelect: function(data) {
+ var evt = $.Event("select2-selecting", { val: this.id(data), object: data });
+ this.opts.element.trigger(evt);
+ return !evt.isDefaultPrevented();
+ },
+
+ /**
+ * Triggers the change event on the source element
+ */
+ // abstract
+ triggerChange: function (details) {
+
+ details = details || {};
+ details= $.extend({}, details, { type: "change", val: this.val() });
+ // prevents recursive triggering
+ this.opts.element.data("select2-change-triggered", true);
+ this.opts.element.trigger(details);
+ this.opts.element.data("select2-change-triggered", false);
+
+ // some validation frameworks ignore the change event and listen instead to keyup, click for selects
+ // so here we trigger the click event manually
+ this.opts.element.click();
+
+ // ValidationEngine ignorea the change event and listens instead to blur
+ // so here we trigger the blur event manually if so desired
+ if (this.opts.blurOnChange)
+ this.opts.element.blur();
+ },
+
+ //abstract
+ isInterfaceEnabled: function()
+ {
+ return this.enabledInterface === true;
+ },
+
+ // abstract
+ enableInterface: function() {
+ var enabled = this._enabled && !this._readonly,
+ disabled = !enabled;
+
+ if (enabled === this.enabledInterface) return false;
+
+ this.container.toggleClass("select2-container-disabled", disabled);
+ this.close();
+ this.enabledInterface = enabled;
+
+ return true;
+ },
+
+ // abstract
+ enable: function(enabled) {
+ if (enabled === undefined) enabled = true;
+ if (this._enabled === enabled) return;
+ this._enabled = enabled;
+
+ this.opts.element.prop("disabled", !enabled);
+ this.enableInterface();
+ },
+
+ // abstract
+ disable: function() {
+ this.enable(false);
+ },
+
+ // abstract
+ readonly: function(enabled) {
+ if (enabled === undefined) enabled = false;
+ if (this._readonly === enabled) return false;
+ this._readonly = enabled;
+
+ this.opts.element.prop("readonly", enabled);
+ this.enableInterface();
+ return true;
+ },
+
+ // abstract
+ opened: function () {
+ return this.container.hasClass("select2-dropdown-open");
+ },
+
+ // abstract
+ positionDropdown: function() {
+ var $dropdown = this.dropdown,
+ offset = this.container.offset(),
+ height = this.container.outerHeight(false),
+ width = this.container.outerWidth(false),
+ dropHeight = $dropdown.outerHeight(false),
+ $window = $(window),
+ windowWidth = $window.width(),
+ windowHeight = $window.height(),
+ viewPortRight = $window.scrollLeft() + windowWidth,
+ viewportBottom = $window.scrollTop() + windowHeight,
+ dropTop = offset.top + height,
+ dropLeft = offset.left,
+ enoughRoomBelow = dropTop + dropHeight <= viewportBottom,
+ enoughRoomAbove = (offset.top - dropHeight) >= this.body().scrollTop(),
+ dropWidth = $dropdown.outerWidth(false),
+ enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight,
+ aboveNow = $dropdown.hasClass("select2-drop-above"),
+ bodyOffset,
+ above,
+ changeDirection,
+ css,
+ resultsListNode;
+
+ // always prefer the current above/below alignment, unless there is not enough room
+ if (aboveNow) {
+ above = true;
+ if (!enoughRoomAbove && enoughRoomBelow) {
+ changeDirection = true;
+ above = false;
+ }
+ } else {
+ above = false;
+ if (!enoughRoomBelow && enoughRoomAbove) {
+ changeDirection = true;
+ above = true;
+ }
+ }
+
+ //if we are changing direction we need to get positions when dropdown is hidden;
+ if (changeDirection) {
+ $dropdown.hide();
+ offset = this.container.offset();
+ height = this.container.outerHeight(false);
+ width = this.container.outerWidth(false);
+ dropHeight = $dropdown.outerHeight(false);
+ viewPortRight = $window.scrollLeft() + windowWidth;
+ viewportBottom = $window.scrollTop() + windowHeight;
+ dropTop = offset.top + height;
+ dropLeft = offset.left;
+ dropWidth = $dropdown.outerWidth(false);
+ enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight;
+ $dropdown.show();
+ }
+
+ if (this.opts.dropdownAutoWidth) {
+ resultsListNode = $('.select2-results', $dropdown)[0];
+ $dropdown.addClass('select2-drop-auto-width');
+ $dropdown.css('width', '');
+ // Add scrollbar width to dropdown if vertical scrollbar is present
+ dropWidth = $dropdown.outerWidth(false) + (resultsListNode.scrollHeight === resultsListNode.clientHeight ? 0 : scrollBarDimensions.width);
+ dropWidth > width ? width = dropWidth : dropWidth = width;
+ enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight;
+ }
+ else {
+ this.container.removeClass('select2-drop-auto-width');
+ }
+
+ //console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow);
+ //console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body().scrollTop(), "enough?", enoughRoomAbove);
+
+ // fix positioning when body has an offset and is not position: static
+ if (this.body().css('position') !== 'static') {
+ bodyOffset = this.body().offset();
+ dropTop -= bodyOffset.top;
+ dropLeft -= bodyOffset.left;
+ }
+
+ if (!enoughRoomOnRight) {
+ dropLeft = offset.left + width - dropWidth;
+ }
+
+ css = {
+ left: dropLeft,
+ width: width
+ };
+
+ if (above) {
+ css.bottom = windowHeight - offset.top;
+ css.top = 'auto';
+ this.container.addClass("select2-drop-above");
+ $dropdown.addClass("select2-drop-above");
+ }
+ else {
+ css.top = dropTop;
+ css.bottom = 'auto';
+ this.container.removeClass("select2-drop-above");
+ $dropdown.removeClass("select2-drop-above");
+ }
+ css = $.extend(css, evaluate(this.opts.dropdownCss));
+
+ $dropdown.css(css);
+ },
+
+ // abstract
+ shouldOpen: function() {
+ var event;
+
+ if (this.opened()) return false;
+
+ if (this._enabled === false || this._readonly === true) return false;
+
+ event = $.Event("select2-opening");
+ this.opts.element.trigger(event);
+ return !event.isDefaultPrevented();
+ },
+
+ // abstract
+ clearDropdownAlignmentPreference: function() {
+ // clear the classes used to figure out the preference of where the dropdown should be opened
+ this.container.removeClass("select2-drop-above");
+ this.dropdown.removeClass("select2-drop-above");
+ },
+
+ /**
+ * Opens the dropdown
+ *
+ * @return {Boolean} whether or not dropdown was opened. This method will return false if, for example,
+ * the dropdown is already open, or if the 'open' event listener on the element called preventDefault().
+ */
+ // abstract
+ open: function () {
+
+ if (!this.shouldOpen()) return false;
+
+ this.opening();
+
+ return true;
+ },
+
+ /**
+ * Performs the opening of the dropdown
+ */
+ // abstract
+ opening: function() {
+ var cid = this.containerId,
+ scroll = "scroll." + cid,
+ resize = "resize."+cid,
+ orient = "orientationchange."+cid,
+ mask;
+
+ this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
+
+ this.clearDropdownAlignmentPreference();
+
+ if(this.dropdown[0] !== this.body().children().last()[0]) {
+ this.dropdown.detach().appendTo(this.body());
+ }
+
+ // create the dropdown mask if doesnt already exist
+ mask = $("#select2-drop-mask");
+ if (mask.length == 0) {
+ mask = $(document.createElement("div"));
+ mask.attr("id","select2-drop-mask").attr("class","select2-drop-mask");
+ mask.hide();
+ mask.appendTo(this.body());
+ mask.on("mousedown touchstart click", function (e) {
+ var dropdown = $("#select2-drop"), self;
+ if (dropdown.length > 0) {
+ self=dropdown.data("select2");
+ if (self.opts.selectOnBlur) {
+ self.selectHighlighted({noFocus: true});
+ }
+ self.close({focus:true});
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ });
+ }
+
+ // ensure the mask is always right before the dropdown
+ if (this.dropdown.prev()[0] !== mask[0]) {
+ this.dropdown.before(mask);
+ }
+
+ // move the global id to the correct dropdown
+ $("#select2-drop").removeAttr("id");
+ this.dropdown.attr("id", "select2-drop");
+
+ // show the elements
+ mask.show();
+
+ this.positionDropdown();
+ this.dropdown.show();
+ this.positionDropdown();
+
+ this.dropdown.addClass("select2-drop-active");
+
+ // attach listeners to events that can change the position of the container and thus require
+ // the position of the dropdown to be updated as well so it does not come unglued from the container
+ var that = this;
+ this.container.parents().add(window).each(function () {
+ $(this).on(resize+" "+scroll+" "+orient, function (e) {
+ that.positionDropdown();
+ });
+ });
+
+
+ },
+
+ // abstract
+ close: function () {
+ if (!this.opened()) return;
+
+ var cid = this.containerId,
+ scroll = "scroll." + cid,
+ resize = "resize."+cid,
+ orient = "orientationchange."+cid;
+
+ // unbind event listeners
+ this.container.parents().add(window).each(function () { $(this).off(scroll).off(resize).off(orient); });
+
+ this.clearDropdownAlignmentPreference();
+
+ $("#select2-drop-mask").hide();
+ this.dropdown.removeAttr("id"); // only the active dropdown has the select2-drop id
+ this.dropdown.hide();
+ this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active");
+ this.results.empty();
+
+
+ this.clearSearch();
+ this.search.removeClass("select2-active");
+ this.opts.element.trigger($.Event("select2-close"));
+ },
+
+ /**
+ * Opens control, sets input value, and updates results.
+ */
+ // abstract
+ externalSearch: function (term) {
+ this.open();
+ this.search.val(term);
+ this.updateResults(false);
+ },
+
+ // abstract
+ clearSearch: function () {
+
+ },
+
+ //abstract
+ getMaximumSelectionSize: function() {
+ return evaluate(this.opts.maximumSelectionSize);
+ },
+
+ // abstract
+ ensureHighlightVisible: function () {
+ var results = this.results, children, index, child, hb, rb, y, more;
+
+ index = this.highlight();
+
+ if (index < 0) return;
+
+ if (index == 0) {
+
+ // if the first element is highlighted scroll all the way to the top,
+ // that way any unselectable headers above it will also be scrolled
+ // into view
+
+ results.scrollTop(0);
+ return;
+ }
+
+ children = this.findHighlightableChoices().find('.select2-result-label');
+
+ child = $(children[index]);
+
+ hb = child.offset().top + child.outerHeight(true);
+
+ // if this is the last child lets also make sure select2-more-results is visible
+ if (index === children.length - 1) {
+ more = results.find("li.select2-more-results");
+ if (more.length > 0) {
+ hb = more.offset().top + more.outerHeight(true);
+ }
+ }
+
+ rb = results.offset().top + results.outerHeight(true);
+ if (hb > rb) {
+ results.scrollTop(results.scrollTop() + (hb - rb));
+ }
+ y = child.offset().top - results.offset().top;
+
+ // make sure the top of the element is visible
+ if (y < 0 && child.css('display') != 'none' ) {
+ results.scrollTop(results.scrollTop() + y); // y is negative
+ }
+ },
+
+ // abstract
+ findHighlightableChoices: function() {
+ return this.results.find(".select2-result-selectable:not(.select2-disabled, .select2-selected)");
+ },
+
+ // abstract
+ moveHighlight: function (delta) {
+ var choices = this.findHighlightableChoices(),
+ index = this.highlight();
+
+ while (index > -1 && index < choices.length) {
+ index += delta;
+ var choice = $(choices[index]);
+ if (choice.hasClass("select2-result-selectable") && !choice.hasClass("select2-disabled") && !choice.hasClass("select2-selected")) {
+ this.highlight(index);
+ break;
+ }
+ }
+ },
+
+ // abstract
+ highlight: function (index) {
+ var choices = this.findHighlightableChoices(),
+ choice,
+ data;
+
+ if (arguments.length === 0) {
+ return indexOf(choices.filter(".select2-highlighted")[0], choices.get());
+ }
+
+ if (index >= choices.length) index = choices.length - 1;
+ if (index < 0) index = 0;
+
+ this.removeHighlight();
+
+ choice = $(choices[index]);
+ choice.addClass("select2-highlighted");
+
+ this.ensureHighlightVisible();
+
+ data = choice.data("select2-data");
+ if (data) {
+ this.opts.element.trigger({ type: "select2-highlight", val: this.id(data), choice: data });
+ }
+ },
+
+ removeHighlight: function() {
+ this.results.find(".select2-highlighted").removeClass("select2-highlighted");
+ },
+
+ // abstract
+ countSelectableResults: function() {
+ return this.findHighlightableChoices().length;
+ },
+
+ // abstract
+ highlightUnderEvent: function (event) {
+ var el = $(event.target).closest(".select2-result-selectable");
+ if (el.length > 0 && !el.is(".select2-highlighted")) {
+ var choices = this.findHighlightableChoices();
+ this.highlight(choices.index(el));
+ } else if (el.length == 0) {
+ // if we are over an unselectable item remove all highlights
+ this.removeHighlight();
+ }
+ },
+
+ // abstract
+ loadMoreIfNeeded: function () {
+ var results = this.results,
+ more = results.find("li.select2-more-results"),
+ below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible
+ page = this.resultsPage + 1,
+ self=this,
+ term=this.search.val(),
+ context=this.context;
+
+ if (more.length === 0) return;
+ below = more.offset().top - results.offset().top - results.height();
+
+ if (below <= this.opts.loadMorePadding) {
+ more.addClass("select2-active");
+ this.opts.query({
+ element: this.opts.element,
+ term: term,
+ page: page,
+ context: context,
+ matcher: this.opts.matcher,
+ callback: this.bind(function (data) {
+
+ // ignore a response if the select2 has been closed before it was received
+ if (!self.opened()) return;
+
+
+ self.opts.populateResults.call(this, results, data.results, {term: term, page: page, context:context});
+ self.postprocessResults(data, false, false);
+
+ if (data.more===true) {
+ more.detach().appendTo(results).text(self.opts.formatLoadMore(page+1));
+ window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
+ } else {
+ more.remove();
+ }
+ self.positionDropdown();
+ self.resultsPage = page;
+ self.context = data.context;
+ this.opts.element.trigger({ type: "select2-loaded", items: data });
+ })});
+ }
+ },
+
+ /**
+ * Default tokenizer function which does nothing
+ */
+ tokenize: function() {
+
+ },
+
+ /**
+ * @param initial whether or not this is the call to this method right after the dropdown has been opened
+ */
+ // abstract
+ updateResults: function (initial) {
+ var search = this.search,
+ results = this.results,
+ opts = this.opts,
+ data,
+ self = this,
+ input,
+ term = search.val(),
+ lastTerm = $.data(this.container, "select2-last-term"),
+ // sequence number used to drop out-of-order responses
+ queryNumber;
+
+ // prevent duplicate queries against the same term
+ if (initial !== true && lastTerm && equal(term, lastTerm)) return;
+
+ $.data(this.container, "select2-last-term", term);
+
+ // if the search is currently hidden we do not alter the results
+ if (initial !== true && (this.showSearchInput === false || !this.opened())) {
+ return;
+ }
+
+ function postRender() {
+ search.removeClass("select2-active");
+ self.positionDropdown();
+ }
+
+ function render(html) {
+ results.html(html);
+ postRender();
+ }
+
+ queryNumber = ++this.queryCount;
+
+ var maxSelSize = this.getMaximumSelectionSize();
+ if (maxSelSize >=1) {
+ data = this.data();
+ if ($.isArray(data) && data.length >= maxSelSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) {
+ render("<li class='select2-selection-limit'>" + opts.formatSelectionTooBig(maxSelSize) + "</li>");
+ return;
+ }
+ }
+
+ if (search.val().length < opts.minimumInputLength) {
+ if (checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) {
+ render("<li class='select2-no-results'>" + opts.formatInputTooShort(search.val(), opts.minimumInputLength) + "</li>");
+ } else {
+ render("");
+ }
+ if (initial && this.showSearch) this.showSearch(true);
+ return;
+ }
+
+ if (opts.maximumInputLength && search.val().length > opts.maximumInputLength) {
+ if (checkFormatter(opts.formatInputTooLong, "formatInputTooLong")) {
+ render("<li class='select2-no-results'>" + opts.formatInputTooLong(search.val(), opts.maximumInputLength) + "</li>");
+ } else {
+ render("");
+ }
+ return;
+ }
+
+ if (opts.formatSearching && this.findHighlightableChoices().length === 0) {
+ render("<li class='select2-searching'>" + opts.formatSearching() + "</li>");
+ }
+
+ search.addClass("select2-active");
+
+ this.removeHighlight();
+
+ // give the tokenizer a chance to pre-process the input
+ input = this.tokenize();
+ if (input != undefined && input != null) {
+ search.val(input);
+ }
+
+ this.resultsPage = 1;
+
+ opts.query({
+ element: opts.element,
+ term: search.val(),
+ page: this.resultsPage,
+ context: null,
+ matcher: opts.matcher,
+ callback: this.bind(function (data) {
+ var def; // default choice
+
+ // ignore old responses
+ if (queryNumber != this.queryCount) {
+ return;
+ }
+
+ // ignore a response if the select2 has been closed before it was received
+ if (!this.opened()) {
+ this.search.removeClass("select2-active");
+ return;
+ }
+
+ // save context, if any
+ this.context = (data.context===undefined) ? null : data.context;
+ // create a default choice and prepend it to the list
+ if (this.opts.createSearchChoice && search.val() !== "") {
+ def = this.opts.createSearchChoice.call(self, search.val(), data.results);
+ if (def !== undefined && def !== null && self.id(def) !== undefined && self.id(def) !== null) {
+ if ($(data.results).filter(
+ function () {
+ return equal(self.id(this), self.id(def));
+ }).length === 0) {
+ data.results.unshift(def);
+ }
+ }
+ }
+
+ if (data.results.length === 0 && checkFormatter(opts.formatNoMatches, "formatNoMatches")) {
+ render("<li class='select2-no-results'>" + opts.formatNoMatches(search.val()) + "</li>");
+ return;
+ }
+
+ results.empty();
+ self.opts.populateResults.call(this, results, data.results, {term: search.val(), page: this.resultsPage, context:null});
+
+ if (data.more === true && checkFormatter(opts.formatLoadMore, "formatLoadMore")) {
+ results.append("<li class='select2-more-results'>" + self.opts.escapeMarkup(opts.formatLoadMore(this.resultsPage)) + "</li>");
+ window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
+ }
+
+ this.postprocessResults(data, initial);
+
+ postRender();
+
+ this.opts.element.trigger({ type: "select2-loaded", items: data });
+ })});
+ },
+
+ // abstract
+ cancel: function () {
+ this.close();
+ },
+
+ // abstract
+ blur: function () {
+ // if selectOnBlur == true, select the currently highlighted option
+ if (this.opts.selectOnBlur)
+ this.selectHighlighted({noFocus: true});
+
+ this.close();
+ this.container.removeClass("select2-container-active");
+ // synonymous to .is(':focus'), which is available in jquery >= 1.6
+ if (this.search[0] === document.activeElement) { this.search.blur(); }
+ this.clearSearch();
+ this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
+ },
+
+ // abstract
+ focusSearch: function () {
+ focus(this.search);
+ },
+
+ // abstract
+ selectHighlighted: function (options) {
+ var index=this.highlight(),
+ highlighted=this.results.find(".select2-highlighted"),
+ data = highlighted.closest('.select2-result').data("select2-data");
+
+ if (data) {
+ this.highlight(index);
+ this.onSelect(data, options);
+ } else if (options && options.noFocus) {
+ this.close();
+ }
+ },
+
+ // abstract
+ getPlaceholder: function () {
+ var placeholderOption;
+ return this.opts.element.attr("placeholder") ||
+ this.opts.element.attr("data-placeholder") || // jquery 1.4 compat
+ this.opts.element.data("placeholder") ||
+ this.opts.placeholder ||
+ ((placeholderOption = this.getPlaceholderOption()) !== undefined ? placeholderOption.text() : undefined);
+ },
+
+ // abstract
+ getPlaceholderOption: function() {
+ if (this.select) {
+ var firstOption = this.select.children('option').first();
+ if (this.opts.placeholderOption !== undefined ) {
+ //Determine the placeholder option based on the specified placeholderOption setting
+ return (this.opts.placeholderOption === "first" && firstOption) ||
+ (typeof this.opts.placeholderOption === "function" && this.opts.placeholderOption(this.select));
+ } else if (firstOption.text() === "" && firstOption.val() === "") {
+ //No explicit placeholder option specified, use the first if it's blank
+ return firstOption;
+ }
+ }
+ },
+
+ /**
+ * Get the desired width for the container element. This is
+ * derived first from option `width` passed to select2, then
+ * the inline 'style' on the original element, and finally
+ * falls back to the jQuery calculated element width.
+ */
+ // abstract
+ initContainerWidth: function () {
+ function resolveContainerWidth() {
+ var style, attrs, matches, i, l, attr;
+
+ if (this.opts.width === "off") {
+ return null;
+ } else if (this.opts.width === "element"){
+ return this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px';
+ } else if (this.opts.width === "copy" || this.opts.width === "resolve") {
+ // check if there is inline style on the element that contains width
+ style = this.opts.element.attr('style');
+ if (style !== undefined) {
+ attrs = style.split(';');
+ for (i = 0, l = attrs.length; i < l; i = i + 1) {
+ attr = attrs[i].replace(/\s/g, '');
+ matches = attr.match(/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i);
+ if (matches !== null && matches.length >= 1)
+ return matches[1];
+ }
+ }
+
+ if (this.opts.width === "resolve") {
+ // next check if css('width') can resolve a width that is percent based, this is sometimes possible
+ // when attached to input type=hidden or elements hidden via css
+ style = this.opts.element.css('width');
+ if (style.indexOf("%") > 0) return style;
+
+ // finally, fallback on the calculated width of the element
+ return (this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px');
+ }
+
+ return null;
+ } else if ($.isFunction(this.opts.width)) {
+ return this.opts.width();
+ } else {
+ return this.opts.width;
+ }
+ };
+
+ var width = resolveContainerWidth.call(this);
+ if (width !== null) {
+ this.container.css("width", width);
+ }
+ }
+ });
+
+ SingleSelect2 = clazz(AbstractSelect2, {
+
+ // single
+
+ createContainer: function () {
+ var container = $(document.createElement("div")).attr({
+ "class": "select2-container"
+ }).html([
+ "<a href='javascript:void(0)' onclick='return false;' class='select2-choice' tabindex='-1'>",
+ " <span class='select2-chosen'> </span><abbr class='select2-search-choice-close'></abbr>",
+ " <span class='select2-arrow'><b></b></span>",
+ "</a>",
+ "<input class='select2-focusser select2-offscreen' type='text'/>",
+ "<div class='select2-drop select2-display-none'>",
+ " <div class='select2-search'>",
+ " <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input'/>",
+ " </div>",
+ " <ul class='select2-results'>",
+ " </ul>",
+ "</div>"].join(""));
+ return container;
+ },
+
+ // single
+ enableInterface: function() {
+ if (this.parent.enableInterface.apply(this, arguments)) {
+ this.focusser.prop("disabled", !this.isInterfaceEnabled());
+ }
+ },
+
+ // single
+ opening: function () {
+ var el, range, len;
+
+ if (this.opts.minimumResultsForSearch >= 0) {
+ this.showSearch(true);
+ }
+
+ this.parent.opening.apply(this, arguments);
+
+ if (this.showSearchInput !== false) {
+ // IE appends focusser.val() at the end of field :/ so we manually insert it at the beginning using a range
+ // all other browsers handle this just fine
+
+ this.search.val(this.focusser.val());
+ }
+ this.search.focus();
+ // move the cursor to the end after focussing, otherwise it will be at the beginning and
+ // new text will appear *before* focusser.val()
+ el = this.search.get(0);
+ if (el.createTextRange) {
+ range = el.createTextRange();
+ range.collapse(false);
+ range.select();
+ } else if (el.setSelectionRange) {
+ len = this.search.val().length;
+ el.setSelectionRange(len, len);
+ }
+
+ // initializes search's value with nextSearchTerm (if defined by user)
+ // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter
+ if(this.search.val() === "") {
+ if(this.nextSearchTerm != undefined){
+ this.search.val(this.nextSearchTerm);
+ this.search.select();
+ }
+ }
+
+ this.focusser.prop("disabled", true).val("");
+ this.updateResults(true);
+ this.opts.element.trigger($.Event("select2-open"));
+ },
+
+ // single
+ close: function (params) {
+ if (!this.opened()) return;
+ this.parent.close.apply(this, arguments);
+
+ params = params || {focus: true};
+ this.focusser.removeAttr("disabled");
+
+ if (params.focus) {
+ this.focusser.focus();
+ }
+ },
+
+ // single
+ focus: function () {
+ if (this.opened()) {
+ this.close();
+ } else {
+ this.focusser.removeAttr("disabled");
+ this.focusser.focus();
+ }
+ },
+
+ // single
+ isFocused: function () {
+ return this.container.hasClass("select2-container-active");
+ },
+
+ // single
+ cancel: function () {
+ this.parent.cancel.apply(this, arguments);
+ this.focusser.removeAttr("disabled");
+ this.focusser.focus();
+ },
+
+ // single
+ destroy: function() {
+ $("label[for='" + this.focusser.attr('id') + "']")
+ .attr('for', this.opts.element.attr("id"));
+ this.parent.destroy.apply(this, arguments);
+ },
+
+ // single
+ initContainer: function () {
+
+ var selection,
+ container = this.container,
+ dropdown = this.dropdown;
+
+ if (this.opts.minimumResultsForSearch < 0) {
+ this.showSearch(false);
+ } else {
+ this.showSearch(true);
+ }
+
+ this.selection = selection = container.find(".select2-choice");
+
+ this.focusser = container.find(".select2-focusser");
+
+ // rewrite labels from original element to focusser
+ this.focusser.attr("id", "s2id_autogen"+nextUid());
+
+ $("label[for='" + this.opts.element.attr("id") + "']")
+ .attr('for', this.focusser.attr('id'));
+
+ this.focusser.attr("tabindex", this.elementTabIndex);
+
+ this.search.on("keydown", this.bind(function (e) {
+ if (!this.isInterfaceEnabled()) return;
+
+ if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
+ // prevent the page from scrolling
+ killEvent(e);
+ return;
+ }
+
+ switch (e.which) {
+ case KEY.UP:
+ case KEY.DOWN:
+ this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
+ killEvent(e);
+ return;
+ case KEY.ENTER:
+ this.selectHighlighted();
+ killEvent(e);
+ return;
+ case KEY.TAB:
+ this.selectHighlighted({noFocus: true});
+ return;
+ case KEY.ESC:
+ this.cancel(e);
+ killEvent(e);
+ return;
+ }
+ }));
+
+ this.search.on("blur", this.bind(function(e) {
+ // a workaround for chrome to keep the search field focussed when the scroll bar is used to scroll the dropdown.
+ // without this the search field loses focus which is annoying
+ if (document.activeElement === this.body().get(0)) {
+ window.setTimeout(this.bind(function() {
+ this.search.focus();
+ }), 0);
+ }
+ }));
+
+ this.focusser.on("keydown", this.bind(function (e) {
+ if (!this.isInterfaceEnabled()) return;
+
+ if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
+ return;
+ }
+
+ if (this.opts.openOnEnter === false && e.which === KEY.ENTER) {
+ killEvent(e);
+ return;
+ }
+
+ if (e.which == KEY.DOWN || e.which == KEY.UP
+ || (e.which == KEY.ENTER && this.opts.openOnEnter)) {
+
+ if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) return;
+
+ this.open();
+ killEvent(e);
+ return;
+ }
+
+ if (e.which == KEY.DELETE || e.which == KEY.BACKSPACE) {
+ if (this.opts.allowClear) {
+ this.clear();
+ }
+ killEvent(e);
+ return;
+ }
+ }));
+
+
+ installKeyUpChangeEvent(this.focusser);
+ this.focusser.on("keyup-change input", this.bind(function(e) {
+ if (this.opts.minimumResultsForSearch >= 0) {
+ e.stopPropagation();
+ if (this.opened()) return;
+ this.open();
+ }
+ }));
+
+ selection.on("mousedown", "abbr", this.bind(function (e) {
+ if (!this.isInterfaceEnabled()) return;
+ this.clear();
+ killEventImmediately(e);
+ this.close();
+ this.selection.focus();
+ }));
+
+ selection.on("mousedown", this.bind(function (e) {
+
+ if (!this.container.hasClass("select2-container-active")) {
+ this.opts.element.trigger($.Event("select2-focus"));
+ }
+
+ if (this.opened()) {
+ this.close();
+ } else if (this.isInterfaceEnabled()) {
+ this.open();
+ }
+
+ killEvent(e);
+ }));
+
+ dropdown.on("mousedown", this.bind(function() { this.search.focus(); }));
+
+ selection.on("focus", this.bind(function(e) {
+ killEvent(e);
+ }));
+
+ this.focusser.on("focus", this.bind(function(){
+ if (!this.container.hasClass("select2-container-active")) {
+ this.opts.element.trigger($.Event("select2-focus"));
+ }
+ this.container.addClass("select2-container-active");
+ })).on("blur", this.bind(function() {
+ if (!this.opened()) {
+ this.container.removeClass("select2-container-active");
+ this.opts.element.trigger($.Event("select2-blur"));
+ }
+ }));
+ this.search.on("focus", this.bind(function(){
+ if (!this.container.hasClass("select2-container-active")) {
+ this.opts.element.trigger($.Event("select2-focus"));
+ }
+ this.container.addClass("select2-container-active");
+ }));
+
+ this.initContainerWidth();
+ this.opts.element.addClass("select2-offscreen");
+ this.setPlaceholder();
+
+ },
+
+ // single
+ clear: function(triggerChange) {
+ var data=this.selection.data("select2-data");
+ if (data) { // guard against queued quick consecutive clicks
+ var evt = $.Event("select2-clearing");
+ this.opts.element.trigger(evt);
+ if (evt.isDefaultPrevented()) {
+ return;
+ }
+ var placeholderOption = this.getPlaceholderOption();
+ this.opts.element.val(placeholderOption ? placeholderOption.val() : "");
+ this.selection.find(".select2-chosen").empty();
+ this.selection.removeData("select2-data");
+ this.setPlaceholder();
+
+ if (triggerChange !== false){
+ this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data });
+ this.triggerChange({removed:data});
+ }
+ }
+ },
+
+ /**
+ * Sets selection based on source element's value
+ */
+ // single
+ initSelection: function () {
+ var selected;
+ if (this.isPlaceholderOptionSelected()) {
+ this.updateSelection(null);
+ this.close();
+ this.setPlaceholder();
+ } else {
+ var self = this;
+ this.opts.initSelection.call(null, this.opts.element, function(selected){
+ if (selected !== undefined && selected !== null) {
+ self.updateSelection(selected);
+ self.close();
+ self.setPlaceholder();
+ }
+ });
+ }
+ },
+
+ isPlaceholderOptionSelected: function() {
+ var placeholderOption;
+ if (!this.getPlaceholder()) return false; // no placeholder specified so no option should be considered
+ return ((placeholderOption = this.getPlaceholderOption()) !== undefined && placeholderOption.prop("selected"))
+ || (this.opts.element.val() === "")
+ || (this.opts.element.val() === undefined)
+ || (this.opts.element.val() === null);
+ },
+
+ // single
+ prepareOpts: function () {
+ var opts = this.parent.prepareOpts.apply(this, arguments),
+ self=this;
+
+ if (opts.element.get(0).tagName.toLowerCase() === "select") {
+ // install the selection initializer
+ opts.initSelection = function (element, callback) {
+ var selected = element.find("option").filter(function() { return this.selected });
+ // a single select box always has a value, no need to null check 'selected'
+ callback(self.optionToData(selected));
+ };
+ } else if ("data" in opts) {
+ // install default initSelection when applied to hidden input and data is local
+ opts.initSelection = opts.initSelection || function (element, callback) {
+ var id = element.val();
+ //search in data by id, storing the actual matching item
+ var match = null;
+ opts.query({
+ matcher: function(term, text, el){
+ var is_match = equal(id, opts.id(el));
+ if (is_match) {
+ match = el;
+ }
+ return is_match;
+ },
+ callback: !$.isFunction(callback) ? $.noop : function() {
+ callback(match);
+ }
+ });
+ };
+ }
+
+ return opts;
+ },
+
+ // single
+ getPlaceholder: function() {
+ // if a placeholder is specified on a single select without a valid placeholder option ignore it
+ if (this.select) {
+ if (this.getPlaceholderOption() === undefined) {
+ return undefined;
+ }
+ }
+
+ return this.parent.getPlaceholder.apply(this, arguments);
+ },
+
+ // single
+ setPlaceholder: function () {
+ var placeholder = this.getPlaceholder();
+
+ if (this.isPlaceholderOptionSelected() && placeholder !== undefined) {
+
+ // check for a placeholder option if attached to a select
+ if (this.select && this.getPlaceholderOption() === undefined) return;
+
+ this.selection.find(".select2-chosen").html(this.opts.escapeMarkup(placeholder));
+
+ this.selection.addClass("select2-default");
+
+ this.container.removeClass("select2-allowclear");
+ }
+ },
+
+ // single
+ postprocessResults: function (data, initial, noHighlightUpdate) {
+ var selected = 0, self = this, showSearchInput = true;
+
+ // find the selected element in the result list
+
+ this.findHighlightableChoices().each2(function (i, elm) {
+ if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) {
+ selected = i;
+ return false;
+ }
+ });
+
+ // and highlight it
+ if (noHighlightUpdate !== false) {
+ if (initial === true && selected >= 0) {
+ this.highlight(selected);
+ } else {
+ this.highlight(0);
+ }
+ }
+
+ // hide the search box if this is the first we got the results and there are enough of them for search
+
+ if (initial === true) {
+ var min = this.opts.minimumResultsForSearch;
+ if (min >= 0) {
+ this.showSearch(countResults(data.results) >= min);
+ }
+ }
+ },
+
+ // single
+ showSearch: function(showSearchInput) {
+ if (this.showSearchInput === showSearchInput) return;
+
+ this.showSearchInput = showSearchInput;
+
+ this.dropdown.find(".select2-search").toggleClass("select2-search-hidden", !showSearchInput);
+ this.dropdown.find(".select2-search").toggleClass("select2-offscreen", !showSearchInput);
+ //add "select2-with-searchbox" to the container if search box is shown
+ $(this.dropdown, this.container).toggleClass("select2-with-searchbox", showSearchInput);
+ },
+
+ // single
+ onSelect: function (data, options) {
+
+ if (!this.triggerSelect(data)) { return; }
+
+ var old = this.opts.element.val(),
+ oldData = this.data();
+
+ this.opts.element.val(this.id(data));
+ this.updateSelection(data);
+
+ this.opts.element.trigger({ type: "select2-selected", val: this.id(data), choice: data });
+
+ this.nextSearchTerm = this.opts.nextSearchTerm(data, this.search.val());
+ this.close();
+
+ if (!options || !options.noFocus)
+ this.focusser.focus();
+
+ if (!equal(old, this.id(data))) { this.triggerChange({added:data,removed:oldData}); }
+ },
+
+ // single
+ updateSelection: function (data) {
+
+ var container=this.selection.find(".select2-chosen"), formatted, cssClass;
+
+ this.selection.data("select2-data", data);
+
+ container.empty();
+ if (data !== null) {
+ formatted=this.opts.formatSelection(data, container, this.opts.escapeMarkup);
+ }
+ if (formatted !== undefined) {
+ container.append(formatted);
+ }
+ cssClass=this.opts.formatSelectionCssClass(data, container);
+ if (cssClass !== undefined) {
+ container.addClass(cssClass);
+ }
+
+ this.selection.removeClass("select2-default");
+
+ if (this.opts.allowClear && this.getPlaceholder() !== undefined) {
+ this.container.addClass("select2-allowclear");
+ }
+ },
+
+ // single
+ val: function () {
+ var val,
+ triggerChange = false,
+ data = null,
+ self = this,
+ oldData = this.data();
+
+ if (arguments.length === 0) {
+ return this.opts.element.val();
+ }
+
+ val = arguments[0];
+
+ if (arguments.length > 1) {
+ triggerChange = arguments[1];
+ }
+
+ if (this.select) {
+ this.select
+ .val(val)
+ .find("option").filter(function() { return this.selected }).each2(function (i, elm) {
+ data = self.optionToData(elm);
+ return false;
+ });
+ this.updateSelection(data);
+ this.setPlaceholder();
+ if (triggerChange) {
+ this.triggerChange({added: data, removed:oldData});
+ }
+ } else {
+ // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
+ if (!val && val !== 0) {
+ this.clear(triggerChange);
+ return;
+ }
+ if (this.opts.initSelection === undefined) {
+ throw new Error("cannot call val() if initSelection() is not defined");
+ }
+ this.opts.element.val(val);
+ this.opts.initSelection(this.opts.element, function(data){
+ self.opts.element.val(!data ? "" : self.id(data));
+ self.updateSelection(data);
+ self.setPlaceholder();
+ if (triggerChange) {
+ self.triggerChange({added: data, removed:oldData});
+ }
+ });
+ }
+ },
+
+ // single
+ clearSearch: function () {
+ this.search.val("");
+ this.focusser.val("");
+ },
+
+ // single
+ data: function(value) {
+ var data,
+ triggerChange = false;
+
+ if (arguments.length === 0) {
+ data = this.selection.data("select2-data");
+ if (data == undefined) data = null;
+ return data;
+ } else {
+ if (arguments.length > 1) {
+ triggerChange = arguments[1];
+ }
+ if (!value) {
+ this.clear(triggerChange);
+ } else {
+ data = this.data();
+ this.opts.element.val(!value ? "" : this.id(value));
+ this.updateSelection(value);
+ if (triggerChange) {
+ this.triggerChange({added: value, removed:data});
+ }
+ }
+ }
+ }
+ });
+
+ MultiSelect2 = clazz(AbstractSelect2, {
+
+ // multi
+ createContainer: function () {
+ var container = $(document.createElement("div")).attr({
+ "class": "select2-container select2-container-multi"
+ }).html([
+ "<ul class='select2-choices'>",
+ " <li class='select2-search-field'>",
+ " <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input'>",
+ " </li>",
+ "</ul>",
+ "<div class='select2-drop select2-drop-multi select2-display-none'>",
+ " <ul class='select2-results'>",
+ " </ul>",
+ "</div>"].join(""));
+ return container;
+ },
+
+ // multi
+ prepareOpts: function () {
+ var opts = this.parent.prepareOpts.apply(this, arguments),
+ self=this;
+
+ // TODO validate placeholder is a string if specified
+
+ if (opts.element.get(0).tagName.toLowerCase() === "select") {
+ // install sthe selection initializer
+ opts.initSelection = function (element, callback) {
+
+ var data = [];
+
+ element.find("option").filter(function() { return this.selected }).each2(function (i, elm) {
+ data.push(self.optionToData(elm));
+ });
+ callback(data);
+ };
+ } else if ("data" in opts) {
+ // install default initSelection when applied to hidden input and data is local
+ opts.initSelection = opts.initSelection || function (element, callback) {
+ var ids = splitVal(element.val(), opts.separator);
+ //search in data by array of ids, storing matching items in a list
+ var matches = [];
+ opts.query({
+ matcher: function(term, text, el){
+ var is_match = $.grep(ids, function(id) {
+ return equal(id, opts.id(el));
+ }).length;
+ if (is_match) {
+ matches.push(el);
+ }
+ return is_match;
+ },
+ callback: !$.isFunction(callback) ? $.noop : function() {
+ // reorder matches based on the order they appear in the ids array because right now
+ // they are in the order in which they appear in data array
+ var ordered = [];
+ for (var i = 0; i < ids.length; i++) {
+ var id = ids[i];
+ for (var j = 0; j < matches.length; j++) {
+ var match = matches[j];
+ if (equal(id, opts.id(match))) {
+ ordered.push(match);
+ matches.splice(j, 1);
+ break;
+ }
+ }
+ }
+ callback(ordered);
+ }
+ });
+ };
+ }
+
+ return opts;
+ },
+
+ // multi
+ selectChoice: function (choice) {
+
+ var selected = this.container.find(".select2-search-choice-focus");
+ if (selected.length && choice && choice[0] == selected[0]) {
+
+ } else {
+ if (selected.length) {
+ this.opts.element.trigger("choice-deselected", selected);
+ }
+ selected.removeClass("select2-search-choice-focus");
+ if (choice && choice.length) {
+ this.close();
+ choice.addClass("select2-search-choice-focus");
+ this.opts.element.trigger("choice-selected", choice);
+ }
+ }
+ },
+
+ // multi
+ destroy: function() {
+ $("label[for='" + this.search.attr('id') + "']")
+ .attr('for', this.opts.element.attr("id"));
+ this.parent.destroy.apply(this, arguments);
+ },
+
+ // multi
+ initContainer: function () {
+
+ var selector = ".select2-choices", selection;
+
+ this.searchContainer = this.container.find(".select2-search-field");
+ this.selection = selection = this.container.find(selector);
+
+ var _this = this;
+ this.selection.on("click", ".select2-search-choice:not(.select2-locked)", function (e) {
+ //killEvent(e);
+ _this.search[0].focus();
+ _this.selectChoice($(this));
+ });
+
+ // rewrite labels from original element to focusser
+ this.search.attr("id", "s2id_autogen"+nextUid());
+ $("label[for='" + this.opts.element.attr("id") + "']")
+ .attr('for', this.search.attr('id'));
+
+ this.search.on("input paste", this.bind(function() {
+ if (!this.isInterfaceEnabled()) return;
+ if (!this.opened()) {
+ this.open();
+ }
+ }));
+
+ this.search.attr("tabindex", this.elementTabIndex);
+
+ this.keydowns = 0;
+ this.search.on("keydown", this.bind(function (e) {
+ if (!this.isInterfaceEnabled()) return;
+
+ ++this.keydowns;
+ var selected = selection.find(".select2-search-choice-focus");
+ var prev = selected.prev(".select2-search-choice:not(.select2-locked)");
+ var next = selected.next(".select2-search-choice:not(.select2-locked)");
+ var pos = getCursorInfo(this.search);
+
+ if (selected.length &&
+ (e.which == KEY.LEFT || e.which == KEY.RIGHT || e.which == KEY.BACKSPACE || e.which == KEY.DELETE || e.which == KEY.ENTER)) {
+ var selectedChoice = selected;
+ if (e.which == KEY.LEFT && prev.length) {
+ selectedChoice = prev;
+ }
+ else if (e.which == KEY.RIGHT) {
+ selectedChoice = next.length ? next : null;
+ }
+ else if (e.which === KEY.BACKSPACE) {
+ this.unselect(selected.first());
+ this.search.width(10);
+ selectedChoice = prev.length ? prev : next;
+ } else if (e.which == KEY.DELETE) {
+ this.unselect(selected.first());
+ this.search.width(10);
+ selectedChoice = next.length ? next : null;
+ } else if (e.which == KEY.ENTER) {
+ selectedChoice = null;
+ }
+
+ this.selectChoice(selectedChoice);
+ killEvent(e);
+ if (!selectedChoice || !selectedChoice.length) {
+ this.open();
+ }
+ return;
+ } else if (((e.which === KEY.BACKSPACE && this.keydowns == 1)
+ || e.which == KEY.LEFT) && (pos.offset == 0 && !pos.length)) {
+
+ this.selectChoice(selection.find(".select2-search-choice:not(.select2-locked)").last());
+ killEvent(e);
+ return;
+ } else {
+ this.selectChoice(null);
+ }
+
+ if (this.opened()) {
+ switch (e.which) {
+ case KEY.UP:
+ case KEY.DOWN:
+ this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
+ killEvent(e);
+ return;
+ case KEY.ENTER:
+ this.selectHighlighted();
+ killEvent(e);
+ return;
+ case KEY.TAB:
+ this.selectHighlighted({noFocus:true});
+ this.close();
+ return;
+ case KEY.ESC:
+ this.cancel(e);
+ killEvent(e);
+ return;
+ }
+ }
+
+ if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e)
+ || e.which === KEY.BACKSPACE || e.which === KEY.ESC) {
+ return;
+ }
+
+ if (e.which === KEY.ENTER) {
+ if (this.opts.openOnEnter === false) {
+ return;
+ } else if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
+ return;
+ }
+ }
+
+ this.open();
+
+ if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
+ // prevent the page from scrolling
+ killEvent(e);
+ }
+
+ if (e.which === KEY.ENTER) {
+ // prevent form from being submitted
+ killEvent(e);
+ }
+
+ }));
+
+ this.search.on("keyup", this.bind(function (e) {
+ this.keydowns = 0;
+ this.resizeSearch();
+ })
+ );
+
+ this.search.on("blur", this.bind(function(e) {
+ this.container.removeClass("select2-container-active");
+ this.search.removeClass("select2-focused");
+ this.selectChoice(null);
+ if (!this.opened()) this.clearSearch();
+ e.stopImmediatePropagation();
+ this.opts.element.trigger($.Event("select2-blur"));
+ }));
+
+ this.container.on("click", selector, this.bind(function (e) {
+ if (!this.isInterfaceEnabled()) return;
+ if ($(e.target).closest(".select2-search-choice").length > 0) {
+ // clicked inside a select2 search choice, do not open
+ return;
+ }
+ this.selectChoice(null);
+ this.clearPlaceholder();
+ if (!this.container.hasClass("select2-container-active")) {
+ this.opts.element.trigger($.Event("select2-focus"));
+ }
+ this.open();
+ this.focusSearch();
+ e.preventDefault();
+ }));
+
+ this.container.on("focus", selector, this.bind(function () {
+ if (!this.isInterfaceEnabled()) return;
+ if (!this.container.hasClass("select2-container-active")) {
+ this.opts.element.trigger($.Event("select2-focus"));
+ }
+ this.container.addClass("select2-container-active");
+ this.dropdown.addClass("select2-drop-active");
+ this.clearPlaceholder();
+ }));
+
+ this.initContainerWidth();
+ this.opts.element.addClass("select2-offscreen");
+
+ // set the placeholder if necessary
+ this.clearSearch();
+ },
+
+ // multi
+ enableInterface: function() {
+ if (this.parent.enableInterface.apply(this, arguments)) {
+ this.search.prop("disabled", !this.isInterfaceEnabled());
+ }
+ },
+
+ // multi
+ initSelection: function () {
+ var data;
+ if (this.opts.element.val() === "" && this.opts.element.text() === "") {
+ this.updateSelection([]);
+ this.close();
+ // set the placeholder if necessary
+ this.clearSearch();
+ }
+ if (this.select || this.opts.element.val() !== "") {
+ var self = this;
+ this.opts.initSelection.call(null, this.opts.element, function(data){
+ if (data !== undefined && data !== null) {
+ self.updateSelection(data);
+ self.close();
+ // set the placeholder if necessary
+ self.clearSearch();
+ }
+ });
+ }
+ },
+
+ // multi
+ clearSearch: function () {
+ var placeholder = this.getPlaceholder(),
+ maxWidth = this.getMaxSearchWidth();
+
+ if (placeholder !== undefined && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) {
+ this.search.val(placeholder).addClass("select2-default");
+ // stretch the search box to full width of the container so as much of the placeholder is visible as possible
+ // we could call this.resizeSearch(), but we do not because that requires a sizer and we do not want to create one so early because of a firefox bug, see #944
+ this.search.width(maxWidth > 0 ? maxWidth : this.container.css("width"));
+ } else {
+ this.search.val("").width(10);
+ }
+ },
+
+ // multi
+ clearPlaceholder: function () {
+ if (this.search.hasClass("select2-default")) {
+ this.search.val("").removeClass("select2-default");
+ }
+ },
+
+ // multi
+ opening: function () {
+ this.clearPlaceholder(); // should be done before super so placeholder is not used to search
+ this.resizeSearch();
+
+ this.parent.opening.apply(this, arguments);
+
+ this.focusSearch();
+
+ this.updateResults(true);
+ this.search.focus();
+ this.opts.element.trigger($.Event("select2-open"));
+ },
+
+ // multi
+ close: function () {
+ if (!this.opened()) return;
+ this.parent.close.apply(this, arguments);
+ },
+
+ // multi
+ focus: function () {
+ this.close();
+ this.search.focus();
+ },
+
+ // multi
+ isFocused: function () {
+ return this.search.hasClass("select2-focused");
+ },
+
+ // multi
+ updateSelection: function (data) {
+ var ids = [], filtered = [], self = this;
+
+ // filter out duplicates
+ $(data).each(function () {
+ if (indexOf(self.id(this), ids) < 0) {
+ ids.push(self.id(this));
+ filtered.push(this);
+ }
+ });
+ data = filtered;
+
+ this.selection.find(".select2-search-choice").remove();
+ $(data).each(function () {
+ self.addSelectedChoice(this);
+ });
+ self.postprocessResults();
+ },
+
+ // multi
+ tokenize: function() {
+ var input = this.search.val();
+ input = this.opts.tokenizer.call(this, input, this.data(), this.bind(this.onSelect), this.opts);
+ if (input != null && input != undefined) {
+ this.search.val(input);
+ if (input.length > 0) {
+ this.open();
+ }
+ }
+
+ },
+
+ // multi
+ onSelect: function (data, options) {
+
+ if (!this.triggerSelect(data)) { return; }
+
+ this.addSelectedChoice(data);
+
+ this.opts.element.trigger({ type: "selected", val: this.id(data), choice: data });
+
+ if (this.select || !this.opts.closeOnSelect) this.postprocessResults(data, false, this.opts.closeOnSelect===true);
+
+ if (this.opts.closeOnSelect) {
+ this.close();
+ this.search.width(10);
+ } else {
+ if (this.countSelectableResults()>0) {
+ this.search.width(10);
+ this.resizeSearch();
+ if (this.getMaximumSelectionSize() > 0 && this.val().length >= this.getMaximumSelectionSize()) {
+ // if we reached max selection size repaint the results so choices
+ // are replaced with the max selection reached message
+ this.updateResults(true);
+ }
+ this.positionDropdown();
+ } else {
+ // if nothing left to select close
+ this.close();
+ this.search.width(10);
+ }
+ }
+
+ // since its not possible to select an element that has already been
+ // added we do not need to check if this is a new element before firing change
+ this.triggerChange({ added: data });
+
+ if (!options || !options.noFocus)
+ this.focusSearch();
+ },
+
+ // multi
+ cancel: function () {
+ this.close();
+ this.focusSearch();
+ },
+
+ addSelectedChoice: function (data) {
+ var enableChoice = !data.locked,
+ enabledItem = $(
+ "<li class='select2-search-choice'>" +
+ " <div></div>" +
+ " <a href='#' onclick='return false;' class='select2-search-choice-close' tabindex='-1'></a>" +
+ "</li>"),
+ disabledItem = $(
+ "<li class='select2-search-choice select2-locked'>" +
+ "<div></div>" +
+ "</li>");
+ var choice = enableChoice ? enabledItem : disabledItem,
+ id = this.id(data),
+ val = this.getVal(),
+ formatted,
+ cssClass;
+
+ formatted=this.opts.formatSelection(data, choice.find("div"), this.opts.escapeMarkup);
+ if (formatted != undefined) {
+ choice.find("div").replaceWith("<div>"+formatted+"</div>");
+ }
+ cssClass=this.opts.formatSelectionCssClass(data, choice.find("div"));
+ if (cssClass != undefined) {
+ choice.addClass(cssClass);
+ }
+
+ if(enableChoice){
+ choice.find(".select2-search-choice-close")
+ .on("mousedown", killEvent)
+ .on("click dblclick", this.bind(function (e) {
+ if (!this.isInterfaceEnabled()) return;
+
+ $(e.target).closest(".select2-search-choice").fadeOut('fast', this.bind(function(){
+ this.unselect($(e.target));
+ this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
+ this.close();
+ this.focusSearch();
+ })).dequeue();
+ killEvent(e);
+ })).on("focus", this.bind(function () {
+ if (!this.isInterfaceEnabled()) return;
+ this.container.addClass("select2-container-active");
+ this.dropdown.addClass("select2-drop-active");
+ }));
+ }
+
+ choice.data("select2-data", data);
+ choice.insertBefore(this.searchContainer);
+
+ val.push(id);
+ this.setVal(val);
+ },
+
+ // multi
+ unselect: function (selected) {
+ var val = this.getVal(),
+ data,
+ index;
+ selected = selected.closest(".select2-search-choice");
+
+ if (selected.length === 0) {
+ throw "Invalid argument: " + selected + ". Must be .select2-search-choice";
+ }
+
+ data = selected.data("select2-data");
+
+ if (!data) {
+ // prevent a race condition when the 'x' is clicked really fast repeatedly the event can be queued
+ // and invoked on an element already removed
+ return;
+ }
+
+ while((index = indexOf(this.id(data), val)) >= 0) {
+ val.splice(index, 1);
+ this.setVal(val);
+ if (this.select) this.postprocessResults();
+ }
+
+ var evt = $.Event("select2-removing");
+ evt.val = this.id(data);
+ evt.choice = data;
+ this.opts.element.trigger(evt);
+
+ if (evt.isDefaultPrevented()) {
+ return;
+ }
+
+ selected.remove();
+
+ this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data });
+ this.triggerChange({ removed: data });
+ },
+
+ // multi
+ postprocessResults: function (data, initial, noHighlightUpdate) {
+ var val = this.getVal(),
+ choices = this.results.find(".select2-result"),
+ compound = this.results.find(".select2-result-with-children"),
+ self = this;
+
+ choices.each2(function (i, choice) {
+ var id = self.id(choice.data("select2-data"));
+ if (indexOf(id, val) >= 0) {
+ choice.addClass("select2-selected");
+ // mark all children of the selected parent as selected
+ choice.find(".select2-result-selectable").addClass("select2-selected");
+ }
+ });
+
+ compound.each2(function(i, choice) {
+ // hide an optgroup if it doesnt have any selectable children
+ if (!choice.is('.select2-result-selectable')
+ && choice.find(".select2-result-selectable:not(.select2-selected)").length === 0) {
+ choice.addClass("select2-selected");
+ }
+ });
+
+ if (this.highlight() == -1 && noHighlightUpdate !== false){
+ self.highlight(0);
+ }
+
+ //If all results are chosen render formatNoMAtches
+ if(!this.opts.createSearchChoice && !choices.filter('.select2-result:not(.select2-selected)').length > 0){
+ if(!data || data && !data.more && this.results.find(".select2-no-results").length === 0) {
+ if (checkFormatter(self.opts.formatNoMatches, "formatNoMatches")) {
+ this.results.append("<li class='select2-no-results'>" + self.opts.formatNoMatches(self.search.val()) + "</li>");
+ }
+ }
+ }
+
+ },
+
+ // multi
+ getMaxSearchWidth: function() {
+ return this.selection.width() - getSideBorderPadding(this.search);
+ },
+
+ // multi
+ resizeSearch: function () {
+ var minimumWidth, left, maxWidth, containerLeft, searchWidth,
+ sideBorderPadding = getSideBorderPadding(this.search);
+
+ minimumWidth = measureTextWidth(this.search) + 10;
+
+ left = this.search.offset().left;
+
+ maxWidth = this.selection.width();
+ containerLeft = this.selection.offset().left;
+
+ searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding;
+
+ if (searchWidth < minimumWidth) {
+ searchWidth = maxWidth - sideBorderPadding;
+ }
+
+ if (searchWidth < 40) {
+ searchWidth = maxWidth - sideBorderPadding;
+ }
+
+ if (searchWidth <= 0) {
+ searchWidth = minimumWidth;
+ }
+
+ this.search.width(Math.floor(searchWidth));
+ },
+
+ // multi
+ getVal: function () {
+ var val;
+ if (this.select) {
+ val = this.select.val();
+ return val === null ? [] : val;
+ } else {
+ val = this.opts.element.val();
+ return splitVal(val, this.opts.separator);
+ }
+ },
+
+ // multi
+ setVal: function (val) {
+ var unique;
+ if (this.select) {
+ this.select.val(val);
+ } else {
+ unique = [];
+ // filter out duplicates
+ $(val).each(function () {
+ if (indexOf(this, unique) < 0) unique.push(this);
+ });
+ this.opts.element.val(unique.length === 0 ? "" : unique.join(this.opts.separator));
+ }
+ },
+
+ // multi
+ buildChangeDetails: function (old, current) {
+ var current = current.slice(0),
+ old = old.slice(0);
+
+ // remove intersection from each array
+ for (var i = 0; i < current.length; i++) {
+ for (var j = 0; j < old.length; j++) {
+ if (equal(this.opts.id(current[i]), this.opts.id(old[j]))) {
+ current.splice(i, 1);
+ if(i>0){
+ i--;
+ }
+ old.splice(j, 1);
+ j--;
+ }
+ }
+ }
+
+ return {added: current, removed: old};
+ },
+
+
+ // multi
+ val: function (val, triggerChange) {
+ var oldData, self=this;
+
+ if (arguments.length === 0) {
+ return this.getVal();
+ }
+
+ oldData=this.data();
+ if (!oldData.length) oldData=[];
+
+ // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
+ if (!val && val !== 0) {
+ this.opts.element.val("");
+ this.updateSelection([]);
+ this.clearSearch();
+ if (triggerChange) {
+ this.triggerChange({added: this.data(), removed: oldData});
+ }
+ return;
+ }
+
+ // val is a list of ids
+ this.setVal(val);
+
+ if (this.select) {
+ this.opts.initSelection(this.select, this.bind(this.updateSelection));
+ if (triggerChange) {
+ this.triggerChange(this.buildChangeDetails(oldData, this.data()));
+ }
+ } else {
+ if (this.opts.initSelection === undefined) {
+ throw new Error("val() cannot be called if initSelection() is not defined");
+ }
+
+ this.opts.initSelection(this.opts.element, function(data){
+ var ids=$.map(data, self.id);
+ self.setVal(ids);
+ self.updateSelection(data);
+ self.clearSearch();
+ if (triggerChange) {
+ self.triggerChange(self.buildChangeDetails(oldData, self.data()));
+ }
+ });
+ }
+ this.clearSearch();
+ },
+
+ // multi
+ onSortStart: function() {
+ if (this.select) {
+ throw new Error("Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead.");
+ }
+
+ // collapse search field into 0 width so its container can be collapsed as well
+ this.search.width(0);
+ // hide the container
+ this.searchContainer.hide();
+ },
+
+ // multi
+ onSortEnd:function() {
+
+ var val=[], self=this;
+
+ // show search and move it to the end of the list
+ this.searchContainer.show();
+ // make sure the search container is the last item in the list
+ this.searchContainer.appendTo(this.searchContainer.parent());
+ // since we collapsed the width in dragStarted, we resize it here
+ this.resizeSearch();
+
+ // update selection
+ this.selection.find(".select2-search-choice").each(function() {
+ val.push(self.opts.id($(this).data("select2-data")));
+ });
+ this.setVal(val);
+ this.triggerChange();
+ },
+
+ // multi
+ data: function(values, triggerChange) {
+ var self=this, ids, old;
+ if (arguments.length === 0) {
+ return this.selection
+ .find(".select2-search-choice")
+ .map(function() { return $(this).data("select2-data"); })
+ .get();
+ } else {
+ old = this.data();
+ if (!values) { values = []; }
+ ids = $.map(values, function(e) { return self.opts.id(e); });
+ this.setVal(ids);
+ this.updateSelection(values);
+ this.clearSearch();
+ if (triggerChange) {
+ this.triggerChange(this.buildChangeDetails(old, this.data()));
+ }
+ }
+ }
+ });
+
+ $.fn.select2 = function () {
+
+ var args = Array.prototype.slice.call(arguments, 0),
+ opts,
+ select2,
+ method, value, multiple,
+ allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "dropdown", "onSortStart", "onSortEnd", "enable", "disable", "readonly", "positionDropdown", "data", "search"],
+ valueMethods = ["opened", "isFocused", "container", "dropdown"],
+ propertyMethods = ["val", "data"],
+ methodsMap = { search: "externalSearch" };
+
+ this.each(function () {
+ if (args.length === 0 || typeof(args[0]) === "object") {
+ opts = args.length === 0 ? {} : $.extend({}, args[0]);
+ opts.element = $(this);
+
+ if (opts.element.get(0).tagName.toLowerCase() === "select") {
+ multiple = opts.element.prop("multiple");
+ } else {
+ multiple = opts.multiple || false;
+ if ("tags" in opts) {opts.multiple = multiple = true;}
+ }
+
+ select2 = multiple ? new MultiSelect2() : new SingleSelect2();
+ select2.init(opts);
+ } else if (typeof(args[0]) === "string") {
+
+ if (indexOf(args[0], allowedMethods) < 0) {
+ throw "Unknown method: " + args[0];
+ }
+
+ value = undefined;
+ select2 = $(this).data("select2");
+ if (select2 === undefined) return;
+
+ method=args[0];
+
+ if (method === "container") {
+ value = select2.container;
+ } else if (method === "dropdown") {
+ value = select2.dropdown;
+ } else {
+ if (methodsMap[method]) method = methodsMap[method];
+
+ value = select2[method].apply(select2, args.slice(1));
+ }
+ if (indexOf(args[0], valueMethods) >= 0
+ || (indexOf(args[0], propertyMethods) && args.length == 1)) {
+ return false; // abort the iteration, ready to return first matched value
+ }
+ } else {
+ throw "Invalid arguments to select2 plugin: " + args;
+ }
+ });
+ return (value === undefined) ? this : value;
+ };
+
+ // plugin defaults, accessible to users
+ $.fn.select2.defaults = {
+ width: "copy",
+ loadMorePadding: 0,
+ closeOnSelect: true,
+ openOnEnter: true,
+ containerCss: {},
+ dropdownCss: {},
+ containerCssClass: "",
+ dropdownCssClass: "",
+ formatResult: function(result, container, query, escapeMarkup) {
+ var markup=[];
+ markMatch(result.text, query.term, markup, escapeMarkup);
+ return markup.join("");
+ },
+ formatSelection: function (data, container, escapeMarkup) {
+ return data ? escapeMarkup(data.text) : undefined;
+ },
+ sortResults: function (results, container, query) {
+ return results;
+ },
+ formatResultCssClass: function(data) {return undefined;},
+ formatSelectionCssClass: function(data, container) {return undefined;},
+ formatNoMatches: function () { return "No matches found"; },
+ formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " more character" + (n == 1? "" : "s"); },
+ formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1? "" : "s"); },
+ formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); },
+ formatLoadMore: function (pageNumber) { return "Loading more results..."; },
+ formatSearching: function () { return "Searching..."; },
+ minimumResultsForSearch: 0,
+ minimumInputLength: 0,
+ maximumInputLength: null,
+ maximumSelectionSize: 0,
+ id: function (e) { return e.id; },
+ matcher: function(term, text) {
+ return stripDiacritics(''+text).toUpperCase().indexOf(stripDiacritics(''+term).toUpperCase()) >= 0;
+ },
+ separator: ",",
+ tokenSeparators: [],
+ tokenizer: defaultTokenizer,
+ escapeMarkup: defaultEscapeMarkup,
+ blurOnChange: false,
+ selectOnBlur: false,
+ adaptContainerCssClass: function(c) { return c; },
+ adaptDropdownCssClass: function(c) { return null; },
+ nextSearchTerm: function(selectedObject, currentSearchTerm) { return undefined; }
+ };
+
+ $.fn.select2.ajaxDefaults = {
+ transport: $.ajax,
+ params: {
+ type: "GET",
+ cache: false,
+ dataType: "json"
+ }
+ };
+
+ // exports
+ window.Select2 = {
+ query: {
+ ajax: ajax,
+ local: local,
+ tags: tags
+ }, util: {
+ debounce: debounce,
+ markMatch: markMatch,
+ escapeMarkup: defaultEscapeMarkup,
+ stripDiacritics: stripDiacritics
+ }, "class": {
+ "abstract": AbstractSelect2,
+ "single": SingleSelect2,
+ "multi": MultiSelect2
+ }
+ };
+
+}(jQuery));
Added: trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.min.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.min.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.min.js 2014-03-19 15:11:43 UTC (rev 1728)
@@ -0,0 +1,22 @@
+/*
+Copyright 2012 Igor Vaynberg
+
+Version: 3.4.5 Timestamp: Mon Nov 4 08:22:42 PST 2013
+
+This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
+General Public License version 2 (the "GPL License"). You may choose either license to govern your
+use of this software only upon the condition that you accept all of the terms of either the Apache
+License or the GPL License.
+
+You may obtain a copy of the Apache License and the GPL License at:
+
+http://www.apache.org/licenses/LICENSE-2.0
+http://www.gnu.org/licenses/gpl-2.0.html
+
+Unless required by applicable law or agreed to in writing, software distributed under the Apache License
+or the GPL Licesnse is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+either express or implied. See the Apache License and the GPL License for the specific language governing
+permissions and limitations under the Apache License and the GPL License.
+*/
+!function(a){"undefined"==typeof a.fn.each2&&a.extend(a.fn,{each2:function(b){for(var c=a([0]),d=-1,e=this.length;++d<e&&(c.context=c[0]=this[d])&&b.call(c[0],d,c)!==!1;);return this}})}(jQuery),function(a,b){"use strict";function n(a){var b,c,d,e;if(!a||a.length<1)return a;for(b="",c=0,d=a.length;d>c;c++)e=a.charAt(c),b+=m[e]||e;return b}function o(a,b){for(var c=0,d=b.length;d>c;c+=1)if(q(a,b[c]))return c;return-1}function p(){var b=a(l);b.appendTo("body");var c={width:b.width()-b[0].clientWidth,height:b.height()-b[0].clientHeight};return b.remove(),c}function q(a,c){return a===c?!0:a===b||c===b?!1:null===a||null===c?!1:a.constructor===String?a+""==c+"":c.constructor===String?c+""==a+"":!1}function r(b,c){var d,e,f;if(null===b||b.length<1)return[];for(d=b.split(c),e=0,f=d.length;f>e;e+=1)d[e]=a.trim(d[e]);return d}function s(a){return a.outerWidth(!1)-a.width()}function t(c){var d="keyup-change-value";c.on("keydown",function(){a.data(c,d)===b&&a.data(c,d,c.val())}),c.on("keyup",function(){var e=a.data(c,d);e!==b&&c.val()!==e&&(a.removeData(c,d),c.trigger("keyup-change"))})}function u(c){c.on("mousemove",function(c){var d=i;(d===b||d.x!==c.pageX||d.y!==c.pageY)&&a(c.target).trigger("mousemove-filtered",c)})}function v(a,c,d){d=d||b;var e;return function(){var b=arguments;window.clearTimeout(e),e=window.setTimeout(function(){c.apply(d,b)},a)}}function w(a){var c,b=!1;return function(){return b===!1&&(c=a(),b=!0),c}}function x(a,b){var c=v(a,function(a){b.trigger("scroll-debounced",a)});b.on("scroll",function(a){o(a.target,b.get())>=0&&c(a)})}function y(a){a[0]!==document.activeElement&&window.setTimeout(function(){var d,b=a[0],c=a.val().length;a.focus(),a.is(":visible")&&b===document.activeElement&&(b.setSelectionRange?b.setSelectionRange(c,c):b.createTextRange&&(d=b.createTextRange(),d.collapse(!1),d.select()))},0)}function z(b){b=a(b)[0];var c=0,d=0;if("selectionStart"in b)c=b.selectionStart,d=b.selectionEnd-c;else if("selection"in document){b.focus();var e=document.selection.createRange();d=document.selection.createRange().text.length,e.moveStart("character",-b.value.length),c=e.text.length-d}return{offset:c,length:d}}function A(a){a.preventDefault(),a.stopPropagation()}function B(a){a.preventDefault(),a.stopImmediatePropagation()}function C(b){if(!h){var c=b[0].currentStyle||window.getComputedStyle(b[0],null);h=a(document.createElement("div")).css({position:"absolute",left:"-10000px",top:"-10000px",display:"none",fontSize:c.fontSize,fontFamily:c.fontFamily,fontStyle:c.fontStyle,fontWeight:c.fontWeight,letterSpacing:c.letterSpacing,textTransform:c.textTransform,whiteSpace:"nowrap"}),h.attr("class","select2-sizer"),a("body").append(h)}return h.text(b.val()),h.width()}function D(b,c,d){var e,g,f=[];e=b.attr("class"),e&&(e=""+e,a(e.split(" ")).each2(function(){0===this.indexOf("select2-")&&f.push(this)})),e=c.attr("class"),e&&(e=""+e,a(e.split(" ")).each2(function(){0!==this.indexOf("select2-")&&(g=d(this),g&&f.push(g))})),b.attr("class",f.join(" "))}function E(a,b,c,d){var e=n(a.toUpperCase()).indexOf(n(b.toUpperCase())),f=b.length;return 0>e?(c.push(d(a)),void 0):(c.push(d(a.substring(0,e))),c.push("<span class='select2-match'>"),c.push(d(a.substring(e,e+f))),c.push("</span>"),c.push(d(a.substring(e+f,a.length))),void 0)}function F(a){var b={"\\":"\","&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})}function G(c){var d,e=null,f=c.quietMillis||100,g=c.url,h=this;return function(i){window.clearTimeout(d),d=window.setTimeout(function(){var d=c.data,f=g,j=c.transport||a.fn.select2.ajaxDefaults.transport,k={type:c.type||"GET",cache:c.cache||!1,jsonpCallback:c.jsonpCallback||b,dataType:c.dataType||"json"},l=a.extend({},a.fn.select2.ajaxDefaults.params,k);d=d?d.call(h,i.term,i.page,i.context):null,f="function"==typeof f?f.call(h,i.term,i.page,i.context):f,e&&e.abort(),c.params&&(a.isFunction(c.params)?a.extend(l,c.params.call(h)):a.extend(l,c.params)),a.extend(l,{url:f,dataType:c.dataType,data:d,success:function(a){var b=c.results(a,i.page);i.callback(b)}}),e=j.call(h,l)},f)}}function H(b){var d,e,c=b,f=function(a){return""+a.text};a.isArray(c)&&(e=c,c={results:e}),a.isFunction(c)===!1&&(e=c,c=function(){return e});var g=c();return g.text&&(f=g.text,a.isFunction(f)||(d=g.text,f=function(a){return a[d]})),function(b){var g,d=b.term,e={results:[]};return""===d?(b.callback(c()),void 0):(g=function(c,e){var h,i;if(c=c[0],c.children){h={};for(i in c)c.hasOwnProperty(i)&&(h[i]=c[i]);h.children=[],a(c.children).each2(function(a,b){g(b,h.children)}),(h.children.length||b.matcher(d,f(h),c))&&e.push(h)}else b.matcher(d,f(c),c)&&e.push(c)},a(c().results).each2(function(a,b){g(b,e.results)}),b.callback(e),void 0)}}function I(c){var d=a.isFunction(c);return function(e){var f=e.term,g={results:[]};a(d?c():c).each(function(){var a=this.text!==b,c=a?this.text:this;(""===f||e.matcher(f,c))&&g.results.push(a?this:{id:this,text:this})}),e.callback(g)}}function J(b,c){if(a.isFunction(b))return!0;if(!b)return!1;throw new Error(c+" must be a function or a falsy value")}function K(b){return a.isFunction(b)?b():b}function L(b){var c=0;return a.each(b,function(a,b){b.children?c+=L(b.children):c++}),c}function M(a,c,d,e){var h,i,j,k,l,f=a,g=!1;if(!e.createSearchChoice||!e.tokenSeparators||e.tokenSeparators.length<1)return b;for(;;){for(i=-1,j=0,k=e.tokenSeparators.length;k>j&&(l=e.tokenSeparators[j],i=a.indexOf(l),!(i>=0));j++);if(0>i)break;if(h=a.substring(0,i),a=a.substring(i+l.length),h.length>0&&(h=e.createSearchChoice.call(this,h,c),h!==b&&null!==h&&e.id(h)!==b&&null!==e.id(h))){for(g=!1,j=0,k=c.length;k>j;j++)if(q(e.id(h),e.id(c[j]))){g=!0;break}g||d(h)}}return f!==a?a:void 0}function N(b,c){var d=function(){};return d.prototype=new b,d.prototype.constructor=d,d.prototype.parent=b.prototype,d.prototype=a.extend(d.prototype,c),d}if(window.Select2===b){var c,d,e,f,g,h,j,k,i={x:0,y:0},c={TAB:9,ENTER:13,ESC:27,SPACE:32,LEFT:37,UP:38,RIGHT:39,DOWN:40,SHIFT:16,CTRL:17,ALT:18,PAGE_UP:33,PAGE_DOWN:34,HOME:36,END:35,BACKSPACE:8,DELETE:46,isArrow:function(a){switch(a=a.which?a.which:a){case c.LEFT:case c.RIGHT:case c.UP:case c.DOWN:return!0}return!1},isControl:function(a){var b=a.which;switch(b){case c.SHIFT:case c.CTRL:case c.ALT:return!0}return a.metaKey?!0:!1},isFunctionKey:function(a){return a=a.which?a.which:a,a>=112&&123>=a}},l="<div class='select2-measure-scrollbar'></div>",m={"\u24b6":"A","\uff21":"A","\xc0":"A","\xc1":"A","\xc2":"A","\u1ea6":"A","\u1ea4":"A","\u1eaa":"A","\u1ea8":"A","\xc3":"A","\u0100":"A","\u0102":"A","\u1eb0":"A","\u1eae":"A","\u1eb4":"A","\u1eb2":"A","\u0226":"A","\u01e0":"A","\xc4":"A","\u01de":"A","\u1ea2":"A","\xc5":"A","\u01fa":"A","\u01cd":"A","\u0200":"A","\u0202":"A","\u1ea0":"A","\u1eac":"A","\u1eb6":"A","\u1e00":"A","\u0104":"A","\u023a":"A","\u2c6f":"A","\ua732":"AA","\xc6":"AE","\u01fc":"AE","\u01e2":"AE","\ua734":"AO","\ua736":"AU","\ua738":"AV","\ua73a":"AV","\ua73c":"AY","\u24b7":"B","\uff22":"B","\u1e02":"B","\u1e04":"B","\u1e06":"B","\u0243":"B","\u0182":"B","\u0181":"B","\u24b8":"C","\uff23":"C","\u0106":"C","\u0108":"C","\u010a":"C","\u010c":"C","\xc7":"C","\u1e08":"C","\u0187":"C","\u023b":"C","\ua73e":"C","\u24b9":"D","\uff24":"D","\u1e0a":"D","\u010e":"D","\u1e0c":"D","\u1e10":"D","\u1e12":"D","\u1e0e":"D","\u0110":"D","\u018b":"D","\u018a":"D","\u0189":"D","\ua779":"D","\u01f1":"DZ","\u01c4":"DZ","\u01f2":"Dz","\u01c5":"Dz","\u24ba":"E","\uff25":"E","\xc8":"E","\xc9":"E","\xca":"E","\u1ec0":"E","\u1ebe":"E","\u1ec4":"E","\u1ec2":"E","\u1ebc":"E","\u0112":"E","\u1e14":"E","\u1e16":"E","\u0114":"E","\u0116":"E","\xcb":"E","\u1eba":"E","\u011a":"E","\u0204":"E","\u0206":"E","\u1eb8":"E","\u1ec6":"E","\u0228":"E","\u1e1c":"E","\u0118":"E","\u1e18":"E","\u1e1a":"E","\u0190":"E","\u018e":"E","\u24bb":"F","\uff26":"F","\u1e1e":"F","\u0191":"F","\ua77b":"F","\u24bc":"G","\uff27":"G","\u01f4":"G","\u011c":"G","\u1e20":"G","\u011e":"G","\u0120":"G","\u01e6":"G","\u0122":"G","\u01e4":"G","\u0193":"G","\ua7a0":"G","\ua77d":"G","\ua77e":"G","\u24bd":"H","\uff28":"H","\u0124":"H","\u1e22":"H","\u1e26":"H","\u021e":"H","\u1e24":"H","\u1e28":"H","\u1e2a":"H","\u0126":"H","\u2c67":"H","\u2c75":"H","\ua78d":"H","\u24be":"I","\uff29":"I","\xcc":"I","\xcd":"I","\xce":"I","\u0128":"I","\u012a":"I","\u012c":"I","\u0130":"I","\xcf":"I","\u1e2e":"I","\u1ec8":"I","\u01cf":"I","\u0208":"I","\u020a":"I","\u1eca":"I","\u012e":"I","\u1e2c":"I","\u0197":"I","\u24bf":"J","\uff2a":"J","\u0134":"J","\u0248":"J","\u24c0":"K","\uff2b":"K","\u1e30":"K","\u01e8":"K","\u1e32":"K","\u0136":"K","\u1e34":"K","\u0198":"K","\u2c69":"K","\ua740":"K","\ua742":"K","\ua744":"K","\ua7a2":"K","\u24c1":"L","\uff2c":"L","\u013f":"L","\u0139":"L","\u013d":"L","\u1e36":"L","\u1e38":"L","\u013b":"L","\u1e3c":"L","\u1e3a":"L","\u0141":"L","\u023d":"L","\u2c62":"L","\u2c60":"L","\ua748":"L","\ua746":"L","\ua780":"L","\u01c7":"LJ","\u01c8":"Lj","\u24c2":"M","\uff2d":"M","\u1e3e":"M","\u1e40":"M","\u1e42":"M","\u2c6e":"M","\u019c":"M","\u24c3":"N","\uff2e":"N","\u01f8":"N","\u0143":"N","\xd1":"N","\u1e44":"N","\u0147":"N","\u1e46":"N","\u0145":"N","\u1e4a":"N","\u1e48":"N","\u0220":"N","\u019d":"N","\ua790":"N","\ua7a4":"N","\u01ca":"NJ","\u01cb":"Nj","\u24c4":"O","\uff2f":"O","\xd2":"O","\xd3":"O","\xd4":"O","\u1ed2":"O","\u1ed0":"O","\u1ed6":"O","\u1ed4":"O","\xd5":"O","\u1e4c":"O","\u022c":"O","\u1e4e":"O","\u014c":"O","\u1e50":"O","\u1e52":"O","\u014e":"O","\u022e":"O","\u0230":"O","\xd6":"O","\u022a":"O","\u1ece":"O","\u0150":"O","\u01d1":"O","\u020c":"O","\u020e":"O","\u01a0":"O","\u1edc":"O","\u1eda":"O","\u1ee0":"O","\u1ede":"O","\u1ee2":"O","\u1ecc":"O","\u1ed8":"O","\u01ea":"O","\u01ec":"O","\xd8":"O","\u01fe":"O","\u0186":"O","\u019f":"O","\ua74a":"O","\ua74c":"O","\u01a2":"OI","\ua74e":"OO","\u0222":"OU","\u24c5":"P","\uff30":"P","\u1e54":"P","\u1e56":"P","\u01a4":"P","\u2c63":"P","\ua750":"P","\ua752":"P","\ua754":"P","\u24c6":"Q","\uff31":"Q","\ua756":"Q","\ua758":"Q","\u024a":"Q","\u24c7":"R","\uff32":"R","\u0154":"R","\u1e58":"R","\u0158":"R","\u0210":"R","\u0212":"R","\u1e5a":"R","\u1e5c":"R","\u0156":"R","\u1e5e":"R","\u024c":"R","\u2c64":"R","\ua75a":"R","\ua7a6":"R","\ua782":"R","\u24c8":"S","\uff33":"S","\u1e9e":"S","\u015a":"S","\u1e64":"S","\u015c":"S","\u1e60":"S","\u0160":"S","\u1e66":"S","\u1e62":"S","\u1e68":"S","\u0218":"S","\u015e":"S","\u2c7e":"S","\ua7a8":"S","\ua784":"S","\u24c9":"T","\uff34":"T","\u1e6a":"T","\u0164":"T","\u1e6c":"T","\u021a":"T","\u0162":"T","\u1e70":"T","\u1e6e":"T","\u0166":"T","\u01ac":"T","\u01ae":"T","\u023e":"T","\ua786":"T","\ua728":"TZ","\u24ca":"U","\uff35":"U","\xd9":"U","\xda":"U","\xdb":"U","\u0168":"U","\u1e78":"U","\u016a":"U","\u1e7a":"U","\u016c":"U","\xdc":"U","\u01db":"U","\u01d7":"U","\u01d5":"U","\u01d9":"U","\u1ee6":"U","\u016e":"U","\u0170":"U","\u01d3":"U","\u0214":"U","\u0216":"U","\u01af":"U","\u1eea":"U","\u1ee8":"U","\u1eee":"U","\u1eec":"U","\u1ef0":"U","\u1ee4":"U","\u1e72":"U","\u0172":"U","\u1e76":"U","\u1e74":"U","\u0244":"U","\u24cb":"V","\uff36":"V","\u1e7c":"V","\u1e7e":"V","\u01b2":"V","\ua75e":"V","\u0245":"V","\ua760":"VY","\u24cc":"W","\uff37":"W","\u1e80":"W","\u1e82":"W","\u0174":"W","\u1e86":"W","\u1e84":"W","\u1e88":"W","\u2c72":"W","\u24cd":"X","\uff38":"X","\u1e8a":"X","\u1e8c":"X","\u24ce":"Y","\uff39":"Y","\u1ef2":"Y","\xdd":"Y","\u0176":"Y","\u1ef8":"Y","\u0232":"Y","\u1e8e":"Y","\u0178":"Y","\u1ef6":"Y","\u1ef4":"Y","\u01b3":"Y","\u024e":"Y","\u1efe":"Y","\u24cf":"Z","\uff3a":"Z","\u0179":"Z","\u1e90":"Z","\u017b":"Z","\u017d":"Z","\u1e92":"Z","\u1e94":"Z","\u01b5":"Z","\u0224":"Z","\u2c7f":"Z","\u2c6b":"Z","\ua762":"Z","\u24d0":"a","\uff41":"a","\u1e9a":"a","\xe0":"a","\xe1":"a","\xe2":"a","\u1ea7":"a","\u1ea5":"a","\u1eab":"a","\u1ea9":"a","\xe3":"a","\u0101":"a","\u0103":"a","\u1eb1":"a","\u1eaf":"a","\u1eb5":"a","\u1eb3":"a","\u0227":"a","\u01e1":"a","\xe4":"a","\u01df":"a","\u1ea3":"a","\xe5":"a","\u01fb":"a","\u01ce":"a","\u0201":"a","\u0203":"a","\u1ea1":"a","\u1ead":"a","\u1eb7":"a","\u1e01":"a","\u0105":"a","\u2c65":"a","\u0250":"a","\ua733":"aa","\xe6":"ae","\u01fd":"ae","\u01e3":"ae","\ua735":"ao","\ua737":"au","\ua739":"av","\ua73b":"av","\ua73d":"ay","\u24d1":"b","\uff42":"b","\u1e03":"b","\u1e05":"b","\u1e07":"b","\u0180":"b","\u0183":"b","\u0253":"b","\u24d2":"c","\uff43":"c","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\xe7":"c","\u1e09":"c","\u0188":"c","\u023c":"c","\ua73f":"c","\u2184":"c","\u24d3":"d","\uff44":"d","\u1e0b":"d","\u010f":"d","\u1e0d":"d","\u1e11":"d","\u1e13":"d","\u1e0f":"d","\u0111":"d","\u018c":"d","\u0256":"d","\u0257":"d","\ua77a":"d","\u01f3":"dz","\u01c6":"dz","\u24d4":"e","\uff45":"e","\xe8":"e","\xe9":"e","\xea":"e","\u1ec1":"e","\u1ebf":"e","\u1ec5":"e","\u1ec3":"e","\u1ebd":"e","\u0113":"e","\u1e15":"e","\u1e17":"e","\u0115":"e","\u0117":"e","\xeb":"e","\u1ebb":"e","\u011b":"e","\u0205":"e","\u0207":"e","\u1eb9":"e","\u1ec7":"e","\u0229":"e","\u1e1d":"e","\u0119":"e","\u1e19":"e","\u1e1b":"e","\u0247":"e","\u025b":"e","\u01dd":"e","\u24d5":"f","\uff46":"f","\u1e1f":"f","\u0192":"f","\ua77c":"f","\u24d6":"g","\uff47":"g","\u01f5":"g","\u011d":"g","\u1e21":"g","\u011f":"g","\u0121":"g","\u01e7":"g","\u0123":"g","\u01e5":"g","\u0260":"g","\ua7a1":"g","\u1d79":"g","\ua77f":"g","\u24d7":"h","\uff48":"h","\u0125":"h","\u1e23":"h","\u1e27":"h","\u021f":"h","\u1e25":"h","\u1e29":"h","\u1e2b":"h","\u1e96":"h","\u0127":"h","\u2c68":"h","\u2c76":"h","\u0265":"h","\u0195":"hv","\u24d8":"i","\uff49":"i","\xec":"i","\xed":"i","\xee":"i","\u0129":"i","\u012b":"i","\u012d":"i","\xef":"i","\u1e2f":"i","\u1ec9":"i","\u01d0":"i","\u0209":"i","\u020b":"i","\u1ecb":"i","\u012f":"i","\u1e2d":"i","\u0268":"i","\u0131":"i","\u24d9":"j","\uff4a":"j","\u0135":"j","\u01f0":"j","\u0249":"j","\u24da":"k","\uff4b":"k","\u1e31":"k","\u01e9":"k","\u1e33":"k","\u0137":"k","\u1e35":"k","\u0199":"k","\u2c6a":"k","\ua741":"k","\ua743":"k","\ua745":"k","\ua7a3":"k","\u24db":"l","\uff4c":"l","\u0140":"l","\u013a":"l","\u013e":"l","\u1e37":"l","\u1e39":"l","\u013c":"l","\u1e3d":"l","\u1e3b":"l","\u017f":"l","\u0142":"l","\u019a":"l","\u026b":"l","\u2c61":"l","\ua749":"l","\ua781":"l","\ua747":"l","\u01c9":"lj","\u24dc":"m","\uff4d":"m","\u1e3f":"m","\u1e41":"m","\u1e43":"m","\u0271":"m","\u026f":"m","\u24dd":"n","\uff4e":"n","\u01f9":"n","\u0144":"n","\xf1":"n","\u1e45":"n","\u0148":"n","\u1e47":"n","\u0146":"n","\u1e4b":"n","\u1e49":"n","\u019e":"n","\u0272":"n","\u0149":"n","\ua791":"n","\ua7a5":"n","\u01cc":"nj","\u24de":"o","\uff4f":"o","\xf2":"o","\xf3":"o","\xf4":"o","\u1ed3":"o","\u1ed1":"o","\u1ed7":"o","\u1ed5":"o","\xf5":"o","\u1e4d":"o","\u022d":"o","\u1e4f":"o","\u014d":"o","\u1e51":"o","\u1e53":"o","\u014f":"o","\u022f":"o","\u0231":"o","\xf6":"o","\u022b":"o","\u1ecf":"o","\u0151":"o","\u01d2":"o","\u020d":"o","\u020f":"o","\u01a1":"o","\u1edd":"o","\u1edb":"o","\u1ee1":"o","\u1edf":"o","\u1ee3":"o","\u1ecd":"o","\u1ed9":"o","\u01eb":"o","\u01ed":"o","\xf8":"o","\u01ff":"o","\u0254":"o","\ua74b":"o","\ua74d":"o","\u0275":"o","\u01a3":"oi","\u0223":"ou","\ua74f":"oo","\u24df":"p","\uff50":"p","\u1e55":"p","\u1e57":"p","\u01a5":"p","\u1d7d":"p","\ua751":"p","\ua753":"p","\ua755":"p","\u24e0":"q","\uff51":"q","\u024b":"q","\ua757":"q","\ua759":"q","\u24e1":"r","\uff52":"r","\u0155":"r","\u1e59":"r","\u0159":"r","\u0211":"r","\u0213":"r","\u1e5b":"r","\u1e5d":"r","\u0157":"r","\u1e5f":"r","\u024d":"r","\u027d":"r","\ua75b":"r","\ua7a7":"r","\ua783":"r","\u24e2":"s","\uff53":"s","\xdf":"s","\u015b":"s","\u1e65":"s","\u015d":"s","\u1e61":"s","\u0161":"s","\u1e67":"s","\u1e63":"s","\u1e69":"s","\u0219":"s","\u015f":"s","\u023f":"s","\ua7a9":"s","\ua785":"s","\u1e9b":"s","\u24e3":"t","\uff54":"t","\u1e6b":"t","\u1e97":"t","\u0165":"t","\u1e6d":"t","\u021b":"t","\u0163":"t","\u1e71":"t","\u1e6f":"t","\u0167":"t","\u01ad":"t","\u0288":"t","\u2c66":"t","\ua787":"t","\ua729":"tz","\u24e4":"u","\uff55":"u","\xf9":"u","\xfa":"u","\xfb":"u","\u0169":"u","\u1e79":"u","\u016b":"u","\u1e7b":"u","\u016d":"u","\xfc":"u","\u01dc":"u","\u01d8":"u","\u01d6":"u","\u01da":"u","\u1ee7":"u","\u016f":"u","\u0171":"u","\u01d4":"u","\u0215":"u","\u0217":"u","\u01b0":"u","\u1eeb":"u","\u1ee9":"u","\u1eef":"u","\u1eed":"u","\u1ef1":"u","\u1ee5":"u","\u1e73":"u","\u0173":"u","\u1e77":"u","\u1e75":"u","\u0289":"u","\u24e5":"v","\uff56":"v","\u1e7d":"v","\u1e7f":"v","\u028b":"v","\ua75f":"v","\u028c":"v","\ua761":"vy","\u24e6":"w","\uff57":"w","\u1e81":"w","\u1e83":"w","\u0175":"w","\u1e87":"w","\u1e85":"w","\u1e98":"w","\u1e89":"w","\u2c73":"w","\u24e7":"x","\uff58":"x","\u1e8b":"x","\u1e8d":"x","\u24e8":"y","\uff59":"y","\u1ef3":"y","\xfd":"y","\u0177":"y","\u1ef9":"y","\u0233":"y","\u1e8f":"y","\xff":"y","\u1ef7":"y","\u1e99":"y","\u1ef5":"y","\u01b4":"y","\u024f":"y","\u1eff":"y","\u24e9":"z","\uff5a":"z","\u017a":"z","\u1e91":"z","\u017c":"z","\u017e":"z","\u1e93":"z","\u1e95":"z","\u01b6":"z","\u0225":"z","\u0240":"z","\u2c6c":"z","\ua763":"z"};j=a(document),g=function(){var a=1;return function(){return a++}}(),j.on("mousemove",function(a){i.x=a.pageX,i.y=a.pageY}),d=N(Object,{bind:function(a){var b=this;return function(){a.apply(b,arguments)}},init:function(c){var d,e,f=".select2-results";this.opts=c=this.prepareOpts(c),this.id=c.id,c.element.data("select2")!==b&&null!==c.element.data("select2")&&c.element.data("select2").destroy(),this.container=this.createContainer(),this.containerId="s2id_"+(c.element.attr("id")||"autogen"+g()),this.containerSelector="#"+this.containerId.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g,"\\$1"),this.container.attr("id",this.containerId),this.body=w(function(){return c.element.closest("body")}),D(this.container,this.opts.element,this.opts.adaptContainerCssClass),this.container.attr("style",c.element.attr("style")),this.container.css(K(c.containerCss)),this.container.addClass(K(c.containerCssClass)),this.elementTabIndex=this.opts.element.attr("tabindex"),this.opts.element.data("select2",this).attr("tabindex","-1").before(this.container).on("click.select2",A),this.container.data("select2",this),this.dropdown=this.container.find(".select2-drop"),D(this.dropdown,this.opts.element,this.opts.adaptDropdownCssClass),this.dropdown.addClass(K(c.dropdownCssClass)),this.dropdown.data("select2",this),this.dropdown.on("click",A),this.results=d=this.container.find(f),this.search=e=this.container.find("input.select2-input"),this.queryCount=0,this.resultsPage=0,this.context=null,this.initContainer(),this.container.on("click",A),u(this.results),this.dropdown.on("mousemove-filtered touchstart touchmove touchend",f,this.bind(this.highlightUnderEvent)),x(80,this.results),this.dropdown.on("scroll-debounced",f,this.bind(this.loadMoreIfNeeded)),a(this.container).on("change",".select2-input",function(a){a.stopPropagation()}),a(this.dropdown).on("change",".select2-input",function(a){a.stopPropagation()}),a.fn.mousewheel&&d.mousewheel(function(a,b,c,e){var f=d.scrollTop();e>0&&0>=f-e?(d.scrollTop(0),A(a)):0>e&&d.get(0).scrollHeight-d.scrollTop()+e<=d.height()&&(d.scrollTop(d.get(0).scrollHeight-d.height()),A(a))}),t(e),e.on("keyup-change input paste",this.bind(this.updateResults)),e.on("focus",function(){e.addClass("select2-focused")}),e.on("blur",function(){e.removeClass("select2-focused")}),this.dropdown.on("mouseup",f,this.bind(function(b){a(b.target).closest(".select2-result-selectable").length>0&&(this.highlightUnderEvent(b),this.selectHighlighted(b))})),this.dropdown.on("click mouseup mousedown",function(a){a.stopPropagation()}),a.isFunction(this.opts.initSelection)&&(this.initSelection(),this.monitorSource()),null!==c.maximumInputLength&&this.search.attr("maxlength",c.maximumInputLength);var h=c.element.prop("disabled");h===b&&(h=!1),this.enable(!h);var i=c.element.prop("readonly");i===b&&(i=!1),this.readonly(i),k=k||p(),this.autofocus=c.element.prop("autofocus"),c.element.prop("autofocus",!1),this.autofocus&&this.focus(),this.nextSearchTerm=b},destroy:function(){var a=this.opts.element,c=a.data("select2");this.close(),this.propertyObserver&&(delete this.propertyObserver,this.propertyObserver=null),c!==b&&(c.container.remove(),c.dropdown.remove(),a.removeClass("select2-offscreen").removeData("select2").off(".select2").prop("autofocus",this.autofocus||!1),this.elementTabIndex?a.attr({tabindex:this.elementTabIndex}):a.removeAttr("tabindex"),a.show())},optionToData:function(a){return a.is("option")?{id:a.prop("value"),text:a.text(),element:a.get(),css:a.attr("class"),disabled:a.prop("disabled"),locked:q(a.attr("locked"),"locked")||q(a.data("locked"),!0)}:a.is("optgroup")?{text:a.attr("label"),children:[],element:a.get(),css:a.attr("class")}:void 0},prepareOpts:function(c){var d,e,f,g,h=this;if(d=c.element,"select"===d.get(0).tagName.toLowerCase()&&(this.select=e=c.element),e&&a.each(["id","multiple","ajax","query","createSearchChoice","initSelection","data","tags"],function(){if(this in c)throw new Error("Option '"+this+"' is not allowed for Select2 when attached to a <select> element.")}),c=a.extend({},{populateResults:function(d,e,f){var g,i=this.opts.id;g=function(d,e,j){var k,l,m,n,o,p,q,r,s,t;for(d=c.sortResults(d,e,f),k=0,l=d.length;l>k;k+=1)m=d[k],o=m.disabled===!0,n=!o&&i(m)!==b,p=m.children&&m.children.length>0,q=a("<li></li>"),q.addClass("select2-results-dept-"+j),q.addClass("select2-result"),q.addClass(n?"select2-result-selectable":"select2-result-unselectable"),o&&q.addClass("select2-disabled"),p&&q.addClass("select2-result-with-children"),q.addClass(h.opts.formatResultCssClass(m)),r=a(document.createElement("div")),r.addClass("select2-result-label"),t=c.formatResult(m,r,f,h.opts.escapeMarkup),t!==b&&r.html(t),q.append(r),p&&(s=a("<ul></ul>"),s.addClass("select2-result-sub"),g(m.children,s,j+1),q.append(s)),q.data("select2-data",m),e.append(q)},g(e,d,0)}},a.fn.select2.defaults,c),"function"!=typeof c.id&&(f=c.id,c.id=function(a){return a[f]}),a.isArray(c.element.data("select2Tags"))){if("tags"in c)throw"tags specified as both an attribute 'data-select2-tags' and in options of Select2 "+c.element.attr("id");c.tags=c.element.data("select2Tags")}if(e?(c.query=this.bind(function(a){var f,g,i,c={results:[],more:!1},e=a.term;i=function(b,c){var d;b.is("option")?a.matcher(e,b.text(),b)&&c.push(h.optionToData(b)):b.is("optgroup")&&(d=h.optionToData(b),b.children().each2(function(a,b){i(b,d.children)}),d.children.length>0&&c.push(d))},f=d.children(),this.getPlaceholder()!==b&&f.length>0&&(g=this.getPlaceholderOption(),g&&(f=f.not(g))),f.each2(function(a,b){i(b,c.results)}),a.callback(c)}),c.id=function(a){return a.id},c.formatResultCssClass=function(a){return a.css}):"query"in c||("ajax"in c?(g=c.element.data("ajax-url"),g&&g.length>0&&(c.ajax.url=g),c.query=G.call(c.element,c.ajax)):"data"in c?c.query=H(c.data):"tags"in c&&(c.query=I(c.tags),c.createSearchChoice===b&&(c.createSearchChoice=function(b){return{id:a.trim(b),text:a.trim(b)}}),c.initSelection===b&&(c.initSelection=function(b,d){var e=[];a(r(b.val(),c.separator)).each(function(){var b={id:this,text:this},d=c.tags;a.isFunction(d)&&(d=d()),a(d).each(function(){return q(this.id,b.id)?(b=this,!1):void 0}),e.push(b)}),d(e)}))),"function"!=typeof c.query)throw"query function not defined for Select2 "+c.element.attr("id");return c},monitorSource:function(){var c,d,a=this.opts.element;a.on("change.select2",this.bind(function(){this.opts.element.data("select2-change-triggered")!==!0&&this.initSelection()})),c=this.bind(function(){var c=a.prop("disabled");c===b&&(c=!1),this.enable(!c);var d=a.prop("readonly");d===b&&(d=!1),this.readonly(d),D(this.container,this.opts.element,this.opts.adaptContainerCssClass),this.container.addClass(K(this.opts.containerCssClass)),D(this.dropdown,this.opts.element,this.opts.adaptDropdownCssClass),this.dropdown.addClass(K(this.opts.dropdownCssClass))}),a.on("propertychange.select2",c),this.mutationCallback===b&&(this.mutationCallback=function(a){a.forEach(c)}),d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver,d!==b&&(this.propertyObserver&&(delete this.propertyObserver,this.propertyObserver=null),this.propertyObserver=new d(this.mutationCallback),this.propertyObserver.observe(a.get(0),{attributes:!0,subtree:!1}))},triggerSelect:function(b){var c=a.Event("select2-selecting",{val:this.id(b),object:b});return this.opts.element.trigger(c),!c.isDefaultPrevented()},triggerChange:function(b){b=b||{},b=a.extend({},b,{type:"change",val:this.val()}),this.opts.element.data("select2-change-triggered",!0),this.opts.element.trigger(b),this.opts.element.data("select2-change-triggered",!1),this.opts.element.click(),this.opts.blurOnChange&&this.opts.element.blur()},isInterfaceEnabled:function(){return this.enabledInterface===!0},enableInterface:function(){var a=this._enabled&&!this._readonly,b=!a;return a===this.enabledInterface?!1:(this.container.toggleClass("select2-container-disabled",b),this.close(),this.enabledInterface=a,!0)},enable:function(a){a===b&&(a=!0),this._enabled!==a&&(this._enabled=a,this.opts.element.prop("disabled",!a),this.enableInterface())},disable:function(){this.enable(!1)},readonly:function(a){return a===b&&(a=!1),this._readonly===a?!1:(this._readonly=a,this.opts.element.prop("readonly",a),this.enableInterface(),!0)},opened:function(){return this.container.hasClass("select2-dropdown-open")},positionDropdown:function(){var t,u,v,w,x,b=this.dropdown,c=this.container.offset(),d=this.container.outerHeight(!1),e=this.container.outerWidth(!1),f=b.outerHeight(!1),g=a(window),h=g.width(),i=g.height(),j=g.scrollLeft()+h,l=g.scrollTop()+i,m=c.top+d,n=c.left,o=l>=m+f,p=c.top-f>=this.body().scrollTop(),q=b.outerWidth(!1),r=j>=n+q,s=b.hasClass("select2-drop-above");s?(u=!0,!p&&o&&(v=!0,u=!1)):(u=!1,!o&&p&&(v=!0,u=!0)),v&&(b.hide(),c=this.container.offset(),d=this.container.outerHeight(!1),e=this.container.outerWidth(!1),f=b.outerHeight(!1),j=g.scrollLeft()+h,l=g.scrollTop()+i,m=c.top+d,n=c.left,q=b.outerWidth(!1),r=j>=n+q,b.show()),this.opts.dropdownAutoWidth?(x=a(".select2-results",b)[0],b.addClass("select2-drop-auto-width"),b.css("width",""),q=b.outerWidth(!1)+(x.scrollHeight===x.clientHeight?0:k.width),q>e?e=q:q=e,r=j>=n+q):this.container.removeClass("select2-drop-auto-width"),"static"!==this.body().css("position")&&(t=this.body().offset(),m-=t.top,n-=t.left),r||(n=c.left+e-q),w={left:n,width:e},u?(w.bottom=i-c.top,w.top="auto",this.container.addClass("select2-drop-above"),b.addClass("select2-drop-above")):(w.top=m,w.bottom="auto",this.container.removeClass("select2-drop-above"),b.removeClass("select2-drop-above")),w=a.extend(w,K(this.opts.dropdownCss)),b.css(w)},shouldOpen:function(){var b;return this.opened()?!1:this._enabled===!1||this._readonly===!0?!1:(b=a.Event("select2-opening"),this.opts.element.trigger(b),!b.isDefaultPrevented())},clearDropdownAlignmentPreference:function(){this.container.removeClass("select2-drop-above"),this.dropdown.removeClass("select2-drop-above")},open:function(){return this.shouldOpen()?(this.opening(),!0):!1},opening:function(){var f,b=this.containerId,c="scroll."+b,d="resize."+b,e="orientationchange."+b;this.container.addClass("select2-dropdown-open").addClass("select2-container-active"),this.clearDropdownAlignmentPreference(),this.dropdown[0]!==this.body().children().last()[0]&&this.dropdown.detach().appendTo(this.body()),f=a("#select2-drop-mask"),0==f.length&&(f=a(document.createElement("div")),f.attr("id","select2-drop-mask").attr("class","select2-drop-mask"),f.hide(),f.appendTo(this.body()),f.on("mousedown touchstart click",function(b){var d,c=a("#select2-drop");c.length>0&&(d=c.data("select2"),d.opts.selectOnBlur&&d.selectHighlighted({noFocus:!0}),d.close({focus:!0}),b.preventDefault(),b.stopPropagation())})),this.dropdown.prev()[0]!==f[0]&&this.dropdown.before(f),a("#select2-drop").removeAttr("id"),this.dropdown.attr("id","select2-drop"),f.show(),this.positionDropdown(),this.dropdown.show(),this.positionDropdown(),this.dropdown.addClass("select2-drop-active");var g=this;this.container.parents().add(window).each(function(){a(this).on(d+" "+c+" "+e,function(){g.positionDropdown()})})},close:function(){if(this.opened()){var b=this.containerId,c="scroll."+b,d="resize."+b,e="orientationchange."+b;this.container.parents().add(window).each(function(){a(this).off(c).off(d).off(e)}),this.clearDropdownAlignmentPreference(),a("#select2-drop-mask").hide(),this.dropdown.removeAttr("id"),this.dropdown.hide(),this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active"),this.results.empty(),this.clearSearch(),this.search.removeClass("select2-active"),this.opts.element.trigger(a.Event("select2-close"))}},externalSearch:function(a){this.open(),this.search.val(a),this.updateResults(!1)},clearSearch:function(){},getMaximumSelectionSize:function(){return K(this.opts.maximumSelectionSize)},ensureHighlightVisible:function(){var c,d,e,f,g,h,i,b=this.results;if(d=this.highlight(),!(0>d)){if(0==d)return b.scrollTop(0),void 0;c=this.findHighlightableChoices().find(".select2-result-label"),e=a(c[d]),f=e.offset().top+e.outerHeight(!0),d===c.length-1&&(i=b.find("li.select2-more-results"),i.length>0&&(f=i.offset().top+i.outerHeight(!0))),g=b.offset().top+b.outerHeight(!0),f>g&&b.scrollTop(b.scrollTop()+(f-g)),h=e.offset().top-b.offset().top,0>h&&"none"!=e.css("display")&&b.scrollTop(b.scrollTop()+h)}},findHighlightableChoices:function(){return this.results.find(".select2-result-selectable:not(.select2-disabled, .select2-selected)")},moveHighlight:function(b){for(var c=this.findHighlightableChoices(),d=this.highlight();d>-1&&d<c.length;){d+=b;var e=a(c[d]);if(e.hasClass("select2-result-selectable")&&!e.hasClass("select2-disabled")&&!e.hasClass("select2-selected")){this.highlight(d);break}}},highlight:function(b){var d,e,c=this.findHighlightableChoices();return 0===arguments.length?o(c.filter(".select2-highlighted")[0],c.get()):(b>=c.length&&(b=c.length-1),0>b&&(b=0),this.removeHighlight(),d=a(c[b]),d.addClass("select2-highlighted"),this.ensureHighlightVisible(),e=d.data("select2-data"),e&&this.opts.element.trigger({type:"select2-highlight",val:this.id(e),choice:e}),void 0)},removeHighlight:function(){this.results.find(".select2-highlighted").removeClass("select2-highlighted")},countSelectableResults:function(){return this.findHighlightableChoices().length},highlightUnderEvent:function(b){var c=a(b.target).closest(".select2-result-selectable");if(c.length>0&&!c.is(".select2-highlighted")){var d=this.findHighlightableChoices();this.highlight(d.index(c))}else 0==c.length&&this.removeHighlight()},loadMoreIfNeeded:function(){var c,a=this.results,b=a.find("li.select2-more-results"),d=this.resultsPage+1,e=this,f=this.search.val(),g=this.context;0!==b.length&&(c=b.offset().top-a.offset().top-a.height(),c<=this.opts.loadMorePadding&&(b.addClass("select2-active"),this.opts.query({element:this.opts.element,term:f,page:d,context:g,matcher:this.opts.matcher,callback:this.bind(function(c){e.opened()&&(e.opts.populateResults.call(this,a,c.results,{term:f,page:d,context:g}),e.postprocessResults(c,!1,!1),c.more===!0?(b.detach().appendTo(a).text(e.opts.formatLoadMore(d+1)),window.setTimeout(function(){e.loadMoreIfNeeded()},10)):b.remove(),e.positionDropdown(),e.resultsPage=d,e.context=c.context,this.opts.element.trigger({type:"select2-loaded",items:c}))})})))},tokenize:function(){},updateResults:function(c){function m(){d.removeClass("select2-active"),h.positionDropdown()}function n(a){e.html(a),m()}var g,i,l,d=this.search,e=this.results,f=this.opts,h=this,j=d.val(),k=a.data(this.container,"select2-last-term");if((c===!0||!k||!q(j,k))&&(a.data(this.container,"select2-last-term",j),c===!0||this.showSearchInput!==!1&&this.opened())){l=++this.queryCount;var o=this.getMaximumSelectionSize();if(o>=1&&(g=this.data(),a.isArray(g)&&g.length>=o&&J(f.formatSelectionTooBig,"formatSelectionTooBig")))return n("<li class='select2-selection-limit'>"+f.formatSelectionTooBig(o)+"</li>"),void 0;if(d.val().length<f.minimumInputLength)return J(f.formatInputTooShort,"formatInputTooShort")?n("<li class='select2-no-results'>"+f.formatInputTooShort(d.val(),f.minimumInputLength)+"</li>"):n(""),c&&this.showSearch&&this.showSearch(!0),void 0;
+if(f.maximumInputLength&&d.val().length>f.maximumInputLength)return J(f.formatInputTooLong,"formatInputTooLong")?n("<li class='select2-no-results'>"+f.formatInputTooLong(d.val(),f.maximumInputLength)+"</li>"):n(""),void 0;f.formatSearching&&0===this.findHighlightableChoices().length&&n("<li class='select2-searching'>"+f.formatSearching()+"</li>"),d.addClass("select2-active"),this.removeHighlight(),i=this.tokenize(),i!=b&&null!=i&&d.val(i),this.resultsPage=1,f.query({element:f.element,term:d.val(),page:this.resultsPage,context:null,matcher:f.matcher,callback:this.bind(function(g){var i;if(l==this.queryCount){if(!this.opened())return this.search.removeClass("select2-active"),void 0;if(this.context=g.context===b?null:g.context,this.opts.createSearchChoice&&""!==d.val()&&(i=this.opts.createSearchChoice.call(h,d.val(),g.results),i!==b&&null!==i&&h.id(i)!==b&&null!==h.id(i)&&0===a(g.results).filter(function(){return q(h.id(this),h.id(i))}).length&&g.results.unshift(i)),0===g.results.length&&J(f.formatNoMatches,"formatNoMatches"))return n("<li class='select2-no-results'>"+f.formatNoMatches(d.val())+"</li>"),void 0;e.empty(),h.opts.populateResults.call(this,e,g.results,{term:d.val(),page:this.resultsPage,context:null}),g.more===!0&&J(f.formatLoadMore,"formatLoadMore")&&(e.append("<li class='select2-more-results'>"+h.opts.escapeMarkup(f.formatLoadMore(this.resultsPage))+"</li>"),window.setTimeout(function(){h.loadMoreIfNeeded()},10)),this.postprocessResults(g,c),m(),this.opts.element.trigger({type:"select2-loaded",items:g})}})})}},cancel:function(){this.close()},blur:function(){this.opts.selectOnBlur&&this.selectHighlighted({noFocus:!0}),this.close(),this.container.removeClass("select2-container-active"),this.search[0]===document.activeElement&&this.search.blur(),this.clearSearch(),this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus")},focusSearch:function(){y(this.search)},selectHighlighted:function(a){var b=this.highlight(),c=this.results.find(".select2-highlighted"),d=c.closest(".select2-result").data("select2-data");d?(this.highlight(b),this.onSelect(d,a)):a&&a.noFocus&&this.close()},getPlaceholder:function(){var a;return this.opts.element.attr("placeholder")||this.opts.element.attr("data-placeholder")||this.opts.element.data("placeholder")||this.opts.placeholder||((a=this.getPlaceholderOption())!==b?a.text():b)},getPlaceholderOption:function(){if(this.select){var a=this.select.children("option").first();if(this.opts.placeholderOption!==b)return"first"===this.opts.placeholderOption&&a||"function"==typeof this.opts.placeholderOption&&this.opts.placeholderOption(this.select);if(""===a.text()&&""===a.val())return a}},initContainerWidth:function(){function c(){var c,d,e,f,g,h;if("off"===this.opts.width)return null;if("element"===this.opts.width)return 0===this.opts.element.outerWidth(!1)?"auto":this.opts.element.outerWidth(!1)+"px";if("copy"===this.opts.width||"resolve"===this.opts.width){if(c=this.opts.element.attr("style"),c!==b)for(d=c.split(";"),f=0,g=d.length;g>f;f+=1)if(h=d[f].replace(/\s/g,""),e=h.match(/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i),null!==e&&e.length>=1)return e[1];return"resolve"===this.opts.width?(c=this.opts.element.css("width"),c.indexOf("%")>0?c:0===this.opts.element.outerWidth(!1)?"auto":this.opts.element.outerWidth(!1)+"px"):null}return a.isFunction(this.opts.width)?this.opts.width():this.opts.width}var d=c.call(this);null!==d&&this.container.css("width",d)}}),e=N(d,{createContainer:function(){var b=a(document.createElement("div")).attr({"class":"select2-container"}).html(["<a href='javascript:void(0)' onclick='return false;' class='select2-choice' tabindex='-1'>"," <span class='select2-chosen'> </span><abbr class='select2-search-choice-close'></abbr>"," <span class='select2-arrow'><b></b></span>","</a>","<input class='select2-focusser select2-offscreen' type='text'/>","<div class='select2-drop select2-display-none'>"," <div class='select2-search'>"," <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input'/>"," </div>"," <ul class='select2-results'>"," </ul>","</div>"].join(""));return b},enableInterface:function(){this.parent.enableInterface.apply(this,arguments)&&this.focusser.prop("disabled",!this.isInterfaceEnabled())},opening:function(){var c,d,e;this.opts.minimumResultsForSearch>=0&&this.showSearch(!0),this.parent.opening.apply(this,arguments),this.showSearchInput!==!1&&this.search.val(this.focusser.val()),this.search.focus(),c=this.search.get(0),c.createTextRange?(d=c.createTextRange(),d.collapse(!1),d.select()):c.setSelectionRange&&(e=this.search.val().length,c.setSelectionRange(e,e)),""===this.search.val()&&this.nextSearchTerm!=b&&(this.search.val(this.nextSearchTerm),this.search.select()),this.focusser.prop("disabled",!0).val(""),this.updateResults(!0),this.opts.element.trigger(a.Event("select2-open"))},close:function(a){this.opened()&&(this.parent.close.apply(this,arguments),a=a||{focus:!0},this.focusser.removeAttr("disabled"),a.focus&&this.focusser.focus())},focus:function(){this.opened()?this.close():(this.focusser.removeAttr("disabled"),this.focusser.focus())},isFocused:function(){return this.container.hasClass("select2-container-active")},cancel:function(){this.parent.cancel.apply(this,arguments),this.focusser.removeAttr("disabled"),this.focusser.focus()},destroy:function(){a("label[for='"+this.focusser.attr("id")+"']").attr("for",this.opts.element.attr("id")),this.parent.destroy.apply(this,arguments)},initContainer:function(){var b,d=this.container,e=this.dropdown;this.opts.minimumResultsForSearch<0?this.showSearch(!1):this.showSearch(!0),this.selection=b=d.find(".select2-choice"),this.focusser=d.find(".select2-focusser"),this.focusser.attr("id","s2id_autogen"+g()),a("label[for='"+this.opts.element.attr("id")+"']").attr("for",this.focusser.attr("id")),this.focusser.attr("tabindex",this.elementTabIndex),this.search.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()){if(a.which===c.PAGE_UP||a.which===c.PAGE_DOWN)return A(a),void 0;switch(a.which){case c.UP:case c.DOWN:return this.moveHighlight(a.which===c.UP?-1:1),A(a),void 0;case c.ENTER:return this.selectHighlighted(),A(a),void 0;case c.TAB:return this.selectHighlighted({noFocus:!0}),void 0;case c.ESC:return this.cancel(a),A(a),void 0}}})),this.search.on("blur",this.bind(function(){document.activeElement===this.body().get(0)&&window.setTimeout(this.bind(function(){this.search.focus()}),0)})),this.focusser.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()&&a.which!==c.TAB&&!c.isControl(a)&&!c.isFunctionKey(a)&&a.which!==c.ESC){if(this.opts.openOnEnter===!1&&a.which===c.ENTER)return A(a),void 0;if(a.which==c.DOWN||a.which==c.UP||a.which==c.ENTER&&this.opts.openOnEnter){if(a.altKey||a.ctrlKey||a.shiftKey||a.metaKey)return;return this.open(),A(a),void 0}return a.which==c.DELETE||a.which==c.BACKSPACE?(this.opts.allowClear&&this.clear(),A(a),void 0):void 0}})),t(this.focusser),this.focusser.on("keyup-change input",this.bind(function(a){if(this.opts.minimumResultsForSearch>=0){if(a.stopPropagation(),this.opened())return;this.open()}})),b.on("mousedown","abbr",this.bind(function(a){this.isInterfaceEnabled()&&(this.clear(),B(a),this.close(),this.selection.focus())})),b.on("mousedown",this.bind(function(b){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.opened()?this.close():this.isInterfaceEnabled()&&this.open(),A(b)})),e.on("mousedown",this.bind(function(){this.search.focus()})),b.on("focus",this.bind(function(a){A(a)})),this.focusser.on("focus",this.bind(function(){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active")})).on("blur",this.bind(function(){this.opened()||(this.container.removeClass("select2-container-active"),this.opts.element.trigger(a.Event("select2-blur")))})),this.search.on("focus",this.bind(function(){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active")})),this.initContainerWidth(),this.opts.element.addClass("select2-offscreen"),this.setPlaceholder()},clear:function(b){var c=this.selection.data("select2-data");if(c){var d=a.Event("select2-clearing");if(this.opts.element.trigger(d),d.isDefaultPrevented())return;var e=this.getPlaceholderOption();this.opts.element.val(e?e.val():""),this.selection.find(".select2-chosen").empty(),this.selection.removeData("select2-data"),this.setPlaceholder(),b!==!1&&(this.opts.element.trigger({type:"select2-removed",val:this.id(c),choice:c}),this.triggerChange({removed:c}))}},initSelection:function(){if(this.isPlaceholderOptionSelected())this.updateSelection(null),this.close(),this.setPlaceholder();else{var c=this;this.opts.initSelection.call(null,this.opts.element,function(a){a!==b&&null!==a&&(c.updateSelection(a),c.close(),c.setPlaceholder())})}},isPlaceholderOptionSelected:function(){var a;return this.getPlaceholder()?(a=this.getPlaceholderOption())!==b&&a.prop("selected")||""===this.opts.element.val()||this.opts.element.val()===b||null===this.opts.element.val():!1},prepareOpts:function(){var b=this.parent.prepareOpts.apply(this,arguments),c=this;return"select"===b.element.get(0).tagName.toLowerCase()?b.initSelection=function(a,b){var d=a.find("option").filter(function(){return this.selected});b(c.optionToData(d))}:"data"in b&&(b.initSelection=b.initSelection||function(c,d){var e=c.val(),f=null;b.query({matcher:function(a,c,d){var g=q(e,b.id(d));return g&&(f=d),g},callback:a.isFunction(d)?function(){d(f)}:a.noop})}),b},getPlaceholder:function(){return this.select&&this.getPlaceholderOption()===b?b:this.parent.getPlaceholder.apply(this,arguments)},setPlaceholder:function(){var a=this.getPlaceholder();if(this.isPlaceholderOptionSelected()&&a!==b){if(this.select&&this.getPlaceholderOption()===b)return;this.selection.find(".select2-chosen").html(this.opts.escapeMarkup(a)),this.selection.addClass("select2-default"),this.container.removeClass("select2-allowclear")}},postprocessResults:function(a,b,c){var d=0,e=this;if(this.findHighlightableChoices().each2(function(a,b){return q(e.id(b.data("select2-data")),e.opts.element.val())?(d=a,!1):void 0}),c!==!1&&(b===!0&&d>=0?this.highlight(d):this.highlight(0)),b===!0){var g=this.opts.minimumResultsForSearch;g>=0&&this.showSearch(L(a.results)>=g)}},showSearch:function(b){this.showSearchInput!==b&&(this.showSearchInput=b,this.dropdown.find(".select2-search").toggleClass("select2-search-hidden",!b),this.dropdown.find(".select2-search").toggleClass("select2-offscreen",!b),a(this.dropdown,this.container).toggleClass("select2-with-searchbox",b))},onSelect:function(a,b){if(this.triggerSelect(a)){var c=this.opts.element.val(),d=this.data();this.opts.element.val(this.id(a)),this.updateSelection(a),this.opts.element.trigger({type:"select2-selected",val:this.id(a),choice:a}),this.nextSearchTerm=this.opts.nextSearchTerm(a,this.search.val()),this.close(),b&&b.noFocus||this.focusser.focus(),q(c,this.id(a))||this.triggerChange({added:a,removed:d})}},updateSelection:function(a){var d,e,c=this.selection.find(".select2-chosen");this.selection.data("select2-data",a),c.empty(),null!==a&&(d=this.opts.formatSelection(a,c,this.opts.escapeMarkup)),d!==b&&c.append(d),e=this.opts.formatSelectionCssClass(a,c),e!==b&&c.addClass(e),this.selection.removeClass("select2-default"),this.opts.allowClear&&this.getPlaceholder()!==b&&this.container.addClass("select2-allowclear")},val:function(){var a,c=!1,d=null,e=this,f=this.data();if(0===arguments.length)return this.opts.element.val();if(a=arguments[0],arguments.length>1&&(c=arguments[1]),this.select)this.select.val(a).find("option").filter(function(){return this.selected}).each2(function(a,b){return d=e.optionToData(b),!1}),this.updateSelection(d),this.setPlaceholder(),c&&this.triggerChange({added:d,removed:f});else{if(!a&&0!==a)return this.clear(c),void 0;if(this.opts.initSelection===b)throw new Error("cannot call val() if initSelection() is not defined");this.opts.element.val(a),this.opts.initSelection(this.opts.element,function(a){e.opts.element.val(a?e.id(a):""),e.updateSelection(a),e.setPlaceholder(),c&&e.triggerChange({added:a,removed:f})})}},clearSearch:function(){this.search.val(""),this.focusser.val("")},data:function(a){var c,d=!1;return 0===arguments.length?(c=this.selection.data("select2-data"),c==b&&(c=null),c):(arguments.length>1&&(d=arguments[1]),a?(c=this.data(),this.opts.element.val(a?this.id(a):""),this.updateSelection(a),d&&this.triggerChange({added:a,removed:c})):this.clear(d),void 0)}}),f=N(d,{createContainer:function(){var b=a(document.createElement("div")).attr({"class":"select2-container select2-container-multi"}).html(["<ul class='select2-choices'>"," <li class='select2-search-field'>"," <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input'>"," </li>","</ul>","<div class='select2-drop select2-drop-multi select2-display-none'>"," <ul class='select2-results'>"," </ul>","</div>"].join(""));return b},prepareOpts:function(){var b=this.parent.prepareOpts.apply(this,arguments),c=this;return"select"===b.element.get(0).tagName.toLowerCase()?b.initSelection=function(a,b){var d=[];a.find("option").filter(function(){return this.selected}).each2(function(a,b){d.push(c.optionToData(b))}),b(d)}:"data"in b&&(b.initSelection=b.initSelection||function(c,d){var e=r(c.val(),b.separator),f=[];b.query({matcher:function(c,d,g){var h=a.grep(e,function(a){return q(a,b.id(g))}).length;return h&&f.push(g),h},callback:a.isFunction(d)?function(){for(var a=[],c=0;c<e.length;c++)for(var g=e[c],h=0;h<f.length;h++){var i=f[h];if(q(g,b.id(i))){a.push(i),f.splice(h,1);break}}d(a)}:a.noop})}),b},selectChoice:function(a){var b=this.container.find(".select2-search-choice-focus");b.length&&a&&a[0]==b[0]||(b.length&&this.opts.element.trigger("choice-deselected",b),b.removeClass("select2-search-choice-focus"),a&&a.length&&(this.close(),a.addClass("select2-search-choice-focus"),this.opts.element.trigger("choice-selected",a)))},destroy:function(){a("label[for='"+this.search.attr("id")+"']").attr("for",this.opts.element.attr("id")),this.parent.destroy.apply(this,arguments)},initContainer:function(){var d,b=".select2-choices";this.searchContainer=this.container.find(".select2-search-field"),this.selection=d=this.container.find(b);var e=this;this.selection.on("click",".select2-search-choice:not(.select2-locked)",function(){e.search[0].focus(),e.selectChoice(a(this))}),this.search.attr("id","s2id_autogen"+g()),a("label[for='"+this.opts.element.attr("id")+"']").attr("for",this.search.attr("id")),this.search.on("input paste",this.bind(function(){this.isInterfaceEnabled()&&(this.opened()||this.open())})),this.search.attr("tabindex",this.elementTabIndex),this.keydowns=0,this.search.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()){++this.keydowns;var b=d.find(".select2-search-choice-focus"),e=b.prev(".select2-search-choice:not(.select2-locked)"),f=b.next(".select2-search-choice:not(.select2-locked)"),g=z(this.search);if(b.length&&(a.which==c.LEFT||a.which==c.RIGHT||a.which==c.BACKSPACE||a.which==c.DELETE||a.which==c.ENTER)){var h=b;return a.which==c.LEFT&&e.length?h=e:a.which==c.RIGHT?h=f.length?f:null:a.which===c.BACKSPACE?(this.unselect(b.first()),this.search.width(10),h=e.length?e:f):a.which==c.DELETE?(this.unselect(b.first()),this.search.width(10),h=f.length?f:null):a.which==c.ENTER&&(h=null),this.selectChoice(h),A(a),h&&h.length||this.open(),void 0}if((a.which===c.BACKSPACE&&1==this.keydowns||a.which==c.LEFT)&&0==g.offset&&!g.length)return this.selectChoice(d.find(".select2-search-choice:not(.select2-locked)").last()),A(a),void 0;if(this.selectChoice(null),this.opened())switch(a.which){case c.UP:case c.DOWN:return this.moveHighlight(a.which===c.UP?-1:1),A(a),void 0;case c.ENTER:return this.selectHighlighted(),A(a),void 0;case c.TAB:return this.selectHighlighted({noFocus:!0}),this.close(),void 0;case c.ESC:return this.cancel(a),A(a),void 0}if(a.which!==c.TAB&&!c.isControl(a)&&!c.isFunctionKey(a)&&a.which!==c.BACKSPACE&&a.which!==c.ESC){if(a.which===c.ENTER){if(this.opts.openOnEnter===!1)return;if(a.altKey||a.ctrlKey||a.shiftKey||a.metaKey)return}this.open(),(a.which===c.PAGE_UP||a.which===c.PAGE_DOWN)&&A(a),a.which===c.ENTER&&A(a)}}})),this.search.on("keyup",this.bind(function(){this.keydowns=0,this.resizeSearch()})),this.search.on("blur",this.bind(function(b){this.container.removeClass("select2-container-active"),this.search.removeClass("select2-focused"),this.selectChoice(null),this.opened()||this.clearSearch(),b.stopImmediatePropagation(),this.opts.element.trigger(a.Event("select2-blur"))})),this.container.on("click",b,this.bind(function(b){this.isInterfaceEnabled()&&(a(b.target).closest(".select2-search-choice").length>0||(this.selectChoice(null),this.clearPlaceholder(),this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.open(),this.focusSearch(),b.preventDefault()))})),this.container.on("focus",b,this.bind(function(){this.isInterfaceEnabled()&&(this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"),this.clearPlaceholder())})),this.initContainerWidth(),this.opts.element.addClass("select2-offscreen"),this.clearSearch()},enableInterface:function(){this.parent.enableInterface.apply(this,arguments)&&this.search.prop("disabled",!this.isInterfaceEnabled())},initSelection:function(){if(""===this.opts.element.val()&&""===this.opts.element.text()&&(this.updateSelection([]),this.close(),this.clearSearch()),this.select||""!==this.opts.element.val()){var c=this;this.opts.initSelection.call(null,this.opts.element,function(a){a!==b&&null!==a&&(c.updateSelection(a),c.close(),c.clearSearch())})}},clearSearch:function(){var a=this.getPlaceholder(),c=this.getMaxSearchWidth();a!==b&&0===this.getVal().length&&this.search.hasClass("select2-focused")===!1?(this.search.val(a).addClass("select2-default"),this.search.width(c>0?c:this.container.css("width"))):this.search.val("").width(10)},clearPlaceholder:function(){this.search.hasClass("select2-default")&&this.search.val("").removeClass("select2-default")},opening:function(){this.clearPlaceholder(),this.resizeSearch(),this.parent.opening.apply(this,arguments),this.focusSearch(),this.updateResults(!0),this.search.focus(),this.opts.element.trigger(a.Event("select2-open"))},close:function(){this.opened()&&this.parent.close.apply(this,arguments)},focus:function(){this.close(),this.search.focus()},isFocused:function(){return this.search.hasClass("select2-focused")},updateSelection:function(b){var c=[],d=[],e=this;a(b).each(function(){o(e.id(this),c)<0&&(c.push(e.id(this)),d.push(this))}),b=d,this.selection.find(".select2-search-choice").remove(),a(b).each(function(){e.addSelectedChoice(this)}),e.postprocessResults()},tokenize:function(){var a=this.search.val();a=this.opts.tokenizer.call(this,a,this.data(),this.bind(this.onSelect),this.opts),null!=a&&a!=b&&(this.search.val(a),a.length>0&&this.open())},onSelect:function(a,b){this.triggerSelect(a)&&(this.addSelectedChoice(a),this.opts.element.trigger({type:"selected",val:this.id(a),choice:a}),(this.select||!this.opts.closeOnSelect)&&this.postprocessResults(a,!1,this.opts.closeOnSelect===!0),this.opts.closeOnSelect?(this.close(),this.search.width(10)):this.countSelectableResults()>0?(this.search.width(10),this.resizeSearch(),this.getMaximumSelectionSize()>0&&this.val().length>=this.getMaximumSelectionSize()&&this.updateResults(!0),this.positionDropdown()):(this.close(),this.search.width(10)),this.triggerChange({added:a}),b&&b.noFocus||this.focusSearch())},cancel:function(){this.close(),this.focusSearch()},addSelectedChoice:function(c){var j,k,d=!c.locked,e=a("<li class='select2-search-choice'> <div></div> <a href='#' onclick='return false;' class='select2-search-choice-close' tabindex='-1'></a></li>"),f=a("<li class='select2-search-choice select2-locked'><div></div></li>"),g=d?e:f,h=this.id(c),i=this.getVal();j=this.opts.formatSelection(c,g.find("div"),this.opts.escapeMarkup),j!=b&&g.find("div").replaceWith("<div>"+j+"</div>"),k=this.opts.formatSelectionCssClass(c,g.find("div")),k!=b&&g.addClass(k),d&&g.find(".select2-search-choice-close").on("mousedown",A).on("click dblclick",this.bind(function(b){this.isInterfaceEnabled()&&(a(b.target).closest(".select2-search-choice").fadeOut("fast",this.bind(function(){this.unselect(a(b.target)),this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"),this.close(),this.focusSearch()})).dequeue(),A(b))})).on("focus",this.bind(function(){this.isInterfaceEnabled()&&(this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"))})),g.data("select2-data",c),g.insertBefore(this.searchContainer),i.push(h),this.setVal(i)},unselect:function(b){var d,e,c=this.getVal();if(b=b.closest(".select2-search-choice"),0===b.length)throw"Invalid argument: "+b+". Must be .select2-search-choice";if(d=b.data("select2-data")){for(;(e=o(this.id(d),c))>=0;)c.splice(e,1),this.setVal(c),this.select&&this.postprocessResults();var f=a.Event("select2-removing");f.val=this.id(d),f.choice=d,this.opts.element.trigger(f),f.isDefaultPrevented()||(b.remove(),this.opts.element.trigger({type:"select2-removed",val:this.id(d),choice:d}),this.triggerChange({removed:d}))}},postprocessResults:function(a,b,c){var d=this.getVal(),e=this.results.find(".select2-result"),f=this.results.find(".select2-result-with-children"),g=this;e.each2(function(a,b){var c=g.id(b.data("select2-data"));o(c,d)>=0&&(b.addClass("select2-selected"),b.find(".select2-result-selectable").addClass("select2-selected"))}),f.each2(function(a,b){b.is(".select2-result-selectable")||0!==b.find(".select2-result-selectable:not(.select2-selected)").length||b.addClass("select2-selected")}),-1==this.highlight()&&c!==!1&&g.highlight(0),!this.opts.createSearchChoice&&!e.filter(".select2-result:not(.select2-selected)").length>0&&(!a||a&&!a.more&&0===this.results.find(".select2-no-results").length)&&J(g.opts.formatNoMatches,"formatNoMatches")&&this.results.append("<li class='select2-no-results'>"+g.opts.formatNoMatches(g.search.val())+"</li>")},getMaxSearchWidth:function(){return this.selection.width()-s(this.search)},resizeSearch:function(){var a,b,c,d,e,f=s(this.search);a=C(this.search)+10,b=this.search.offset().left,c=this.selection.width(),d=this.selection.offset().left,e=c-(b-d)-f,a>e&&(e=c-f),40>e&&(e=c-f),0>=e&&(e=a),this.search.width(Math.floor(e))},getVal:function(){var a;return this.select?(a=this.select.val(),null===a?[]:a):(a=this.opts.element.val(),r(a,this.opts.separator))},setVal:function(b){var c;this.select?this.select.val(b):(c=[],a(b).each(function(){o(this,c)<0&&c.push(this)}),this.opts.element.val(0===c.length?"":c.join(this.opts.separator)))},buildChangeDetails:function(a,b){for(var b=b.slice(0),a=a.slice(0),c=0;c<b.length;c++)for(var d=0;d<a.length;d++)q(this.opts.id(b[c]),this.opts.id(a[d]))&&(b.splice(c,1),c>0&&c--,a.splice(d,1),d--);return{added:b,removed:a}},val:function(c,d){var e,f=this;if(0===arguments.length)return this.getVal();if(e=this.data(),e.length||(e=[]),!c&&0!==c)return this.opts.element.val(""),this.updateSelection([]),this.clearSearch(),d&&this.triggerChange({added:this.data(),removed:e}),void 0;if(this.setVal(c),this.select)this.opts.initSelection(this.select,this.bind(this.updateSelection)),d&&this.triggerChange(this.buildChangeDetails(e,this.data()));else{if(this.opts.initSelection===b)throw new Error("val() cannot be called if initSelection() is not defined");this.opts.initSelection(this.opts.element,function(b){var c=a.map(b,f.id);f.setVal(c),f.updateSelection(b),f.clearSearch(),d&&f.triggerChange(f.buildChangeDetails(e,f.data()))})}this.clearSearch()},onSortStart:function(){if(this.select)throw new Error("Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead.");this.search.width(0),this.searchContainer.hide()},onSortEnd:function(){var b=[],c=this;this.searchContainer.show(),this.searchContainer.appendTo(this.searchContainer.parent()),this.resizeSearch(),this.selection.find(".select2-search-choice").each(function(){b.push(c.opts.id(a(this).data("select2-data")))}),this.setVal(b),this.triggerChange()},data:function(b,c){var e,f,d=this;return 0===arguments.length?this.selection.find(".select2-search-choice").map(function(){return a(this).data("select2-data")}).get():(f=this.data(),b||(b=[]),e=a.map(b,function(a){return d.opts.id(a)}),this.setVal(e),this.updateSelection(b),this.clearSearch(),c&&this.triggerChange(this.buildChangeDetails(f,this.data())),void 0)}}),a.fn.select2=function(){var d,g,h,i,j,c=Array.prototype.slice.call(arguments,0),k=["val","destroy","opened","open","close","focus","isFocused","container","dropdown","onSortStart","onSortEnd","enable","disable","readonly","positionDropdown","data","search"],l=["opened","isFocused","container","dropdown"],m=["val","data"],n={search:"externalSearch"};return this.each(function(){if(0===c.length||"object"==typeof c[0])d=0===c.length?{}:a.extend({},c[0]),d.element=a(this),"select"===d.element.get(0).tagName.toLowerCase()?j=d.element.prop("multiple"):(j=d.multiple||!1,"tags"in d&&(d.multiple=j=!0)),g=j?new f:new e,g.init(d);else{if("string"!=typeof c[0])throw"Invalid arguments to select2 plugin: "+c;if(o(c[0],k)<0)throw"Unknown method: "+c[0];if(i=b,g=a(this).data("select2"),g===b)return;if(h=c[0],"container"===h?i=g.container:"dropdown"===h?i=g.dropdown:(n[h]&&(h=n[h]),i=g[h].apply(g,c.slice(1))),o(c[0],l)>=0||o(c[0],m)&&1==c.length)return!1}}),i===b?this:i},a.fn.select2.defaults={width:"copy",loadMorePadding:0,closeOnSelect:!0,openOnEnter:!0,containerCss:{},dropdownCss:{},containerCssClass:"",dropdownCssClass:"",formatResult:function(a,b,c,d){var e=[];return E(a.text,c.term,e,d),e.join("")},formatSelection:function(a,c,d){return a?d(a.text):b},sortResults:function(a){return a},formatResultCssClass:function(){return b},formatSelectionCssClass:function(){return b},formatNoMatches:function(){return"No matches found"},formatInputTooShort:function(a,b){var c=b-a.length;return"Please enter "+c+" more character"+(1==c?"":"s")},formatInputTooLong:function(a,b){var c=a.length-b;return"Please delete "+c+" character"+(1==c?"":"s")},formatSelectionTooBig:function(a){return"You can only select "+a+" item"+(1==a?"":"s")},formatLoadMore:function(){return"Loading more results..."},formatSearching:function(){return"Searching..."},minimumResultsForSearch:0,minimumInputLength:0,maximumInputLength:null,maximumSelectionSize:0,id:function(a){return a.id},matcher:function(a,b){return n(""+b).toUpperCase().indexOf(n(""+a).toUpperCase())>=0},separator:",",tokenSeparators:[],tokenizer:M,escapeMarkup:F,blurOnChange:!1,selectOnBlur:!1,adaptContainerCssClass:function(a){return a},adaptDropdownCssClass:function(){return null},nextSearchTerm:function(){return b}},a.fn.select2.ajaxDefaults={transport:a.ajax,params:{type:"GET",cache:!1,dataType:"json"}},window.Select2={query:{ajax:G,local:H,tags:I},util:{debounce:v,markMatch:E,escapeMarkup:F,stripDiacritics:n},"class":{"abstract":d,single:e,multi:f}}}}(jQuery);
\ No newline at end of file
Added: trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.png
===================================================================
(Binary files differ)
Property changes on: trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2_locale_fr.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2_locale_fr.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2_locale_fr.js 2014-03-19 15:11:43 UTC (rev 1728)
@@ -0,0 +1,15 @@
+/**
+ * Select2 French translation
+ */
+(function ($) {
+ "use strict";
+
+ $.extend($.fn.select2.defaults, {
+ formatNoMatches: function () { return "Aucun résultat trouvé"; },
+ formatInputTooShort: function (input, min) { var n = min - input.length; return "Merci de saisir " + n + " caractère" + (n == 1? "" : "s") + " de plus"; },
+ formatInputTooLong: function (input, max) { var n = input.length - max; return "Merci de supprimer " + n + " caractère" + (n == 1? "" : "s"); },
+ formatSelectionTooBig: function (limit) { return "Vous pouvez seulement sélectionner " + limit + " élément" + (limit == 1 ? "" : "s"); },
+ formatLoadMore: function (pageNumber) { return "Chargement de résultats supplémentaires..."; },
+ formatSearching: function () { return "Recherche en cours..."; }
+ });
+})(jQuery);
Added: trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2x2.png
===================================================================
(Binary files differ)
Property changes on: trunk/wao-web/src/main/webapp/js/select2-3.4.5/select2x2.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
1
0
r1727 - in trunk: wao-services/src/main/java/fr/ifremer/wao/services/service wao-services/src/main/java/fr/ifremer/wao/services/service/administration wao-web/src/main/java/fr/ifremer/wao/web/action/administration wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer wao-web/src/main/resources/i18n wao-web/src/main/webapp/WEB-INF/content/administration
by bleny@users.forge.codelutin.com 19 Mar '14
by bleny@users.forge.codelutin.com 19 Mar '14
19 Mar '14
Author: bleny
Date: 2014-03-19 12:26:36 +0100 (Wed, 19 Mar 2014)
New Revision: 1727
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1727
Log:
check uniquenesses on Company#name WaoUser#login SampleRow#code
Added:
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/SampleRowCodeMustBeUniqueException.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/CompanyNameMustBeUniqueException.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/LoginMustBeUniqueException.java
Modified:
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/CompaniesService.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/EditCompanyAction.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/EditWaoUserAction.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/WaoUsersAction.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditSampleRowAction.java
trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties
trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties
trunk/wao-web/src/main/webapp/WEB-INF/content/administration/companies.jsp
trunk/wao-web/src/main/webapp/WEB-INF/content/administration/edit-company.jsp
trunk/wao-web/src/main/webapp/WEB-INF/content/administration/edit-wao-user.jsp
trunk/wao-web/src/main/webapp/WEB-INF/content/administration/wao-users.jsp
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java 2014-03-18 17:28:59 UTC (rev 1726)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java 2014-03-19 11:26:36 UTC (rev 1727)
@@ -3,6 +3,7 @@
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import fr.ifremer.wao.SampleRowsFilter;
@@ -185,7 +186,7 @@
}
public void preValidate(AuthenticatedWaoUser authenticatedWaoUser, UpdateSampleRowCommand updateSampleRowCommand)
- throws UnknownBoatImmatriculationsException, UnknownFishingGearDcfCodeException, UnknownTargetSpeciesDcfCodeException {
+ throws UnknownBoatImmatriculationsException, UnknownFishingGearDcfCodeException, UnknownTargetSpeciesDcfCodeException, SampleRowCodeMustBeUniqueException {
String elligibleBoatImmatriculations = updateSampleRowCommand.getElligibleBoatImmatriculations();
@@ -269,6 +270,14 @@
sampleMonthDao.delete(sampleMonth);
}
+ SampleRowTopiaDao dao = getSampleRowDao();
+ List<SampleRow> existingSampleRowsForTheSameCode = dao.forCodeEquals(sampleRow.getCode()).findAll();
+ if (existingSampleRowsForTheSameCode.size() > 1) {
+ throw new SampleRowCodeMustBeUniqueException();
+ } else if (existingSampleRowsForTheSameCode.size() == 1 && ! Iterables.getOnlyElement(existingSampleRowsForTheSameCode).equals(sampleRow)) {
+ throw new SampleRowCodeMustBeUniqueException();
+ }
+
}
public void save(UpdateSampleRowCommand updateSampleRowCommand) {
Added: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/SampleRowCodeMustBeUniqueException.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/SampleRowCodeMustBeUniqueException.java (rev 0)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/SampleRowCodeMustBeUniqueException.java 2014-03-19 11:26:36 UTC (rev 1727)
@@ -0,0 +1,7 @@
+package fr.ifremer.wao.services.service;
+
+import fr.ifremer.wao.WaoException;
+
+public class SampleRowCodeMustBeUniqueException extends WaoException {
+
+}
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/CompaniesService.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/CompaniesService.java 2014-03-18 17:28:59 UTC (rev 1726)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/CompaniesService.java 2014-03-19 11:26:36 UTC (rev 1727)
@@ -1,5 +1,6 @@
package fr.ifremer.wao.services.service.administration;
+import com.google.common.collect.Iterables;
import fr.ifremer.wao.entity.Company;
import fr.ifremer.wao.entity.CompanyImpl;
import fr.ifremer.wao.entity.CompanyTopiaDao;
@@ -74,4 +75,15 @@
}
+ public void preValidate(Company company) throws CompanyNameMustBeUniqueException {
+
+ CompanyTopiaDao dao = getCompanyDao();
+ List<Company> existingCompaniesForTheSameName = dao.forNameEquals(company.getName()).findAll();
+ if (existingCompaniesForTheSameName.size() > 1) {
+ throw new CompanyNameMustBeUniqueException();
+ } else if (existingCompaniesForTheSameName.size() == 1 && ! Iterables.getOnlyElement(existingCompaniesForTheSameName).equals(company)) {
+ throw new CompanyNameMustBeUniqueException();
+ }
+
+ }
}
Added: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/CompanyNameMustBeUniqueException.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/CompanyNameMustBeUniqueException.java (rev 0)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/CompanyNameMustBeUniqueException.java 2014-03-19 11:26:36 UTC (rev 1727)
@@ -0,0 +1,7 @@
+package fr.ifremer.wao.services.service.administration;
+
+import fr.ifremer.wao.WaoException;
+
+public class CompanyNameMustBeUniqueException extends WaoException {
+
+}
Added: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/LoginMustBeUniqueException.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/LoginMustBeUniqueException.java (rev 0)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/LoginMustBeUniqueException.java 2014-03-19 11:26:36 UTC (rev 1727)
@@ -0,0 +1,7 @@
+package fr.ifremer.wao.services.service.administration;
+
+import fr.ifremer.wao.WaoException;
+
+public class LoginMustBeUniqueException extends WaoException {
+
+}
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java 2014-03-18 17:28:59 UTC (rev 1726)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java 2014-03-19 11:26:36 UTC (rev 1727)
@@ -2,6 +2,7 @@
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import fr.ifremer.wao.entity.Boat;
import fr.ifremer.wao.entity.Boats;
@@ -105,7 +106,7 @@
}
- public void preValidate(UpdateWaoUserCommand updateWaoUserCommand) throws UnknownBoatImmatriculationsException {
+ public void preValidate(UpdateWaoUserCommand updateWaoUserCommand) throws UnknownBoatImmatriculationsException, LoginMustBeUniqueException {
ObsProgram obsProgram = updateWaoUserCommand.getObsProgram();
WaoUser waoUser = updateWaoUserCommand.getWaoUser();
@@ -116,6 +117,14 @@
waoUser.setCanReadBoats(boatsFromImmatriculations);
}
+ WaoUserTopiaDao dao = getWaoUserDao();
+ List<WaoUser> existingWaoUsersForTheSameLogin = dao.forLoginEquals(waoUser.getLogin()).findAll();
+ if (existingWaoUsersForTheSameLogin.size() > 1) {
+ throw new LoginMustBeUniqueException();
+ } else if (existingWaoUsersForTheSameLogin.size() == 1 && ! Iterables.getOnlyElement(existingWaoUsersForTheSameLogin).equals(waoUser)) {
+ throw new LoginMustBeUniqueException();
+ }
+
}
public void save(UpdateWaoUserCommand updateWaoUserCommand) {
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/EditCompanyAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/EditCompanyAction.java 2014-03-18 17:28:59 UTC (rev 1726)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/EditCompanyAction.java 2014-03-19 11:26:36 UTC (rev 1727)
@@ -3,6 +3,7 @@
import com.opensymphony.xwork2.Preparable;
import fr.ifremer.wao.entity.Company;
import fr.ifremer.wao.services.service.administration.CompaniesService;
+import fr.ifremer.wao.services.service.administration.CompanyNameMustBeUniqueException;
import fr.ifremer.wao.web.WaoJspActionSupport;
import org.apache.commons.lang3.StringUtils;
import org.apache.struts2.convention.annotation.Result;
@@ -41,6 +42,17 @@
}
@Override
+ public void validate() {
+
+ try {
+ service.preValidate(company);
+ } catch (CompanyNameMustBeUniqueException e) {
+ addFieldError("company.name", t("wao.ui.form.Company.error.nameMustBeUnique"));
+ }
+
+ }
+
+ @Override
public String execute() {
service.save(company);
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/EditWaoUserAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/EditWaoUserAction.java 2014-03-18 17:28:59 UTC (rev 1726)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/EditWaoUserAction.java 2014-03-19 11:26:36 UTC (rev 1727)
@@ -5,6 +5,7 @@
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.opensymphony.xwork2.Preparable;
+import fr.ifremer.wao.services.service.administration.LoginMustBeUniqueException;
import fr.ifremer.wao.services.service.administration.UnknownBoatImmatriculationsException;
import fr.ifremer.wao.services.service.administration.UpdateWaoUserCommand;
import fr.ifremer.wao.services.service.administration.UpdateWaoUserCommandPasswordStrategy;
@@ -58,6 +59,8 @@
service.preValidate(updateWaoUserCommand);
} catch (UnknownBoatImmatriculationsException unknownBoatImmatriculations) {
addFieldError("updateWaoUserCommand.canReadBoats", t("wao.ui.unknownBoatImmatriculations", Joiner.on(", ").join(unknownBoatImmatriculations.getImmatriculations())));
+ } catch (LoginMustBeUniqueException e) {
+ addFieldError("updateWaoUserCommand.waoUser.login", t("wao.ui.form.updateWaoUserCommand.error.loginMustBeUnique"));
}
if (updateWaoUserCommand.getPasswordStrategy().isDefinePassword()) {
@@ -77,8 +80,6 @@
addFieldError("updateWaoUserCommand.companyId", t("wao.ui.form.updateWaoUserCommand.requiredCompany"));
}
- // FIXME brendan 06/03/14 check login uniqueness
-
}
@Override
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/WaoUsersAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/WaoUsersAction.java 2014-03-18 17:28:59 UTC (rev 1726)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/WaoUsersAction.java 2014-03-19 11:26:36 UTC (rev 1727)
@@ -26,6 +26,10 @@
this.optionalCompanyId = Optional.fromNullable(Strings.emptyToNull(companyId));
}
+ public String getCompanyId() {
+ return optionalCompanyId.orNull();
+ }
+
@Override
public void prepare() {
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditSampleRowAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditSampleRowAction.java 2014-03-18 17:28:59 UTC (rev 1726)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditSampleRowAction.java 2014-03-19 11:26:36 UTC (rev 1727)
@@ -7,6 +7,7 @@
import fr.ifremer.wao.WaoUtils;
import fr.ifremer.wao.entity.SampleRow;
import fr.ifremer.wao.services.service.ObsMerSamplingPlanService;
+import fr.ifremer.wao.services.service.SampleRowCodeMustBeUniqueException;
import fr.ifremer.wao.services.service.UpdateSampleRowCommand;
import fr.ifremer.wao.services.service.administration.UnknownBoatImmatriculationsException;
import fr.ifremer.wao.services.service.administration.UnknownFishingGearDcfCodeException;
@@ -72,6 +73,8 @@
addFieldError("updateSampleRowCommand.dcf5Codes", t("wao.ui.form.SampleRow.error.unknownFishingGearDcfCode", e.getCode()));
} catch (UnknownTargetSpeciesDcfCodeException e) {
addFieldError("updateSampleRowCommand.dcf5Codes", t("wao.ui.form.SampleRow.error.unknownTargetSpeciesDcfCode", e.getCode()));
+ } catch (SampleRowCodeMustBeUniqueException e) {
+ addFieldError("updateSampleRowCommand.code", t("wao.ui.form.SampleRow.error.codeMustBeUnique"));
}
if (updateSampleRowCommand.isCreation()) {
Modified: trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties
===================================================================
--- trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties 2014-03-18 17:28:59 UTC (rev 1726)
+++ trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties 2014-03-19 11:26:36 UTC (rev 1727)
@@ -203,7 +203,11 @@
wao.ui.filters.filters=Search filters
wao.ui.filters.refresh=Refresh filters
wao.ui.forgotPassword=Forgot password ?
+wao.ui.form.Company.error.nameMustBeUnique=Company name must be unique
+wao.ui.form.Company.title.creation=Create a new company
+wao.ui.form.Company.title.edition=Edit company %s
wao.ui.form.SampleRow.boatsDescription=This field contains registration numbers of the ships that areeligible for this ligne. You can use any separator to separate numbers (space, dot, line-return, comma)
+wao.ui.form.SampleRow.error.codeMustBeUnique=Sample row code must be unique
wao.ui.form.SampleRow.error.missingDcf5Codes=You must provide at least one DCF5 code
wao.ui.form.SampleRow.error.missingFishingZones=You must at least provide a fishing zone
wao.ui.form.SampleRow.error.missingLogComment=You must add a comment to explain why you modified the line
@@ -253,6 +257,7 @@
wao.ui.form.updateWaoUserCommand.canReadBoats=Only able to read boats
wao.ui.form.updateWaoUserCommand.coordinator=Coordinator
wao.ui.form.updateWaoUserCommand.coordinatorReadOnly=Coordinator (read-only)
+wao.ui.form.updateWaoUserCommand.error.loginMustBeUnique=Login must be unique on the system
wao.ui.form.updateWaoUserCommand.guest=Guest
wao.ui.form.updateWaoUserCommand.guestReadOnly=Guest (read-only)
wao.ui.form.updateWaoUserCommand.loginMustBeEmail=The login must be a valid email address
@@ -265,6 +270,8 @@
wao.ui.form.updateWaoUserCommand.professionalReadOnly=Professional (read-only)
wao.ui.form.updateWaoUserCommand.requiredCompany=The company is required
wao.ui.form.updateWaoUserCommand.success=Modifications about %s saved
+wao.ui.form.updateWaoUserCommand.title.creation=Create a new user
+wao.ui.form.updateWaoUserCommand.title.edition=Edit user %s
wao.ui.import.boatDistrictLabel=of the coordinates of boat districts
wao.ui.import.boatGroupsLabel=of the boat groups
wao.ui.import.boatsLabel=of boats
Modified: trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties
===================================================================
--- trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties 2014-03-18 17:28:59 UTC (rev 1726)
+++ trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties 2014-03-19 11:26:36 UTC (rev 1727)
@@ -203,7 +203,11 @@
wao.ui.filters.filters=Filtres de recherche
wao.ui.filters.refresh=Rafraîchir les champs du filtre
wao.ui.forgotPassword=Mot de passe oublié ?
+wao.ui.form.Company.error.nameMustBeUnique=Le nom de la société doit être unique
+wao.ui.form.Company.title.creation=Création d'une société
+wao.ui.form.Company.title.edition=Modification de la société %s
wao.ui.form.SampleRow.boatsDescription=Ce champs correspond aux immatriculations des navires qui sont éligibles pour cette ligne. Vous pouvez utiliser n'importe quel séparateur pour séparer les immatriculations (virgule, point, espace ou saut de ligne)
+wao.ui.form.SampleRow.error.codeMustBeUnique=
wao.ui.form.SampleRow.error.missingDcf5Codes=Il faut préciser au moins un code DCF5
wao.ui.form.SampleRow.error.missingFishingZones=Il faut préciser au moins une zone de pêche
wao.ui.form.SampleRow.error.missingLogComment=Vous devez préciser la raison de vos modifications
@@ -253,6 +257,7 @@
wao.ui.form.updateWaoUserCommand.canReadBoats=Limiter à la consultation des navires
wao.ui.form.updateWaoUserCommand.coordinator=Coordinateur
wao.ui.form.updateWaoUserCommand.coordinatorReadOnly=Coordinateur (lecture seule)
+wao.ui.form.updateWaoUserCommand.error.loginMustBeUnique=L'identifiant doit être unique au sein du système
wao.ui.form.updateWaoUserCommand.guest=Invité
wao.ui.form.updateWaoUserCommand.guestReadOnly=Invité (lecture seule)
wao.ui.form.updateWaoUserCommand.loginMustBeEmail=Il faut utiliser un email valide comme identifiant
@@ -265,6 +270,8 @@
wao.ui.form.updateWaoUserCommand.professionalReadOnly=Professionel (lecture seule)
wao.ui.form.updateWaoUserCommand.requiredCompany=Il faut associer l'utilisateur à une société
wao.ui.form.updateWaoUserCommand.success=Les modifications concernant l'utilisateur %s ont bien été enregistrées
+wao.ui.form.updateWaoUserCommand.title.creation=Création d'un utilisateur
+wao.ui.form.updateWaoUserCommand.title.edition=Modification de l'utilisateur %s
wao.ui.import.boatDistrictLabel=des coordonnées des quartiers des navires
wao.ui.import.boatGroupsLabel=des strates des navires
wao.ui.import.boatsLabel=des navires
Modified: trunk/wao-web/src/main/webapp/WEB-INF/content/administration/companies.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/administration/companies.jsp 2014-03-18 17:28:59 UTC (rev 1726)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/administration/companies.jsp 2014-03-19 11:26:36 UTC (rev 1727)
@@ -4,7 +4,7 @@
<html>
<head>
-
+ <title><s:text name="wao.ui.page.companies.title" /></title>
</head>
<h1><s:text name="wao.ui.page.companies.title" /></h1>
@@ -55,6 +55,13 @@
<s:a href="%{waoUsersUrl}">
<i class="icon-user"></i> <s:text name="wao.ui.action.viewCompanyWaoUsers" />
</s:a>
+
+ <s:url action="edit-wao-user!input" id="createWaoUserUrl">
+ <s:param name="companyId" value="topiaId" />
+ </s:url>
+ <s:a href="%{createWaoUserUrl}">
+ <i class="icon-plus"></i> <s:text name="wao.ui.action.createWaoUser" />
+ </s:a>
</td>
</tr>
</s:iterator>
Modified: trunk/wao-web/src/main/webapp/WEB-INF/content/administration/edit-company.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/administration/edit-company.jsp 2014-03-18 17:28:59 UTC (rev 1726)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/administration/edit-company.jsp 2014-03-19 11:26:36 UTC (rev 1727)
@@ -3,8 +3,25 @@
<html>
<head>
+ <title>
+ <s:if test="companyId == null">
+ <s:text name="wao.ui.form.Company.title.creation" />
+ </s:if>
+ <s:else>
+ <s:property value="%{t('wao.ui.form.Company.title.edition', company.name)}" />
+ </s:else>
+ </title>
</head>
+ <h1>
+ <s:if test="companyId == null">
+ <s:text name="wao.ui.form.Company.title.creation" />
+ </s:if>
+ <s:else>
+ <s:property value="%{t('wao.ui.form.Company.title.edition', company.name)}" />
+ </s:else>
+ </h1>
+
<s:form>
<s:hidden name="companyId" value="%{companyId}" />
Modified: trunk/wao-web/src/main/webapp/WEB-INF/content/administration/edit-wao-user.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/administration/edit-wao-user.jsp 2014-03-18 17:28:59 UTC (rev 1726)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/administration/edit-wao-user.jsp 2014-03-19 11:26:36 UTC (rev 1727)
@@ -39,8 +39,25 @@
});
</script>
+ <title>
+ <s:if test="waoUserId == null">
+ <s:text name="wao.ui.form.updateWaoUserCommand.title.creation" />
+ </s:if>
+ <s:else>
+ <s:property value="%{t('wao.ui.form.updateWaoUserCommand.title.edition', updateWaoUserCommand.waoUser.fullName)}" />
+ </s:else>
+ </title>
</head>
+ <h1>
+ <s:if test="waoUserId == null">
+ <s:text name="wao.ui.form.updateWaoUserCommand.title.creation" />
+ </s:if>
+ <s:else>
+ <s:property value="%{t('wao.ui.form.updateWaoUserCommand.title.edition', updateWaoUserCommand.waoUser.fullName)}" />
+ </s:else>
+ </h1>
+
<s:form>
<s:hidden name="waoUserId" value="%{waoUserId}" />
Modified: trunk/wao-web/src/main/webapp/WEB-INF/content/administration/wao-users.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/administration/wao-users.jsp 2014-03-18 17:28:59 UTC (rev 1726)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/administration/wao-users.jsp 2014-03-19 11:26:36 UTC (rev 1727)
@@ -5,6 +5,8 @@
<head>
+ <title><s:text name="wao.ui.page.waoUsers.title" /></title>
+
</head>
<h1><s:text name="wao.ui.page.waoUsers.title" /></h1>
@@ -59,7 +61,9 @@
</s:iterator>
</table>
- <s:url action="edit-wao-user!input" id="createWaoUserUrl" />
+ <s:url action="edit-wao-user!input" id="createWaoUserUrl">
+ <s:param name="companyId" value="%{companyId}" />
+ </s:url>
<s:a href="%{createWaoUserUrl}">
<i class="icon-plus"></i> <s:text name="wao.ui.action.createWaoUser" />
</s:a>
1
0
r1726 - in trunk: wao-services/src/main/java/fr/ifremer/wao/services wao-web/src/main/java/fr/ifremer/wao/web/action wao-web/src/main/java/fr/ifremer/wao/web/action/administration wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer wao-web/src/main/resources/i18n wao-web/src/main/webapp/WEB-INF/content/administration wao-web/src/main/webapp/WEB-INF/content/obsmer wao-web/src/main/webapp/WEB-INF/decorators
by bleny@users.forge.codelutin.com 18 Mar '14
by bleny@users.forge.codelutin.com 18 Mar '14
18 Mar '14
Author: bleny
Date: 2014-03-18 18:28:59 +0100 (Tue, 18 Mar 2014)
New Revision: 1726
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1726
Log:
refs #4483 add import UI
Added:
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/AbstractImportCsvAction.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/ImportSamplingPlanAction.java
trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/import-sampling-plan-input.jsp
Modified:
trunk/wao-services/src/main/java/fr/ifremer/wao/services/AuthenticatedWaoUser.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/AbstractImportReferentialAction.java
trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties
trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties
trunk/wao-web/src/main/webapp/WEB-INF/content/administration/companies.jsp
trunk/wao-web/src/main/webapp/WEB-INF/content/administration/referential-management.jsp
trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout.jsp
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/AuthenticatedWaoUser.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/AuthenticatedWaoUser.java 2014-03-18 16:39:19 UTC (rev 1725)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/AuthenticatedWaoUser.java 2014-03-18 17:28:59 UTC (rev 1726)
@@ -175,4 +175,7 @@
return userProfile.isCoordinator();
}
+ public boolean isAuthorizedToImportSamplingPlan() {
+ return userProfile.isAdmin();
+ }
}
Added: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/AbstractImportCsvAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/AbstractImportCsvAction.java (rev 0)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/AbstractImportCsvAction.java 2014-03-18 17:28:59 UTC (rev 1726)
@@ -0,0 +1,99 @@
+package fr.ifremer.wao.web.action;
+
+import com.google.common.collect.ImmutableSet;
+import fr.ifremer.wao.WaoTechnicalException;
+import fr.ifremer.wao.services.service.ImportErrorException;
+import fr.ifremer.wao.services.service.administration.ReferentialService;
+import fr.ifremer.wao.web.WaoJspActionSupport;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+public abstract class AbstractImportCsvAction extends WaoJspActionSupport {
+
+ private static final Log log = LogFactory.getLog(AbstractImportCsvAction.class);
+
+ protected static final ImmutableSet<String> CSV_CONTENT_TYPES =
+ ImmutableSet.of("text/csv", "text/comma-separated-values", "application/vnd.ms-excel");
+
+ protected ReferentialService service;
+
+ protected File csvFile;
+
+ protected String csvFileContentType;
+
+ public void setCsvFile(File csvFile) {
+ this.csvFile = csvFile;
+ }
+
+ public void setCsvFileContentType(String csvFileContentType) {
+ this.csvFileContentType = csvFileContentType;
+ }
+
+ public void setService(ReferentialService service) {
+ this.service = service;
+ }
+
+ protected abstract void importCsv(InputStream csvInputStream) throws ImportErrorException;
+
+ protected abstract String getSuccessMessage();
+
+ @Override
+ public String execute() {
+
+ String result = SUCCESS;
+
+ if (CSV_CONTENT_TYPES.contains(csvFileContentType)) {
+
+ InputStream csvInputStream = null;
+
+ try {
+
+ csvInputStream = new FileInputStream(csvFile);
+
+ importCsv(csvInputStream);
+
+ session.addMessage(getSuccessMessage());
+
+ } catch (FileNotFoundException e) {
+
+ if (log.isErrorEnabled()) {
+ log.error("should never occur", e);
+ }
+
+ throw new WaoTechnicalException(e);
+
+ } catch (ImportErrorException e) {
+
+ session.addErrorMessages(e.getMessage());
+
+ result = ERROR;
+
+ } finally {
+
+ IOUtils.closeQuietly(csvInputStream);
+
+ }
+
+ } else {
+
+ if (log.isWarnEnabled()) {
+ log.warn("content type " + csvFileContentType + " is not CSV compatible");
+ }
+
+ session.addErrorMessages(t("wao.ui.import.csvTypeRequired", csvFileContentType));
+
+ result = ERROR;
+
+ }
+
+ return result;
+
+ }
+
+}
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/AbstractImportReferentialAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/AbstractImportReferentialAction.java 2014-03-18 16:39:19 UTC (rev 1725)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/AbstractImportReferentialAction.java 2014-03-18 17:28:59 UTC (rev 1726)
@@ -1,105 +1,14 @@
package fr.ifremer.wao.web.action.administration;
-import com.google.common.collect.ImmutableSet;
-import fr.ifremer.wao.WaoTechnicalException;
-import fr.ifremer.wao.services.service.ImportErrorException;
-import fr.ifremer.wao.services.service.administration.ReferentialService;
-import fr.ifremer.wao.web.WaoJspActionSupport;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import fr.ifremer.wao.web.action.AbstractImportCsvAction;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-
@Results({
@Result(name="error", type="redirectAction", params = { "actionName", "referential-management" }),
@Result(name="success", type="redirectAction", params = { "actionName", "referential-management" })
})
-public abstract class AbstractImportReferentialAction extends WaoJspActionSupport {
+public abstract class AbstractImportReferentialAction extends AbstractImportCsvAction {
- private static final Log log = LogFactory.getLog(AbstractImportReferentialAction.class);
- protected static final ImmutableSet<String> CSV_CONTENT_TYPES =
- ImmutableSet.of("text/csv", "text/comma-separated-values", "application/vnd.ms-excel");
-
- protected ReferentialService service;
-
- protected File csvFile;
-
- protected String csvFileContentType;
-
- public void setCsvFile(File csvFile) {
- this.csvFile = csvFile;
- }
-
- public void setCsvFileContentType(String csvFileContentType) {
- this.csvFileContentType = csvFileContentType;
- }
-
- public void setService(ReferentialService service) {
- this.service = service;
- }
-
- protected abstract void importCsv(InputStream csvInputStream) throws ImportErrorException;
-
- protected abstract String getSuccessMessage();
-
- @Override
- public String execute() {
-
- String result = SUCCESS;
-
- if (CSV_CONTENT_TYPES.contains(csvFileContentType)) {
-
- InputStream csvInputStream = null;
-
- try {
-
- csvInputStream = new FileInputStream(csvFile);
-
- importCsv(csvInputStream);
-
- session.addMessage(getSuccessMessage());
-
- } catch (FileNotFoundException e) {
-
- if (log.isErrorEnabled()) {
- log.error("should never occur", e);
- }
-
- throw new WaoTechnicalException(e);
-
- } catch (ImportErrorException e) {
-
- session.addErrorMessages(e.getMessage());
-
- result = ERROR;
-
- } finally {
-
- IOUtils.closeQuietly(csvInputStream);
-
- }
-
- } else {
-
- if (log.isWarnEnabled()) {
- log.warn("content type " + csvFileContentType + " is not CSV compatible");
- }
-
- session.addErrorMessages(t("wao.ui.import.csvTypeRequired", csvFileContentType));
-
- result = ERROR;
-
- }
-
- return result;
-
- }
-
}
Added: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/ImportSamplingPlanAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/ImportSamplingPlanAction.java (rev 0)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/ImportSamplingPlanAction.java 2014-03-18 17:28:59 UTC (rev 1726)
@@ -0,0 +1,33 @@
+package fr.ifremer.wao.web.action.obsmer;
+
+import fr.ifremer.wao.services.service.ImportErrorException;
+import fr.ifremer.wao.services.service.ObsMerSamplingPlanService;
+import fr.ifremer.wao.web.action.AbstractImportCsvAction;
+import org.apache.struts2.convention.annotation.Result;
+import org.apache.struts2.convention.annotation.Results;
+
+import java.io.InputStream;
+
+@Results({
+ @Result(name="error", type="redirectAction", params = { "actionName", "import-sampling-plan!input" }) ,
+ @Result(name="success", type="redirectAction", params = { "actionName", "sampling-plan" })
+})
+public class ImportSamplingPlanAction extends AbstractImportCsvAction {
+
+ protected ObsMerSamplingPlanService service;
+
+ public void setService(ObsMerSamplingPlanService service) {
+ this.service = service;
+ }
+
+ @Override
+ protected void importCsv(InputStream csvInputStream) throws ImportErrorException {
+ service.importSamplingPlan(getAuthenticatedWaoUser(), csvInputStream);
+ }
+
+ @Override
+ protected String getSuccessMessage() {
+ return t("wao.ui.import.samplingPlan.success");
+ }
+
+}
Modified: trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties
===================================================================
--- trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties 2014-03-18 16:39:19 UTC (rev 1725)
+++ trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties 2014-03-18 17:28:59 UTC (rev 1726)
@@ -276,6 +276,8 @@
wao.ui.import.fullDescription=Import %s (%s format with UTF-8 characters set)
wao.ui.import.longTitle=CSV Import/Export (UTF-8)
wao.ui.import.obsDebCodesLabel=of the ObsDeb profession codes
+wao.ui.import.samplingPlan=Sampling plan import
+wao.ui.import.samplingPlan.success=Sampling plan import successful
wao.ui.import.samplingPlanLabel=of the sampling plan
wao.ui.import.terrestrialDivisionsLabel=of the terrestrial divisions (ObsDeb)
wao.ui.import.terrestrialLocationsLabel=of the terrestrial locations
Modified: trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties
===================================================================
--- trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties 2014-03-18 16:39:19 UTC (rev 1725)
+++ trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties 2014-03-18 17:28:59 UTC (rev 1726)
@@ -276,6 +276,8 @@
wao.ui.import.fullDescription=Import %s (format %s avec encodage UTF-8)
wao.ui.import.longTitle=Import/Export CSV (UTF-8)
wao.ui.import.obsDebCodesLabel=des codes métiers ObsDeb
+wao.ui.import.samplingPlan=Import du plan d'échantillonnage
+wao.ui.import.samplingPlan.success=Import du plan d'échantillonnage réalisé avec succès
wao.ui.import.samplingPlanLabel=du plan d'échantillonnage
wao.ui.import.terrestrialDivisionsLabel=du référentiel de stratification spatiale (ObsDeb)
wao.ui.import.terrestrialLocationsLabel=des lieux terrestres
Modified: trunk/wao-web/src/main/webapp/WEB-INF/content/administration/companies.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/administration/companies.jsp 2014-03-18 16:39:19 UTC (rev 1725)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/administration/companies.jsp 2014-03-18 17:28:59 UTC (rev 1726)
@@ -53,7 +53,7 @@
<s:param name="companyId" value="topiaId" />
</s:url>
<s:a href="%{waoUsersUrl}">
- <i class="icon-list"></i> <s:text name="wao.ui.action.viewCompanyWaoUsers" />
+ <i class="icon-user"></i> <s:text name="wao.ui.action.viewCompanyWaoUsers" />
</s:a>
</td>
</tr>
Modified: trunk/wao-web/src/main/webapp/WEB-INF/content/administration/referential-management.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/administration/referential-management.jsp 2014-03-18 16:39:19 UTC (rev 1725)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/administration/referential-management.jsp 2014-03-18 17:28:59 UTC (rev 1726)
@@ -4,9 +4,11 @@
<html>
<head>
-
+ <title><s:text name="wao.ui.page.ReferentialManagement.title" /></title>
</head>
+ <h1><s:text name="wao.ui.page.ReferentialManagement.title" /></h1>
+
<div class="alert alert-info">
<s:text name="wao.ui.import.disclaimer" />
</div>
Added: trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/import-sampling-plan-input.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/import-sampling-plan-input.jsp (rev 0)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/import-sampling-plan-input.jsp 2014-03-18 17:28:59 UTC (rev 1726)
@@ -0,0 +1,32 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
+<%@taglib uri="/struts-tags" prefix="s" %>
+
+<html>
+
+ <head>
+ <title><s:text name="wao.ui.import.samplingPlan" /></title>
+ </head>
+
+ <h1><s:text name="wao.ui.import.samplingPlan" /></h1>
+
+ <div class="alert alert-info">
+ <s:text name="wao.ui.import.disclaimer" />
+ </div>
+
+ <s:form action="import-sampling-plan" method="POST" enctype="multipart/form-data">
+
+ <fieldset>
+ <legend>
+ <s:text name="wao.ui.import.samplingPlan" />
+ </legend>
+
+ <s:file name="csvFile" />
+
+ <s:submit type="button">
+ <i class="icon-upload"></i> Importer
+ </s:submit>
+ </fieldset>
+
+ </s:form>
+
+</html>
Modified: trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout.jsp 2014-03-18 16:39:19 UTC (rev 1725)
+++ trunk/wao-web/src/main/webapp/WEB-INF/decorators/layout.jsp 2014-03-18 17:28:59 UTC (rev 1726)
@@ -36,7 +36,7 @@
<li>
<s:url namespace="/%{obsProgram.name().toLowerCase()}" action="sampling-plan" id="samplingPlanUrl" />
<s:a href="%{samplingPlanUrl}">
- <s:text name="wao.ui.page.SamplingPlan.title" />
+ <i class="icon-tasks"></i> <s:text name="wao.ui.page.SamplingPlan.title" />
</s:a>
</li>
<li>
@@ -65,6 +65,14 @@
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
+ <s:if test="authenticatedWaoUser.authorizedToImportSamplingPlan">
+ <li>
+ <s:url namespace="/%{obsProgram.name().toLowerCase()}" action="import-sampling-plan!input" id="importSamplingPlanUrl" />
+ <s:a href="%{importSamplingPlanUrl}">
+ <i class="icon-upload"></i> <s:text name="wao.ui.import.samplingPlan" />
+ </s:a>
+ </li>
+ </s:if>
<s:if test="authenticatedWaoUser.authorizedToUpdateReferential">
<li>
<s:url namespace="/administration" action="referential-management" id="referentialManagementUrl" />
@@ -77,7 +85,7 @@
<li>
<s:url namespace="/administration" action="companies" id="companiesUrl" />
<s:a href="%{companiesUrl}">
- <i class="icon-user"></i> <s:text name="wao.ui.page.companies.title" />
+ <i class="icon-briefcase"></i> <s:text name="wao.ui.page.companies.title" />
</s:a>
</li>
</s:if>
1
0
18 Mar '14
Author: bleny
Date: 2014-03-18 17:39:19 +0100 (Tue, 18 Mar 2014)
New Revision: 1725
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1725
Log:
refs #4483 add import service, enable validation for boats and DCF codes
Added:
trunk/wao-persistence/src/main/java/fr/ifremer/wao/SampleRowsFilter.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/ObsMerFixtures.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlan.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/UnknownBoatImmatriculationsException.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/UnknownFishingGearDcfCodeException.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/UnknownTargetSpeciesDcfCodeException.java
trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanServiceTest.java
Modified:
trunk/wao-persistence/src/main/java/fr/ifremer/wao/WaoUtils.java
trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/ObservationType.java
trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/SampleRowImpl.java
trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/SampleRowTopiaDao.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/ReferentialService.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/ObsMerObsVenteSamplingPlanImportExportModel.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/operations/DCF5CodesParserFormatter.java
trunk/wao-services/src/test/java/fr/ifremer/wao/services/AbstractWaoServiceTest.java
trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/administration/ReferentialServiceTest.java
trunk/wao-services/src/test/resources/import/echantillonnage.csv
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/EditWaoUserAction.java
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditSampleRowAction.java
trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties
trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties
Added: trunk/wao-persistence/src/main/java/fr/ifremer/wao/SampleRowsFilter.java
===================================================================
--- trunk/wao-persistence/src/main/java/fr/ifremer/wao/SampleRowsFilter.java (rev 0)
+++ trunk/wao-persistence/src/main/java/fr/ifremer/wao/SampleRowsFilter.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -0,0 +1,202 @@
+package fr.ifremer.wao;
+
+import fr.ifremer.wao.entity.ObsProgram;
+import fr.ifremer.wao.entity.ObservationType;
+import fr.ifremer.wao.entity.SamplingStrategy;
+
+import java.util.Date;
+import java.util.Set;
+
+public class SampleRowsFilter {
+
+ protected ObsProgram obsProgram;
+
+ protected ObservationType expectedObservationType;
+
+ protected Date periodFrom;
+
+ protected Date periodTo;
+
+ protected Set<String> fishingZoneFacadeNames;
+
+ protected Set<String> fishingZoneSectorNames;
+
+ protected Set<String> sampleRowCodes;
+
+ protected Set<String> programNames;
+
+ protected Set<String> fishingGearDcfIds;
+
+ protected Set<String> targetSpeciesDcfIds;
+
+ protected Set<SamplingStrategy> samplingStrategies;
+
+ protected Set<String> terrestrialDistrictIds;
+
+ protected Set<String> observationUnitIds;
+
+ protected Set<String> observerIds;
+
+ protected Set<String> subPopulationCodes;
+
+ protected Set<String> skillZoneCodes;
+
+ protected Set<String> regionIfremerIds;
+
+ protected Set<String> companyIds;
+
+ protected Set<String> orderByArguments;
+
+ public ObsProgram getObsProgram() {
+ return obsProgram;
+ }
+
+ public void setObsProgram(ObsProgram obsProgram) {
+ this.obsProgram = obsProgram;
+ }
+
+ public ObservationType getExpectedObservationType() {
+ return expectedObservationType;
+ }
+
+ public void setExpectedObservationType(ObservationType expectedObservationType) {
+ this.expectedObservationType = expectedObservationType;
+ }
+
+ public Date getPeriodFrom() {
+ return periodFrom;
+ }
+
+ public void setPeriodFrom(Date periodFrom) {
+ this.periodFrom = periodFrom;
+ }
+
+ public Date getPeriodTo() {
+ return periodTo;
+ }
+
+ public void setPeriodTo(Date periodTo) {
+ this.periodTo = periodTo;
+ }
+
+ public Set<String> getFishingZoneFacadeNames() {
+ return fishingZoneFacadeNames;
+ }
+
+ public void setFishingZoneFacadeNames(Set<String> fishingZoneFacadeNames) {
+ this.fishingZoneFacadeNames = fishingZoneFacadeNames;
+ }
+
+ public Set<String> getFishingZoneSectorNames() {
+ return fishingZoneSectorNames;
+ }
+
+ public void setFishingZoneSectorNames(Set<String> fishingZoneSectorNames) {
+ this.fishingZoneSectorNames = fishingZoneSectorNames;
+ }
+
+ public Set<String> getSampleRowCodes() {
+ return sampleRowCodes;
+ }
+
+ public void setSampleRowCodes(Set<String> sampleRowCodes) {
+ this.sampleRowCodes = sampleRowCodes;
+ }
+
+ public Set<String> getProgramNames() {
+ return programNames;
+ }
+
+ public void setProgramNames(Set<String> programNames) {
+ this.programNames = programNames;
+ }
+
+ public Set<String> getFishingGearDcfIds() {
+ return fishingGearDcfIds;
+ }
+
+ public void setFishingGearDcfIds(Set<String> fishingGearDcfIds) {
+ this.fishingGearDcfIds = fishingGearDcfIds;
+ }
+
+ public Set<String> getTargetSpeciesDcfIds() {
+ return targetSpeciesDcfIds;
+ }
+
+ public void setTargetSpeciesDcfIds(Set<String> targetSpeciesDcfIds) {
+ this.targetSpeciesDcfIds = targetSpeciesDcfIds;
+ }
+
+ public Set<SamplingStrategy> getSamplingStrategies() {
+ return samplingStrategies;
+ }
+
+ public void setSamplingStrategies(Set<SamplingStrategy> samplingStrategies) {
+ this.samplingStrategies = samplingStrategies;
+ }
+
+ public Set<String> getTerrestrialDistrictIds() {
+ return terrestrialDistrictIds;
+ }
+
+ public void setTerrestrialDistrictIds(Set<String> terrestrialDistrictIds) {
+ this.terrestrialDistrictIds = terrestrialDistrictIds;
+ }
+
+ public Set<String> getObservationUnitIds() {
+ return observationUnitIds;
+ }
+
+ public void setObservationUnitIds(Set<String> observationUnitIds) {
+ this.observationUnitIds = observationUnitIds;
+ }
+
+ public Set<String> getObserverIds() {
+ return observerIds;
+ }
+
+ public void setObserverIds(Set<String> observerIds) {
+ this.observerIds = observerIds;
+ }
+
+ public Set<String> getSubPopulationCodes() {
+ return subPopulationCodes;
+ }
+
+ public void setSubPopulationCodes(Set<String> subPopulationCodes) {
+ this.subPopulationCodes = subPopulationCodes;
+ }
+
+ public Set<String> getSkillZoneCodes() {
+ return skillZoneCodes;
+ }
+
+ public void setSkillZoneCodes(Set<String> skillZoneCodes) {
+ this.skillZoneCodes = skillZoneCodes;
+ }
+
+ public Set<String> getRegionIfremerIds() {
+ return regionIfremerIds;
+ }
+
+ public void setRegionIfremerIds(Set<String> regionIfremerIds) {
+ this.regionIfremerIds = regionIfremerIds;
+ }
+
+ public Set<String> getCompanyIds() {
+ return companyIds;
+ }
+
+ public void setCompanyIds(Set<String> companyIds) {
+ this.companyIds = companyIds;
+ }
+
+ public Set<String> getOrderByArguments() {
+ return orderByArguments;
+ }
+
+ public void setOrderByArguments(Set<String> orderByArguments) {
+ this.orderByArguments = orderByArguments;
+ }
+
+}
Modified: trunk/wao-persistence/src/main/java/fr/ifremer/wao/WaoUtils.java
===================================================================
--- trunk/wao-persistence/src/main/java/fr/ifremer/wao/WaoUtils.java 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-persistence/src/main/java/fr/ifremer/wao/WaoUtils.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -5,7 +5,10 @@
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.Collection;
import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
import java.util.regex.Pattern;
public class WaoUtils {
@@ -48,4 +51,12 @@
return Pattern.compile("^(\\d{4})_" + obsProgram.getShortCode() + "(\\d{4})$");
}
+ public static <T extends Enum> Collection<Integer> toOrdinals(Collection<T> enums) {
+ List<Integer> ordinals = new LinkedList<>();
+ for (Enum anEnum : enums) {
+ ordinals.add(anEnum.ordinal());
+ }
+ return ordinals;
+ }
+
}
Modified: trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/ObservationType.java
===================================================================
--- trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/ObservationType.java 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/ObservationType.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -56,4 +56,8 @@
}
throw new IllegalArgumentException("ordinal " + observationTypeOrdinal);
}
+
+ public boolean isFieldWorkObservation() {
+ return this == ObservationType.FIELD_WORK_OBSERVATION;
+ }
}
Modified: trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/SampleRowImpl.java
===================================================================
--- trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/SampleRowImpl.java 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/SampleRowImpl.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -257,9 +257,11 @@
Preconditions.checkState(
! getObsProgram().equals(ObsProgram.OBSDEB),
"opération non prévue pour ObsDeb");
- for (SampleMonth sampleMonth : getSampleMonth()) {
- if (sampleMonth.isPeriodDatesContains(date)) {
- return sampleMonth;
+ if (CollectionUtils.isNotEmpty(getSampleMonth())) {
+ for (SampleMonth sampleMonth : getSampleMonth()) {
+ if (sampleMonth.isPeriodDatesContains(date)) {
+ return sampleMonth;
+ }
}
}
return null;
Modified: trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/SampleRowTopiaDao.java
===================================================================
--- trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/SampleRowTopiaDao.java 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-persistence/src/main/java/fr/ifremer/wao/entity/SampleRowTopiaDao.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -1,10 +1,24 @@
package fr.ifremer.wao.entity;
import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
+import fr.ifremer.wao.SampleRowsFilter;
+import fr.ifremer.wao.WaoUtils;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.time.DateUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuiton.topia.persistence.HqlAndParametersBuilder;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
public class SampleRowTopiaDao extends AbstractSampleRowTopiaDao<SampleRow> {
+ private static final Log log = LogFactory.getLog(SampleRowTopiaDao.class);
+
public Optional<String> findMaxSampleRowCode(String sampleRowCodePrefix) {
String hql = "select max(sr.code) "
+ newFromClause("sr")
@@ -15,4 +29,147 @@
return optionalMaxSampleRowCode;
}
+ public List<SampleRow> findAll(SampleRowsFilter filter) {
+
+ HqlAndParametersBuilder<SampleRow> query = toSampleRowHqlAndParametersBuilder(filter);
+
+ if (CollectionUtils.isNotEmpty(filter.getOrderByArguments())) {
+ query.setOrderByArguments(filter.getOrderByArguments());
+ }
+
+ List<SampleRow> all = findAll(query.getHql(), query.getHqlParameters());
+
+ return all;
+
+ }
+
+ protected HqlAndParametersBuilder<SampleRow> toSampleRowHqlAndParametersBuilder(SampleRowsFilter filter) {
+
+ ObsProgram obsProgram = filter.getObsProgram();
+
+ Preconditions.checkNotNull(obsProgram, "ObsProgram cannot be null");
+
+ HqlAndParametersBuilder<SampleRow> query = newHqlAndParametersBuilder();
+
+ query.setAlias("sr");
+
+ query.addEquals(SampleRow.PROPERTY_OBS_PROGRAM_ORDINAL, obsProgram.ordinal());
+
+ if (filter.getExpectedObservationType() != null) {
+ query.addEquals(SampleRow.PROPERTY_OBSERVATION_TYPE_ORDINAL, filter.getExpectedObservationType().ordinal());
+ }
+
+ if (filter.getPeriodFrom() != null) {
+ if (obsProgram.isObsDeb()) {
+ query.addWhereClause("sr." + SampleRow.PROPERTY_EXPECTED_DATE + " >= :periodFrom", ImmutableMap.of("periodTo", (Object) filter.getPeriodFrom()));
+ } else {
+ Date periodFromMonth = DateUtils.truncate(filter.getPeriodFrom(), Calendar.MONTH);
+ query.addWhereClause("sr." + SampleRow.PROPERTY_PERIOD_END + " >= :periodFromMonth", ImmutableMap.of("periodFromMonth", (Object) periodFromMonth));
+ }
+ }
+
+ if (filter.getPeriodTo() != null) {
+ if (obsProgram.isObsDeb()) {
+ query.addWhereClause("sr." + SampleRow.PROPERTY_EXPECTED_DATE + " <= :periodTo", ImmutableMap.of("periodTo", (Object) filter.getPeriodTo()));
+ } else {
+ Date periodToMonth = DateUtils.truncate(filter.getPeriodTo(), Calendar.MONTH);
+ query.addWhereClause("sr." + SampleRow.PROPERTY_PERIOD_END + " <= :periodToMonth", ImmutableMap.of("periodToMonth", (Object) periodToMonth));
+ }
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getSampleRowCodes())) {
+ query.addIn(SampleRow.PROPERTY_CODE, filter.getSampleRowCodes());
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getProgramNames())) {
+ query.addIn(SampleRow.PROPERTY_PROGRAM_NAME, filter.getProgramNames());
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getCompanyIds())) {
+ query.addTopiaIdIn(SampleRow.PROPERTY_COMPANY, filter.getCompanyIds());
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getSamplingStrategies())) {
+ query.addIn(SampleRow.PROPERTY_SAMPLING_STRATEGY_ORDINAL, WaoUtils.toOrdinals(filter.getSamplingStrategies()));
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getObservationUnitIds())) {
+ query.addTopiaIdIn(SampleRow.PROPERTY_OBSERVATION_UNIT, filter.getObservationUnitIds());
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getSubPopulationCodes())) {
+ query.addTopiaIdIn(
+ SampleRow.PROPERTY_OBSERVATION_UNIT + "." + TerrestrialDivision.PROPERTY_SUB_POPULATION_CODE,
+ filter.getSubPopulationCodes());
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getSkillZoneCodes())) {
+ query.addTopiaIdIn(
+ SampleRow.PROPERTY_OBSERVATION_UNIT + "." + TerrestrialDivision.PROPERTY_SKILL_ZONE_CODE,
+ filter.getSkillZoneCodes());
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getSkillZoneCodes())) {
+ query.addTopiaIdIn(
+ SampleRow.PROPERTY_OBSERVATION_UNIT + "." + TerrestrialDivision.PROPERTY_SKILL_ZONE_CODE,
+ filter.getSkillZoneCodes());
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getRegionIfremerIds())) {
+ Preconditions.checkNotNull(filter.getExpectedObservationType(), "expected observation type needed");
+ if (filter.getExpectedObservationType().isFieldWorkObservation()) {
+ query.addTopiaIdIn(
+ SampleRow.PROPERTY_OBSERVATION_UNIT + "." + TerrestrialDivision.PROPERTY_REGION_IFREMER,
+ filter.getRegionIfremerIds());
+ } else {
+ query.addWhereClause(
+ "sr.topiaId in (select sr2.topiaId from fr.ifremer.wao.entity.SampleRowImpl as sr2 join sr2.terrestrialLocations as regionIfremer where regionIfremer.topiaId in (:regionIfremerIds))",
+ ImmutableMap.of("regionIfremerIds", (Object) filter.getRegionIfremerIds()));
+ }
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getObserverIds())) {
+ query.addWhereClause(
+ "sr.topiaId in (select sr2.topiaId from fr.ifremer.wao.entity.SampleRowImpl as sr2 join sr2.observers as observer where observer.topiaId in (:observerIds))",
+ ImmutableMap.<String, Object>of("observerIds", filter.getObserverIds()));
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getTerrestrialDistrictIds())) {
+ query.addWhereClause(
+ "sr.topiaId in (select sr2.topiaId from fr.ifremer.wao.entity.SampleRowImpl as sr2 join sr2.terrestrialLocations as terrestrialDistrict where terrestrialDistrict.topiaId in (:terrestrialDistrictIds))",
+ ImmutableMap.of("terrestrialDistrictIds", (Object) filter.getTerrestrialDistrictIds()));
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getFishingZoneFacadeNames())) {
+ query.addWhereClause(
+ "sr.topiaId in (select sr2.topiaId from fr.ifremer.wao.entity.SampleRowImpl as sr2 join sr2.fishingZone as fz where fz.facadeName in (:fishingZoneFacadeNames))",
+ ImmutableMap.of("fishingZoneFacadeNames", (Object) filter.getFishingZoneFacadeNames()));
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getFishingZoneSectorNames())) {
+ query.addWhereClause(
+ "sr.topiaId in (select sr2.topiaId from fr.ifremer.wao.entity.SampleRowImpl as sr2 join sr2.fishingZone as fz where fz.sectorName in (:fishingZoneSectorNames))",
+ ImmutableMap.of("fishingZoneSectorNames", (Object) filter.getFishingZoneSectorNames()));
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getFishingGearDcfIds())) {
+ query.addWhereClause(
+ "sr.topiaId in (select sr2.topiaId from fr.ifremer.wao.entity.SampleRowImpl as sr2 join sr2.dCF5Code as dcf where dcf.fishingGearDCF.topiaId in (:fishingGearDcfIds))",
+ ImmutableMap.of("fishingGearDcfIds", (Object) filter.getFishingGearDcfIds()));
+ }
+
+ if (CollectionUtils.isNotEmpty(filter.getTargetSpeciesDcfIds())) {
+ query.addWhereClause(
+ "sr.topiaId in (select sr2.topiaId from fr.ifremer.wao.entity.SampleRowImpl as sr2 join sr2.dCF5Code as dcf where dcf.targetSpeciesDCF.topiaId in (:targetSpeciesDcfIds))",
+ ImmutableMap.of("targetSpeciesDcfIds", (Object) filter.getTargetSpeciesDcfIds()));
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("query to filter sample rows " + query.getHql() + " " + query.getHqlParameters());
+ }
+
+ return query;
+
+ }
+
}
Added: trunk/wao-services/src/main/java/fr/ifremer/wao/services/ObsMerFixtures.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/ObsMerFixtures.java (rev 0)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/ObsMerFixtures.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -0,0 +1,115 @@
+package fr.ifremer.wao.services;
+
+import fr.ifremer.wao.WaoTechnicalException;
+import fr.ifremer.wao.entity.Company;
+import fr.ifremer.wao.entity.CompanyTopiaDao;
+import fr.ifremer.wao.entity.FishingGearDCF;
+import fr.ifremer.wao.entity.FishingGearDCFTopiaDao;
+import fr.ifremer.wao.entity.ObsProgram;
+import fr.ifremer.wao.entity.TargetSpeciesDCF;
+import fr.ifremer.wao.entity.TargetSpeciesDCFTopiaDao;
+import fr.ifremer.wao.entity.UserProfile;
+import fr.ifremer.wao.entity.UserProfileImpl;
+import fr.ifremer.wao.entity.UserProfileTopiaDao;
+import fr.ifremer.wao.entity.UserRole;
+import fr.ifremer.wao.entity.WaoUser;
+import fr.ifremer.wao.entity.WaoUserImpl;
+import fr.ifremer.wao.entity.WaoUserTopiaDao;
+import fr.ifremer.wao.services.service.ImportErrorException;
+import fr.ifremer.wao.services.service.ObsMerSamplingPlanService;
+import fr.ifremer.wao.services.service.administration.ReferentialService;
+import org.apache.commons.io.IOUtils;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ObsMerFixtures {
+
+ protected WaoServiceContext serviceContext;
+
+ protected Map<String, Company> companies = new HashMap<>();
+
+ public ObsMerFixtures(WaoServiceContext serviceContext) {
+ this.serviceContext = serviceContext;
+ }
+
+ protected Company getCompany(String companyName) {
+ Company company = companies.get(companyName);
+ if (company == null) {
+ CompanyTopiaDao companyDao = serviceContext.getPersistenceContext().getCompanyDao();
+ company = companyDao.create(
+ Company.PROPERTY_NAME, companyName,
+ Company.PROPERTY_ACTIVE, true);
+ companies.put(companyName, company);
+ serviceContext.getPersistenceContext().commit();
+ }
+ return company;
+ }
+
+ public Company ifremer() {
+ return getCompany("Ifremer");
+ }
+
+ public Company oceanet() {
+ return getCompany("OCEANET");
+ }
+
+ public AuthenticatedWaoUser admin() {
+ WaoUserTopiaDao waoUserDao = serviceContext.getPersistenceContext().getWaoUserDao();
+ WaoUser admin = new WaoUserImpl();
+ admin.setCompany(ifremer());
+ admin.setLogin("admin");
+ admin.setActive(true);
+ UserProfileTopiaDao userProfileDao = serviceContext.getPersistenceContext().getUserProfileDao();
+ UserProfile userProfile = new UserProfileImpl();
+ userProfile.setUserRole(UserRole.ADMIN);
+ userProfile.setObsProgram(ObsProgram.OBSMER);
+ userProfile.setCanWrite(true);
+ userProfileDao.create(userProfile);
+ admin.addUserProfile(userProfile);
+ waoUserDao.create(admin);
+ AuthenticatedWaoUser authenticatedWaoUser = new AuthenticatedWaoUser(admin, userProfile);
+ return authenticatedWaoUser;
+ }
+
+ public void fishingZones() {
+ InputStream input = null;
+ try {
+ input = getClass().getResourceAsStream("/import/zonesPeche.csv");
+ serviceContext.newService(ReferentialService.class).importFishingZones(input);
+ } catch (ImportErrorException e) {
+ throw new WaoTechnicalException(e);
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+
+ public void samplingPlan() {
+ ifremer();
+ oceanet();
+ fishingZones();
+ InputStream input = null;
+ try {
+ input = getClass().getResourceAsStream("/import/echantillonnage.csv");
+ serviceContext.newService(ObsMerSamplingPlanService.class).importSamplingPlan(admin(), input);
+ } catch (ImportErrorException e) {
+ throw new WaoTechnicalException(e);
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+
+ public FishingGearDCF ptb() {
+ FishingGearDCFTopiaDao fishingGearDCFDao = serviceContext.getPersistenceContext().getFishingGearDCFDao();
+ FishingGearDCF ptb = fishingGearDCFDao.forCodeEquals("PTB").findUnique();
+ return ptb;
+ }
+
+ public TargetSpeciesDCF def() {
+ TargetSpeciesDCFTopiaDao fishingGearDCFDao = serviceContext.getPersistenceContext().getTargetSpeciesDCFDao();
+ TargetSpeciesDCF def = fishingGearDCFDao.forCodeEquals("DEF").findUnique();
+ return def;
+ }
+
+}
Added: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlan.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlan.java (rev 0)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlan.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -0,0 +1,5 @@
+package fr.ifremer.wao.services.service;
+
+public class ObsMerSamplingPlan {
+
+}
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanService.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -4,14 +4,19 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import fr.ifremer.wao.SampleRowsFilter;
import fr.ifremer.wao.entity.Boat;
import fr.ifremer.wao.entity.Boats;
import fr.ifremer.wao.entity.Company;
+import fr.ifremer.wao.entity.CompanyTopiaDao;
import fr.ifremer.wao.entity.DCF5Code;
import fr.ifremer.wao.entity.ElligibleBoat;
import fr.ifremer.wao.entity.ElligibleBoatImpl;
import fr.ifremer.wao.entity.ElligibleBoatTopiaDao;
import fr.ifremer.wao.entity.FishingZone;
+import fr.ifremer.wao.entity.FishingZoneTopiaDao;
+import fr.ifremer.wao.entity.LocationType;
import fr.ifremer.wao.entity.ObsProgram;
import fr.ifremer.wao.entity.ProfessionImpl;
import fr.ifremer.wao.entity.SampleMonth;
@@ -23,13 +28,26 @@
import fr.ifremer.wao.entity.SampleRowLogImpl;
import fr.ifremer.wao.entity.SampleRowTopiaDao;
import fr.ifremer.wao.entity.SamplingStrategy;
+import fr.ifremer.wao.entity.TerrestrialLocation;
+import fr.ifremer.wao.entity.TerrestrialLocationTopiaDao;
import fr.ifremer.wao.services.AuthenticatedWaoUser;
+import fr.ifremer.wao.services.service.administration.ReferentialService;
+import fr.ifremer.wao.services.service.administration.UnknownBoatImmatriculationsException;
+import fr.ifremer.wao.services.service.administration.UnknownFishingGearDcfCodeException;
+import fr.ifremer.wao.services.service.administration.UnknownTargetSpeciesDcfCodeException;
+import fr.ifremer.wao.services.service.csv.ObsMerObsVenteSamplingPlanImportExportModel;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.nuiton.csv.Import;
+import org.nuiton.csv.ImportModel;
+import org.nuiton.csv.ImportRuntimeException;
import org.nuiton.topia.persistence.TopiaEntities;
+import org.nuiton.util.DateUtil;
+import java.io.InputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
@@ -166,7 +184,8 @@
}
- public void preValidate(AuthenticatedWaoUser authenticatedWaoUser, UpdateSampleRowCommand updateSampleRowCommand) {
+ public void preValidate(AuthenticatedWaoUser authenticatedWaoUser, UpdateSampleRowCommand updateSampleRowCommand)
+ throws UnknownBoatImmatriculationsException, UnknownFishingGearDcfCodeException, UnknownTargetSpeciesDcfCodeException {
String elligibleBoatImmatriculations = updateSampleRowCommand.getElligibleBoatImmatriculations();
@@ -303,4 +322,156 @@
}
+ public List<SampleRow> getSamplingPlan(SampleRowsFilter sampleRowsFilter) {
+
+ SampleRowTopiaDao dao = getSampleRowDao();
+
+ return dao.findAll(sampleRowsFilter);
+
+ }
+
+ public SampleRowsFilter newSampleRowsFilter(AuthenticatedWaoUser authenticatedWaoUser) {
+
+ SampleRowsFilter newFilter = new SampleRowsFilter();
+
+ // all users can see only rows for the program they are logged for
+ ObsProgram obsProgram = authenticatedWaoUser.getObsProgram();
+ newFilter.setObsProgram(obsProgram);
+
+ // We don't want to see all rows but the ones important for today
+ Date fromDate = serviceContext.getNow();
+ Date toDate = fromDate;
+
+ if (obsProgram.isObsDeb()) {
+
+ if (authenticatedWaoUser.isObserver()) {
+
+ // observer only need to see what he has to do in the next weeks
+ fromDate = DateUtils.addMonths(fromDate, -1); // needed for phone sampling plan
+ toDate = DateUtils.addMonths(toDate, 1);
+
+ } else {
+
+ // in the firsts months of a new year go from now to 6 months before
+ // in the rest of the year, just start at the beginning of the current year
+ if (DateUtil.getMonth(fromDate) < 3) {
+ fromDate = DateUtils.addMonths(fromDate, -6);
+ toDate = DateUtils.addMonths(toDate, 6);
+ } else {
+ fromDate = DateUtil.setFirstDayOfYear(fromDate);
+ toDate = DateUtil.setLastDayOfYear(toDate);
+ }
+ }
+
+ fromDate = DateUtil.setMinTimeOfDay(fromDate);
+ toDate = DateUtil.setMaxTimeOfDay(toDate);
+
+ } else {
+
+ // Dans ObsMer et ObsVente, le plan est mensualisé
+ fromDate = DateUtils.truncate(fromDate, Calendar.MONTH);
+
+ // la période initiale doit toujours aller d'avril à mars (contractuel).
+ // on prend la période dans laquelle on se trouve actuellement
+
+ // si on est avant le premier avril de l'année courante
+ boolean beforePeriodChangeInCurrentYear = DateUtil.getMonth(fromDate) < 3;
+
+ if (beforePeriodChangeInCurrentYear) {
+ // on prend la période qui a commencé l'année dernière et qui finit
+ // cette année
+ fromDate = DateUtils.setMonths(fromDate, 3);
+ fromDate = DateUtils.addYears(fromDate, -1);
+ } else {
+ // on prend la période qui commence cette année et se termine
+ // l'année prochaine
+ fromDate = DateUtils.setMonths(fromDate, 3);
+ }
+
+ // on a fromDate sur le premier avril de la bonne année, on va
+ // jusqu'au mois de mars suivant
+ toDate = DateUtils.addMonths(fromDate, 11);
+ }
+
+ newFilter.setPeriodFrom(fromDate);
+ newFilter.setPeriodTo(toDate);
+
+ // Very very important to do that at beginning
+ // Evo #2227 : Guest user has no default company filter
+ if (authenticatedWaoUser.isCoordinatorOrObserver()) {
+ newFilter.setCompanyIds(Sets.newHashSet(authenticatedWaoUser.getCompany().getTopiaId()));
+ }
+
+ if (obsProgram.isObsDeb() && authenticatedWaoUser.isObserver()) {
+ // observer can only see the row he has been associated
+ newFilter.setObserverIds(Sets.newHashSet(authenticatedWaoUser.getWaoUser().getTopiaId()));
+ }
+
+ return newFilter;
+
+ }
+
+ public void importSamplingPlan(AuthenticatedWaoUser authenticatedWaoUser, InputStream csv) throws ImportErrorException {
+
+ ImportModel<SampleRow> samplingPlanImportModel =
+ newSamplingPlanImportModel(authenticatedWaoUser);
+
+ Import<SampleRow> samplingPlanImport =
+ Import.newImport(samplingPlanImportModel, csv);
+
+ SampleRowTopiaDao dao = getSampleRowDao();
+
+ try {
+
+ for (SampleRow sampleRow : samplingPlanImport) {
+
+ if (StringUtils.isBlank(sampleRow.getCode())) {
+
+ String newSampleRowCode = getNewSampleRowCode(ObsProgram.OBSMER);
+
+ sampleRow.setCode(newSampleRowCode);
+
+ } else {
+
+ // TODO brendan 18/03/14
+
+ }
+
+ getProfessionDao().create(sampleRow.getProfession());
+
+ dao.create(sampleRow);
+
+ }
+
+ } catch (ImportRuntimeException e) {
+ throw new ImportErrorException(e);
+ }
+
+ commit();
+
+ }
+
+ protected ImportModel<SampleRow> newSamplingPlanImportModel(AuthenticatedWaoUser authenticatedWaoUser) {
+
+ ObsProgram obsProgram = authenticatedWaoUser.getObsProgram();
+
+ CompanyTopiaDao companyDao = getCompanyDao();
+ List<Company> activeCompanies = companyDao.forActiveEquals(true).findAll();
+
+ TerrestrialLocationTopiaDao terrestrialLocationDao = getTerrestrialLocationDao();
+ List<TerrestrialLocation> terrestrialDistricts =
+ terrestrialLocationDao.forLocationTypeEquals(LocationType.DISTRICT).findAll();
+
+ FishingZoneTopiaDao fishingZoneDao = getFishingZoneDao();
+ List<FishingZone> fishingZones = fishingZoneDao.findAll();
+
+ ReferentialService referentialService = getReferentialService();
+
+ ImportModel<SampleRow> samplingPlanImportModel =
+ new ObsMerObsVenteSamplingPlanImportExportModel(obsProgram,
+ activeCompanies, fishingZones, terrestrialDistricts, referentialService);
+
+ return samplingPlanImportModel;
+
+ }
}
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/ReferentialService.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/ReferentialService.java 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/ReferentialService.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -88,6 +88,7 @@
import org.nuiton.i18n.I18n;
import org.nuiton.topia.persistence.TopiaDao;
import org.nuiton.topia.persistence.TopiaEntity;
+import org.nuiton.topia.persistence.TopiaNoResultException;
import org.nuiton.topia.persistence.TopiaQueryBuilderAddCriteriaOrRunQueryStep;
import org.nuiton.util.beans.Binder;
import org.nuiton.util.beans.BinderFactory;
@@ -996,7 +997,7 @@
}
- public List<Boat> getBoatsFromImmatriculations(String joinedImmatriculations) {
+ public List<Boat> getBoatsFromImmatriculations(String joinedImmatriculations) throws UnknownBoatImmatriculationsException {
Iterable<String> splitImmatriculations = BOAT_IMMATRICULATIONS_SPLITTER.split(joinedImmatriculations);
@@ -1009,6 +1010,17 @@
List<Boat> boats = getBoatDao().forImmatriculationIn(immatriculations).findAll();
+ if (boats.size() < immatriculations.size()) {
+ for (Boat boat : boats) {
+ immatriculations.remove(boat.getImmatriculation());
+ }
+ Set<String> immatriculationStrings = new HashSet<>();
+ for (Integer immatriculation : immatriculations) {
+ immatriculationStrings.add(String.valueOf(immatriculation));
+ }
+ throw new UnknownBoatImmatriculationsException(immatriculationStrings);
+ }
+
return boats;
}
@@ -1018,7 +1030,7 @@
* les instances de {@link fr.ifremer.wao.entity.DCF5Code} correspondantes en ajoutant
* les manquantes au référentiel au besoin.
*/
- public Collection<DCF5Code> getDcf5Codes(String joinedDcf5Codes) {
+ public Collection<DCF5Code> getDcf5Codes(String joinedDcf5Codes) throws UnknownFishingGearDcfCodeException, UnknownTargetSpeciesDcfCodeException {
Collection<DCF5Code> dcf5Codes = new HashSet<>();
@@ -1026,16 +1038,26 @@
if (StringUtils.isNotBlank(joinedDcf5Codes)) {
- Iterable<String> splitCodes = Splitter.onPattern("\\s+").split(joinedDcf5Codes);
+ Iterable<String> splitCodes = Splitter.onPattern("[,\\s]+").split(joinedDcf5Codes);
for (String code : splitCodes) {
String[] codeParts = code.split("_");
- FishingGearDCF fishingGearDCF = getFishingGearDCFDao().forCodeEquals(codeParts[0]).findUnique();
+ FishingGearDCF fishingGearDCF;
+ try {
+ fishingGearDCF = getFishingGearDCFDao().forCodeEquals(codeParts[0]).findUnique();
+ } catch (TopiaNoResultException e) {
+ throw new UnknownFishingGearDcfCodeException(codeParts[0]);
+ }
+
TargetSpeciesDCF targetSpeciesDCF = null;
if (codeParts.length == 2) {
- targetSpeciesDCF = getTargetSpeciesDCFDao().forCodeEquals(codeParts[1]).findUnique();
+ try {
+ targetSpeciesDCF = getTargetSpeciesDCFDao().forCodeEquals(codeParts[1]).findUnique();
+ } catch (TopiaNoResultException e) {
+ throw new UnknownTargetSpeciesDcfCodeException(codeParts[1]);
+ }
}
Map<String, Object> properties = new HashMap<>();
Added: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/UnknownBoatImmatriculationsException.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/UnknownBoatImmatriculationsException.java (rev 0)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/UnknownBoatImmatriculationsException.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -0,0 +1,19 @@
+package fr.ifremer.wao.services.service.administration;
+
+import fr.ifremer.wao.WaoException;
+
+import java.util.Set;
+
+public class UnknownBoatImmatriculationsException extends WaoException {
+
+ protected Set<String> immatriculations;
+
+ public UnknownBoatImmatriculationsException(Set<String> immatriculations) {
+ this.immatriculations = immatriculations;
+ }
+
+ public Set<String> getImmatriculations() {
+ return immatriculations;
+ }
+
+}
Added: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/UnknownFishingGearDcfCodeException.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/UnknownFishingGearDcfCodeException.java (rev 0)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/UnknownFishingGearDcfCodeException.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -0,0 +1,17 @@
+package fr.ifremer.wao.services.service.administration;
+
+import fr.ifremer.wao.WaoException;
+
+public class UnknownFishingGearDcfCodeException extends WaoException {
+
+ protected String code;
+
+ public UnknownFishingGearDcfCodeException(String code) {
+ this.code = code;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+}
Added: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/UnknownTargetSpeciesDcfCodeException.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/UnknownTargetSpeciesDcfCodeException.java (rev 0)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/UnknownTargetSpeciesDcfCodeException.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -0,0 +1,17 @@
+package fr.ifremer.wao.services.service.administration;
+
+import fr.ifremer.wao.WaoException;
+
+public class UnknownTargetSpeciesDcfCodeException extends WaoException {
+
+ protected String code;
+
+ public UnknownTargetSpeciesDcfCodeException(String code) {
+ this.code = code;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+}
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -105,7 +105,7 @@
}
- public void save(UpdateWaoUserCommand updateWaoUserCommand) {
+ public void preValidate(UpdateWaoUserCommand updateWaoUserCommand) throws UnknownBoatImmatriculationsException {
ObsProgram obsProgram = updateWaoUserCommand.getObsProgram();
WaoUser waoUser = updateWaoUserCommand.getWaoUser();
@@ -116,6 +116,12 @@
waoUser.setCanReadBoats(boatsFromImmatriculations);
}
+ }
+
+ public void save(UpdateWaoUserCommand updateWaoUserCommand) {
+
+ WaoUser waoUser = updateWaoUserCommand.getWaoUser();
+
String newPassword;
switch (updateWaoUserCommand.getPasswordStrategy()) {
case GENERATE_NEW_PASSWORD:
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/ObsMerObsVenteSamplingPlanImportExportModel.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/ObsMerObsVenteSamplingPlanImportExportModel.java 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/ObsMerObsVenteSamplingPlanImportExportModel.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -37,6 +37,7 @@
import fr.ifremer.wao.entity.SampleRowImpl;
import fr.ifremer.wao.entity.SamplingStrategy;
import fr.ifremer.wao.entity.TerrestrialLocation;
+import fr.ifremer.wao.services.service.administration.ReferentialService;
import fr.ifremer.wao.services.service.csv.operations.CompanyParserFormatter;
import fr.ifremer.wao.services.service.csv.operations.DCF5CodesParserFormatter;
import fr.ifremer.wao.services.service.csv.operations.DistrictNamesFormatter;
@@ -116,6 +117,8 @@
protected List<TerrestrialLocation> terrestrialDistricts;
+ protected ReferentialService referentialService;
+
protected ModelBuilder<SampleRow> modelBuilder;
protected PeriodDates periodDates;
@@ -129,11 +132,13 @@
/** Pour l'import */
public ObsMerObsVenteSamplingPlanImportExportModel(ObsProgram obsProgram,
List<Company> companies, List<FishingZone> fishingZones,
- List<TerrestrialLocation> terrestrialDistricts) {
+ List<TerrestrialLocation> terrestrialDistricts,
+ ReferentialService referentialService) {
this.obsProgram = obsProgram;
this.companies = companies;
this.fishingZones = fishingZones;
this.terrestrialDistricts = terrestrialDistricts;
+ this.referentialService = referentialService;
}
@Override
@@ -180,7 +185,7 @@
protected void buildModel() {
- modelBuilder = new ModelBuilder<SampleRow>();
+ modelBuilder = new ModelBuilder<>();
modelBuilder.newColumnForImportExport("PLAN_CODE", SampleRow.PROPERTY_CODE, new SampleRowCodeParserFormatter(obsProgram));
modelBuilder.newColumnForImportExport("SOCIETE_NOM", SampleRow.PROPERTY_COMPANY, new CompanyParserFormatter(companies));
@@ -193,7 +198,7 @@
modelBuilder.newColumnForImportExport("PECHE_DIVISION", SampleRow.PROPERTY_FISHING_ZONE, new FishingZonesParserFormatter(fishingZones));
modelBuilder.newColumnForImportExport("PECHE_AUTRE", SampleRow.PROPERTY_FISHING_ZONES_INFOS);
- modelBuilder.newColumnForImportExport("METIER_CODE_DCF5", SampleRow.PROPERTY_D_CF5_CODE, new DCF5CodesParserFormatter());
+ modelBuilder.newColumnForImportExport("METIER_CODE_DCF5", SampleRow.PROPERTY_D_CF5_CODE, new DCF5CodesParserFormatter(referentialService));
modelBuilder.newColumnForImportExport("METIER_MAILLAGE", SampleRow.PROPERTY_PROFESSION + "." + Profession.PROPERTY_MESH_SIZE);
modelBuilder.newColumnForImportExport("METIER_TAILLE", SampleRow.PROPERTY_PROFESSION + "." + Profession.PROPERTY_SIZE);
modelBuilder.newColumnForImportExport("METIER_AUTRE", SampleRow.PROPERTY_PROFESSION + "." + Profession.PROPERTY_OTHER);
@@ -217,7 +222,7 @@
}
if (obsProgram == ObsProgram.OBSVENTE) {
- modelBuilder.newColumnForImportExport("PLAN_STRATEGIE", "samplingStrategy", new Common.ToStringParserFormatter<SamplingStrategy>(SamplingStrategy.values()));
+ modelBuilder.newColumnForImportExport("PLAN_STRATEGIE", "samplingStrategy", new Common.ToStringParserFormatter<>(SamplingStrategy.values()));
modelBuilder.newColumnForImportExport("PLAN_LIEU_CODE", SampleRow.PROPERTY_TERRESTRIAL_LOCATIONS, new DistrictsParserFormatter(terrestrialDistricts));
modelBuilder.newColumnForExport("PLAN_LIEU_NOM", SampleRow.PROPERTY_TERRESTRIAL_LOCATIONS, new DistrictNamesFormatter());
modelBuilder.newIgnoredColumn("PLAN_LIEU_NOM");
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/operations/DCF5CodesParserFormatter.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/operations/DCF5CodesParserFormatter.java 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/csv/operations/DCF5CodesParserFormatter.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -24,43 +24,44 @@
package fr.ifremer.wao.services.service.csv.operations;
import fr.ifremer.wao.entity.DCF5Code;
-import fr.ifremer.wao.entity.DCF5CodeImpl;
-import fr.ifremer.wao.entity.FishingGearDCFImpl;
-import fr.ifremer.wao.entity.TargetSpeciesDCFImpl;
-import org.apache.commons.lang3.StringUtils;
+import fr.ifremer.wao.services.service.administration.ReferentialService;
+import fr.ifremer.wao.services.service.administration.UnknownFishingGearDcfCodeException;
+import fr.ifremer.wao.services.service.administration.UnknownTargetSpeciesDcfCodeException;
+import org.nuiton.csv.ImportRuntimeException;
import org.nuiton.csv.ValueParserFormatter;
import org.nuiton.util.StringUtil;
-import java.text.ParseException;
-import java.util.LinkedList;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
public class DCF5CodesParserFormatter implements ValueParserFormatter<List<DCF5Code>> {
protected static final String DCF5_CODES_SEPARATOR = ",";
+ protected ReferentialService referentialService;
+
+ public DCF5CodesParserFormatter() {}
+
+ public DCF5CodesParserFormatter(ReferentialService referentialService) {
+ this.referentialService = referentialService;
+ }
+
@Override
public String format(List<DCF5Code> dcf5Codes) {
return StringUtil.join(dcf5Codes, DCF5_CODES_SEPARATOR, true);
}
@Override
- public List<DCF5Code> parse(String codes) throws ParseException {
- List<DCF5Code> dcfFives = new LinkedList<DCF5Code>();
- if (StringUtils.isNotBlank(codes)) {
- String[] splitCodes = codes.split(DCF5_CODES_SEPARATOR);
- for (String code : splitCodes) {
- String[] codeParts = code.split("_");
- DCF5Code dcfFive = new DCF5CodeImpl();
-
- dcfFive.setFishingGearDCF(new FishingGearDCFImpl(codeParts[0]));
- if (codeParts.length == 2) {
- dcfFive.setTargetSpeciesDCF(new TargetSpeciesDCFImpl(codeParts[1]));
- }
-
- dcfFives.add(dcfFive);
- }
+ public List<DCF5Code> parse(String codes) {
+ try {
+ Collection<DCF5Code> dcf5Codes = referentialService.getDcf5Codes(codes);
+ List<DCF5Code> value = new ArrayList<>(dcf5Codes);
+ return value;
+ } catch (UnknownFishingGearDcfCodeException unknownFishingGearDcfCode) {
+ throw new ImportRuntimeException(unknownFishingGearDcfCode);
+ } catch (UnknownTargetSpeciesDcfCodeException unknownTargetSpeciesDcfCode) {
+ throw new ImportRuntimeException(unknownTargetSpeciesDcfCode);
}
- return dcfFives;
}
}
Modified: trunk/wao-services/src/test/java/fr/ifremer/wao/services/AbstractWaoServiceTest.java
===================================================================
--- trunk/wao-services/src/test/java/fr/ifremer/wao/services/AbstractWaoServiceTest.java 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-services/src/test/java/fr/ifremer/wao/services/AbstractWaoServiceTest.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -10,6 +10,7 @@
import org.apache.commons.logging.LogFactory;
import org.hibernate.cfg.Environment;
import org.junit.After;
+import org.nuiton.util.DateUtil;
import org.nuiton.util.FileUtil;
import java.io.File;
@@ -78,7 +79,7 @@
return false;
}
- protected WaoTopiaApplicationContext getApplicationContext () {
+ protected WaoTopiaApplicationContext getApplicationContext() {
String databaseName = UUID.randomUUID().toString();
@@ -106,6 +107,8 @@
FakeWaoServiceContext serviceContext = new FakeWaoServiceContext();
+ serviceContext.setDate(DateUtil.createDate(1, 11, 2009));
+
serviceContext.setApplicationConfig(getApplicationConfig());
serviceContext.setPersistenceContext(persistenceContext);
@@ -220,30 +223,32 @@
}
- protected FakeWaoServiceContext getServiceContext() {
+ protected FakeWaoServiceContext newServiceContext() {
- if (serviceContext == null) {
+ FakeWaoServiceContext serviceContext = new FakeWaoServiceContext();
- FakeWaoServiceContext serviceContext = new FakeWaoServiceContext();
+ serviceContext.setApplicationConfig(getApplicationConfig());
- serviceContext.setApplicationConfig(getApplicationConfig());
+ WaoTopiaPersistenceContext persistenceContext = newPersistenceContext();
- WaoTopiaPersistenceContext persistenceContext = newPersistenceContext();
+ serviceContext.setPersistenceContext(persistenceContext);
- serviceContext.setPersistenceContext(persistenceContext);
+ serviceContext.setDate(DateUtil.createDate(2, 11, 2009));
- this.serviceContext = serviceContext;
-
- }
-
return serviceContext;
}
protected <E extends WaoService> E newService(Class<E> serviceClass) {
- return getServiceContext().newService(serviceClass);
+ if (serviceContext == null) {
+ serviceContext = newServiceContext();
+
+ }
+
+ return serviceContext.newService(serviceClass);
+
}
}
Added: trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanServiceTest.java
===================================================================
--- trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanServiceTest.java (rev 0)
+++ trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/ObsMerSamplingPlanServiceTest.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -0,0 +1,85 @@
+package fr.ifremer.wao.services.service;
+
+import com.google.common.collect.ImmutableSet;
+import fr.ifremer.wao.SampleRowsFilter;
+import fr.ifremer.wao.entity.SampleRow;
+import fr.ifremer.wao.services.AbstractWaoServiceTest;
+import fr.ifremer.wao.services.ObsMerFixtures;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.nuiton.util.DateUtil;
+
+import java.io.InputStream;
+import java.util.List;
+
+public class ObsMerSamplingPlanServiceTest extends AbstractWaoServiceTest {
+
+ private static final Log log = LogFactory.getLog(ObsMerSamplingPlanServiceTest.class);
+
+ protected ObsMerSamplingPlanService service;
+
+ protected ObsMerFixtures fixtures;
+
+ protected boolean isDatabaseWithReferential() {
+ return true;
+ }
+
+ @Before
+ public void setUp() {
+ service = newService(ObsMerSamplingPlanService.class);
+ fixtures = new ObsMerFixtures(newServiceContext());
+ }
+
+ @Test
+ public void testImportSamplingPlan() {
+ fixtures.ifremer();
+ fixtures.oceanet();
+ fixtures.fishingZones();
+ InputStream input = null;
+ try {
+ input = getClass().getResourceAsStream("/import/echantillonnage.csv");
+ service.importSamplingPlan(fixtures.admin(), input);
+ } catch (ImportErrorException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("unexpected exception raised", e);
+ }
+ Assert.fail("exception should not be raised");
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+
+ @Test
+ public void testGetSamplingPlan() {
+
+ fixtures.samplingPlan();
+
+ serviceContext.setDate(DateUtil.createDate(15, 5, 2010));
+
+ SampleRowsFilter filter = service.newSampleRowsFilter(fixtures.admin());
+ List<SampleRow> samplingPlan = service.getSamplingPlan(filter);
+ Assert.assertEquals(12, samplingPlan.size());
+
+ filter = service.newSampleRowsFilter(fixtures.admin());
+ filter.setFishingGearDcfIds(ImmutableSet.of(fixtures.ptb().getTopiaId()));
+ samplingPlan = service.getSamplingPlan(filter);
+ Assert.assertEquals(3, samplingPlan.size());
+
+ filter = service.newSampleRowsFilter(fixtures.admin());
+ filter.setTargetSpeciesDcfIds(ImmutableSet.of(fixtures.def().getTopiaId()));
+ samplingPlan = service.getSamplingPlan(filter);
+ Assert.assertEquals(4, samplingPlan.size());
+
+ filter = service.newSampleRowsFilter(fixtures.admin());
+ filter.setFishingGearDcfIds(ImmutableSet.of(fixtures.ptb().getTopiaId()));
+ filter.setTargetSpeciesDcfIds(ImmutableSet.of(fixtures.def().getTopiaId()));
+ samplingPlan = service.getSamplingPlan(filter);
+ Assert.assertEquals(1, samplingPlan.size());
+
+ }
+
+}
Modified: trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/administration/ReferentialServiceTest.java
===================================================================
--- trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/administration/ReferentialServiceTest.java 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/administration/ReferentialServiceTest.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -13,7 +13,6 @@
import java.io.InputStream;
import java.util.Collection;
-import java.util.Date;
public class ReferentialServiceTest extends AbstractWaoServiceTest {
@@ -23,7 +22,6 @@
@Before
public void setUp() {
- getServiceContext().setDate(new Date(1393607049651l));
service = newService(ReferentialService.class);
}
@@ -147,10 +145,17 @@
@Test
public void testGetBoatsByImmatriculations() {
testImportBoats();
- Collection<Boat> boats = service.getBoatsFromImmatriculations("174258, 284595 978419");
- Assert.assertEquals(3, boats.size());
- boats = service.getBoatsFromImmatriculations(" ");
- Assert.assertTrue(boats.isEmpty());
+ try {
+ Collection<Boat> boats = service.getBoatsFromImmatriculations("174258, 284595 978419");
+ Assert.assertEquals(3, boats.size());
+ boats = service.getBoatsFromImmatriculations(" ");
+ Assert.assertTrue(boats.isEmpty());
+ } catch (UnknownBoatImmatriculationsException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("unexpected exception raised", e);
+ }
+ Assert.fail("exception should not be raised");
+ }
}
@Test
@@ -158,12 +163,28 @@
service.initialDCF5CodesImport();
- Collection<DCF5Code> dcf5Codes = service.getDcf5Codes("OTT_DWS OTB_DEF OTB OTT_DEF");
+ try {
+ Collection<DCF5Code> dcf5Codes = service.getDcf5Codes("OTT_DWS OTB_DEF OTB OTT_DEF");
- Assert.assertEquals(4, dcf5Codes.size());
+ Assert.assertEquals(4, dcf5Codes.size());
- for (DCF5Code dcf5Code : dcf5Codes) {
- Assert.assertTrue(dcf5Code.isPersisted());
+ for (DCF5Code dcf5Code : dcf5Codes) {
+ Assert.assertTrue(dcf5Code.isPersisted());
+ }
+
+ dcf5Codes = service.getDcf5Codes("OTT_DWS, OTB");
+
+ Assert.assertEquals(2, dcf5Codes.size());
+ } catch (UnknownFishingGearDcfCodeException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("unexpected exception raised", e);
+ }
+ Assert.fail("exception should not be raised");
+ } catch (UnknownTargetSpeciesDcfCodeException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("unexpected exception raised", e);
+ }
+ Assert.fail("exception should not be raised");
}
}
Modified: trunk/wao-services/src/test/resources/import/echantillonnage.csv
===================================================================
--- trunk/wao-services/src/test/resources/import/echantillonnage.csv 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-services/src/test/resources/import/echantillonnage.csv 2014-03-18 16:39:19 UTC (rev 1725)
@@ -1,14 +1,14 @@
"PLAN_CODE";"SOCIETE_NOM";"PECHE_DIVISION";"PECHE_AUTRE";"METIER_CODE_DCF5";"METIER_MAILLAGE";"METIER_TAILLE";"METIER_AUTRE";"METIER_LIBELLE";"METIER_ESPECES";"PROGRAMME_CODE";"PROGRAMME_DEBUT";"PROGRAMME_FIN";"12/2009";"01/2010";"02/2010";"03/2010";"04/2010";"05/2010";"06/2010";"07/2010";"08/2010";"09/2010";"10/2010";"11/2010";"12/2010";"01/2011";"02/2011";"03/2011";"PLAN_DUREE_MOY";"PLAN_NB_OBSERV";"PLAN_COMMENT";"TAUX_COUVERTURE_APPLIQUE"
-"2010_M0001";"TARTANPION";"IId,I";;"DRB_ALG";;;;;;"MA-2009";"03/2010";"01/2011";;;;0;0;0;0;0;0;0;2;0;0;4;;;"1.5";1;"Un petit commentaire";5
-"2010_M0002";"TARTANPION";"IId";;"DRH_CAT,DRH_CRU";;;;"Fileyage";"poissons tubes";"MANY";"01/2008";"12/2008";;20;0;0;21;0;0;21;0;0;21;0;0;;;;1;1;;5
+"2010_M0001";"Ifremer";"IId,I";;"DRB_ALG";;;;;;"MA-2009";"03/2010";"01/2011";;;;0;0;0;0;0;0;0;2;0;0;4;;;"1.5";1;"Un petit commentaire";5
+"2010_M0002";"Ifremer";"IId";;"DRH_CAT,DRH_CRU";;;;"Fileyage";"poissons tubes";"MANY";"01/2008";"12/2008";;20;0;0;21;0;0;21;0;0;21;0;0;;;;1;1;;5
"2010_M0003";;"IId,IV";;"DRH";;;;"balottage en plein air";"divers choses visqueuses";"MA-2009";"03/2010";"01/2011";;;;0;22;0;0;25;0;0;21;0;0;25;;;2;1;;5
"2009_M0001";;"IId,IV";;"GTR_DEF,GNS_DEF";"110-119";;;"Fileyage à poissons tubes";"poissons plats";"PPP2010";"12/2009";"10/2010";0;1;1;1;2;1;1;1;1;1;2;;;;;;1;1;;5
"2009_M0002";;"IId";;"GTR_DEF,GNS_DEF";"9888-99";;;"Fileyage à sirènes ";"nymphes";"PPP2010";"12/2009";"10/2010";1;1;1;2;3;4;4;4;4;3;3;;;;;;1;1;;10
-"2009_M0003";"TARTANPION";"IV";;"GTR_MOL,GNS";;;;"Fileyages à baleines à bosse";"chameaux";"PPP2010";"12/2009";"10/2010";2;2;2;2;2;2;2;2;2;2;4;;;;;;1;1;;10
+"2009_M0003";"Ifremer";"IV";;"GTR_MOL,GNS";;;;"Fileyages à baleines à bosse";"chameaux";"PPP2010";"12/2009";"10/2010";2;2;2;2;2;2;2;2;2;2;4;;;;;;1;1;;10
"2009_M0004";;"IId";;"PTB_ALG";">=190";">18";;"Chalutage de fond machiavélique";"gadidés; divers";"PPP2010";"12/2009";"10/2010";1;1;1;1;1;1;1;1;1;1;2;;;;;;3;2;;10
"2009_M0005";;"IId";;"PTB_ALG";"70-1100";">1140";;"Plongeur de fond sous-marin";"gadidés; divers";"PPP2010";"12/2009";"10/2010";1;1;1;1;1;1;1;1;1;1;2;;;;;;3;2;;10
-"2009_M0006";"BIS";"I,IV";;"DRB_DEF";"70-1100";">1140";;"Leon";"poissons plats";"PPP2010";"12/2009";"10/2010";0;1;0;1;0;1;2;2;2;2;1;;;;;;3;2;;
-"2010_M0004";"TARTANPION";"IId,IV";;"PTB_DEF,PTB_ALG,DRH";;;;"Chalutage de fond";"gadidés; poissons plats; divers";"BLOB";"01/2010";"12/2010";;3;0;0;3;0;0;3;0;0;3;0;0;;;;3;1;;
+"2009_M0006";"OCEANET";"I,IV";;"DRB_DEF";"70-1100";">1140";;"Leon";"poissons plats";"PPP2010";"12/2009";"10/2010";0;1;0;1;0;1;2;2;2;2;1;;;;;;3;2;;
+"2010_M0004";"Ifremer";"IId,IV";;"PTB_DEF,PTB_ALG,DRH";;;;"Chalutage de fond";"gadidés; poissons plats; divers";"BLOB";"01/2010";"12/2010";;3;0;0;3;0;0;3;0;0;3;0;0;;;;3;1;;
"2010_M0005";;"IId";;"DRB";;;;"Kamoulox";"merlu; poissons cerises; divers";"MA-2009";"03/2010";"01/2011";;;;0;5;0;0;5;0;0;6;0;0;3;;;3;1;;15
"2010_M0010";;"I,IV";;"DRB";"70-1100";;;"Kamoulox";"merlu; poissons pommes; divers";"MA-2009";"03/2010";"01/2011";;;;0;5;0;0;5;0;0;6;0;0;3;;;3;1;;15
"2010_M0011";;"I,IV";;"DRB_ANA";"70-1100";;;"Troubidou";"merlu; poissons cerises";"MA-2009";"03/2010";"01/2011";;;;0;5;0;0;5;0;0;6;0;0;3;;;3;1;;
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/EditWaoUserAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/EditWaoUserAction.java 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/administration/EditWaoUserAction.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -1,9 +1,11 @@
package fr.ifremer.wao.web.action.administration;
+import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.opensymphony.xwork2.Preparable;
+import fr.ifremer.wao.services.service.administration.UnknownBoatImmatriculationsException;
import fr.ifremer.wao.services.service.administration.UpdateWaoUserCommand;
import fr.ifremer.wao.services.service.administration.UpdateWaoUserCommandPasswordStrategy;
import fr.ifremer.wao.services.service.administration.WaoUsersService;
@@ -52,6 +54,12 @@
@Override
public void validate() {
+ try {
+ service.preValidate(updateWaoUserCommand);
+ } catch (UnknownBoatImmatriculationsException unknownBoatImmatriculations) {
+ addFieldError("updateWaoUserCommand.canReadBoats", t("wao.ui.unknownBoatImmatriculations", Joiner.on(", ").join(unknownBoatImmatriculations.getImmatriculations())));
+ }
+
if (updateWaoUserCommand.getPasswordStrategy().isDefinePassword()) {
if (StringUtils.isEmpty(updateWaoUserCommand.getClearPassword())) {
addFieldError("updateWaoUserCommand.clearPassword", t("wao.ui.form.updateWaoUserCommand.missingPassword"));
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditSampleRowAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditSampleRowAction.java 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditSampleRowAction.java 2014-03-18 16:39:19 UTC (rev 1725)
@@ -1,5 +1,6 @@
package fr.ifremer.wao.web.action.obsmer;
+import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.opensymphony.xwork2.Preparable;
@@ -7,6 +8,9 @@
import fr.ifremer.wao.entity.SampleRow;
import fr.ifremer.wao.services.service.ObsMerSamplingPlanService;
import fr.ifremer.wao.services.service.UpdateSampleRowCommand;
+import fr.ifremer.wao.services.service.administration.UnknownBoatImmatriculationsException;
+import fr.ifremer.wao.services.service.administration.UnknownFishingGearDcfCodeException;
+import fr.ifremer.wao.services.service.administration.UnknownTargetSpeciesDcfCodeException;
import fr.ifremer.wao.web.WaoJspActionSupport;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@@ -52,10 +56,24 @@
@Override
public void validate() {
- service.preValidate(getAuthenticatedWaoUser(), updateSampleRowCommand);
-
SampleRow sampleRow = updateSampleRowCommand.getSampleRow();
+ try {
+
+ service.preValidate(getAuthenticatedWaoUser(), updateSampleRowCommand);
+
+ if (CollectionUtils.isEmpty(sampleRow.getdCF5Code())) {
+ addFieldError("updateSampleRowCommand.dcf5Codes", t("wao.ui.form.SampleRow.error.missingDcf5Codes"));
+ }
+
+ } catch (UnknownBoatImmatriculationsException e) {
+ addFieldError("updateSampleRowCommand.elligibleBoatImmatriculations", t("wao.ui.unknownBoatImmatriculations", Joiner.on(", ").join(e.getImmatriculations())));
+ } catch (UnknownFishingGearDcfCodeException e) {
+ addFieldError("updateSampleRowCommand.dcf5Codes", t("wao.ui.form.SampleRow.error.unknownFishingGearDcfCode", e.getCode()));
+ } catch (UnknownTargetSpeciesDcfCodeException e) {
+ addFieldError("updateSampleRowCommand.dcf5Codes", t("wao.ui.form.SampleRow.error.unknownTargetSpeciesDcfCode", e.getCode()));
+ }
+
if (updateSampleRowCommand.isCreation()) {
if ( ! WaoUtils.getSampleRowCodePattern(sampleRow.getObsProgram()).matcher(sampleRow.getCode()).matches()) {
addFieldError("updateSampleRowCommand.code", t("wao.import.sampleRow.failure.wrongSampleRowCodeFormat", sampleRow.getCode()));
@@ -78,10 +96,6 @@
addFieldError("updateSampleRowCommand.sampleRow.periodEnd", t("wao.ui.form.SampleRow.error.missingPeriodEnd"));
}
- if (CollectionUtils.isEmpty(sampleRow.getdCF5Code())) {
- addFieldError("updateSampleRowCommand.dcf5Codes", t("wao.ui.form.SampleRow.error.missingDcf5Codes"));
- }
-
if (CollectionUtils.isEmpty(sampleRow.getFishingZone())) {
addFieldError("updateSampleRowCommand.fishingZones", t("wao.ui.form.SampleRow.error.missingFishingZones"));
}
Modified: trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties
===================================================================
--- trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties 2014-03-18 16:39:19 UTC (rev 1725)
@@ -211,6 +211,8 @@
wao.ui.form.SampleRow.error.missingPeriodEnd=You must define the month when the observations ends
wao.ui.form.SampleRow.error.missingProfessionSpecies=You must define the target species
wao.ui.form.SampleRow.error.missingProgramName=You must provide the program name
+wao.ui.form.SampleRow.error.unknownFishingGearDcfCode=Unknown fishing gear code\: %s
+wao.ui.form.SampleRow.error.unknownTargetSpeciesDcfCode=Unknown target species code\: %s
wao.ui.form.SampleRow.missingBeginDate=Begin date missing to generate line code
wao.ui.form.SampleRow.others=Others data of the sample row
wao.ui.form.SampleRow.program=Associated program and observation effort by months
@@ -413,6 +415,7 @@
wao.ui.unavailableOperation=Unavailable operation
wao.ui.unit.days=day(s)
wao.ui.unit.observers=observer(s)
+wao.ui.unknownBoatImmatriculations=Unknown boats\: %s
wao.ui.userList=Users list
wao.ui.userMustAcceptCgu=You must accept the terms of use
wao.ui.validLogin=Valid identifier
Modified: trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties
===================================================================
--- trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties 2014-03-17 09:58:05 UTC (rev 1724)
+++ trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties 2014-03-18 16:39:19 UTC (rev 1725)
@@ -211,6 +211,8 @@
wao.ui.form.SampleRow.error.missingPeriodEnd=Il faut préciser le mois auquel se termine la période d'observation
wao.ui.form.SampleRow.error.missingProfessionSpecies=Il faut préciser les espèces cibles
wao.ui.form.SampleRow.error.missingProgramName=Il faut préciser le nom du programme
+wao.ui.form.SampleRow.error.unknownFishingGearDcfCode=Le code engin %s est inconnu du référentiel
+wao.ui.form.SampleRow.error.unknownTargetSpeciesDcfCode=Le code espèce cible %s est inconnu du référentiel
wao.ui.form.SampleRow.missingBeginDate=Date de début manquante pour générer le code de la ligne \!
wao.ui.form.SampleRow.others=Autres données de la ligne d'échantillonnage
wao.ui.form.SampleRow.program=Programme de rattachement et effort d'observation par mois
@@ -413,6 +415,7 @@
wao.ui.unavailableOperation=Opération non-disponible
wao.ui.unit.days=jour(s)
wao.ui.unit.observers=observateur(s)
+wao.ui.unknownBoatImmatriculations=Les navires ayant pour immatriculations %s sont inconnus du référentiel
wao.ui.userList=Liste des utilisateurs
wao.ui.userMustAcceptCgu=Vous devez accepter les conditions d'utilisation pour utiliser Wao
wao.ui.validLogin=Identifiant valide
1
0
Jenkins build is back to normal : wao-nightly » Wao #12
by admin+ci-codelutin.com@codelutin.com 17 Mar '14
by admin+ci-codelutin.com@codelutin.com 17 Mar '14
17 Mar '14