From 3fbdcf25cd174332b1e8de012f38f977f2563cf9 Mon Sep 17 00:00:00 2001 From: Vane Zulu Date: Thu, 22 Jan 2015 12:19:27 +0200 Subject: [PATCH] Code format and added "spring-servlet file" to Git --- .gitignore | 2 +- leaveDatabase.sql | 94 +- pom.xml | 8 +- .../leaveTrack/SessionListener.java | 6 +- .../leaveTrack/calendar/FullCalendar.java | 4 +- .../leaveTrack/dao/EmployeeDao.java | 16 +- .../leaveTrack/dao/EmployeeDaoImpl.java | 11 +- .../ubiquitech/leaveTrack/dao/RequestDao.java | 8 +- .../leaveTrack/dao/RequestImpl.java | 63 +- .../leaveTrack/domain/Employee.java | 38 +- .../leaveTrack/domain/LeaveDays.java | 30 +- .../ubiquitech/leaveTrack/domain/Request.java | 17 +- .../com/ubiquitech/leaveTrack/eMail/Mail.java | 12 +- .../leaveTrack/form/ChangePasswordForm.java | 6 +- .../leaveTrack/form/CreateEmployeeForm.java | 14 +- .../leaveTrack/form/EditEmployeeForm.java | 12 +- .../form/ProcessEmployeeLeaveForm.java | 6 +- .../leaveTrack/form/RequestLeaveForm.java | 6 +- .../leaveTrack/form/RequestQueryForm.java | 10 +- .../leaveTrack/form/ViewRequestsForm.java | 2 +- .../leaveTrack/hash/HashPassword.java | 4 +- .../leaveTrack/mvc/HelloController.java | 64 +- .../leaveTrack/services/EmployeeService.java | 9 +- .../services/EmployeeServiceImpl.java | 9 +- .../leaveTrack/services/RequestService.java | 9 +- .../services/RequestServiceImpl.java | 8 +- .../webflow/EditEmployeeDatabaseAction.java | 54 +- .../LoginAndCreateEmployeeDatabaseAction.java | 147 +- .../webflow/PasswordChangeDatabaseAction.java | 13 +- .../webflow/ProcessRequestsActions.java | 5 +- .../webflow/RequestLeaveActions.java | 48 +- .../webflow/RequestQueryActions.java | 28 +- .../leaveTrack/webflow/SupervisorActions.java | 30 +- .../leaveTrack/webflow/ViewRequestAction.java | 6 +- src/main/resources/logback.xml | 2 +- .../changePassword/changePassword-flow.xml | 9 +- .../flows/changePassword/changePassword.jsp | 73 +- .../passwordChangeSuccessful.jsp | 47 +- .../flows/createEmployee/confirmDetails.jsp | 18 +- .../createEmployee/createEmployee-flow.xml | 57 +- .../createEmployee/createEmployeeForm.jsp | 41 +- .../flows/createEmployee/createSuccessful.jsp | 13 +- .../flows/editEmployee/confirmDetails.jsp | 17 +- .../flows/editEmployee/editEmployee-flow.xml | 8 +- .../flows/editEmployee/editEmployeeForm.jsp | 27 +- .../flows/editEmployee/editSuccessful.jsp | 57 +- .../flows/processRequests/emailSendError.jsp | 11 +- .../processRequests/processLeaveRequest.jsp | 40 +- .../processRequests/processRequests-flow.xml | 12 +- .../flows/processRequests/receiveRequests.jsp | 15 +- .../requestProcessedconfirmation.jsp | 12 +- .../flows/requestLeave/confirmDetails.jsp | 25 +- .../flows/requestLeave/emailSendError.jsp | 12 +- .../flows/requestLeave/requestLeave-flow.xml | 10 +- .../flows/requestLeave/requestLeaveForm.jsp | 38 +- .../flows/requestLeave/requestSuccessful.jsp | 14 +- .../flows/requestQuery/requestDetails.jsp | 48 +- .../flows/requestQuery/requestQuery-flow.xml | 19 +- .../flows/requestQuery/requestQueryForm.jsp | 28 +- .../requestQuery/requestQueryResults.jsp | 15 +- .../webapp/WEB-INF/pages/calendarView.jsp | 144 +- src/main/webapp/WEB-INF/pages/footer.jsp | 3 +- src/main/webapp/WEB-INF/pages/home.jsp | 57 +- src/main/webapp/WEB-INF/pages/index.jsp | 71 +- src/main/webapp/WEB-INF/security-config.xml | 36 +- src/main/webapp/WEB-INF/spring-servlet.xml | 99 + src/main/webapp/WEB-INF/web.xml | 24 +- src/main/webapp/WEB-INF/webflowContext.xml | 28 +- .../resources/fullCalendar/fullcalendar.css | 776 +- .../resources/fullCalendar/fullcalendar.js | 17453 ++++++++-------- .../fullCalendar/fullcalendar.print.css | 97 +- .../resources/fullCalendar/jquery.min.js | 2068 +- .../resources/fullCalendar/moment.min.js | 1019 +- .../webapp/resources/theme/css/design.css | 104 +- src/main/webapp/resources/theme/footer.jsp | 3 +- .../java/com/ubiquitech/app/AppTests.java | 3 +- 76 files changed, 13375 insertions(+), 10077 deletions(-) create mode 100644 src/main/webapp/WEB-INF/spring-servlet.xml diff --git a/.gitignore b/.gitignore index a806e30..150a0e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ .idea leavetrack.iml target -src/main/webapp/WEB-INF/spring-servlet.xml +src/main/resources/config.properties \ No newline at end of file diff --git a/leaveDatabase.sql b/leaveDatabase.sql index f8f615c..6c840c4 100644 --- a/leaveDatabase.sql +++ b/leaveDatabase.sql @@ -2,52 +2,54 @@ CREATE DATABASE leaveDatabase; USE leaveDatabase; -Create table LeaveDays( -id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, -PRIMARY KEY (id), -yearEmployed DATE NOT NULL, -sickDaysLeft INT UNSIGNED NOT NULL DEFAULT 0, -sickDaysTotal INT UNSIGNED NOT NULL DEFAULT 0, -familyDaysLeft INT UNSIGNED NOT NULL DEFAULT 0, -familyDaysTotal INT UNSIGNED NOT NULL DEFAULT 0, -maternityDaysLeft INT UNSIGNED NOT NULL DEFAULT 0, -maternityDaysTotal INT UNSIGNED NOT NULL DEFAULT 0, -annualDaysLeft INT UNSIGNED NOT NULL DEFAULT 0, -annualDaysTotal INT UNSIGNED NOT NULL DEFAULT 0 ); - -Create table Employee(id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, -PRIMARY KEY (id), -leaveId BIGINT UNSIGNED NOT NULL, -FOREIGN KEY (leaveId) REFERENCES leavedays (id), -username varchar (20) NOT NULL, -firstName varchar (255) NOT NULL, -lastName varchar (255) NOT NULL , -phoneNumber varchar (255) NOT NULL, -email varchar (255) NOT NULL, -jobTitle varchar (255), -supervisorId BIGINT unsigned, -FOREIGN KEY (supervisorId) REFERENCES Employee (id), -active Boolean NOT NULL DEFAULT TRUE, -password varchar(255) NOT NULL, -role varchar(255) NOT NULL DEFAULT 'employee', -version INT UNSIGNED DEFAULT 0); - -Create table Request( -id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, -PRIMARY KEY (id), -employeeid BIGINT UNSIGNED NOT NULL, -FOREIGN KEY (employeeid) REFERENCES Employee (id), -timestamp timestamp NOT NULL , -leaveType varchar (255) NOT NULL , -reason TEXT NOT NULL , -startDate Date NOT NULL , -endDate Date NOT NULL , -state varchar (255), -comment TEXT); - - -INSERT INTO leavedays(yearEmployed) VALUE ('2006-01-01'); -INSERT INTO Employee (leaveId,username,firstName,lastName,phoneNumber,email,jobTitle,password)VALUES (1,'Vzulu','Vane','Zulu','27718903620','vane@ubiquitech.co.za','Trainee','$2a$10$HTwDZJ1tLQobcObmlQH28.DC3fe1coMj.9H1dXj.Ul7A0q9Ktk9P.'); +CREATE TABLE LeaveDays ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + PRIMARY KEY (id), + yearEmployed DATE NOT NULL, + sickDaysLeft INT UNSIGNED NOT NULL DEFAULT 0, + sickDaysTotal INT UNSIGNED NOT NULL DEFAULT 0, + familyDaysLeft INT UNSIGNED NOT NULL DEFAULT 0, + familyDaysTotal INT UNSIGNED NOT NULL DEFAULT 0, + maternityDaysLeft INT UNSIGNED NOT NULL DEFAULT 0, + maternityDaysTotal INT UNSIGNED NOT NULL DEFAULT 0, + annualDaysLeft INT UNSIGNED NOT NULL DEFAULT 0, + annualDaysTotal INT UNSIGNED NOT NULL DEFAULT 0); + +CREATE TABLE Employee (id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + PRIMARY KEY (id), + leaveId BIGINT UNSIGNED NOT NULL, + FOREIGN KEY (leaveId) REFERENCES leavedays (id), + username VARCHAR(20) NOT NULL, + firstName VARCHAR(255) NOT NULL, + lastName VARCHAR(255) NOT NULL, + phoneNumber VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL, + jobTitle VARCHAR(255), + supervisorId BIGINT UNSIGNED, + FOREIGN KEY (supervisorId) REFERENCES Employee (id), + active BOOLEAN NOT NULL DEFAULT TRUE, + password VARCHAR(255) NOT NULL, + role VARCHAR(255) NOT NULL DEFAULT 'employee', + version INT UNSIGNED DEFAULT 0); + +CREATE TABLE Request ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + PRIMARY KEY (id), + employeeid BIGINT UNSIGNED NOT NULL, + FOREIGN KEY (employeeid) REFERENCES Employee (id), + timestamp TIMESTAMP NOT NULL, + leaveType VARCHAR(255) NOT NULL, + reason TEXT NOT NULL, + startDate DATE NOT NULL, + endDate DATE NOT NULL, + state VARCHAR(255), + comment TEXT); + + +INSERT INTO leavedays (yearEmployed) VALUE ('2006-01-01'); +INSERT INTO Employee (leaveId, username, firstName, lastName, phoneNumber, email, jobTitle, password) VALUES + (1, 'Vzulu', 'Vane', 'Zulu', '27718903620', 'vane@ubiquitech.co.za', 'Trainee', + '$2a$10$HTwDZJ1tLQobcObmlQH28.DC3fe1coMj.9H1dXj.Ul7A0q9Ktk9P.'); diff --git a/pom.xml b/pom.xml index 938b42a..f349351 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,4 @@ - 4.0.0 com.springapp @@ -72,7 +72,7 @@ - + org.springframework spring-orm ${spring.version} @@ -146,7 +146,7 @@ ${security.version} - + joda-time joda-time @@ -177,7 +177,7 @@ 2.0.6 - + org.slf4j slf4j-api diff --git a/src/main/java/com/ubiquitech/leaveTrack/SessionListener.java b/src/main/java/com/ubiquitech/leaveTrack/SessionListener.java index 3ea48fb..ec072ee 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/SessionListener.java +++ b/src/main/java/com/ubiquitech/leaveTrack/SessionListener.java @@ -4,9 +4,9 @@ import javax.servlet.http.HttpSessionListener; /** - * Created by vane on 2014/12/01. + * vane created on 2014/12/01. */ -public class SessionListener implements HttpSessionListener{ +public class SessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent httpSessionEvent) { System.out.println("session created"); @@ -14,6 +14,6 @@ public void sessionCreated(HttpSessionEvent httpSessionEvent) { @Override public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { - System.out.println("session destroyed"); + System.out.println("session destroyed"); } } diff --git a/src/main/java/com/ubiquitech/leaveTrack/calendar/FullCalendar.java b/src/main/java/com/ubiquitech/leaveTrack/calendar/FullCalendar.java index 417d582..6e8a817 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/calendar/FullCalendar.java +++ b/src/main/java/com/ubiquitech/leaveTrack/calendar/FullCalendar.java @@ -1,7 +1,7 @@ package com.ubiquitech.leaveTrack.calendar; /** - * Created by vane on 2015/01/16. + * vane created on 2015/01/16. */ public class FullCalendar { String start; @@ -9,7 +9,7 @@ public class FullCalendar { String title; String color; - public FullCalendar(String color, String title, String start,String end) { + public FullCalendar(String color, String title, String start, String end) { this.color = color; this.title = title; this.end = end; diff --git a/src/main/java/com/ubiquitech/leaveTrack/dao/EmployeeDao.java b/src/main/java/com/ubiquitech/leaveTrack/dao/EmployeeDao.java index 14f8d02..c0b6c36 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/dao/EmployeeDao.java +++ b/src/main/java/com/ubiquitech/leaveTrack/dao/EmployeeDao.java @@ -5,12 +5,16 @@ import java.util.List; /** - * Created by vane on 2014/11/20. + * vane created on 2014/11/20. */ public interface EmployeeDao { - public void createEmployee(Employee emp); - public List getEmployeeNames(); - public Boolean checkUsername(String username); - public Employee getEmployee(String username); - public Employee getEmployeeById(Long id); + void createEmployee(Employee emp); + + List getEmployeeNames(); + + Boolean checkUsername(String username); + + Employee getEmployee(String username); + + Employee getEmployeeById(Long id); } diff --git a/src/main/java/com/ubiquitech/leaveTrack/dao/EmployeeDaoImpl.java b/src/main/java/com/ubiquitech/leaveTrack/dao/EmployeeDaoImpl.java index 326ba3a..bc827ad 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/dao/EmployeeDaoImpl.java +++ b/src/main/java/com/ubiquitech/leaveTrack/dao/EmployeeDaoImpl.java @@ -5,20 +5,19 @@ import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; import java.util.List; /** - * Created by vane on 2014/11/20. + * vane created on 2014/11/20. */ @Repository @SuppressWarnings("unchecked") public class EmployeeDaoImpl implements EmployeeDao { - @Autowired - private SessionFactory sessionFactory; + @Autowired + private SessionFactory sessionFactory; - @Override + @Override public void createEmployee(Employee employee) { Session session = this.sessionFactory.getCurrentSession(); session.saveOrUpdate(employee); @@ -27,7 +26,7 @@ public void createEmployee(Employee employee) { @Override public Employee getEmployeeById(Long id) { Session session = sessionFactory.getCurrentSession(); - return (Employee) session.get(Employee.class,id); + return (Employee) session.get(Employee.class, id); } @Override diff --git a/src/main/java/com/ubiquitech/leaveTrack/dao/RequestDao.java b/src/main/java/com/ubiquitech/leaveTrack/dao/RequestDao.java index 6a1b890..a3be355 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/dao/RequestDao.java +++ b/src/main/java/com/ubiquitech/leaveTrack/dao/RequestDao.java @@ -6,12 +6,16 @@ import java.util.List; /** - * Created by vane on 2014/12/08. + * vane created on 2014/12/08. */ public interface RequestDao { public void createRequest(Request request); + public List getRequestsByStatusAndSupervisorId(String status, Long id); - public List getRequestsByStatusAndRequestId(String status,Long id); + + public List getRequestsByStatusAndRequestId(String status, Long id); + public List getRequestsByState(String status); + public List getQueriedRequests(RequestQueryForm requestQueryForm); } diff --git a/src/main/java/com/ubiquitech/leaveTrack/dao/RequestImpl.java b/src/main/java/com/ubiquitech/leaveTrack/dao/RequestImpl.java index 506df57..94ec435 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/dao/RequestImpl.java +++ b/src/main/java/com/ubiquitech/leaveTrack/dao/RequestImpl.java @@ -12,9 +12,8 @@ import java.util.List; /** - * Created by vane on 2014/12/08. + * vane created on 2014/12/08. */ - @Repository @SuppressWarnings("unchecked") public class RequestImpl implements RequestDao { @@ -33,9 +32,9 @@ public List getRequestsByStatusAndSupervisorId(String state, Long id) { Session session = sessionFactory.openSession(); Criteria request = session.createCriteria(Request.class); Criteria employee = request.createCriteria("employee"); - Criteria supervisor=employee.createCriteria("supervisor"); - supervisor.add(Restrictions.eq("id",id)); - request.add((Restrictions.eq("state",state))); + Criteria supervisor = employee.createCriteria("supervisor"); + supervisor.add(Restrictions.eq("id", id)); + request.add((Restrictions.eq("state", state))); return request.list(); } @@ -43,10 +42,10 @@ public List getRequestsByStatusAndSupervisorId(String state, Long id) { public List getRequestsByStatusAndRequestId(String state, Long id) { Session session = sessionFactory.openSession(); Criteria request = session.createCriteria(Request.class); - request.add(Restrictions.eq("id",id)); + request.add(Restrictions.eq("id", id)); - if(!state.equals("")){ - request.add(Restrictions.and(Restrictions.eq("state",state))); + if (!state.equals("")) { + request.add(Restrictions.and(Restrictions.eq("state", state))); } return request.list(); } @@ -55,52 +54,52 @@ public List getRequestsByStatusAndRequestId(String state, Long id) { public List getRequestsByState(String state) { Session session = sessionFactory.openSession(); Criteria request = session.createCriteria(Request.class); - request.add(Restrictions.eq("state",state)); + request.add(Restrictions.eq("state", state)); return request.list(); } @Override public List getQueriedRequests(RequestQueryForm requestQueryForm) { - String state=requestQueryForm.getState(); - String leaveType=requestQueryForm.getLeaveType(); - String employeeFirstName=requestQueryForm.getEmployeeFirstName(); - String employeeLastName=requestQueryForm.getEmployeeLastName(); - String supervisorFirstName=requestQueryForm.getSupervisorFirstName(); - String supervisorLastName=requestQueryForm.getEmployeeLastName(); - Long requestId=requestQueryForm.getRequestId(); + String state = requestQueryForm.getState(); + String leaveType = requestQueryForm.getLeaveType(); + String employeeFirstName = requestQueryForm.getEmployeeFirstName(); + String employeeLastName = requestQueryForm.getEmployeeLastName(); + String supervisorFirstName = requestQueryForm.getSupervisorFirstName(); + String supervisorLastName = requestQueryForm.getEmployeeLastName(); + Long requestId = requestQueryForm.getRequestId(); Session session = sessionFactory.openSession(); Criteria request = session.createCriteria(Request.class); - Criteria employee=request.createCriteria("employee"); - Criteria supervisor=employee.createCriteria("supervisor"); + Criteria employee = request.createCriteria("employee"); + Criteria supervisor = employee.createCriteria("supervisor"); - if(!state.equals("")){ - request.add(Restrictions.and(Restrictions.eq("state",state))); + if (!state.equals("")) { + request.add(Restrictions.and(Restrictions.eq("state", state))); } - if(!leaveType.equals("")){ - request.add(Restrictions.and(Restrictions.eq("leaveType",leaveType))); + if (!leaveType.equals("")) { + request.add(Restrictions.and(Restrictions.eq("leaveType", leaveType))); } - if(!(requestId==null)){ - request.add(Restrictions.and(Restrictions.eq("id",requestId))); + if (!(requestId == null)) { + request.add(Restrictions.and(Restrictions.eq("id", requestId))); } - if(!employeeFirstName.equals("")){ - employee.add(Restrictions.and(Restrictions.eq("firstName",employeeFirstName))); + if (!employeeFirstName.equals("")) { + employee.add(Restrictions.and(Restrictions.eq("firstName", employeeFirstName))); } - if(!employeeLastName.equals("")){ - employee.add(Restrictions.and(Restrictions.eq("lastName",employeeLastName))); + if (!employeeLastName.equals("")) { + employee.add(Restrictions.and(Restrictions.eq("lastName", employeeLastName))); } - if(!supervisorFirstName.equals("")){ - supervisor.add(Restrictions.and(Restrictions.eq("firstName",supervisorFirstName))); + if (!supervisorFirstName.equals("")) { + supervisor.add(Restrictions.and(Restrictions.eq("firstName", supervisorFirstName))); } - if(!supervisorLastName.equals("")){ - supervisor.add(Restrictions.and(Restrictions.eq("lastName",supervisorLastName))); + if (!supervisorLastName.equals("")) { + supervisor.add(Restrictions.and(Restrictions.eq("lastName", supervisorLastName))); } return request.list(); diff --git a/src/main/java/com/ubiquitech/leaveTrack/domain/Employee.java b/src/main/java/com/ubiquitech/leaveTrack/domain/Employee.java index 4545ef1..d4a0256 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/domain/Employee.java +++ b/src/main/java/com/ubiquitech/leaveTrack/domain/Employee.java @@ -8,16 +8,16 @@ import java.util.Set; /** - * Created by vane on 2014/11/17. + * vane created on 2014/11/17. */ @Entity -@Table(name="employee") -public class Employee implements Serializable{ - private static final long serialVersionUID=1L; +@Table(name = "employee") +public class Employee implements Serializable { + private static final long serialVersionUID = 1L; @Id - @Column(name="id") - @GeneratedValue(strategy=GenerationType.AUTO) + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version @@ -35,23 +35,23 @@ public class Employee implements Serializable{ private String email; private String jobTitle; - @ManyToOne(cascade ={CascadeType.ALL}) - @JoinColumn(name="supervisorid") - private Employee supervisor; + @ManyToOne(cascade = {CascadeType.ALL}) + @JoinColumn(name = "supervisorid") + private Employee supervisor; - @OneToMany(mappedBy = "supervisor") - private Set subordinates = new HashSet(); + @OneToMany(mappedBy = "supervisor") + private Set subordinates = new HashSet(); private String password; - @OneToMany(mappedBy="employee",cascade = {CascadeType.ALL}) + @OneToMany(mappedBy = "employee", cascade = {CascadeType.ALL}) private Set request; - @OneToOne(cascade ={CascadeType.ALL})//Parent Table - @JoinColumn(name="leaveId")//Column in parent table that joins with the child table column + @OneToOne(cascade = {CascadeType.ALL})//Parent Table + @JoinColumn(name = "leaveId")//Column in parent table that joins with the child table column private LeaveDays leaveDays; //Child table class (DOMAIN) - private boolean active =true; + private boolean active = true; public Employee() { this.leaveDays = new LeaveDays(); @@ -141,6 +141,10 @@ public Long getId() { return id; } + public void setId(Long id) { + this.id = id; + } + public Set getRequest() { return request; } @@ -164,8 +168,4 @@ public Employee getSupervisor() { public void setSupervisor(Employee supervisor) { this.supervisor = supervisor; } - - public void setId(Long id) { - this.id = id; - } } diff --git a/src/main/java/com/ubiquitech/leaveTrack/domain/LeaveDays.java b/src/main/java/com/ubiquitech/leaveTrack/domain/LeaveDays.java index fb2114d..d129fe6 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/domain/LeaveDays.java +++ b/src/main/java/com/ubiquitech/leaveTrack/domain/LeaveDays.java @@ -8,31 +8,31 @@ import java.io.Serializable; /** - * Created by vane on 2014/11/27. + * vane created on 2014/11/27. */ @Entity -@Table(name="leavedays") +@Table(name = "leavedays") public class LeaveDays implements Serializable { private static final long serialVersionUID = 1L; @Id - @GeneratedValue(strategy=GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.AUTO) private long id; - @Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDate") + @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate") private LocalDate yearEmployed; - private int sickDaysLeft=0; - private int sickDaysTotal=0; - private int familyDaysLeft=0; - private int familyDaysTotal=0; - private int maternityDaysLeft=0; - private int maternityDaysTotal=0; - private int annualDaysLeft=0; - private int annualDaysTotal=0; - - @OneToOne(mappedBy ="leaveDays")//maps to the Child table domain as in the Parent table - private Employee employee; //Parent table class (Domain) + private int sickDaysLeft = 0; + private int sickDaysTotal = 0; + private int familyDaysLeft = 0; + private int familyDaysTotal = 0; + private int maternityDaysLeft = 0; + private int maternityDaysTotal = 0; + private int annualDaysLeft = 0; + private int annualDaysTotal = 0; + + @OneToOne(mappedBy = "leaveDays")//maps to the Child table domain as in the Parent table + private Employee employee; //Parent table class (Domain) public int getMaternityDaysTotal() { return maternityDaysTotal; diff --git a/src/main/java/com/ubiquitech/leaveTrack/domain/Request.java b/src/main/java/com/ubiquitech/leaveTrack/domain/Request.java index 581d33b..08c6242 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/domain/Request.java +++ b/src/main/java/com/ubiquitech/leaveTrack/domain/Request.java @@ -8,23 +8,22 @@ import java.io.Serializable; /** - * Created by vane on 2014/12/08. + * vane created on 2014/12/08. */ - @Entity -@Table(name="request") +@Table(name = "request") public class Request implements Serializable { private static final long serialVersionUID = 1L; @Id - @GeneratedValue(strategy= GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.AUTO) private long id; @ManyToOne - @JoinColumn(name="employeeid", referencedColumnName ="id") + @JoinColumn(name = "employeeid", referencedColumnName = "id") private Employee employee; - @Column(name="employeeid",insertable = false,updatable = false) + @Column(name = "employeeid", insertable = false, updatable = false) private long employeeId; private String timestamp; @@ -33,9 +32,9 @@ public class Request implements Serializable { private String leaveType; @NotEmpty private String reason; - @Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDate") + @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate") private LocalDate startDate; - @Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDate") + @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate") private LocalDate endDate; private String comment; @NotEmpty @@ -57,7 +56,7 @@ public void setEndDate(LocalDate endDate) { this.endDate = endDate; } - public LocalDate getStartDate() { + public LocalDate getStartDate() { return startDate; } diff --git a/src/main/java/com/ubiquitech/leaveTrack/eMail/Mail.java b/src/main/java/com/ubiquitech/leaveTrack/eMail/Mail.java index 32dc04e..5190741 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/eMail/Mail.java +++ b/src/main/java/com/ubiquitech/leaveTrack/eMail/Mail.java @@ -4,18 +4,18 @@ import org.springframework.mail.SimpleMailMessage; /** - * Created by vane on 2014/12/04. + * vane created on 2014/12/04. */ public class Mail { - private MailSender mailSender; + private MailSender mailSender; - public void setMailSender(MailSender mailSender){ - this.mailSender=mailSender; + public void setMailSender(MailSender mailSender) { + this.mailSender = mailSender; } - public void sendMail(String from,String to,String subject,String message){ - SimpleMailMessage mailMessage=new SimpleMailMessage(); + public void sendMail(String from, String to, String subject, String message) { + SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setFrom(from); mailMessage.setTo(to); diff --git a/src/main/java/com/ubiquitech/leaveTrack/form/ChangePasswordForm.java b/src/main/java/com/ubiquitech/leaveTrack/form/ChangePasswordForm.java index 8b14174..3c9b1ae 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/form/ChangePasswordForm.java +++ b/src/main/java/com/ubiquitech/leaveTrack/form/ChangePasswordForm.java @@ -6,10 +6,10 @@ import java.io.Serializable; /** - * Created by vane on 2014/12/02. + * vane created on 2014/12/02. */ public class ChangePasswordForm implements Serializable { - public static final long serialVersionUID=1L; + public static final long serialVersionUID = 1L; @NotEmpty protected String oldPassword; @NotEmpty @@ -17,7 +17,7 @@ public class ChangePasswordForm implements Serializable { @NotEmpty protected String confirmNewPassword; - protected Employee employee=new Employee(); + protected Employee employee = new Employee(); public Employee getEmployee() { return employee; diff --git a/src/main/java/com/ubiquitech/leaveTrack/form/CreateEmployeeForm.java b/src/main/java/com/ubiquitech/leaveTrack/form/CreateEmployeeForm.java index a18d94c..0b1438a 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/form/CreateEmployeeForm.java +++ b/src/main/java/com/ubiquitech/leaveTrack/form/CreateEmployeeForm.java @@ -10,19 +10,19 @@ import java.util.Map; /** - * Created by vane on 2014/11/20. + * vane created on 2014/11/20. */ -public class CreateEmployeeForm implements Serializable{ - private static final long serialVersionUID=1L; +public class CreateEmployeeForm implements Serializable { + private static final long serialVersionUID = 1L; - protected Map map; + protected Map map; @NotEmpty protected String confirmPassword; protected String supervisorName; protected Integer supervisorID; @Valid - protected Employee employee=new Employee(); + protected Employee employee = new Employee(); protected String dateEmployed; @@ -50,8 +50,8 @@ public Map getMap() { } public void setMap(List nameList) { - map = new HashMap(); - map.put("name",nameList); + map = new HashMap(); + map.put("name", nameList); } public String getSupervisorName() { diff --git a/src/main/java/com/ubiquitech/leaveTrack/form/EditEmployeeForm.java b/src/main/java/com/ubiquitech/leaveTrack/form/EditEmployeeForm.java index ccb2c47..8b89f25 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/form/EditEmployeeForm.java +++ b/src/main/java/com/ubiquitech/leaveTrack/form/EditEmployeeForm.java @@ -9,18 +9,18 @@ import java.util.Map; /** - * Created by vane on 2014/12/04. + * vane created on 2014/12/04. */ -public class EditEmployeeForm implements Serializable{ - private static final long serialVersionUID=1L; +public class EditEmployeeForm implements Serializable { + private static final long serialVersionUID = 1L; - protected Map map; + protected Map map; protected String supervisorName; protected Integer supervisorID; @Valid - protected Employee employee=new Employee(); + protected Employee employee = new Employee(); public Employee getEmployee() { return employee; @@ -36,7 +36,7 @@ public Map getMap() { public void setMap(List nameList) { map = new HashMap(); - map.put("name",nameList); + map.put("name", nameList); } public String getSupervisorName() { diff --git a/src/main/java/com/ubiquitech/leaveTrack/form/ProcessEmployeeLeaveForm.java b/src/main/java/com/ubiquitech/leaveTrack/form/ProcessEmployeeLeaveForm.java index a951a5f..4cc0970 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/form/ProcessEmployeeLeaveForm.java +++ b/src/main/java/com/ubiquitech/leaveTrack/form/ProcessEmployeeLeaveForm.java @@ -10,7 +10,7 @@ import java.util.Map; /** - * Created by vane on 2014/12/19. + * vane created on 2014/12/19. */ public class ProcessEmployeeLeaveForm implements Serializable { @@ -24,13 +24,13 @@ public class ProcessEmployeeLeaveForm implements Serializable { @NotEmpty protected Map map; - public Map getMap() { + public Map getMap() { return map; } public void setMap(List nextState) { map = new HashMap(); - map.put("nextState",nextState); + map.put("nextState", nextState); } public Request getRequest() { diff --git a/src/main/java/com/ubiquitech/leaveTrack/form/RequestLeaveForm.java b/src/main/java/com/ubiquitech/leaveTrack/form/RequestLeaveForm.java index 62fa043..624ae7c 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/form/RequestLeaveForm.java +++ b/src/main/java/com/ubiquitech/leaveTrack/form/RequestLeaveForm.java @@ -10,12 +10,12 @@ import java.util.Map; /** - * Created by vane on 2014/12/08. + * vane created on 2014/12/08. */ public class RequestLeaveForm implements Serializable { private static final long serialVersionUID = 1L; - @Valid + @Valid protected Request request = new Request(); @NotEmpty @@ -51,7 +51,7 @@ public Map getMap() { public void setMap(List leaveType) { map = new HashMap(); - map.put("leaveType",leaveType); + map.put("leaveType", leaveType); } public Request getRequest() { diff --git a/src/main/java/com/ubiquitech/leaveTrack/form/RequestQueryForm.java b/src/main/java/com/ubiquitech/leaveTrack/form/RequestQueryForm.java index 3cd3edc..787b585 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/form/RequestQueryForm.java +++ b/src/main/java/com/ubiquitech/leaveTrack/form/RequestQueryForm.java @@ -9,19 +9,19 @@ import java.util.Map; /** - * Created by vane on 2015/01/20. + * vane created on 2015/01/20. */ public class RequestQueryForm implements Serializable { - private static final long serialVersionUID=1L; + private static final long serialVersionUID = 1L; + protected Map map = new HashMap(); + protected Request request = new Request(); String state; String leaveType; String employeeFirstName; String employeeLastName; String supervisorFirstName; String supervisorLastName; - Long requestId; - protected Map map = new HashMap(); - protected Request request = new Request(); + Long requestId; String employeeFullName; String supervisorFullName; diff --git a/src/main/java/com/ubiquitech/leaveTrack/form/ViewRequestsForm.java b/src/main/java/com/ubiquitech/leaveTrack/form/ViewRequestsForm.java index 7e1b4ea..c99f827 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/form/ViewRequestsForm.java +++ b/src/main/java/com/ubiquitech/leaveTrack/form/ViewRequestsForm.java @@ -5,7 +5,7 @@ import java.io.Serializable; /** - * Created by vane on 2015/01/16. + * vane created on 2015/01/16. */ public class ViewRequestsForm implements Serializable { diff --git a/src/main/java/com/ubiquitech/leaveTrack/hash/HashPassword.java b/src/main/java/com/ubiquitech/leaveTrack/hash/HashPassword.java index e4f78d7..9eaed1b 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/hash/HashPassword.java +++ b/src/main/java/com/ubiquitech/leaveTrack/hash/HashPassword.java @@ -3,11 +3,11 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; /** - * Created by vane on 2014/12/02. + * vane created on 2014/12/02. */ public class HashPassword { - public String hash(String password){ + public String hash(String password) { BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); return passwordEncoder.encode(password); diff --git a/src/main/java/com/ubiquitech/leaveTrack/mvc/HelloController.java b/src/main/java/com/ubiquitech/leaveTrack/mvc/HelloController.java index bd9f643..496fc87 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/mvc/HelloController.java +++ b/src/main/java/com/ubiquitech/leaveTrack/mvc/HelloController.java @@ -6,9 +6,7 @@ import com.ubiquitech.leaveTrack.domain.LeaveDays; import com.ubiquitech.leaveTrack.domain.Request; import com.ubiquitech.leaveTrack.services.EmployeeService; -import com.ubiquitech.leaveTrack.services.EmployeeServiceImpl; import com.ubiquitech.leaveTrack.services.RequestService; -import com.ubiquitech.leaveTrack.services.RequestServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.security.core.Authentication; @@ -30,24 +28,24 @@ @Controller public class HelloController { - @Autowired - private EmployeeService employeeService; + @Autowired + private EmployeeService employeeService; @Autowired - private RequestService requestService; + private RequestService requestService; private List fullCalendar = new ArrayList(); - private boolean calendarBoolean=true; + private boolean calendarBoolean = true; - @RequestMapping("/") - public ModelAndView index() { - return new ModelAndView("index"); - } + @RequestMapping("/") + public ModelAndView index() { + return new ModelAndView("index"); + } @RequestMapping("/setupCalendar") - public ModelAndView setupCalendar() { - fullCalendar.clear(); + public ModelAndView setupCalendar() { + fullCalendar.clear(); List requestsApproved; requestsApproved = requestService.getRequestsByState("Approved"); @@ -69,7 +67,7 @@ public ModelAndView setupCalendar() { FullCalendar fc = new FullCalendar("#" + randomColor, title, startDate, endDate); fullCalendar.add(fc); } - calendarBoolean=false; + calendarBoolean = false; } return new ModelAndView("/calendarView"); @@ -83,13 +81,13 @@ public ModelAndView calendar() { @RequestMapping("calendar") - public void calendar(@RequestParam(required = false)HttpServletRequest request,HttpServletResponse response) { + public void calendar(@RequestParam(required = false) HttpServletRequest request, HttpServletResponse response) { // you might find it easier to use Jackson JSON mapper, which is supported by spring. You can return the object // that you want converted to JSON from the method, and spring will automatically convert it to JSON. You just // need to add a @ResponseBody annotation to the method. - //Convert FullCalendar from Java to JSON + //Convert FullCalendar from Java to JSON Gson gson = new Gson(); String jsonAppointment = gson.toJson(fullCalendar); @@ -98,30 +96,30 @@ public void calendar(@RequestParam(required = false)HttpServletRequest request,H response.setCharacterEncoding("UTF-8"); try { response.getWriter().write(jsonAppointment); - } catch (IOException e) { + } catch (IOException e) { e.printStackTrace(); } } - @RequestMapping ("home") - public ModelAndView menu(@ModelAttribute Employee employee){ - calendarBoolean=true; + @RequestMapping("home") + public ModelAndView menu(@ModelAttribute Employee employee) { + calendarBoolean = true; return new ModelAndView("home"); } - @RequestMapping("index") + @RequestMapping("index") public ModelAndView getLoginForm( - @RequestParam(required = false) String authfailed, String logout,String denied,HttpServletRequest request) { + @RequestParam(required = false) String authfailed, String logout, String denied, HttpServletRequest request) { - String message = ""; + String message = ""; if (authfailed != null) { message = "Invalid username or password"; - } else if (logout != null) { - HttpSession session = request.getSession(); + } else if (logout != null) { + HttpSession session = request.getSession(); - session.invalidate(); - message = "Logged Out successfully"; + session.invalidate(); + message = "Logged Out successfully"; } else if (denied != null) { message = "Access denied for this user !"; } @@ -138,19 +136,19 @@ public String geUserPage() { public ModelAndView checkDetails(HttpServletRequest request) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - String username=auth.getName(); + String username = auth.getName(); Employee employee; LeaveDays leaveDays; - try { + try { employee = employeeService.getEmployee(username.toUpperCase()); - HttpSession session=request.getSession(true); - session.setAttribute("employeeSession",employee); - }catch (Exception e){ - e.printStackTrace(); + HttpSession session = request.getSession(true); + session.setAttribute("employeeSession", employee); + } catch (Exception e) { + e.printStackTrace(); } - return new ModelAndView("home"); + return new ModelAndView("home"); } diff --git a/src/main/java/com/ubiquitech/leaveTrack/services/EmployeeService.java b/src/main/java/com/ubiquitech/leaveTrack/services/EmployeeService.java index a7b674f..db96480 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/services/EmployeeService.java +++ b/src/main/java/com/ubiquitech/leaveTrack/services/EmployeeService.java @@ -5,14 +5,17 @@ import java.util.List; /** - * Created by vane on 2014/11/20. + * vane created on 2014/11/20. */ - public interface EmployeeService { public void createEmployee(Employee employee); - public List getEmployeeNames(); + + public List getEmployeeNames(); + public Boolean checkUsername(String userName); + public Employee getEmployee(String username); + public Employee getEmployeeById(Long id); } diff --git a/src/main/java/com/ubiquitech/leaveTrack/services/EmployeeServiceImpl.java b/src/main/java/com/ubiquitech/leaveTrack/services/EmployeeServiceImpl.java index e9a7815..7fed5f6 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/services/EmployeeServiceImpl.java +++ b/src/main/java/com/ubiquitech/leaveTrack/services/EmployeeServiceImpl.java @@ -9,24 +9,23 @@ import java.util.List; /** - * Created by vane on 2014/11/20. + * vane created on 2014/11/20. */ - @Service("employeeService") @Transactional public class EmployeeServiceImpl implements EmployeeService { - @Autowired + @Autowired EmployeeDao employeeDao; @Transactional // this is the only method in this class that needs to be transactional @Override public void createEmployee(Employee employee) { - employeeDao.createEmployee(employee); + employeeDao.createEmployee(employee); } @Override public List getEmployeeNames() { - return employeeDao.getEmployeeNames(); + return employeeDao.getEmployeeNames(); } @Override diff --git a/src/main/java/com/ubiquitech/leaveTrack/services/RequestService.java b/src/main/java/com/ubiquitech/leaveTrack/services/RequestService.java index e7ab9ff..e92f12f 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/services/RequestService.java +++ b/src/main/java/com/ubiquitech/leaveTrack/services/RequestService.java @@ -6,14 +6,17 @@ import java.util.List; /** - * Created by vane on 2014/12/08. + * vane created on 2014/12/08. */ - public interface RequestService { public void createRequest(Request request); + public List getRequestsByStatusAndSupervisorId(String status, Long id); - public List getRequestsByStatusAndRequestId(String status,Long id); + + public List getRequestsByStatusAndRequestId(String status, Long id); + public List getRequestsByState(String status); + public List getQueriedRequests(RequestQueryForm requestQueryForm); } diff --git a/src/main/java/com/ubiquitech/leaveTrack/services/RequestServiceImpl.java b/src/main/java/com/ubiquitech/leaveTrack/services/RequestServiceImpl.java index d9d860e..da6e940 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/services/RequestServiceImpl.java +++ b/src/main/java/com/ubiquitech/leaveTrack/services/RequestServiceImpl.java @@ -11,7 +11,7 @@ import java.util.List; /** - * Created by vane on 2014/12/08. + * vane created on 2014/12/08. */ @Service("requestService") @Transactional @@ -28,12 +28,12 @@ public void createRequest(Request request) { @Override public List getRequestsByStatusAndSupervisorId(String status, Long id) { - return requestDao.getRequestsByStatusAndSupervisorId(status,id); + return requestDao.getRequestsByStatusAndSupervisorId(status, id); } @Override - public List getRequestsByStatusAndRequestId(String status,Long id) { - return requestDao.getRequestsByStatusAndRequestId(status,id); + public List getRequestsByStatusAndRequestId(String status, Long id) { + return requestDao.getRequestsByStatusAndRequestId(status, id); } @Override diff --git a/src/main/java/com/ubiquitech/leaveTrack/webflow/EditEmployeeDatabaseAction.java b/src/main/java/com/ubiquitech/leaveTrack/webflow/EditEmployeeDatabaseAction.java index 54287cf..4d5f313 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/webflow/EditEmployeeDatabaseAction.java +++ b/src/main/java/com/ubiquitech/leaveTrack/webflow/EditEmployeeDatabaseAction.java @@ -3,7 +3,6 @@ import com.ubiquitech.leaveTrack.domain.Employee; import com.ubiquitech.leaveTrack.form.EditEmployeeForm; import com.ubiquitech.leaveTrack.services.EmployeeService; -import com.ubiquitech.leaveTrack.services.EmployeeServiceImpl; import org.springframework.binding.message.MessageBuilder; import org.springframework.binding.message.MessageContext; import org.springframework.webflow.action.EventFactorySupport; @@ -17,28 +16,28 @@ import java.util.List; /** - * Created by vane on 2014/12/04. + * vane created on 2014/12/04. */ -public class EditEmployeeDatabaseAction extends MultiAction{ +public class EditEmployeeDatabaseAction extends MultiAction { private EmployeeService employeeService; public Event setupFormObject(EditEmployeeForm form, SharedAttributeMap map) { - Employee newEmployee =(Employee)map.get("employeeSession"); + Employee newEmployee = (Employee) map.get("employeeSession"); form.setEmployee(newEmployee); List nameList = new ArrayList(); - List employees =employeeService.getEmployeeNames(); + List employees = employeeService.getEmployeeNames(); nameList.add("Select"); - for(Object[] employee:employees){ - String name = employee[1]+" "+ employee[2]; + for (Object[] employee : employees) { + String name = employee[1] + " " + employee[2]; nameList.add(name); } //Prevent from selecting yourself as a supervisor - for (Iterator iterator=nameList.iterator();iterator.hasNext();){ - String name=newEmployee.getFirstName()+" "+newEmployee.getLastName(); - String string=iterator.next(); - if(string.equals(name)){ + for (Iterator iterator = nameList.iterator(); iterator.hasNext(); ) { + String name = newEmployee.getFirstName() + " " + newEmployee.getLastName(); + String string = iterator.next(); + if (string.equals(name)) { iterator.remove(); } } @@ -48,23 +47,23 @@ public Event setupFormObject(EditEmployeeForm form, SharedAttributeMap map) { try { Integer supervisorID = (int) (long) newEmployee.getSupervisor().getId(); form.setSupervisorID(supervisorID); - }catch (Exception e){ + } catch (Exception e) { form.setSupervisorID(0); - } + } - return success(); + return success(); } - public Event setSupervisorName (EditEmployeeForm form, MessageContext messageContext ) { + public Event setSupervisorName(EditEmployeeForm form, MessageContext messageContext) { List map = (List) form.getMap().get("name"); - if (form.getSupervisorID() > 0) { + if (form.getSupervisorID() > 0) { form.setSupervisorName((String) map.get(form.getSupervisorID())); - form.getEmployee().setSupervisor(employeeService.getEmployeeById((long)form.getSupervisorID())); + form.getEmployee().setSupervisor(employeeService.getEmployeeById((long) form.getSupervisorID())); } else { form.setSupervisorName(""); } - if (form.getSupervisorID()== 0) { + if (form.getSupervisorID() == 0) { form.getEmployee().setSupervisor(null); } return success(); @@ -75,28 +74,27 @@ public Event confirmDetails(RequestContext content, MessageContext messageContex Event event = null; EditEmployeeForm form = (EditEmployeeForm) content.getFlowScope().get("target"); - String phoneNumber=form.getEmployee().getPhoneNumber(); - String email=form.getEmployee().getEmail(); - + String phoneNumber = form.getEmployee().getPhoneNumber(); + String email = form.getEmployee().getEmail(); - String emailPattern="^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + String emailPattern = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; - if(email.matches(emailPattern)){ - }else { - MessageBuilder errorMessageBuilder= new MessageBuilder().error(); + if (email.matches(emailPattern)) { + } else { + MessageBuilder errorMessageBuilder = new MessageBuilder().error(); errorMessageBuilder.source("employee.email"); errorMessageBuilder.code("wrongEmailFormat"); messageContext.addMessage(errorMessageBuilder.build()); return new EventFactorySupport().error(this); } - if (phoneNumber.matches("[2][7]\\d{9}")){ + if (phoneNumber.matches("[2][7]\\d{9}")) { return success(); - }else{ - MessageBuilder errorMessageBuilder= new MessageBuilder().error(); + } else { + MessageBuilder errorMessageBuilder = new MessageBuilder().error(); errorMessageBuilder.source("employee.phoneNumber"); errorMessageBuilder.code("wrongPhoneNumberFormat"); messageContext.addMessage(errorMessageBuilder.build()); diff --git a/src/main/java/com/ubiquitech/leaveTrack/webflow/LoginAndCreateEmployeeDatabaseAction.java b/src/main/java/com/ubiquitech/leaveTrack/webflow/LoginAndCreateEmployeeDatabaseAction.java index 669cbd2..8b6ebfa 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/webflow/LoginAndCreateEmployeeDatabaseAction.java +++ b/src/main/java/com/ubiquitech/leaveTrack/webflow/LoginAndCreateEmployeeDatabaseAction.java @@ -19,105 +19,104 @@ import java.util.List; /** - * Created by vane on 2014/11/20. + * vane created on 2014/11/20. */ public class LoginAndCreateEmployeeDatabaseAction extends MultiAction { - private EmployeeService employeeService; final DateTimeFormatter dateFormat = DateTimeFormat.forPattern("yyyy/MM/dd"); + private EmployeeService employeeService; - -public Event setupSupervisorOptions(CreateEmployeeForm form){//Set Supervisor options to be displayed on dropdown list + public Event setupSupervisorOptions(CreateEmployeeForm form) {//Set Supervisor options to be displayed on dropdown list List nameList = new ArrayList(); - List employees =employeeService.getEmployeeNames();//Gets all employee names - nameList.add("Select"); - for(Object[] employee:employees){ - String name = employee[1]+" "+ employee[2]; + List employees = employeeService.getEmployeeNames();//Gets all employee names + nameList.add("Select"); + for (Object[] employee : employees) { + String name = employee[1] + " " + employee[2]; nameList.add(name); - } - form.setMap(nameList); + } + form.setMap(nameList); return success(); } - public Event setSupervisorName (CreateEmployeeForm form, MessageContext messageContext ){ //set supervisor ID based on name selected - List map= (List) form.getMap().get("name"); - if(form.getSupervisorID()>0) { + public Event setSupervisorName(CreateEmployeeForm form, MessageContext messageContext) { //set supervisor ID based on name selected + List map = (List) form.getMap().get("name"); + if (form.getSupervisorID() > 0) { form.setSupervisorName((String) map.get(form.getSupervisorID())); - form.getEmployee().setSupervisor(employeeService.getEmployeeById((long)form.getSupervisorID())); - } else{ - form.setSupervisorName(""); - } + form.getEmployee().setSupervisor(employeeService.getEmployeeById((long) form.getSupervisorID())); + } else { + form.setSupervisorName(""); + } - if(form.getSupervisorID()==0){ + if (form.getSupervisorID() == 0) { form.getEmployee().setSupervisor(null); - } - - boolean found= employeeService.checkUsername(form.getEmployee().getUsername()); - - if (found) { - MessageBuilder errorMessageBuilder = new MessageBuilder().error(); - errorMessageBuilder.source("employee.username"); - errorMessageBuilder.code("userExists"); - messageContext.addMessage(errorMessageBuilder.build()); - return new EventFactorySupport().error(this); - } else { - return success(); - } + } + + boolean found = employeeService.checkUsername(form.getEmployee().getUsername()); + + if (found) { + MessageBuilder errorMessageBuilder = new MessageBuilder().error(); + errorMessageBuilder.source("employee.username"); + errorMessageBuilder.code("userExists"); + messageContext.addMessage(errorMessageBuilder.build()); + return new EventFactorySupport().error(this); + } else { + return success(); + } } public Event hashPassword(CreateEmployeeForm form) throws UnsupportedEncodingException, NoSuchAlgorithmException { - HashPassword hashPassword = new HashPassword(); - String password=form.getPassword(); - String hashedPass = hashPassword.hash(password); - form.getEmployee().setPassword(hashedPass); + HashPassword hashPassword = new HashPassword(); + String password = form.getPassword(); + String hashedPass = hashPassword.hash(password); + form.getEmployee().setPassword(hashedPass); return success(); } - public Event confirmDetails(RequestContext content, MessageContext messageContext) { + public Event confirmDetails(RequestContext content, MessageContext messageContext) { Event event = null; CreateEmployeeForm form = (CreateEmployeeForm) content.getFlowScope().get("target"); String userPassword = form.getPassword(); String confirmPassword = form.getConfirmPassword(); - String phoneNumber=form.getEmployee().getPhoneNumber(); - String email=form.getEmployee().getEmail(); - - if (phoneNumber.matches("[2][7]\\d{9}")){ - }else{ - MessageBuilder errorMessageBuilder= new MessageBuilder().error(); - errorMessageBuilder.source("employee.phoneNumber"); - errorMessageBuilder.code("wrongPhoneNumberFormat"); - messageContext.addMessage(errorMessageBuilder.build()); - return new EventFactorySupport().error(this); - } - - String emailPattern="^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" - + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; - - - if(email.matches(emailPattern)){ - }else { - MessageBuilder errorMessageBuilder= new MessageBuilder().error(); - errorMessageBuilder.source("employee.email"); - errorMessageBuilder.code("wrongEmailFormat"); - messageContext.addMessage(errorMessageBuilder.build()); - return new EventFactorySupport().error(this); - } - - try { - form.getEmployee().getLeaveDays().setYearEmployed(dateFormat.parseLocalDate(form.getDateEmployed())); - }catch (Exception e){ - MessageBuilder errorMessageBuilder= new MessageBuilder().error(); - errorMessageBuilder.source("dateEmployed"); - errorMessageBuilder.code("wrongDateFormat"); - messageContext.addMessage(errorMessageBuilder.build()); - return new EventFactorySupport().error(this); - } + String phoneNumber = form.getEmployee().getPhoneNumber(); + String email = form.getEmployee().getEmail(); + + if (phoneNumber.matches("[2][7]\\d{9}")) { + } else { + MessageBuilder errorMessageBuilder = new MessageBuilder().error(); + errorMessageBuilder.source("employee.phoneNumber"); + errorMessageBuilder.code("wrongPhoneNumberFormat"); + messageContext.addMessage(errorMessageBuilder.build()); + return new EventFactorySupport().error(this); + } + + String emailPattern = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; + + + if (email.matches(emailPattern)) { + } else { + MessageBuilder errorMessageBuilder = new MessageBuilder().error(); + errorMessageBuilder.source("employee.email"); + errorMessageBuilder.code("wrongEmailFormat"); + messageContext.addMessage(errorMessageBuilder.build()); + return new EventFactorySupport().error(this); + } + + try { + form.getEmployee().getLeaveDays().setYearEmployed(dateFormat.parseLocalDate(form.getDateEmployed())); + } catch (Exception e) { + MessageBuilder errorMessageBuilder = new MessageBuilder().error(); + errorMessageBuilder.source("dateEmployed"); + errorMessageBuilder.code("wrongDateFormat"); + messageContext.addMessage(errorMessageBuilder.build()); + return new EventFactorySupport().error(this); + } if (userPassword.equals(confirmPassword)) { return success(); } else { - MessageBuilder errorMessageBuilder= new MessageBuilder().error(); + MessageBuilder errorMessageBuilder = new MessageBuilder().error(); errorMessageBuilder.source("confirmPassword"); errorMessageBuilder.code("passwordMisMatch"); messageContext.addMessage(errorMessageBuilder.build()); @@ -130,9 +129,9 @@ public Event createEmployee(RequestContext content) { Event event = null; CreateEmployeeForm form = (CreateEmployeeForm) content.getFlowScope().get("target"); Employee employee = form.getEmployee(); - if ((employee != null)) { - employeeService.createEmployee(employee); - event = success(); + if ((employee != null)) { + employeeService.createEmployee(employee); + event = success(); } else { event = error(); } diff --git a/src/main/java/com/ubiquitech/leaveTrack/webflow/PasswordChangeDatabaseAction.java b/src/main/java/com/ubiquitech/leaveTrack/webflow/PasswordChangeDatabaseAction.java index e3a923b..6c145f5 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/webflow/PasswordChangeDatabaseAction.java +++ b/src/main/java/com/ubiquitech/leaveTrack/webflow/PasswordChangeDatabaseAction.java @@ -14,7 +14,7 @@ import org.springframework.webflow.execution.RequestContext; /** - * Created by vane on 2014/12/02. + * vane created on 2014/12/02. */ public class PasswordChangeDatabaseAction extends MultiAction { private EmployeeService employeeService; @@ -26,13 +26,13 @@ public Event confirmDetails(RequestContext context, MessageContext messageContex HashPassword hashPassword = new HashPassword(); - /// HttpServletRequest request=(HttpServletRequest) context.getExternalContext().getNativeRequest(); - Employee employee =(Employee)map.get("employeeSession"); + /// HttpServletRequest request=(HttpServletRequest) context.getExternalContext().getNativeRequest(); + Employee employee = (Employee) map.get("employeeSession"); form.setEmployee(employee); - // employee=(Employee)request.getSession().getAttribute("employeeSession"); + // employee=(Employee)request.getSession().getAttribute("employeeSession"); - if (BCrypt.checkpw(form.getOldPassword(),employee.getPassword())) { - } else { + if (BCrypt.checkpw(form.getOldPassword(), employee.getPassword())) { + } else { MessageBuilder errorMessageBuilder = new MessageBuilder().error(); errorMessageBuilder.source("oldPassword"); errorMessageBuilder.code("passwordInvalid"); @@ -54,7 +54,6 @@ public Event confirmDetails(RequestContext context, MessageContext messageContex } - public void setEmployeeService(EmployeeService employeeService) { this.employeeService = employeeService; } diff --git a/src/main/java/com/ubiquitech/leaveTrack/webflow/ProcessRequestsActions.java b/src/main/java/com/ubiquitech/leaveTrack/webflow/ProcessRequestsActions.java index 0e13dcf..09d753c 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/webflow/ProcessRequestsActions.java +++ b/src/main/java/com/ubiquitech/leaveTrack/webflow/ProcessRequestsActions.java @@ -1,13 +1,11 @@ package com.ubiquitech.leaveTrack.webflow; import com.ubiquitech.leaveTrack.services.EmployeeService; -import com.ubiquitech.leaveTrack.services.EmployeeServiceImpl; import com.ubiquitech.leaveTrack.services.RequestService; -import com.ubiquitech.leaveTrack.services.RequestServiceImpl; import org.springframework.webflow.action.MultiAction; /** - * Created by vane on 2014/12/10. + * vane created on 2014/12/10. */ public class ProcessRequestsActions extends MultiAction { private RequestService requestService; @@ -17,6 +15,7 @@ public class ProcessRequestsActions extends MultiAction { public void setEmployeeService(EmployeeService employeeService) { this.employeeService = employeeService; } + public void setRequestService(RequestService requestService) { this.requestService = requestService; } diff --git a/src/main/java/com/ubiquitech/leaveTrack/webflow/RequestLeaveActions.java b/src/main/java/com/ubiquitech/leaveTrack/webflow/RequestLeaveActions.java index 7d9f1f7..b805a46 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/webflow/RequestLeaveActions.java +++ b/src/main/java/com/ubiquitech/leaveTrack/webflow/RequestLeaveActions.java @@ -5,9 +5,7 @@ import com.ubiquitech.leaveTrack.eMail.Mail; import com.ubiquitech.leaveTrack.form.RequestLeaveForm; import com.ubiquitech.leaveTrack.services.EmployeeService; -import com.ubiquitech.leaveTrack.services.EmployeeServiceImpl; import com.ubiquitech.leaveTrack.services.RequestService; -import com.ubiquitech.leaveTrack.services.RequestServiceImpl; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.slf4j.Logger; @@ -24,17 +22,17 @@ import java.util.List; /** - * Created by vane on 2014/12/08. + * vane created on 2014/12/08. */ public class RequestLeaveActions extends MultiAction { - private RequestService requestService; - private EmployeeService employeeService; - final DateTimeFormatter dateFormat = DateTimeFormat.forPattern("yyyy/MM/dd"); - final Logger logger = LoggerFactory.getLogger(RequestLeaveActions.class); + final DateTimeFormatter dateFormat = DateTimeFormat.forPattern("yyyy/MM/dd"); + final Logger logger = LoggerFactory.getLogger(RequestLeaveActions.class); + private RequestService requestService; + private EmployeeService employeeService; @Autowired private Mail mail; - public Event setupSupervisorOptions(RequestLeaveForm form){ + public Event setupSupervisorOptions(RequestLeaveForm form) { form.getRequest().setComment(""); form.getRequest().setState("Logged"); List leaveTypes = new ArrayList(); @@ -51,8 +49,8 @@ public Event confirmDetails(RequestLeaveForm form, MessageContext messageContext Event event = null; try { form.getRequest().setStartDate(dateFormat.parseLocalDate(form.getStartDate())); - }catch (Exception e){ - MessageBuilder errorMessageBuilder= new MessageBuilder().error(); + } catch (Exception e) { + MessageBuilder errorMessageBuilder = new MessageBuilder().error(); errorMessageBuilder.source("startDate"); errorMessageBuilder.code("wrongDateFormat"); messageContext.addMessage(errorMessageBuilder.build()); @@ -62,8 +60,8 @@ public Event confirmDetails(RequestLeaveForm form, MessageContext messageContext try { form.getRequest().setEndDate(dateFormat.parseLocalDate(form.getEndDate())); return success(); - }catch (Exception e){ - MessageBuilder errorMessageBuilder= new MessageBuilder().error(); + } catch (Exception e) { + MessageBuilder errorMessageBuilder = new MessageBuilder().error(); errorMessageBuilder.source("endDate"); errorMessageBuilder.code("wrongDateFormat"); messageContext.addMessage(errorMessageBuilder.build()); @@ -72,30 +70,30 @@ public Event confirmDetails(RequestLeaveForm form, MessageContext messageContext } public Event apply(RequestLeaveForm form, SharedAttributeMap map) { - Employee employee =(Employee)map.get("employeeSession"); + Employee employee = (Employee) map.get("employeeSession"); Request request = form.getRequest(); - // request.setSupervisorId(employee.getSupervisor().getId()); + // request.setSupervisorId(employee.getSupervisor().getId()); request.setEmployee(employee); requestService.createRequest(request); return success(); } - public Event sendSupervisorEmail(RequestLeaveForm form,SharedAttributeMap map) { - Employee employee =(Employee)map.get("employeeSession"); - String supervisorEmail= employee.getSupervisor().getEmail(); - try { - mail.sendMail( + public Event sendSupervisorEmail(RequestLeaveForm form, SharedAttributeMap map) { + Employee employee = (Employee) map.get("employeeSession"); + String supervisorEmail = employee.getSupervisor().getEmail(); + try { + mail.sendMail( /*FROM:*/ employee.getEmail(), /*TO:*/ supervisorEmail, /*SUBJECT:*/ "Leave Request", /*MESSAGE:*/ "==========This is an automatically generated Email, Please do not reply.==========\n" + employee.getFirstName() + " " + employee.getLastName() - + " has applied for leave, please log into leaveTrack to process this request."); + + " has applied for leave, please log into leaveTrack to process this request."); return success(); - }catch (Exception e){ - logger.error("Email could not be sent"); - logger.debug("Debug message",e); - return error(); - } + } catch (Exception e) { + logger.error("Email could not be sent"); + logger.debug("Debug message", e); + return error(); + } } diff --git a/src/main/java/com/ubiquitech/leaveTrack/webflow/RequestQueryActions.java b/src/main/java/com/ubiquitech/leaveTrack/webflow/RequestQueryActions.java index 14696a1..2c1223d 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/webflow/RequestQueryActions.java +++ b/src/main/java/com/ubiquitech/leaveTrack/webflow/RequestQueryActions.java @@ -11,37 +11,37 @@ import java.util.List; /** - * Created by vane on 2015/01/20. + * vane created on 2015/01/20. */ public class RequestQueryActions extends MultiAction { private RequestService requestService; private EmployeeService employeeService; private List requestsFound; - private List requestSelected; + private List requestSelected; - public Event getRequests(RequestContext context){ + public Event getRequests(RequestContext context) { - RequestQueryForm form=(RequestQueryForm) context.getFlowScope().get("target"); - requestsFound=requestService.getQueriedRequests(form); + RequestQueryForm form = (RequestQueryForm) context.getFlowScope().get("target"); + requestsFound = requestService.getQueriedRequests(form); context.getFlowScope().put("requestsFound", requestsFound); return success(); } - public Event selectLeaveRequest(int requestId,RequestQueryForm form){ + public Event selectLeaveRequest(int requestId, RequestQueryForm form) { - requestSelected = requestService.getRequestsByStatusAndRequestId("",(long)requestId); + requestSelected = requestService.getRequestsByStatusAndRequestId("", (long) requestId); form.setRequest(requestSelected.get(0)); - form.setEmployeeFullName(form.getRequest().getEmployee().getFirstName()+" "+ form.getRequest().getEmployee().getLastName()); - form.setSupervisorFullName(form.getRequest().getEmployee().getSupervisor().getFirstName()+" "+form.getRequest().getEmployee().getSupervisor().getLastName()); + form.setEmployeeFullName(form.getRequest().getEmployee().getFirstName() + " " + form.getRequest().getEmployee().getLastName()); + form.setSupervisorFullName(form.getRequest().getEmployee().getSupervisor().getFirstName() + " " + form.getRequest().getEmployee().getSupervisor().getLastName()); return success(); } public void setEmployeeService(EmployeeService employeeService) { - this.employeeService = employeeService; - } + this.employeeService = employeeService; + } - public void setRequestService(RequestService requestService) { - this.requestService = requestService; - } + public void setRequestService(RequestService requestService) { + this.requestService = requestService; + } } diff --git a/src/main/java/com/ubiquitech/leaveTrack/webflow/SupervisorActions.java b/src/main/java/com/ubiquitech/leaveTrack/webflow/SupervisorActions.java index 54dbd7e..6ab87c7 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/webflow/SupervisorActions.java +++ b/src/main/java/com/ubiquitech/leaveTrack/webflow/SupervisorActions.java @@ -18,35 +18,35 @@ import java.util.List; /** - * Created by vane on 2014/12/11. + * vane created on 2014/12/11. */ public class SupervisorActions extends MultiAction { + final Logger logger = LoggerFactory.getLogger(RequestLeaveActions.class); private RequestService requestService; private EmployeeService employeeService; - private List requestsInLoggedStatus; - private List requestSelected; + private List requestsInLoggedStatus; + private List requestSelected; @Autowired private Mail mail; - final Logger logger = LoggerFactory.getLogger(RequestLeaveActions.class); - public Event getLoggedRequests(RequestContext context,SharedAttributeMap map){ - Employee employee =(Employee)map.get("employeeSession"); - System.out.println("Employee ID:"+employee.getId()); - requestsInLoggedStatus = requestService.getRequestsByStatusAndSupervisorId("Logged",employee.getId()); + public Event getLoggedRequests(RequestContext context, SharedAttributeMap map) { + Employee employee = (Employee) map.get("employeeSession"); + System.out.println("Employee ID:" + employee.getId()); + requestsInLoggedStatus = requestService.getRequestsByStatusAndSupervisorId("Logged", employee.getId()); context.getFlowScope().put("requestsLogged", requestsInLoggedStatus); return success(); } - public Event selectLeaveRequest(int requestId,ProcessEmployeeLeaveForm form){ + public Event selectLeaveRequest(int requestId, ProcessEmployeeLeaveForm form) { - requestSelected = requestService.getRequestsByStatusAndRequestId("Logged",(long)requestId); + requestSelected = requestService.getRequestsByStatusAndRequestId("Logged", (long) requestId); form.setRequest(requestSelected.get(0)); //setting next state options List nextState = new ArrayList(); nextState.add("Approved"); nextState.add("Rejected"); form.setMap(nextState); - form.setEmployeeFullName(form.getRequest().getEmployee().getFirstName()+" "+ form.getRequest().getEmployee().getLastName()); + form.setEmployeeFullName(form.getRequest().getEmployee().getFirstName() + " " + form.getRequest().getEmployee().getLastName()); return success(); } @@ -56,17 +56,17 @@ public Event updateRequest(ProcessEmployeeLeaveForm form) { return success(); } - public Event sendEmplyeeEmail(ProcessEmployeeLeaveForm form,SharedAttributeMap map) { + public Event sendEmplyeeEmail(ProcessEmployeeLeaveForm form, SharedAttributeMap map) { Employee employee = (Employee) map.get("employeeSession"); - String employeeEmail=form.getRequest().getEmployee().getEmail(); - System.out.println("Employee Email: "+ employeeEmail ); + String employeeEmail = form.getRequest().getEmployee().getEmail(); + System.out.println("Employee Email: " + employeeEmail); try { mail.sendMail( /*FROM:*/ employee.getEmail(), /*TO:*/ employeeEmail, /*SUBJECT:*/ "Leave Request", /*MESSAGE:*/ "==========This is an automatically generated Email, Please do not reply.==========\n" + employee.getFirstName() + " " + employee.getLastName() - + " has "+form.getRequest().getState()+" you leave request, Please log into LeaveTrack for more details"); + + " has " + form.getRequest().getState() + " you leave request, Please log into LeaveTrack for more details"); return success(); } catch (Exception e) { logger.error("Email could not be sent"); diff --git a/src/main/java/com/ubiquitech/leaveTrack/webflow/ViewRequestAction.java b/src/main/java/com/ubiquitech/leaveTrack/webflow/ViewRequestAction.java index 2964232..a234787 100644 --- a/src/main/java/com/ubiquitech/leaveTrack/webflow/ViewRequestAction.java +++ b/src/main/java/com/ubiquitech/leaveTrack/webflow/ViewRequestAction.java @@ -5,12 +5,12 @@ import org.springframework.webflow.execution.RequestContext; /** - * Created by vane on 2015/01/16. + * vane created on 2015/01/16. */ public class ViewRequestAction extends MultiAction { - public Event setupCalendar(RequestContext context){ - return success(); + public Event setupCalendar(RequestContext context) { + return success(); } } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 2b931ba..1b0aa2b 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -20,6 +20,6 @@ - + diff --git a/src/main/webapp/WEB-INF/flows/changePassword/changePassword-flow.xml b/src/main/webapp/WEB-INF/flows/changePassword/changePassword-flow.xml index 3f56af6..49aba41 100644 --- a/src/main/webapp/WEB-INF/flows/changePassword/changePassword-flow.xml +++ b/src/main/webapp/WEB-INF/flows/changePassword/changePassword-flow.xml @@ -1,8 +1,8 @@ - + start-state="changePassword"> @@ -16,7 +16,8 @@ - + diff --git a/src/main/webapp/WEB-INF/flows/changePassword/changePassword.jsp b/src/main/webapp/WEB-INF/flows/changePassword/changePassword.jsp index c202923..f835ba2 100644 --- a/src/main/webapp/WEB-INF/flows/changePassword/changePassword.jsp +++ b/src/main/webapp/WEB-INF/flows/changePassword/changePassword.jsp @@ -5,11 +5,11 @@ Time: 08:58 AM To change this template use File | Settings | File Templates. --%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> - +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> -
+
-
-

 Change Password - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

-
+
+

 Change Password + ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

+
-
- +
+ - - - - - - - - - - - - - - - - -
Enter your current password:
Enter the new password:
Enter the new password again:
+ + + + + + + + + + + + + + + + +
Enter your current password:
Enter the new password:
Enter the new password again:
-
-      Cancel -      Save -
- <%@ include file="/resources/theme/footer.jsp" %> -
-
+
+      Cancel +      Save + + <%@ include file="/resources/theme/footer.jsp" %> +
+
diff --git a/src/main/webapp/WEB-INF/flows/changePassword/passwordChangeSuccessful.jsp b/src/main/webapp/WEB-INF/flows/changePassword/passwordChangeSuccessful.jsp index ae49e82..6ec5eb3 100644 --- a/src/main/webapp/WEB-INF/flows/changePassword/passwordChangeSuccessful.jsp +++ b/src/main/webapp/WEB-INF/flows/changePassword/passwordChangeSuccessful.jsp @@ -1,3 +1,4 @@ + <%-- Created by IntelliJ IDEA. User: vane @@ -7,34 +8,38 @@ --%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@page session="true"%> - - +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@page session="true" %> + +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> +<%--@elvariable id="_csrf" type="org.springframework.security.web.csrf.DefaultCsrfToken"--%> leaveTrack " rel="stylesheet"/> - -
+ +
+ +
+

 Change Password + ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

+
+ +
+
+

Password successfully changed

-
-

 Change Password - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

-
+

Please log out

-
-
-

Password successfully changed

-

Please log out

-
- - -
+
+ + +
+
+ <%@ include file="/resources/theme/footer.jsp" %> +
- <%@ include file="/resources/theme/footer.jsp" %> -
-
diff --git a/src/main/webapp/WEB-INF/flows/createEmployee/confirmDetails.jsp b/src/main/webapp/WEB-INF/flows/createEmployee/confirmDetails.jsp index 7c589ab..be74333 100644 --- a/src/main/webapp/WEB-INF/flows/createEmployee/confirmDetails.jsp +++ b/src/main/webapp/WEB-INF/flows/createEmployee/confirmDetails.jsp @@ -5,10 +5,11 @@ Time: 02:13 PM To change this template use File | Settings | File Templates. --%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

 Create New Employee - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

+ ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

-

Please verify the following data is correct. Press +

Please verify the following data is correct. Press the "Back" button to make any necessary changes.

@@ -94,8 +96,8 @@
-      Back -      Create +      Back +      Create <%@ include file="/resources/theme/footer.jsp" %>
diff --git a/src/main/webapp/WEB-INF/flows/createEmployee/createEmployee-flow.xml b/src/main/webapp/WEB-INF/flows/createEmployee/createEmployee-flow.xml index 97c08b2..aee8361 100644 --- a/src/main/webapp/WEB-INF/flows/createEmployee/createEmployee-flow.xml +++ b/src/main/webapp/WEB-INF/flows/createEmployee/createEmployee-flow.xml @@ -1,34 +1,35 @@ - + start-state="createEmployeeForm"> - + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - - + + @@ -41,11 +42,11 @@ - - - - - + + + + + diff --git a/src/main/webapp/WEB-INF/flows/createEmployee/createEmployeeForm.jsp b/src/main/webapp/WEB-INF/flows/createEmployee/createEmployeeForm.jsp index 8795921..a43f795 100644 --- a/src/main/webapp/WEB-INF/flows/createEmployee/createEmployeeForm.jsp +++ b/src/main/webapp/WEB-INF/flows/createEmployee/createEmployeeForm.jsp @@ -5,11 +5,11 @@ Time: 02:15 PM To change this template use File | Settings | File Templates. --%> - -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> @@ -37,7 +37,8 @@

 Create New Employee - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

+ ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

@@ -47,18 +48,18 @@ - + - - + + - - + + @@ -68,11 +69,11 @@ - + - + @@ -87,23 +88,23 @@ - + - + - +
Username:
First name:
Last name:
Phone number
email:
Job title:
Supervisor
Date employed:
Password
Confirm password

-      Cancel -      Next +      Cancel +      Next <%@ include file="/resources/theme/footer.jsp" %>
diff --git a/src/main/webapp/WEB-INF/flows/createEmployee/createSuccessful.jsp b/src/main/webapp/WEB-INF/flows/createEmployee/createSuccessful.jsp index 4af58a5..dd01242 100644 --- a/src/main/webapp/WEB-INF/flows/createEmployee/createSuccessful.jsp +++ b/src/main/webapp/WEB-INF/flows/createEmployee/createSuccessful.jsp @@ -1,4 +1,6 @@ +<%--@elvariable id="_csrf" type="org.springframework.security.web.csrf.DefaultCsrfToken"--%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> <%-- Created by IntelliJ IDEA. User: vane @@ -7,27 +9,30 @@ To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> - + leaveTrack " rel="stylesheet"/> - +

 Create New Employee - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

+ ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

Employee successfully created

+

New employee can now log into LeaveTrack

+
- +
diff --git a/src/main/webapp/WEB-INF/flows/editEmployee/confirmDetails.jsp b/src/main/webapp/WEB-INF/flows/editEmployee/confirmDetails.jsp index 9a2a40b..559641e 100644 --- a/src/main/webapp/WEB-INF/flows/editEmployee/confirmDetails.jsp +++ b/src/main/webapp/WEB-INF/flows/editEmployee/confirmDetails.jsp @@ -13,10 +13,10 @@ To change this template use File | Settings | File Templates. --%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

 Edit Employee - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

+ ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

-

Please verify the following data is correct. Press +

Please verify the following data is correct. Press the "Back" button to make any necessary changes.

@@ -87,8 +88,8 @@
-      Back -      Confirm +      Back +      Confirm <%@ include file="/resources/theme/footer.jsp" %>
diff --git a/src/main/webapp/WEB-INF/flows/editEmployee/editEmployee-flow.xml b/src/main/webapp/WEB-INF/flows/editEmployee/editEmployee-flow.xml index 7aad8fa..aceee97 100644 --- a/src/main/webapp/WEB-INF/flows/editEmployee/editEmployee-flow.xml +++ b/src/main/webapp/WEB-INF/flows/editEmployee/editEmployee-flow.xml @@ -1,6 +1,6 @@ - @@ -23,8 +23,8 @@ - - + + diff --git a/src/main/webapp/WEB-INF/flows/editEmployee/editEmployeeForm.jsp b/src/main/webapp/WEB-INF/flows/editEmployee/editEmployeeForm.jsp index 918c05d..c553260 100644 --- a/src/main/webapp/WEB-INF/flows/editEmployee/editEmployeeForm.jsp +++ b/src/main/webapp/WEB-INF/flows/editEmployee/editEmployeeForm.jsp @@ -13,10 +13,10 @@ To change this template use File | Settings | File Templates. --%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

 Edit Employee - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

+ ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

@@ -46,13 +47,13 @@ - - + + - - + + @@ -62,11 +63,11 @@ - + - + @@ -81,8 +82,8 @@
First name:
Last name:
Phone number
email:
Job title:
Supervisor

-      Cancel -      Next +      Cancel +      Next <%@ include file="/resources/theme/footer.jsp" %>
diff --git a/src/main/webapp/WEB-INF/flows/editEmployee/editSuccessful.jsp b/src/main/webapp/WEB-INF/flows/editEmployee/editSuccessful.jsp index d49164a..bbc4313 100644 --- a/src/main/webapp/WEB-INF/flows/editEmployee/editSuccessful.jsp +++ b/src/main/webapp/WEB-INF/flows/editEmployee/editSuccessful.jsp @@ -5,35 +5,40 @@ Time: 03:45 PM To change this template use File | Settings | File Templates. --%> +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> +<%--@elvariable id="_csrf" type="org.springframework.security.web.csrf.DefaultCsrfToken"--%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@page session="true"%> - +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@page session="true" %> + - - leaveTrack - " rel="stylesheet"/> - - - -
+ + leaveTrack + " rel="stylesheet"/> + + + +
-
-

 Change Password - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

-
+
+

 Change Password + ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

+
-
-
-

Employee details successfully changed

-

Please log out

-
- - -
-
- <%@ include file="/resources/theme/footer.jsp" %> -
+
+
+

Employee details successfully changed

+ +

Please log out

+ +
+ + +
- + <%@ include file="/resources/theme/footer.jsp" %> +
+
+ diff --git a/src/main/webapp/WEB-INF/flows/processRequests/emailSendError.jsp b/src/main/webapp/WEB-INF/flows/processRequests/emailSendError.jsp index 5a73ee4..3b0ca81 100644 --- a/src/main/webapp/WEB-INF/flows/processRequests/emailSendError.jsp +++ b/src/main/webapp/WEB-INF/flows/processRequests/emailSendError.jsp @@ -5,9 +5,9 @@ Time: 02:28 PM To change this template use File | Settings | File Templates. --%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@page session="true"%> - +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@page session="true" %> + leaveTrack @@ -18,13 +18,16 @@

 Process Leave Request - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

+ ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

The Application has been processed successfully

+

However an email could not be sent to the employee

+
diff --git a/src/main/webapp/WEB-INF/flows/processRequests/processLeaveRequest.jsp b/src/main/webapp/WEB-INF/flows/processRequests/processLeaveRequest.jsp index 7389dd1..b637c14 100644 --- a/src/main/webapp/WEB-INF/flows/processRequests/processLeaveRequest.jsp +++ b/src/main/webapp/WEB-INF/flows/processRequests/processLeaveRequest.jsp @@ -1,4 +1,3 @@ - <%-- Created by IntelliJ IDEA. User: vane @@ -6,10 +5,11 @@ Time: 07:33 PM To change this template use File | Settings | File Templates. --%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

 Process Leave Request - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

+ ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

@@ -39,52 +40,53 @@ Employee name - + Leave type - + Start Date - + End Date - + Reason: Next Status - + - - + + Comment: - - + +
-      Back -      Save -      Cancel +      Back +      Save +      Cancel <%@ include file="/resources/theme/footer.jsp" %>
diff --git a/src/main/webapp/WEB-INF/flows/processRequests/processRequests-flow.xml b/src/main/webapp/WEB-INF/flows/processRequests/processRequests-flow.xml index 752caea..0199849 100644 --- a/src/main/webapp/WEB-INF/flows/processRequests/processRequests-flow.xml +++ b/src/main/webapp/WEB-INF/flows/processRequests/processRequests-flow.xml @@ -1,6 +1,6 @@ - - + - - + + @@ -38,7 +38,7 @@ - + diff --git a/src/main/webapp/WEB-INF/flows/processRequests/receiveRequests.jsp b/src/main/webapp/WEB-INF/flows/processRequests/receiveRequests.jsp index b50bc5d..e3f11c5 100644 --- a/src/main/webapp/WEB-INF/flows/processRequests/receiveRequests.jsp +++ b/src/main/webapp/WEB-INF/flows/processRequests/receiveRequests.jsp @@ -6,10 +6,10 @@ To change this template use File | Settings | File Templates. --%> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> + pageEncoding="ISO-8859-1" %> -<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%> -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> Leave Track @@ -27,6 +27,7 @@ height: 22px; text-align: center; } + .heading { font-size: 18px; color: white; @@ -57,7 +58,7 @@ - Process + Process ${requests.employee.firstName} ${requests.employee.lastName} @@ -73,9 +74,9 @@
-      Cancel - +      Cancel + - + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/flows/processRequests/requestProcessedconfirmation.jsp b/src/main/webapp/WEB-INF/flows/processRequests/requestProcessedconfirmation.jsp index 64fb055..a80b7a9 100644 --- a/src/main/webapp/WEB-INF/flows/processRequests/requestProcessedconfirmation.jsp +++ b/src/main/webapp/WEB-INF/flows/processRequests/requestProcessedconfirmation.jsp @@ -5,9 +5,10 @@ Time: 04:42 PM To change this template use File | Settings | File Templates. --%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@page session="true"%> - +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@page session="true" %> + leaveTrack @@ -18,13 +19,16 @@

 Process Leave Request - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

+ ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

The Application has been processed successfully

+

An email has been sent to the employee

+
diff --git a/src/main/webapp/WEB-INF/flows/requestLeave/confirmDetails.jsp b/src/main/webapp/WEB-INF/flows/requestLeave/confirmDetails.jsp index d78b430..72fb3d7 100644 --- a/src/main/webapp/WEB-INF/flows/requestLeave/confirmDetails.jsp +++ b/src/main/webapp/WEB-INF/flows/requestLeave/confirmDetails.jsp @@ -5,12 +5,13 @@ Time: 03:29 PM To change this template use File | Settings | File Templates. --%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%>

 Request Leave - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

+ ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

-

Please verify the following data is correct. Press +

Please verify the following data is correct. Press the "Back" button to make any necessary changes.

- +
Confirm Details @@ -61,15 +63,16 @@
Reason:
-
-      Back -      Confirm +
+      Back +      Confirm
<%@ include file="/resources/theme/footer.jsp" %>
diff --git a/src/main/webapp/WEB-INF/flows/requestLeave/emailSendError.jsp b/src/main/webapp/WEB-INF/flows/requestLeave/emailSendError.jsp index a4bd3f1..b0e2379 100644 --- a/src/main/webapp/WEB-INF/flows/requestLeave/emailSendError.jsp +++ b/src/main/webapp/WEB-INF/flows/requestLeave/emailSendError.jsp @@ -5,9 +5,10 @@ Time: 02:28 PM To change this template use File | Settings | File Templates. --%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@page session="true"%> - +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@page session="true" %> +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> + leaveTrack @@ -18,13 +19,16 @@

 Request Leave - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

+ ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

Leave request has been logged successfully

+

However an email could not be sent to your supervisor due to technical difficulties

+
diff --git a/src/main/webapp/WEB-INF/flows/requestLeave/requestLeave-flow.xml b/src/main/webapp/WEB-INF/flows/requestLeave/requestLeave-flow.xml index 489ee3c..70e1ef6 100644 --- a/src/main/webapp/WEB-INF/flows/requestLeave/requestLeave-flow.xml +++ b/src/main/webapp/WEB-INF/flows/requestLeave/requestLeave-flow.xml @@ -1,13 +1,13 @@ - - + @@ -19,7 +19,7 @@ - + @@ -27,7 +27,7 @@ - + diff --git a/src/main/webapp/WEB-INF/flows/requestLeave/requestLeaveForm.jsp b/src/main/webapp/WEB-INF/flows/requestLeave/requestLeaveForm.jsp index af55502..b4713eb 100644 --- a/src/main/webapp/WEB-INF/flows/requestLeave/requestLeaveForm.jsp +++ b/src/main/webapp/WEB-INF/flows/requestLeave/requestLeaveForm.jsp @@ -5,10 +5,11 @@ Time: 02:03 PM To change this template use File | Settings | File Templates. --%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> - @@ -38,7 +39,8 @@

 Request Leave - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

+ ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

@@ -48,7 +50,7 @@ - - + - + - - + +
Request Type: + @@ -58,26 +60,26 @@
Start Date
End Date
Reason:

-      Cancel -      Next +      Cancel +      Next - <%@ include file="/resources/theme/footer.jsp" %> + <%@ include file="/resources/theme/footer.jsp" %>
diff --git a/src/main/webapp/WEB-INF/flows/requestLeave/requestSuccessful.jsp b/src/main/webapp/WEB-INF/flows/requestLeave/requestSuccessful.jsp index c965e37..551afa6 100644 --- a/src/main/webapp/WEB-INF/flows/requestLeave/requestSuccessful.jsp +++ b/src/main/webapp/WEB-INF/flows/requestLeave/requestSuccessful.jsp @@ -1,4 +1,3 @@ -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%-- Created by IntelliJ IDEA. User: vane @@ -6,9 +5,11 @@ Time: 08:14 AM To change this template use File | Settings | File Templates. --%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@page session="true"%> - +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@page session="true" %> + leaveTrack @@ -19,13 +20,16 @@

 Request Leave - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

+ ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

Leave request has been logged successfully

+

The request has been sent to your supervisor for processing

+
diff --git a/src/main/webapp/WEB-INF/flows/requestQuery/requestDetails.jsp b/src/main/webapp/WEB-INF/flows/requestQuery/requestDetails.jsp index d697225..9890030 100644 --- a/src/main/webapp/WEB-INF/flows/requestQuery/requestDetails.jsp +++ b/src/main/webapp/WEB-INF/flows/requestQuery/requestDetails.jsp @@ -5,10 +5,11 @@ Time: 06:02 PM To change this template use File | Settings | File Templates. --%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

 Query Requests - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

+ ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

@@ -38,52 +40,66 @@ Employee name - + Supervisor name - + Leave type - + Start Date - + End Date - + Reason: Status - - + + Supervisor comment:
-      Back -      Cancel +      Back +      Cancel <%@ include file="/resources/theme/footer.jsp" %>
diff --git a/src/main/webapp/WEB-INF/flows/requestQuery/requestQuery-flow.xml b/src/main/webapp/WEB-INF/flows/requestQuery/requestQuery-flow.xml index 1d16aa0..1b1a042 100644 --- a/src/main/webapp/WEB-INF/flows/requestQuery/requestQuery-flow.xml +++ b/src/main/webapp/WEB-INF/flows/requestQuery/requestQuery-flow.xml @@ -1,8 +1,8 @@ - + start-state="requestQueryForm"> @@ -18,7 +18,7 @@ - + @@ -26,14 +26,14 @@ - + - + - - + + @@ -51,8 +51,7 @@ - - + diff --git a/src/main/webapp/WEB-INF/flows/requestQuery/requestQueryForm.jsp b/src/main/webapp/WEB-INF/flows/requestQuery/requestQueryForm.jsp index 740e10b..f559731 100644 --- a/src/main/webapp/WEB-INF/flows/requestQuery/requestQueryForm.jsp +++ b/src/main/webapp/WEB-INF/flows/requestQuery/requestQueryForm.jsp @@ -5,10 +5,11 @@ Time: 02:33 PM To change this template use File | Settings | File Templates. --%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

 Request Query - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

+ ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

@@ -38,20 +40,20 @@ - + - - + + - + - + - + @@ -77,8 +79,8 @@
Employee firstname: Employee surname:
Supervisor firstname: Supervisor surname:
Request ID:

-      Cancel -      Next +      Cancel +      Next <%@ include file="/resources/theme/footer.jsp" %>
diff --git a/src/main/webapp/WEB-INF/flows/requestQuery/requestQueryResults.jsp b/src/main/webapp/WEB-INF/flows/requestQuery/requestQueryResults.jsp index 0b71379..9ea9da4 100644 --- a/src/main/webapp/WEB-INF/flows/requestQuery/requestQueryResults.jsp +++ b/src/main/webapp/WEB-INF/flows/requestQuery/requestQueryResults.jsp @@ -6,10 +6,10 @@ To change this template use File | Settings | File Templates. --%> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> + pageEncoding="ISO-8859-1" %> -<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%> -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> Leave Track @@ -27,6 +27,7 @@ height: 22px; text-align: center; } + .heading { font-size: 18px; color: white; @@ -53,8 +54,8 @@ End Date Status - - <%--@elvariable id="requestsFound" type="java.util.List"--%> + + <%--@elvariable id="requestsFound" type="java.util.List"--%> @@ -74,8 +75,8 @@
-      Back -      Cancel +      Back +      Cancel diff --git a/src/main/webapp/WEB-INF/pages/calendarView.jsp b/src/main/webapp/WEB-INF/pages/calendarView.jsp index ec56930..43c23ed 100644 --- a/src/main/webapp/WEB-INF/pages/calendarView.jsp +++ b/src/main/webapp/WEB-INF/pages/calendarView.jsp @@ -5,10 +5,11 @@ Time: 10:27 AM To change this template use File | Settings | File Templates. --%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> @@ -18,84 +19,85 @@ xmlns:spring="http://www.springframework.org/tags" xmlns:form="http://www.springframework.org/tags/form"> - - " rel="stylesheet"/> - " rel="stylesheet" media="print"/> - " rel="stylesheet"/> - - - + + " rel="stylesheet"/> + " rel="stylesheet" media="print"/> + " rel="stylesheet"/> + + + - - - + }); + + + -
+
-
-

 Calendar - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

-
+
+

 Calendar + ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

+
-
-
-
- -
+
+
+
+ +
<%@ include file="/resources/theme/footer.jsp" %> -
+
- + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/pages/footer.jsp b/src/main/webapp/WEB-INF/pages/footer.jsp index 6c19bf0..20d026f 100644 --- a/src/main/webapp/WEB-INF/pages/footer.jsp +++ b/src/main/webapp/WEB-INF/pages/footer.jsp @@ -7,4 +7,5 @@ --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %>

- + + diff --git a/src/main/webapp/WEB-INF/pages/home.jsp b/src/main/webapp/WEB-INF/pages/home.jsp index 54f5f1e..c664b41 100644 --- a/src/main/webapp/WEB-INF/pages/home.jsp +++ b/src/main/webapp/WEB-INF/pages/home.jsp @@ -1,36 +1,37 @@ <%--@elvariable id="employeeSession" type="com.ubiquitech.leaveTrack.domain.Employee"--%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - + - - leaveTrack - " rel="stylesheet"/> - - - -
-
-

 LeaveTrack Main Menu - ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username})

-
+ + leaveTrack + " rel="stylesheet"/> + + + +
+
+

 LeaveTrack Main Menu + ${employeeSession.firstName} ${employeeSession.lastName} (${employeeSession.username}) +

+
-
- - <%@ include file="/resources/theme/footer.jsp" %> -
+
+ + <%@ include file="/resources/theme/footer.jsp" %> +
-
+
- + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/pages/index.jsp b/src/main/webapp/WEB-INF/pages/index.jsp index 076b4a6..d77d102 100644 --- a/src/main/webapp/WEB-INF/pages/index.jsp +++ b/src/main/webapp/WEB-INF/pages/index.jsp @@ -1,44 +1,49 @@ <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@page session="true"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@page session="true" %> - + - leaveTrack + leaveTrack " rel="stylesheet"/> - - + + -
+
-
-

Log in to LeaveTrack

-
-
-

    ${message}

+
+

Log in to LeaveTrack

+
+
+

    ${message}

-
- - - - - - - - - - - - - -
<%--@declare id="username"--%>
<%--@declare id="password"--%>
-
-
+
+ + + + + + + + + + + + + +
<%--@declare id="username"--%>
<%--@declare id="password"--%> +
+
+
- -
-
- + +
+
+ \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/security-config.xml b/src/main/webapp/WEB-INF/security-config.xml index 227a7ff..287d352 100644 --- a/src/main/webapp/WEB-INF/security-config.xml +++ b/src/main/webapp/WEB-INF/security-config.xml @@ -1,40 +1,40 @@ - - - - - - - + + + + + + - + - - + - + + users-by-username-query="select username,password,active from employee where username=?" + authorities-by-username-query="select username,role from employee where username=?"/> - - - + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/spring-servlet.xml b/src/main/webapp/WEB-INF/spring-servlet.xml new file mode 100644 index 0000000..46439d7 --- /dev/null +++ b/src/main/webapp/WEB-INF/spring-servlet.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + com.ubiquitech.leaveTrack.domain.Employee + com.ubiquitech.leaveTrack.domain.LeaveDays + com.ubiquitech.leaveTrack.domain.Request + + + + + org.hibernate.dialect.MySQL5Dialect + ${hibernate.show_sql} + + + + + + + + + + + + + + + + + + true + true + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 5a2ac84..0f286b1 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -1,28 +1,28 @@ - - leaveTrack + leaveTrack - spring - org.springframework.web.servlet.DispatcherServlet + spring + org.springframework.web.servlet.DispatcherServlet 1 - + - - spring - / - + + spring + / + com.ubiquitech.leaveTrack.SessionListener - contextConfigLocation + contextConfigLocation /WEB-INF/spring-servlet.xml, /WEB-INF/security-config.xml @@ -39,7 +39,7 @@ org.springframework.web.filter.DelegatingFilterProxy - + springSecurityFilterChain /* diff --git a/src/main/webapp/WEB-INF/webflowContext.xml b/src/main/webapp/WEB-INF/webflowContext.xml index 0174e47..7ff7651 100644 --- a/src/main/webapp/WEB-INF/webflowContext.xml +++ b/src/main/webapp/WEB-INF/webflowContext.xml @@ -1,29 +1,28 @@ - + http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.3.xsd"> - - + class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> + + + development="false" + validator="validator"/> + id="flowRegistry" + base-path="/WEB-INF/flows" + flow-builder-services="flowBuilderServices"> @@ -36,9 +35,10 @@ - + - + diff --git a/src/main/webapp/resources/fullCalendar/fullcalendar.css b/src/main/webapp/resources/fullCalendar/fullcalendar.css index 31b5afe..09a1928 100644 --- a/src/main/webapp/resources/fullCalendar/fullcalendar.css +++ b/src/main/webapp/resources/fullCalendar/fullcalendar.css @@ -4,21 +4,20 @@ * (c) 2013 Adam Shaw */ - .fc { - direction: ltr; - text-align: left; + direction: ltr; + text-align: left; } .fc-rtl { - text-align: right; + text-align: right; } -body .fc { /* extra precedence to overcome jqui */ - font-size: 1em; +body .fc { + /* extra precedence to overcome jqui */ + font-size: 1em; } - /* Colors --------------------------------------------------------------------------------------------------*/ @@ -29,182 +28,189 @@ body .fc { /* extra precedence to overcome jqui */ .fc-unthemed tbody, .fc-unthemed .fc-row, .fc-unthemed .fc-popover { - border-color: #ddd; + border-color: #ddd; } .fc-unthemed .fc-popover { - background-color: #fff; + background-color: #fff; } .fc-unthemed hr, .fc-unthemed .fc-popover .fc-header { - background: #eee; + background: #eee; } .fc-unthemed .fc-popover .fc-header .fc-close { - color: #666; + color: #666; } .fc-unthemed .fc-today { - background: #fcf8e3; + background: #fcf8e3; } -.fc-highlight { /* when user is selecting cells */ - background: #bce8f1; - opacity: .3; - filter: alpha(opacity=30); /* for IE */ +.fc-highlight { + /* when user is selecting cells */ + background: #bce8f1; + opacity: .3; + filter: alpha(opacity=30); /* for IE */ } -.fc-bgevent { /* default look for background events */ - background: rgb(143, 223, 130); - opacity: .3; - filter: alpha(opacity=30); /* for IE */ +.fc-bgevent { + /* default look for background events */ + background: rgb(143, 223, 130); + opacity: .3; + filter: alpha(opacity=30); /* for IE */ } -.fc-nonbusiness { /* default look for non-business-hours areas */ - /* will inherit .fc-bgevent's styles */ - background: #ccc; +.fc-nonbusiness { + /* default look for non-business-hours areas */ + /* will inherit .fc-bgevent's styles */ + background: #ccc; } - /* Icons (inline elements with styled text that mock arrow icons) --------------------------------------------------------------------------------------------------*/ .fc-icon { - display: inline-block; - font-size: 2em; - line-height: .5em; - height: .5em; /* will make the total height 1em */ - font-family: "Courier New", Courier, monospace; + display: inline-block; + font-size: 2em; + line-height: .5em; + height: .5em; /* will make the total height 1em */ + font-family: "Courier New", Courier, monospace; } .fc-icon-left-single-arrow:after { - content: "\02039"; - font-weight: bold; + content: "\02039"; + font-weight: bold; } .fc-icon-right-single-arrow:after { - content: "\0203A"; - font-weight: bold; + content: "\0203A"; + font-weight: bold; } .fc-icon-left-double-arrow:after { - content: "\000AB"; + content: "\000AB"; } .fc-icon-right-double-arrow:after { - content: "\000BB"; + content: "\000BB"; } .fc-icon-x:after { - content: "\000D7"; + content: "\000D7"; } - /* Buttons (styled ' - ) - .click(function() { - // don't process clicks for disabled buttons - if (!button.hasClass(tm + '-state-disabled')) { - - buttonClick(); - - // after the click action, if the button becomes the "active" tab, or disabled, - // it should never have a hover class, so remove it now. - if ( - button.hasClass(tm + '-state-active') || - button.hasClass(tm + '-state-disabled') - ) { - button.removeClass(tm + '-state-hover'); - } - } - }) - .mousedown(function() { - // the *down* effect (mouse pressed in). - // only on buttons that are not the "active" tab, or disabled - button - .not('.' + tm + '-state-active') - .not('.' + tm + '-state-disabled') - .addClass(tm + '-state-down'); - }) - .mouseup(function() { - // undo the *down* effect - button.removeClass(tm + '-state-down'); - }) - .hover( - function() { - // the *hover* effect. - // only on buttons that are not the "active" tab, or disabled - button - .not('.' + tm + '-state-active') - .not('.' + tm + '-state-disabled') - .addClass(tm + '-state-hover'); - }, - function() { - // undo the *hover* effect - button - .removeClass(tm + '-state-hover') - .removeClass(tm + '-state-down'); // if mouseleave happens before mouseup - } - ); - - groupChildren = groupChildren.add(button); - } - } - }); - - if (isOnlyButtons) { - groupChildren - .first().addClass(tm + '-corner-left').end() - .last().addClass(tm + '-corner-right').end(); - } - - if (groupChildren.length > 1) { - groupEl = $('
'); - if (isOnlyButtons) { - groupEl.addClass('fc-button-group'); - } - groupEl.append(groupChildren); - sectionEl.append(groupEl); - } - else { - sectionEl.append(groupChildren); // 1 or 0 children - } - }); - } - - return sectionEl; - } - - - function updateTitle(text) { - el.find('h2').text(text); - } - - - function activateButton(buttonName) { - el.find('.fc-' + buttonName + '-button') - .addClass(tm + '-state-active'); - } - - - function deactivateButton(buttonName) { - el.find('.fc-' + buttonName + '-button') - .removeClass(tm + '-state-active'); - } - - - function disableButton(buttonName) { - el.find('.fc-' + buttonName + '-button') - .attr('disabled', 'disabled') - .addClass(tm + '-state-disabled'); - } - - - function enableButton(buttonName) { - el.find('.fc-' + buttonName + '-button') - .removeAttr('disabled') - .removeClass(tm + '-state-disabled'); - } - - - function getViewsWithButtons() { - return viewsWithButtons; - } - -} - -;; - -fc.sourceNormalizers = []; -fc.sourceFetchers = []; - -var ajaxDefaults = { - dataType: 'json', - cache: false -}; - -var eventGUID = 1; - - -function EventManager(options) { // assumed to be a calendar - var t = this; - - - // exports - t.isFetchNeeded = isFetchNeeded; - t.fetchEvents = fetchEvents; - t.addEventSource = addEventSource; - t.removeEventSource = removeEventSource; - t.updateEvent = updateEvent; - t.renderEvent = renderEvent; - t.removeEvents = removeEvents; - t.clientEvents = clientEvents; - t.mutateEvent = mutateEvent; - t.normalizeEventDateProps = normalizeEventDateProps; - t.ensureVisibleEventRange = ensureVisibleEventRange; - - - // imports - var trigger = t.trigger; - var getView = t.getView; - var reportEvents = t.reportEvents; - - - // locals - var stickySource = { events: [] }; - var sources = [ stickySource ]; - var rangeStart, rangeEnd; - var currentFetchID = 0; - var pendingSourceCnt = 0; - var loadingLevel = 0; - var cache = []; // holds events that have already been expanded - - - $.each( - (options.events ? [ options.events ] : []).concat(options.eventSources || []), - function(i, sourceInput) { - var source = buildEventSource(sourceInput); - if (source) { - sources.push(source); - } - } - ); - - - - /* Fetching - -----------------------------------------------------------------------------*/ - - - function isFetchNeeded(start, end) { - return !rangeStart || // nothing has been fetched yet? - // or, a part of the new range is outside of the old range? (after normalizing) - start.clone().stripZone() < rangeStart.clone().stripZone() || - end.clone().stripZone() > rangeEnd.clone().stripZone(); - } - - - function fetchEvents(start, end) { - rangeStart = start; - rangeEnd = end; - cache = []; - var fetchID = ++currentFetchID; - var len = sources.length; - pendingSourceCnt = len; - for (var i=0; i eventRange system - function ensureVisibleEventRange(range) { - var allDay; - - if (!range.end) { - - allDay = range.allDay; // range might be more event-ish than we think - if (allDay == null) { - allDay = !range.start.hasTime(); - } - - range = { - start: range.start, - end: t.getDefaultEventEnd(allDay, range.start) - }; - } - return range; - } - - - // If the given event is a recurring event, break it down into an array of individual instances. - // If not a recurring event, return an array with the single original event. - // If given a falsy input (probably because of a failed buildEventFromInput call), returns an empty array. - // HACK: can override the recurring window by providing custom rangeStart/rangeEnd (for businessHours). - function expandEvent(abstractEvent, _rangeStart, _rangeEnd) { - var events = []; - var dowHash; - var dow; - var i; - var date; - var startTime, endTime; - var start, end; - var event; - - _rangeStart = _rangeStart || rangeStart; - _rangeEnd = _rangeEnd || rangeEnd; - - if (abstractEvent) { - if (abstractEvent._recurring) { - - // make a boolean hash as to whether the event occurs on each day-of-week - if ((dow = abstractEvent.dow)) { - dowHash = {}; - for (i = 0; i < dow.length; i++) { - dowHash[dow[i]] = true; - } - } - - // iterate through every day in the current range - date = _rangeStart.clone().stripTime(); // holds the date of the current day - while (date.isBefore(_rangeEnd)) { - - if (!dowHash || dowHash[date.day()]) { // if everyday, or this particular day-of-week - - startTime = abstractEvent.start; // the stored start and end properties are times (Durations) - endTime = abstractEvent.end; // " - start = date.clone(); - end = null; - - if (startTime) { - start = start.time(startTime); - } - if (endTime) { - end = date.clone().time(endTime); - } - - event = $.extend({}, abstractEvent); // make a copy of the original - assignDatesToEvent( - start, end, - !startTime && !endTime, // allDay? - event - ); - events.push(event); - } - - date.add(1, 'days'); - } - } - else { - events.push(abstractEvent); // return the original event. will be a one-item array - } - } - - return events; - } - - - - /* Event Modification Math - -----------------------------------------------------------------------------------------*/ - - - // Modifies an event and all related events by applying the given properties. - // Special date-diffing logic is used for manipulation of dates. - // If `props` does not contain start/end dates, the updated values are assumed to be the event's current start/end. - // All date comparisons are done against the event's pristine _start and _end dates. - // Returns an object with delta information and a function to undo all operations. - // - function mutateEvent(event, props) { - var miscProps = {}; - var clearEnd; - var dateDelta; - var durationDelta; - var undoFunc; - - props = props || {}; - - // ensure new date-related values to compare against - if (!props.start) { - props.start = event.start.clone(); - } - if (props.end === undefined) { - props.end = event.end ? event.end.clone() : null; - } - if (props.allDay == null) { // is null or undefined? - props.allDay = event.allDay; - } - - normalizeEventDateProps(props); // massages start/end/allDay - - // clear the end date if explicitly changed to null - clearEnd = event._end !== null && props.end === null; - - // compute the delta for moving the start and end dates together - if (props.allDay) { - dateDelta = diffDay(props.start, event._start); // whole-day diff from start-of-day - } - else { - dateDelta = diffDayTime(props.start, event._start); - } - - // compute the delta for moving the end date (after applying dateDelta) - if (!clearEnd && props.end) { - durationDelta = diffDayTime( - // new duration - props.end, - props.start - ).subtract(diffDayTime( - // subtract old duration - event._end || t.getDefaultEventEnd(event._allDay, event._start), - event._start - )); - } - - // gather all non-date-related properties - $.each(props, function(name, val) { - if (isMiscEventPropName(name)) { - if (val !== undefined) { - miscProps[name] = val; - } - } - }); - - // apply the operations to the event and all related events - undoFunc = mutateEvents( - clientEvents(event._id), // get events with this ID - clearEnd, - props.allDay, - dateDelta, - durationDelta, - miscProps - ); - - return { - dateDelta: dateDelta, - durationDelta: durationDelta, - undo: undoFunc - }; - } - - - // Modifies an array of events in the following ways (operations are in order): - // - clear the event's `end` - // - convert the event to allDay - // - add `dateDelta` to the start and end - // - add `durationDelta` to the event's duration - // - assign `miscProps` to the event - // - // Returns a function that can be called to undo all the operations. - // - // TODO: don't use so many closures. possible memory issues when lots of events with same ID. - // - function mutateEvents(events, clearEnd, allDay, dateDelta, durationDelta, miscProps) { - var isAmbigTimezone = t.getIsAmbigTimezone(); - var undoFunctions = []; - - // normalize zero-length deltas to be null - if (dateDelta && !dateDelta.valueOf()) { dateDelta = null; } - if (durationDelta && !durationDelta.valueOf()) { durationDelta = null; } - - $.each(events, function(i, event) { - var oldProps; - var newProps; - - // build an object holding all the old values, both date-related and misc. - // for the undo function. - oldProps = { - start: event.start.clone(), - end: event.end ? event.end.clone() : null, - allDay: event.allDay - }; - $.each(miscProps, function(name) { - oldProps[name] = event[name]; - }); - - // new date-related properties. work off the original date snapshot. - // ok to use references because they will be thrown away when backupEventDates is called. - newProps = { - start: event._start, - end: event._end, - allDay: event._allDay - }; - - if (clearEnd) { - newProps.end = null; - } - - newProps.allDay = allDay; - - normalizeEventDateProps(newProps); // massages start/end/allDay - - if (dateDelta) { - newProps.start.add(dateDelta); - if (newProps.end) { - newProps.end.add(dateDelta); - } - } - - if (durationDelta) { - if (!newProps.end) { - newProps.end = t.getDefaultEventEnd(newProps.allDay, newProps.start); - } - newProps.end.add(durationDelta); - } - - // if the dates have changed, and we know it is impossible to recompute the - // timezone offsets, strip the zone. - if ( - isAmbigTimezone && - !newProps.allDay && - (dateDelta || durationDelta) - ) { - newProps.start.stripZone(); - if (newProps.end) { - newProps.end.stripZone(); - } - } - - $.extend(event, miscProps, newProps); // copy over misc props, then date-related props - backupEventDates(event); // regenerate internal _start/_end/_allDay - - undoFunctions.push(function() { - $.extend(event, oldProps); - backupEventDates(event); // regenerate internal _start/_end/_allDay - }); - }); - - return function() { - for (var i = 0; i < undoFunctions.length; i++) { - undoFunctions[i](); - } - }; - } - - - /* Business Hours - -----------------------------------------------------------------------------------------*/ - - t.getBusinessHoursEvents = getBusinessHoursEvents; - - - // Returns an array of events as to when the business hours occur in the given view. - // Abuse of our event system :( - function getBusinessHoursEvents() { - var optionVal = options.businessHours; - var defaultVal = { - className: 'fc-nonbusiness', - start: '09:00', - end: '17:00', - dow: [ 1, 2, 3, 4, 5 ], // monday - friday - rendering: 'inverse-background' - }; - var view = t.getView(); - var eventInput; - - if (optionVal) { - if (typeof optionVal === 'object') { - // option value is an object that can override the default business hours - eventInput = $.extend({}, defaultVal, optionVal); - } - else { - // option value is `true`. use default business hours - eventInput = defaultVal; - } - } - - if (eventInput) { - return expandEvent( - buildEventFromInput(eventInput), - view.start, - view.end - ); - } - - return []; - } - - - /* Overlapping / Constraining - -----------------------------------------------------------------------------------------*/ - - t.isEventRangeAllowed = isEventRangeAllowed; - t.isSelectionRangeAllowed = isSelectionRangeAllowed; - t.isExternalDropRangeAllowed = isExternalDropRangeAllowed; - - - function isEventRangeAllowed(range, event) { - var source = event.source || {}; - var constraint = firstDefined( - event.constraint, - source.constraint, - options.eventConstraint - ); - var overlap = firstDefined( - event.overlap, - source.overlap, - options.eventOverlap - ); - - range = ensureVisibleEventRange(range); // ensure a proper range with an end for isRangeAllowed - - return isRangeAllowed(range, constraint, overlap, event); - } - - - function isSelectionRangeAllowed(range) { - return isRangeAllowed(range, options.selectConstraint, options.selectOverlap); - } - - - // when `eventProps` is defined, consider this an event. - // `eventProps` can contain misc non-date-related info about the event. - function isExternalDropRangeAllowed(range, eventProps) { - var eventInput; - var event; - - // note: very similar logic is in View's reportExternalDrop - if (eventProps) { - eventInput = $.extend({}, eventProps, range); - event = expandEvent(buildEventFromInput(eventInput))[0]; - } - - if (event) { - return isEventRangeAllowed(range, event); - } - else { // treat it as a selection - - range = ensureVisibleEventRange(range); // ensure a proper range with an end for isSelectionRangeAllowed - - return isSelectionRangeAllowed(range); - } - } - - - // Returns true if the given range (caused by an event drop/resize or a selection) is allowed to exist - // according to the constraint/overlap settings. - // `event` is not required if checking a selection. - function isRangeAllowed(range, constraint, overlap, event) { - var constraintEvents; - var anyContainment; - var i, otherEvent; - var otherOverlap; - - // normalize. fyi, we're normalizing in too many places :( - range = { - start: range.start.clone().stripZone(), - end: range.end.clone().stripZone() - }; - - // the range must be fully contained by at least one of produced constraint events - if (constraint != null) { - - // not treated as an event! intermediate data structure - // TODO: use ranges in the future - constraintEvents = constraintToEvents(constraint); - - anyContainment = false; - for (i = 0; i < constraintEvents.length; i++) { - if (eventContainsRange(constraintEvents[i], range)) { - anyContainment = true; - break; - } - } - - if (!anyContainment) { - return false; - } - } - - for (i = 0; i < cache.length; i++) { // loop all events and detect overlap - otherEvent = cache[i]; - - // don't compare the event to itself or other related [repeating] events - if (event && event._id === otherEvent._id) { - continue; - } - - // there needs to be an actual intersection before disallowing anything - if (eventIntersectsRange(otherEvent, range)) { - - // evaluate overlap for the given range and short-circuit if necessary - if (overlap === false) { - return false; - } - else if (typeof overlap === 'function' && !overlap(otherEvent, event)) { - return false; - } - - // if we are computing if the given range is allowable for an event, consider the other event's - // EventObject-specific or Source-specific `overlap` property - if (event) { - otherOverlap = firstDefined( - otherEvent.overlap, - (otherEvent.source || {}).overlap - // we already considered the global `eventOverlap` - ); - if (otherOverlap === false) { - return false; - } - if (typeof otherOverlap === 'function' && !otherOverlap(event, otherEvent)) { - return false; - } - } - } - } - - return true; - } - - - // Given an event input from the API, produces an array of event objects. Possible event inputs: - // 'businessHours' - // An event ID (number or string) - // An object with specific start/end dates or a recurring event (like what businessHours accepts) - function constraintToEvents(constraintInput) { - - if (constraintInput === 'businessHours') { - return getBusinessHoursEvents(); - } - - if (typeof constraintInput === 'object') { - return expandEvent(buildEventFromInput(constraintInput)); - } - - return clientEvents(constraintInput); // probably an ID - } - - - // Does the event's date range fully contain the given range? - // start/end already assumed to have stripped zones :( - function eventContainsRange(event, range) { - var eventStart = event.start.clone().stripZone(); - var eventEnd = t.getEventEnd(event).stripZone(); - - return range.start >= eventStart && range.end <= eventEnd; - } - - - // Does the event's date range intersect with the given range? - // start/end already assumed to have stripped zones :( - function eventIntersectsRange(event, range) { - var eventStart = event.start.clone().stripZone(); - var eventEnd = t.getEventEnd(event).stripZone(); - - return range.start < eventEnd && range.end > eventStart; - } - -} + // Locals + // ----------------------------------------------------------------------------------- -// updates the "backup" properties, which are preserved in order to compute diffs later on. -function backupEventDates(event) { - event._allDay = event.allDay; - event._start = event.start.clone(); - event._end = event.end ? event.end.clone() : null; -} + var _element = element[0]; + var header; + var headerElement; + var content; + var tm; // for making theme classes + var viewSpecCache = {}; + var currentView; + var suggestedViewHeight; + var windowResizeProxy; // wraps the windowResize function + var ignoreWindowResize = 0; + var date; + var events = []; -;; -/* An abstract class for the "basic" views, as well as month view. Renders one or more rows of day cells. -----------------------------------------------------------------------------------------------------------------------*/ -// It is a manager for a DayGrid subcomponent, which does most of the heavy lifting. -// It is responsible for managing width/height. + // Main Rendering + // ----------------------------------------------------------------------------------- + -var BasicView = fcViews.basic = View.extend({ + if (options.defaultDate != null) { + date = t.moment(options.defaultDate); + } + else { + date = t.getNow(); + } - dayGrid: null, // the main subcomponent that does most of the heavy lifting - dayNumbersVisible: false, // display day numbers on each day cell? - weekNumbersVisible: false, // display week numbers along the side? + function render(inc) { + if (!content) { + initialRender(); + } + else if (elementVisible()) { + // mainly for the public API + calcSize(); + renderView(inc); + } + } + + + function initialRender() { + tm = options.theme ? 'ui' : 'fc'; + element.addClass('fc'); - weekNumberWidth: null, // width of all the week-number cells running down the side + if (options.isRTL) { + element.addClass('fc-rtl'); + } + else { + element.addClass('fc-ltr'); + } - headRowEl: null, // the fake row element of the day-of-week header + if (options.theme) { + element.addClass('ui-widget'); + } + else { + element.addClass('fc-unthemed'); + } + content = $("
").prependTo(element); - initialize: function() { - this.dayGrid = new DayGrid(this); - this.coordMap = this.dayGrid.coordMap; // the view's date-to-cell mapping is identical to the subcomponent's - }, + header = new Header(t, options); + headerElement = header.render(); + if (headerElement) { + element.prepend(headerElement); + } + renderView(options.defaultView); - // Sets the display range and computes all necessary dates - setRange: function(range) { - View.prototype.setRange.call(this, range); // call the super-method + if (options.handleWindowResize) { + windowResizeProxy = debounce(windowResize, options.windowResizeDelay); // prevents rapid calls + $(window).resize(windowResizeProxy); + } + } - this.dayGrid.breakOnWeeks = /year|month|week/.test(this.intervalUnit); // do before setRange - this.dayGrid.setRange(range); - }, + function destroy() { - // Compute the value to feed into setRange. Overrides superclass. - computeRange: function(date) { - var range = View.prototype.computeRange.call(this, date); // get value from the super-method - - // year and month views should be aligned with weeks. this is already done for week - if (/year|month/.test(range.intervalUnit)) { - range.start.startOf('week'); - range.start = this.skipHiddenDays(range.start); + if (currentView) { + currentView.destroyView(); + } - // make end-of-week if not already - if (range.end.weekday()) { - range.end.add(1, 'week').startOf('week'); - range.end = this.skipHiddenDays(range.end, -1, true); // exclusively move backwards - } - } - - return range; - }, + header.destroy(); + content.remove(); + element.removeClass('fc fc-ltr fc-rtl fc-unthemed ui-widget'); + $(window).unbind('resize', windowResizeProxy); + } - // Renders the view into `this.el`, which should already be assigned - render: function() { - - this.dayNumbersVisible = this.dayGrid.rowCnt > 1; // TODO: make grid responsible - this.weekNumbersVisible = this.opt('weekNumbers'); - this.dayGrid.numbersVisible = this.dayNumbersVisible || this.weekNumbersVisible; - - this.el.addClass('fc-basic-view').html(this.renderHtml()); - - this.headRowEl = this.el.find('thead .fc-row'); - - this.scrollerEl = this.el.find('.fc-day-grid-container'); - this.dayGrid.coordMap.containerEl = this.scrollerEl; // constrain clicks/etc to the dimensions of the scroller - - this.dayGrid.el = this.el.find('.fc-day-grid'); - this.dayGrid.render(this.hasRigidRows()); - }, + function elementVisible() { + return element.is(':visible'); + } + + + // View Rendering + // ----------------------------------------------------------------------------------- + + + // Renders a view because of a date change, view-type change, or for the first time + function renderView(viewType) { + ignoreWindowResize++; + + // if viewType is changing, destroy the old view + if (currentView && viewType && currentView.type !== viewType) { + header.deactivateButton(currentView.type); + freezeContentHeight(); // prevent a scroll jump when view element is removed + if (currentView.start) { // rendered before? + currentView.destroyView(); + } + currentView.el.remove(); + currentView = null; + } + + // if viewType changed, or the view was never created, create a fresh view + if (!currentView && viewType) { + currentView = instantiateView(viewType); + currentView.el = $("
").appendTo(content); + header.activateButton(viewType); + } + + if (currentView) { + + // in case the view should render a period of time that is completely hidden + date = currentView.massageCurrentDate(date); + + // render or rerender the view + if ( + !currentView.start || // never rendered before + !date.isWithin(currentView.intervalStart, currentView.intervalEnd) // implicit date window change + ) { + if (elementVisible()) { + + freezeContentHeight(); + if (currentView.start) { // rendered before? + currentView.destroyView(); + } + currentView.setDate(date); + currentView.renderView(); + unfreezeContentHeight(); + + // need to do this after View::render, so dates are calculated + updateTitle(); + updateTodayButton(); + + getAndRenderEvents(); + } + } + } + + unfreezeContentHeight(); // undo any lone freezeContentHeight calls + ignoreWindowResize--; + } + + + // View Instantiation + // ----------------------------------------------------------------------------------- + + + // Given a view name for a custom view or a standard view, creates a ready-to-go View object + function instantiateView(viewType) { + var spec = getViewSpec(viewType); + + return new spec['class'](t, spec.options, viewType); + } + + + // Gets information about how to create a view + function getViewSpec(requestedViewType) { + var allDefaultButtonText = options.defaultButtonText || {}; + var allButtonText = options.buttonText || {}; + var hash = options.views || {}; // the `views` option object + var viewType = requestedViewType; + var viewOptionsChain = []; + var viewOptions; + var viewClass; + var duration, unit, unitIsSingle = false; + var buttonText; + + if (viewSpecCache[requestedViewType]) { + return viewSpecCache[requestedViewType]; + } + + function processSpecInput(input) { + if (typeof input === 'function') { + viewClass = input; + } + else if (typeof input === 'object') { + $.extend(viewOptions, input); + } + } + + // iterate up a view's spec ancestor chain util we find a class to instantiate + while (viewType && !viewClass) { + viewOptions = {}; // only for this specific view in the ancestry + processSpecInput(fcViews[viewType]); // $.fullCalendar.views, lower precedence + processSpecInput(hash[viewType]); // options at initialization, higher precedence + viewOptionsChain.unshift(viewOptions); // record older ancestors first + viewType = viewOptions.type; + } + + viewOptionsChain.unshift({}); // jQuery's extend needs at least one arg + viewOptions = $.extend.apply($, viewOptionsChain); // combine all, newer ancestors overwritting old + + if (viewClass) { + + duration = viewOptions.duration || viewClass.duration; + if (duration) { + duration = moment.duration(duration); + unit = computeIntervalUnit(duration); + unitIsSingle = computeIntervalAs(unit, duration) === 1; + } + + // options that are specified per the view's duration, like "week" or "day" + if (unitIsSingle && hash[unit]) { + viewOptions = $.extend({}, hash[unit], viewOptions); // lowest priority + } + + // compute the final text for the button representing this view + buttonText = + allButtonText[requestedViewType] || // init options, like "agendaWeek" + (unitIsSingle ? allButtonText[unit] : null) || // init options, like "week" + allDefaultButtonText[requestedViewType] || // lang data, like "agendaWeek" + (unitIsSingle ? allDefaultButtonText[unit] : null) || // lang data, like "week" + viewOptions.buttonText || + viewClass.buttonText || + (duration ? humanizeDuration(duration) : null) || + requestedViewType; + + return (viewSpecCache[requestedViewType] = { + 'class': viewClass, + options: viewOptions, + buttonText: buttonText + }); + } + } + + + // Returns a boolean about whether the view is okay to instantiate at some point + function isValidViewType(viewType) { + return Boolean(getViewSpec(viewType)); + } + + + // Gets the text that should be displayed on a view's button in the header + function getViewButtonText(viewType) { + var spec = getViewSpec(viewType); + + if (spec) { + return spec.buttonText; + } + } + + + // Resizing + // ----------------------------------------------------------------------------------- + + + t.getSuggestedViewHeight = function () { + if (suggestedViewHeight === undefined) { + calcSize(); + } + return suggestedViewHeight; + }; + + + t.isHeightAuto = function () { + return options.contentHeight === 'auto' || options.height === 'auto'; + }; + + + function updateSize(shouldRecalc) { + if (elementVisible()) { + + if (shouldRecalc) { + _calcSize(); + } + + ignoreWindowResize++; + currentView.updateSize(true); // isResize=true. will poll getSuggestedViewHeight() and isHeightAuto() + ignoreWindowResize--; + + return true; // signal success + } + } - // Make subcomponents ready for cleanup - destroy: function() { - this.dayGrid.destroy(); - View.prototype.destroy.call(this); // call the super-method - }, - - - // Builds the HTML skeleton for the view. - // The day-grid component will render inside of a container defined by this HTML. - renderHtml: function() { - return '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
' + - this.dayGrid.headHtml() + // render the day-of-week headers - '
' + - '
' + - '
' + - '
' + - '
'; - }, + function calcSize() { + if (elementVisible()) { + _calcSize(); + } + } - // Generates the HTML that will go before the day-of week header cells. - // Queried by the DayGrid subcomponent when generating rows. Ordering depends on isRTL. - headIntroHtml: function() { - if (this.weekNumbersVisible) { - return '' + - '' + - '' + // needed for matchCellWidths - htmlEscape(this.opt('weekNumberTitle')) + - '' + - ''; - } - }, - - - // Generates the HTML that will go before content-skeleton cells that display the day/week numbers. - // Queried by the DayGrid subcomponent. Ordering depends on isRTL. - numberIntroHtml: function(row) { - if (this.weekNumbersVisible) { - return '' + - '' + - '' + // needed for matchCellWidths - this.calendar.calculateWeekNumber(this.dayGrid.getCell(row, 0).start) + - '' + - ''; - } - }, - - - // Generates the HTML that goes before the day bg cells for each day-row. - // Queried by the DayGrid subcomponent. Ordering depends on isRTL. - dayIntroHtml: function() { - if (this.weekNumbersVisible) { - return ''; - } - }, - - - // Generates the HTML that goes before every other type of row generated by DayGrid. Ordering depends on isRTL. - // Affects helper-skeleton and highlight-skeleton rows. - introHtml: function() { - if (this.weekNumbersVisible) { - return ''; - } - }, - - - // Generates the HTML for the s of the "number" row in the DayGrid's content skeleton. - // The number row will only exist if either day numbers or week numbers are turned on. - numberCellHtml: function(cell) { - var date = cell.start; - var classes; - - if (!this.dayNumbersVisible) { // if there are week numbers but not day numbers - return ''; // will create an empty space above events :( - } - - classes = this.dayGrid.getDayClasses(date); - classes.unshift('fc-day-number'); - - return '' + - '' + - date.date() + - ''; - }, - - - // Generates an HTML attribute string for setting the width of the week number column, if it is known - weekNumberStyleAttr: function() { - if (this.weekNumberWidth !== null) { - return 'style="width:' + this.weekNumberWidth + 'px"'; - } - return ''; - }, - - - // Determines whether each row should have a constant height - hasRigidRows: function() { - var eventLimit = this.opt('eventLimit'); - return eventLimit && typeof eventLimit !== 'number'; - }, - - - /* Dimensions - ------------------------------------------------------------------------------------------------------------------*/ + function _calcSize() { // assumes elementVisible + if (typeof options.contentHeight === 'number') { // exists and not 'auto' + suggestedViewHeight = options.contentHeight; + } + else if (typeof options.height === 'number') { // exists and not 'auto' + suggestedViewHeight = options.height - (headerElement ? headerElement.outerHeight(true) : 0); + } + else { + suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5)); + } + } - // Refreshes the horizontal dimensions of the view - updateWidth: function() { - if (this.weekNumbersVisible) { - // Make sure all week number cells running down the side have the same width. - // Record the width for cells created later. - this.weekNumberWidth = matchCellWidths( - this.el.find('.fc-week-number') - ); - } - }, + function windowResize(ev) { + if ( + !ignoreWindowResize && + ev.target === window && // so we don't process jqui "resize" events that have bubbled up + currentView.start // view has already been rendered + ) { + if (updateSize(true)) { + currentView.trigger('windowResize', _element); + } + } + } - // Adjusts the vertical dimensions of the view to the specified values - setHeight: function(totalHeight, isAuto) { - var eventLimit = this.opt('eventLimit'); - var scrollerHeight; - // reset all heights to be natural - unsetScroller(this.scrollerEl); - uncompensateScroll(this.headRowEl); + /* Event Fetching/Rendering + -----------------------------------------------------------------------------*/ + // TODO: going forward, most of this stuff should be directly handled by the view - this.dayGrid.destroySegPopover(); // kill the "more" popover if displayed - // is the event limit a constant level number? - if (eventLimit && typeof eventLimit === 'number') { - this.dayGrid.limitRows(eventLimit); // limit the levels first so the height can redistribute after - } + function refetchEvents() { // can be called as an API method + destroyEvents(); // so that events are cleared before user starts waiting for AJAX + fetchAndRenderEvents(); + } - scrollerHeight = this.computeScrollerHeight(totalHeight); - this.setGridHeight(scrollerHeight, isAuto); - // is the event limit dynamically calculated? - if (eventLimit && typeof eventLimit !== 'number') { - this.dayGrid.limitRows(eventLimit); // limit the levels after the grid's row heights have been set - } + function renderEvents() { // destroys old events if previously rendered + if (elementVisible()) { + freezeContentHeight(); + currentView.destroyViewEvents(); // no performance cost if never rendered + currentView.renderViewEvents(events); + unfreezeContentHeight(); + } + } - if (!isAuto && setPotentialScroller(this.scrollerEl, scrollerHeight)) { // using scrollbars? - compensateScroll(this.headRowEl, getScrollbarWidths(this.scrollerEl)); + function destroyEvents() { + freezeContentHeight(); + currentView.destroyViewEvents(); + unfreezeContentHeight(); + } - // doing the scrollbar compensation might have created text overflow which created more height. redo - scrollerHeight = this.computeScrollerHeight(totalHeight); - this.scrollerEl.height(scrollerHeight); - this.restoreScroll(); - } - }, + function getAndRenderEvents() { + if (!options.lazyFetching || isFetchNeeded(currentView.start, currentView.end)) { + fetchAndRenderEvents(); + } + else { + renderEvents(); + } + } - // Sets the height of just the DayGrid component in this view - setGridHeight: function(height, isAuto) { - if (isAuto) { - undistributeHeight(this.dayGrid.rowEls); // let the rows be their natural height with no expanding - } - else { - distributeHeight(this.dayGrid.rowEls, height, true); // true = compensate for height-hogging rows - } - }, + function fetchAndRenderEvents() { + fetchEvents(currentView.start, currentView.end); + // ... will call reportEvents + // ... which will call renderEvents + } - /* Events - ------------------------------------------------------------------------------------------------------------------*/ + // called when event data arrives + function reportEvents(_events) { + events = _events; + renderEvents(); + } - // Renders the given events onto the view and populates the segments array - renderEvents: function(events) { - this.dayGrid.renderEvents(events); + // called when a single event's data has been changed + function reportEventChange() { + renderEvents(); + } - this.updateHeight(); // must compensate for events that overflow the row - }, + /* Header Updating + -----------------------------------------------------------------------------*/ - // Retrieves all segment objects that are rendered in the view - getEventSegs: function() { - return this.dayGrid.getEventSegs(); - }, + function updateTitle() { + currentView.updateTitle(); + header.updateTitle(currentView.title); + } - // Unrenders all event elements and clears internal segment data - destroyEvents: function() { - this.recordScroll(); // removing events will reduce height and mess with the scroll, so record beforehand - this.dayGrid.destroyEvents(); - // we DON'T need to call updateHeight() because: - // A) a renderEvents() call always happens after this, which will eventually call updateHeight() - // B) in IE8, this causes a flash whenever events are rerendered - }, + function updateTodayButton() { + var now = t.getNow(); + if (now.isWithin(currentView.intervalStart, currentView.intervalEnd)) { + header.disableButton('today'); + } + else { + header.enableButton('today'); + } + } - /* Dragging (for both events and external elements) - ------------------------------------------------------------------------------------------------------------------*/ + /* Selection + -----------------------------------------------------------------------------*/ - // A returned value of `true` signals that a mock "helper" event has been rendered. - renderDrag: function(dropLocation, seg) { - return this.dayGrid.renderDrag(dropLocation, seg); - }, + function select(start, end) { + start = t.moment(start); + if (end) { + end = t.moment(end); + } + else if (start.hasTime()) { + end = start.clone().add(t.defaultTimedEventDuration); + } + else { + end = start.clone().add(t.defaultAllDayEventDuration); + } - destroyDrag: function() { - this.dayGrid.destroyDrag(); - }, + currentView.select({ start: start, end: end }); // accepts a range + } - /* Selection - ------------------------------------------------------------------------------------------------------------------*/ + function unselect() { // safe to be called before renderView + if (currentView) { + currentView.unselect(); + } + } - // Renders a visual indication of a selection - renderSelection: function(range) { - this.dayGrid.renderSelection(range); - }, + /* Date + -----------------------------------------------------------------------------*/ - // Unrenders a visual indications of a selection - destroySelection: function() { - this.dayGrid.destroySelection(); - } + function prev() { + date = currentView.computePrevDate(date); + renderView(); + } -}); -;; + function next() { + date = currentView.computeNextDate(date); + renderView(); + } -/* A month view with day cells running in rows (one-per-week) and columns -----------------------------------------------------------------------------------------------------------------------*/ -setDefaults({ - fixedWeekCount: true -}); + function prevYear() { + date.add(-1, 'years'); + renderView(); + } -var MonthView = fcViews.month = BasicView.extend({ - // Produces information about what range to display - computeRange: function(date) { - var range = BasicView.prototype.computeRange.call(this, date); // get value from super-method - var rowCnt; + function nextYear() { + date.add(1, 'years'); + renderView(); + } - // ensure 6 weeks - if (this.isFixedWeeks()) { - rowCnt = Math.ceil(range.end.diff(range.start, 'weeks', true)); // could be partial weeks due to hiddenDays - range.end.add(6 - rowCnt, 'weeks'); - } - return range; - }, + function today() { + date = t.getNow(); + renderView(); + } - // Overrides the default BasicView behavior to have special multi-week auto-height logic - setGridHeight: function(height, isAuto) { + function gotoDate(dateInput) { + date = t.moment(dateInput); + renderView(); + } - isAuto = isAuto || this.opt('weekMode') === 'variable'; // LEGACY: weekMode is deprecated - // if auto, make the height of each row the height that it would be if there were 6 weeks - if (isAuto) { - height *= this.rowCnt / 6; - } + function incrementDate(delta) { + date.add(moment.duration(delta)); + renderView(); + } - distributeHeight(this.dayGrid.rowEls, height, !isAuto); // if auto, don't compensate for height-hogging rows - }, + // Forces navigation to a view for the given date. + // `viewType` can be a specific view name or a generic one like "week" or "day". + function zoomTo(newDate, viewType) { + var viewStr; + var match; - isFixedWeeks: function() { - var weekMode = this.opt('weekMode'); // LEGACY: weekMode is deprecated - if (weekMode) { - return weekMode === 'fixed'; // if any other type of weekMode, assume NOT fixed - } + if (!viewType || !isValidViewType(viewType)) { // a general view name, or "auto" + viewType = viewType || 'day'; + viewStr = header.getViewsWithButtons().join(' '); // space-separated string of all the views in the header - return this.opt('fixedWeekCount'); - } + // try to match a general view name, like "week", against a specific one, like "agendaWeek" + match = viewStr.match(new RegExp('\\w+' + capitaliseFirstLetter(viewType))); -}); + // fall back to the day view being used in the header + if (!match) { + match = viewStr.match(/\w+Day/); + } -MonthView.duration = { months: 1 }; + viewType = match ? match[0] : 'agendaDay'; // fall back to agendaDay + } -;; + date = newDate; + renderView(viewType); + } -/* A week view with simple day cells running horizontally -----------------------------------------------------------------------------------------------------------------------*/ -fcViews.basicWeek = { - type: 'basic', - duration: { weeks: 1 } -}; -;; + function getDate() { + return date.clone(); + } -/* A view with a single simple day cell -----------------------------------------------------------------------------------------------------------------------*/ -fcViews.basicDay = { - type: 'basic', - duration: { days: 1 } -}; -;; + /* Height "Freezing" + -----------------------------------------------------------------------------*/ + + + function freezeContentHeight() { + content.css({ + width: '100%', + height: content.height(), + overflow: 'hidden' + }); + } + + + function unfreezeContentHeight() { + content.css({ + width: '', + height: '', + overflow: '' + }); + } + + + /* Misc + -----------------------------------------------------------------------------*/ + + + function getCalendar() { + return t; + } + + + function getView() { + return currentView; + } + + + function option(name, value) { + if (value === undefined) { + return options[name]; + } + if (name == 'height' || name == 'contentHeight' || name == 'aspectRatio') { + options[name] = value; + updateSize(true); // true = allow recalculation of height + } + } + + + function trigger(name, thisObj) { + if (options[name]) { + return options[name].apply( + thisObj || _element, + Array.prototype.slice.call(arguments, 2) + ); + } + } + + } + + ; + ; + + /* Top toolbar area with buttons and title + ----------------------------------------------------------------------------------------------------------------------*/ +// TODO: rename all header-related things to "toolbar" + + function Header(calendar, options) { + var t = this; + + // exports + t.render = render; + t.destroy = destroy; + t.updateTitle = updateTitle; + t.activateButton = activateButton; + t.deactivateButton = deactivateButton; + t.disableButton = disableButton; + t.enableButton = enableButton; + t.getViewsWithButtons = getViewsWithButtons; + + // locals + var el = $(); + var viewsWithButtons = []; + var tm; + + + function render() { + var sections = options.header; + + tm = options.theme ? 'ui' : 'fc'; + + if (sections) { + el = $("
") + .append(renderSection('left')) + .append(renderSection('right')) + .append(renderSection('center')) + .append('
'); + + return el; + } + } + + + function destroy() { + el.remove(); + } + + + function renderSection(position) { + var sectionEl = $('
'); + var buttonStr = options.header[position]; + + if (buttonStr) { + $.each(buttonStr.split(' '), function (i) { + var groupChildren = $(); + var isOnlyButtons = true; + var groupEl; + + $.each(this.split(','), function (j, buttonName) { + var buttonClick; + var themeIcon; + var normalIcon; + var defaultText; + var viewText; // highest priority + var customText; + var innerHtml; + var classes; + var button; + + if (buttonName == 'title') { + groupChildren = groupChildren.add($('

 

')); // we always want it to take up height + isOnlyButtons = false; + } + else { + if (calendar[buttonName]) { // a calendar method + buttonClick = function () { + calendar[buttonName](); + }; + } + else if (calendar.isValidViewType(buttonName)) { // a view type + buttonClick = function () { + calendar.changeView(buttonName); + }; + viewsWithButtons.push(buttonName); + viewText = calendar.getViewButtonText(buttonName); + } + if (buttonClick) { + + // smartProperty allows different text per view button (ex: "Agenda Week" vs "Basic Week") + themeIcon = smartProperty(options.themeButtonIcons, buttonName); + normalIcon = smartProperty(options.buttonIcons, buttonName); + defaultText = smartProperty(options.defaultButtonText, buttonName); // from languages + customText = smartProperty(options.buttonText, buttonName); + + if (viewText || customText) { + innerHtml = htmlEscape(viewText || customText); + } + else if (themeIcon && options.theme) { + innerHtml = ""; + } + else if (normalIcon && !options.theme) { + innerHtml = ""; + } + else { + innerHtml = htmlEscape(defaultText || buttonName); + } + + classes = [ + 'fc-' + buttonName + '-button', + tm + '-button', + tm + '-state-default' + ]; + + button = $( // type="button" so that it doesn't submit a form + '' + ) + .click(function () { + // don't process clicks for disabled buttons + if (!button.hasClass(tm + '-state-disabled')) { + + buttonClick(); + + // after the click action, if the button becomes the "active" tab, or disabled, + // it should never have a hover class, so remove it now. + if ( + button.hasClass(tm + '-state-active') || + button.hasClass(tm + '-state-disabled') + ) { + button.removeClass(tm + '-state-hover'); + } + } + }) + .mousedown(function () { + // the *down* effect (mouse pressed in). + // only on buttons that are not the "active" tab, or disabled + button + .not('.' + tm + '-state-active') + .not('.' + tm + '-state-disabled') + .addClass(tm + '-state-down'); + }) + .mouseup(function () { + // undo the *down* effect + button.removeClass(tm + '-state-down'); + }) + .hover( + function () { + // the *hover* effect. + // only on buttons that are not the "active" tab, or disabled + button + .not('.' + tm + '-state-active') + .not('.' + tm + '-state-disabled') + .addClass(tm + '-state-hover'); + }, + function () { + // undo the *hover* effect + button + .removeClass(tm + '-state-hover') + .removeClass(tm + '-state-down'); // if mouseleave happens before mouseup + } + ); + + groupChildren = groupChildren.add(button); + } + } + }); + + if (isOnlyButtons) { + groupChildren + .first().addClass(tm + '-corner-left').end() + .last().addClass(tm + '-corner-right').end(); + } + + if (groupChildren.length > 1) { + groupEl = $('
'); + if (isOnlyButtons) { + groupEl.addClass('fc-button-group'); + } + groupEl.append(groupChildren); + sectionEl.append(groupEl); + } + else { + sectionEl.append(groupChildren); // 1 or 0 children + } + }); + } + + return sectionEl; + } + + + function updateTitle(text) { + el.find('h2').text(text); + } + + + function activateButton(buttonName) { + el.find('.fc-' + buttonName + '-button') + .addClass(tm + '-state-active'); + } + + + function deactivateButton(buttonName) { + el.find('.fc-' + buttonName + '-button') + .removeClass(tm + '-state-active'); + } + + + function disableButton(buttonName) { + el.find('.fc-' + buttonName + '-button') + .attr('disabled', 'disabled') + .addClass(tm + '-state-disabled'); + } + + + function enableButton(buttonName) { + el.find('.fc-' + buttonName + '-button') + .removeAttr('disabled') + .removeClass(tm + '-state-disabled'); + } + + + function getViewsWithButtons() { + return viewsWithButtons; + } + + } + + ; + ; + + fc.sourceNormalizers = []; + fc.sourceFetchers = []; + + var ajaxDefaults = { + dataType: 'json', + cache: false + }; + + var eventGUID = 1; + + + function EventManager(options) { // assumed to be a calendar + var t = this; + + + // exports + t.isFetchNeeded = isFetchNeeded; + t.fetchEvents = fetchEvents; + t.addEventSource = addEventSource; + t.removeEventSource = removeEventSource; + t.updateEvent = updateEvent; + t.renderEvent = renderEvent; + t.removeEvents = removeEvents; + t.clientEvents = clientEvents; + t.mutateEvent = mutateEvent; + t.normalizeEventDateProps = normalizeEventDateProps; + t.ensureVisibleEventRange = ensureVisibleEventRange; + + + // imports + var trigger = t.trigger; + var getView = t.getView; + var reportEvents = t.reportEvents; + + + // locals + var stickySource = { events: [] }; + var sources = [ stickySource ]; + var rangeStart, rangeEnd; + var currentFetchID = 0; + var pendingSourceCnt = 0; + var loadingLevel = 0; + var cache = []; // holds events that have already been expanded + + + $.each( + (options.events ? [ options.events ] : []).concat(options.eventSources || []), + function (i, sourceInput) { + var source = buildEventSource(sourceInput); + if (source) { + sources.push(source); + } + } + ); + + + /* Fetching + -----------------------------------------------------------------------------*/ + + + function isFetchNeeded(start, end) { + return !rangeStart || // nothing has been fetched yet? + // or, a part of the new range is outside of the old range? (after normalizing) + start.clone().stripZone() < rangeStart.clone().stripZone() || + end.clone().stripZone() > rangeEnd.clone().stripZone(); + } + + + function fetchEvents(start, end) { + rangeStart = start; + rangeEnd = end; + cache = []; + var fetchID = ++currentFetchID; + var len = sources.length; + pendingSourceCnt = len; + for (var i = 0; i < len; i++) { + fetchEventSource(sources[i], fetchID); + } + } + + + function fetchEventSource(source, fetchID) { + _fetchEventSource(source, function (eventInputs) { + var isArraySource = $.isArray(source.events); + var i, eventInput; + var abstractEvent; + + if (fetchID == currentFetchID) { + + if (eventInputs) { + for (i = 0; i < eventInputs.length; i++) { + eventInput = eventInputs[i]; + + if (isArraySource) { // array sources have already been convert to Event Objects + abstractEvent = eventInput; + } + else { + abstractEvent = buildEventFromInput(eventInput, source); + } + + if (abstractEvent) { // not false (an invalid event) + cache.push.apply( + cache, + expandEvent(abstractEvent) // add individual expanded events to the cache + ); + } + } + } + + pendingSourceCnt--; + if (!pendingSourceCnt) { + reportEvents(cache); + } + } + }); + } + + + function _fetchEventSource(source, callback) { + var i; + var fetchers = fc.sourceFetchers; + var res; + + for (i = 0; i < fetchers.length; i++) { + res = fetchers[i].call( + t, // this, the Calendar object + source, + rangeStart.clone(), + rangeEnd.clone(), + options.timezone, + callback + ); + + if (res === true) { + // the fetcher is in charge. made its own async request + return; + } + else if (typeof res == 'object') { + // the fetcher returned a new source. process it + _fetchEventSource(res, callback); + return; + } + } + + var events = source.events; + if (events) { + if ($.isFunction(events)) { + pushLoading(); + events.call( + t, // this, the Calendar object + rangeStart.clone(), + rangeEnd.clone(), + options.timezone, + function (events) { + callback(events); + popLoading(); + } + ); + } + else if ($.isArray(events)) { + callback(events); + } + else { + callback(); + } + } else { + var url = source.url; + if (url) { + var success = source.success; + var error = source.error; + var complete = source.complete; + + // retrieve any outbound GET/POST $.ajax data from the options + var customData; + if ($.isFunction(source.data)) { + // supplied as a function that returns a key/value object + customData = source.data(); + } + else { + // supplied as a straight key/value object + customData = source.data; + } + + // use a copy of the custom data so we can modify the parameters + // and not affect the passed-in object. + var data = $.extend({}, customData || {}); + + var startParam = firstDefined(source.startParam, options.startParam); + var endParam = firstDefined(source.endParam, options.endParam); + var timezoneParam = firstDefined(source.timezoneParam, options.timezoneParam); + + if (startParam) { + data[startParam] = rangeStart.format(); + } + if (endParam) { + data[endParam] = rangeEnd.format(); + } + if (options.timezone && options.timezone != 'local') { + data[timezoneParam] = options.timezone; + } + + pushLoading(); + $.ajax($.extend({}, ajaxDefaults, source, { + data: data, + success: function (events) { + events = events || []; + var res = applyAll(success, this, arguments); + if ($.isArray(res)) { + events = res; + } + callback(events); + }, + error: function () { + applyAll(error, this, arguments); + callback(); + }, + complete: function () { + applyAll(complete, this, arguments); + popLoading(); + } + })); + } else { + callback(); + } + } + } + + + /* Sources + -----------------------------------------------------------------------------*/ + + + function addEventSource(sourceInput) { + var source = buildEventSource(sourceInput); + if (source) { + sources.push(source); + pendingSourceCnt++; + fetchEventSource(source, currentFetchID); // will eventually call reportEvents + } + } + + + function buildEventSource(sourceInput) { // will return undefined if invalid source + var normalizers = fc.sourceNormalizers; + var source; + var i; + + if ($.isFunction(sourceInput) || $.isArray(sourceInput)) { + source = { events: sourceInput }; + } + else if (typeof sourceInput === 'string') { + source = { url: sourceInput }; + } + else if (typeof sourceInput === 'object') { + source = $.extend({}, sourceInput); // shallow copy + } + + if (source) { + + // TODO: repeat code, same code for event classNames + if (source.className) { + if (typeof source.className === 'string') { + source.className = source.className.split(/\s+/); + } + // otherwise, assumed to be an array + } + else { + source.className = []; + } + + // for array sources, we convert to standard Event Objects up front + if ($.isArray(source.events)) { + source.origArray = source.events; // for removeEventSource + source.events = $.map(source.events, function (eventInput) { + return buildEventFromInput(eventInput, source); + }); + } + + for (i = 0; i < normalizers.length; i++) { + normalizers[i].call(t, source); + } + + return source; + } + } + + + function removeEventSource(source) { + sources = $.grep(sources, function (src) { + return !isSourcesEqual(src, source); + }); + // remove all client events from that source + cache = $.grep(cache, function (e) { + return !isSourcesEqual(e.source, source); + }); + reportEvents(cache); + } + + + function isSourcesEqual(source1, source2) { + return source1 && source2 && getSourcePrimitive(source1) == getSourcePrimitive(source2); + } + + + function getSourcePrimitive(source) { + return ( + (typeof source === 'object') ? // a normalized event source? + (source.origArray || source.googleCalendarId || source.url || source.events) : // get the primitive + null + ) || + source; // the given argument *is* the primitive + } + + + /* Manipulation + -----------------------------------------------------------------------------*/ + + + // Only ever called from the externally-facing API + function updateEvent(event) { + + // massage start/end values, even if date string values + event.start = t.moment(event.start); + if (event.end) { + event.end = t.moment(event.end); + } + else { + event.end = null; + } + + mutateEvent(event, getMiscEventProps(event)); // will handle start/end/allDay normalization + reportEvents(cache); // reports event modifications (so we can redraw) + } + + + // Returns a hash of misc event properties that should be copied over to related events. + function getMiscEventProps(event) { + var props = {}; + + $.each(event, function (name, val) { + if (isMiscEventPropName(name)) { + if (val !== undefined && isAtomic(val)) { // a defined non-object + props[name] = val; + } + } + }); + + return props; + } + + // non-date-related, non-id-related, non-secret + function isMiscEventPropName(name) { + return !/^_|^(id|allDay|start|end)$/.test(name); + } + + + // returns the expanded events that were created + function renderEvent(eventInput, stick) { + var abstractEvent = buildEventFromInput(eventInput); + var events; + var i, event; + + if (abstractEvent) { // not false (a valid input) + events = expandEvent(abstractEvent); + + for (i = 0; i < events.length; i++) { + event = events[i]; + + if (!event.source) { + if (stick) { + stickySource.events.push(event); + event.source = stickySource; + } + cache.push(event); + } + } + + reportEvents(cache); + + return events; + } + + return []; + } + + + function removeEvents(filter) { + var eventID; + var i; + + if (filter == null) { // null or undefined. remove all events + filter = function () { + return true; + }; // will always match + } + else if (!$.isFunction(filter)) { // an event ID + eventID = filter + ''; + filter = function (event) { + return event._id == eventID; + }; + } + + // Purge event(s) from our local cache + cache = $.grep(cache, filter, true); // inverse=true + + // Remove events from array sources. + // This works because they have been converted to official Event Objects up front. + // (and as a result, event._id has been calculated). + for (i = 0; i < sources.length; i++) { + if ($.isArray(sources[i].events)) { + sources[i].events = $.grep(sources[i].events, filter, true); + } + } + + reportEvents(cache); + } + + + function clientEvents(filter) { + if ($.isFunction(filter)) { + return $.grep(cache, filter); + } + else if (filter != null) { // not null, not undefined. an event ID + filter += ''; + return $.grep(cache, function (e) { + return e._id == filter; + }); + } + return cache; // else, return all + } + + + /* Loading State + -----------------------------------------------------------------------------*/ + + + function pushLoading() { + if (!(loadingLevel++)) { + trigger('loading', null, true, getView()); + } + } + + + function popLoading() { + if (!(--loadingLevel)) { + trigger('loading', null, false, getView()); + } + } + + + /* Event Normalization + -----------------------------------------------------------------------------*/ + + + // Given a raw object with key/value properties, returns an "abstract" Event object. + // An "abstract" event is an event that, if recurring, will not have been expanded yet. + // Will return `false` when input is invalid. + // `source` is optional + function buildEventFromInput(input, source) { + var out = {}; + var start, end; + var allDay; + + if (options.eventDataTransform) { + input = options.eventDataTransform(input); + } + if (source && source.eventDataTransform) { + input = source.eventDataTransform(input); + } + + // Copy all properties over to the resulting object. + // The special-case properties will be copied over afterwards. + $.extend(out, input); + + if (source) { + out.source = source; + } + + out._id = input._id || (input.id === undefined ? '_fc' + eventGUID++ : input.id + ''); + + if (input.className) { + if (typeof input.className == 'string') { + out.className = input.className.split(/\s+/); + } + else { // assumed to be an array + out.className = input.className; + } + } + else { + out.className = []; + } + + start = input.start || input.date; // "date" is an alias for "start" + end = input.end; + + // parse as a time (Duration) if applicable + if (isTimeString(start)) { + start = moment.duration(start); + } + if (isTimeString(end)) { + end = moment.duration(end); + } + + if (input.dow || moment.isDuration(start) || moment.isDuration(end)) { + + // the event is "abstract" (recurring) so don't calculate exact start/end dates just yet + out.start = start ? moment.duration(start) : null; // will be a Duration or null + out.end = end ? moment.duration(end) : null; // will be a Duration or null + out._recurring = true; // our internal marker + } + else { + + if (start) { + start = t.moment(start); + if (!start.isValid()) { + return false; + } + } + + if (end) { + end = t.moment(end); + if (!end.isValid()) { + end = null; // let defaults take over + } + } + + allDay = input.allDay; + if (allDay === undefined) { // still undefined? fallback to default + allDay = firstDefined( + source ? source.allDayDefault : undefined, + options.allDayDefault + ); + // still undefined? normalizeEventDateProps will calculate it + } + + assignDatesToEvent(start, end, allDay, out); + } + + return out; + } + + + // Normalizes and assigns the given dates to the given partially-formed event object. + // NOTE: mutates the given start/end moments. does not make a copy. + function assignDatesToEvent(start, end, allDay, event) { + event.start = start; + event.end = end; + event.allDay = allDay; + normalizeEventDateProps(event); + backupEventDates(event); + } + + + // Ensures the allDay property exists. + // Ensures the start/end dates are consistent with allDay and forceEventDuration. + // Accepts an Event object, or a plain object with event-ish properties. + // NOTE: Will modify the given object. + function normalizeEventDateProps(props) { + + if (props.allDay == null) { + props.allDay = !(props.start.hasTime() || (props.end && props.end.hasTime())); + } + + if (props.allDay) { + props.start.stripTime(); + if (props.end) { + props.end.stripTime(); + } + } + else { + if (!props.start.hasTime()) { + props.start = t.rezoneDate(props.start); // will also give it a 00:00 time + } + if (props.end && !props.end.hasTime()) { + props.end = t.rezoneDate(props.end); // will also give it a 00:00 time + } + } + + if (props.end && !props.end.isAfter(props.start)) { + props.end = null; + } + + if (!props.end) { + if (options.forceEventDuration) { + props.end = t.getDefaultEventEnd(props.allDay, props.start); + } + else { + props.end = null; + } + } + } + + + // If `range` is a proper range with a start and end, returns the original object. + // If missing an end, computes a new range with an end, computing it as if it were an event. + // TODO: make this a part of the event -> eventRange system + function ensureVisibleEventRange(range) { + var allDay; + + if (!range.end) { + + allDay = range.allDay; // range might be more event-ish than we think + if (allDay == null) { + allDay = !range.start.hasTime(); + } + + range = { + start: range.start, + end: t.getDefaultEventEnd(allDay, range.start) + }; + } + return range; + } + + + // If the given event is a recurring event, break it down into an array of individual instances. + // If not a recurring event, return an array with the single original event. + // If given a falsy input (probably because of a failed buildEventFromInput call), returns an empty array. + // HACK: can override the recurring window by providing custom rangeStart/rangeEnd (for businessHours). + function expandEvent(abstractEvent, _rangeStart, _rangeEnd) { + var events = []; + var dowHash; + var dow; + var i; + var date; + var startTime, endTime; + var start, end; + var event; + + _rangeStart = _rangeStart || rangeStart; + _rangeEnd = _rangeEnd || rangeEnd; + + if (abstractEvent) { + if (abstractEvent._recurring) { + + // make a boolean hash as to whether the event occurs on each day-of-week + if ((dow = abstractEvent.dow)) { + dowHash = {}; + for (i = 0; i < dow.length; i++) { + dowHash[dow[i]] = true; + } + } + + // iterate through every day in the current range + date = _rangeStart.clone().stripTime(); // holds the date of the current day + while (date.isBefore(_rangeEnd)) { + + if (!dowHash || dowHash[date.day()]) { // if everyday, or this particular day-of-week + + startTime = abstractEvent.start; // the stored start and end properties are times (Durations) + endTime = abstractEvent.end; // " + start = date.clone(); + end = null; + + if (startTime) { + start = start.time(startTime); + } + if (endTime) { + end = date.clone().time(endTime); + } + + event = $.extend({}, abstractEvent); // make a copy of the original + assignDatesToEvent( + start, end, + !startTime && !endTime, // allDay? + event + ); + events.push(event); + } + + date.add(1, 'days'); + } + } + else { + events.push(abstractEvent); // return the original event. will be a one-item array + } + } + + return events; + } + + + /* Event Modification Math + -----------------------------------------------------------------------------------------*/ + + + // Modifies an event and all related events by applying the given properties. + // Special date-diffing logic is used for manipulation of dates. + // If `props` does not contain start/end dates, the updated values are assumed to be the event's current start/end. + // All date comparisons are done against the event's pristine _start and _end dates. + // Returns an object with delta information and a function to undo all operations. + // + function mutateEvent(event, props) { + var miscProps = {}; + var clearEnd; + var dateDelta; + var durationDelta; + var undoFunc; + + props = props || {}; + + // ensure new date-related values to compare against + if (!props.start) { + props.start = event.start.clone(); + } + if (props.end === undefined) { + props.end = event.end ? event.end.clone() : null; + } + if (props.allDay == null) { // is null or undefined? + props.allDay = event.allDay; + } + + normalizeEventDateProps(props); // massages start/end/allDay + + // clear the end date if explicitly changed to null + clearEnd = event._end !== null && props.end === null; + + // compute the delta for moving the start and end dates together + if (props.allDay) { + dateDelta = diffDay(props.start, event._start); // whole-day diff from start-of-day + } + else { + dateDelta = diffDayTime(props.start, event._start); + } + + // compute the delta for moving the end date (after applying dateDelta) + if (!clearEnd && props.end) { + durationDelta = diffDayTime( + // new duration + props.end, + props.start + ).subtract(diffDayTime( + // subtract old duration + event._end || t.getDefaultEventEnd(event._allDay, event._start), + event._start + )); + } + + // gather all non-date-related properties + $.each(props, function (name, val) { + if (isMiscEventPropName(name)) { + if (val !== undefined) { + miscProps[name] = val; + } + } + }); + + // apply the operations to the event and all related events + undoFunc = mutateEvents( + clientEvents(event._id), // get events with this ID + clearEnd, + props.allDay, + dateDelta, + durationDelta, + miscProps + ); + + return { + dateDelta: dateDelta, + durationDelta: durationDelta, + undo: undoFunc + }; + } + + + // Modifies an array of events in the following ways (operations are in order): + // - clear the event's `end` + // - convert the event to allDay + // - add `dateDelta` to the start and end + // - add `durationDelta` to the event's duration + // - assign `miscProps` to the event + // + // Returns a function that can be called to undo all the operations. + // + // TODO: don't use so many closures. possible memory issues when lots of events with same ID. + // + function mutateEvents(events, clearEnd, allDay, dateDelta, durationDelta, miscProps) { + var isAmbigTimezone = t.getIsAmbigTimezone(); + var undoFunctions = []; + + // normalize zero-length deltas to be null + if (dateDelta && !dateDelta.valueOf()) { + dateDelta = null; + } + if (durationDelta && !durationDelta.valueOf()) { + durationDelta = null; + } + + $.each(events, function (i, event) { + var oldProps; + var newProps; + + // build an object holding all the old values, both date-related and misc. + // for the undo function. + oldProps = { + start: event.start.clone(), + end: event.end ? event.end.clone() : null, + allDay: event.allDay + }; + $.each(miscProps, function (name) { + oldProps[name] = event[name]; + }); + + // new date-related properties. work off the original date snapshot. + // ok to use references because they will be thrown away when backupEventDates is called. + newProps = { + start: event._start, + end: event._end, + allDay: event._allDay + }; + + if (clearEnd) { + newProps.end = null; + } + + newProps.allDay = allDay; + + normalizeEventDateProps(newProps); // massages start/end/allDay + + if (dateDelta) { + newProps.start.add(dateDelta); + if (newProps.end) { + newProps.end.add(dateDelta); + } + } + + if (durationDelta) { + if (!newProps.end) { + newProps.end = t.getDefaultEventEnd(newProps.allDay, newProps.start); + } + newProps.end.add(durationDelta); + } + + // if the dates have changed, and we know it is impossible to recompute the + // timezone offsets, strip the zone. + if ( + isAmbigTimezone && !newProps.allDay && + (dateDelta || durationDelta) + ) { + newProps.start.stripZone(); + if (newProps.end) { + newProps.end.stripZone(); + } + } + + $.extend(event, miscProps, newProps); // copy over misc props, then date-related props + backupEventDates(event); // regenerate internal _start/_end/_allDay + + undoFunctions.push(function () { + $.extend(event, oldProps); + backupEventDates(event); // regenerate internal _start/_end/_allDay + }); + }); + + return function () { + for (var i = 0; i < undoFunctions.length; i++) { + undoFunctions[i](); + } + }; + } + + + /* Business Hours + -----------------------------------------------------------------------------------------*/ + + t.getBusinessHoursEvents = getBusinessHoursEvents; + + + // Returns an array of events as to when the business hours occur in the given view. + // Abuse of our event system :( + function getBusinessHoursEvents() { + var optionVal = options.businessHours; + var defaultVal = { + className: 'fc-nonbusiness', + start: '09:00', + end: '17:00', + dow: [ 1, 2, 3, 4, 5 ], // monday - friday + rendering: 'inverse-background' + }; + var view = t.getView(); + var eventInput; + + if (optionVal) { + if (typeof optionVal === 'object') { + // option value is an object that can override the default business hours + eventInput = $.extend({}, defaultVal, optionVal); + } + else { + // option value is `true`. use default business hours + eventInput = defaultVal; + } + } + + if (eventInput) { + return expandEvent( + buildEventFromInput(eventInput), + view.start, + view.end + ); + } + + return []; + } + + + /* Overlapping / Constraining + -----------------------------------------------------------------------------------------*/ + + t.isEventRangeAllowed = isEventRangeAllowed; + t.isSelectionRangeAllowed = isSelectionRangeAllowed; + t.isExternalDropRangeAllowed = isExternalDropRangeAllowed; + + + function isEventRangeAllowed(range, event) { + var source = event.source || {}; + var constraint = firstDefined( + event.constraint, + source.constraint, + options.eventConstraint + ); + var overlap = firstDefined( + event.overlap, + source.overlap, + options.eventOverlap + ); + + range = ensureVisibleEventRange(range); // ensure a proper range with an end for isRangeAllowed + + return isRangeAllowed(range, constraint, overlap, event); + } + + + function isSelectionRangeAllowed(range) { + return isRangeAllowed(range, options.selectConstraint, options.selectOverlap); + } + + + // when `eventProps` is defined, consider this an event. + // `eventProps` can contain misc non-date-related info about the event. + function isExternalDropRangeAllowed(range, eventProps) { + var eventInput; + var event; + + // note: very similar logic is in View's reportExternalDrop + if (eventProps) { + eventInput = $.extend({}, eventProps, range); + event = expandEvent(buildEventFromInput(eventInput))[0]; + } + + if (event) { + return isEventRangeAllowed(range, event); + } + else { // treat it as a selection + + range = ensureVisibleEventRange(range); // ensure a proper range with an end for isSelectionRangeAllowed + + return isSelectionRangeAllowed(range); + } + } + + + // Returns true if the given range (caused by an event drop/resize or a selection) is allowed to exist + // according to the constraint/overlap settings. + // `event` is not required if checking a selection. + function isRangeAllowed(range, constraint, overlap, event) { + var constraintEvents; + var anyContainment; + var i, otherEvent; + var otherOverlap; + + // normalize. fyi, we're normalizing in too many places :( + range = { + start: range.start.clone().stripZone(), + end: range.end.clone().stripZone() + }; + + // the range must be fully contained by at least one of produced constraint events + if (constraint != null) { + + // not treated as an event! intermediate data structure + // TODO: use ranges in the future + constraintEvents = constraintToEvents(constraint); + + anyContainment = false; + for (i = 0; i < constraintEvents.length; i++) { + if (eventContainsRange(constraintEvents[i], range)) { + anyContainment = true; + break; + } + } + + if (!anyContainment) { + return false; + } + } + + for (i = 0; i < cache.length; i++) { // loop all events and detect overlap + otherEvent = cache[i]; + + // don't compare the event to itself or other related [repeating] events + if (event && event._id === otherEvent._id) { + continue; + } + + // there needs to be an actual intersection before disallowing anything + if (eventIntersectsRange(otherEvent, range)) { + + // evaluate overlap for the given range and short-circuit if necessary + if (overlap === false) { + return false; + } + else if (typeof overlap === 'function' && !overlap(otherEvent, event)) { + return false; + } + + // if we are computing if the given range is allowable for an event, consider the other event's + // EventObject-specific or Source-specific `overlap` property + if (event) { + otherOverlap = firstDefined( + otherEvent.overlap, + (otherEvent.source || {}).overlap + // we already considered the global `eventOverlap` + ); + if (otherOverlap === false) { + return false; + } + if (typeof otherOverlap === 'function' && !otherOverlap(event, otherEvent)) { + return false; + } + } + } + } + + return true; + } + + + // Given an event input from the API, produces an array of event objects. Possible event inputs: + // 'businessHours' + // An event ID (number or string) + // An object with specific start/end dates or a recurring event (like what businessHours accepts) + function constraintToEvents(constraintInput) { + + if (constraintInput === 'businessHours') { + return getBusinessHoursEvents(); + } + + if (typeof constraintInput === 'object') { + return expandEvent(buildEventFromInput(constraintInput)); + } + + return clientEvents(constraintInput); // probably an ID + } + + + // Does the event's date range fully contain the given range? + // start/end already assumed to have stripped zones :( + function eventContainsRange(event, range) { + var eventStart = event.start.clone().stripZone(); + var eventEnd = t.getEventEnd(event).stripZone(); + + return range.start >= eventStart && range.end <= eventEnd; + } + + + // Does the event's date range intersect with the given range? + // start/end already assumed to have stripped zones :( + function eventIntersectsRange(event, range) { + var eventStart = event.start.clone().stripZone(); + var eventEnd = t.getEventEnd(event).stripZone(); + + return range.start < eventEnd && range.end > eventStart; + } + + } + + +// updates the "backup" properties, which are preserved in order to compute diffs later on. + function backupEventDates(event) { + event._allDay = event.allDay; + event._start = event.start.clone(); + event._end = event.end ? event.end.clone() : null; + } + + ; + ; + + /* An abstract class for the "basic" views, as well as month view. Renders one or more rows of day cells. + ----------------------------------------------------------------------------------------------------------------------*/ +// It is a manager for a DayGrid subcomponent, which does most of the heavy lifting. +// It is responsible for managing width/height. + + var BasicView = fcViews.basic = View.extend({ + + dayGrid: null, // the main subcomponent that does most of the heavy lifting + + dayNumbersVisible: false, // display day numbers on each day cell? + weekNumbersVisible: false, // display week numbers along the side? + + weekNumberWidth: null, // width of all the week-number cells running down the side + + headRowEl: null, // the fake row element of the day-of-week header + + + initialize: function () { + this.dayGrid = new DayGrid(this); + this.coordMap = this.dayGrid.coordMap; // the view's date-to-cell mapping is identical to the subcomponent's + }, + + + // Sets the display range and computes all necessary dates + setRange: function (range) { + View.prototype.setRange.call(this, range); // call the super-method + + this.dayGrid.breakOnWeeks = /year|month|week/.test(this.intervalUnit); // do before setRange + this.dayGrid.setRange(range); + }, + + + // Compute the value to feed into setRange. Overrides superclass. + computeRange: function (date) { + var range = View.prototype.computeRange.call(this, date); // get value from the super-method + + // year and month views should be aligned with weeks. this is already done for week + if (/year|month/.test(range.intervalUnit)) { + range.start.startOf('week'); + range.start = this.skipHiddenDays(range.start); + + // make end-of-week if not already + if (range.end.weekday()) { + range.end.add(1, 'week').startOf('week'); + range.end = this.skipHiddenDays(range.end, -1, true); // exclusively move backwards + } + } + + return range; + }, + + + // Renders the view into `this.el`, which should already be assigned + render: function () { + + this.dayNumbersVisible = this.dayGrid.rowCnt > 1; // TODO: make grid responsible + this.weekNumbersVisible = this.opt('weekNumbers'); + this.dayGrid.numbersVisible = this.dayNumbersVisible || this.weekNumbersVisible; + + this.el.addClass('fc-basic-view').html(this.renderHtml()); + + this.headRowEl = this.el.find('thead .fc-row'); + + this.scrollerEl = this.el.find('.fc-day-grid-container'); + this.dayGrid.coordMap.containerEl = this.scrollerEl; // constrain clicks/etc to the dimensions of the scroller + + this.dayGrid.el = this.el.find('.fc-day-grid'); + this.dayGrid.render(this.hasRigidRows()); + }, + + + // Make subcomponents ready for cleanup + destroy: function () { + this.dayGrid.destroy(); + View.prototype.destroy.call(this); // call the super-method + }, + + + // Builds the HTML skeleton for the view. + // The day-grid component will render inside of a container defined by this HTML. + renderHtml: function () { + return '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
' + + this.dayGrid.headHtml() + // render the day-of-week headers + '
' + + '
' + + '
' + + '
' + + '
'; + }, -/* An abstract class for all agenda-related views. Displays one more columns with time slots running vertically. -----------------------------------------------------------------------------------------------------------------------*/ -// Is a manager for the TimeGrid subcomponent and possibly the DayGrid subcomponent (if allDaySlot is on). -// Responsible for managing width/height. -setDefaults({ - allDaySlot: true, - allDayText: 'all-day', - scrollTime: '06:00:00', - slotDuration: '00:30:00', - minTime: '00:00:00', - maxTime: '24:00:00', - slotEventOverlap: true -}); + // Generates the HTML that will go before the day-of week header cells. + // Queried by the DayGrid subcomponent when generating rows. Ordering depends on isRTL. + headIntroHtml: function () { + if (this.weekNumbersVisible) { + return '' + + '' + + '' + // needed for matchCellWidths + htmlEscape(this.opt('weekNumberTitle')) + + '' + + ''; + } + }, + + + // Generates the HTML that will go before content-skeleton cells that display the day/week numbers. + // Queried by the DayGrid subcomponent. Ordering depends on isRTL. + numberIntroHtml: function (row) { + if (this.weekNumbersVisible) { + return '' + + '' + + '' + // needed for matchCellWidths + this.calendar.calculateWeekNumber(this.dayGrid.getCell(row, 0).start) + + '' + + ''; + } + }, + + + // Generates the HTML that goes before the day bg cells for each day-row. + // Queried by the DayGrid subcomponent. Ordering depends on isRTL. + dayIntroHtml: function () { + if (this.weekNumbersVisible) { + return ''; + } + }, + + + // Generates the HTML that goes before every other type of row generated by DayGrid. Ordering depends on isRTL. + // Affects helper-skeleton and highlight-skeleton rows. + introHtml: function () { + if (this.weekNumbersVisible) { + return ''; + } + }, + + + // Generates the HTML for the s of the "number" row in the DayGrid's content skeleton. + // The number row will only exist if either day numbers or week numbers are turned on. + numberCellHtml: function (cell) { + var date = cell.start; + var classes; + + if (!this.dayNumbersVisible) { // if there are week numbers but not day numbers + return ''; // will create an empty space above events :( + } + + classes = this.dayGrid.getDayClasses(date); + classes.unshift('fc-day-number'); + + return '' + + '' + + date.date() + + ''; + }, + + + // Generates an HTML attribute string for setting the width of the week number column, if it is known + weekNumberStyleAttr: function () { + if (this.weekNumberWidth !== null) { + return 'style="width:' + this.weekNumberWidth + 'px"'; + } + return ''; + }, + + + // Determines whether each row should have a constant height + hasRigidRows: function () { + var eventLimit = this.opt('eventLimit'); + return eventLimit && typeof eventLimit !== 'number'; + }, + + + /* Dimensions + ------------------------------------------------------------------------------------------------------------------*/ -var AGENDA_ALL_DAY_EVENT_LIMIT = 5; -fcViews.agenda = View.extend({ // AgendaView + // Refreshes the horizontal dimensions of the view + updateWidth: function () { + if (this.weekNumbersVisible) { + // Make sure all week number cells running down the side have the same width. + // Record the width for cells created later. + this.weekNumberWidth = matchCellWidths( + this.el.find('.fc-week-number') + ); + } + }, - timeGrid: null, // the main time-grid subcomponent of this view - dayGrid: null, // the "all-day" subcomponent. if all-day is turned off, this will be null - axisWidth: null, // the width of the time axis running down the side - - noScrollRowEls: null, // set of fake row elements that must compensate when scrollerEl has scrollbars - - // when the time-grid isn't tall enough to occupy the given height, we render an
underneath - bottomRuleEl: null, - bottomRuleHeight: null, - - - initialize: function() { - this.timeGrid = new TimeGrid(this); - - if (this.opt('allDaySlot')) { // should we display the "all-day" area? - this.dayGrid = new DayGrid(this); // the all-day subcomponent of this view - - // the coordinate grid will be a combination of both subcomponents' grids - this.coordMap = new ComboCoordMap([ - this.dayGrid.coordMap, - this.timeGrid.coordMap - ]); - } - else { - this.coordMap = this.timeGrid.coordMap; - } - }, - - - /* Rendering - ------------------------------------------------------------------------------------------------------------------*/ - - - // Sets the display range and computes all necessary dates - setRange: function(range) { - View.prototype.setRange.call(this, range); // call the super-method - - this.timeGrid.setRange(range); - if (this.dayGrid) { - this.dayGrid.setRange(range); - } - }, - - - // Renders the view into `this.el`, which has already been assigned - render: function() { - - this.el.addClass('fc-agenda-view').html(this.renderHtml()); - - // the element that wraps the time-grid that will probably scroll - this.scrollerEl = this.el.find('.fc-time-grid-container'); - this.timeGrid.coordMap.containerEl = this.scrollerEl; // don't accept clicks/etc outside of this - - this.timeGrid.el = this.el.find('.fc-time-grid'); - this.timeGrid.render(); - - // the
that sometimes displays under the time-grid - this.bottomRuleEl = $('
') - .appendTo(this.timeGrid.el); // inject it into the time-grid - - if (this.dayGrid) { - this.dayGrid.el = this.el.find('.fc-day-grid'); - this.dayGrid.render(); - - // have the day-grid extend it's coordinate area over the
dividing the two grids - this.dayGrid.bottomCoordPadding = this.dayGrid.el.next('hr').outerHeight(); - } - - this.noScrollRowEls = this.el.find('.fc-row:not(.fc-scroller *)'); // fake rows not within the scroller - }, - - - // Make subcomponents ready for cleanup - destroy: function() { - this.timeGrid.destroy(); - if (this.dayGrid) { - this.dayGrid.destroy(); - } - View.prototype.destroy.call(this); // call the super-method - }, - - - // Builds the HTML skeleton for the view. - // The day-grid and time-grid components will render inside containers defined by this HTML. - renderHtml: function() { - return '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
' + - this.timeGrid.headHtml() + // render the day-of-week headers - '
' + - (this.dayGrid ? - '
' + - '
' : - '' - ) + - '
' + - '
' + - '
' + - '
'; - }, - - - // Generates the HTML that will go before the day-of week header cells. - // Queried by the TimeGrid subcomponent when generating rows. Ordering depends on isRTL. - headIntroHtml: function() { - var date; - var weekNumber; - var weekTitle; - var weekText; - - if (this.opt('weekNumbers')) { - date = this.timeGrid.getCell(0).start; - weekNumber = this.calendar.calculateWeekNumber(date); - weekTitle = this.opt('weekNumberTitle'); - - if (this.opt('isRTL')) { - weekText = weekNumber + weekTitle; - } - else { - weekText = weekTitle + weekNumber; - } - - return '' + - '' + - '' + // needed for matchCellWidths - htmlEscape(weekText) + - '' + - ''; - } - else { - return ''; - } - }, - - - // Generates the HTML that goes before the all-day cells. - // Queried by the DayGrid subcomponent when generating rows. Ordering depends on isRTL. - dayIntroHtml: function() { - return '' + - '' + - '' + // needed for matchCellWidths - (this.opt('allDayHtml') || htmlEscape(this.opt('allDayText'))) + - '' + - ''; - }, - - - // Generates the HTML that goes before the bg of the TimeGrid slot area. Long vertical column. - slotBgIntroHtml: function() { - return ''; - }, - - - // Generates the HTML that goes before all other types of cells. - // Affects content-skeleton, helper-skeleton, highlight-skeleton for both the time-grid and day-grid. - // Queried by the TimeGrid and DayGrid subcomponents when generating rows. Ordering depends on isRTL. - introHtml: function() { - return ''; - }, - - - // Generates an HTML attribute string for setting the width of the axis, if it is known - axisStyleAttr: function() { - if (this.axisWidth !== null) { - return 'style="width:' + this.axisWidth + 'px"'; - } - return ''; - }, - - - /* Dimensions - ------------------------------------------------------------------------------------------------------------------*/ - - - updateSize: function(isResize) { - if (isResize) { - this.timeGrid.resize(); - } - View.prototype.updateSize.call(this, isResize); - }, - - - // Refreshes the horizontal dimensions of the view - updateWidth: function() { - // make all axis cells line up, and record the width so newly created axis cells will have it - this.axisWidth = matchCellWidths(this.el.find('.fc-axis')); - }, - - - // Adjusts the vertical dimensions of the view to the specified values - setHeight: function(totalHeight, isAuto) { - var eventLimit; - var scrollerHeight; - - if (this.bottomRuleHeight === null) { - // calculate the height of the rule the very first time - this.bottomRuleHeight = this.bottomRuleEl.outerHeight(); - } - this.bottomRuleEl.hide(); // .show() will be called later if this
is necessary - - // reset all dimensions back to the original state - this.scrollerEl.css('overflow', ''); - unsetScroller(this.scrollerEl); - uncompensateScroll(this.noScrollRowEls); - - // limit number of events in the all-day area - if (this.dayGrid) { - this.dayGrid.destroySegPopover(); // kill the "more" popover if displayed - - eventLimit = this.opt('eventLimit'); - if (eventLimit && typeof eventLimit !== 'number') { - eventLimit = AGENDA_ALL_DAY_EVENT_LIMIT; // make sure "auto" goes to a real number - } - if (eventLimit) { - this.dayGrid.limitRows(eventLimit); - } - } - - if (!isAuto) { // should we force dimensions of the scroll container, or let the contents be natural height? - - scrollerHeight = this.computeScrollerHeight(totalHeight); - if (setPotentialScroller(this.scrollerEl, scrollerHeight)) { // using scrollbars? - - // make the all-day and header rows lines up - compensateScroll(this.noScrollRowEls, getScrollbarWidths(this.scrollerEl)); - - // the scrollbar compensation might have changed text flow, which might affect height, so recalculate - // and reapply the desired height to the scroller. - scrollerHeight = this.computeScrollerHeight(totalHeight); - this.scrollerEl.height(scrollerHeight); - - this.restoreScroll(); - } - else { // no scrollbars - // still, force a height and display the bottom rule (marks the end of day) - this.scrollerEl.height(scrollerHeight).css('overflow', 'hidden'); // in case
goes outside - this.bottomRuleEl.show(); - } - } - }, - - - // Sets the scroll value of the scroller to the initial pre-configured state prior to allowing the user to change it - initializeScroll: function() { - var _this = this; - var scrollTime = moment.duration(this.opt('scrollTime')); - var top = this.timeGrid.computeTimeTop(scrollTime); - - // zoom can give weird floating-point values. rather scroll a little bit further - top = Math.ceil(top); + // Adjusts the vertical dimensions of the view to the specified values + setHeight: function (totalHeight, isAuto) { + var eventLimit = this.opt('eventLimit'); + var scrollerHeight; - if (top) { - top++; // to overcome top border that slots beyond the first have. looks better - } - - function scroll() { - _this.scrollerEl.scrollTop(top); - } - - scroll(); - setTimeout(scroll, 0); // overrides any previous scroll state made by the browser - }, - - - /* Events - ------------------------------------------------------------------------------------------------------------------*/ - - - // Renders events onto the view and populates the View's segment array - renderEvents: function(events) { - var dayEvents = []; - var timedEvents = []; - var daySegs = []; - var timedSegs; - var i; - - // separate the events into all-day and timed - for (i = 0; i < events.length; i++) { - if (events[i].allDay) { - dayEvents.push(events[i]); - } - else { - timedEvents.push(events[i]); - } - } - - // render the events in the subcomponents - timedSegs = this.timeGrid.renderEvents(timedEvents); - if (this.dayGrid) { - daySegs = this.dayGrid.renderEvents(dayEvents); - } - - // the all-day area is flexible and might have a lot of events, so shift the height - this.updateHeight(); - }, - - - // Retrieves all segment objects that are rendered in the view - getEventSegs: function() { - return this.timeGrid.getEventSegs().concat( - this.dayGrid ? this.dayGrid.getEventSegs() : [] - ); - }, - - - // Unrenders all event elements and clears internal segment data - destroyEvents: function() { - - // if destroyEvents is being called as part of an event rerender, renderEvents will be called shortly - // after, so remember what the scroll value was so we can restore it. - this.recordScroll(); - - // destroy the events in the subcomponents - this.timeGrid.destroyEvents(); - if (this.dayGrid) { - this.dayGrid.destroyEvents(); - } + // reset all heights to be natural + unsetScroller(this.scrollerEl); + uncompensateScroll(this.headRowEl); - // we DON'T need to call updateHeight() because: - // A) a renderEvents() call always happens after this, which will eventually call updateHeight() - // B) in IE8, this causes a flash whenever events are rerendered - }, - - - /* Dragging (for events and external elements) - ------------------------------------------------------------------------------------------------------------------*/ - - - // A returned value of `true` signals that a mock "helper" event has been rendered. - renderDrag: function(dropLocation, seg) { - if (dropLocation.start.hasTime()) { - return this.timeGrid.renderDrag(dropLocation, seg); - } - else if (this.dayGrid) { - return this.dayGrid.renderDrag(dropLocation, seg); - } - }, + this.dayGrid.destroySegPopover(); // kill the "more" popover if displayed + // is the event limit a constant level number? + if (eventLimit && typeof eventLimit === 'number') { + this.dayGrid.limitRows(eventLimit); // limit the levels first so the height can redistribute after + } - destroyDrag: function() { - this.timeGrid.destroyDrag(); - if (this.dayGrid) { - this.dayGrid.destroyDrag(); - } - }, + scrollerHeight = this.computeScrollerHeight(totalHeight); + this.setGridHeight(scrollerHeight, isAuto); + + // is the event limit dynamically calculated? + if (eventLimit && typeof eventLimit !== 'number') { + this.dayGrid.limitRows(eventLimit); // limit the levels after the grid's row heights have been set + } + + if (!isAuto && setPotentialScroller(this.scrollerEl, scrollerHeight)) { // using scrollbars? + + compensateScroll(this.headRowEl, getScrollbarWidths(this.scrollerEl)); + + // doing the scrollbar compensation might have created text overflow which created more height. redo + scrollerHeight = this.computeScrollerHeight(totalHeight); + this.scrollerEl.height(scrollerHeight); + + this.restoreScroll(); + } + }, + + + // Sets the height of just the DayGrid component in this view + setGridHeight: function (height, isAuto) { + if (isAuto) { + undistributeHeight(this.dayGrid.rowEls); // let the rows be their natural height with no expanding + } + else { + distributeHeight(this.dayGrid.rowEls, height, true); // true = compensate for height-hogging rows + } + }, + + + /* Events + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders the given events onto the view and populates the segments array + renderEvents: function (events) { + this.dayGrid.renderEvents(events); + + this.updateHeight(); // must compensate for events that overflow the row + }, + + + // Retrieves all segment objects that are rendered in the view + getEventSegs: function () { + return this.dayGrid.getEventSegs(); + }, + + + // Unrenders all event elements and clears internal segment data + destroyEvents: function () { + this.recordScroll(); // removing events will reduce height and mess with the scroll, so record beforehand + this.dayGrid.destroyEvents(); + + // we DON'T need to call updateHeight() because: + // A) a renderEvents() call always happens after this, which will eventually call updateHeight() + // B) in IE8, this causes a flash whenever events are rerendered + }, + + + /* Dragging (for both events and external elements) + ------------------------------------------------------------------------------------------------------------------*/ + + + // A returned value of `true` signals that a mock "helper" event has been rendered. + renderDrag: function (dropLocation, seg) { + return this.dayGrid.renderDrag(dropLocation, seg); + }, + + + destroyDrag: function () { + this.dayGrid.destroyDrag(); + }, + + + /* Selection + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of a selection + renderSelection: function (range) { + this.dayGrid.renderSelection(range); + }, + + + // Unrenders a visual indications of a selection + destroySelection: function () { + this.dayGrid.destroySelection(); + } + + }); + + ; + ; + + /* A month view with day cells running in rows (one-per-week) and columns + ----------------------------------------------------------------------------------------------------------------------*/ + + setDefaults({ + fixedWeekCount: true + }); + + var MonthView = fcViews.month = BasicView.extend({ + + // Produces information about what range to display + computeRange: function (date) { + var range = BasicView.prototype.computeRange.call(this, date); // get value from super-method + var rowCnt; + + // ensure 6 weeks + if (this.isFixedWeeks()) { + rowCnt = Math.ceil(range.end.diff(range.start, 'weeks', true)); // could be partial weeks due to hiddenDays + range.end.add(6 - rowCnt, 'weeks'); + } + + return range; + }, + + + // Overrides the default BasicView behavior to have special multi-week auto-height logic + setGridHeight: function (height, isAuto) { + + isAuto = isAuto || this.opt('weekMode') === 'variable'; // LEGACY: weekMode is deprecated + + // if auto, make the height of each row the height that it would be if there were 6 weeks + if (isAuto) { + height *= this.rowCnt / 6; + } + + distributeHeight(this.dayGrid.rowEls, height, !isAuto); // if auto, don't compensate for height-hogging rows + }, + + + isFixedWeeks: function () { + var weekMode = this.opt('weekMode'); // LEGACY: weekMode is deprecated + if (weekMode) { + return weekMode === 'fixed'; // if any other type of weekMode, assume NOT fixed + } + + return this.opt('fixedWeekCount'); + } + + }); + + MonthView.duration = { months: 1 }; + + ; + ; + + /* A week view with simple day cells running horizontally + ----------------------------------------------------------------------------------------------------------------------*/ + + fcViews.basicWeek = { + type: 'basic', + duration: { weeks: 1 } + }; + ; + ; + + /* A view with a single simple day cell + ----------------------------------------------------------------------------------------------------------------------*/ + + fcViews.basicDay = { + type: 'basic', + duration: { days: 1 } + }; + ; + ; + + /* An abstract class for all agenda-related views. Displays one more columns with time slots running vertically. + ----------------------------------------------------------------------------------------------------------------------*/ +// Is a manager for the TimeGrid subcomponent and possibly the DayGrid subcomponent (if allDaySlot is on). +// Responsible for managing width/height. + setDefaults({ + allDaySlot: true, + allDayText: 'all-day', + scrollTime: '06:00:00', + slotDuration: '00:30:00', + minTime: '00:00:00', + maxTime: '24:00:00', + slotEventOverlap: true + }); - /* Selection - ------------------------------------------------------------------------------------------------------------------*/ + var AGENDA_ALL_DAY_EVENT_LIMIT = 5; + fcViews.agenda = View.extend({ // AgendaView - // Renders a visual indication of a selection - renderSelection: function(range) { - if (range.start.hasTime() || range.end.hasTime()) { - this.timeGrid.renderSelection(range); - } - else if (this.dayGrid) { - this.dayGrid.renderSelection(range); - } - }, + timeGrid: null, // the main time-grid subcomponent of this view + dayGrid: null, // the "all-day" subcomponent. if all-day is turned off, this will be null + axisWidth: null, // the width of the time axis running down the side + + noScrollRowEls: null, // set of fake row elements that must compensate when scrollerEl has scrollbars + + // when the time-grid isn't tall enough to occupy the given height, we render an
underneath + bottomRuleEl: null, + bottomRuleHeight: null, + + + initialize: function () { + this.timeGrid = new TimeGrid(this); + + if (this.opt('allDaySlot')) { // should we display the "all-day" area? + this.dayGrid = new DayGrid(this); // the all-day subcomponent of this view + + // the coordinate grid will be a combination of both subcomponents' grids + this.coordMap = new ComboCoordMap([ + this.dayGrid.coordMap, + this.timeGrid.coordMap + ]); + } + else { + this.coordMap = this.timeGrid.coordMap; + } + }, + + + /* Rendering + ------------------------------------------------------------------------------------------------------------------*/ + + + // Sets the display range and computes all necessary dates + setRange: function (range) { + View.prototype.setRange.call(this, range); // call the super-method + + this.timeGrid.setRange(range); + if (this.dayGrid) { + this.dayGrid.setRange(range); + } + }, + + + // Renders the view into `this.el`, which has already been assigned + render: function () { + + this.el.addClass('fc-agenda-view').html(this.renderHtml()); + + // the element that wraps the time-grid that will probably scroll + this.scrollerEl = this.el.find('.fc-time-grid-container'); + this.timeGrid.coordMap.containerEl = this.scrollerEl; // don't accept clicks/etc outside of this + + this.timeGrid.el = this.el.find('.fc-time-grid'); + this.timeGrid.render(); + + // the
that sometimes displays under the time-grid + this.bottomRuleEl = $('
') + .appendTo(this.timeGrid.el); // inject it into the time-grid + + if (this.dayGrid) { + this.dayGrid.el = this.el.find('.fc-day-grid'); + this.dayGrid.render(); + + // have the day-grid extend it's coordinate area over the
dividing the two grids + this.dayGrid.bottomCoordPadding = this.dayGrid.el.next('hr').outerHeight(); + } + + this.noScrollRowEls = this.el.find('.fc-row:not(.fc-scroller *)'); // fake rows not within the scroller + }, + + + // Make subcomponents ready for cleanup + destroy: function () { + this.timeGrid.destroy(); + if (this.dayGrid) { + this.dayGrid.destroy(); + } + View.prototype.destroy.call(this); // call the super-method + }, + + + // Builds the HTML skeleton for the view. + // The day-grid and time-grid components will render inside containers defined by this HTML. + renderHtml: function () { + return '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
' + + this.timeGrid.headHtml() + // render the day-of-week headers + '
' + + (this.dayGrid ? + '
' + + '
' : + '' + ) + + '
' + + '
' + + '
' + + '
'; + }, + + + // Generates the HTML that will go before the day-of week header cells. + // Queried by the TimeGrid subcomponent when generating rows. Ordering depends on isRTL. + headIntroHtml: function () { + var date; + var weekNumber; + var weekTitle; + var weekText; + + if (this.opt('weekNumbers')) { + date = this.timeGrid.getCell(0).start; + weekNumber = this.calendar.calculateWeekNumber(date); + weekTitle = this.opt('weekNumberTitle'); + + if (this.opt('isRTL')) { + weekText = weekNumber + weekTitle; + } + else { + weekText = weekTitle + weekNumber; + } + + return '' + + '' + + '' + // needed for matchCellWidths + htmlEscape(weekText) + + '' + + ''; + } + else { + return ''; + } + }, + + + // Generates the HTML that goes before the all-day cells. + // Queried by the DayGrid subcomponent when generating rows. Ordering depends on isRTL. + dayIntroHtml: function () { + return '' + + '' + + '' + // needed for matchCellWidths + (this.opt('allDayHtml') || htmlEscape(this.opt('allDayText'))) + + '' + + ''; + }, + + + // Generates the HTML that goes before the bg of the TimeGrid slot area. Long vertical column. + slotBgIntroHtml: function () { + return ''; + }, + + + // Generates the HTML that goes before all other types of cells. + // Affects content-skeleton, helper-skeleton, highlight-skeleton for both the time-grid and day-grid. + // Queried by the TimeGrid and DayGrid subcomponents when generating rows. Ordering depends on isRTL. + introHtml: function () { + return ''; + }, + + + // Generates an HTML attribute string for setting the width of the axis, if it is known + axisStyleAttr: function () { + if (this.axisWidth !== null) { + return 'style="width:' + this.axisWidth + 'px"'; + } + return ''; + }, + + + /* Dimensions + ------------------------------------------------------------------------------------------------------------------*/ + + + updateSize: function (isResize) { + if (isResize) { + this.timeGrid.resize(); + } + View.prototype.updateSize.call(this, isResize); + }, + + + // Refreshes the horizontal dimensions of the view + updateWidth: function () { + // make all axis cells line up, and record the width so newly created axis cells will have it + this.axisWidth = matchCellWidths(this.el.find('.fc-axis')); + }, + + + // Adjusts the vertical dimensions of the view to the specified values + setHeight: function (totalHeight, isAuto) { + var eventLimit; + var scrollerHeight; + + if (this.bottomRuleHeight === null) { + // calculate the height of the rule the very first time + this.bottomRuleHeight = this.bottomRuleEl.outerHeight(); + } + this.bottomRuleEl.hide(); // .show() will be called later if this
is necessary + + // reset all dimensions back to the original state + this.scrollerEl.css('overflow', ''); + unsetScroller(this.scrollerEl); + uncompensateScroll(this.noScrollRowEls); + + // limit number of events in the all-day area + if (this.dayGrid) { + this.dayGrid.destroySegPopover(); // kill the "more" popover if displayed + + eventLimit = this.opt('eventLimit'); + if (eventLimit && typeof eventLimit !== 'number') { + eventLimit = AGENDA_ALL_DAY_EVENT_LIMIT; // make sure "auto" goes to a real number + } + if (eventLimit) { + this.dayGrid.limitRows(eventLimit); + } + } + + if (!isAuto) { // should we force dimensions of the scroll container, or let the contents be natural height? + + scrollerHeight = this.computeScrollerHeight(totalHeight); + if (setPotentialScroller(this.scrollerEl, scrollerHeight)) { // using scrollbars? + + // make the all-day and header rows lines up + compensateScroll(this.noScrollRowEls, getScrollbarWidths(this.scrollerEl)); + + // the scrollbar compensation might have changed text flow, which might affect height, so recalculate + // and reapply the desired height to the scroller. + scrollerHeight = this.computeScrollerHeight(totalHeight); + this.scrollerEl.height(scrollerHeight); + + this.restoreScroll(); + } + else { // no scrollbars + // still, force a height and display the bottom rule (marks the end of day) + this.scrollerEl.height(scrollerHeight).css('overflow', 'hidden'); // in case
goes outside + this.bottomRuleEl.show(); + } + } + }, + + + // Sets the scroll value of the scroller to the initial pre-configured state prior to allowing the user to change it + initializeScroll: function () { + var _this = this; + var scrollTime = moment.duration(this.opt('scrollTime')); + var top = this.timeGrid.computeTimeTop(scrollTime); + + // zoom can give weird floating-point values. rather scroll a little bit further + top = Math.ceil(top); - // Unrenders a visual indications of a selection - destroySelection: function() { - this.timeGrid.destroySelection(); - if (this.dayGrid) { - this.dayGrid.destroySelection(); - } - } - -}); - -;; - -/* A week view with an all-day cell area at the top, and a time grid below -----------------------------------------------------------------------------------------------------------------------*/ - -fcViews.agendaWeek = { - type: 'agenda', - duration: { weeks: 1 } -}; -;; + if (top) { + top++; // to overcome top border that slots beyond the first have. looks better + } + + function scroll() { + _this.scrollerEl.scrollTop(top); + } + + scroll(); + setTimeout(scroll, 0); // overrides any previous scroll state made by the browser + }, + + + /* Events + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders events onto the view and populates the View's segment array + renderEvents: function (events) { + var dayEvents = []; + var timedEvents = []; + var daySegs = []; + var timedSegs; + var i; + + // separate the events into all-day and timed + for (i = 0; i < events.length; i++) { + if (events[i].allDay) { + dayEvents.push(events[i]); + } + else { + timedEvents.push(events[i]); + } + } + + // render the events in the subcomponents + timedSegs = this.timeGrid.renderEvents(timedEvents); + if (this.dayGrid) { + daySegs = this.dayGrid.renderEvents(dayEvents); + } + + // the all-day area is flexible and might have a lot of events, so shift the height + this.updateHeight(); + }, + + + // Retrieves all segment objects that are rendered in the view + getEventSegs: function () { + return this.timeGrid.getEventSegs().concat( + this.dayGrid ? this.dayGrid.getEventSegs() : [] + ); + }, + + + // Unrenders all event elements and clears internal segment data + destroyEvents: function () { + + // if destroyEvents is being called as part of an event rerender, renderEvents will be called shortly + // after, so remember what the scroll value was so we can restore it. + this.recordScroll(); + + // destroy the events in the subcomponents + this.timeGrid.destroyEvents(); + if (this.dayGrid) { + this.dayGrid.destroyEvents(); + } + + // we DON'T need to call updateHeight() because: + // A) a renderEvents() call always happens after this, which will eventually call updateHeight() + // B) in IE8, this causes a flash whenever events are rerendered + }, + + + /* Dragging (for events and external elements) + ------------------------------------------------------------------------------------------------------------------*/ + + + // A returned value of `true` signals that a mock "helper" event has been rendered. + renderDrag: function (dropLocation, seg) { + if (dropLocation.start.hasTime()) { + return this.timeGrid.renderDrag(dropLocation, seg); + } + else if (this.dayGrid) { + return this.dayGrid.renderDrag(dropLocation, seg); + } + }, + + + destroyDrag: function () { + this.timeGrid.destroyDrag(); + if (this.dayGrid) { + this.dayGrid.destroyDrag(); + } + }, + + + /* Selection + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of a selection + renderSelection: function (range) { + if (range.start.hasTime() || range.end.hasTime()) { + this.timeGrid.renderSelection(range); + } + else if (this.dayGrid) { + this.dayGrid.renderSelection(range); + } + }, + + + // Unrenders a visual indications of a selection + destroySelection: function () { + this.timeGrid.destroySelection(); + if (this.dayGrid) { + this.dayGrid.destroySelection(); + } + } + + }); + + ; + ; + + /* A week view with an all-day cell area at the top, and a time grid below + ----------------------------------------------------------------------------------------------------------------------*/ + + fcViews.agendaWeek = { + type: 'agenda', + duration: { weeks: 1 } + }; + ; + ; -/* A day view with an all-day cell area at the top, and a time grid below -----------------------------------------------------------------------------------------------------------------------*/ + /* A day view with an all-day cell area at the top, and a time grid below + ----------------------------------------------------------------------------------------------------------------------*/ -fcViews.agendaDay = { - type: 'agenda', - duration: { days: 1 } -}; -;; + fcViews.agendaDay = { + type: 'agenda', + duration: { days: 1 } + }; + ; + ; }); \ No newline at end of file diff --git a/src/main/webapp/resources/fullCalendar/fullcalendar.print.css b/src/main/webapp/resources/fullCalendar/fullcalendar.print.css index f98f539..8535858 100644 --- a/src/main/webapp/resources/fullCalendar/fullcalendar.print.css +++ b/src/main/webapp/resources/fullCalendar/fullcalendar.print.css @@ -11,23 +11,22 @@ */ .fc { - max-width: 100% !important; + max-width: 100% !important; } /* Global Event Restyling --------------------------------------------------------------------------------------------------*/ .fc-event { - background: #fff !important; - color: #000 !important; - page-break-inside: avoid; + background: #fff !important; + color: #000 !important; + page-break-inside: avoid; } .fc-event .fc-resizer { - display: none; + display: none; } - /* Table & Day-Row Restyling --------------------------------------------------------------------------------------------------*/ @@ -37,8 +36,8 @@ hr, thead, tbody, .fc-row { - border-color: #ccc !important; - background: #fff !important; + border-color: #ccc !important; + background: #fff !important; } /* kill the overlaid, absolutely-positioned common components */ @@ -46,156 +45,154 @@ tbody, .fc-bgevent-skeleton, .fc-highlight-skeleton, .fc-helper-skeleton { - display: none; + display: none; } /* don't force a min-height on rows (for DayGrid) */ .fc tbody .fc-row { - height: auto !important; /* undo height that JS set in distributeHeight */ - min-height: 0 !important; /* undo the min-height from each view's specific stylesheet */ + height: auto !important; /* undo height that JS set in distributeHeight */ + min-height: 0 !important; /* undo the min-height from each view's specific stylesheet */ } .fc tbody .fc-row .fc-content-skeleton { - position: static; /* undo .fc-rigid */ - padding-bottom: 0 !important; /* use a more border-friendly method for this... */ + position: static; /* undo .fc-rigid */ + padding-bottom: 0 !important; /* use a more border-friendly method for this... */ } -.fc tbody .fc-row .fc-content-skeleton tbody tr:last-child td { /* only works in newer browsers */ - padding-bottom: 1em; /* ...gives space within the skeleton. also ensures min height in a way */ +.fc tbody .fc-row .fc-content-skeleton tbody tr:last-child td { + /* only works in newer browsers */ + padding-bottom: 1em; /* ...gives space within the skeleton. also ensures min height in a way */ } .fc tbody .fc-row .fc-content-skeleton table { - /* provides a min-height for the row, but only effective for IE, which exaggerates this value, - making it look more like 3em. for other browers, it will already be this tall */ - height: 1em; + /* provides a min-height for the row, but only effective for IE, which exaggerates this value, + making it look more like 3em. for other browers, it will already be this tall */ + height: 1em; } - /* Undo month-view event limiting. Display all events and hide the "more" links --------------------------------------------------------------------------------------------------*/ .fc-more-cell, .fc-more { - display: none !important; + display: none !important; } .fc tr.fc-limited { - display: table-row !important; + display: table-row !important; } .fc td.fc-limited { - display: table-cell !important; + display: table-cell !important; } .fc-popover { - display: none; /* never display the "more.." popover in print mode */ + display: none; /* never display the "more.." popover in print mode */ } - /* TimeGrid Restyling --------------------------------------------------------------------------------------------------*/ /* undo the min-height 100% trick used to fill the container's height */ .fc-time-grid { - min-height: 0 !important; + min-height: 0 !important; } /* don't display the side axis at all ("all-day" and time cells) */ .fc-agenda-view .fc-axis { - display: none; + display: none; } /* don't display the horizontal lines */ .fc-slats, -.fc-time-grid hr { /* this hr is used when height is underused and needs to be filled */ - display: none !important; /* important overrides inline declaration */ +.fc-time-grid hr { + /* this hr is used when height is underused and needs to be filled */ + display: none !important; /* important overrides inline declaration */ } /* let the container that holds the events be naturally positioned and create real height */ .fc-time-grid .fc-content-skeleton { - position: static; + position: static; } /* in case there are no events, we still want some height */ .fc-time-grid .fc-content-skeleton table { - height: 4em; + height: 4em; } /* kill the horizontal spacing made by the event container. event margins will be done below */ .fc-time-grid .fc-event-container { - margin: 0 !important; + margin: 0 !important; } - /* TimeGrid *Event* Restyling --------------------------------------------------------------------------------------------------*/ /* naturally position events, vertically stacking them */ .fc-time-grid .fc-event { - position: static !important; - margin: 3px 2px !important; + position: static !important; + margin: 3px 2px !important; } /* for events that continue to a future day, give the bottom border back */ .fc-time-grid .fc-event.fc-not-end { - border-bottom-width: 1px !important; + border-bottom-width: 1px !important; } /* indicate the event continues via "..." text */ .fc-time-grid .fc-event.fc-not-end:after { - content: "..."; + content: "..."; } /* for events that are continuations from previous days, give the top border back */ .fc-time-grid .fc-event.fc-not-start { - border-top-width: 1px !important; + border-top-width: 1px !important; } /* indicate the event is a continuation via "..." text */ .fc-time-grid .fc-event.fc-not-start:before { - content: "..."; + content: "..."; } /* time */ /* undo a previous declaration and let the time text span to a second line */ .fc-time-grid .fc-event .fc-time { - white-space: normal !important; + white-space: normal !important; } /* hide the the time that is normally displayed... */ .fc-time-grid .fc-event .fc-time span { - display: none; + display: none; } /* ...replace it with a more verbose version (includes AM/PM) stored in an html attribute */ .fc-time-grid .fc-event .fc-time:after { - content: attr(data-full); + content: attr(data-full); } - /* Vertical Scroller & Containers --------------------------------------------------------------------------------------------------*/ /* kill the scrollbars and allow natural height */ .fc-scroller, -.fc-day-grid-container, /* these divs might be assigned height, which we need to cleared */ -.fc-time-grid-container { /* */ - overflow: visible !important; - height: auto !important; +.fc-day-grid-container, /* these divs might be assigned height, which we need to cleared */ +.fc-time-grid-container { + /* */ + overflow: visible !important; + height: auto !important; } /* kill the horizontal border/padding used to compensate for scrollbars */ .fc-row { - border: 0 !important; - margin: 0 !important; + border: 0 !important; + margin: 0 !important; } - /* Button Controls --------------------------------------------------------------------------------------------------*/ .fc-button-group, .fc button { - display: none; /* don't display any button-related controls */ + display: none; /* don't display any button-related controls */ } diff --git a/src/main/webapp/resources/fullCalendar/jquery.min.js b/src/main/webapp/resources/fullCalendar/jquery.min.js index c4643af..bd0d81b 100644 --- a/src/main/webapp/resources/fullCalendar/jquery.min.js +++ b/src/main/webapp/resources/fullCalendar/jquery.min.js @@ -1,5 +1,2067 @@ /*! jQuery v2.1.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ -!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.1",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="
",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+Math.random()}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b) -},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*\s*$/g,ib={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(ob(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(ob(c,"script"),kb),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(hb,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("