Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/applicationContext.xml'. Fisheye: No comparison available. Pass `N' to diff? Index: WEB-INF/lib/activation-1.1.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/commons-beanutils-1.7.0.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/commons-digester-1.6.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/commons-lang-2.1.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/commons-logging-1.0.4.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/jstl-1.1.2.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/lemans-user-common-1.0.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/log4j-1.2.9.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/mail-1.4.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/spring-beans-1.2.6.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/spring-context-1.2.6.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/spring-core-1.2.6.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/spring-dao-1.2.6.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/spring-jdbc-1.2.6.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/spring-web-1.2.6.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/standard-1.1.2.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/lib/struts-1.2.9.jar =================================================================== diff -u -rcf85c0a6b46ed700a99900be1d32c9370580c7ce -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/MessageResources.properties'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/application.properties'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/com/lemans/usermanager/filters/ParamFilter.java'. Fisheye: No comparison available. Pass `N' to diff? Index: WEB-INF/src/com/lemans/usermanager/images/bg.gif =================================================================== diff -u -re49ae46538b0df62139389cdd705c46cdd159ea9 -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/src/com/lemans/usermanager/images/bg_tile.gif =================================================================== diff -u -re49ae46538b0df62139389cdd705c46cdd159ea9 -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/src/com/lemans/usermanager/images/button_max.gif =================================================================== diff -u -re49ae46538b0df62139389cdd705c46cdd159ea9 -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/src/com/lemans/usermanager/images/button_min.gif =================================================================== diff -u -re49ae46538b0df62139389cdd705c46cdd159ea9 -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/src/com/lemans/usermanager/images/buttonbg.gif =================================================================== diff -u -re49ae46538b0df62139389cdd705c46cdd159ea9 -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/src/com/lemans/usermanager/images/center_header.gif =================================================================== diff -u -re49ae46538b0df62139389cdd705c46cdd159ea9 -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/src/com/lemans/usermanager/images/lines_fade.gif =================================================================== diff -u -re49ae46538b0df62139389cdd705c46cdd159ea9 -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/src/com/lemans/usermanager/images/subnav_bg.gif =================================================================== diff -u -re49ae46538b0df62139389cdd705c46cdd159ea9 -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Index: WEB-INF/src/com/lemans/usermanager/images/white.gif =================================================================== diff -u -re49ae46538b0df62139389cdd705c46cdd159ea9 -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 Binary files differ Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/com/lemans/usermanager/javascript/effects.js'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/com/lemans/usermanager/javascript/prototype.js'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/com/lemans/usermanager/javascript/site.js'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/com/lemans/usermanager/presentation/actions/ApplicationDispatchAction.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/com/lemans/usermanager/presentation/actions/GroupDispatchAction.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/com/lemans/usermanager/presentation/actions/PermissionDispatchAction.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/com/lemans/usermanager/presentation/forms/ApplicationForm.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/com/lemans/usermanager/presentation/forms/GroupForm.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/com/lemans/usermanager/presentation/forms/PermissionForm.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/com/lemans/usermanager/stylesheets/site.css'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/com/lemans/usermanager/stylesheets/standard.css'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/log4j.properties'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/pages/applications.jsp'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/pages/editApplication.jsp'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/pages/editGroup.jsp'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/pages/groups.jsp'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/pages/inc/footer.jsp'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/pages/inc/header.jsp'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/pages/main.jsp'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/pages/permissions.jsp'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/src/userloader.properties'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/struts-config.xml'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/tiles-defs.xml'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `WEB-INF/web.xml'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 04b2890465d7f3cec6198ac9fdedd60fdc60ae81 refers to a dead (removed) revision in file `index.jsp'. Fisheye: No comparison available. Pass `N' to diff? Index: pom.xml =================================================================== diff -u -rc5ba112c24b867b8d2fc62aaf944e6da09abd3d0 -r04b2890465d7f3cec6198ac9fdedd60fdc60ae81 --- pom.xml (.../pom.xml) (revision c5ba112c24b867b8d2fc62aaf944e6da09abd3d0) +++ pom.xml (.../pom.xml) (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -32,8 +32,8 @@ build build/test-classes build/classes - WEB-INF/src - test + src/main + src/test org.codehaus.mojo Index: src/main/java/com/lemans/usermanager/filters/ParamFilter.java =================================================================== diff -u --- src/main/java/com/lemans/usermanager/filters/ParamFilter.java (revision 0) +++ src/main/java/com/lemans/usermanager/filters/ParamFilter.java (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,52 @@ +package com.lemans.usermanager.filters; + +import java.io.IOException; +import java.util.*; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +public final class ParamFilter implements Filter { + + FilterConfig filterConfig = null; + + public void init(FilterConfig filterConfig) throws ServletException { + this.filterConfig = filterConfig; + } + + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + String regex = this.filterConfig.getInitParameter("excludeParams"); + chain.doFilter(new ParamFilteredRequest(request, regex), response); + } + + public void destroy() { } + + static class ParamFilteredRequest extends HttpServletRequestWrapper { + + private HttpServletRequest originalRequest; + private String regex; + + public ParamFilteredRequest(ServletRequest request, String regex) { + super((HttpServletRequest)request); + this.originalRequest = (HttpServletRequest) request; + this.regex = regex; + } + public Enumeration getParameterNames() { + List requestParameterNames = Collections.list((Enumeration) super.getParameterNames()); + List finalParameterNames = new ArrayList(); + + for (String parameterName:requestParameterNames) { + if (!parameterName.matches(regex)) { + finalParameterNames.add(parameterName); + System.out.println("Param : " + parameterName); + } + } + return Collections.enumeration(finalParameterNames); + } + } +} \ No newline at end of file Index: src/main/java/com/lemans/usermanager/presentation/actions/ApplicationDispatchAction.java =================================================================== diff -u --- src/main/java/com/lemans/usermanager/presentation/actions/ApplicationDispatchAction.java (revision 0) +++ src/main/java/com/lemans/usermanager/presentation/actions/ApplicationDispatchAction.java (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,439 @@ +package com.lemans.usermanager.presentation.actions; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.struts.action.ActionErrors; +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionForward; +import org.apache.struts.action.ActionMapping; +import org.apache.struts.action.ActionMessage; +import org.apache.struts.action.ActionMessages; +import org.springframework.web.struts.LookupDispatchActionSupport; + +import com.lemans.intranet.user.integration.dao.UserDao; +import com.lemans.intranet.user.integration.dto.Application; +import com.lemans.intranet.user.integration.dto.Permission; +import com.lemans.usermanager.presentation.forms.ApplicationForm; + +/** + * This dispatch action handles all application and permission related server requests, including + * creating, updating, and deleting. + * + * @author Luke Forehand + */ +public class ApplicationDispatchAction extends LookupDispatchActionSupport { + + /** + * Log + */ + Log log = LogFactory.getLog(ApplicationDispatchAction.class); + + /** + * UserDao that handles all Data Access + */ + private UserDao userDao; + + /** + * Retrieves the UserDao from the application context. + * @see org.springframework.web.struts.DispatchActionSupport#onInit() + */ + protected void onInit() { + log.debug("onInit called"); + userDao = (UserDao) getWebApplicationContext().getBean("userDao"); + } + + /** + * sets the UserDao to null + * @see org.springframework.web.struts.DispatchActionSupport#onDestroy() + */ + protected void onDestroy() { + log.debug("onDestroy called"); + userDao = null; + } + + /** + * Edit Application retrieves a single application for editing. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward editApplication(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("editApplication called"); + ApplicationForm applicationForm = (ApplicationForm) form; + applicationForm.setCurrentApplication(userDao.getApplication(Integer.parseInt(request.getParameter("currentApplication.id")))); + return mapping.findForward("editApplication"); + } + + /** + * Save Application validates the application, then determines whether it's a new application or an update to an + * existing application. It sets either an error or a success message, then retrieves a list of all applications + * for the next page. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward saveApplication(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("saveApplication called"); + ApplicationForm applicationForm = (ApplicationForm) form; + ActionMessages messages = new ActionMessages(); + String applicationName = applicationForm.getCurrentApplication().getName(); + try { + if (applicationForm.getCurrentApplication().getId() != null && !"".equals(applicationForm.getCurrentApplication().getId())) { + applicationForm.getCurrentApplication().setPermissions(userDao.getPermissions(Integer.parseInt(applicationForm.getCurrentApplication().getId()))); + } + // validate application form + request.setAttribute("messageResources", getResources(request)); + ActionErrors errors = applicationForm.validate(mapping, request); + if (errors != null) { + saveMessages(request, errors); + return mapping.findForward("editApplication"); + } + + Application currentApplication = applicationForm.getCurrentApplication(); + + // check if application with same name already exists + for (int i = 0; i < applicationForm.getApplications().size(); i++) { + if (((Application)applicationForm.getApplications().get(i)).getName().trim().equals(applicationName.trim()) && + !((Application)applicationForm.getApplications().get(i)).getId().equals(applicationForm.getCurrentApplication().getId())) { + // application with same name found, add error message and return + messages.add("exception", new ActionMessage("application.exists", applicationName)); + saveMessages(request, messages); + List applications = userDao.getAllApplications(); + applicationForm.setApplications(applications); + int index = applications.indexOf(applicationForm.getCurrentApplication()); + if (index >= 0) { + currentApplication = (Application)applications.get(index); + applicationForm.setCurrentApplication(currentApplication); + } + return mapping.findForward("editApplication"); + } + } + + if (currentApplication.getId() != null && !"".equals(currentApplication.getId())) { + userDao.updateApplication(currentApplication.getName(), currentApplication.getDescription(), Integer.parseInt(currentApplication.getId())); + } + else { + int applicationId = userDao.insertApplication(currentApplication.getName(), currentApplication.getDescription()); + currentApplication.setId("" + applicationId); + } + List applications = userDao.getAllApplications(); + applicationForm.setApplications(applications); + currentApplication = (Application)applications.get(applications.indexOf(applicationForm.getCurrentApplication())); + applicationForm.setCurrentApplication(currentApplication); + } + catch (Exception e) { + log.error("exception-> ", e); + messages.add("exception", new ActionMessage("application.save.exception", applicationName)); + saveMessages(request, messages); + return mapping.findForward("editApplication"); + } + messages.add("success", new ActionMessage("application.save.success", applicationName)); + saveMessages(request, messages); + return mapping.findForward("editApplication"); + } + + /** + * Create Application gets a list of all applications and sets the current application to a new instance. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward createApplication(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("createApplication called"); + ApplicationForm applicationForm = (ApplicationForm) form; + List applications = userDao.getAllApplications(); + applicationForm.setApplications(applications); + applicationForm.setCurrentApplication(new Application()); + return mapping.findForward("editApplication"); + } + + /** + * Delete Application attempts to delete the given application. It sets either an error or a success message, + * then retrieves a list of all applications for the next page. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward deleteApplication(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("deleteApplication called"); + ApplicationForm applicationForm = (ApplicationForm) form; + ActionMessages messages = new ActionMessages(); + String applicationName = applicationForm.getCurrentApplication().getName(); + try { + userDao.deleteApplication(Integer.parseInt(applicationForm.getCurrentApplication().getId())); + List applications = userDao.getAllApplications(); + applicationForm.setApplications(applications); + applicationForm.setCurrentApplication(new Application()); + } + catch (Exception e) { + log.error("exception-> ", e); + messages.add("exception", new ActionMessage("application.delete.exception", applicationName)); + saveMessages(request, messages); + return mapping.findForward("editApplication"); + } + messages.add("success", new ActionMessage("application.delete.success", applicationName)); + saveMessages(request, messages); + return listApplications(mapping, form, request, response); + } + + /** + * Search retrieves a list of all applications matching the given searchText + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward search(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("search called"); + String searchText = request.getParameter("searchText"); + List applications = new ArrayList(); + if (searchText != null && !"".equals(searchText)) { + applications = userDao.getApplications(searchText); + } + else { + applications = userDao.getAllApplications(); + } + request.setAttribute("applications", applications); + return mapping.findForward("start"); + } + + /** + * Unspecified retrieves a list of all applications and goes to the start page. This method is called when first entering + * the applications page. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward listApplications(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("listApplications called"); + List applications = userDao.getAllApplications(); + request.setAttribute("applications", applications); + return mapping.findForward("start"); + } + + /** + * Edit Permission retrieves a single permission from the current application for editing. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward editPermission(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("editPermission called"); + + String saveApplicationInfo = request.getParameter("saveApplicationInfo"); + if (saveApplicationInfo != null) { + log.debug("saveApplicationInfo was found->" + saveApplicationInfo); + if (saveApplicationInfo.equalsIgnoreCase("true")) { + // save application data + saveApplication(mapping, form, request, response); + } + } + + ApplicationForm applicationForm = (ApplicationForm) form; + List applications = userDao.getAllApplications(); + applicationForm.setApplications(applications); + Application currentApplication = new Application(); + currentApplication.setId(request.getParameter("currentApplication.id")); + currentApplication = (Application)applications.get(applications.indexOf(currentApplication)); + applicationForm.setCurrentApplication(currentApplication); + + Permission currentPermission = applicationForm.getCurrentPermission(); + int index = -1; + if (currentPermission.getId() != null) { + index = currentApplication.getPermissions().indexOf(applicationForm.getCurrentPermission()); + } + if (index >= 0) { + applicationForm.setCurrentPermission((Permission)currentApplication.getPermissions().get(index)); + } + else { + // initialize current permission and group list + currentPermission = new Permission(); + currentPermission.setId(applicationForm.getCurrentPermission().getId()); + applicationForm.setCurrentPermission(currentPermission); + } + return mapping.findForward("editPermission"); + } + + /** + * Save Permission validates the permission, then determines whether it's a new permission or an update to an + * existing permission. It sets either an error or a success message, then retrieves a list of all applications + * (and thus permissions) for the next page. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward savePermission(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("savePermission called"); + ApplicationForm applicationForm = (ApplicationForm) form; + ActionMessages messages = new ActionMessages(); + String permissionName = applicationForm.getCurrentPermission().getName(); + try { + applicationForm.getCurrentApplication().setPermissions(userDao.getPermissions(Integer.parseInt(applicationForm.getCurrentApplication().getId()))); + // validate permission form + request.setAttribute("messageResources", getResources(request)); + ActionErrors errors = applicationForm.validate(mapping, request); + if (errors != null) { + saveMessages(request, errors); + return mapping.findForward("editPermission"); + } + Permission currentPermission = applicationForm.getCurrentPermission(); + + // check if permission with same name already exists + for (int i = 0; i < applicationForm.getCurrentApplication().getPermissions().size(); i++) { + if (((Permission)applicationForm.getCurrentApplication().getPermissions().get(i)).getName().trim().equals(permissionName.trim()) && + !((Permission)applicationForm.getCurrentApplication().getPermissions().get(i)).getId().equals(applicationForm.getCurrentPermission().getId())) { + // permission with same name found, add error message and return + messages.add("exception", new ActionMessage("permission.exists", permissionName)); + saveMessages(request, messages); + return mapping.findForward("editPermission"); + } + } + + if (currentPermission.getId() != null && !"".equals(currentPermission.getId()) && !"0".equals(currentPermission.getId())) { + userDao.updatePermission(currentPermission.getName(), currentPermission.getDescription(), Integer.parseInt(currentPermission.getId())); + } + else { + int permissionId = userDao.insertPermission(currentPermission.getName(), currentPermission.getDescription(), Integer.parseInt(applicationForm.getCurrentApplication().getId())); + currentPermission.setId("" + permissionId); + } + List applications = userDao.getAllApplications(); + applicationForm.setApplications(applications); + Application currentApplication = (Application)applications.get(applications.indexOf(applicationForm.getCurrentApplication())); + applicationForm.setCurrentApplication(currentApplication); + applicationForm.setCurrentPermission(new Permission()); + } + catch (Exception e) { + log.error("exception-> ", e); + messages.add("exception", new ActionMessage("permission.save.exception", permissionName)); + saveMessages(request, messages); + return mapping.findForward("editPermission"); + } + messages.add("success", new ActionMessage("permission.save.success", permissionName)); + saveMessages(request, messages); + return mapping.findForward("editPermission"); + } + + /** + * Cancel Permission gets a list of all applications (and thus permissions), sets the current application, + * and sets the current permission to a new instance. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward cancelPermission(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("cancelPermission called"); + ApplicationForm applicationForm = (ApplicationForm) form; + List applications = userDao.getAllApplications(); + applicationForm.setApplications(applications); + Application currentApplication = (Application)applications.get(applications.indexOf(applicationForm.getCurrentApplication())); + applicationForm.setCurrentApplication(currentApplication); + applicationForm.setCurrentPermission(new Permission()); + return mapping.findForward("editPermission"); + } + + /** + * Delete Permission attempts to delete the given permission. It sets either an error or a success message, + * then retrieves a list of all applications (and thus permissions) for the next page. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward deletePermission(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("deletePermission called"); + ApplicationForm applicationForm = (ApplicationForm) form; + ActionMessages messages = new ActionMessages(); + String permissionName = applicationForm.getCurrentPermission().getName(); + try { + userDao.deletePermission(Integer.parseInt(applicationForm.getCurrentPermission().getId())); + List applications = userDao.getAllApplications(); + applicationForm.setApplications(applications); + Application currentApplication = (Application)applications.get(applications.indexOf(applicationForm.getCurrentApplication())); + applicationForm.setCurrentApplication(currentApplication); + applicationForm.setCurrentPermission(new Permission()); + } + catch (Exception e) { + log.error("exception-> ", e); + messages.add("exception", new ActionMessage("permission.delete.exception", permissionName)); + saveMessages(request, messages); + return mapping.findForward("editApplication"); + } + messages.add("success", new ActionMessage("permission.delete.success", permissionName)); + saveMessages(request, messages); + return mapping.findForward("editPermission"); + } + + /** + * Unspecified sets up the create application page. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward unspecified(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("unspecified called"); + return createApplication(mapping, form, request, response); + } + + /** + * @see org.apache.struts.actions.LookupDispatchAction#getKeyMethodMap() + */ + protected Map getKeyMethodMap() { + Map keyMethodMap = new HashMap(); + keyMethodMap.put("button.editPermission", "editPermission"); + + // both of these buttons map to the same method + keyMethodMap.put("button.createPermission", "savePermission"); + keyMethodMap.put("button.savePermission", "savePermission"); + + keyMethodMap.put("button.deletePermission", "deletePermission"); + keyMethodMap.put("button.cancelPermission", "cancelPermission"); + + keyMethodMap.put("button.editApplication", "editApplication"); + keyMethodMap.put("button.saveApplication", "saveApplication"); + keyMethodMap.put("button.createApplication", "createApplication"); + keyMethodMap.put("button.deleteApplication", "deleteApplication"); + keyMethodMap.put("button.listApplications", "listApplications"); + + keyMethodMap.put("button.search", "search"); + return keyMethodMap; + } +} Index: src/main/java/com/lemans/usermanager/presentation/actions/GroupDispatchAction.java =================================================================== diff -u --- src/main/java/com/lemans/usermanager/presentation/actions/GroupDispatchAction.java (revision 0) +++ src/main/java/com/lemans/usermanager/presentation/actions/GroupDispatchAction.java (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,330 @@ +package com.lemans.usermanager.presentation.actions; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.struts.action.ActionErrors; +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionForward; +import org.apache.struts.action.ActionMapping; +import org.apache.struts.action.ActionMessage; +import org.apache.struts.action.ActionMessages; +import org.springframework.web.struts.LookupDispatchActionSupport; + +import com.lemans.intranet.user.integration.dao.UserDao; +import com.lemans.intranet.user.integration.dto.Group; +import com.lemans.usermanager.presentation.forms.GroupForm; + +/** + * This dispatch action handles all group related server requests, including + * creating, updating, deleting, searching, and importing from Active Directory. + * + * @author Luke Forehand + */ +public class GroupDispatchAction extends LookupDispatchActionSupport { + + /** + * Log + */ + Log log = LogFactory.getLog(GroupDispatchAction.class); + + /** + * UserDao that handles all Data Access + */ + private UserDao userDao; + + /** + * Retrieves the UserDao from the application context. + * @see org.springframework.web.struts.DispatchActionSupport#onInit() + */ + protected void onInit() { + log.debug("onInit called"); + userDao = (UserDao) getWebApplicationContext().getBean("userDao"); + } + + /** + * sets the UserDao to null + * @see org.springframework.web.struts.DispatchActionSupport#onDestroy() + */ + protected void onDestroy() { + log.debug("onDestroy called"); + userDao = null; + } + + /** + * Edit Group retrieves a single group for editing. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward editGroup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("editGroup called"); + GroupForm groupForm = (GroupForm) form; + if (groupForm.getCurrentGroup().getId().equals("")) { + return createGroup(mapping, form, request, response); + } + List groups = userDao.getAllGroups(); + groupForm.setGroups(groups); + List activeDirectoryGroups = userDao.getAllActiveDirectoryGroups(); + groupForm.setActiveDirectoryGroups(activeDirectoryGroups); + List applications = userDao.getAllApplications(); + groupForm.setApplications(applications); + List users = userDao.getAllUsers(); + groupForm.setUsers(users); + Group currentGroup = new Group(); + currentGroup.setId(request.getParameter("currentGroup.id")); + currentGroup = (Group)groups.get(groups.indexOf(currentGroup)); + groupForm.setCurrentGroup(currentGroup); + return mapping.findForward("editGroup"); + } + + /** + * Save Group validates the group, then determines whether it's a new group or an update to an + * existing group. It sets either an error or a success message, then retrieves a list of + * all groups, active directory groups, applications, and users for the next page. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward saveGroup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("saveGroup called"); + GroupForm groupForm = (GroupForm) form; + ActionMessages messages = new ActionMessages(); + String groupName = groupForm.getCurrentGroup().getName(); + try { + // validate group form + ActionErrors errors = groupForm.validate(mapping, request); + if (errors != null) { + saveMessages(request, errors); + return mapping.findForward("editGroup"); + } + Group currentGroup = groupForm.getCurrentGroup(); + + // check if group with same name already exists + for (int i = 0; i < groupForm.getGroups().size(); i++) { + if (((Group)groupForm.getGroups().get(i)).getName().trim().equals(groupName.trim()) && + !((Group)groupForm.getGroups().get(i)).getId().equals(groupForm.getCurrentGroup().getId())) { + // group with same name found, add error message and return + messages.add("exception", new ActionMessage("group.import.exists", groupName)); + saveMessages(request, messages); + return mapping.findForward("editGroup"); + } + } + + if (currentGroup.getId() != null && !"".equals(currentGroup.getId())) { + userDao.updateGroup(currentGroup, groupForm.getSelectedUserIds(), groupForm.getSelectedPermissionIds()); + } + else { + int groupId = userDao.insertGroup(currentGroup, groupForm.getSelectedUserIds(), groupForm.getSelectedPermissionIds()); + currentGroup.setId("" + groupId); + } + + List groups = userDao.getAllGroups(); + groupForm.setGroups(groups); + List applications = userDao.getAllApplications(); + groupForm.setApplications(applications); + List users = userDao.getAllUsers(); + groupForm.setUsers(users); + + currentGroup = (Group)groups.get(groups.indexOf(groupForm.getCurrentGroup())); + groupForm.setCurrentGroup(currentGroup); + } + catch (Exception e) { + log.error("exception-> ", e); + messages.add("exception", new ActionMessage("group.save.exception", groupName)); + saveMessages(request, messages); + return mapping.findForward("editGroup"); + } + messages.add("success", new ActionMessage("group.save.success", groupName)); + saveMessages(request, messages); + return search(mapping, form, request, response); + } + + /** + * Create Group gets a list of all groups, active directory groups, applications, and users, and sets the + * current group to a new instance. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward createGroup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("createGroup called"); + GroupForm groupForm = (GroupForm) form; + + List groups = userDao.getAllGroups(); + groupForm.setGroups(groups); + List activeDirectoryGroups = userDao.getAllActiveDirectoryGroups(); + groupForm.setActiveDirectoryGroups(activeDirectoryGroups); + List applications = userDao.getAllApplications(); + groupForm.setApplications(applications); + List users = userDao.getAllUsers(); + groupForm.setUsers(users); + + groupForm.setCurrentGroup(new Group()); + return mapping.findForward("editGroup"); + } + + /** + * Delete Group attempts to delete the given group. It sets either an error or a success message, + * then retrieves a list of all groups, active directory groups, applications, and users for the next page. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward deleteGroup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("deleteGroup called"); + GroupForm groupForm = (GroupForm) form; + ActionMessages messages = new ActionMessages(); + String groupName = groupForm.getCurrentGroup().getName(); + try { + userDao.deleteGroup(Integer.parseInt(groupForm.getCurrentGroup().getId())); + + List groups = userDao.getAllGroups(); + groupForm.setGroups(groups); + List applications = userDao.getAllApplications(); + groupForm.setApplications(applications); + List users = userDao.getAllUsers(); + groupForm.setUsers(users); + groupForm.setCurrentGroup(new Group()); + } + catch (Exception e) { + log.error("exception-> ", e); + messages.add("exception", new ActionMessage("group.delete.exception", groupName)); + saveMessages(request, messages); + return mapping.findForward("editGroup"); + } + messages.add("success", new ActionMessage("group.delete.success", groupName)); + saveMessages(request, messages); + return search(mapping, form, request, response); + } + + /** + * Search retrieves a list of all groups matching the given searchText + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward search(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("search called"); + String searchText = request.getParameter("searchText"); + List groups = new ArrayList(); + if (searchText != null && !"".equals(searchText)) { + groups = userDao.getGroups(searchText); + } + else { + groups = userDao.getAllGroups(); + } + request.setAttribute("groups", groups); + + GroupForm groupForm = (GroupForm) form; + List activeDirectoryGroups = userDao.getAllActiveDirectoryGroups(); + groupForm.setActiveDirectoryGroups(activeDirectoryGroups); + + return mapping.findForward("start"); + } + + /** + * Import Group attempts to import a group from active directory. It sets either an error or a success message. + * If successful, it returns to the start page, else it loads the necessary form data and returns back to the + * edit groups page. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward importGroup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("importGroup called"); + GroupForm groupForm = (GroupForm) form; + ActionMessages messages = new ActionMessages(); + Group activeDirectoryGroup = new Group(); + activeDirectoryGroup.setId("" + groupForm.getSelectedActiveDirectoryGroupId()); + String groupName = ((Group) groupForm.getActiveDirectoryGroups().get(groupForm.getActiveDirectoryGroups().indexOf(activeDirectoryGroup))).getName(); + try { + int selectedActiveDirectoryGroupId = groupForm.getSelectedActiveDirectoryGroupId(); + if (selectedActiveDirectoryGroupId > 0) { + // check if group with same name already exists + for (int i = 0; i < groupForm.getGroups().size(); i++) { + if (((Group)groupForm.getGroups().get(i)).getName().equals(groupName)) { + // group with same name found, add error message and return + messages.add("exception", new ActionMessage("group.import.exists", groupName)); + saveMessages(request, messages); + return search(mapping, form, request, response); + } + } + // made it through the loop with no same name so import the group + userDao.importGroup(selectedActiveDirectoryGroupId); + } + else { + throw new Exception("selectedActiveDirectoryGroupId must be greater than 0"); + } + } + catch (Exception e) { + log.error("exception-> ", e); + messages.add("exception", new ActionMessage("group.import.exception", groupName)); + saveMessages(request, messages); + List groups = userDao.getAllGroups(); + groupForm.setGroups(groups); + List applications = userDao.getAllApplications(); + groupForm.setApplications(applications); + List users = userDao.getAllUsers(); + groupForm.setUsers(users); + return mapping.findForward("editGroup"); + } + messages.add("success", new ActionMessage("group.import.success", groupName)); + saveMessages(request, messages); + return search(mapping, form, request, response); + } + + /** + * Unspecified sets up the create group page. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward unspecified(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("unspecified called"); + return createGroup(mapping, form, request, response); + } + + /** + * @see org.apache.struts.actions.LookupDispatchAction#getKeyMethodMap() + */ + protected Map getKeyMethodMap() { + Map keyMethodMap = new HashMap(); + keyMethodMap.put("button.editGroup", "editGroup"); + keyMethodMap.put("button.createGroup", "createGroup"); + keyMethodMap.put("button.importGroup", "importGroup"); + keyMethodMap.put("button.saveGroup", "saveGroup"); + keyMethodMap.put("button.deleteGroup", "deleteGroup"); + keyMethodMap.put("button.search", "search"); + keyMethodMap.put("button.listGroups", "search"); + return keyMethodMap; + } +} Index: src/main/java/com/lemans/usermanager/presentation/actions/PermissionDispatchAction.java =================================================================== diff -u --- src/main/java/com/lemans/usermanager/presentation/actions/PermissionDispatchAction.java (revision 0) +++ src/main/java/com/lemans/usermanager/presentation/actions/PermissionDispatchAction.java (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,149 @@ +package com.lemans.usermanager.presentation.actions; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionForward; +import org.apache.struts.action.ActionMapping; +import org.apache.struts.action.ActionMessage; +import org.apache.struts.action.ActionMessages; +import org.springframework.web.struts.LookupDispatchActionSupport; + +import com.lemans.intranet.user.integration.dao.UserDao; +import com.lemans.intranet.user.integration.dto.Application; +import com.lemans.intranet.user.integration.dto.Permission; +import com.lemans.usermanager.presentation.forms.PermissionForm; + +/** + * This dispatch action handles all application and permission related server requests, including + * creating, updating, and deleting. + * + * @author Luke Forehand + */ +public class PermissionDispatchAction extends LookupDispatchActionSupport { + + /** + * Log + */ + Log log = LogFactory.getLog(PermissionDispatchAction.class); + + /** + * UserDao that handles all Data Access + */ + private UserDao userDao; + + /** + * Retrieves the UserDao from the application context. + * @see org.springframework.web.struts.DispatchActionSupport#onInit() + */ + protected void onInit() { + log.debug("onInit called"); + userDao = (UserDao) getWebApplicationContext().getBean("userDao"); + } + + /** + * sets the UserDao to null + * @see org.springframework.web.struts.DispatchActionSupport#onDestroy() + */ + protected void onDestroy() { + log.debug("onDestroy called"); + userDao = null; + } + + /** + * Save the permission + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward savePermission(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("savePermission called"); + PermissionForm permissionForm = (PermissionForm) form; + ActionMessages messages = new ActionMessages(); + //String permissionName = permissionForm.getCurrentPermission().getName(); + String permissionName = ((Permission) permissionForm.getCurrentApplication().getPermissions().get(permissionForm.getCurrentApplication().getPermissions().indexOf(permissionForm.getCurrentPermission()))).getName(); + try { + Permission currentPermission = permissionForm.getCurrentPermission(); + if (currentPermission.getId() != null && !"".equals(currentPermission.getId())) { + log.debug("saving permission"); + // save permission and group ids + if (permissionForm.getSelectedGroupIds() == null) { + permissionForm.setSelectedGroupIds(new int[] {}); + } + userDao.updatePermissionGroups(Integer.parseInt(currentPermission.getId()), permissionForm.getSelectedGroupIds()); + } + } + catch (Exception e) { + log.error("exception-> ", e); + messages.add("exception", new ActionMessage("permission.save.exception", permissionName)); + saveMessages(request, messages); + return unspecified(mapping, form, request, response); + } + messages.add("success", new ActionMessage("permission.save.success", permissionName)); + saveMessages(request, messages); + return unspecified(mapping, form, request, response); + } + + /** + * Unspecified sets up the create application page. + * @param mapping + * @param form + * @param request + * @param response + * @return Action Forward + * @throws Exception + */ + public ActionForward unspecified(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { + log.debug("unspecified called"); + PermissionForm permissionForm = (PermissionForm) form; + List groups = userDao.getAllGroups(); + permissionForm.setGroups(groups); + List applications = userDao.getAllApplications(); + permissionForm.setApplications(applications); + // initialize current application and permission list + Application currentApplication = new Application(); + currentApplication.setPermissions(new ArrayList()); + currentApplication.setId(request.getParameter("currentApplication.id")); + if (currentApplication.getId() != null && currentApplication.getId().length() > 0 && !currentApplication.getId().equals("0")) { + currentApplication = (Application)applications.get(applications.indexOf(currentApplication)); + currentApplication.setPermissions(userDao.getPermissions(Integer.parseInt(currentApplication.getId()))); + } + permissionForm.setCurrentApplication(currentApplication); + Permission currentPermission = permissionForm.getCurrentPermission(); + int index = -1; + if (currentPermission.getId() != null) { + index = currentApplication.getPermissions().indexOf(permissionForm.getCurrentPermission()); + } + if (index >= 0) { + permissionForm.setCurrentPermission((Permission)currentApplication.getPermissions().get(index)); + } + else { + // initialize current permission and group list + currentPermission = new Permission(); + currentPermission.setId(permissionForm.getCurrentPermission().getId()); + currentPermission.setGroups(new ArrayList()); + permissionForm.setCurrentPermission(currentPermission); + } + return mapping.findForward("start"); + } + + /** + * @see org.apache.struts.actions.LookupDispatchAction#getKeyMethodMap() + */ + protected Map getKeyMethodMap() { + Map keyMethodMap = new HashMap(); + keyMethodMap.put("button.savePermission", "savePermission"); + return keyMethodMap; + } +} Index: src/main/java/com/lemans/usermanager/presentation/forms/ApplicationForm.java =================================================================== diff -u --- src/main/java/com/lemans/usermanager/presentation/forms/ApplicationForm.java (revision 0) +++ src/main/java/com/lemans/usermanager/presentation/forms/ApplicationForm.java (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,141 @@ +package com.lemans.usermanager.presentation.forms; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.struts.action.ActionErrors; +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionMapping; +import org.apache.struts.action.ActionMessage; +import org.apache.struts.util.MessageResources; + +import com.lemans.intranet.user.integration.dto.Application; +import com.lemans.intranet.user.integration.dto.Permission; + +/** + * This class backs the HTML application form. It contains a list of applications to choose from, the current application + * selected, and the current permission selected. + * + * @author Luke Forehand + */ +public class ApplicationForm extends ActionForm { + + /** + * SerialVersionUID + */ + private static final long serialVersionUID = -7369733244048127435L; + + /** + * Log + */ + private static Log log = LogFactory.getLog(ApplicationForm.class); + + /** + * List of type Application + */ + private List applications = new ArrayList(); + + /** + * Currently selected Application + */ + private Application currentApplication = new Application(); + + /** + * Currently selected Permission + */ + private Permission currentPermission = new Permission(); + + public List getApplications() { + return applications; + } + + public void setApplications(List applications) { + this.applications = applications; + } + + public Application getCurrentApplication() { + return currentApplication; + } + + public void setCurrentApplication(Application currentApplication) { + this.currentApplication = currentApplication; + } + + public Permission getCurrentPermission() { + return currentPermission; + } + + public void setCurrentPermission(Permission currentPermission) { + this.currentPermission = currentPermission; + } + + /** + * This method validates the current permission name and description. + * @see org.apache.struts.action.ActionForm#validate(org.apache.struts.action.ActionMapping, javax.servlet.http.HttpServletRequest) + */ + public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { + ActionErrors errors = new ActionErrors(); + // if the id is not present, then don't validate + String dispatch = request.getParameter("dispatch") == null ? "" : request.getParameter("dispatch"); + log.debug("dispatch->" + dispatch); + + MessageResources messageResources = (MessageResources) request.getAttribute("messageResources"); + String editPermission = messageResources.getMessage("button.editPermission"); + String createPermission = messageResources.getMessage("button.createPermission"); + String savePermission = messageResources.getMessage("button.savePermission"); + String saveApplication = messageResources.getMessage("button.saveApplication"); + + // save application OR edit permission + if (dispatch.equalsIgnoreCase(saveApplication) || dispatch.equalsIgnoreCase(editPermission)) { + // validate application data + log.debug("validating application data"); + if (this.currentApplication.getName() == null || "".equals(this.currentApplication.getName().trim())) { + errors.add("error", new ActionMessage("errors.required", "Application Name")); + } else if (this.currentApplication.getName().length() > 50) { + errors.add("error", new ActionMessage("errors.maxlength", "Application Name", "50")); + } + if (this.currentApplication.getDescription() == null || "".equals(this.currentApplication.getDescription().trim())) { + errors.add("error", new ActionMessage("errors.required", "Application Description")); + } else if (this.currentApplication.getDescription().length() > 255) { + errors.add("error", new ActionMessage("errors.maxlength", "Application Description", "255")); + } + } + + if (dispatch.equalsIgnoreCase(createPermission) || dispatch.equalsIgnoreCase(savePermission)) { + // validate permission data + log.debug("validating permission data"); + if (this.currentPermission.getName() == null || "".equals(this.currentPermission.getName().trim())) { + errors.add("error", new ActionMessage("errors.required", "Permission Name")); + } else if (this.currentPermission.getName().length() > 50) { + errors.add("error", new ActionMessage("errors.maxlength", "Permission Name", "50")); + } + if (this.currentPermission.getDescription() == null || "".equals(this.currentPermission.getDescription().trim())) { + errors.add("error", new ActionMessage("errors.required", "Permission Description")); + } else if (this.currentPermission.getDescription().length() > 255) { + errors.add("error", new ActionMessage("errors.maxlength", "Permission Description", "255")); + } + } + + if (errors.size() > 0) { + return errors; + } + else { + return null; + } + + } + + /** + * Resets the currently selected application and permission + * @see org.apache.struts.action.ActionForm#reset(org.apache.struts.action.ActionMapping, javax.servlet.http.HttpServletRequest) + */ + public void reset(ActionMapping mapping, HttpServletRequest request) { + this.currentApplication = new Application(); + this.currentPermission = new Permission(); + } + +} \ No newline at end of file Index: src/main/java/com/lemans/usermanager/presentation/forms/GroupForm.java =================================================================== diff -u --- src/main/java/com/lemans/usermanager/presentation/forms/GroupForm.java (revision 0) +++ src/main/java/com/lemans/usermanager/presentation/forms/GroupForm.java (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,168 @@ +package com.lemans.usermanager.presentation.forms; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.struts.action.ActionErrors; +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionMapping; +import org.apache.struts.action.ActionMessage; + +import com.lemans.intranet.user.integration.dto.Group; + +/** + * This class backs the HTML group form. It contains the currently selected group, a list of groups to choose from, + * list of active directory groups to choose from, list of applications (containing the permission lists), + * list of users, and arrays of ints containing selected user ids and permission ids. + * + * @author Luke Forehand + */ +public class GroupForm extends ActionForm { + + /** + * SerialVersionUID + */ + private static final long serialVersionUID = -5951979244791921393L; + + /** + * List of type Group + */ + private List groups = new ArrayList(); + + /** + * List of type Group that is the list of all Active Directory groups + */ + private List activeDirectoryGroups = new ArrayList(); + + /** + * Currently selected Active Directory Group Id + */ + private int selectedActiveDirectoryGroupId = -1; + + /** + * List of type Application that is the list of all applications + */ + private List applications = new ArrayList(); + + /** + * List of type User that is the list of all users + */ + private List users = new ArrayList(); + + /** + * Currently selected user ids + */ + private int[] selectedUserIds; + + /** + * Currently selected permission ids + */ + private int[] selectedPermissionIds; + + /** + * Currently selected Group + */ + private Group currentGroup = new Group(); + + public Group getCurrentGroup() { + return currentGroup; + } + + public void setCurrentGroup(Group currentGroup) { + this.currentGroup = currentGroup; + } + + public List getGroups() { + return groups; + } + + public void setGroups(List groups) { + this.groups = groups; + } + + public List getActiveDirectoryGroups() { + return activeDirectoryGroups; + } + + public void setActiveDirectoryGroups(List activeDirectoryGroups) { + this.activeDirectoryGroups = activeDirectoryGroups; + } + + public int getSelectedActiveDirectoryGroupId() { + return selectedActiveDirectoryGroupId; + } + + public void setSelectedActiveDirectoryGroupId(int selectedActiveDirectoryGroupId) { + this.selectedActiveDirectoryGroupId = selectedActiveDirectoryGroupId; + } + + public List getApplications() { + return applications; + } + + public void setApplications(List applications) { + this.applications = applications; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + public int[] getSelectedUserIds() { + return selectedUserIds; + } + + public void setSelectedUserIds(int[] selectedUserIds) { + this.selectedUserIds = selectedUserIds; + } + + public int[] getSelectedPermissionIds() { + return selectedPermissionIds; + } + + public void setSelectedPermissionIds(int[] selectedPermissionIds) { + this.selectedPermissionIds = selectedPermissionIds; + } + + /** + * This method validates the current group name and description. + * @see org.apache.struts.action.ActionForm#validate(org.apache.struts.action.ActionMapping, javax.servlet.http.HttpServletRequest) + */ + public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { + ActionErrors errors = new ActionErrors(); + if (this.currentGroup.getName() == null || "".equals(this.currentGroup.getName().trim())) { + errors.add("error", new ActionMessage("errors.required", "Group Name")); + } else if (this.currentGroup.getName().length() > 50) { + errors.add("error", new ActionMessage("errors.maxlength", "Group Name", "50")); + } + if (this.currentGroup.getDescription() == null || "".equals(this.currentGroup.getDescription().trim())) { + errors.add("error", new ActionMessage("errors.required", "Group Description")); + } else if (this.currentGroup.getDescription().length() > 255) { + errors.add("error", new ActionMessage("errors.maxlength", "Group Description", "255")); + } + if (errors.size() > 0) { + return errors; + } + else { + return null; + } + + } + + /** + * Resets the currently selected Active Directory Group Id every time the form is reloaded. + * @see org.apache.struts.action.ActionForm#reset(org.apache.struts.action.ActionMapping, javax.servlet.http.HttpServletRequest) + */ + public void reset(ActionMapping mapping, HttpServletRequest request) { + this.selectedActiveDirectoryGroupId = -1; + this.selectedPermissionIds = null; + this.selectedUserIds = null; + } + +} Index: src/main/java/com/lemans/usermanager/presentation/forms/PermissionForm.java =================================================================== diff -u --- src/main/java/com/lemans/usermanager/presentation/forms/PermissionForm.java (revision 0) +++ src/main/java/com/lemans/usermanager/presentation/forms/PermissionForm.java (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,101 @@ +package com.lemans.usermanager.presentation.forms; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionMapping; + +import com.lemans.intranet.user.integration.dto.Application; +import com.lemans.intranet.user.integration.dto.Permission; + +/** + * This class backs the HTML permission form. It contains a list of applications to choose from, the current application + * selected, the current permission selected, list of all groups, and list of groups selected. + * + * @author Luke Forehand + */ +public class PermissionForm extends ActionForm { + + /** + * Serial Version UID + */ + private static final long serialVersionUID = -2899836808966019002L; + + /** + * List of type Application + */ + private List applications = new ArrayList(); + + /** + * List of type Group + */ + private List groups = new ArrayList(); + + /** + * Currently selected group ids + */ + private int[] selectedGroupIds; + + /** + * Currently selected Application + */ + private Application currentApplication = new Application(); + + /** + * Currently selected Permission + */ + private Permission currentPermission = new Permission(); + + public List getApplications() { + return applications; + } + + public void setApplications(List applications) { + this.applications = applications; + } + + public Application getCurrentApplication() { + return currentApplication; + } + + public void setCurrentApplication(Application currentApplication) { + this.currentApplication = currentApplication; + } + + public Permission getCurrentPermission() { + return currentPermission; + } + + public void setCurrentPermission(Permission currentPermission) { + this.currentPermission = currentPermission; + } + + public List getGroups() { + return this.groups; + } + + public void setGroups(List groups) { + this.groups = groups; + } + + public int[] getSelectedGroupIds() { + return this.selectedGroupIds; + } + + public void setSelectedGroupIds(int[] selectedGroupIds) { + this.selectedGroupIds = selectedGroupIds; + } + + /** + * Resets the form since it is stored in session + * @see org.apache.struts.action.ActionForm#reset(org.apache.struts.action.ActionMapping, javax.servlet.http.HttpServletRequest) + */ + public void reset(ActionMapping mapping, HttpServletRequest request) { + this.selectedGroupIds = null; + this.currentPermission = new Permission(); + } + +} Index: src/main/resources/application.production.properties =================================================================== diff -u --- src/main/resources/application.production.properties (revision 0) +++ src/main/resources/application.production.properties (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,15 @@ +#database config +datasource=org.apache.tomcat.dbcp.dbcp.BasicDataSource +dbdriver=com.microsoft.sqlserver.jdbc.SQLServerDriver +# dbserver=sql02\\sqlexpress:1433 +dbserver=prd-dbprod04vm:1433 +database=intranet_authorizations +dbusername=ad_user +dbpassword=Ad12#4 + +#ldap config +ldapurl=ldaps://dc1:636 +ldapbase=dc=lemanscorp,dc=com +ldapusername=ldapsearch +ldappassword=search! + Index: src/main/resources/application.properties =================================================================== diff -u --- src/main/resources/application.properties (revision 0) +++ src/main/resources/application.properties (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,14 @@ +#database config +datasource=org.apache.tomcat.dbcp.dbcp.BasicDataSource +dbdriver=com.microsoft.sqlserver.jdbc.SQLServerDriver +# dbserver=sql02\\sqlexpress:1433 +dbserver=prd-dbprod04vm:1433 +database=intranet_authorizations +dbusername=ad_user +dbpassword=Ad12#4 + +#ldap config +ldapurl=ldaps://dc1:636 +ldapbase=dc=lemanscorp,dc=com +ldapusername=ldapsearch +ldappassword=search! \ No newline at end of file Index: src/main/resources/applicationContext.xml =================================================================== diff -u --- src/main/resources/applicationContext.xml (revision 0) +++ src/main/resources/applicationContext.xml (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,36 @@ + + + + + + + + com.microsoft.sqlserver.jdbc.SQLServerDriver + + + jdbc:sqlserver://${dbserver};DatabaseName=${database} + + + ${dbusername} + + + ${dbpassword} + + + SELECT 1 + + + + + + + + + + + + /WEB-INF/classes/application.properties + + + + \ No newline at end of file Index: src/main/resources/log4j.production.properties =================================================================== diff -u --- src/main/resources/log4j.production.properties (revision 0) +++ src/main/resources/log4j.production.properties (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,18 @@ +#production log4j properties + +log4j.rootCategory=ERROR, file, mail +log4j.appender.file=org.apache.log4j.FileAppender +log4j.appender.file.File=/usr/local/tomcat/logs/usermanager.log +log4j.appender.file.Append=true +log4j.appender.file.layout=org.apache.log4j.PatternLayout +log4j.appender.file.layout.ConversionPattern=%d %-5p %c %x - %m%n + +log4j.appender.mail=org.apache.log4j.net.SMTPAppender +log4j.appender.mail.Threshold=ERROR +log4j.appender.mail.BufferSize=1 +log4j.appender.mail.From=usermanager@lemansnet.com +log4j.appender.mail.SMTPHost=localhost +log4j.appender.mail.Subject=PRODUCTION ERROR - User Manager +log4j.appender.mail.To=lforehand@parts-unltd.com +log4j.appender.mail.layout=org.apache.log4j.PatternLayout +log4j.appender.mail.layout.ConversionPattern=%d %-5p %c %x - %m%n \ No newline at end of file Index: src/main/resources/log4j.properties =================================================================== diff -u --- src/main/resources/log4j.properties (revision 0) +++ src/main/resources/log4j.properties (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,10 @@ +#stage log4j properties + +log4j.rootCategory=ERROR, file +log4j.appender.file=org.apache.log4j.FileAppender +log4j.appender.file.File=/var/log/tomcat6/usermanager.log +log4j.appender.file.Append=true +log4j.appender.file.layout=org.apache.log4j.PatternLayout +log4j.appender.file.layout.ConversionPattern=%d %-5p %c %x - %m%n + +log4j.category.com.lemans=DEBUG Index: src/main/resources/struts-config.xml =================================================================== diff -u --- src/main/resources/struts-config.xml (revision 0) +++ src/main/resources/struts-config.xml (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: src/main/resources/tiles-defs.xml =================================================================== diff -u --- src/main/resources/tiles-defs.xml (revision 0) +++ src/main/resources/tiles-defs.xml (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: src/main/resources/tomcatTasks.properties =================================================================== diff -u --- src/main/resources/tomcatTasks.properties (revision 0) +++ src/main/resources/tomcatTasks.properties (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,10 @@ +deploy=org.apache.catalina.ant.DeployTask +install=org.apache.catalina.ant.InstallTask +list=org.apache.catalina.ant.ListTask +reload=org.apache.catalina.ant.ReloadTask +remove=org.apache.catalina.ant.RemoveTask +resources=org.apache.catalina.ant.ResourcesTask +roles=org.apache.catalina.ant.RolesTask +start=org.apache.catalina.ant.StartTask +stop=org.apache.catalina.ant.StopTask +undeploy=org.apache.catalina.ant.UndeployTask \ No newline at end of file Index: src/main/resources/userloader.production.properties =================================================================== diff -u --- src/main/resources/userloader.production.properties (revision 0) +++ src/main/resources/userloader.production.properties (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,15 @@ +#database config +datasource=org.apache.tomcat.dbcp.dbcp.BasicDataSource +dbdriver=com.microsoft.sqlserver.jdbc.SQLServerDriver +# dbserver=sql02\\sqlexpress:1433 +dbserver=prd-dbprod04vm:1433 +database=intranet_authorizations +dbusername=ad_user +dbpassword=Ad12#4 + +#ldap config +ldapurl=ldaps://dc-2.lemanscorp.com:636 +ldapbase=dc=lemanscorp,dc=com +ldapusername=ldapsearch +ldappassword=search! + Index: src/main/resources/userloader.properties =================================================================== diff -u --- src/main/resources/userloader.properties (revision 0) +++ src/main/resources/userloader.properties (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,13 @@ +#database config +datasource=org.apache.tomcat.dbcp.dbcp.BasicDataSource +dbdriver=com.microsoft.sqlserver.jdbc.SQLServerDriver +dbserver=nav-us-db:57228 +database=intranet_authorizations +dbusername=intranet_auth_stage_user +dbpassword=IntranetAuth$stage + +#ldap config +ldapurl=ldap://dc1 +ldapbase=dc=lemanscorp,dc=com +ldapusername=ldapsearch +ldappassword=search! Index: src/main/resources/web.xml =================================================================== diff -u --- src/main/resources/web.xml (revision 0) +++ src/main/resources/web.xml (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,58 @@ + + + + IntranetUserFilter + com.lemans.intranet.user.presentation.filters.IntranetUserFilter + + applicationName + usermanager.lemansnet.com + + + + + IntranetUserFilter + /* + + + ParamFilter + com.lemans.usermanager.filters.ParamFilter + + excludeParams + (.*\.|^|.*|\[('|"))(c|C)lass(\.|('|")]|\[).* + + + + ParamFilter + action + + + action + org.apache.struts.action.ActionServlet + + config + /WEB-INF/struts-config.xml + + 2 + + + + context + org.springframework.web.context.ContextLoaderServlet + 1 + + + + action + *.do + + + + index.jsp + + + Index: src/main/webapp/WEB-INF/lib/activation-1.1.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/commons-beanutils-1.7.0.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/commons-digester-1.6.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/commons-lang-2.1.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/commons-logging-1.0.4.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/jstl-1.1.2.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/lemans-user-common-1.0.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/log4j-1.2.9.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/mail-1.4.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/spring-beans-1.2.6.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/spring-context-1.2.6.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/spring-core-1.2.6.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/spring-dao-1.2.6.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/spring-jdbc-1.2.6.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/spring-web-1.2.6.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/standard-1.1.2.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/WEB-INF/lib/struts-1.2.9.jar =================================================================== diff -u Binary files differ Index: src/main/webapp/images/bg.gif =================================================================== diff -u Binary files differ Index: src/main/webapp/images/bg_tile.gif =================================================================== diff -u Binary files differ Index: src/main/webapp/images/button_max.gif =================================================================== diff -u Binary files differ Index: src/main/webapp/images/button_min.gif =================================================================== diff -u Binary files differ Index: src/main/webapp/images/buttonbg.gif =================================================================== diff -u Binary files differ Index: src/main/webapp/images/center_header.gif =================================================================== diff -u Binary files differ Index: src/main/webapp/images/lines_fade.gif =================================================================== diff -u Binary files differ Index: src/main/webapp/images/subnav_bg.gif =================================================================== diff -u Binary files differ Index: src/main/webapp/images/white.gif =================================================================== diff -u Binary files differ Index: src/main/webapp/index.jsp =================================================================== diff -u --- src/main/webapp/index.jsp (revision 0) +++ src/main/webapp/index.jsp (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,4 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> + + Index: src/main/webapp/javascript/effects.js =================================================================== diff -u --- src/main/webapp/javascript/effects.js (revision 0) +++ src/main/webapp/javascript/effects.js (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,811 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Contributors: +// Justin Palmer (http://encytemedia.com/) +// Mark Pilgrim (http://diveintomark.org/) +// Martin Bialasinki +// +// See scriptaculous.js for full license. + +var Effect = { + tagifyText: function(element) { + var tagifyStyle = "position:relative"; + if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ";zoom:1"; + element = $(element); + $A(element.childNodes).each( function(child) { + if(child.nodeType==3) { + child.nodeValue.toArray().each( function(character) { + element.insertBefore( + Builder.node('span',{style: tagifyStyle}, + character == " " ? String.fromCharCode(160) : character), + child); + }); + Element.remove(child); + } + }); + }, + multiple: function(element, effect) { + var elements; + if(((typeof element == 'object') || + (typeof element == 'function')) && + (element.length)) + elements = element; + else + elements = $(element).childNodes; + + var options = Object.extend({ + speed: 0.1, + delay: 0.0 + }, arguments[2] || {}); + var speed = options.speed; + var delay = options.delay; + + $A(elements).each( function(element, index) { + new effect(element, Object.extend(options, { delay: delay + index * speed })); + }); + } +}; + +var Effect2 = Effect; // deprecated + +/* ------------- transitions ------------- */ + +Effect.Transitions = {} + +Effect.Transitions.linear = function(pos) { + return pos; +} +Effect.Transitions.sinoidal = function(pos) { + return (-Math.cos(pos*Math.PI)/2) + 0.5; +} +Effect.Transitions.reverse = function(pos) { + return 1-pos; +} +Effect.Transitions.flicker = function(pos) { + return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; +} +Effect.Transitions.wobble = function(pos) { + return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; +} +Effect.Transitions.pulse = function(pos) { + return (Math.floor(pos*10) % 2 == 0 ? + (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10))); +} +Effect.Transitions.none = function(pos) { + return 0; +} +Effect.Transitions.full = function(pos) { + return 1; +} + +/* ------------- core effects ------------- */ + +Effect.Queue = { + effects: [], + interval: null, + add: function(effect) { + var timestamp = new Date().getTime(); + + switch(effect.options.queue) { + case 'front': + // move unstarted effects after this effect + this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { + e.startOn += effect.finishOn; + e.finishOn += effect.finishOn; + }); + break; + case 'end': + // start effect after last queued effect has finished + timestamp = this.effects.pluck('finishOn').max() || timestamp; + break; + } + + effect.startOn += timestamp; + effect.finishOn += timestamp; + this.effects.push(effect); + if(!this.interval) + this.interval = setInterval(this.loop.bind(this), 40); + }, + remove: function(effect) { + this.effects = this.effects.reject(function(e) { return e==effect }); + if(this.effects.length == 0) { + clearInterval(this.interval); + this.interval = null; + } + }, + loop: function() { + var timePos = new Date().getTime(); + this.effects.invoke('loop', timePos); + } +} + +Effect.Base = function() {}; +Effect.Base.prototype = { + position: null, + setOptions: function(options) { + this.options = Object.extend({ + transition: Effect.Transitions.sinoidal, + duration: 1.0, // seconds + fps: 25.0, // max. 25fps due to Effect.Queue implementation + sync: false, // true for combining + from: 0.0, + to: 1.0, + delay: 0.0, + queue: 'parallel' + }, options || {}); + }, + start: function(options) { + this.setOptions(options || {}); + this.currentFrame = 0; + this.state = 'idle'; + this.startOn = this.options.delay*1000; + this.finishOn = this.startOn + (this.options.duration*1000); + this.event('beforeStart'); + if(!this.options.sync) Effect.Queue.add(this); + }, + loop: function(timePos) { + if(timePos >= this.startOn) { + if(timePos >= this.finishOn) { + this.render(1.0); + this.cancel(); + this.event('beforeFinish'); + if(this.finish) this.finish(); + this.event('afterFinish'); + return; + } + var pos = (timePos - this.startOn) / (this.finishOn - this.startOn); + var frame = Math.round(pos * this.options.fps * this.options.duration); + if(frame > this.currentFrame) { + this.render(pos); + this.currentFrame = frame; + } + } + }, + render: function(pos) { + if(this.state == 'idle') { + this.state = 'running'; + this.event('beforeSetup'); + if(this.setup) this.setup(); + this.event('afterSetup'); + } + if(this.options.transition) pos = this.options.transition(pos); + pos *= (this.options.to-this.options.from); + pos += this.options.from; + this.position = pos; + this.event('beforeUpdate'); + if(this.update) this.update(pos); + this.event('afterUpdate'); + }, + cancel: function() { + if(!this.options.sync) Effect.Queue.remove(this); + this.state = 'finished'; + }, + event: function(eventName) { + if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); + if(this.options[eventName]) this.options[eventName](this); + } +} + +Effect.Parallel = Class.create(); +Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { + initialize: function(effects) { + this.effects = effects || []; + this.start(arguments[1]); + }, + update: function(position) { + this.effects.invoke('render', position); + }, + finish: function(position) { + this.effects.each( function(effect) { + effect.render(1.0); + effect.cancel(); + effect.event('beforeFinish'); + if(effect.finish) effect.finish(position); + effect.event('afterFinish'); + }); + } +}); + +Effect.Opacity = Class.create(); +Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + // make this work on IE on elements without 'layout' + if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout)) + this.element.style.zoom = 1; + var options = Object.extend({ + from: Element.getOpacity(this.element) || 0.0, + to: 1.0 + }, arguments[1] || {}); + this.start(options); + }, + update: function(position) { + Element.setOpacity(this.element, position); + } +}); + +Effect.MoveBy = Class.create(); +Object.extend(Object.extend(Effect.MoveBy.prototype, Effect.Base.prototype), { + initialize: function(element, toTop, toLeft) { + this.element = $(element); + this.toTop = toTop; + this.toLeft = toLeft; + this.start(arguments[3]); + }, + setup: function() { + // Bug in Opera: Opera returns the "real" position of a static element or + // relative element that does not have top/left explicitly set. + // ==> Always set top and left for position relative elements in your stylesheets + // (to 0 if you do not need them) + + Element.makePositioned(this.element); + this.originalTop = parseFloat(Element.getStyle(this.element,'top') || '0'); + this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0'); + }, + update: function(position) { + var topd = this.toTop * position + this.originalTop; + var leftd = this.toLeft * position + this.originalLeft; + this.setPosition(topd, leftd); + }, + setPosition: function(topd, leftd) { + this.element.style.top = topd + "px"; + this.element.style.left = leftd + "px"; + } +}); + +Effect.Scale = Class.create(); +Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { + initialize: function(element, percent) { + this.element = $(element) + var options = Object.extend({ + scaleX: true, + scaleY: true, + scaleContent: true, + scaleFromCenter: false, + scaleMode: 'box', // 'box' or 'contents' or {} with provided values + scaleFrom: 100.0, + scaleTo: percent + }, arguments[2] || {}); + this.start(options); + }, + setup: function() { + var effect = this; + + this.restoreAfterFinish = this.options.restoreAfterFinish || false; + this.elementPositioning = Element.getStyle(this.element,'position'); + + effect.originalStyle = {}; + ['top','left','width','height','fontSize'].each( function(k) { + effect.originalStyle[k] = effect.element.style[k]; + }); + + this.originalTop = this.element.offsetTop; + this.originalLeft = this.element.offsetLeft; + + var fontSize = Element.getStyle(this.element,'font-size') || "100%"; + ['em','px','%'].each( function(fontSizeType) { + if(fontSize.indexOf(fontSizeType)>0) { + effect.fontSize = parseFloat(fontSize); + effect.fontSizeType = fontSizeType; + } + }); + + this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; + + this.dims = null; + if(this.options.scaleMode=='box') + this.dims = [this.element.clientHeight, this.element.clientWidth]; + if(this.options.scaleMode=='content') + this.dims = [this.element.scrollHeight, this.element.scrollWidth]; + if(!this.dims) + this.dims = [this.options.scaleMode.originalHeight, + this.options.scaleMode.originalWidth]; + }, + update: function(position) { + var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); + if(this.options.scaleContent && this.fontSize) + this.element.style.fontSize = this.fontSize*currentScale + this.fontSizeType; + this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); + }, + finish: function(position) { + if (this.restoreAfterFinish) { + var effect = this; + ['top','left','width','height','fontSize'].each( function(k) { + effect.element.style[k] = effect.originalStyle[k]; + }); + } + }, + setDimensions: function(height, width) { + var els = this.element.style; + if(this.options.scaleX) els.width = width + 'px'; + if(this.options.scaleY) els.height = height + 'px'; + if(this.options.scaleFromCenter) { + var topd = (height - this.dims[0])/2; + var leftd = (width - this.dims[1])/2; + if(this.elementPositioning == 'absolute') { + if(this.options.scaleY) els.top = this.originalTop-topd + "px"; + if(this.options.scaleX) els.left = this.originalLeft-leftd + "px"; + } else { + if(this.options.scaleY) els.top = -topd + "px"; + if(this.options.scaleX) els.left = -leftd + "px"; + } + } + } +}); + +Effect.Highlight = Class.create(); +Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + var options = Object.extend({ + startcolor: "#ffff99" + }, arguments[1] || {}); + this.start(options); + }, + setup: function() { + // Disable background image during the effect + this.oldBgImage = this.element.style.backgroundImage; + this.element.style.backgroundImage = "none"; + if(!this.options.endcolor) + this.options.endcolor = Element.getStyle(this.element, 'background-color').parseColor('#ffffff'); + if (typeof this.options.restorecolor == "undefined") + this.options.restorecolor = this.element.style.backgroundColor; + // init color calculations + this.colors_base = [ + parseInt(this.options.startcolor.slice(1,3),16), + parseInt(this.options.startcolor.slice(3,5),16), + parseInt(this.options.startcolor.slice(5),16) ]; + this.colors_delta = [ + parseInt(this.options.endcolor.slice(1,3),16)-this.colors_base[0], + parseInt(this.options.endcolor.slice(3,5),16)-this.colors_base[1], + parseInt(this.options.endcolor.slice(5),16)-this.colors_base[2]]; + }, + update: function(position) { + var effect = this; var colors = $R(0,2).map( function(i){ + return Math.round(effect.colors_base[i]+(effect.colors_delta[i]*position)) + }); + this.element.style.backgroundColor = "#" + + colors[0].toColorPart() + colors[1].toColorPart() + colors[2].toColorPart(); + }, + finish: function() { + this.element.style.backgroundColor = this.options.restorecolor; + this.element.style.backgroundImage = this.oldBgImage; + } +}); + +Effect.ScrollTo = Class.create(); +Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + this.start(arguments[1] || {}); + }, + setup: function() { + Position.prepare(); + var offsets = Position.cumulativeOffset(this.element); + var max = window.innerHeight ? + window.height - window.innerHeight : + document.body.scrollHeight - + (document.documentElement.clientHeight ? + document.documentElement.clientHeight : document.body.clientHeight); + this.scrollStart = Position.deltaY; + this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart; + }, + update: function(position) { + Position.prepare(); + window.scrollTo(Position.deltaX, + this.scrollStart + (position*this.delta)); + } +}); + +/* ------------- combination effects ------------- */ + +Effect.Fade = function(element) { + var oldOpacity = Element.getInlineOpacity(element); + var options = Object.extend({ + from: Element.getOpacity(element) || 1.0, + to: 0.0, + afterFinishInternal: function(effect) + { if (effect.options.to == 0) { + Element.hide(effect.element); + Element.setInlineOpacity(effect.element, oldOpacity); + } + } + }, arguments[1] || {}); + return new Effect.Opacity(element,options); +} + +Effect.Appear = function(element) { + var options = Object.extend({ + from: (Element.getStyle(element, "display") == "none" ? 0.0 : Element.getOpacity(element) || 0.0), + to: 1.0, + beforeSetup: function(effect) + { Element.setOpacity(effect.element, effect.options.from); + Element.show(effect.element); } + }, arguments[1] || {}); + return new Effect.Opacity(element,options); +} + +Effect.Puff = function(element) { + element = $(element); + var oldOpacity = Element.getInlineOpacity(element); + var oldPosition = element.style.position; + return new Effect.Parallel( + [ new Effect.Scale(element, 200, + { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], + Object.extend({ duration: 1.0, + beforeSetupInternal: function(effect) + { effect.effects[0].element.style.position = 'absolute'; }, + afterFinishInternal: function(effect) + { Element.hide(effect.effects[0].element); + effect.effects[0].element.style.position = oldPosition; + Element.setInlineOpacity(effect.effects[0].element, oldOpacity); } + }, arguments[1] || {}) + ); +} + +Effect.BlindUp = function(element) { + element = $(element); + Element.makeClipping(element); + return new Effect.Scale(element, 0, + Object.extend({ scaleContent: false, + scaleX: false, + restoreAfterFinish: true, + afterFinishInternal: function(effect) + { + Element.hide(effect.element); + Element.undoClipping(effect.element); + } + }, arguments[1] || {}) + ); +} + +Effect.BlindDown = function(element) { + element = $(element); + var oldHeight = element.style.height; + var elementDimensions = Element.getDimensions(element); + return new Effect.Scale(element, 100, + Object.extend({ scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + Element.makeClipping(effect.element); + effect.element.style.height = "0px"; + Element.show(effect.element); + }, + afterFinishInternal: function(effect) { + Element.undoClipping(effect.element); + effect.element.style.height = oldHeight; + } + }, arguments[1] || {}) + ); +} + +Effect.SwitchOff = function(element) { + element = $(element); + var oldOpacity = Element.getInlineOpacity(element); + return new Effect.Appear(element, { + duration: 0.4, + from: 0, + transition: Effect.Transitions.flicker, + afterFinishInternal: function(effect) { + new Effect.Scale(effect.element, 1, { + duration: 0.3, scaleFromCenter: true, + scaleX: false, scaleContent: false, restoreAfterFinish: true, + beforeSetup: function(effect) { + Element.makePositioned(effect.element); + Element.makeClipping(effect.element); + }, + afterFinishInternal: function(effect) { + Element.hide(effect.element); + Element.undoClipping(effect.element); + Element.undoPositioned(effect.element); + Element.setInlineOpacity(effect.element, oldOpacity); + } + }) + } + }); +} + +Effect.DropOut = function(element) { + element = $(element); + var oldTop = element.style.top; + var oldLeft = element.style.left; + var oldOpacity = Element.getInlineOpacity(element); + return new Effect.Parallel( + [ new Effect.MoveBy(element, 100, 0, { sync: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 }) ], + Object.extend( + { duration: 0.5, + beforeSetup: function(effect) { + Element.makePositioned(effect.effects[0].element); }, + afterFinishInternal: function(effect) { + Element.hide(effect.effects[0].element); + Element.undoPositioned(effect.effects[0].element); + effect.effects[0].element.style.left = oldLeft; + effect.effects[0].element.style.top = oldTop; + Element.setInlineOpacity(effect.effects[0].element, oldOpacity); } + }, arguments[1] || {})); +} + +Effect.Shake = function(element) { + element = $(element); + var oldTop = element.style.top; + var oldLeft = element.style.left; + return new Effect.MoveBy(element, 0, 20, + { duration: 0.05, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, -40, + { duration: 0.1, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, 40, + { duration: 0.1, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, -40, + { duration: 0.1, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, 40, + { duration: 0.1, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, -20, + { duration: 0.05, afterFinishInternal: function(effect) { + Element.undoPositioned(effect.element); + effect.element.style.left = oldLeft; + effect.element.style.top = oldTop; + }}) }}) }}) }}) }}) }}); +} + +Effect.SlideDown = function(element) { + element = $(element); + Element.cleanWhitespace(element); + // SlideDown need to have the content of the element wrapped in a container element with fixed height! + var oldInnerBottom = element.firstChild.style.bottom; + var elementDimensions = Element.getDimensions(element); + return new Effect.Scale(element, 100, + Object.extend({ scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + Element.makePositioned(effect.element.firstChild); + if (window.opera) effect.element.firstChild.style.top = ""; + Element.makeClipping(effect.element); + element.style.height = '0'; + Element.show(element); + }, + afterUpdateInternal: function(effect) { + effect.element.firstChild.style.bottom = + (effect.originalHeight - effect.element.clientHeight) + 'px'; }, + afterFinishInternal: function(effect) { + Element.undoClipping(effect.element); + Element.undoPositioned(effect.element.firstChild); + effect.element.firstChild.style.bottom = oldInnerBottom; } + }, arguments[1] || {}) + ); +} + +Effect.SlideUp = function(element) { + element = $(element); + Element.cleanWhitespace(element); + var oldInnerBottom = element.firstChild.style.bottom; + return new Effect.Scale(element, 0, + Object.extend({ scaleContent: false, + scaleX: false, + scaleMode: 'box', + scaleFrom: 100, + restoreAfterFinish: true, + beforeStartInternal: function(effect) { + Element.makePositioned(effect.element.firstChild); + if (window.opera) effect.element.firstChild.style.top = ""; + Element.makeClipping(effect.element); + Element.show(element); + }, + afterUpdateInternal: function(effect) { + effect.element.firstChild.style.bottom = + (effect.originalHeight - effect.element.clientHeight) + 'px'; }, + afterFinishInternal: function(effect) { + Element.hide(effect.element); + Element.undoClipping(effect.element); + Element.undoPositioned(effect.element.firstChild); + effect.element.firstChild.style.bottom = oldInnerBottom; } + }, arguments[1] || {}) + ); +} + +Effect.Squish = function(element) { + // Bug in opera makes the TD containing this element expand for a instance after finish + return new Effect.Scale(element, window.opera ? 1 : 0, + { restoreAfterFinish: true, + beforeSetup: function(effect) { + Element.makeClipping(effect.element); }, + afterFinishInternal: function(effect) { + Element.hide(effect.element); + Element.undoClipping(effect.element); } + }); +} + +Effect.Grow = function(element) { + element = $(element); + var options = arguments[1] || {}; + + var elementDimensions = Element.getDimensions(element); + var originalWidth = elementDimensions.width; + var originalHeight = elementDimensions.height; + var oldTop = element.style.top; + var oldLeft = element.style.left; + var oldHeight = element.style.height; + var oldWidth = element.style.width; + var oldOpacity = Element.getInlineOpacity(element); + + var direction = options.direction || 'center'; + var moveTransition = options.moveTransition || Effect.Transitions.sinoidal; + var scaleTransition = options.scaleTransition || Effect.Transitions.sinoidal; + var opacityTransition = options.opacityTransition || Effect.Transitions.full; + + var initialMoveX, initialMoveY; + var moveX, moveY; + + switch (direction) { + case 'top-left': + initialMoveX = initialMoveY = moveX = moveY = 0; + break; + case 'top-right': + initialMoveX = originalWidth; + initialMoveY = moveY = 0; + moveX = -originalWidth; + break; + case 'bottom-left': + initialMoveX = moveX = 0; + initialMoveY = originalHeight; + moveY = -originalHeight; + break; + case 'bottom-right': + initialMoveX = originalWidth; + initialMoveY = originalHeight; + moveX = -originalWidth; + moveY = -originalHeight; + break; + case 'center': + initialMoveX = originalWidth / 2; + initialMoveY = originalHeight / 2; + moveX = -originalWidth / 2; + moveY = -originalHeight / 2; + break; + } + + return new Effect.MoveBy(element, initialMoveY, initialMoveX, { + duration: 0.01, + beforeSetup: function(effect) { + Element.hide(effect.element); + Element.makeClipping(effect.element); + Element.makePositioned(effect.element); + }, + afterFinishInternal: function(effect) { + new Effect.Parallel( + [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: opacityTransition }), + new Effect.MoveBy(effect.element, moveY, moveX, { sync: true, transition: moveTransition }), + new Effect.Scale(effect.element, 100, { + scaleMode: { originalHeight: originalHeight, originalWidth: originalWidth }, + sync: true, scaleFrom: window.opera ? 1 : 0, transition: scaleTransition, restoreAfterFinish: true}) + ], Object.extend({ + beforeSetup: function(effect) { + effect.effects[0].element.style.height = 0; + Element.show(effect.effects[0].element); + }, + afterFinishInternal: function(effect) { + var el = effect.effects[0].element; + var els = el.style; + Element.undoClipping(el); + Element.undoPositioned(el); + els.top = oldTop; + els.left = oldLeft; + els.height = oldHeight; + els.width = originalWidth; + Element.setInlineOpacity(el, oldOpacity); + } + }, options) + ) + } + }); +} + +Effect.Shrink = function(element) { + element = $(element); + var options = arguments[1] || {}; + + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + var oldTop = element.style.top; + var oldLeft = element.style.left; + var oldHeight = element.style.height; + var oldWidth = element.style.width; + var oldOpacity = Element.getInlineOpacity(element); + + var direction = options.direction || 'center'; + var moveTransition = options.moveTransition || Effect.Transitions.sinoidal; + var scaleTransition = options.scaleTransition || Effect.Transitions.sinoidal; + var opacityTransition = options.opacityTransition || Effect.Transitions.none; + + var moveX, moveY; + + switch (direction) { + case 'top-left': + moveX = moveY = 0; + break; + case 'top-right': + moveX = originalWidth; + moveY = 0; + break; + case 'bottom-left': + moveX = 0; + moveY = originalHeight; + break; + case 'bottom-right': + moveX = originalWidth; + moveY = originalHeight; + break; + case 'center': + moveX = originalWidth / 2; + moveY = originalHeight / 2; + break; + } + + return new Effect.Parallel( + [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: opacityTransition }), + new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: scaleTransition, restoreAfterFinish: true}), + new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: moveTransition }) + ], Object.extend({ + beforeStartInternal: function(effect) { + Element.makePositioned(effect.effects[0].element); + Element.makeClipping(effect.effects[0].element); + }, + afterFinishInternal: function(effect) { + var el = effect.effects[0].element; + var els = el.style; + Element.hide(el); + Element.undoClipping(el); + Element.undoPositioned(el); + els.top = oldTop; + els.left = oldLeft; + els.height = oldHeight; + els.width = oldWidth; + Element.setInlineOpacity(el, oldOpacity); + } + }, options) + ); +} + +Effect.Pulsate = function(element) { + element = $(element); + var options = arguments[1] || {}; + var oldOpacity = Element.getInlineOpacity(element); + var transition = options.transition || Effect.Transitions.sinoidal; + var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) }; + reverser.bind(transition); + return new Effect.Opacity(element, + Object.extend(Object.extend({ duration: 3.0, from: 0, + afterFinishInternal: function(effect) { Element.setInlineOpacity(effect.element, oldOpacity); } + }, options), {transition: reverser})); +} + +Effect.Fold = function(element) { + element = $(element); + var originalTop = element.style.top; + var originalLeft = element.style.left; + var originalWidth = element.style.width; + var originalHeight = element.style.height; + Element.makeClipping(element); + return new Effect.Scale(element, 5, Object.extend({ + scaleContent: false, + scaleX: false, + afterFinishInternal: function(effect) { + new Effect.Scale(element, 1, { + scaleContent: false, + scaleY: false, + afterFinishInternal: function(effect) { + Element.hide(effect.element); + Element.undoClipping(effect.element); + effect.element.style.top = originalTop; + effect.element.style.left = originalLeft; + effect.element.style.width = originalWidth; + effect.element.style.height = originalHeight; + } }); + }}, arguments[1] || {})); +} Index: src/main/webapp/javascript/prototype.js =================================================================== diff -u --- src/main/webapp/javascript/prototype.js (revision 0) +++ src/main/webapp/javascript/prototype.js (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,2006 @@ +/* Prototype JavaScript framework, version 1.5.0_rc0 + * (c) 2005 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://prototype.conio.net/ + * +/*--------------------------------------------------------------------------*/ + +var Prototype = { + Version: '1.5.0_rc0', + ScriptFragment: '(?:)((\n|\r|.)*?)(?:<\/script>)', + + emptyFunction: function() {}, + K: function(x) {return x} +} + +var Class = { + create: function() { + return function() { + this.initialize.apply(this, arguments); + } + } +} + +var Abstract = new Object(); + +Object.extend = function(destination, source) { + for (var property in source) { + destination[property] = source[property]; + } + return destination; +} + +Object.inspect = function(object) { + try { + if (object == undefined) return 'undefined'; + if (object == null) return 'null'; + return object.inspect ? object.inspect() : object.toString(); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } +} + +Function.prototype.bind = function() { + var __method = this, args = $A(arguments), object = args.shift(); + return function() { + return __method.apply(object, args.concat($A(arguments))); + } +} + +Function.prototype.bindAsEventListener = function(object) { + var __method = this; + return function(event) { + return __method.call(object, event || window.event); + } +} + +Object.extend(Number.prototype, { + toColorPart: function() { + var digits = this.toString(16); + if (this < 16) return '0' + digits; + return digits; + }, + + succ: function() { + return this + 1; + }, + + times: function(iterator) { + $R(0, this, true).each(iterator); + return this; + } +}); + +var Try = { + these: function() { + var returnValue; + + for (var i = 0; i < arguments.length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) {} + } + + return returnValue; + } +} + +/*--------------------------------------------------------------------------*/ + +var PeriodicalExecuter = Class.create(); +PeriodicalExecuter.prototype = { + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.callback(); + } finally { + this.currentlyExecuting = false; + } + } + } +} +Object.extend(String.prototype, { + gsub: function(pattern, replacement) { + var result = '', source = this, match; + replacement = arguments.callee.prepareReplacement(replacement); + + while (source.length > 0) { + if (match = source.match(pattern)) { + result += source.slice(0, match.index); + result += (replacement(match) || '').toString(); + source = source.slice(match.index + match[0].length); + } else { + result += source, source = ''; + } + } + return result; + }, + + sub: function(pattern, replacement, count) { + replacement = this.gsub.prepareReplacement(replacement); + count = count === undefined ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + }, + + scan: function(pattern, iterator) { + this.gsub(pattern, iterator); + return this; + }, + + truncate: function(length, truncation) { + length = length || 30; + truncation = truncation === undefined ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : this; + }, + + strip: function() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + }, + + stripTags: function() { + return this.replace(/<\/?[^>]+>/gi, ''); + }, + + stripScripts: function() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + }, + + extractScripts: function() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); + var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + }, + + evalScripts: function() { + return this.extractScripts().map(function(script) { return eval(script) }); + }, + + escapeHTML: function() { + var div = document.createElement('div'); + var text = document.createTextNode(this); + div.appendChild(text); + return div.innerHTML; + }, + + unescapeHTML: function() { + var div = document.createElement('div'); + div.innerHTML = this.stripTags(); + return div.childNodes[0] ? div.childNodes[0].nodeValue : ''; + }, + + toQueryParams: function() { + var pairs = this.match(/^\??(.*)$/)[1].split('&'); + return pairs.inject({}, function(params, pairString) { + var pair = pairString.split('='); + params[pair[0]] = pair[1]; + return params; + }); + }, + + toArray: function() { + return this.split(''); + }, + + camelize: function() { + var oStringList = this.split('-'); + if (oStringList.length == 1) return oStringList[0]; + + var camelizedString = this.indexOf('-') == 0 + ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) + : oStringList[0]; + + for (var i = 1, len = oStringList.length; i < len; i++) { + var s = oStringList[i]; + camelizedString += s.charAt(0).toUpperCase() + s.substring(1); + } + + return camelizedString; + }, + + inspect: function() { + return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'"; + } +}); + +String.prototype.gsub.prepareReplacement = function(replacement) { + if (typeof replacement == 'function') return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; +} + +String.prototype.parseQuery = String.prototype.toQueryParams; + +var Template = Class.create(); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; +Template.prototype = { + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + return this.template.gsub(this.pattern, function(match) { + var before = match[1]; + if (before == '\\') return match[2]; + return before + (object[match[3]] || '').toString(); + }); + } +} + +var $break = new Object(); +var $continue = new Object(); + +var Enumerable = { + each: function(iterator) { + var index = 0; + try { + this._each(function(value) { + try { + iterator(value, index++); + } catch (e) { + if (e != $continue) throw e; + } + }); + } catch (e) { + if (e != $break) throw e; + } + }, + + all: function(iterator) { + var result = true; + this.each(function(value, index) { + result = result && !!(iterator || Prototype.K)(value, index); + if (!result) throw $break; + }); + return result; + }, + + any: function(iterator) { + var result = true; + this.each(function(value, index) { + if (result = !!(iterator || Prototype.K)(value, index)) + throw $break; + }); + return result; + }, + + collect: function(iterator) { + var results = []; + this.each(function(value, index) { + results.push(iterator(value, index)); + }); + return results; + }, + + detect: function (iterator) { + var result; + this.each(function(value, index) { + if (iterator(value, index)) { + result = value; + throw $break; + } + }); + return result; + }, + + findAll: function(iterator) { + var results = []; + this.each(function(value, index) { + if (iterator(value, index)) + results.push(value); + }); + return results; + }, + + grep: function(pattern, iterator) { + var results = []; + this.each(function(value, index) { + var stringValue = value.toString(); + if (stringValue.match(pattern)) + results.push((iterator || Prototype.K)(value, index)); + }) + return results; + }, + + include: function(object) { + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + }, + + inject: function(memo, iterator) { + this.each(function(value, index) { + memo = iterator(memo, value, index); + }); + return memo; + }, + + invoke: function(method) { + var args = $A(arguments).slice(1); + return this.collect(function(value) { + return value[method].apply(value, args); + }); + }, + + max: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (result == undefined || value >= result) + result = value; + }); + return result; + }, + + min: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (result == undefined || value < result) + result = value; + }); + return result; + }, + + partition: function(iterator) { + var trues = [], falses = []; + this.each(function(value, index) { + ((iterator || Prototype.K)(value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + }, + + pluck: function(property) { + var results = []; + this.each(function(value, index) { + results.push(value[property]); + }); + return results; + }, + + reject: function(iterator) { + var results = []; + this.each(function(value, index) { + if (!iterator(value, index)) + results.push(value); + }); + return results; + }, + + sortBy: function(iterator) { + return this.collect(function(value, index) { + return {value: value, criteria: iterator(value, index)}; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + }, + + toArray: function() { + return this.collect(Prototype.K); + }, + + zip: function() { + var iterator = Prototype.K, args = $A(arguments); + if (typeof args.last() == 'function') + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + return iterator(collections.pluck(index)); + }); + }, + + inspect: function() { + return '#'; + } +} + +Object.extend(Enumerable, { + map: Enumerable.collect, + find: Enumerable.detect, + select: Enumerable.findAll, + member: Enumerable.include, + entries: Enumerable.toArray +}); +var $A = Array.from = function(iterable) { + if (!iterable) return []; + if (iterable.toArray) { + return iterable.toArray(); + } else { + var results = []; + for (var i = 0; i < iterable.length; i++) + results.push(iterable[i]); + return results; + } +} + +Object.extend(Array.prototype, Enumerable); + +if (!Array.prototype._reverse) + Array.prototype._reverse = Array.prototype.reverse; + +Object.extend(Array.prototype, { + _each: function(iterator) { + for (var i = 0; i < this.length; i++) + iterator(this[i]); + }, + + clear: function() { + this.length = 0; + return this; + }, + + first: function() { + return this[0]; + }, + + last: function() { + return this[this.length - 1]; + }, + + compact: function() { + return this.select(function(value) { + return value != undefined || value != null; + }); + }, + + flatten: function() { + return this.inject([], function(array, value) { + return array.concat(value && value.constructor == Array ? + value.flatten() : [value]); + }); + }, + + without: function() { + var values = $A(arguments); + return this.select(function(value) { + return !values.include(value); + }); + }, + + indexOf: function(object) { + for (var i = 0; i < this.length; i++) + if (this[i] == object) return i; + return -1; + }, + + reverse: function(inline) { + return (inline !== false ? this : this.toArray())._reverse(); + }, + + inspect: function() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + } +}); +var Hash = { + _each: function(iterator) { + for (var key in this) { + var value = this[key]; + if (typeof value == 'function') continue; + + var pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + }, + + keys: function() { + return this.pluck('key'); + }, + + values: function() { + return this.pluck('value'); + }, + + merge: function(hash) { + return $H(hash).inject($H(this), function(mergedHash, pair) { + mergedHash[pair.key] = pair.value; + return mergedHash; + }); + }, + + toQueryString: function() { + return this.map(function(pair) { + return pair.map(encodeURIComponent).join('='); + }).join('&'); + }, + + inspect: function() { + return '#'; + } +} + +function $H(object) { + var hash = Object.extend({}, object || {}); + Object.extend(hash, Enumerable); + Object.extend(hash, Hash); + return hash; +} +ObjectRange = Class.create(); +Object.extend(ObjectRange.prototype, Enumerable); +Object.extend(ObjectRange.prototype, { + initialize: function(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + }, + + _each: function(iterator) { + var value = this.start; + do { + iterator(value); + value = value.succ(); + } while (this.include(value)); + }, + + include: function(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } +}); + +var $R = function(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new XMLHttpRequest()}, + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')} + ) || false; + }, + + activeRequestCount: 0 +} + +Ajax.Responders = { + responders: [], + + _each: function(iterator) { + this.responders._each(iterator); + }, + + register: function(responderToAdd) { + if (!this.include(responderToAdd)) + this.responders.push(responderToAdd); + }, + + unregister: function(responderToRemove) { + this.responders = this.responders.without(responderToRemove); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (responder[callback] && typeof responder[callback] == 'function') { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) {} + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { + Ajax.activeRequestCount++; + }, + + onComplete: function() { + Ajax.activeRequestCount--; + } +}); + +Ajax.Base = function() {}; +Ajax.Base.prototype = { + setOptions: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + parameters: '' + } + Object.extend(this.options, options || {}); + }, + + responseIsSuccess: function() { + return this.transport.status == undefined + || this.transport.status == 0 + || (this.transport.status >= 200 && this.transport.status < 300); + }, + + responseIsFailure: function() { + return !this.responseIsSuccess(); + } +} + +Ajax.Request = Class.create(); +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Request.prototype = Object.extend(new Ajax.Base(), { + initialize: function(url, options) { + this.transport = Ajax.getTransport(); + this.setOptions(options); + this.request(url); + }, + + request: function(url) { + var parameters = this.options.parameters || ''; + if (parameters.length > 0) parameters += '&_='; + + try { + this.url = url; + if (this.options.method == 'get' && parameters.length > 0) + this.url += (this.url.match(/\?/) ? '&' : '?') + parameters; + + Ajax.Responders.dispatch('onCreate', this, this.transport); + + this.transport.open(this.options.method, this.url, + this.options.asynchronous); + + if (this.options.asynchronous) { + this.transport.onreadystatechange = this.onStateChange.bind(this); + setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); + } + + this.setRequestHeaders(); + + var body = this.options.postBody ? this.options.postBody : parameters; + this.transport.send(this.options.method == 'post' ? body : null); + + } catch (e) { + this.dispatchException(e); + } + }, + + setRequestHeaders: function() { + var requestHeaders = + ['X-Requested-With', 'XMLHttpRequest', + 'X-Prototype-Version', Prototype.Version, + 'Accept', 'text/javascript, text/html, application/xml, text/xml, */*']; + + if (this.options.method == 'post') { + requestHeaders.push('Content-type', this.options.contentType); + + /* Force "Connection: close" for Mozilla browsers to work around + * a bug where XMLHttpReqeuest sends an incorrect Content-length + * header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType) + requestHeaders.push('Connection', 'close'); + } + + if (this.options.requestHeaders) + requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); + + for (var i = 0; i < requestHeaders.length; i += 2) + this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState != 1) + this.respondToReadyState(this.transport.readyState); + }, + + header: function(name) { + try { + return this.transport.getResponseHeader(name); + } catch (e) {} + }, + + evalJSON: function() { + try { + return eval('(' + this.header('X-JSON') + ')'); + } catch (e) {} + }, + + evalResponse: function() { + try { + return eval(this.transport.responseText); + } catch (e) { + this.dispatchException(e); + } + }, + + respondToReadyState: function(readyState) { + var event = Ajax.Request.Events[readyState]; + var transport = this.transport, json = this.evalJSON(); + + if (event == 'Complete') { + try { + (this.options['on' + this.transport.status] + || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(transport, json); + } catch (e) { + this.dispatchException(e); + } + + if ((this.header('Content-type') || '').match(/^text\/javascript/i)) + this.evalResponse(); + } + + try { + (this.options['on' + event] || Prototype.emptyFunction)(transport, json); + Ajax.Responders.dispatch('on' + event, this, transport, json); + } catch (e) { + this.dispatchException(e); + } + + /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ + if (event == 'Complete') + this.transport.onreadystatechange = Prototype.emptyFunction; + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Updater = Class.create(); + +Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { + initialize: function(container, url, options) { + this.containers = { + success: container.success ? $(container.success) : $(container), + failure: container.failure ? $(container.failure) : + (container.success ? null : $(container)) + } + + this.transport = Ajax.getTransport(); + this.setOptions(options); + + var onComplete = this.options.onComplete || Prototype.emptyFunction; + this.options.onComplete = (function(transport, object) { + this.updateContent(); + onComplete(transport, object); + }).bind(this); + + this.request(url); + }, + + updateContent: function() { + var receiver = this.responseIsSuccess() ? + this.containers.success : this.containers.failure; + var response = this.transport.responseText; + + if (!this.options.evalScripts) + response = response.stripScripts(); + + if (receiver) { + if (this.options.insertion) { + new this.options.insertion(receiver, response); + } else { + Element.update(receiver, response); + } + } + + if (this.responseIsSuccess()) { + if (this.onComplete) + setTimeout(this.onComplete.bind(this), 10); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(); +Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { + initialize: function(container, url, options) { + this.setOptions(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = {}; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(request) { + if (this.options.decay) { + this.decay = (request.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = request.responseText; + } + this.timer = setTimeout(this.onTimerEvent.bind(this), + this.decay * this.frequency * 1000); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); +function $() { + var results = [], element; + for (var i = 0; i < arguments.length; i++) { + element = arguments[i]; + if (typeof element == 'string') + element = document.getElementById(element); + results.push(Element.extend(element)); + } + return results.length < 2 ? results[0] : results; +} + +document.getElementsByClassName = function(className, parentElement) { + var children = ($(parentElement) || document.body).getElementsByTagName('*'); + return $A(children).inject([], function(elements, child) { + if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) + elements.push(Element.extend(child)); + return elements; + }); +} + +/*--------------------------------------------------------------------------*/ + +if (!window.Element) + var Element = new Object(); + +Element.extend = function(element) { + if (!element) return; + if (_nativeExtensions) return element; + + if (!element._extended && element.tagName && element != window) { + var methods = Element.Methods, cache = Element.extend.cache; + for (property in methods) { + var value = methods[property]; + if (typeof value == 'function') + element[property] = cache.findOrStore(value); + } + } + + element._extended = true; + return element; +} + +Element.extend.cache = { + findOrStore: function(value) { + return this[value] = this[value] || function() { + return value.apply(null, [this].concat($A(arguments))); + } + } +} + +Element.Methods = { + visible: function(element) { + return $(element).style.display != 'none'; + }, + + toggle: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + Element[Element.visible(element) ? 'hide' : 'show'](element); + } + }, + + hide: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = 'none'; + } + }, + + show: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = ''; + } + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + }, + + update: function(element, html) { + $(element).innerHTML = html.stripScripts(); + setTimeout(function() {html.evalScripts()}, 10); + }, + + replace: function(element, html) { + element = $(element); + if (element.outerHTML) { + element.outerHTML = html.stripScripts(); + } else { + var range = element.ownerDocument.createRange(); + range.selectNodeContents(element); + element.parentNode.replaceChild( + range.createContextualFragment(html.stripScripts()), element); + } + setTimeout(function() {html.evalScripts()}, 10); + }, + + getHeight: function(element) { + element = $(element); + return element.offsetHeight; + }, + + classNames: function(element) { + return new Element.ClassNames(element); + }, + + hasClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).include(className); + }, + + addClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).add(className); + }, + + removeClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).remove(className); + }, + + // removes whitespace-only text node children + cleanWhitespace: function(element) { + element = $(element); + for (var i = 0; i < element.childNodes.length; i++) { + var node = element.childNodes[i]; + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) + Element.remove(node); + } + }, + + empty: function(element) { + return $(element).innerHTML.match(/^\s*$/); + }, + + childOf: function(element, ancestor) { + element = $(element), ancestor = $(ancestor); + while (element = element.parentNode) + if (element == ancestor) return true; + return false; + }, + + scrollTo: function(element) { + element = $(element); + var x = element.x ? element.x : element.offsetLeft, + y = element.y ? element.y : element.offsetTop; + window.scrollTo(x, y); + }, + + getStyle: function(element, style) { + element = $(element); + var value = element.style[style.camelize()]; + if (!value) { + if (document.defaultView && document.defaultView.getComputedStyle) { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css.getPropertyValue(style) : null; + } else if (element.currentStyle) { + value = element.currentStyle[style.camelize()]; + } + } + + if (window.opera && ['left', 'top', 'right', 'bottom'].include(style)) + if (Element.getStyle(element, 'position') == 'static') value = 'auto'; + + return value == 'auto' ? null : value; + }, + + setStyle: function(element, style) { + element = $(element); + for (var name in style) + element.style[name.camelize()] = style[name]; + }, + + getDimensions: function(element) { + element = $(element); + if (Element.getStyle(element, 'display') != 'none') + return {width: element.offsetWidth, height: element.offsetHeight}; + + // All *Width and *Height properties give 0 on elements with display none, + // so enable the element temporarily + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + els.visibility = 'hidden'; + els.position = 'absolute'; + els.display = ''; + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + els.display = 'none'; + els.position = originalPosition; + els.visibility = originalVisibility; + return {width: originalWidth, height: originalHeight}; + }, + + makePositioned: function(element) { + element = $(element); + var pos = Element.getStyle(element, 'position'); + if (pos == 'static' || !pos) { + element._madePositioned = true; + element.style.position = 'relative'; + // Opera returns the offset relative to the positioning context, when an + // element is position relative but top and left have not been defined + if (window.opera) { + element.style.top = 0; + element.style.left = 0; + } + } + }, + + undoPositioned: function(element) { + element = $(element); + if (element._madePositioned) { + element._madePositioned = undefined; + element.style.position = + element.style.top = + element.style.left = + element.style.bottom = + element.style.right = ''; + } + }, + + makeClipping: function(element) { + element = $(element); + if (element._overflow) return; + element._overflow = element.style.overflow; + if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') + element.style.overflow = 'hidden'; + }, + + undoClipping: function(element) { + element = $(element); + if (element._overflow) return; + element.style.overflow = element._overflow; + element._overflow = undefined; + } +} + +Object.extend(Element, Element.Methods); + +var _nativeExtensions = false; + +if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) { + var HTMLElement = {} + HTMLElement.prototype = document.createElement('div').__proto__; +} + +Element.addMethods = function(methods) { + Object.extend(Element.Methods, methods || {}); + + if(typeof HTMLElement != 'undefined') { + var methods = Element.Methods, cache = Element.extend.cache; + for (property in methods) { + var value = methods[property]; + if (typeof value == 'function') + HTMLElement.prototype[property] = cache.findOrStore(value); + } + _nativeExtensions = true; + } +} + +Element.addMethods(); + +var Toggle = new Object(); +Toggle.display = Element.toggle; + +/*--------------------------------------------------------------------------*/ + +Abstract.Insertion = function(adjacency) { + this.adjacency = adjacency; +} + +Abstract.Insertion.prototype = { + initialize: function(element, content) { + this.element = $(element); + this.content = content.stripScripts(); + + if (this.adjacency && this.element.insertAdjacentHTML) { + try { + this.element.insertAdjacentHTML(this.adjacency, this.content); + } catch (e) { + var tagName = this.element.tagName.toLowerCase(); + if (tagName == 'tbody' || tagName == 'tr') { + this.insertContent(this.contentFromAnonymousTable()); + } else { + throw e; + } + } + } else { + this.range = this.element.ownerDocument.createRange(); + if (this.initializeRange) this.initializeRange(); + this.insertContent([this.range.createContextualFragment(this.content)]); + } + + setTimeout(function() {content.evalScripts()}, 10); + }, + + contentFromAnonymousTable: function() { + var div = document.createElement('div'); + div.innerHTML = '' + this.content + '
'; + return $A(div.childNodes[0].childNodes[0].childNodes); + } +} + +var Insertion = new Object(); + +Insertion.Before = Class.create(); +Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { + initializeRange: function() { + this.range.setStartBefore(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, this.element); + }).bind(this)); + } +}); + +Insertion.Top = Class.create(); +Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(true); + }, + + insertContent: function(fragments) { + fragments.reverse(false).each((function(fragment) { + this.element.insertBefore(fragment, this.element.firstChild); + }).bind(this)); + } +}); + +Insertion.Bottom = Class.create(); +Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.appendChild(fragment); + }).bind(this)); + } +}); + +Insertion.After = Class.create(); +Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { + initializeRange: function() { + this.range.setStartAfter(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, + this.element.nextSibling); + }).bind(this)); + } +}); + +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator) { + this.element.className.split(/\s+/).select(function(name) { + return name.length > 0; + })._each(iterator); + }, + + set: function(className) { + this.element.className = className; + }, + + add: function(classNameToAdd) { + if (this.include(classNameToAdd)) return; + this.set(this.toArray().concat(classNameToAdd).join(' ')); + }, + + remove: function(classNameToRemove) { + if (!this.include(classNameToRemove)) return; + this.set(this.select(function(className) { + return className != classNameToRemove; + }).join(' ')); + }, + + toString: function() { + return this.toArray().join(' '); + } +} + +Object.extend(Element.ClassNames.prototype, Enumerable); +var Selector = Class.create(); +Selector.prototype = { + initialize: function(expression) { + this.params = {classNames: []}; + this.expression = expression.toString().strip(); + this.parseExpression(); + this.compileMatcher(); + }, + + parseExpression: function() { + function abort(message) { throw 'Parse error in selector: ' + message; } + + if (this.expression == '') abort('empty expression'); + + var params = this.params, expr = this.expression, match, modifier, clause, rest; + while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) { + params.attributes = params.attributes || []; + params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''}); + expr = match[1]; + } + + if (expr == '*') return this.params.wildcard = true; + + while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) { + modifier = match[1], clause = match[2], rest = match[3]; + switch (modifier) { + case '#': params.id = clause; break; + case '.': params.classNames.push(clause); break; + case '': + case undefined: params.tagName = clause.toUpperCase(); break; + default: abort(expr.inspect()); + } + expr = rest; + } + + if (expr.length > 0) abort(expr.inspect()); + }, + + buildMatchExpression: function() { + var params = this.params, conditions = [], clause; + + if (params.wildcard) + conditions.push('true'); + if (clause = params.id) + conditions.push('element.id == ' + clause.inspect()); + if (clause = params.tagName) + conditions.push('element.tagName.toUpperCase() == ' + clause.inspect()); + if ((clause = params.classNames).length > 0) + for (var i = 0; i < clause.length; i++) + conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')'); + if (clause = params.attributes) { + clause.each(function(attribute) { + var value = 'element.getAttribute(' + attribute.name.inspect() + ')'; + var splitValueBy = function(delimiter) { + return value + ' && ' + value + '.split(' + delimiter.inspect() + ')'; + } + + switch (attribute.operator) { + case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break; + case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break; + case '|=': conditions.push( + splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect() + ); break; + case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break; + case '': + case undefined: conditions.push(value + ' != null'); break; + default: throw 'Unknown operator ' + attribute.operator + ' in selector'; + } + }); + } + + return conditions.join(' && '); + }, + + compileMatcher: function() { + this.match = new Function('element', 'if (!element.tagName) return false; \ + return ' + this.buildMatchExpression()); + }, + + findElements: function(scope) { + var element; + + if (element = $(this.params.id)) + if (this.match(element)) + if (!scope || Element.childOf(element, scope)) + return [element]; + + scope = (scope || document).getElementsByTagName(this.params.tagName || '*'); + + var results = []; + for (var i = 0; i < scope.length; i++) + if (this.match(element = scope[i])) + results.push(Element.extend(element)); + + return results; + }, + + toString: function() { + return this.expression; + } +} + +function $$() { + return $A(arguments).map(function(expression) { + return expression.strip().split(/\s+/).inject([null], function(results, expr) { + var selector = new Selector(expr); + return results.map(selector.findElements.bind(selector)).flatten(); + }); + }).flatten(); +} +var Field = { + clear: function() { + for (var i = 0; i < arguments.length; i++) + $(arguments[i]).value = ''; + }, + + focus: function(element) { + $(element).focus(); + }, + + present: function() { + for (var i = 0; i < arguments.length; i++) + if ($(arguments[i]).value == '') return false; + return true; + }, + + select: function(element) { + $(element).select(); + }, + + activate: function(element) { + element = $(element); + element.focus(); + if (element.select) + element.select(); + } +} + +/*--------------------------------------------------------------------------*/ + +var Form = { + serialize: function(form) { + var elements = Form.getElements($(form)); + var queryComponents = new Array(); + + for (var i = 0; i < elements.length; i++) { + var queryComponent = Form.Element.serialize(elements[i]); + if (queryComponent) + queryComponents.push(queryComponent); + } + + return queryComponents.join('&'); + }, + + getElements: function(form) { + form = $(form); + var elements = new Array(); + + for (var tagName in Form.Element.Serializers) { + var tagElements = form.getElementsByTagName(tagName); + for (var j = 0; j < tagElements.length; j++) + elements.push(tagElements[j]); + } + return elements; + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) + return inputs; + + var matchingInputs = new Array(); + for (var i = 0; i < inputs.length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || + (name && input.name != name)) + continue; + matchingInputs.push(input); + } + + return matchingInputs; + }, + + disable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.blur(); + element.disabled = 'true'; + } + }, + + enable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.disabled = ''; + } + }, + + findFirstElement: function(form) { + return Form.getElements(form).find(function(element) { + return element.type != 'hidden' && !element.disabled && + ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); + }); + }, + + focusFirstElement: function(form) { + Field.activate(Form.findFirstElement(form)); + }, + + reset: function(form) { + $(form).reset(); + } +} + +Form.Element = { + serialize: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) { + var key = encodeURIComponent(parameter[0]); + if (key.length == 0) return; + + if (parameter[1].constructor != Array) + parameter[1] = [parameter[1]]; + + return parameter[1].map(function(value) { + return key + '=' + encodeURIComponent(value); + }).join('&'); + } + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) + return parameter[1]; + } +} + +Form.Element.Serializers = { + input: function(element) { + switch (element.type.toLowerCase()) { + case 'submit': + case 'hidden': + case 'password': + case 'text': + return Form.Element.Serializers.textarea(element); + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element); + } + return false; + }, + + inputSelector: function(element) { + if (element.checked) + return [element.name, element.value]; + }, + + textarea: function(element) { + return [element.name, element.value]; + }, + + select: function(element) { + return Form.Element.Serializers[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + }, + + selectOne: function(element) { + var value = '', opt, index = element.selectedIndex; + if (index >= 0) { + opt = element.options[index]; + value = opt.value || opt.text; + } + return [element.name, value]; + }, + + selectMany: function(element) { + var value = []; + for (var i = 0; i < element.length; i++) { + var opt = element.options[i]; + if (opt.selected) + value.push(opt.value || opt.text); + } + return [element.name, value]; + } +} + +/*--------------------------------------------------------------------------*/ + +var $F = Form.Element.getValue; + +/*--------------------------------------------------------------------------*/ + +Abstract.TimedObserver = function() {} +Abstract.TimedObserver.prototype = { + initialize: function(element, frequency, callback) { + this.frequency = frequency; + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + } +} + +Form.Element.Observer = Class.create(); +Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(); +Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = function() {} +Abstract.EventObserver.prototype = { + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + var elements = Form.getElements(this.element); + for (var i = 0; i < elements.length; i++) + this.registerCallback(elements[i]); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + case 'password': + case 'text': + case 'textarea': + case 'select-one': + case 'select-multiple': + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +} + +Form.Element.EventObserver = Class.create(); +Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(); +Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); +if (!window.Event) { + var Event = new Object(); +} + +Object.extend(Event, { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + + element: function(event) { + return event.target || event.srcElement; + }, + + isLeftClick: function(event) { + return (((event.which) && (event.which == 1)) || + ((event.button) && (event.button == 1))); + }, + + pointerX: function(event) { + return event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)); + }, + + pointerY: function(event) { + return event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)); + }, + + stop: function(event) { + if (event.preventDefault) { + event.preventDefault(); + event.stopPropagation(); + } else { + event.returnValue = false; + event.cancelBubble = true; + } + }, + + // find the first node with the given tagName, starting from the + // node the event was triggered on; traverses the DOM upwards + findElement: function(event, tagName) { + var element = Event.element(event); + while (element.parentNode && (!element.tagName || + (element.tagName.toUpperCase() != tagName.toUpperCase()))) + element = element.parentNode; + return element; + }, + + observers: false, + + _observeAndCache: function(element, name, observer, useCapture) { + if (!this.observers) this.observers = []; + if (element.addEventListener) { + this.observers.push([element, name, observer, useCapture]); + element.addEventListener(name, observer, useCapture); + } else if (element.attachEvent) { + this.observers.push([element, name, observer, useCapture]); + element.attachEvent('on' + name, observer); + } + }, + + unloadCache: function() { + if (!Event.observers) return; + for (var i = 0; i < Event.observers.length; i++) { + Event.stopObserving.apply(this, Event.observers[i]); + Event.observers[i][0] = null; + } + Event.observers = false; + }, + + observe: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) + || element.attachEvent)) + name = 'keydown'; + + this._observeAndCache(element, name, observer, useCapture); + }, + + stopObserving: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) + || element.detachEvent)) + name = 'keydown'; + + if (element.removeEventListener) { + element.removeEventListener(name, observer, useCapture); + } else if (element.detachEvent) { + element.detachEvent('on' + name, observer); + } + } +}); + +/* prevent memory leaks in IE */ +if (navigator.appVersion.match(/\bMSIE\b/)) + Event.observe(window, 'unload', Event.unloadCache, false); +var Position = { + // set to true if needed, warning: firefox performance problems + // NOT neeeded for page scrolling, only if draggable contained in + // scrollable elements + includeScrollOffsets: false, + + // must be called before calling withinIncludingScrolloffset, every time the + // page is scrolled + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + realOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return [valueL, valueT]; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return [valueL, valueT]; + }, + + positionedOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + p = Element.getStyle(element, 'position'); + if (p == 'relative' || p == 'absolute') break; + } + } while (element); + return [valueL, valueT]; + }, + + offsetParent: function(element) { + if (element.offsetParent) return element.offsetParent; + if (element == document.body) return element; + + while ((element = element.parentNode) && element != document.body) + if (Element.getStyle(element, 'position') != 'static') + return element; + + return document.body; + }, + + // caches x/y coordinate pair to use with overlap + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = this.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = this.realOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = this.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + // within must be called directly before + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + clone: function(source, target) { + source = $(source); + target = $(target); + target.style.position = 'absolute'; + var offsets = this.cumulativeOffset(source); + target.style.top = offsets[1] + 'px'; + target.style.left = offsets[0] + 'px'; + target.style.width = source.offsetWidth + 'px'; + target.style.height = source.offsetHeight + 'px'; + }, + + page: function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + // Safari fix + if (element.offsetParent==document.body) + if (Element.getStyle(element,'position')=='absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } while (element = element.parentNode); + + return [valueL, valueT]; + }, + + clone: function(source, target) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || {}) + + // find page position of source + source = $(source); + var p = Position.page(source); + + // find coordinate system to use + target = $(target); + var delta = [0, 0]; + var parent = null; + // delta [0,0] will do fine with position: fixed elements, + // position:absolute needs offsetParent deltas + if (Element.getStyle(target,'position') == 'absolute') { + parent = Position.offsetParent(target); + delta = Position.page(parent); + } + + // correct by body offsets (fixes Safari) + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + // set position + if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if(options.setWidth) target.style.width = source.offsetWidth + 'px'; + if(options.setHeight) target.style.height = source.offsetHeight + 'px'; + }, + + absolutize: function(element) { + element = $(element); + if (element.style.position == 'absolute') return; + Position.prepare(); + + var offsets = Position.positionedOffset(element); + var top = offsets[1]; + var left = offsets[0]; + var width = element.clientWidth; + var height = element.clientHeight; + + element._originalLeft = left - parseFloat(element.style.left || 0); + element._originalTop = top - parseFloat(element.style.top || 0); + element._originalWidth = element.style.width; + element._originalHeight = element.style.height; + + element.style.position = 'absolute'; + element.style.top = top + 'px';; + element.style.left = left + 'px';; + element.style.width = width + 'px';; + element.style.height = height + 'px';; + }, + + relativize: function(element) { + element = $(element); + if (element.style.position == 'relative') return; + Position.prepare(); + + element.style.position = 'relative'; + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.height = element._originalHeight; + element.style.width = element._originalWidth; + } +} + +// Safari returns margins on body which is incorrect if the child is absolutely +// positioned. For performance reasons, redefine Position.cumulativeOffset for +// KHTML/WebKit only. +if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { + Position.cumulativeOffset = function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) + if (Element.getStyle(element, 'position') == 'absolute') break; + + element = element.offsetParent; + } while (element); + + return [valueL, valueT]; + } +} \ No newline at end of file Index: src/main/webapp/javascript/site.js =================================================================== diff -u --- src/main/webapp/javascript/site.js (revision 0) +++ src/main/webapp/javascript/site.js (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,352 @@ +var OLD_TIME_OUT; //so we can cancel the timeout +var FADE_EFFECT; //so we can cancel the fade effect +var IS_VALUE_CHANGED = false; +//Used to check for confirmation before we cancel or redirect + +function checkGroup(){ + var selectedGroups = $('groupId').options; + var newGroup = $('ActiveDirectoryGroups').options[$('ActiveDirectoryGroups').options.selectedIndex].text; + for(var x=0; x < selectedGroups.length; x++){ + if(newGroup == selectedGroups[x].text){ + alert('The group you choose to import has aleady been imported.'); + return false; + } + } + return true; +} + +function findPos(obj) { + var curleft = curtop = 0; + if (obj.offsetParent) { + curleft = obj.offsetLeft + curtop = obj.offsetTop + while (obj = obj.offsetParent) { + curleft += obj.offsetLeft + curtop += obj.offsetTop + } + } + return [curleft,curtop]; +} + +function showPopUpMenu(thisObject){ + var thisMenu = thisObject.getElementsByTagName('div')[0]; + //thisMenu.style.left = -150; + thisMenu.style.display=''; + thisMenu = thisMenu.getElementsByTagName('div')[0]; + thisMenuHeight = thisMenu.getElementsByTagName('ul')[0].offsetHeight; + if(thisMenuHeight > thisMenu.offsetHeight){ + thisMenuHeight = thisMenu.offsetHeight; + } + if(findPos(thisObject)[1] + thisMenuHeight > 500){ + thisMenu.style.top = (thisMenuHeight + 0) * -1; + } +} + +function findOption(SelectedOptions, newOption){ + for(var x=0; x < SelectedOptions.length; x++){ + if(newOption == SelectedOptions[x].value){ + return true; + } + } + return false; +} + +function addOptions(from, to){ + var allOptions = from.options; + var selectedOptions = to.options; + var previousOptions = to.cloneNode(true); + previousOptions = previousOptions.options; + //this is a clone of the original selected options that we will check against to see if the option has already been added + //we don't need to check the new options we are adding so we use a clone with only the options that were there when we started - to speed things up + var showAlert = false; + for(var i=0; i < allOptions.length; i++){ + if(allOptions[i].selected){ + if(!findOption(previousOptions,allOptions[i].value)){ + selectedOptions[selectedOptions.length] = new Option(allOptions[i].text,allOptions[i].value); + }else{ + showAlert = true; + } + } + } + if(showAlert == true){ + alert('One or more of the selected values could not be added because they were already selected.'); + } + IS_VALUE_CHANGED = true; +} + +function removeOptions(from){ + IS_VALUE_CHANGED = true; + var selectedOptions = from.options; + for (var i=selectedOptions.length-1; i >= 0 ; i--){ + if (selectedOptions[i].selected){ + selectedOptions[i] = null; + } + } +} + +function selectOptions(from){ + var selectedOptions = from.options; + for (var i=0; i < selectedOptions.length; i++){ + selectedOptions[i].selected = true; + } +} + +function hideObj(objName){ + if($(objName)){ + $(objName).style.display = "none"; + } +} + +function hideMessage(obj){ + if(FADE_EFFECT){ + FADE_EFFECT.cancel() + } + if($('jsError').className == 'jsError'){ + //the jsError div is always there but this class is only set when displayed + FADE_EFFECT = new Effect.Highlight("jsError", {startcolor: "#FF0000", endcolor: "#FFFFFF", restorecolor: "#FFFFFF", duration: 2, afterFinish: function(){hideObj("jsError")}}); + }else if($('error')){ + FADE_EFFECT = new Effect.Highlight("error", {startcolor: "#FF0000", endcolor: "#FFFFFF", restorecolor: "#FFFFFF", duration: 2, afterFinish: function(){hideObj("error")}}); + }else if($('success')){ + FADE_EFFECT = new Effect.Highlight("success", {startcolor: "#009900", endcolor: "#FFFFFF", restorecolor: "#FFFFFF", duration: 2, afterFinish: function(){hideObj("success")}}); + } +} + +function showHide(thisObject,thisImg){ + if(thisObject.style.display == ""){ + thisObject.style.display = "none"; + thisImg.src = "images/button_max.gif"; + }else{ + thisObject.style.display = ""; + thisImg.src = "images/button_min.gif"; + } +} + +function getPermission(){ + //check to see if the name or description has been updated + if (originalName != $('applicationName').value){ + //check to see if the data is valid + if(confirm('You have made updates to the application name. Would you like to save your changes? \n If you choose cancel your changes will be lost.')){ + if(validateApplication()){ + //set the hidden from equal to the answer to "update app?" + $('saveApplicationInfo').value = "true"; + }else{ + return; + } + } + }else if (originalDescription != $('applicationDescription').value){ + //check to see if the data is valid + if(confirm('You have made updates to the application description. Would you like to save your changes? \n If you choose cancel your changes will be lost.')){ + if(validateApplication()){ + //set the hidden form equal to the answer to "update app?" + //this value is read by server side code + $('saveApplicationInfo').value = "true"; + }else{ + return; + } + } + } + this.form.submit(); +} + +function resetErrors(){ + var errorElements = document.getElementsByClassName('validateError',$('content')); + for(var i =0; i 255){ + $('applicationDescription').value = $('applicationDescription').value.substr(0,255); + showError('The description for this application is too big. Please make your description smaller.',$('applicationDescription')); + returnValue = false; + } + return returnValue; +} + +function validateGroups(){ + var returnValue = true; + resetErrors(); + if($('groupName').value.length < 1){ + showError('Please enter a name for the group',$('groupName')); + returnValue = false; + } + if($('description').value.length < 1){ + showError('Please enter a description for the group',$('description')); + returnValue = false; + } + if($('description').value.length > 255){ + $('description').value = $('description').value.substr(0,255); + showError('The description for this group is too big. Please make your description smaller.',$('description')); + returnValue = false; + } + selectOptions($('selectedUsers')); + return returnValue; +}; + +function validatePermission(){ + var returnValue = true; + resetErrors(); + if($('permissionName').value.length < 1){ + showError('Please enter a name for the permission',$('permissionName')); + returnValue = false; + } + + var selectedId = $('currentPermissionId').selectedIndex; + for(var i=0; i<$('currentPermissionId').options.length; i++){ + if($('permissionName').value == $('currentPermissionId').options[i].text){ + if(i != selectedId || selectedId == 0){ + showError('The permission name you entered already exists.',$('permissionName')); + returnValue = false; + } + } + } + + if($('permissionDescription').value.length < 1){ + showError('Please enter a Description for the permission',$('permissionDescription')); + returnValue = false; + } + if($('permissionDescription').value.length > 255){ + $('permissionDescription').value = $('permissionDescription').value.substr(0,255); + showError('The description for this permission is too big. Please make your description smaller.',$('permissionDescription')); + returnValue = false; + } + return returnValue; +} + +if($('applicationForm')){ + var originalName = ""; + var originalDescription = ""; +} + +function initialize(){ + var rows,cells //used as named holders for rows and cells of a table + if($('error')||$('success')){ + window.setTimeout(hideMessage,2000); + } + + if($('applicationListTable')){ + var rows = $('applicationListTable').getElementsByTagName('tr'); + for(var i=1; i < rows.length; i++){ + rows[i].onmouseout = function(){this.className = this.oldClassName}; + rows[i].onmouseover = function(){this.oldClassName = this.className; this.className="over"}; + rows[i].onclick = function(){ window.location.href = "applications.do?dispatch=" + $('onClickDispatch').value + "¤tApplication.id=" + this.id }; + cells = rows[i].getElementsByTagName('td'); + cells[2].onmouseover = function(){showPopUpMenu(this);}; + cells[2].onmouseout = function(){this.getElementsByTagName('div')[0].style.display='none'}; + } + } + if($('groupListTable')){ + rows = $('groupListTable').getElementsByTagName('tr'); + for(var i=1; i < rows.length; i++){ + rows[i].onmouseout = function(){this.className = this.oldClassName}; + rows[i].onmouseover = function(){this.oldClassName = this.className; this.className="over"}; + rows[i].onclick = function(){ window.location.href = "groups.do?dispatch=" + $('onClickDispatch').value + "¤tGroup.id=" + this.id }; + cells = rows[i].getElementsByTagName('td'); + cells[2].onmouseover = function(){showPopUpMenu(this);}; + cells[2].onmouseout = function(){this.getElementsByTagName('div')[0].style.display='none'}; + } + } + if($('applicationForm')){ + originalName = $('applicationName').value; + originalDescription = $('applicationDescription').value; + if($('permissionSaveButton')){ + $('permissionSaveButton').onclick = validatePermission; + } + if($('permissionAddButton')){ + $('permissionAddButton').onclick = validatePermission; + } + if($('currentApplicationId')){ + $('currentApplicationId').onchange = function(){this.form.submit();}; + } + if($('permissionDeleteButton')){ + $('permissionDeleteButton').onclick = function(){return confirm("Are you sure you want to delete this permission?")}; + } + if($('currentPermissionId')){ + $('currentPermissionId').onclick = getPermission; + } + if($('applicationDeleteButton')){ + $('applicationDeleteButton').onclick = function(){return confirm("Are you sure you want to delete this application?")}; + } + if($('applicationSaveButton')){ + $('applicationSaveButton').onclick = validateApplication; + } + if($('applicationCancelButton')){ + $('applicationCancelButton').onclick = function(){$('currentPermissionId').selectedIndex = -1; this.form.submit();}; + } + } + if($('groupsForm')){ + $('groupsForm').onsubmit = validateGroups; + + if($('groupDeleteButton')){ + $('groupDeleteButton').onclick = function(){return confirm("Are you sure you want to delete this group?");}; + } + //if($('ActiveDirectoryButton')){ + // $('ActiveDirectoryButton').onclick = function(){return checkGroup()}; + //}; + } + if($('permissionForm')){ + if($('currentApplicationId')){ + $('currentApplicationId').onchange = function(){ + var selectedID = this.options[this.selectedIndex].value; + if(IS_VALUE_CHANGED){ + if(!confirm('You have modified the groups associated with this permission. \n if you change the application your changes will be lost. \n Are you sure you want to change the application without saving?')){ + for(var i=0; i +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> +<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> +
+ Add New Application   |  + + " name="dispatch" /> +   Search results for: ${param.searchText} +
+ +
+" /> + + + + + + + + + + + + + + + + +
NameDescriptionPermissions
${fn:length(application.permissions)} view + +
+
+
+ + No Applications Found. + Index: src/main/webapp/pages/editApplication.jsp =================================================================== diff -u --- src/main/webapp/pages/editApplication.jsp (revision 0) +++ src/main/webapp/pages/editApplication.jsp (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,118 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%> +<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%> +<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Application Name
Application Description
+ + + + + + + + + + + + + + +

+
+ + + + + + + + +
Add / Edit Permissions:
+ + --- Create a new permission --- + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
Permission Name:
Permission Description
+
+ + + + + + + + + + + + + + + + + + +
+
+
+ " /> +
+
Index: src/main/webapp/pages/editGroup.jsp =================================================================== diff -u --- src/main/webapp/pages/editGroup.jsp (revision 0) +++ src/main/webapp/pages/editGroup.jsp (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,59 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> +<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %> +<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> + + + +
+ + + + + + + + + + + + + +
+
+ + + + + + + + + + + +
AllSelected
+ + +
+ +
+ + + + +
+
+ + + + + +
+
+ " name="dispatch"> +
Index: src/main/webapp/pages/groups.jsp =================================================================== diff -u --- src/main/webapp/pages/groups.jsp (revision 0) +++ src/main/webapp/pages/groups.jsp (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,61 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> +<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %> +<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> +
+ Add New Group   |  + + " name="dispatch" /> +   Search results for: ${param.searchText} +
+ +
+ " /> + + + + + + + + + + + + + + + + + + + +
NameDescriptionUsers
${fn:length(group.users)} view +
+
+
+ + No Groups Found. + +
+ + + + --- Select A Group to Import --- + + + + +
Index: src/main/webapp/pages/inc/footer.jsp =================================================================== diff -u --- src/main/webapp/pages/inc/footer.jsp (revision 0) +++ src/main/webapp/pages/inc/footer.jsp (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1 @@ + \ No newline at end of file Index: src/main/webapp/pages/inc/header.jsp =================================================================== diff -u --- src/main/webapp/pages/inc/header.jsp (revision 0) +++ src/main/webapp/pages/inc/header.jsp (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,16 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%> +<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%> +<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%> +<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> +<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles"%> + + Index: src/main/webapp/pages/main.jsp =================================================================== diff -u --- src/main/webapp/pages/main.jsp (revision 0) +++ src/main/webapp/pages/main.jsp (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,60 @@ + +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> +<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %> +<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> +<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %> +<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%> + + + + + <tiles:getAsString name="title" /> + + + + + + + +
+ +
+ + +
+ + + +
+
+ + + +
+ + + +
+
+ + + +
+ + + +
+
+
+
+
+
+ +
+
+ + +
+ + Index: src/main/webapp/pages/permissions.jsp =================================================================== diff -u --- src/main/webapp/pages/permissions.jsp (revision 0) +++ src/main/webapp/pages/permissions.jsp (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,83 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> +<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> +<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %> + + + + + + + + + + + + + + + + + + + + + + + + + +
Applications  add newPermissions  ¤tApplication.id=¤tPermission.id=0" class="small">add new
" /> + + + + " /> + + + + + + None Available + + + +

All Groups  add new

Selected Groups
+ + + + + + + +
+ +
+ + + + + + + + + + +
+ + + + + + +
+ +
Index: src/main/webapp/stylesheets/site.css =================================================================== diff -u --- src/main/webapp/stylesheets/site.css (revision 0) +++ src/main/webapp/stylesheets/site.css (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,175 @@ +a { + color: #1163A6; +} + + +a.button{ + color: white; + text-align: center; + display: block; + height: 18px; + width: 120px; + border: 1px solid silver; + background-color: #1D71C2; +} + +a.button:hover{ + border: 1px solid gray; + background-color: #1163A6; +} + +.bigSelect { + height: 175px; + width: 294px; +} + +.autoSize{ + width: auto; +} + +.lines { + border-top: 1px solid #efefef; + background-image: url(../images/lines_fade.gif); + background-position: top left; + background-repeat: repeat-x; + background-color: white; + padding: 8px 0px 0px 0px; + margin-top: 10px; +} + +.showHide { + font-size: 10px; + cursor: pointer; +} + +#searchForm { + margin: 0px 0px 8px 0px ; + padding: 0px 0px 0px 0px; + text-align: left; + font-size: .9em; + font-weight: bold; +} +#searchForm label{ + margin: 0px; +} + + +.relativeDiv { + position: relative; +} + +.dropDownList{ + position: absolute; + top: -10px; + right: 50px; + width: 150px; + height: 200px; + overflow: auto; + cursor: auto; +} + +.dropDownList ul{ + font-size: 11px; + background-color: white; + margin: 0px; + border: 1px solid gray; + padding: 5px; + list-style-type: none; +} + +.buttonsDiv{ + padding-top: 10px; + clear: both; + text-align: right; +} + +.centerColumn{ + width: 62px; + text-align: center; +} + + +/*********************** permission edit page ***********************/ +#permissionsEditTable{ + width: 650px; +} +#currentPermissionId, #currentApplicationId{ + height: 110px; + width: 294px; +} +#allGroups, #selectedGroups{ + height: 150px; + width: 294px; +} + +/*********************** group list page ***********************/ +#activeDirectoryDiv{ + margin-top: 15px; + font-size: .8em; +} + +#activeDirectoryDiv select{ + margin-right: 10px; +} + + +/*********************** group edit page ***********************/ +#groupTables{ + width: 650px; +} +#groupTables input, #groupTables textarea{ + width: 642px; +} + +#groupTables table{ + width: 100%; +} + +#groupTables table .centerColumn{ + width: 74px; + text-align: center; +} + +#groupTables select{ + width: 294px; +} + +#groupTables .buttonsDiv input{ + width: auto; +} + +/*********************** application edit page ***********************/ +#applicationTable, #permissionsDiv{ + width: 650px; +} +#applicationTable input, #applicationTable textarea{ + width: 642px; +} + +#permissionsTable { + width: auto; + float: left; +} +#permissionsTable select{ + width: 294px; + height: 100px; +} + +#permissionDetailsTable { + width: auto; + float: right; +} +#permissionDetailsTable input, #permissionDetailsTable textarea{ + width: 294px; +} + +#applicationForm .buttonsDiv input{ + width: auto; +} + + +a.viewlink, .verticalTable a.viewlink{ + font-size: 10px; + color: #1163A6; +} + Index: src/main/webapp/stylesheets/standard.css =================================================================== diff -u --- src/main/webapp/stylesheets/standard.css (revision 0) +++ src/main/webapp/stylesheets/standard.css (revision 04b2890465d7f3cec6198ac9fdedd60fdc60ae81) @@ -0,0 +1,314 @@ +html { + filter: expression(document.execCommand("BackgroundImageCache", false, true)); + overflow: scroll; +} + +body { + margin: 0px; + padding: 0px; + font-family: arial, sans-serif; + background-image: url(../images/bg_tile.gif); + background-position: top center; +} + +form { + margin: 0px; +} + +input, select, textarea, td, th { + font-family: arial, sans-serif; + font-size: 13px; +} + +input, select, textarea { +font-weight: normal; +} + +label, .label{ + font-weight: bold; + margin-right: 10px; +} + +table { + border-collapse: collapse; + /* border: 1px solid silver; */ +} + + +th { + vertical-align: top; + padding: 2px; + text-align: left; +} + +td{ + padding: 2px; +} + +tr.odd td { + background-color: #F4F4F4; +} + +tr.read td { + color: #999999; +} + +tr.selected td{ + background-color: #4298e9; + color: white; +} + +tr.over td{ + background-color: #dddddd; + cursor: pointer; +} + +tr.header th{ + vertical-align: middle; + text-align: left; + padding-left: 10px; +} + + + +.alignLeft { text-align: left;} +.alignRight { text-align: right;} +.alignCenter { text-align: center;} + +.big {font-size: 1.1em;} + +.bold {font-weight: bold;} +.normal {font-weight: normal;} + +.clear {clear: both;} + +.hide {display: none;} + +.floatLeft, .left{ float: left;} + +.floatRight, .right {float: right;} + +.small {font-size: .8em; font-weight: normal;} + +.grayBgDiv { + background-color: #efefef; + border: 1px solid silver; +} + +.whiteBgDiv { + background-color: white; + border: 1px solid silver; +} + +.autoCompleteDiv { + background-color: #ffffff; + border: 1px solid #efefef; +} +.autoCompleteDiv ul { + list-style-type: none; + padding: 0px; + margin: 0px; + border-left: 1px solid #efefef; + border-right: 1px solid #efefef; +} +.autoCompleteDiv ul li { + width: 100%; + border-bottom: 1px solid #efefef; + padding: 3px 0px 3px 3px; + margin: 0px; + cursor: pointer; + font-size: .9em; + line-height: 1.1em; +} +.autoCompleteDiv ul li.selected { + background-color: #F4F4F4; + /* #ffffee */ +} +.autoCompleteDiv span.small{ + font-size: .9em; +} + + + +.tableVertical, .scrollTable { + background-color: white; + border: 1px solid silver; +} + +.scrollTable { + overflow-y: auto; +} + +.tableVertical table, .scrollTable table{ + width: 100%; + border: 0px; +} + +.scrollTable th, .tableVertical th, .header { + vertical-align: top; + text-align: left; + background-color: #1D71C2; + border-bottom: 1px solid silver; + padding-left: 5px; + color: white; +} + +.scrollTable td, .tableVertical td{ + vertical-align: top; + text-align: left; + border-bottom: 1px solid #efefef; + padding-left: 5px; +} + +.scrollTable a, .tableVertical a{ + text-decoration: none; + color: black; +} + +.error { + color: red; +} + +.validateError { + background-color: red; + color: white; +} + +#pageholder { + background-color: #ffffff; + margin: 0px auto 0px auto; + width: 930px; + text-align:left; +} + +#topmenu { + width: 930px; + height: 104px; + position: relative; + background-image: url(../images/center_header.gif); +} + +#welcomeDiv { + position: relative; + width: 820px; + left: 100px; + top: 4px; + padding: 2px; + text-align: left; + padding: 2px; + color: white; + font-size: .7em; +} + +#topmenu ul { + margin: 0px; + padding: 0px; + list-style-type: none; + display: block; + position: absolute; + left: 4px; + bottom: 0px; + width: auto; + height: auto; +} + +#topmenu li { + margin: 0px; + padding: 0px; + float: left; + position: relative; + margin-right: 4px; + margin-left: 4px; + border: 1px solid #FFFFFF; + border-width: 1px 1px 0px 1px; +} +#topmenu li a { + display: block; + margin: 0px; + padding: 6px 8px 6px 8px; + text-decoration: none; + background-color: #ccccef; + height: auto; + width: auto; + font-size: .75em; + font-weight: bold; + color: #FFFFFF; + background-image: url(../images/buttonbg.gif); + background-position: bottom; + background-repeat: repeat-x; +} +#topmenu li a:hover { + background-color: #ffffcc; + background-image: url(../images/buttonbg.gif); + background-position: top; + background-repeat: repeat-x; +} + +#topmenu li.selected a:hover { + background-color: #ffffff; + color: #1163A6; + background-image: url(../images/white.gif); +} + +#topmenu li.selected a { + background-color: #ffffff; + color: #1163A6; + background-image: url(../images/white.gif); +} + +#submenu{ + margin: 0px; + padding: 0px; + list-style-type: none; + height: 20px; + width: 450px; + left: 10px; + position: relative; +} +#subMenuSpacer{ + display: block; + margin: 0px; + padding: 0px; + height: 5px; +} +#submenu li { + float: left; + padding: 5px 5px 0px 10px; +} +#submenu li a { + padding: 0px; + font-size: .7em; + margin: 0px; + color: silver; + text-decoration: none; + font-weight: bold; +} +#submenu li a:hover { + color: #4B4B4B; +} +#submenu li.selected a{ + color: #1163A6; +} +#submenu li.selected a:hover { + color: #1163A6; +} + +#content { + padding: 12px 10px 10px 10px; +} + +#success, #error, .jsError{ + position: absolute; + color: white; + background-color: green; + font-weight: bold; + margin: 5px 0px 10px 0px; + padding: 2px 0px 2px 0px; + text-align: center; + width: 930px; + top: 103px; +} + +#error, .jsError { + background-color: red; +} \ No newline at end of file