diff --git a/samples/coordinatesGrid-sample/pom.xml b/samples/coordinatesGrid-sample/pom.xml new file mode 100644 index 0000000..529c26a --- /dev/null +++ b/samples/coordinatesGrid-sample/pom.xml @@ -0,0 +1,34 @@ + + + + org.richfaces.sandbox.bernard + parent + 3.3.4-SNAPSHOT + ../pom.xml + + 4.0.0 + org.richfaces.sandbox.ui + coordinatesGrid-sample + war + coordinatesGrid-sample Maven Webapp + + ${project.artifactId} + + + + org.richfaces.ui + richfaces-ui + + + org.richfaces.sandbox.ui + coordinatesGrid + ${project.version} + + + com.uwyn + jhighlight + 1.0 + + + diff --git a/samples/coordinatesGrid-sample/src/main/java/org/richfaces/sandbox/coordinatesGrid/Seat.java b/samples/coordinatesGrid-sample/src/main/java/org/richfaces/sandbox/coordinatesGrid/Seat.java new file mode 100644 index 0000000..13a9bc6 --- /dev/null +++ b/samples/coordinatesGrid-sample/src/main/java/org/richfaces/sandbox/coordinatesGrid/Seat.java @@ -0,0 +1,37 @@ +package org.richfaces.sandbox.coordinatesGrid; + +import java.io.Serializable; + +/** + * @author a180963 + */ +public class Seat implements Serializable { + + private int row; + private int seatNr; + private boolean enabled; + + public int getRow() { + return row; + } + + public void setRow(int row) { + this.row = row; + } + + public int getSeatNr() { + return seatNr; + } + + public void setSeatNr(int seatNr) { + this.seatNr = seatNr; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } +} diff --git a/samples/coordinatesGrid-sample/src/main/java/org/richfaces/sandbox/coordinatesGrid/SeatSelector.java b/samples/coordinatesGrid-sample/src/main/java/org/richfaces/sandbox/coordinatesGrid/SeatSelector.java new file mode 100644 index 0000000..43f9389 --- /dev/null +++ b/samples/coordinatesGrid-sample/src/main/java/org/richfaces/sandbox/coordinatesGrid/SeatSelector.java @@ -0,0 +1,151 @@ +package org.richfaces.sandbox.coordinatesGrid; + +import org.ajax4jsf.model.DataVisitor; +import org.ajax4jsf.model.Range; +import org.ajax4jsf.model.SequenceDataModel; +import org.ajax4jsf.model.SequenceRange; +import org.richfaces.component.event.CoordinatesSelectionEvent; +import org.richfaces.component.model.SquareRange; + +import javax.faces.application.FacesMessage; +import javax.faces.context.FacesContext; +import javax.faces.model.DataModel; +import javax.faces.model.ListDataModel; +import java.io.IOException; +import java.util.AbstractList; + +public class SeatSelector { + + private Integer rows = 10; + private Integer cols = 20; + private Integer firstRow = 0; + private Integer firstCol = 0; + private Seat[][] seats = new Seat[rows][cols]; + private DataModel dataModel = new CustomDataModel(); + + public SeatSelector() { + + final Seat seat = new Seat(); + seat.setEnabled(true); + seat.setRow(3); + seat.setSeatNr(5); + seats[3][4] = seat; + } + + public Integer getRows() { + return rows; + } + + public void setRows(Integer rows) { + this.rows = rows; + resizeSeats(); + } + + public Integer getCols() { + return cols; + } + + public void setCols(Integer cols) { + this.cols = cols; + resizeSeats(); + } + + public Integer getFirstRow() { + return firstRow; + } + + public void setFirstRow(Integer row) { + this.firstRow = row; + } + + public Integer getFirstCol() { + return firstCol; + } + + public void setFirstCol(Integer col) { + this.firstCol = col; + } + + public DataModel getDataModel() { + return dataModel; + } + + public void itemsSelected(CoordinatesSelectionEvent e) { + for (int y = e.getStartY(); y <= e.getEndY(); y++) { + for (int x = e.getStartX(); x <= e.getEndX(); x++) { + if (seats[y][x] == null) { + Seat seat = new Seat(); + seat.setRow(y); + seat.setSeatNr(x); + seat.setEnabled(true); + seats[y][x] = seat; + } else { + seats[y][x] = null; + } + } + } + FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(e.toString())); + } + + public void itemsReserved(CoordinatesSelectionEvent e) { + for (int y = e.getStartY(); y <= e.getEndY(); y++) { + for (int x = e.getStartX(); x <= e.getEndX(); x++) { + if (seats[y][x] != null) { + seats[y][x].setEnabled(!seats[y][x].isEnabled()); + } + } + } + } + + private void resizeSeats() { + Seat[][] newSeats = new Seat[rows][cols]; + for (int y = 0; y < Math.min(seats.length, newSeats.length); y++) { + System.arraycopy(seats[y], 0, newSeats[y], 0, Math.min(seats[y].length, newSeats[y].length)); + } + seats = newSeats; + } + + private class CustomDataModel extends SequenceDataModel { + + public CustomDataModel() { + super(new ListDataModel(new AbstractList() { + + @Override + public Seat get(int index) { + int y = index / cols; + int x = index % cols; + return seats[y][x]; + } + + @Override + public int size() { + return cols * rows; + } + })); + } + + @Override + public boolean isRowAvailable() { + return super.isRowAvailable() && getRowData() != null; + } + + @Override + public void walk(FacesContext context, DataVisitor visitor, Range range, Object argument) throws IOException { + SquareRange squareRange = null; + if (range instanceof SquareRange) { + squareRange = (SquareRange) range; + } + if (squareRange == null) { + super.walk(context, visitor, new SequenceRange(0, -1), argument); + } else { + for (int y = squareRange.getStartY(); y <= squareRange.getEndY(); y++) { + for (int x = squareRange.getStartX(); x <= squareRange.getEndX(); x++) { + if (0 <= y && y < seats.length && 0 <= x && x < seats[y].length && seats[y][x] != null) { + visitor.process(context, y * cols + x, argument); + } + } + } + } + } + } +} diff --git a/samples/coordinatesGrid-sample/src/main/webapp/META-INF/MANIFEST.MF b/samples/coordinatesGrid-sample/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 0000000..254272e --- /dev/null +++ b/samples/coordinatesGrid-sample/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/samples/coordinatesGrid-sample/src/main/webapp/META-INF/context.xml b/samples/coordinatesGrid-sample/src/main/webapp/META-INF/context.xml new file mode 100644 index 0000000..9837cef --- /dev/null +++ b/samples/coordinatesGrid-sample/src/main/webapp/META-INF/context.xml @@ -0,0 +1,2 @@ + + diff --git a/samples/coordinatesGrid-sample/src/main/webapp/WEB-INF/faces-config.xml b/samples/coordinatesGrid-sample/src/main/webapp/WEB-INF/faces-config.xml new file mode 100644 index 0000000..0160fed --- /dev/null +++ b/samples/coordinatesGrid-sample/src/main/webapp/WEB-INF/faces-config.xml @@ -0,0 +1,11 @@ + + + + + seatSelector + org.richfaces.sandbox.coordinatesGrid.SeatSelector + session + + + diff --git a/samples/coordinatesGrid-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/samples/coordinatesGrid-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml new file mode 100644 index 0000000..5f07358 --- /dev/null +++ b/samples/coordinatesGrid-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/samples/coordinatesGrid-sample/src/main/webapp/WEB-INF/web.xml b/samples/coordinatesGrid-sample/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..8073af3 --- /dev/null +++ b/samples/coordinatesGrid-sample/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,74 @@ + + + Richfaces coordinatesGrid-sm Sample + coordinatesGrid-sm-sample + + javax.faces.DEFAULT_SUFFIX + .xhtml + + + facelets.REFRESH_PERIOD + 2 + + + facelets.DEVELOPMENT + true + + + javax.faces.STATE_SAVING_METHOD + server + + + com.sun.faces.validateXml + true + + + com.sun.faces.verifyObjects + false + + + org.ajax4jsf.VIEW_HANDLERS + com.sun.facelets.FaceletViewHandler + + + org.ajax4jsf.COMPRESS_SCRIPT + false + + + org.richfaces.SKIN + blueSky + + + org.richfaces.CONTROL_SKINNING_LEVEL + extended + + + Ajax4jsf Filter + ajax4jsf + org.ajax4jsf.Filter + + enable-cache + false + + + + ajax4jsf + Faces Servlet + FORWARD + REQUEST + INCLUDE + + + Faces Servlet + javax.faces.webapp.FacesServlet + 1 + + + Faces Servlet + *.jsf + + + BASIC + + diff --git a/samples/coordinatesGrid-sample/src/main/webapp/index.jsp b/samples/coordinatesGrid-sample/src/main/webapp/index.jsp new file mode 100644 index 0000000..1b45339 --- /dev/null +++ b/samples/coordinatesGrid-sample/src/main/webapp/index.jsp @@ -0,0 +1,7 @@ + + + + + + + diff --git a/samples/coordinatesGrid-sample/src/main/webapp/index.xhtml b/samples/coordinatesGrid-sample/src/main/webapp/index.xhtml new file mode 100644 index 0000000..81754c3 --- /dev/null +++ b/samples/coordinatesGrid-sample/src/main/webapp/index.xhtml @@ -0,0 +1,110 @@ + + + + CoordinatesGrid sample + + + + + + + +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + diff --git a/samples/coordinatesGrid-sample/src/main/webapp/menu.xhtml b/samples/coordinatesGrid-sample/src/main/webapp/menu.xhtml new file mode 100644 index 0000000..8a1ce20 --- /dev/null +++ b/samples/coordinatesGrid-sample/src/main/webapp/menu.xhtml @@ -0,0 +1,8 @@ + + + Example 1 + + diff --git a/samples/dock-sample/pom.xml b/samples/dock-sample/pom.xml new file mode 100644 index 0000000..ed3b6b3 --- /dev/null +++ b/samples/dock-sample/pom.xml @@ -0,0 +1,39 @@ + + + org.richfaces.sandbox.bernard + parent + 3.3.4-SNAPSHOT + ../pom.xml + + 4.0.0 + org.richfaces.sandbox.ui + dock-sample + war + dock-sample Maven Webapp + + ${project.artifactId} + + + + org.richfaces.ui + richfaces-ui + + + org.richfaces.sandbox.ui + dock + ${project.version} + + + org.richfaces.sandbox.ui + notify + ${project.version} + + + com.uwyn + jhighlight + 1.0 + + + + diff --git a/samples/dock-sample/src/main/java/org/richfaces/sandbox/dock/Greeter.java b/samples/dock-sample/src/main/java/org/richfaces/sandbox/dock/Greeter.java new file mode 100644 index 0000000..5a16ccf --- /dev/null +++ b/samples/dock-sample/src/main/java/org/richfaces/sandbox/dock/Greeter.java @@ -0,0 +1,20 @@ +package org.richfaces.sandbox.dock; + +import javax.faces.application.FacesMessage; +import javax.faces.context.FacesContext; + +public class Greeter { + + public String sayHello() { + FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Hello", + "You've just made JSF postback. Isn't this great!")); + return "hello"; + } + + public String sayHelloAjax() { + FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Hello", + "This is AJAX request. Isn't this even greater!")); + return "helloAjax"; + } + +} diff --git a/samples/dock-sample/src/main/webapp/META-INF/MANIFEST.MF b/samples/dock-sample/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 0000000..254272e --- /dev/null +++ b/samples/dock-sample/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/samples/dock-sample/src/main/webapp/META-INF/context.xml b/samples/dock-sample/src/main/webapp/META-INF/context.xml new file mode 100644 index 0000000..0771698 --- /dev/null +++ b/samples/dock-sample/src/main/webapp/META-INF/context.xml @@ -0,0 +1,2 @@ + + diff --git a/samples/dock-sample/src/main/webapp/WEB-INF/faces-config.xml b/samples/dock-sample/src/main/webapp/WEB-INF/faces-config.xml new file mode 100644 index 0000000..52ae594 --- /dev/null +++ b/samples/dock-sample/src/main/webapp/WEB-INF/faces-config.xml @@ -0,0 +1,11 @@ + + + + + greeter + org.richfaces.sandbox.dock.Greeter + request + + + diff --git a/samples/dock-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/samples/dock-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml new file mode 100644 index 0000000..5f07358 --- /dev/null +++ b/samples/dock-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/samples/dock-sample/src/main/webapp/WEB-INF/web.xml b/samples/dock-sample/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..34d30b9 --- /dev/null +++ b/samples/dock-sample/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,74 @@ + + + Richfaces Notify Sample + notify-sample + + javax.faces.DEFAULT_SUFFIX + .xhtml + + + facelets.REFRESH_PERIOD + 2 + + + facelets.DEVELOPMENT + true + + + javax.faces.STATE_SAVING_METHOD + server + + + com.sun.faces.validateXml + true + + + com.sun.faces.verifyObjects + false + + + org.ajax4jsf.VIEW_HANDLERS + com.sun.facelets.FaceletViewHandler + + + org.ajax4jsf.COMPRESS_SCRIPT + false + + + org.richfaces.SKIN + blueSky + + + org.richfaces.CONTROL_SKINNING_LEVEL + extended + + + Ajax4jsf Filter + ajax4jsf + org.ajax4jsf.Filter + + enable-cache + false + + + + ajax4jsf + Faces Servlet + FORWARD + REQUEST + INCLUDE + + + Faces Servlet + javax.faces.webapp.FacesServlet + 1 + + + Faces Servlet + *.jsf + + + BASIC + + diff --git a/samples/dock-sample/src/main/webapp/dock.xhtml b/samples/dock-sample/src/main/webapp/dock.xhtml new file mode 100644 index 0000000..ad7eef4 --- /dev/null +++ b/samples/dock-sample/src/main/webapp/dock.xhtml @@ -0,0 +1,111 @@ + + + + Dock sample + + + + + + +

+ Here we have notify displaying all messages (not only global). + Severity is reflected in overriden style classes. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/dock-sample/src/main/webapp/images/next.png b/samples/dock-sample/src/main/webapp/images/next.png new file mode 100644 index 0000000..7e94662 Binary files /dev/null and b/samples/dock-sample/src/main/webapp/images/next.png differ diff --git a/samples/dock-sample/src/main/webapp/images/pause.png b/samples/dock-sample/src/main/webapp/images/pause.png new file mode 100644 index 0000000..e195de3 Binary files /dev/null and b/samples/dock-sample/src/main/webapp/images/pause.png differ diff --git a/samples/dock-sample/src/main/webapp/images/play.png b/samples/dock-sample/src/main/webapp/images/play.png new file mode 100644 index 0000000..fa49a06 Binary files /dev/null and b/samples/dock-sample/src/main/webapp/images/play.png differ diff --git a/samples/dock-sample/src/main/webapp/images/previous.png b/samples/dock-sample/src/main/webapp/images/previous.png new file mode 100644 index 0000000..47668e9 Binary files /dev/null and b/samples/dock-sample/src/main/webapp/images/previous.png differ diff --git a/samples/dock-sample/src/main/webapp/images/stop.png b/samples/dock-sample/src/main/webapp/images/stop.png new file mode 100644 index 0000000..15fa853 Binary files /dev/null and b/samples/dock-sample/src/main/webapp/images/stop.png differ diff --git a/samples/dock-sample/src/main/webapp/index.jsp b/samples/dock-sample/src/main/webapp/index.jsp new file mode 100644 index 0000000..f914127 --- /dev/null +++ b/samples/dock-sample/src/main/webapp/index.jsp @@ -0,0 +1,7 @@ + + + + + + + diff --git a/samples/dock-sample/src/main/webapp/menu.xhtml b/samples/dock-sample/src/main/webapp/menu.xhtml new file mode 100644 index 0000000..4b5b778 --- /dev/null +++ b/samples/dock-sample/src/main/webapp/menu.xhtml @@ -0,0 +1,8 @@ + + + Example 1 + + diff --git a/samples/focus-sample/pom.xml b/samples/focus-sample/pom.xml new file mode 100644 index 0000000..73bdc36 --- /dev/null +++ b/samples/focus-sample/pom.xml @@ -0,0 +1,38 @@ + + + org.richfaces.sandbox.bernard + parent + 3.3.4-SNAPSHOT + ../pom.xml + + 4.0.0 + org.richfaces + focus-sample + war + focus-sample Maven Webapp + + ${project.artifactId} + + + + org.richfaces.ui + core + ${project.version} + + + org.richfaces.ui + richfaces-ui + + + com.uwyn + jhighlight + 1.0 + + + org.richfaces.sandbox.ui + focus + ${project.version} + + + diff --git a/samples/focus-sample/src/main/webapp/META-INF/MANIFEST.MF b/samples/focus-sample/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 0000000..254272e --- /dev/null +++ b/samples/focus-sample/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/samples/focus-sample/src/main/webapp/META-INF/context.xml b/samples/focus-sample/src/main/webapp/META-INF/context.xml new file mode 100644 index 0000000..cbfd42a --- /dev/null +++ b/samples/focus-sample/src/main/webapp/META-INF/context.xml @@ -0,0 +1,2 @@ + + diff --git a/samples/focus-sample/src/main/webapp/WEB-INF/faces-config.xml b/samples/focus-sample/src/main/webapp/WEB-INF/faces-config.xml new file mode 100644 index 0000000..2ac4867 --- /dev/null +++ b/samples/focus-sample/src/main/webapp/WEB-INF/faces-config.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/samples/focus-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/samples/focus-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml new file mode 100644 index 0000000..5f07358 --- /dev/null +++ b/samples/focus-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/samples/focus-sample/src/main/webapp/WEB-INF/web.xml b/samples/focus-sample/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..9799443 --- /dev/null +++ b/samples/focus-sample/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,63 @@ + + + Richfaces Focus Sample + focus-sample + + javax.faces.DEFAULT_SUFFIX + .xhtml + + + facelets.REFRESH_PERIOD + 2 + + + facelets.DEVELOPMENT + true + + + javax.faces.STATE_SAVING_METHOD + client + + + com.sun.faces.validateXml + true + + + com.sun.faces.verifyObjects + true + + + org.ajax4jsf.VIEW_HANDLERS + com.sun.facelets.FaceletViewHandler + + + org.ajax4jsf.COMPRESS_SCRIPT + false + + + Ajax4jsf Filter + ajax4jsf + org.ajax4jsf.Filter + + + ajax4jsf + Faces Servlet + FORWARD + REQUEST + INCLUDE + + + Faces Servlet + javax.faces.webapp.FacesServlet + 1 + + + Faces Servlet + *.jsf + + + BASIC + + diff --git a/samples/focus-sample/src/main/webapp/focus_1.xhtml b/samples/focus-sample/src/main/webapp/focus_1.xhtml new file mode 100644 index 0000000..5b03987 --- /dev/null +++ b/samples/focus-sample/src/main/webapp/focus_1.xhtml @@ -0,0 +1,42 @@ + + + + Focus sample + + + + +

Focus should be placed on email: +

    +
  • username gets default priority 0
  • +
  • email gets priority -1
  • +
+

+ + + + + + + + + + + + + + + + + + + + + diff --git a/samples/focus-sample/src/main/webapp/focus_2.xhtml b/samples/focus-sample/src/main/webapp/focus_2.xhtml new file mode 100644 index 0000000..526d9aa --- /dev/null +++ b/samples/focus-sample/src/main/webapp/focus_2.xhtml @@ -0,0 +1,46 @@ + + + + Focus sample + + + + +

Focus should be placed on first radio : +

    +
  • username gets priority 9999
  • +
  • email gets default priority 1
  • +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/focus-sample/src/main/webapp/focus_3.xhtml b/samples/focus-sample/src/main/webapp/focus_3.xhtml new file mode 100644 index 0000000..acc8b53 --- /dev/null +++ b/samples/focus-sample/src/main/webapp/focus_3.xhtml @@ -0,0 +1,40 @@ + + + + Focus sample + + + + +

Focus should be placed on username : +

    +
  • username gets default priority 0
  • +
  • email gets priority 1
  • +
+

+ + + + + + + + + + + + + + + + + + + + diff --git a/samples/focus-sample/src/main/webapp/focus_4.xhtml b/samples/focus-sample/src/main/webapp/focus_4.xhtml new file mode 100644 index 0000000..c0e2ff4 --- /dev/null +++ b/samples/focus-sample/src/main/webapp/focus_4.xhtml @@ -0,0 +1,24 @@ + + + + Focus sample + + + + +

This example shows that focus can be put on non JSF control

+ + + 1 + 2 + + + + + + + diff --git a/samples/focus-sample/src/main/webapp/focus_5.xhtml b/samples/focus-sample/src/main/webapp/focus_5.xhtml new file mode 100644 index 0000000..13ed874 --- /dev/null +++ b/samples/focus-sample/src/main/webapp/focus_5.xhtml @@ -0,0 +1,51 @@ + + + + Focus sample + + + + +

On initial request focus should be placed on username. This is because direct parent of + focus component is not UIInput. + In such situation focus is put on first UIInput in parent form. + Please not that focus component is not direct child of form. + This sample is to ensure you that UIInput is not searched + within direct parent of focus component, but within nesting form. +

+
    +
  • username gets default priority 0
  • +
  • email gets priority 1
  • +
+

On form submit, when username is valid and email is invalid, focus + should be placed on email. +

+ +

On form submit, when all fields are valid, focus + should be placed on username. +

+ + + + + + + + + + + + + + + + + + + diff --git a/samples/focus-sample/src/main/webapp/focus_6.xhtml b/samples/focus-sample/src/main/webapp/focus_6.xhtml new file mode 100644 index 0000000..304e443 --- /dev/null +++ b/samples/focus-sample/src/main/webapp/focus_6.xhtml @@ -0,0 +1,53 @@ + + + + Focus sample + + + + +

+ Focus is placed on control withing modal panel. Modal panel is rendered before it is shown, thus + javascript that sets focus should not be invoked at that moment. + To cope with that focus component has "timing" attribute which, when valued to "onJScall" + allows setting focus later whenever user wishes. When "timing" is set to "onJScall" then "name" attribute + must be also specified. It is name of JavaScript function that will be generated and can be called by user + to set focus in any moment. + In this example we put onshow="focusTheForm();" in modal panel which causes + focus to be placed after the modal is visible. + Notice that "name" attribute of focuse component is equal to "focusTheForm". +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/focus-sample/src/main/webapp/focus_7.xhtml b/samples/focus-sample/src/main/webapp/focus_7.xhtml new file mode 100644 index 0000000..8424098 --- /dev/null +++ b/samples/focus-sample/src/main/webapp/focus_7.xhtml @@ -0,0 +1,50 @@ + + + + Focus sample + + + + +

+ Some components like h:selectOneRadio render elements that have identifiers matching component's clientId+suffix. + This might be a pain if we want to have single "focus" component inside form, but be able to focus any type of the invalid + components. + That's what focusModifier component is for. + Some components cause even grater pain cause you cannot nest anything inside (rich:calendar). + To cope with this put focusModifier inside "focusModifier" facet.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/focus-sample/src/main/webapp/index.jsp b/samples/focus-sample/src/main/webapp/index.jsp new file mode 100644 index 0000000..005171f --- /dev/null +++ b/samples/focus-sample/src/main/webapp/index.jsp @@ -0,0 +1,7 @@ + + + + + + + diff --git a/samples/focus-sample/src/main/webapp/menu.xhtml b/samples/focus-sample/src/main/webapp/menu.xhtml new file mode 100644 index 0000000..07627d7 --- /dev/null +++ b/samples/focus-sample/src/main/webapp/menu.xhtml @@ -0,0 +1,14 @@ + +
+
    +
  • Example 1
  • +
  • Example 2
  • +
  • Example 3
  • +
  • Example 4
  • +
  • Example 5
  • +
  • Example 6
  • +
  • Example 7
  • +
+
+
diff --git a/samples/gmapMarker-sample/pom.xml b/samples/gmapMarker-sample/pom.xml new file mode 100644 index 0000000..b190df7 --- /dev/null +++ b/samples/gmapMarker-sample/pom.xml @@ -0,0 +1,38 @@ + + + org.richfaces.sandbox.bernard + parent + 3.3.4-SNAPSHOT + ../pom.xml + + 4.0.0 + org.richfaces + gmapMarker-sample + war + gmapMarker-sample Maven Webapp + + ${project.artifactId} + + + + org.richfaces.ui + core + ${project.version} + + + org.richfaces.ui + richfaces-ui + + + com.uwyn + jhighlight + 1.0 + + + org.richfaces.sandbox.ui + gmapMarker + ${project.version} + + + diff --git a/samples/gmapMarker-sample/src/main/webapp/META-INF/MANIFEST.MF b/samples/gmapMarker-sample/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 0000000..254272e --- /dev/null +++ b/samples/gmapMarker-sample/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/samples/gmapMarker-sample/src/main/webapp/WEB-INF/faces-config.xml b/samples/gmapMarker-sample/src/main/webapp/WEB-INF/faces-config.xml new file mode 100644 index 0000000..2ac4867 --- /dev/null +++ b/samples/gmapMarker-sample/src/main/webapp/WEB-INF/faces-config.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/samples/gmapMarker-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/samples/gmapMarker-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml new file mode 100644 index 0000000..5f07358 --- /dev/null +++ b/samples/gmapMarker-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/samples/gmapMarker-sample/src/main/webapp/WEB-INF/web.xml b/samples/gmapMarker-sample/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..d33a2b4 --- /dev/null +++ b/samples/gmapMarker-sample/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,68 @@ + + + Richfaces gmapMarker Sample + gmapMarker-sample + + javax.faces.DEFAULT_SUFFIX + .xhtml + + + facelets.REFRESH_PERIOD + 2 + + + facelets.DEVELOPMENT + true + + + javax.faces.STATE_SAVING_METHOD + client + + + com.sun.faces.validateXml + true + + + com.sun.faces.verifyObjects + true + + + org.ajax4jsf.VIEW_HANDLERS + com.sun.facelets.FaceletViewHandler + + + org.ajax4jsf.COMPRESS_SCRIPT + false + + + Ajax4jsf Filter + ajax4jsf + org.ajax4jsf.Filter + + + ajax4jsf + Faces Servlet + FORWARD + REQUEST + INCLUDE + + + Faces Servlet + javax.faces.webapp.FacesServlet + 1 + + + Faces Servlet + *.jsf + + + BASIC + + + + com.sun.faces.config.ConfigureListener + + + diff --git a/samples/gmapMarker-sample/src/main/webapp/index.jsp b/samples/gmapMarker-sample/src/main/webapp/index.jsp new file mode 100644 index 0000000..d72c769 --- /dev/null +++ b/samples/gmapMarker-sample/src/main/webapp/index.jsp @@ -0,0 +1,7 @@ + + + + + + + diff --git a/samples/gmapMarker-sample/src/main/webapp/menu.xhtml b/samples/gmapMarker-sample/src/main/webapp/menu.xhtml new file mode 100644 index 0000000..63ffca6 --- /dev/null +++ b/samples/gmapMarker-sample/src/main/webapp/menu.xhtml @@ -0,0 +1,10 @@ + +
+
    + +
+
+
diff --git a/samples/gmapMarker-sample/src/main/webapp/sample_1.xhtml b/samples/gmapMarker-sample/src/main/webapp/sample_1.xhtml new file mode 100644 index 0000000..ae0a727 --- /dev/null +++ b/samples/gmapMarker-sample/src/main/webapp/sample_1.xhtml @@ -0,0 +1,69 @@ + + + + gmapMarker sample + + + + + + + +
+ Add marker with JavaScript +
+ Latitude + Longitude + Title + Draggable + +
+ +
+ + + + + +
+ +
+
+
+
+ + + + + + + + diff --git a/samples/imageSelectTool-sample/pom.xml b/samples/imageSelectTool-sample/pom.xml new file mode 100644 index 0000000..c317125 --- /dev/null +++ b/samples/imageSelectTool-sample/pom.xml @@ -0,0 +1,34 @@ + + + + org.richfaces.sandbox.bernard + parent + 3.3.4-SNAPSHOT + ../pom.xml + + 4.0.0 + org.richfaces.sandbox.ui + imageSelectTool-sample + war + imageSelectTool-sample Maven Webapp + + ${project.artifactId} + + + + org.richfaces.ui + richfaces-ui + + + org.richfaces.sandbox.ui + imageSelectTool + ${project.version} + + + com.uwyn + jhighlight + 1.0 + + + \ No newline at end of file diff --git a/samples/imageSelectTool-sample/src/main/java/org/richfaces/sandbox/imageSelectTool/Cropper.java b/samples/imageSelectTool-sample/src/main/java/org/richfaces/sandbox/imageSelectTool/Cropper.java new file mode 100644 index 0000000..f16461f --- /dev/null +++ b/samples/imageSelectTool-sample/src/main/java/org/richfaces/sandbox/imageSelectTool/Cropper.java @@ -0,0 +1,88 @@ +package org.richfaces.sandbox.imageSelectTool; + +import java.awt.*; + +public class Cropper { + + private String srcImage = "images/sample.jpg"; + private Double aspectRatio; + private String backgroundColor; + private Double backgroundOpacity; + private Integer maxHeight; + private Integer maxWidth; + private Integer minHeight; + private Integer minWidth; + private Rectangle selection; + + public String getSrcImage() { + return srcImage; + } + + public void setSrcImage(String srcImage) { + this.srcImage = srcImage; + } + + public Double getAspectRatio() { + return aspectRatio; + } + + public void setAspectRatio(Double aspectRatio) { + this.aspectRatio = aspectRatio; + } + + public String getBackgroundColor() { + return backgroundColor; + } + + public void setBackgroundColor(String backgroundColor) { + this.backgroundColor = backgroundColor; + } + + public Double getBackgroundOpacity() { + return backgroundOpacity; + } + + public void setBackgroundOpacity(Double backgroundOpacity) { + this.backgroundOpacity = backgroundOpacity; + } + + public Integer getMaxHeight() { + return maxHeight; + } + + public void setMaxHeight(Integer maxHeight) { + this.maxHeight = maxHeight; + } + + public Integer getMaxWidth() { + return maxWidth; + } + + public void setMaxWidth(Integer maxWidth) { + this.maxWidth = maxWidth; + } + + public Integer getMinHeight() { + return minHeight; + } + + public void setMinHeight(Integer minHeight) { + this.minHeight = minHeight; + } + + public Integer getMinWidth() { + return minWidth; + } + + public void setMinWidth(Integer minWidth) { + this.minWidth = minWidth; + } + + public Rectangle getSelection() { + return selection; + } + + public void setSelection(Rectangle selection) { + this.selection = selection; + } +} diff --git a/samples/imageSelectTool-sample/src/main/webapp/META-INF/MANIFEST.MF b/samples/imageSelectTool-sample/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 0000000..254272e --- /dev/null +++ b/samples/imageSelectTool-sample/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/samples/imageSelectTool-sample/src/main/webapp/META-INF/context.xml b/samples/imageSelectTool-sample/src/main/webapp/META-INF/context.xml new file mode 100644 index 0000000..88bd08f --- /dev/null +++ b/samples/imageSelectTool-sample/src/main/webapp/META-INF/context.xml @@ -0,0 +1,2 @@ + + diff --git a/samples/imageSelectTool-sample/src/main/webapp/WEB-INF/faces-config.xml b/samples/imageSelectTool-sample/src/main/webapp/WEB-INF/faces-config.xml new file mode 100644 index 0000000..e9ddef9 --- /dev/null +++ b/samples/imageSelectTool-sample/src/main/webapp/WEB-INF/faces-config.xml @@ -0,0 +1,11 @@ + + + + + cropper + org.richfaces.sandbox.imageSelectTool.Cropper + session + + + diff --git a/samples/imageSelectTool-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/samples/imageSelectTool-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml new file mode 100644 index 0000000..5f07358 --- /dev/null +++ b/samples/imageSelectTool-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/samples/imageSelectTool-sample/src/main/webapp/WEB-INF/web.xml b/samples/imageSelectTool-sample/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..d6ef69a --- /dev/null +++ b/samples/imageSelectTool-sample/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,74 @@ + + + Richfaces imageSelectTool Sample + imageSelectTool-sample + + javax.faces.DEFAULT_SUFFIX + .xhtml + + + facelets.REFRESH_PERIOD + 2 + + + facelets.DEVELOPMENT + true + + + javax.faces.STATE_SAVING_METHOD + server + + + com.sun.faces.validateXml + true + + + com.sun.faces.verifyObjects + false + + + org.ajax4jsf.VIEW_HANDLERS + com.sun.facelets.FaceletViewHandler + + + org.ajax4jsf.COMPRESS_SCRIPT + false + + + org.richfaces.SKIN + blueSky + + + org.richfaces.CONTROL_SKINNING_LEVEL + extended + + + Ajax4jsf Filter + ajax4jsf + org.ajax4jsf.Filter + + enable-cache + false + + + + ajax4jsf + Faces Servlet + FORWARD + REQUEST + INCLUDE + + + Faces Servlet + javax.faces.webapp.FacesServlet + 1 + + + Faces Servlet + *.jsf + + + BASIC + + diff --git a/samples/imageSelectTool-sample/src/main/webapp/images/sample.jpg b/samples/imageSelectTool-sample/src/main/webapp/images/sample.jpg new file mode 100644 index 0000000..0a825f2 Binary files /dev/null and b/samples/imageSelectTool-sample/src/main/webapp/images/sample.jpg differ diff --git a/samples/imageSelectTool-sample/src/main/webapp/index.jsp b/samples/imageSelectTool-sample/src/main/webapp/index.jsp new file mode 100644 index 0000000..1b45339 --- /dev/null +++ b/samples/imageSelectTool-sample/src/main/webapp/index.jsp @@ -0,0 +1,7 @@ + + + + + + + diff --git a/samples/imageSelectTool-sample/src/main/webapp/index.xhtml b/samples/imageSelectTool-sample/src/main/webapp/index.xhtml new file mode 100644 index 0000000..3ed52af --- /dev/null +++ b/samples/imageSelectTool-sample/src/main/webapp/index.xhtml @@ -0,0 +1,117 @@ + + + + ImageSelectTool sample + + + + + +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + #{cropper.selection} + + + + + + + + + + + + + + diff --git a/samples/imageSelectTool-sample/src/main/webapp/menu.xhtml b/samples/imageSelectTool-sample/src/main/webapp/menu.xhtml new file mode 100644 index 0000000..d2607b5 --- /dev/null +++ b/samples/imageSelectTool-sample/src/main/webapp/menu.xhtml @@ -0,0 +1,8 @@ + + + Example 1 + + diff --git a/samples/jQueryPlugin-sample/pom.xml b/samples/jQueryPlugin-sample/pom.xml new file mode 100644 index 0000000..d62057a --- /dev/null +++ b/samples/jQueryPlugin-sample/pom.xml @@ -0,0 +1,38 @@ + + + org.richfaces.sandbox.bernard + parent + 3.3.4-SNAPSHOT + ../pom.xml + + 4.0.0 + org.richfaces + jQueryPlugin-sample + war + jQueryPlugin-sample Maven Webapp + + ${project.artifactId} + + + + org.richfaces.ui + core + ${project.version} + + + org.richfaces.ui + richfaces-ui + + + com.uwyn + jhighlight + 1.0 + + + org.richfaces.sandbox.ui + jQueryPlugin + ${project.version} + + + diff --git a/samples/jQueryPlugin-sample/src/main/resources/META-INF/resources-config.xml b/samples/jQueryPlugin-sample/src/main/resources/META-INF/resources-config.xml new file mode 100644 index 0000000..6b5c303 --- /dev/null +++ b/samples/jQueryPlugin-sample/src/main/resources/META-INF/resources-config.xml @@ -0,0 +1,7 @@ + + + + css/jquery.pnotify.xcss + css/jquery.pnotify.xcss + + \ No newline at end of file diff --git a/samples/jQueryPlugin-sample/src/main/resources/jquery.pnotify.xcss b/samples/jQueryPlugin-sample/src/main/resources/jquery.pnotify.xcss new file mode 100644 index 0000000..e40be17 --- /dev/null +++ b/samples/jQueryPlugin-sample/src/main/resources/jquery.pnotify.xcss @@ -0,0 +1,179 @@ + + + + +/* Notice----------------------------------*/ +.rf-ny { + top: 18px; + right: 18px; + position: absolute; + height: auto; + /* Ensure that the notices are on top of everything else. */ + z-index: 9999; +} + + + + + +.rf-ny-warn { + color:orange; +} +.rf-ny-error { + color:red; +} +.rf-ny-fatal { + color:red; + font-weight:bold; +} +/* This hides position: fixed from IE6, which doesn't understand it. */ +html > body .rf-ny { + position: fixed; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +.rf-ny-co-hover { + background:red; +} +.rf-ny-cl { + float: right; + margin-left: .2em; +} + + + + + + + + + + +.rf-ny-ti { + display: block; + font-size: 1.2em; + font-weight: bold; + margin-bottom: .4em; +} +.rf-ny-te { + display: block; +} +.rf-ny-ic { + display: none; + float: left; + margin-right: .2em; + width:32px; + height:32px; +} +/* History Pulldown----------------------------------*/ + + + + + + + + + + + + + + + + + + + + + + + + + +.rf-ny-hc .rf-ny-hh { + padding: 2px; +} +.rf-ny-hc button { + cursor: pointer; + display: block; + width: 100%; +} +.rc-ny-ha { + -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/error.png b/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/error.png new file mode 100644 index 0000000..9d9030d Binary files /dev/null and b/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/error.png differ diff --git a/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/fatal.png b/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/fatal.png new file mode 100644 index 0000000..c050f1b Binary files /dev/null and b/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/fatal.png differ diff --git a/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/info.png b/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/info.png new file mode 100644 index 0000000..25022a7 Binary files /dev/null and b/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/info.png differ diff --git a/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/triangleIconDown.png b/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/triangleIconDown.png new file mode 100644 index 0000000..0b94b2b Binary files /dev/null and b/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/triangleIconDown.png differ diff --git a/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/warn.png b/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/warn.png new file mode 100644 index 0000000..8aa86fc Binary files /dev/null and b/samples/jQueryPlugin-sample/src/main/resources/org/richfaces/renderkit/html/images/warn.png differ diff --git a/samples/jQueryPlugin-sample/src/main/webapp/META-INF/MANIFEST.MF b/samples/jQueryPlugin-sample/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 0000000..254272e --- /dev/null +++ b/samples/jQueryPlugin-sample/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/samples/jQueryPlugin-sample/src/main/webapp/WEB-INF/faces-config.xml b/samples/jQueryPlugin-sample/src/main/webapp/WEB-INF/faces-config.xml new file mode 100644 index 0000000..2ac4867 --- /dev/null +++ b/samples/jQueryPlugin-sample/src/main/webapp/WEB-INF/faces-config.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/samples/jQueryPlugin-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/samples/jQueryPlugin-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml new file mode 100644 index 0000000..5f07358 --- /dev/null +++ b/samples/jQueryPlugin-sample/src/main/webapp/WEB-INF/jboss-deployment-structure.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/samples/jQueryPlugin-sample/src/main/webapp/WEB-INF/web.xml b/samples/jQueryPlugin-sample/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..a65a54c --- /dev/null +++ b/samples/jQueryPlugin-sample/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,68 @@ + + + Richfaces jQueryPlugin Sample + jQueryPlugin-sample + + javax.faces.DEFAULT_SUFFIX + .xhtml + + + facelets.REFRESH_PERIOD + 2 + + + facelets.DEVELOPMENT + true + + + javax.faces.STATE_SAVING_METHOD + client + + + com.sun.faces.validateXml + true + + + com.sun.faces.verifyObjects + true + + + org.ajax4jsf.VIEW_HANDLERS + com.sun.facelets.FaceletViewHandler + + + org.ajax4jsf.COMPRESS_SCRIPT + false + + + Ajax4jsf Filter + ajax4jsf + org.ajax4jsf.Filter + + + ajax4jsf + Faces Servlet + FORWARD + REQUEST + INCLUDE + + + Faces Servlet + javax.faces.webapp.FacesServlet + 1 + + + Faces Servlet + *.jsf + + + BASIC + + + + com.sun.faces.config.ConfigureListener + + + diff --git a/samples/jQueryPlugin-sample/src/main/webapp/fullcalendar.js b/samples/jQueryPlugin-sample/src/main/webapp/fullcalendar.js new file mode 100644 index 0000000..34a98e5 --- /dev/null +++ b/samples/jQueryPlugin-sample/src/main/webapp/fullcalendar.js @@ -0,0 +1,3955 @@ +/** + * @preserve + * FullCalendar v1.4.6 + * http://arshaw.com/fullcalendar/ + * + * Use fullcalendar.css for basic styling. + * For event drag & drop, required jQuery UI draggable. + * For event resizing, requires jQuery UI resizable. + * + * Copyright (c) 2009 Adam Shaw + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Date: Mon May 31 10:18:29 2010 -0700 + * + */ + +(function($) { + + + var fc = $.fullCalendar = {}; + var views = fc.views = {}; + + + /* Defaults + -----------------------------------------------------------------------------*/ + + var defaults = { + + // display + defaultView: 'month', + aspectRatio: 1.35, + header: { + left: 'title', + center: '', + right: 'today prev,next' + }, + weekends: true, + + // editing + //editable: false, + //disableDragging: false, + //disableResizing: false, + + allDayDefault: true, + + // event ajax + lazyFetching: true, + startParam: 'start', + endParam: 'end', + + // time formats + titleFormat: { + month: 'MMMM yyyy', + week: "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", + day: 'dddd, MMM d, yyyy' + }, + columnFormat: { + month: 'ddd', + week: 'ddd M/d', + day: 'dddd M/d' + }, + timeFormat: { // for event elements + '': 'h(:mm)t' // default + }, + + // locale + isRTL: false, + firstDay: 0, + monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'], + dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], + dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], + buttonText: { + prev: ' ◄ ', + next: ' ► ', + prevYear: ' << ', + nextYear: ' >> ', + today: 'today', + month: 'month', + week: 'week', + day: 'day' + }, + + // jquery-ui theming + theme: false, + buttonIcons: { + prev: 'circle-triangle-w', + next: 'circle-triangle-e' + }, + + //selectable: false, + unselectAuto: true + + }; + + // right-to-left defaults + var rtlDefaults = { + header: { + left: 'next,prev today', + center: '', + right: 'title' + }, + buttonText: { + prev: ' ► ', + next: ' ◄ ', + prevYear: ' >> ', + nextYear: ' << ' + }, + buttonIcons: { + prev: 'circle-triangle-e', + next: 'circle-triangle-w' + } + }; + + // function for adding/overriding defaults + var setDefaults = fc.setDefaults = function(d) { + $.extend(true, defaults, d); + }; + + + /* .fullCalendar jQuery function + -----------------------------------------------------------------------------*/ + + $.fn.fullCalendar = function(options) { + + // method calling + if (typeof options == 'string') { + var args = Array.prototype.slice.call(arguments, 1), + res; + this.each(function() { + var data = $.data(this, 'fullCalendar'); + if (data) { + var meth = data[options]; + if (meth) { + var r = meth.apply(this, args); + if (res === undefined) { + res = r; + } + } + } + }); + if (res !== undefined) { + return res; + } + return this; + } + + // pluck the 'events' and 'eventSources' options + var eventSources = options.eventSources || []; + delete options.eventSources; + if (options.events) { + eventSources.push(options.events); + delete options.events; + } + + // first event source reserved for 'sticky' events + eventSources.unshift([]); + + // initialize options + options = $.extend(true, {}, + defaults, + (options.isRTL || options.isRTL === undefined && defaults.isRTL) ? rtlDefaults : {}, + options + ); + var tm = options.theme ? 'ui' : 'fc'; // for making theme classes + + + this.each(function() { + + + /* Instance Initialization + -----------------------------------------------------------------------------*/ + + // element + var _element = this, + element = $(_element).addClass('fc'), + elementOuterWidth, + content = $("
").prependTo(_element), + suggestedViewHeight, + resizeUID = 0, + ignoreWindowResize = 0, + date = new Date(), + viewName, // the current view name (TODO: look into getting rid of) + view, // the current view + viewInstances = {}, + absoluteViewElement; + + + if (options.isRTL) { + element.addClass('fc-rtl'); + } + if (options.theme) { + element.addClass('ui-widget'); + } + + if (options.year !== undefined && options.year != date.getFullYear()) { + date.setDate(1); + date.setMonth(0); + date.setFullYear(options.year); + } + if (options.month !== undefined && options.month != date.getMonth()) { + date.setDate(1); + date.setMonth(options.month); + } + if (options.date !== undefined) { + date.setDate(options.date); + } + + + /* View Rendering + -----------------------------------------------------------------------------*/ + + function changeView(v) { + if (v != viewName) { + ignoreWindowResize++; // because setMinHeight might change the height before render (and subsequently setSize) is reached + + viewUnselect(); + + var oldView = view, + newViewElement; + + if (oldView) { + if (oldView.eventsChanged) { + eventsDirty(); + oldView.eventDirty = oldView.eventsChanged = false; + } + if (oldView.beforeHide) { + oldView.beforeHide(); // called before changing min-height. if called after, scroll state is reset (in Opera) + } + setMinHeight(content, content.height()); + oldView.element.hide(); + } else { + setMinHeight(content, 1); // needs to be 1 (not 0) for IE7, or else view dimensions miscalculated + } + content.css('overflow', 'hidden'); + + if (viewInstances[v]) { + (view = viewInstances[v]).element.show(); + } else { + view = viewInstances[v] = fc.views[v]( + newViewElement = absoluteViewElement = + $("
") + .appendTo(content), + options + ); + } + + if (header) { + // update 'active' view button + header.find('div.fc-button-' + viewName).removeClass(tm + '-state-active'); + header.find('div.fc-button-' + v).addClass(tm + '-state-active'); + } + + view.name = viewName = v; + render(); // after height has been set, will make absoluteViewElement's position=relative, then set to null + content.css('overflow', ''); + if (oldView) { + setMinHeight(content, 1); + } + if (!newViewElement && view.afterShow) { + view.afterShow(); // called after setting min-height/overflow, so in final scroll state (for Opera) + } + + ignoreWindowResize--; + } + } + + + function render(inc) { + if (elementVisible()) { + ignoreWindowResize++; // because view.renderEvents might temporarily change the height before setSize is reached + + viewUnselect(); + + if (suggestedViewHeight === undefined) { + calcSize(); + } + + if (!view.start || inc || date < view.start || date >= view.end) { + view.render(date, inc || 0); // responsible for clearing events + setSize(true); + if (!eventStart || !options.lazyFetching || view.visStart < eventStart || view.visEnd > eventEnd) { + fetchAndRenderEvents(); + } else { + view.renderEvents(events); // don't refetch + } + } + else if (view.sizeDirty || view.eventsDirty || !options.lazyFetching) { + view.clearEvents(); + if (view.sizeDirty) { + setSize(); + } + if (options.lazyFetching) { + view.renderEvents(events); // don't refetch + } else { + fetchAndRenderEvents(); + } + } + elementOuterWidth = element.outerWidth(); + view.sizeDirty = false; + view.eventsDirty = false; + + if (header) { + // update title text + header.find('h2.fc-header-title').html(view.title); + // enable/disable 'today' button + var today = new Date(); + if (today >= view.start && today < view.end) { + header.find('div.fc-button-today').addClass(tm + '-state-disabled'); + } else { + header.find('div.fc-button-today').removeClass(tm + '-state-disabled'); + } + } + + ignoreWindowResize--; + view.trigger('viewDisplay', _element); + } + } + + + function elementVisible() { + return _element.offsetWidth !== 0; + } + + function bodyVisible() { + return $('body')[0].offsetWidth !== 0; + } + + function viewUnselect() { + if (view) { + view.unselect(); + } + } + + + // called when any event objects have been added/removed/changed, rerenders + function eventsChanged() { + eventsDirty(); + if (elementVisible()) { + view.clearEvents(); + view.renderEvents(events); + view.eventsDirty = false; + } + } + + // marks other views' events as dirty + function eventsDirty() { + $.each(viewInstances, function() { + this.eventsDirty = true; + }); + } + + // called when we know the element size has changed + function sizeChanged() { + sizesDirty(); + if (elementVisible()) { + calcSize(); + setSize(); + viewUnselect(); + view.rerenderEvents(); + view.sizeDirty = false; + } + } + + // marks other views' sizes as dirty + function sizesDirty() { + $.each(viewInstances, function() { + this.sizeDirty = true; + }); + } + + + /* Event Sources and Fetching + -----------------------------------------------------------------------------*/ + + var events = [], + eventStart, eventEnd; + + // Fetch from ALL sources. Clear 'events' array and populate + function fetchEvents(callback) { + events = []; + eventStart = cloneDate(view.visStart); + eventEnd = cloneDate(view.visEnd); + var queued = eventSources.length, + sourceDone = function() { + if (!--queued) { + if (callback) { + callback(events); + } + } + }, i = 0; + for (; i < eventSources.length; i++) { + fetchEventSource(eventSources[i], sourceDone); + } + } + + // Fetch from a particular source. Append to the 'events' array + function fetchEventSource(src, callback) { + var prevViewName = view.name, + prevDate = cloneDate(date), + reportEvents = function(a) { + if (prevViewName == view.name && +prevDate == +date && // protects from fast switching + $.inArray(src, eventSources) != -1) { // makes sure source hasn't been removed + for (var i = 0; i < a.length; i++) { + normalizeEvent(a[i], options); + a[i].source = src; + } + events = events.concat(a); + if (callback) { + callback(a); + } + } + }, + reportEventsAndPop = function(a) { + reportEvents(a); + popLoading(); + }; + if (typeof src == 'string') { + var params = {}; + params[options.startParam] = Math.round(eventStart.getTime() / 1000); + params[options.endParam] = Math.round(eventEnd.getTime() / 1000); + if (options.cacheParam) { + params[options.cacheParam] = (new Date()).getTime(); // TODO: deprecate cacheParam + } + pushLoading(); + $.ajax({ + url: src, + dataType: 'json', + data: params, + cache: options.cacheParam || false, // don't let jquery prevent caching if cacheParam is being used + success: reportEventsAndPop + }); + } + else if ($.isFunction(src)) { + pushLoading(); + src(cloneDate(eventStart), cloneDate(eventEnd), reportEventsAndPop); + } + else { + reportEvents(src); // src is an array + } + } + + + // for convenience + function fetchAndRenderEvents() { + fetchEvents(function(events) { + view.renderEvents(events); // maintain `this` in view + }); + } + + + /* Loading State + -----------------------------------------------------------------------------*/ + + var loadingLevel = 0; + + function pushLoading() { + if (!loadingLevel++) { + view.trigger('loading', _element, true); + } + } + + function popLoading() { + if (!--loadingLevel) { + view.trigger('loading', _element, false); + } + } + + + /* Public Methods + -----------------------------------------------------------------------------*/ + + var publicMethods = { + + render: function() { + calcSize(); + sizesDirty(); + eventsDirty(); + render(); + }, + + changeView: changeView, + + getView: function() { + return view; + }, + + getDate: function() { + return date; + }, + + option: function(name, value) { + if (value === undefined) { + return options[name]; + } + if (name == 'height' || name == 'contentHeight' || name == 'aspectRatio') { + options[name] = value; + sizeChanged(); + } + }, + + destroy: function() { + $(window).unbind('resize', windowResize); + if (header) { + header.remove(); + } + content.remove(); + $.removeData(_element, 'fullCalendar'); + }, + + // + // Navigation + // + + prev: function() { + render(-1); + }, + + next: function() { + render(1); + }, + + prevYear: function() { + addYears(date, -1); + render(); + }, + + nextYear: function() { + addYears(date, 1); + render(); + }, + + today: function() { + date = new Date(); + render(); + }, + + gotoDate: function(year, month, dateNum) { + if (typeof year == 'object') { + date = cloneDate(year); // provided 1 argument, a Date + } else { + if (year !== undefined) { + date.setFullYear(year); + } + if (month !== undefined) { + date.setMonth(month); + } + if (dateNum !== undefined) { + date.setDate(dateNum); + } + } + render(); + }, + + incrementDate: function(years, months, days) { + if (years !== undefined) { + addYears(date, years); + } + if (months !== undefined) { + addMonths(date, months); + } + if (days !== undefined) { + addDays(date, days); + } + render(); + }, + + // + // Event Manipulation + // + + updateEvent: function(event) { // update an existing event + var i, len = events.length, e, + startDelta = event.start - event._start, + endDelta = event.end ? + (event.end - (event._end || view.defaultEventEnd(event))) // event._end would be null if event.end + : 0; // was null and event was just resized + for (i = 0; i < len; i++) { + e = events[i]; + if (e._id == event._id && e != event) { + e.start = new Date(+e.start + startDelta); + if (event.end) { + if (e.end) { + e.end = new Date(+e.end + endDelta); + } else { + e.end = new Date(+view.defaultEventEnd(e) + endDelta); + } + } else { + e.end = null; + } + e.title = event.title; + e.url = event.url; + e.allDay = event.allDay; + e.className = event.className; + e.editable = event.editable; + normalizeEvent(e, options); + } + } + normalizeEvent(event, options); + eventsChanged(); + }, + + renderEvent: function(event, stick) { // render a new event + normalizeEvent(event, options); + if (!event.source) { + if (stick) { + (event.source = eventSources[0]).push(event); + } + events.push(event); + } + eventsChanged(); + }, + + removeEvents: function(filter) { + if (!filter) { // remove all + events = []; + // clear all array sources + for (var i = 0; i < eventSources.length; i++) { + if (typeof eventSources[i] == 'object') { + eventSources[i] = []; + } + } + } else { + if (!$.isFunction(filter)) { // an event ID + var id = filter + ''; + filter = function(e) { + return e._id == id; + }; + } + events = $.grep(events, filter, true); + // remove events from array sources + for (var i = 0; i < eventSources.length; i++) { + if (typeof eventSources[i] == 'object') { + eventSources[i] = $.grep(eventSources[i], filter, true); + } + } + } + eventsChanged(); + }, + + clientEvents: function(filter) { + if ($.isFunction(filter)) { + return $.grep(events, filter); + } + else if (filter) { // an event ID + filter += ''; + return $.grep(events, function(e) { + return e._id == filter; + }); + } + return events; // else, return all + }, + + rerenderEvents: eventsChanged, // TODO: think of renaming eventsChanged + + // + // Event Source + // + + addEventSource: function(source) { + eventSources.push(source); + fetchEventSource(source, eventsChanged); + }, + + removeEventSource: function(source) { + eventSources = $.grep(eventSources, function(src) { + return src != source; + }); + // remove all client events from that source + events = $.grep(events, function(e) { + return e.source != source; + }); + eventsChanged(); + }, + + refetchEvents: function() { + fetchEvents(eventsChanged); + }, + + // + // selection + // + + select: function(start, end, allDay) { + view.select(start, end, allDay === undefined ? true : allDay); + }, + + unselect: function() { + view.unselect(); + } + + }; + + $.data(this, 'fullCalendar', publicMethods); // TODO: look into memory leak implications + + + /* Header + -----------------------------------------------------------------------------*/ + + var header, + sections = options.header; + if (sections) { + header = $("") + .append($("") + .append($(""); + $.each(buttonStr.split(' '), function(i) { + if (i > 0) { + tr.append(""); + } + var prevButton; + $.each(this.split(','), function(j, buttonName) { + if (buttonName == 'title') { + tr.append(""); + if (prevButton) { + prevButton.addClass(tm + '-corner-right'); + } + prevButton = null; + } else { + var buttonClick; + if (publicMethods[buttonName]) { + buttonClick = publicMethods[buttonName]; + } + else if (views[buttonName]) { + buttonClick = function() { + button.removeClass(tm + '-state-hover'); + changeView(buttonName); + }; + } + if (buttonClick) { + if (prevButton) { + prevButton.addClass(tm + '-no-right'); + } + var button, + icon = options.theme ? smartProperty(options.buttonIcons, buttonName) : null, + text = smartProperty(options.buttonText, buttonName); + if (icon) { + button = $("
" + + "
"); + } + else if (text) { + button = $(""); + } + if (button) { + button + .click(function() { + if (!button.hasClass(tm + '-state-disabled')) { + buttonClick(); + } + }) + .mousedown(function() { + button + .not('.' + tm + '-state-active') + .not('.' + tm + '-state-disabled') + .addClass(tm + '-state-down'); + }) + .mouseup(function() { + button.removeClass(tm + '-state-down'); + }) + .hover( + function() { + button + .not('.' + tm + '-state-active') + .not('.' + tm + '-state-disabled') + .addClass(tm + '-state-hover'); + }, + function() { + button + .removeClass(tm + '-state-hover') + .removeClass(tm + '-state-down'); + } + ) + .appendTo($("
").append(buildSection(sections.left))) + .append($("").append(buildSection(sections.center))) + .append($("").append(buildSection(sections.right)))) + .prependTo(element); + } + function buildSection(buttonStr) { + if (buttonStr) { + var tr = $("

 

").appendTo(tr)); + if (prevButton) { + prevButton.addClass(tm + '-no-right'); + } else { + button.addClass(tm + '-corner-left'); + } + prevButton = button; + } + } + } + }); + if (prevButton) { + prevButton.addClass(tm + '-corner-right'); + } + }); + return $("").append(tr); + } + } + + + /* Resizing + -----------------------------------------------------------------------------*/ + + + function calcSize() { + if (options.contentHeight) { + suggestedViewHeight = options.contentHeight; + } + else if (options.height) { + suggestedViewHeight = options.height - (header ? header.height() : 0) - vsides(content[0]); + } + else { + suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5)); + } + } + + + function setSize(dateChanged) { + ignoreWindowResize++; + view.setHeight(suggestedViewHeight, dateChanged); + if (absoluteViewElement) { + absoluteViewElement.css('position', 'relative'); + absoluteViewElement = null; + } + view.setWidth(content.width(), dateChanged); + ignoreWindowResize--; + } + + + function windowResize() { + if (!ignoreWindowResize) { + if (view.start) { // view has already been rendered + var uid = ++resizeUID; + setTimeout(function() { // add a delay + if (uid == resizeUID && !ignoreWindowResize && elementVisible()) { + if (elementOuterWidth != (elementOuterWidth = element.outerWidth())) { + ignoreWindowResize++; // in case the windowResize callback changes the height + sizeChanged(); + view.trigger('windowResize', _element); + ignoreWindowResize--; + } + } + }, 200); + } else { + // calendar must have been initialized in a 0x0 iframe that has just been resized + lateRender(); + } + } + } + + $(window).resize(windowResize); + + + // let's begin... + changeView(options.defaultView); + + + // needed for IE in a 0x0 iframe, b/c when it is resized, never triggers a windowResize + if (!bodyVisible()) { + lateRender(); + } + + + // called when we know the calendar couldn't be rendered when it was initialized, + // but we think it's ready now + function lateRender() { + setTimeout(function() { // IE7 needs this so dimensions are calculated correctly + if (!view.start && bodyVisible()) { // !view.start makes sure this never happens more than once + render(); + } + }, 0); + } + + + }); + + return this; + + }; + + + /* Important Event Utilities + -----------------------------------------------------------------------------*/ + + var fakeID = 0; + + function normalizeEvent(event, options) { + event._id = event._id || (event.id === undefined ? '_fc' + fakeID++ : event.id + ''); + if (event.date) { + if (!event.start) { + event.start = event.date; + } + delete event.date; + } + event._start = cloneDate(event.start = parseDate(event.start)); + event.end = parseDate(event.end); + if (event.end && event.end <= event.start) { + event.end = null; + } + event._end = event.end ? cloneDate(event.end) : null; + if (event.allDay === undefined) { + event.allDay = options.allDayDefault; + } + if (event.className) { + if (typeof event.className == 'string') { + event.className = event.className.split(/\s+/); + } + } else { + event.className = []; + } + } + + // TODO: if there is no start date, return false to indicate an invalid event + + + /* Grid-based Views: month, basicWeek, basicDay + -----------------------------------------------------------------------------*/ + + setDefaults({ + weekMode: 'fixed' + }); + + views.month = function(element, options) { + return new Grid(element, options, { + render: function(date, delta) { + if (delta) { + addMonths(date, delta); + date.setDate(1); + } + // start/end + var start = this.start = cloneDate(date, true); + start.setDate(1); + this.end = addMonths(cloneDate(start), 1); + // visStart/visEnd + var visStart = this.visStart = cloneDate(start), + visEnd = this.visEnd = cloneDate(this.end), + nwe = options.weekends ? 0 : 1; + if (nwe) { + skipWeekend(visStart); + skipWeekend(visEnd, -1, true); + } + addDays(visStart, -((visStart.getDay() - Math.max(options.firstDay, nwe) + 7) % 7)); + addDays(visEnd, (7 - visEnd.getDay() + Math.max(options.firstDay, nwe)) % 7); + // row count + var rowCnt = Math.round((visEnd - visStart) / (DAY_MS * 7)); + if (options.weekMode == 'fixed') { + addDays(visEnd, (6 - rowCnt) * 7); + rowCnt = 6; + } + // title + this.title = formatDate( + start, + this.option('titleFormat'), + options + ); + // render + this.renderGrid( + rowCnt, options.weekends ? 7 : 5, + this.option('columnFormat'), + true + ); + } + }); + }; + + views.basicWeek = function(element, options) { + return new Grid(element, options, { + render: function(date, delta) { + if (delta) { + addDays(date, delta * 7); + } + var visStart = this.visStart = cloneDate( + this.start = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7)) + ), + visEnd = this.visEnd = cloneDate( + this.end = addDays(cloneDate(visStart), 7) + ); + if (!options.weekends) { + skipWeekend(visStart); + skipWeekend(visEnd, -1, true); + } + this.title = formatDates( + visStart, + addDays(cloneDate(visEnd), -1), + this.option('titleFormat'), + options + ); + this.renderGrid( + 1, options.weekends ? 7 : 5, + this.option('columnFormat'), + false + ); + } + }); + }; + + views.basicDay = function(element, options) { + return new Grid(element, options, { + render: function(date, delta) { + if (delta) { + addDays(date, delta); + if (!options.weekends) { + skipWeekend(date, delta < 0 ? -1 : 1); + } + } + this.title = formatDate(date, this.option('titleFormat'), options); + this.start = this.visStart = cloneDate(date, true); + this.end = this.visEnd = addDays(cloneDate(this.start), 1); + this.renderGrid( + 1, 1, + this.option('columnFormat'), + false + ); + } + }); + }; + + + // rendering bugs + + var tdHeightBug; + + + function Grid(element, options, methods) { + + var tm, firstDay, + nwe, // no weekends (int) + rtl, dis, dit, // day index sign / translate + viewWidth, viewHeight, + rowCnt, colCnt, + colWidth, + thead, tbody, + cachedEvents = [], + segmentContainer, + dayContentPositions = new HorizontalPositionCache(function(dayOfWeek) { + return tbody.find('td:eq(' + ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt) + ') div div'); + }), + selectionManager, + selectionMatrix, + // ... + + // initialize superclass + view = $.extend(this, viewMethods, methods, { + renderGrid: renderGrid, + renderEvents: renderEvents, + rerenderEvents: rerenderEvents, + clearEvents: clearEvents, + setHeight: setHeight, + setWidth: setWidth, + defaultEventEnd: function(event) { // calculates an end if event doesnt have one, mostly for resizing + return cloneDate(event.start); + } + }); + view.init(element, options); + + + /* Grid Rendering + -----------------------------------------------------------------------------*/ + + + disableTextSelection(element.addClass('fc-grid')); + + + function renderGrid(r, c, colFormat, showNumbers) { + + rowCnt = r; + colCnt = c; + + // update option-derived variables + tm = options.theme ? 'ui' : 'fc'; + nwe = options.weekends ? 0 : 1; + firstDay = options.firstDay; + if (rtl = options.isRTL) { + dis = -1; + dit = colCnt - 1; + } else { + dis = 1; + dit = 0; + } + + var month = view.start.getMonth(), + today = clearTime(new Date()), + s, i, j, d = cloneDate(view.visStart); + + if (!tbody) { // first time, build all cells from scratch + + var table = $("
").appendTo(element); + + s = ""; + for (i = 0; i < colCnt; i++) { + s += ""; + addDays(d, 1); + if (nwe) { + skipWeekend(d); + } + } + thead = $(s + "").appendTo(table); + + s = ""; + d = cloneDate(view.visStart); + for (i = 0; i < rowCnt; i++) { + s += ""; + for (j = 0; j < colCnt; j++) { + s += ""; + addDays(d, 1); + if (nwe) { + skipWeekend(d); + } + } + s += ""; + } + tbody = $(s + "").appendTo(table); + dayBind(tbody.find('td')); + + segmentContainer = $("
").appendTo(element); + + } else { // NOT first time, reuse as many cells as possible + + clearEvents(); + + var prevRowCnt = tbody.find('tr').length; + if (rowCnt < prevRowCnt) { + tbody.find('tr:gt(' + (rowCnt - 1) + ')').remove(); // remove extra rows + } + else if (rowCnt > prevRowCnt) { // needs to create new rows... + s = ''; + for (i = prevRowCnt; i < rowCnt; i++) { + s += "
"; + for (j = 0; j < colCnt; j++) { + s += ""; + addDays(d, 1); + if (nwe) { + skipWeekend(d); + } + } + s += ""; + } + tbody.append(s); + } + dayBind(tbody.find('td.fc-new').removeClass('fc-new')); + + // re-label and re-class existing cells + d = cloneDate(view.visStart); + tbody.find('td').each(function() { + var td = $(this); + if (rowCnt > 1) { + if (d.getMonth() == month) { + td.removeClass('fc-other-month'); + } else { + td.addClass('fc-other-month'); + } + } + if (+d == +today) { + td.removeClass('fc-not-today') + .addClass('fc-today') + .addClass(tm + '-state-highlight'); + } else { + td.addClass('fc-not-today') + .removeClass('fc-today') + .removeClass(tm + '-state-highlight'); + } + td.find('div.fc-day-number').text(d.getDate()); + addDays(d, 1); + if (nwe) { + skipWeekend(d); + } + }); + + if (rowCnt == 1) { // more changes likely (week or day view) + + // redo column header text and class + d = cloneDate(view.visStart); + thead.find('th').each(function() { + $(this).text(formatDate(d, colFormat, options)); + this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); + addDays(d, 1); + if (nwe) { + skipWeekend(d); + } + }); + + // redo cell day-of-weeks + d = cloneDate(view.visStart); + tbody.find('td').each(function() { + this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); + addDays(d, 1); + if (nwe) { + skipWeekend(d); + } + }); + + } + + } + + } + + + function setHeight(height) { + viewHeight = height; + var leftTDs = tbody.find('tr td:first-child'), + tbodyHeight = viewHeight - thead.height(), + rowHeight1, rowHeight2; + if (options.weekMode == 'variable') { + rowHeight1 = rowHeight2 = Math.floor(tbodyHeight / (rowCnt == 1 ? 2 : 6)); + } else { + rowHeight1 = Math.floor(tbodyHeight / rowCnt); + rowHeight2 = tbodyHeight - rowHeight1 * (rowCnt - 1); + } + if (tdHeightBug === undefined) { + // bug in firefox where cell height includes padding + var tr = tbody.find('tr:first'), + td = tr.find('td:first'); + td.height(rowHeight1); + tdHeightBug = rowHeight1 != td.height(); + } + if (tdHeightBug) { + leftTDs.slice(0, -1).height(rowHeight1); + leftTDs.slice(-1).height(rowHeight2); + } else { + setOuterHeight(leftTDs.slice(0, -1), rowHeight1); + setOuterHeight(leftTDs.slice(-1), rowHeight2); + } + } + + + function setWidth(width) { + viewWidth = width; + dayContentPositions.clear(); + setOuterWidth( + thead.find('th').slice(0, -1), + colWidth = Math.floor(viewWidth / colCnt) + ); + } + + + /* Event Rendering + -----------------------------------------------------------------------------*/ + + + function renderEvents(events) { + view.reportEvents(cachedEvents = events); + renderSegs(compileSegs(events)); + } + + + function rerenderEvents(modifiedEventId) { + clearEvents(); + renderSegs(compileSegs(cachedEvents), modifiedEventId); + } + + + function clearEvents() { + view._clearEvents(); // only clears the hashes + segmentContainer.empty(); + } + + + function compileSegs(events) { + var d1 = cloneDate(view.visStart), + d2 = addDays(cloneDate(d1), colCnt), + visEventsEnds = $.map(events, exclEndDay), + i, row, + j, level, + k, seg, + segs = []; + for (i = 0; i < rowCnt; i++) { + row = stackSegs(view.sliceSegs(events, visEventsEnds, d1, d2)); + for (j = 0; j < row.length; j++) { + level = row[j]; + for (k = 0; k < level.length; k++) { + seg = level[k]; + seg.row = i; + seg.level = j; + segs.push(seg); + } + } + addDays(d1, 7); + addDays(d2, 7); + } + return segs; + } + + + function renderSegs(segs, modifiedEventId) { + _renderDaySegs( + segs, + rowCnt, + view, + 0, + viewWidth, + function(i) { + return tbody.find('tr:eq(' + i + ')') + }, + dayContentPositions.left, + dayContentPositions.right, + segmentContainer, + bindSegHandlers, + modifiedEventId + ); + } + + + function bindSegHandlers(event, eventElement, seg) { + view.eventElementHandlers(event, eventElement); + if (event.editable || event.editable === undefined && options.editable) { + draggableEvent(event, eventElement); + if (seg.isEnd) { + view.resizableDayEvent(event, eventElement, colWidth); + } + } + } + + + /* Event Dragging + -----------------------------------------------------------------------------*/ + + + function draggableEvent(event, eventElement) { + if (!options.disableDragging && eventElement.draggable) { + var matrix, + dayDelta = 0; + eventElement.draggable({ + zIndex: 9, + delay: 50, + opacity: view.option('dragOpacity'), + revertDuration: options.dragRevertDuration, + start: function(ev, ui) { + view.hideEvents(event, eventElement); + view.trigger('eventDragStart', eventElement, event, ev, ui); + matrix = buildDayMatrix(function(cell) { + eventElement.draggable('option', 'revert', !cell || !cell.rowDelta && !cell.colDelta); + clearOverlays(); + if (cell) { + dayDelta = cell.rowDelta * 7 + cell.colDelta * dis; + renderDayOverlays( + matrix, + addDays(cloneDate(event.start), dayDelta), + addDays(exclEndDay(event), dayDelta) + ); + } else { + dayDelta = 0; + } + }); + matrix.mouse(ev); + }, + drag: function(ev) { + matrix.mouse(ev); + }, + stop: function(ev, ui) { + clearOverlays(); + view.trigger('eventDragStop', eventElement, event, ev, ui); + if (dayDelta) { + eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link + view.eventDrop(this, event, dayDelta, 0, event.allDay, ev, ui); + } else { + if ($.browser.msie) { + eventElement.css('filter', ''); // clear IE opacity side-effects + } + view.showEvents(event, eventElement); + } + } + }); + } + } + + + /* Day clicking and binding + ---------------------------------------------------------*/ + + function dayBind(days) { + days.click(dayClick) + .mousedown(selectionMousedown); + } + + function dayClick(ev) { + if (!view.option('selectable')) { // SelectionManager will worry about dayClick + var n = parseInt(this.className.match(/fc\-day(\d+)/)[1]), + date = addDays( + cloneDate(view.visStart), + Math.floor(n / colCnt) * 7 + n % colCnt + ); + // TODO: what about weekends in middle of week? + view.trigger('dayClick', this, date, true, ev); + } + } + + + /* Selecting + --------------------------------------------------------*/ + + selectionManager = new SelectionManager( + view, + unselect, + function(startDate, endDate, allDay) { + renderDayOverlays( + selectionMatrix, + startDate, + addDays(cloneDate(endDate), 1) + ); + }, + clearOverlays + ); + + function selectionMousedown(ev) { + if (view.option('selectable')) { + selectionMatrix = buildDayMatrix(function(cell) { + if (cell) { + var d = cellDate(cell.row, cell.col); + selectionManager.drag(d, d, true); + } else { + selectionManager.drag(); + } + }); + documentDragHelp( + function(ev) { + selectionMatrix.mouse(ev); + }, + function(ev) { + selectionManager.dragStop(ev); + } + ); + selectionManager.dragStart(ev); + selectionMatrix.mouse(ev); + return false; // prevent auto-unselect and text selection + } + } + + documentUnselectAuto(view, unselect); + + view.select = function(start, end, allDay) { + if (!end) { + end = cloneDate(start); + } + selectionMatrix = buildDayMatrix(); + selectionManager.select(start, end, allDay); + }; + + function unselect() { + selectionManager.unselect(); + } + + view.unselect = unselect; + + + /* Semi-transparent Overlay Helpers + ------------------------------------------------------*/ + + function renderDayOverlays(matrix, overlayStart, overlayEnd) { // overlayEnd is exclusive + var rowStart = cloneDate(view.visStart); + var rowEnd = addDays(cloneDate(rowStart), colCnt); + for (var i = 0; i < rowCnt; i++) { + var stretchStart = new Date(Math.max(rowStart, overlayStart)); + var stretchEnd = new Date(Math.min(rowEnd, overlayEnd)); + if (stretchStart < stretchEnd) { + var colStart, colEnd; + if (rtl) { + colStart = dayDiff(stretchEnd, rowStart) * dis + dit + 1; + colEnd = dayDiff(stretchStart, rowStart) * dis + dit + 1; + } else { + colStart = dayDiff(stretchStart, rowStart); + colEnd = dayDiff(stretchEnd, rowStart); + } + var rect = matrix.rect(i, colStart, i + 1, colEnd, element); + dayBind( + view.renderOverlay(rect, element) + ); + } + addDays(rowStart, 7); + addDays(rowEnd, 7); + } + } + + function clearOverlays() { + view.clearOverlays(); + } + + + /* Utils + ---------------------------------------------------*/ + + + function buildDayMatrix(changeCallback) { + var tds = tbody.find('tr:first td'); + if (rtl) { + tds = $(tds.get().reverse()); + } + return new HoverMatrix(tbody.find('tr'), tds, changeCallback); + } + + + function cellDate(r, c) { // convert r,c to date + return addDays(cloneDate(view.visStart), r * 7 + c * dis + dit); + // TODO: what about weekends in middle of week? + } + + + } + + + function _renderDaySegs(segs, rowCnt, view, minLeft, maxLeft, getRow, dayContentLeft, dayContentRight, segmentContainer, bindSegHandlers, modifiedEventId) { + + var options = view.options, + rtl = options.isRTL, + i, segCnt = segs.length, seg, + event, + className, + left, right, + html = '', + eventElements, + eventElement, + triggerRes, + hsideCache = {}, + vmarginCache = {}, + key, val, + rowI, top, levelI, levelHeight, + rowDivs = [], + rowDivTops = []; + + // calculate desired position/dimensions, create html + for (i = 0; i < segCnt; i++) { + seg = segs[i]; + event = seg.event; + className = 'fc-event fc-event-hori '; + if (rtl) { + if (seg.isStart) { + className += 'fc-corner-right '; + } + if (seg.isEnd) { + className += 'fc-corner-left '; + } + left = seg.isEnd ? dayContentLeft(seg.end.getDay() - 1) : minLeft; + right = seg.isStart ? dayContentRight(seg.start.getDay()) : maxLeft; + } else { + if (seg.isStart) { + className += 'fc-corner-left '; + } + if (seg.isEnd) { + className += 'fc-corner-right '; + } + left = seg.isStart ? dayContentLeft(seg.start.getDay()) : minLeft; + right = seg.isEnd ? dayContentRight(seg.end.getDay() - 1) : maxLeft; + } + html += + "
" + + "" + + (!event.allDay && seg.isStart ? + "" + + htmlEscape(formatDates(event.start, event.end, view.option('timeFormat'), options)) + + "" + : '') + + "" + htmlEscape(event.title) + "" + + "" + + ((event.editable || event.editable === undefined && options.editable) && !options.disableResizing && $.fn.resizable ? + "
" + : '') + + "
"; + seg.left = left; + seg.outerWidth = right - left; + } + segmentContainer[0].innerHTML = html; // faster than html() + eventElements = segmentContainer.children(); + + // retrieve elements, run through eventRender callback, bind handlers + for (i = 0; i < segCnt; i++) { + seg = segs[i]; + eventElement = $(eventElements[i]); // faster than eq() + event = seg.event; + triggerRes = view.trigger('eventRender', event, event, eventElement); + if (triggerRes === false) { + eventElement.remove(); + } else { + if (triggerRes && triggerRes !== true) { + eventElement.remove(); + eventElement = $(triggerRes) + .css({ + position: 'absolute', + left: seg.left + }) + .appendTo(segmentContainer); + } + seg.element = eventElement; + if (event._id === modifiedEventId) { + bindSegHandlers(event, eventElement, seg); + } else { + eventElement[0]._fci = i; // for lazySegBind + } + view.reportEventElement(event, eventElement); + } + } + + lazySegBind(segmentContainer, segs, bindSegHandlers); + + // record event horizontal sides + for (i = 0; i < segCnt; i++) { + seg = segs[i]; + if (eventElement = seg.element) { + val = hsideCache[key = seg.key = cssKey(eventElement[0])]; + seg.hsides = val === undefined ? (hsideCache[key] = hsides(eventElement[0], true)) : val; + } + } + + // set event widths + for (i = 0; i < segCnt; i++) { + seg = segs[i]; + if (eventElement = seg.element) { + eventElement[0].style.width = seg.outerWidth - seg.hsides + 'px'; + } + } + + // record event heights + for (i = 0; i < segCnt; i++) { + seg = segs[i]; + if (eventElement = seg.element) { + val = vmarginCache[key = seg.key]; + seg.outerHeight = eventElement[0].offsetHeight + ( + val === undefined ? (vmarginCache[key] = vmargins(eventElement[0])) : val + ); + } + } + + // set row heights, calculate event tops (in relation to row top) + for (i = 0,rowI = 0; rowI < rowCnt; rowI++) { + top = levelI = levelHeight = 0; + while (i < segCnt && (seg = segs[i]).row == rowI) { + if (seg.level != levelI) { + top += levelHeight; + levelHeight = 0; + levelI++; + } + levelHeight = Math.max(levelHeight, seg.outerHeight || 0); + seg.top = top; + i++; + } + rowDivs[rowI] = getRow(rowI).find('td:first div.fc-day-content > div')// optimal selector? + .height(top + levelHeight); + } + + // calculate row tops + for (rowI = 0; rowI < rowCnt; rowI++) { + rowDivTops[rowI] = rowDivs[rowI][0].offsetTop; + } + + // set event tops + for (i = 0; i < segCnt; i++) { + seg = segs[i]; + if (eventElement = seg.element) { + eventElement[0].style.top = rowDivTops[seg.row] + seg.top + 'px'; + event = seg.event; + view.trigger('eventAfterRender', event, event, eventElement); + } + } + + } + + + /* Agenda Views: agendaWeek/agendaDay + -----------------------------------------------------------------------------*/ + + setDefaults({ + allDaySlot: true, + allDayText: 'all-day', + firstHour: 6, + slotMinutes: 30, + defaultEventMinutes: 120, + axisFormat: 'h(:mm)tt', + timeFormat: { + agenda: 'h:mm{ - h:mm}' + }, + dragOpacity: { + agenda: .5 + }, + minTime: 0, + maxTime: 24 + }); + + views.agendaWeek = function(element, options) { + return new Agenda(element, options, { + render: function(date, delta) { + if (delta) { + addDays(date, delta * 7); + } + var visStart = this.visStart = cloneDate( + this.start = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7)) + ), + visEnd = this.visEnd = cloneDate( + this.end = addDays(cloneDate(visStart), 7) + ); + if (!options.weekends) { + skipWeekend(visStart); + skipWeekend(visEnd, -1, true); + } + this.title = formatDates( + visStart, + addDays(cloneDate(visEnd), -1), + this.option('titleFormat'), + options + ); + this.renderAgenda( + options.weekends ? 7 : 5, + this.option('columnFormat') + ); + } + }); + }; + + views.agendaDay = function(element, options) { + return new Agenda(element, options, { + render: function(date, delta) { + if (delta) { + addDays(date, delta); + if (!options.weekends) { + skipWeekend(date, delta < 0 ? -1 : 1); + } + } + this.title = formatDate(date, this.option('titleFormat'), options); + this.start = this.visStart = cloneDate(date, true); + this.end = this.visEnd = addDays(cloneDate(this.start), 1); + this.renderAgenda( + 1, + this.option('columnFormat') + ); + } + }); + }; + + function Agenda(element, options, methods) { + + var head, body, bodyContent, bodyTable, bg, + colCnt, + slotCnt = 0, // spanning all the way across + axisWidth, colWidth, slotHeight, + viewWidth, viewHeight, + savedScrollTop, + cachedEvents = [], + daySegmentContainer, + slotSegmentContainer, + tm, firstDay, + nwe, // no weekends (int) + rtl, dis, dit, // day index sign / translate + minMinute, maxMinute, + colContentPositions = new HorizontalPositionCache(function(col) { + return bg.find('td:eq(' + col + ') div div'); + }), + slotTopCache = {}, + daySelectionManager, + slotSelectionManager, + selectionHelper, + selectionMatrix, + // ... + + view = $.extend(this, viewMethods, methods, { + renderAgenda: renderAgenda, + renderEvents: renderEvents, + rerenderEvents: rerenderEvents, + clearEvents: clearEvents, + setHeight: setHeight, + setWidth: setWidth, + beforeHide: function() { + savedScrollTop = body.scrollTop(); + }, + afterShow: function() { + body.scrollTop(savedScrollTop); + }, + defaultEventEnd: function(event) { + var start = cloneDate(event.start); + if (event.allDay) { + return start; + } + return addMinutes(start, options.defaultEventMinutes); + } + }); + view.init(element, options); + + + /* Time-slot rendering + -----------------------------------------------------------------------------*/ + + + disableTextSelection(element.addClass('fc-agenda')); + + + function renderAgenda(c, colFormat) { + + colCnt = c; + + // update option-derived variables + tm = options.theme ? 'ui' : 'fc'; + nwe = options.weekends ? 0 : 1; + firstDay = options.firstDay; + if (rtl = options.isRTL) { + dis = -1; + dit = colCnt - 1; + } else { + dis = 1; + dit = 0; + } + minMinute = parseTime(options.minTime); + maxMinute = parseTime(options.maxTime); + + var d0 = rtl ? addDays(cloneDate(view.visEnd), -1) : cloneDate(view.visStart), + d = cloneDate(d0), + today = clearTime(new Date()); + + if (!head) { // first time rendering, build from scratch + + var i, + minutes, + slotNormal = options.slotMinutes % 15 == 0, //... + + // head + s = "
" + + "
" + formatDate(d, colFormat, options) + "
1 && d.getMonth() != month ? ' fc-other-month' : '') + + (+d == +today ? + ' fc-today ' + tm + '-state-highlight' : + ' fc-not-today') + "'>" + + (showNumbers ? "
" + d.getDate() + "
" : '') + + "
 
" + + (showNumbers ? "
" : '') + + "
 
" + + "
" + + "" + + ""; + for (i = 0; i < colCnt; i++) { + s += ""; + addDays(d, dis); + if (nwe) { + skipWeekend(d, dis); + } + } + s += ""; + if (options.allDaySlot) { + s += "" + + "" + + "" + + "" + + ""; + } + s += "
 " + formatDate(d, colFormat, options) + " 
" + options.allDayText + "" + + "
 
 
"; + head = $(s).appendTo(element); + dayBind(head.find('td')); + + // all-day event container + daySegmentContainer = $("
").appendTo(head); + + // body + d = zeroDate(); + var maxd = addMinutes(cloneDate(d), maxMinute); + addMinutes(d, minMinute); + s = ""; + for (i = 0; d < maxd; i++) { + minutes = d.getMinutes(); + s += ""; + addMinutes(d, options.slotMinutes); + slotCnt++; + } + s += "
" + + ((!slotNormal || !minutes) ? formatDate(d, options.axisFormat) : ' ') + + "
 
"; + body = $("
") + .append(bodyContent = $("
") + .append(bodyTable = $(s))) + .appendTo(element); + slotBind(body.find('td')); + + // slot event container + slotSegmentContainer = $("
").appendTo(bodyContent); + + // background stripes + d = cloneDate(d0); + s = "
" + + ""; + for (i = 0; i < colCnt; i++) { + s += ""; + addDays(d, dis); + if (nwe) { + skipWeekend(d, dis); + } + } + s += "
 
"; + bg = $(s).appendTo(element); + + } else { // skeleton already built, just modify it + + clearEvents(); + + // redo column header text and class + head.find('tr:first th').slice(1, -1).each(function() { + $(this).text(formatDate(d, colFormat, options)); + this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); + addDays(d, dis); + if (nwe) { + skipWeekend(d, dis); + } + }); + + // change classes of background stripes + d = cloneDate(d0); + bg.find('td').each(function() { + this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); + if (+d == +today) { + $(this) + .removeClass('fc-not-today') + .addClass('fc-today') + .addClass(tm + '-state-highlight'); + } else { + $(this) + .addClass('fc-not-today') + .removeClass('fc-today') + .removeClass(tm + '-state-highlight'); + } + addDays(d, dis); + if (nwe) { + skipWeekend(d, dis); + } + }); + + } + + } + + + function resetScroll() { + var d0 = zeroDate(), + scrollDate = cloneDate(d0); + scrollDate.setHours(options.firstHour); + var top = timePosition(d0, scrollDate) + 1, // +1 for the border + scroll = function() { + body.scrollTop(top); + }; + scroll(); + setTimeout(scroll, 0); // overrides any previous scroll state made by the browser + } + + + function setHeight(height, dateChanged) { + viewHeight = height; + slotTopCache = {}; + + body.height(height - head.height()); + + slotHeight = body.find('tr:first div').height() + 1; + + bg.css({ + top: head.find('tr').height(), + height: height + }); + + if (dateChanged) { + resetScroll(); + } + } + + + function setWidth(width) { + viewWidth = width; + colContentPositions.clear(); + + body.width(width); + bodyTable.width(''); + + var topTDs = head.find('tr:first th'), + stripeTDs = bg.find('td'), + clientWidth = body[0].clientWidth; + + bodyTable.width(clientWidth); + + // time-axis width + axisWidth = 0; + setOuterWidth( + head.find('tr:lt(2) th:first').add(body.find('tr:first th')) + .width('') + .each(function() { + axisWidth = Math.max(axisWidth, $(this).outerWidth()); + }), + axisWidth + ); + + // column width + colWidth = Math.floor((clientWidth - axisWidth) / colCnt); + setOuterWidth(stripeTDs.slice(0, -1), colWidth); + setOuterWidth(topTDs.slice(1, -2), colWidth); + setOuterWidth(topTDs.slice(-2, -1), clientWidth - axisWidth - colWidth * (colCnt - 1)); + + bg.css({ + left: axisWidth, + width: clientWidth - axisWidth + }); + } + + + /* Slot/Day clicking and binding + -----------------------------------------------------------------------*/ + + + function dayBind(tds) { + tds.click(slotClick) + .mousedown(daySelectionMousedown); + } + + + function slotBind(tds) { + tds.click(slotClick) + .mousedown(slotSelectionMousedown); + } + + + function slotClick(ev) { + if (!view.option('selectable')) { // SelectionManager will worry about dayClick + var col = Math.floor((ev.pageX - bg.offset().left) / colWidth), + date = addDays(cloneDate(view.visStart), dit + dis * col), + rowMatch = this.className.match(/fc-slot(\d+)/); + if (rowMatch) { + var mins = parseInt(rowMatch[1]) * options.slotMinutes, + hours = Math.floor(mins / 60); + date.setHours(hours); + date.setMinutes(mins % 60 + minMinute); + view.trigger('dayClick', this, date, false, ev); + } else { + view.trigger('dayClick', this, date, true, ev); + } + } + } + + + /* Event Rendering + -----------------------------------------------------------------------------*/ + + function renderEvents(events, modifiedEventId) { + view.reportEvents(cachedEvents = events); + var i, len = events.length, + dayEvents = [], + slotEvents = []; + for (i = 0; i < len; i++) { + if (events[i].allDay) { + dayEvents.push(events[i]); + } else { + slotEvents.push(events[i]); + } + } + renderDaySegs(compileDaySegs(dayEvents), modifiedEventId); + renderSlotSegs(compileSlotSegs(slotEvents), modifiedEventId); + } + + + function rerenderEvents(modifiedEventId) { + clearEvents(); + renderEvents(cachedEvents, modifiedEventId); + } + + + function clearEvents() { + view._clearEvents(); // only clears the hashes + daySegmentContainer.empty(); + slotSegmentContainer.empty(); + } + + + function compileDaySegs(events) { + var levels = stackSegs(view.sliceSegs(events, $.map(events, exclEndDay), view.visStart, view.visEnd)), + i, levelCnt = levels.length, level, + j, seg, + segs = []; + for (i = 0; i < levelCnt; i++) { + level = levels[i]; + for (j = 0; j < level.length; j++) { + seg = level[j]; + seg.row = 0; + seg.level = i; + segs.push(seg); + } + } + return segs; + } + + + function compileSlotSegs(events) { + var d = addMinutes(cloneDate(view.visStart), minMinute), + visEventEnds = $.map(events, slotEventEnd), + i, col, + j, level, + k, seg, + segs = []; + for (i = 0; i < colCnt; i++) { + col = stackSegs(view.sliceSegs(events, visEventEnds, d, addMinutes(cloneDate(d), maxMinute - minMinute))); + countForwardSegs(col); + for (j = 0; j < col.length; j++) { + level = col[j]; + for (k = 0; k < level.length; k++) { + seg = level[k]; + seg.col = i; + seg.level = j; + segs.push(seg); + } + } + addDays(d, 1, true); + } + return segs; + } + + + // renders 'all-day' events at the top + + function renderDaySegs(segs, modifiedEventId) { + if (options.allDaySlot) { + _renderDaySegs( + segs, + 1, + view, + axisWidth, + viewWidth, + function() { + return head.find('tr.fc-all-day'); + }, + function(dayOfWeek) { + return axisWidth + colContentPositions.left(dayOfWeekCol(dayOfWeek)); + }, + function(dayOfWeek) { + return axisWidth + colContentPositions.right(dayOfWeekCol(dayOfWeek)); + }, + daySegmentContainer, + daySegBind, + modifiedEventId + ); + setHeight(viewHeight); // might have pushed the body down, so resize + } + } + + + // renders events in the 'time slots' at the bottom + + function renderSlotSegs(segs, modifiedEventId) { + + var i, segCnt = segs.length, seg, + event, + className, + top, bottom, + colI, levelI, forward, + leftmost, + availWidth, + outerWidth, + left, + html = '', + eventElements, + eventElement, + triggerRes, + vsideCache = {}, + hsideCache = {}, + key, val, + titleSpan, + height; + + // calculate position/dimensions, create html + for (i = 0; i < segCnt; i++) { + seg = segs[i]; + event = seg.event; + className = 'fc-event fc-event-vert '; + if (seg.isStart) { + className += 'fc-corner-top '; + } + if (seg.isEnd) { + className += 'fc-corner-bottom '; + } + top = timePosition(seg.start, seg.start); + bottom = timePosition(seg.start, seg.end); + colI = seg.col; + levelI = seg.level; + forward = seg.forward || 0; + leftmost = axisWidth + colContentPositions.left(colI * dis + dit); + availWidth = axisWidth + colContentPositions.right(colI * dis + dit) - leftmost; + availWidth = Math.min(availWidth - 6, availWidth * .95); // TODO: move this to CSS + if (levelI) { + // indented and thin + outerWidth = availWidth / (levelI + forward + 1); + } else { + if (forward) { + // moderately wide, aligned left still + outerWidth = ((availWidth / (forward + 1)) - (12 / 2)) * 2; // 12 is the predicted width of resizer = + } else { + // can be entire width, aligned left + outerWidth = availWidth; + } + } + left = leftmost + // leftmost possible + (availWidth / (levelI + forward + 1) * levelI) // indentation + * dis + (rtl ? availWidth - outerWidth : 0); // rtl + seg.top = top; + seg.left = left; + seg.outerWidth = outerWidth; + seg.outerHeight = bottom - top; + html += slotSegHtml(event, seg, className); + } + slotSegmentContainer[0].innerHTML = html; // faster than html() + eventElements = slotSegmentContainer.children(); + + // retrieve elements, run through eventRender callback, bind event handlers + for (i = 0; i < segCnt; i++) { + seg = segs[i]; + event = seg.event; + eventElement = $(eventElements[i]); // faster than eq() + triggerRes = view.trigger('eventRender', event, event, eventElement); + if (triggerRes === false) { + eventElement.remove(); + } else { + if (triggerRes && triggerRes !== true) { + eventElement.remove(); + eventElement = $(triggerRes) + .css({ + position: 'absolute', + top: seg.top, + left: seg.left + }) + .appendTo(slotSegmentContainer); + } + seg.element = eventElement; + if (event._id === modifiedEventId) { + slotSegBind(event, eventElement, seg); + } else { + eventElement[0]._fci = i; // for lazySegBind + } + view.reportEventElement(event, eventElement); + } + } + + lazySegBind(slotSegmentContainer, segs, slotSegBind); + + // record event sides and title positions + for (i = 0; i < segCnt; i++) { + seg = segs[i]; + if (eventElement = seg.element) { + val = vsideCache[key = seg.key = cssKey(eventElement[0])]; + seg.vsides = val === undefined ? (vsideCache[key] = vsides(eventElement[0], true)) : val; + val = hsideCache[key]; + seg.hsides = val === undefined ? (hsideCache[key] = hsides(eventElement[0], true)) : val; + titleSpan = eventElement.find('span.fc-event-title'); + if (titleSpan.length) { + seg.titleTop = titleSpan[0].offsetTop; + } + } + } + + // set all positions/dimensions at once + for (i = 0; i < segCnt; i++) { + seg = segs[i]; + if (eventElement = seg.element) { + eventElement[0].style.width = seg.outerWidth - seg.hsides + 'px'; + eventElement[0].style.height = (height = seg.outerHeight - seg.vsides) + 'px'; + event = seg.event; + if (seg.titleTop !== undefined && height - seg.titleTop < 10) { + // not enough room for title, put it in the time header + eventElement.find('span.fc-event-time') + .text(formatDate(event.start, view.option('timeFormat')) + ' - ' + event.title); + eventElement.find('span.fc-event-title') + .remove(); + } + view.trigger('eventAfterRender', event, event, eventElement); + } + } + + } + + function slotSegHtml(event, seg, className) { + return "
" + + "" + + "" + + "" + htmlEscape(formatDates(event.start, event.end, view.option('timeFormat'))) + "" + + "" + htmlEscape(event.title) + "" + + "" + + ((event.editable || event.editable === undefined && options.editable) && !options.disableResizing && $.fn.resizable ? + "
=
" + : '') + + "
"; + } + + + function daySegBind(event, eventElement, seg) { + view.eventElementHandlers(event, eventElement); + if (event.editable || event.editable === undefined && options.editable) { + draggableDayEvent(event, eventElement, seg.isStart); + if (seg.isEnd) { + view.resizableDayEvent(event, eventElement, colWidth); + } + } + } + + + function slotSegBind(event, eventElement, seg) { + view.eventElementHandlers(event, eventElement); + if (event.editable || event.editable === undefined && options.editable) { + var timeElement = eventElement.find('span.fc-event-time'); + draggableSlotEvent(event, eventElement, timeElement); + if (seg.isEnd) { + resizableSlotEvent(event, eventElement, timeElement); + } + } + } + + + /* Event Dragging + -----------------------------------------------------------------------------*/ + + + // when event starts out FULL-DAY + + function draggableDayEvent(event, eventElement, isStart) { + if (!options.disableDragging && eventElement.draggable) { + var origPosition, origWidth, + resetElement, + allDay = true, + matrix; + eventElement.draggable({ + zIndex: 9, + opacity: view.option('dragOpacity', 'month'), // use whatever the month view was using + revertDuration: options.dragRevertDuration, + start: function(ev, ui) { + view.hideEvents(event, eventElement); + view.trigger('eventDragStart', eventElement, event, ev, ui); + origPosition = eventElement.position(); + origWidth = eventElement.width(); + resetElement = function() { + if (!allDay) { + eventElement + .width(origWidth) + .height('') + .draggable('option', 'grid', null); + allDay = true; + } + }; + matrix = buildDayMatrix(function(cell) { + eventElement.draggable('option', 'revert', !cell || !cell.rowDelta && !cell.colDelta); + view.clearOverlays(); + if (cell) { + if (!cell.row) { + // on full-days + renderDayOverlay( + matrix, + addDays(cloneDate(event.start), cell.colDelta), + addDays(exclEndDay(event), cell.colDelta) + ); + resetElement(); + } else { + // mouse is over bottom slots + if (isStart && allDay) { + // convert event to temporary slot-event + setOuterHeight( + eventElement.width(colWidth - 10), // don't use entire width + slotHeight * Math.round( + (event.end ? ((event.end - event.start) / MINUTE_MS) : options.defaultEventMinutes) + / options.slotMinutes) + ); + eventElement.draggable('option', 'grid', [colWidth, 1]); + allDay = false; + } + } + } + }, true); + matrix.mouse(ev); + }, + drag: function(ev, ui) { + matrix.mouse(ev); + }, + stop: function(ev, ui) { + view.trigger('eventDragStop', eventElement, event, ev, ui); + view.clearOverlays(); + var cell = matrix.cell; + var dayDelta = dis * ( + allDay ? // can't trust cell.colDelta when using slot grid + (cell ? cell.colDelta : 0) : + Math.floor((ui.position.left - origPosition.left) / colWidth) + ); + if (!cell || !dayDelta && !cell.rowDelta) { + // over nothing (has reverted) + resetElement(); + if ($.browser.msie) { + eventElement.css('filter', ''); // clear IE opacity side-effects + } + view.showEvents(event, eventElement); + } else { + eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link + view.eventDrop( + this, event, dayDelta, + allDay ? 0 : // minute delta + Math.round((eventElement.offset().top - bodyContent.offset().top) / slotHeight) + * options.slotMinutes + + minMinute + - (event.start.getHours() * 60 + event.start.getMinutes()), + allDay, ev, ui + ); + } + } + }); + } + } + + + // when event starts out IN TIMESLOTS + + function draggableSlotEvent(event, eventElement, timeElement) { + if (!options.disableDragging && eventElement.draggable) { + var origPosition, + resetElement, + prevSlotDelta, slotDelta, + allDay = false, + matrix; + eventElement.draggable({ + zIndex: 9, + scroll: false, + grid: [colWidth, slotHeight], + axis: colCnt == 1 ? 'y' : false, + opacity: view.option('dragOpacity'), + revertDuration: options.dragRevertDuration, + start: function(ev, ui) { + view.hideEvents(event, eventElement); + view.trigger('eventDragStart', eventElement, event, ev, ui); + if ($.browser.msie) { + eventElement.find('span.fc-event-bg').hide(); // nested opacities mess up in IE, just hide + } + origPosition = eventElement.position(); + resetElement = function() { + // convert back to original slot-event + if (allDay) { + timeElement.css('display', ''); // show() was causing display=inline + eventElement.draggable('option', 'grid', [colWidth, slotHeight]); + allDay = false; + } + }; + prevSlotDelta = 0; + matrix = buildDayMatrix(function(cell) { + eventElement.draggable('option', 'revert', !cell); + view.clearOverlays(); + if (cell) { + if (!cell.row && options.allDaySlot) { // over full days + if (!allDay) { + // convert to temporary all-day event + allDay = true; + timeElement.hide(); + eventElement.draggable('option', 'grid', null); + } + renderDayOverlay( + matrix, + addDays(cloneDate(event.start), cell.colDelta), + addDays(exclEndDay(event), cell.colDelta) + ); + } else { // on slots + resetElement(); + } + } + }, true); + matrix.mouse(ev); + }, + drag: function(ev, ui) { + slotDelta = Math.round((ui.position.top - origPosition.top) / slotHeight); + if (slotDelta != prevSlotDelta) { + if (!allDay) { + // update time header + var minuteDelta = slotDelta * options.slotMinutes, + newStart = addMinutes(cloneDate(event.start), minuteDelta), + newEnd; + if (event.end) { + newEnd = addMinutes(cloneDate(event.end), minuteDelta); + } + timeElement.text(formatDates(newStart, newEnd, view.option('timeFormat'))); + } + prevSlotDelta = slotDelta; + } + matrix.mouse(ev); + }, + stop: function(ev, ui) { + view.clearOverlays(); + view.trigger('eventDragStop', eventElement, event, ev, ui); + var cell = matrix.cell, + dayDelta = dis * ( + allDay ? // can't trust cell.colDelta when using slot grid + (cell ? cell.colDelta : 0) : + Math.floor((ui.position.left - origPosition.left) / colWidth) + ); + if (!cell || !slotDelta && !dayDelta) { + resetElement(); + if ($.browser.msie) { + eventElement + .css('filter', '')// clear IE opacity side-effects + .find('span.fc-event-bg').css('display', ''); // .show() made display=inline + } + eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position + view.showEvents(event, eventElement); + } else { + view.eventDrop( + this, event, dayDelta, + allDay ? 0 : slotDelta * options.slotMinutes, // minute delta + allDay, ev, ui + ); + } + } + }); + } + } + + + /* Event Resizing + -----------------------------------------------------------------------------*/ + + // for TIMESLOT events + + function resizableSlotEvent(event, eventElement, timeElement) { + if (!options.disableResizing && eventElement.resizable) { + var slotDelta, prevSlotDelta; + eventElement.resizable({ + handles: { + s: 'div.ui-resizable-s' + }, + grid: slotHeight, + start: function(ev, ui) { + slotDelta = prevSlotDelta = 0; + view.hideEvents(event, eventElement); + if ($.browser.msie && $.browser.version == '6.0') { + eventElement.css('overflow', 'hidden'); + } + eventElement.css('z-index', 9); + view.trigger('eventResizeStart', this, event, ev, ui); + }, + resize: function(ev, ui) { + // don't rely on ui.size.height, doesn't take grid into account + slotDelta = Math.round((Math.max(slotHeight, eventElement.height()) - ui.originalSize.height) / slotHeight); + if (slotDelta != prevSlotDelta) { + timeElement.text( + formatDates( + event.start, + (!slotDelta && !event.end) ? null : // no change, so don't display time range + addMinutes(view.eventEnd(event), options.slotMinutes * slotDelta), + view.option('timeFormat') + ) + ); + prevSlotDelta = slotDelta; + } + }, + stop: function(ev, ui) { + view.trigger('eventResizeStop', this, event, ev, ui); + if (slotDelta) { + view.eventResize(this, event, 0, options.slotMinutes * slotDelta, ev, ui); + } else { + eventElement.css('z-index', 8); + view.showEvents(event, eventElement); + // BUG: if event was really short, need to put title back in span + } + } + }); + } + } + + + /* Selecting + -----------------------------------------------------------------------------*/ + + daySelectionManager = new SelectionManager( + view, + unselect, + function(startDate, endDate, allDay) { + renderDayOverlay( + selectionMatrix, + startDate, + addDays(cloneDate(endDate), 1) + ); + }, + clearSelection + ); + + function daySelectionMousedown(ev) { + if (view.option('selectable')) { + selectionMatrix = buildDayMatrix(function(cell) { + if (cell) { + var d = dayColDate(cell.col); + daySelectionManager.drag(d, d, true); + } else { + daySelectionManager.drag(); + } + }); + documentDragHelp( + function(ev) { + selectionMatrix.mouse(ev); + }, + function(ev) { + daySelectionManager.dragStop(ev); + } + ); + daySelectionManager.dragStart(ev); + selectionMatrix.mouse(ev); + return false; // prevent auto-unselect and text selection + } + } + + slotSelectionManager = new SelectionManager( + view, + unselect, + renderSlotSelection, + clearSelection + ); + + function slotSelectionMousedown(ev) { + if (view.option('selectable')) { + selectionMatrix = buildSlotMatrix(function(cell) { + if (cell) { + var d = slotCellDate(cell.row, cell.origCol); + slotSelectionManager.drag(d, addMinutes(cloneDate(d), options.slotMinutes), false); + } else { + slotSelectionManager.drag(); + } + }); + documentDragHelp( + function(ev) { + selectionMatrix.mouse(ev); + }, + function(ev) { + slotSelectionManager.dragStop(ev); + } + ); + slotSelectionManager.dragStart(ev); + selectionMatrix.mouse(ev); + return false; // prevent auto-unselect and text selection + } + } + + documentUnselectAuto(view, unselect); + + this.select = function(start, end, allDay) { + if (allDay) { + if (options.allDaySlot) { + if (!end) { + end = cloneDate(start); + } + selectionMatrix = buildDayMatrix(); + daySelectionManager.select(start, end, allDay); + } + } else { + if (!end) { + end = addMinutes(cloneDate(start), options.slotMinutes); + } + selectionMatrix = buildSlotMatrix(); + slotSelectionManager.select(start, end, allDay); + } + }; + + function unselect() { + slotSelectionManager.unselect(); + daySelectionManager.unselect(); + } + + this.unselect = unselect; + + + /* Selecting drawing utils + -----------------------------------------------------------------------------*/ + + function renderSlotSelection(startDate, endDate) { + var helperOption = view.option('selectHelper'); + if (helperOption) { + var col = dayDiff(startDate, view.visStart); + if (col >= 0 && col < colCnt) { // only works when times are on same day + var rect = selectionMatrix.rect(0, col * dis + dit, 1, col * dis + dit + 1, bodyContent); // only for horizontal coords + var top = timePosition(startDate, startDate); + var bottom = timePosition(startDate, endDate); + if (bottom > top) { // protect against selections that are entirely before or after visible range + rect.top = top; + rect.height = bottom - top; + rect.left += 2; + rect.width -= 5; + if ($.isFunction(helperOption)) { + var helperRes = helperOption(startDate, endDate); + if (helperRes) { + rect.position = 'absolute'; + rect.zIndex = 8; + selectionHelper = $(helperRes) + .css(rect) + .appendTo(bodyContent); + } + } else { + selectionHelper = $(slotSegHtml( + { + title: '', + start: startDate, + end: endDate, + className: [], + editable: false + }, + rect, + 'fc-event fc-event-vert fc-corner-top fc-corner-bottom ' + )); + if ($.browser.msie) { + selectionHelper.find('span.fc-event-bg').hide(); // nested opacities mess up in IE, just hide + } + selectionHelper.css('opacity', view.option('dragOpacity')); + } + if (selectionHelper) { + slotBind(selectionHelper); + bodyContent.append(selectionHelper); + setOuterWidth(selectionHelper, rect.width, true); // needs to be after appended + setOuterHeight(selectionHelper, rect.height, true); + } + } + } + } else { + renderSlotOverlay(selectionMatrix, startDate, endDate); + } + } + + function clearSelection() { + clearOverlays(); + if (selectionHelper) { + selectionHelper.remove(); + selectionHelper = null; + } + } + + + /* Semi-transparent Overlay Helpers + -----------------------------------------------------*/ + + function renderDayOverlay(matrix, startDate, endDate) { + var startCol, endCol; + if (rtl) { + startCol = dayDiff(endDate, view.visStart) * dis + dit + 1; + endCol = dayDiff(startDate, view.visStart) * dis + dit + 1; + } else { + startCol = dayDiff(startDate, view.visStart); + endCol = dayDiff(endDate, view.visStart); + } + startCol = Math.max(0, startCol); + endCol = Math.min(colCnt, endCol); + if (startCol < endCol) { + var rect = matrix.rect(0, startCol, 1, endCol, head); + dayBind( + view.renderOverlay(rect, head) + ); + } + } + + function renderSlotOverlay(matrix, overlayStart, overlayEnd) { + var dayStart = cloneDate(view.visStart); + var dayEnd = addDays(cloneDate(dayStart), 1); + for (var i = 0; i < colCnt; i++) { + var stretchStart = new Date(Math.max(dayStart, overlayStart)); + var stretchEnd = new Date(Math.min(dayEnd, overlayEnd)); + if (stretchStart < stretchEnd) { + var rect = matrix.rect(0, i * dis + dit, 1, i * dis + dit + 1, bodyContent); // only use it for horizontal coords + var top = timePosition(dayStart, stretchStart); + var bottom = timePosition(dayStart, stretchEnd); + rect.top = top; + rect.height = bottom - top; + slotBind( + view.renderOverlay(rect, bodyContent) + ); + } + addDays(dayStart, 1); + addDays(dayEnd, 1); + } + } + + function clearOverlays() { + view.clearOverlays(); + } + + + /* Coordinate Utilities + -----------------------------------------------------------------------------*/ + + // get the Y coordinate of the given time on the given day (both Date objects) + function timePosition(day, time) { // both date objects. day holds 00:00 of current day + day = cloneDate(day, true); + if (time < addMinutes(cloneDate(day), minMinute)) { + return 0; + } + if (time >= addMinutes(cloneDate(day), maxMinute)) { + return bodyContent.height(); + } + var slotMinutes = options.slotMinutes, + minutes = time.getHours() * 60 + time.getMinutes() - minMinute, + slotI = Math.floor(minutes / slotMinutes), + slotTop = slotTopCache[slotI]; + if (slotTop === undefined) { + slotTop = slotTopCache[slotI] = body.find('tr:eq(' + slotI + ') td div')[0].offsetTop; + } + return Math.max(0, Math.round( + slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes) + )); + } + + function buildDayMatrix(changeCallback, includeSlotArea) { + var rowElements = options.allDaySlot ? head.find('td') : $([]); + if (includeSlotArea) { + rowElements = rowElements.add(body); + } + return new HoverMatrix(rowElements, bg.find('td'), changeCallback); + } + + function buildSlotMatrix(changeCallback) { + return new HoverMatrix(bodyTable.find('td'), bg.find('td'), changeCallback); + } + + + /* Date Utilities + ----------------------------------------------------*/ + + function slotEventEnd(event) { + if (event.end) { + return cloneDate(event.end); + } else { + return addMinutes(cloneDate(event.start), options.defaultEventMinutes); + } + } + + function dayOfWeekCol(dayOfWeek) { + return ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt) * dis + dit; + } + + + // generating dates from cell row & columns + + function dayColDate(col) { + return addDays(cloneDate(view.visStart), col * dis + dit); + } + + function slotCellDate(row, col) { + var d = dayColDate(col); + addMinutes(d, minMinute + row * options.slotMinutes); + return d; + } + + + } + + + // count the number of colliding, higher-level segments (for event squishing) + + function countForwardSegs(levels) { + var i, j, k, level, segForward, segBack; + for (i = levels.length - 1; i > 0; i--) { + level = levels[i]; + for (j = 0; j < level.length; j++) { + segForward = level[j]; + for (k = 0; k < levels[i - 1].length; k++) { + segBack = levels[i - 1][k]; + if (segsCollide(segForward, segBack)) { + segBack.forward = Math.max(segBack.forward || 0, (segForward.forward || 0) + 1); + } + } + } + } + } + + + /* Methods & Utilities for All Views + -----------------------------------------------------------------------------*/ + + var viewMethods = { + + /* + * Objects inheriting these methods must implement the following properties/methods: + * - title + * - start + * - end + * - visStart + * - visEnd + * - defaultEventEnd(event) + * - render(events) + * - rerenderEvents() + * + * + * z-index reservations: + * 3 - day-overlay + * 8 - events + * 9 - dragging/resizing events + * + */ + + + + init: function(element, options) { + this.element = element; + this.options = options; + this.eventsByID = {}; + this.eventElements = []; + this.eventElementsByID = {}; + this.usedOverlays = []; + this.unusedOverlays = []; + }, + + + + // triggers an event handler, always append view as last arg + + trigger: function(name, thisObj) { + if (this.options[name]) { + return this.options[name].apply(thisObj || this, Array.prototype.slice.call(arguments, 2).concat([this])); + } + }, + + + + // returns a Date object for an event's end + + eventEnd: function(event) { + return event.end ? cloneDate(event.end) : this.defaultEventEnd(event); // TODO: make sure always using copies + }, + + + + // report when view receives new events + + reportEvents: function(events) { // events are already normalized at this point + var i, len = events.length, event, + eventsByID = this.eventsByID = {}; + for (i = 0; i < len; i++) { + event = events[i]; + if (eventsByID[event._id]) { + eventsByID[event._id].push(event); + } else { + eventsByID[event._id] = [event]; + } + } + }, + + + + // report when view creates an element for an event + + reportEventElement: function(event, element) { + this.eventElements.push(element); + var eventElementsByID = this.eventElementsByID; + if (eventElementsByID[event._id]) { + eventElementsByID[event._id].push(element); + } else { + eventElementsByID[event._id] = [element]; + } + }, + + + + // event element manipulation + + _clearEvents: function() { // only resets hashes + this.eventElements = []; + this.eventElementsByID = {}; + }, + + showEvents: function(event, exceptElement) { + this._eee(event, exceptElement, 'show'); + }, + + hideEvents: function(event, exceptElement) { + this._eee(event, exceptElement, 'hide'); + }, + + _eee: function(event, exceptElement, funcName) { // event-element-each + var elements = this.eventElementsByID[event._id], + i, len = elements.length; + for (i = 0; i < len; i++) { + if (elements[i][0] != exceptElement[0]) { // AHAHAHAHAHAHAHAH + elements[i][funcName](); + } + } + }, + + + + // event modification reporting + + eventDrop: function(e, event, dayDelta, minuteDelta, allDay, ev, ui) { + var view = this, + oldAllDay = event.allDay, + eventId = event._id; + view.moveEvents(view.eventsByID[eventId], dayDelta, minuteDelta, allDay); + view.trigger('eventDrop', e, event, dayDelta, minuteDelta, allDay, function() { // TODO: change docs + // TODO: investigate cases where this inverse technique might not work + view.moveEvents(view.eventsByID[eventId], -dayDelta, -minuteDelta, oldAllDay); + view.rerenderEvents(); + }, ev, ui); + view.eventsChanged = true; + view.rerenderEvents(eventId); + }, + + eventResize: function(e, event, dayDelta, minuteDelta, ev, ui) { + var view = this, + eventId = event._id; + view.elongateEvents(view.eventsByID[eventId], dayDelta, minuteDelta); + view.trigger('eventResize', e, event, dayDelta, minuteDelta, function() { + // TODO: investigate cases where this inverse technique might not work + view.elongateEvents(view.eventsByID[eventId], -dayDelta, -minuteDelta); + view.rerenderEvents(); + }, ev, ui); + view.eventsChanged = true; + view.rerenderEvents(eventId); + }, + + + + // event modification + + moveEvents: function(events, dayDelta, minuteDelta, allDay) { + minuteDelta = minuteDelta || 0; + for (var e, len = events.length, i = 0; i < len; i++) { + e = events[i]; + if (allDay !== undefined) { + e.allDay = allDay; + } + addMinutes(addDays(e.start, dayDelta, true), minuteDelta); + if (e.end) { + e.end = addMinutes(addDays(e.end, dayDelta, true), minuteDelta); + } + normalizeEvent(e, this.options); + } + }, + + elongateEvents: function(events, dayDelta, minuteDelta) { + minuteDelta = minuteDelta || 0; + for (var e, len = events.length, i = 0; i < len; i++) { + e = events[i]; + e.end = addMinutes(addDays(this.eventEnd(e), dayDelta, true), minuteDelta); + normalizeEvent(e, this.options); + } + }, + + + + // semi-transparent overlay (while dragging or selecting) + + renderOverlay: function(rect, parent) { + var e = this.unusedOverlays.shift(); + if (!e) { + e = $("
"); + } + if (e[0].parentNode != parent[0]) { + e.appendTo(parent); + } + this.usedOverlays.push(e.css(rect).show()); + return e; + }, + + clearOverlays: function() { + var e; + while (e = this.usedOverlays.shift()) { + this.unusedOverlays.push(e.hide().unbind()); + } + }, + + + + + // common horizontal event resizing + + resizableDayEvent: function(event, eventElement, colWidth) { + var view = this; + if (!view.options.disableResizing && eventElement.resizable) { + eventElement.resizable({ + handles: view.options.isRTL ? {w:'div.ui-resizable-w'} : {e:'div.ui-resizable-e'}, + grid: colWidth, + minWidth: colWidth / 2, // need this or else IE throws errors when too small + containment: view.element.parent().parent(), // the main element... + // ... a fix. wouldn't allow extending to last column in agenda views (jq ui bug?) + start: function(ev, ui) { + eventElement.css('z-index', 9); + view.hideEvents(event, eventElement); + view.trigger('eventResizeStart', this, event, ev, ui); + }, + stop: function(ev, ui) { + view.trigger('eventResizeStop', this, event, ev, ui); + // ui.size.width wasn't working with grid correctly, use .width() + var dayDelta = Math.round((eventElement.width() - ui.originalSize.width) / colWidth); + if (dayDelta) { + view.eventResize(this, event, dayDelta, 0, ev, ui); + } else { + eventElement.css('z-index', 8); + view.showEvents(event, eventElement); + } + } + }); + } + }, + + + + // attaches eventClick, eventMouseover, eventMouseout + + eventElementHandlers: function(event, eventElement) { + var view = this; + eventElement + .click(function(ev) { + if (!eventElement.hasClass('ui-draggable-dragging') && + !eventElement.hasClass('ui-resizable-resizing')) { + return view.trigger('eventClick', this, event, ev); + } + }) + .hover( + function(ev) { + view.trigger('eventMouseover', this, event, ev); + }, + function(ev) { + view.trigger('eventMouseout', this, event, ev); + } + ); + }, + + + + // get a property from the 'options' object, using smart view naming + + option: function(name, viewName) { + var v = this.options[name]; + if (typeof v == 'object') { + return smartProperty(v, viewName || this.name); + } + return v; + }, + + + + // event rendering utilities + + sliceSegs: function(events, visEventEnds, start, end) { + var segs = [], + i, len = events.length, event, + eventStart, eventEnd, + segStart, segEnd, + isStart, isEnd; + for (i = 0; i < len; i++) { + event = events[i]; + eventStart = event.start; + eventEnd = visEventEnds[i]; + if (eventEnd > start && eventStart < end) { + if (eventStart < start) { + segStart = cloneDate(start); + isStart = false; + } else { + segStart = eventStart; + isStart = true; + } + if (eventEnd > end) { + segEnd = cloneDate(end); + isEnd = false; + } else { + segEnd = eventEnd; + isEnd = true; + } + segs.push({ + event: event, + start: segStart, + end: segEnd, + isStart: isStart, + isEnd: isEnd, + msLength: segEnd - segStart + }); + } + } + return segs.sort(segCmp); + } + + + }; + + + function lazySegBind(container, segs, bindHandlers) { + container.unbind('mouseover').mouseover(function(ev) { + var parent = ev.target, e, + i, seg; + while (parent != this) { + e = parent; + parent = parent.parentNode; + } + if ((i = e._fci) !== undefined) { + e._fci = undefined; + seg = segs[i]; + bindHandlers(seg.event, seg.element, seg); + $(ev.target).trigger(ev); + } + ev.stopPropagation(); + }); + } + + + // event rendering calculation utilities + + function stackSegs(segs) { + var levels = [], + i, len = segs.length, seg, + j, collide, k; + for (i = 0; i < len; i++) { + seg = segs[i]; + j = 0; // the level index where seg should belong + while (true) { + collide = false; + if (levels[j]) { + for (k = 0; k < levels[j].length; k++) { + if (segsCollide(levels[j][k], seg)) { + collide = true; + break; + } + } + } + if (collide) { + j++; + } else { + break; + } + } + if (levels[j]) { + levels[j].push(seg); + } else { + levels[j] = [seg]; + } + } + return levels; + } + + function segCmp(a, b) { + return (b.msLength - a.msLength) * 100 + (a.event.start - b.event.start); + } + + function segsCollide(seg1, seg2) { + return seg1.end > seg2.start && seg1.start < seg2.end; + } + + + function SelectionManager(view, initFunc, displayFunc, clearFunc) { + + var t = this; + var selected = false; + var initialElement; + var initialRange; + var start; + var end; + var allDay; + + + t.dragStart = function(ev) { + initFunc(); + start = end = undefined; + initialRange = undefined; + initialElement = ev.currentTarget; + }; + + + t.drag = function(currentStart, currentEnd, currentAllDay) { + if (currentStart) { + var range = [currentStart, currentEnd]; + if (!initialRange) { + initialRange = range; + } + var dates = initialRange.concat(range).sort(cmp); + start = dates[0]; + end = dates[3]; + allDay = currentAllDay; + clearFunc(); + displayFunc(cloneDate(start), cloneDate(end), allDay); + } else { + // called with no arguments + start = end = undefined; + clearFunc(); + } + }; + + + t.dragStop = function(ev) { + if (start) { + if (+initialRange[0] == +start && +initialRange[1] == +end) { + view.trigger('dayClick', initialElement, start, allDay, ev); + } + _select(); + } + }; + + + t.select = function(newStart, newEnd, newAllDay) { + initFunc(); + start = newStart; + end = newEnd; + allDay = newAllDay; + displayFunc(cloneDate(start), cloneDate(end), allDay); + _select(); + }; + + + function _select() { // just set the selected flag, and trigger + selected = true; + view.trigger('select', view, start, end, allDay); + } + + + function unselect() { + if (selected) { + selected = false; + start = end = undefined; + clearFunc(); + view.trigger('unselect', view); + } + } + + t.unselect = unselect; + + } + + + function documentDragHelp(mousemove, mouseup) { + function _mouseup(ev) { + mouseup(ev); + $(document) + .unbind('mousemove', mousemove) + .unbind('mouseup', _mouseup); + } + + $(document) + .mousemove(mousemove) + .mouseup(_mouseup); + } + + + function documentUnselectAuto(view, unselectFunc) { + if (view.option('selectable') && view.option('unselectAuto')) { + $(document).mousedown(function(ev) { + var ignore = view.option('unselectCancel'); + if (ignore) { + if ($(ev.target).parents(ignore).length) { // could be optimized to stop after first match + return; + } + } + unselectFunc(); + }); + } + } + + + /* Date Math + -----------------------------------------------------------------------------*/ + + var DAY_MS = 86400000, + HOUR_MS = 3600000, + MINUTE_MS = 60000; + + function addYears(d, n, keepTime) { + d.setFullYear(d.getFullYear() + n); + if (!keepTime) { + clearTime(d); + } + return d; + } + + function addMonths(d, n, keepTime) { // prevents day overflow/underflow + if (+d) { // prevent infinite looping on invalid dates + var m = d.getMonth() + n, + check = cloneDate(d); + check.setDate(1); + check.setMonth(m); + d.setMonth(m); + if (!keepTime) { + clearTime(d); + } + while (d.getMonth() != check.getMonth()) { + d.setDate(d.getDate() + (d < check ? 1 : -1)); + } + } + return d; + } + + function addDays(d, n, keepTime) { // deals with daylight savings + if (+d) { + var dd = d.getDate() + n, + check = cloneDate(d); + check.setHours(9); // set to middle of day + check.setDate(dd); + d.setDate(dd); + if (!keepTime) { + clearTime(d); + } + fixDate(d, check); + } + return d; + } + + fc.addDays = addDays; + + function fixDate(d, check) { // force d to be on check's YMD, for daylight savings purposes + if (+d) { // prevent infinite looping on invalid dates + while (d.getDate() != check.getDate()) { + d.setTime(+d + (d < check ? 1 : -1) * HOUR_MS); + } + } + } + + function addMinutes(d, n) { + d.setMinutes(d.getMinutes() + n); + return d; + } + + function clearTime(d) { + d.setHours(0); + d.setMinutes(0); + d.setSeconds(0); + d.setMilliseconds(0); + return d; + } + + function cloneDate(d, dontKeepTime) { + if (dontKeepTime) { + return clearTime(new Date(+d)); + } + return new Date(+d); + } + + fc.cloneDate = cloneDate; + + function zeroDate() { // returns a Date with time 00:00:00 and dateOfMonth=1 + var i = 0, d; + do { + d = new Date(1970, i++, 1); + } while (d.getHours()); // != 0 + return d; + } + + function skipWeekend(date, inc, excl) { + inc = inc || 1; + while (!date.getDay() || (excl && date.getDay() == 1 || !excl && date.getDay() == 6)) { + addDays(date, inc); + } + return date; + } + + function dayDiff(d1, d2) { // d1 - d2 + return Math.round((cloneDate(d1, true) - cloneDate(d2, true)) / DAY_MS); + } + + + /* Date Parsing + -----------------------------------------------------------------------------*/ + + var parseDate = fc.parseDate = function(s) { + if (typeof s == 'object') { // already a Date object + return s; + } + if (typeof s == 'number') { // a UNIX timestamp + return new Date(s * 1000); + } + if (typeof s == 'string') { + if (s.match(/^\d+$/)) { // a UNIX timestamp + return new Date(parseInt(s) * 1000); + } + return parseISO8601(s, true) || (s ? new Date(s) : null); + } + // TODO: never return invalid dates (like from new Date()), return null instead + return null; + }; + + var parseISO8601 = fc.parseISO8601 = function(s, ignoreTimezone) { + // derived from http://delete.me.uk/2005/03/iso8601.html + // TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html + var m = s.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?$/); + if (!m) { + return null; + } + var date = new Date(m[1], 0, 1), + check = new Date(m[1], 0, 1, 9, 0), + offset = 0; + if (m[3]) { + date.setMonth(m[3] - 1); + check.setMonth(m[3] - 1); + } + if (m[5]) { + date.setDate(m[5]); + check.setDate(m[5]); + } + fixDate(date, check); + if (m[7]) { + date.setHours(m[7]); + } + if (m[8]) { + date.setMinutes(m[8]); + } + if (m[10]) { + date.setSeconds(m[10]); + } + if (m[12]) { + date.setMilliseconds(Number("0." + m[12]) * 1000); + } + fixDate(date, check); + if (!ignoreTimezone) { + if (m[14]) { + offset = Number(m[16]) * 60 + Number(m[17]); + offset *= m[15] == '-' ? 1 : -1; + } + offset -= date.getTimezoneOffset(); + } + return new Date(+date + (offset * 60 * 1000)); + }; + + var parseTime = fc.parseTime = function(s) { // returns minutes since start of day + if (typeof s == 'number') { // an hour + return s * 60; + } + if (typeof s == 'object') { // a Date object + return s.getHours() * 60 + s.getMinutes(); + } + var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/); + if (m) { + var h = parseInt(m[1]); + if (m[3]) { + h %= 12; + if (m[3].toLowerCase().charAt(0) == 'p') { + h += 12; + } + } + return h * 60 + (m[2] ? parseInt(m[2]) : 0); + } + }; + + + /* Date Formatting + -----------------------------------------------------------------------------*/ + + var formatDate = fc.formatDate = function(date, format, options) { + return formatDates(date, null, format, options); + }; + + var formatDates = fc.formatDates = function(date1, date2, format, options) { + options = options || defaults; + var date = date1, + otherDate = date2, + i, len = format.length, c, + i2, formatter, + res = ''; + for (i = 0; i < len; i++) { + c = format.charAt(i); + if (c == "'") { + for (i2 = i + 1; i2 < len; i2++) { + if (format.charAt(i2) == "'") { + if (date) { + if (i2 == i + 1) { + res += "'"; + } else { + res += format.substring(i + 1, i2); + } + i = i2; + } + break; + } + } + } + else if (c == '(') { + for (i2 = i + 1; i2 < len; i2++) { + if (format.charAt(i2) == ')') { + var subres = formatDate(date, format.substring(i + 1, i2), options); + if (parseInt(subres.replace(/\D/, ''))) { + res += subres; + } + i = i2; + break; + } + } + } + else if (c == '[') { + for (i2 = i + 1; i2 < len; i2++) { + if (format.charAt(i2) == ']') { + var subformat = format.substring(i + 1, i2); + var subres = formatDate(date, subformat, options); + if (subres != formatDate(otherDate, subformat, options)) { + res += subres; + } + i = i2; + break; + } + } + } + else if (c == '{') { + date = date2; + otherDate = date1; + } + else if (c == '}') { + date = date1; + otherDate = date2; + } + else { + for (i2 = len; i2 > i; i2--) { + if (formatter = dateFormatters[format.substring(i, i2)]) { + if (date) { + res += formatter(date, options); + } + i = i2 - 1; + break; + } + } + if (i2 == i) { + if (date) { + res += c; + } + } + } + } + return res; + }; + + var dateFormatters = { + s : function(d) { + return d.getSeconds() + }, + ss : function(d) { + return zeroPad(d.getSeconds()) + }, + m : function(d) { + return d.getMinutes() + }, + mm : function(d) { + return zeroPad(d.getMinutes()) + }, + h : function(d) { + return d.getHours() % 12 || 12 + }, + hh : function(d) { + return zeroPad(d.getHours() % 12 || 12) + }, + H : function(d) { + return d.getHours() + }, + HH : function(d) { + return zeroPad(d.getHours()) + }, + d : function(d) { + return d.getDate() + }, + dd : function(d) { + return zeroPad(d.getDate()) + }, + ddd : function(d, o) { + return o.dayNamesShort[d.getDay()] + }, + dddd: function(d, o) { + return o.dayNames[d.getDay()] + }, + M : function(d) { + return d.getMonth() + 1 + }, + MM : function(d) { + return zeroPad(d.getMonth() + 1) + }, + MMM : function(d, o) { + return o.monthNamesShort[d.getMonth()] + }, + MMMM: function(d, o) { + return o.monthNames[d.getMonth()] + }, + yy : function(d) { + return (d.getFullYear() + '').substring(2) + }, + yyyy: function(d) { + return d.getFullYear() + }, + t : function(d) { + return d.getHours() < 12 ? 'a' : 'p' + }, + tt : function(d) { + return d.getHours() < 12 ? 'am' : 'pm' + }, + T : function(d) { + return d.getHours() < 12 ? 'A' : 'P' + }, + TT : function(d) { + return d.getHours() < 12 ? 'AM' : 'PM' + }, + u : function(d) { + return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") + }, + S : function(d) { + var date = d.getDate(); + if (date > 10 && date < 20) { + return 'th'; + } + return ['st', 'nd', 'rd'][date % 10 - 1] || 'th'; + } + }; + + + /* Element Dimensions + -----------------------------------------------------------------------------*/ + + function setOuterWidth(element, width, includeMargins) { + element.each(function(i, _element) { + _element.style.width = width - hsides(_element, includeMargins) + 'px'; + }); + } + + function setOuterHeight(element, height, includeMargins) { + element.each(function(i, _element) { + _element.style.height = height - vsides(_element, includeMargins) + 'px'; + }); + } + + + function hsides(_element, includeMargins) { + return (parseFloat(jQuery.curCSS(_element, 'paddingLeft', true)) || 0) + + (parseFloat(jQuery.curCSS(_element, 'paddingRight', true)) || 0) + + (parseFloat(jQuery.curCSS(_element, 'borderLeftWidth', true)) || 0) + + (parseFloat(jQuery.curCSS(_element, 'borderRightWidth', true)) || 0) + + (includeMargins ? hmargins(_element) : 0); + } + + function hmargins(_element) { + return (parseFloat(jQuery.curCSS(_element, 'marginLeft', true)) || 0) + + (parseFloat(jQuery.curCSS(_element, 'marginRight', true)) || 0); + } + + function vsides(_element, includeMargins) { + return (parseFloat(jQuery.curCSS(_element, 'paddingTop', true)) || 0) + + (parseFloat(jQuery.curCSS(_element, 'paddingBottom', true)) || 0) + + (parseFloat(jQuery.curCSS(_element, 'borderTopWidth', true)) || 0) + + (parseFloat(jQuery.curCSS(_element, 'borderBottomWidth', true)) || 0) + + (includeMargins ? vmargins(_element) : 0); + } + + function vmargins(_element) { + return (parseFloat(jQuery.curCSS(_element, 'marginTop', true)) || 0) + + (parseFloat(jQuery.curCSS(_element, 'marginBottom', true)) || 0); + } + + + function setMinHeight(element, h) { + h = typeof h == 'number' ? h + 'px' : h; + element[0].style.cssText += ';min-height:' + h + ';_height:' + h; + } + + + /* Position Calculation + -----------------------------------------------------------------------------*/ + // nasty bugs in opera 9.25 + // position()'s top returning incorrectly with TR/TD or elements within TD + + var topBug; + + function topCorrect(tr) { // tr/th/td or anything else + if (topBug !== false) { + var cell; + if (tr.is('th,td')) { + tr = (cell = tr).parent(); + } + if (topBug === undefined && tr.is('tr')) { + topBug = tr.position().top != tr.children().position().top; + } + if (topBug) { + return tr.parent().position().top + (cell ? tr.position().top - cell.position().top : 0); + } + } + return 0; + } + + + /* Hover Matrix + -----------------------------------------------------------------------------*/ + + function HoverMatrix(rowElements, colElements, changeCallback) { + + var t = this, + tops = [], lefts = [], + origRow, origCol, + currRow, currCol, + e; + + $.each(rowElements, function(i, _e) { + e = $(_e); + tops.push(e.offset().top + topCorrect(e)); + }); + tops.push(tops[tops.length - 1] + e.outerHeight()); + $.each(colElements, function(i, _e) { + e = $(_e); + lefts.push(e.offset().left); + }); + lefts.push(lefts[lefts.length - 1] + e.outerWidth()); + + + t.mouse = function(ev) { + var x = ev.pageX; + var y = ev.pageY; + var r, c; + for (r = 0; r < tops.length && y >= tops[r]; r++) { + } + for (c = 0; c < lefts.length && x >= lefts[c]; c++) { + } + r = r >= tops.length ? -1 : r - 1; + c = c >= lefts.length ? -1 : c - 1; + if (r != currRow || c != currCol) { + currRow = r; + currCol = c; + if (r == -1 || c == -1) { + t.cell = null; + } else { + if (origRow === undefined) { + origRow = r; + origCol = c; + } + t.cell = { + row: r, + col: c, + top: tops[r], + left: lefts[c], + width: lefts[c + 1] - lefts[c], + height: tops[r + 1] - tops[r], + origRow: origRow, + origCol: origCol, + isOrig: r == origRow && c == origCol, + rowDelta: r - origRow, + colDelta: c - origCol + }; + } + changeCallback(t.cell); + } + }; + + t.rect = function(row0, col0, row1, col1, originElement) { // row1,col1 are exclusive + var origin = originElement.offset(); + return { + top: tops[row0] - origin.top, + left: lefts[col0] - origin.left, + width: lefts[col1] - lefts[col0], + height: tops[row1] - tops[row0] + }; + }; + + } + + + /* Misc Utils + -----------------------------------------------------------------------------*/ + + var undefined, + dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; + + function zeroPad(n) { + return (n < 10 ? '0' : '') + n; + } + + function smartProperty(obj, name) { // get a camel-cased/namespaced property of an object + if (obj[name] !== undefined) { + return obj[name]; + } + var parts = name.split(/(?=[A-Z])/), + i = parts.length - 1, res; + for (; i >= 0; i--) { + res = obj[parts[i].toLowerCase()]; + if (res !== undefined) { + return res; + } + } + return obj['']; + } + + function htmlEscape(s) { + return s + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/'/g, ''') + .replace(/"/g, '"'); + } + + + function HorizontalPositionCache(getElement) { + + var t = this, + elements = {}, + lefts = {}, + rights = {}; + + function e(i) { + return elements[i] = elements[i] || getElement(i); + } + + t.left = function(i) { + return lefts[i] = lefts[i] === undefined ? e(i).position().left : lefts[i]; + }; + + t.right = function(i) { + return rights[i] = rights[i] === undefined ? t.left(i) + e(i).width() : rights[i]; + }; + + t.clear = function() { + elements = {}; + lefts = {}; + rights = {}; + }; + + } + + + function cssKey(_element) { + return _element.id + '/' + _element.className + '/' + _element.style.cssText.replace(/(^|;)\s*(top|left|width|height)\s*:[^;]*/ig, ''); + } + + + function cmp(a, b) { + return a - b; + } + + + function exclEndDay(event) { + if (event.end) { + return _exclEndDay(event.end, event.allDay); + } else { + return addDays(cloneDate(event.start), 1); + } + } + + function _exclEndDay(end, allDay) { + end = cloneDate(end); + return allDay || end.getHours() || end.getMinutes() ? addDays(end, 1) : end; + } + + + function disableTextSelection(element) { + element + .attr('unselectable', 'on') + .css('MozUserSelect', 'none') + .bind('selectstart.ui', function() { + return false; + }); + } + + /* + function enableTextSelection(element) { + element + .attr('unselectable', 'off') + .css('MozUserSelect', '') + .unbind('selectstart.ui'); + } + */ + + +})(jQuery); \ No newline at end of file diff --git a/samples/jQueryPlugin-sample/src/main/webapp/index.jsp b/samples/jQueryPlugin-sample/src/main/webapp/index.jsp new file mode 100644 index 0000000..d72c769 --- /dev/null +++ b/samples/jQueryPlugin-sample/src/main/webapp/index.jsp @@ -0,0 +1,7 @@ + + + + + + + diff --git a/samples/jQueryPlugin-sample/src/main/webapp/jquery.pnotify.js b/samples/jQueryPlugin-sample/src/main/webapp/jquery.pnotify.js new file mode 100644 index 0000000..6e02376 --- /dev/null +++ b/samples/jQueryPlugin-sample/src/main/webapp/jquery.pnotify.js @@ -0,0 +1,767 @@ +/* + * jQuery Pines Notify (pnotify) Plugin 1.0.0 + * + * Copyright (c) 2009 Hunter Perrin + * + * Licensed (along with all of Pines) under the GNU Affero GPL: + * http://www.gnu.org/licenses/agpl.html + */ + +(function($) { + var history_handle_top, timer; + var body; + var jwindow; + + function $14(element, options) { + var attrFn = { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }; + var o = jQuery(element); + for (var key in options) { + if (jQuery.isFunction(options[key])) { + o.bind(key, options[key]); + } else if (key in attrFn) { + o[key](options[key]); + } else { + o.attr(key, options[key]); + } + } + return o; + } + + $.extend({ + pnotify_remove_all: function () { + var body_data = body.data("pnotify"); + /* POA: Added null-check */ + if (body_data && body_data.length) { + $.each(body_data, function() { + if (this.pnotify_remove) + this.pnotify_remove(); + }); + } + }, + pnotify_position_all: function () { + if (timer) + clearTimeout(timer); + timer = null; + var body_data = body.data("pnotify"); + if (!body_data || !body_data.length) + return; + $.each(body_data, function() { + var s = this.opts.pnotify_stack; + if (!s) return; + if (!s.nextpos1) + s.nextpos1 = s.firstpos1; + if (!s.nextpos2) + s.nextpos2 = s.firstpos2; + if (!s.addpos2) + s.addpos2 = 0; + if (this.css("display") != "none") { + var curpos1, curpos2; + var animate = {}; + // Calculate the current pos1 value. + var csspos1; + switch (s.dir1) { + case "down": + csspos1 = "top"; + break; + case "up": + csspos1 = "bottom"; + break; + case "left": + csspos1 = "right"; + break; + case "right": + csspos1 = "left"; + break; + } + curpos1 = parseInt(this.css(csspos1)); + if (isNaN(curpos1)) + curpos1 = 0; + // Remember the first pos1, so the first visible notice goes there. + if (typeof s.firstpos1 == "undefined") { + s.firstpos1 = curpos1; + s.nextpos1 = s.firstpos1; + } + // Calculate the current pos2 value. + var csspos2; + switch (s.dir2) { + case "down": + csspos2 = "top"; + break; + case "up": + csspos2 = "bottom"; + break; + case "left": + csspos2 = "right"; + break; + case "right": + csspos2 = "left"; + break; + } + curpos2 = parseInt(this.css(csspos2)); + if (isNaN(curpos2)) + curpos2 = 0; + // Remember the first pos2, so the first visible notice goes there. + if (typeof s.firstpos2 == "undefined") { + s.firstpos2 = curpos2; + s.nextpos2 = s.firstpos2; + } + // Check that it's not beyond the viewport edge. + if ((s.dir1 == "down" && s.nextpos1 + this.height() > jwindow.height()) || + (s.dir1 == "up" && s.nextpos1 + this.height() > jwindow.height()) || + (s.dir1 == "left" && s.nextpos1 + this.width() > jwindow.width()) || + (s.dir1 == "right" && s.nextpos1 + this.width() > jwindow.width())) { + // If it is, it needs to go back to the first pos1, and over on pos2. + s.nextpos1 = s.firstpos1; + s.nextpos2 += s.addpos2 + 10; + s.addpos2 = 0; + } + // Animate if we're moving on dir2. + if (s.animation && s.nextpos2 < curpos2) { + switch (s.dir2) { + case "down": + animate.top = s.nextpos2 + "px"; + break; + case "up": + animate.bottom = s.nextpos2 + "px"; + break; + case "left": + animate.right = s.nextpos2 + "px"; + break; + case "right": + animate.left = s.nextpos2 + "px"; + break; + } + } else + this.css(csspos2, s.nextpos2 + "px"); + // Keep track of the widest/tallest notice in the column/row, so we can push the next column/row. + switch (s.dir2) { + case "down": + case "up": + if (this.outerHeight(true) > s.addpos2) + s.addpos2 = this.height(); + break; + case "left": + case "right": + if (this.outerWidth(true) > s.addpos2) + s.addpos2 = this.width(); + break; + } + // Move the notice on dir1. + if (s.nextpos1) { + // Animate if we're moving toward the first pos. + if (s.animation && (curpos1 > s.nextpos1 || animate.top || animate.bottom || animate.right || animate.left)) { + switch (s.dir1) { + case "down": + animate.top = s.nextpos1 + "px"; + break; + case "up": + animate.bottom = s.nextpos1 + "px"; + break; + case "left": + animate.right = s.nextpos1 + "px"; + break; + case "right": + animate.left = s.nextpos1 + "px"; + break; + } + } else + this.css(csspos1, s.nextpos1 + "px"); + } + if (animate.top || animate.bottom || animate.right || animate.left) + this.animate(animate, {duration: 500, queue: false}); + // Calculate the next dir1 position. + switch (s.dir1) { + case "down": + case "up": + s.nextpos1 += this.height() + 10; + break; + case "left": + case "right": + s.nextpos1 += this.width() + 10; + break; + } + } + }); + // Reset the next position data. + $.each(body_data, function() { + var s = this.opts.pnotify_stack; + if (!s) return; + s.nextpos1 = s.firstpos1; + s.nextpos2 = s.firstpos2; + s.addpos2 = 0; + s.animation = true; + }); + }, + pnotify: function(options) { + if (!body) + body = $("body"); + if (!jwindow) + jwindow = $(window); + + var animating; + + // Build main options. + var opts; + if (typeof options != "object") { + opts = $.extend({}, $.pnotify.defaults); + opts.pnotify_text = options; + } else { + opts = $.extend({}, $.pnotify.defaults, options); + if (opts['pnotify_animation'] instanceof Object) { + opts['pnotify_animation'] = $.extend({ + effect_in:$.pnotify.defaults.pnotify_animation, + effect_out:$.pnotify.defaults.pnotify_animation + }, opts['pnotify_animation']); + } + } + + if (opts.pnotify_before_init) { + if (opts.pnotify_before_init(opts) === false) + return null; + } + + // This keeps track of the last element the mouse was over, so + // mouseleave, mouseenter, etc can be called. + var nonblock_last_elem; + // This is used to pass events through the notice if it is non-blocking. + var nonblock_pass = function(e, e_name) { + pnotify.css("display", "none"); + var element_below = document.elementFromPoint(e.clientX, e.clientY); + pnotify.css("display", "block"); + var jelement_below = $(element_below); + var cursor_style = jelement_below.css("cursor"); + pnotify.css("cursor", cursor_style != "auto" ? cursor_style : "default"); + // If the element changed, call mouseenter, mouseleave, etc. + if (!nonblock_last_elem || nonblock_last_elem.get(0) != element_below) { + if (nonblock_last_elem) { + dom_event.call(nonblock_last_elem.get(0), "mouseleave", e.originalEvent); + dom_event.call(nonblock_last_elem.get(0), "mouseout", e.originalEvent); + } + dom_event.call(element_below, "mouseenter", e.originalEvent); + dom_event.call(element_below, "mouseover", e.originalEvent); + } + dom_event.call(element_below, e_name, e.originalEvent); + // Remember the latest element the mouse was over. + nonblock_last_elem = jelement_below; + }; + + // Create our widget. + // Stop animation, reset the removal timer, and show the close + // button when the user mouses over. + var pnotify = $14("
", { + "class": "rf-ny " + opts.pnotify_addclass, + "css": {"display": "none"}, + "mouseenter": function(e) { + if (opts.pnotify_nonblock) e.stopPropagation(); + if (opts.pnotify_mouse_reset && animating == "out") { + // If it's animating out, animate back in really quick. + pnotify.stop(true); + pnotify.css("height", "auto").animate({"width": opts.pnotify_width, "opacity": opts.pnotify_nonblock ? opts.pnotify_nonblock_opacity : opts.pnotify_opacity}, "fast"); + } else if (opts.pnotify_nonblock && animating != "out") { + // If it's non-blocking, animate to the other opacity. + pnotify.animate({"opacity": opts.pnotify_nonblock_opacity}, "fast"); + } + if (opts.pnotify_hide && opts.pnotify_mouse_reset) pnotify.pnotify_cancel_remove(); + if (opts.pnotify_closer && !opts.pnotify_nonblock) pnotify.closer.show(); + }, + "mouseleave": function(e) { + if (opts.pnotify_nonblock) e.stopPropagation(); + nonblock_last_elem = null; + pnotify.css("cursor", "auto"); + if (opts.pnotify_nonblock && animating != "out") + pnotify.animate({"opacity": opts.pnotify_opacity}, "fast"); + if (opts.pnotify_hide && opts.pnotify_mouse_reset) pnotify.pnotify_queue_remove(); + pnotify.closer.hide(); + $.pnotify_position_all(); + }, + "mouseover": function(e) { + if (opts.pnotify_nonblock) e.stopPropagation(); + }, + "mouseout": function(e) { + if (opts.pnotify_nonblock) e.stopPropagation(); + }, + "mousemove": function(e) { + if (opts.pnotify_nonblock) { + e.stopPropagation(); + nonblock_pass(e, "onmousemove"); + } + }, + "mousedown": function(e) { + if (opts.pnotify_nonblock) { + e.stopPropagation(); + e.preventDefault(); + nonblock_pass(e, "onmousedown"); + } + }, + "mouseup": function(e) { + if (opts.pnotify_nonblock) { + e.stopPropagation(); + e.preventDefault(); + nonblock_pass(e, "onmouseup"); + } + }, + "click": function(e) { + if (opts.pnotify_nonblock) { + e.stopPropagation(); + nonblock_pass(e, "onclick"); + } + }, + "dblclick": function(e) { + if (opts.pnotify_nonblock) { + e.stopPropagation(); + nonblock_pass(e, "ondblclick"); + } + } + }); + pnotify.opts = opts; + // Create a drop shadow. + if (opts.pnotify_shadow && !$.browser.msie) + pnotify.shadow_container = $14("
", {"class": "rf-ny-sh"}).prependTo(pnotify); + // Create a container for the notice contents. + pnotify.container = $14("
", {"class": "rf-ny-co"}) + .appendTo(pnotify); + + pnotify.pnotify_version = "1.0.0"; + + // This function is for updating the notice. + pnotify.pnotify = function(options) { + // Update the notice. + var old_opts = opts; + if (typeof options == "string") + opts.pnotify_text = options; + else + opts = $.extend({}, opts, options); + pnotify.opts = opts; + // Update the shadow. + if (opts.pnotify_shadow != old_opts.pnotify_shadow) { + if (opts.pnotify_shadow && !$.browser.msie) + pnotify.shadow_container = $14("
", {"class": "rf-ny-sh"}).prependTo(pnotify); + else + pnotify.children(".rf-ny-sh").remove(); + } + // Update the additional classes. + if (opts.pnotify_addclass === false) + pnotify.removeClass(old_opts.pnotify_addclass); + else if (opts.pnotify_addclass !== old_opts.pnotify_addclass) + pnotify.removeClass(old_opts.pnotify_addclass).addClass(opts.pnotify_addclass); + // Update the title. + if (opts.pnotify_title === false) + pnotify.title_container.hide("fast"); + else if (opts.pnotify_title !== old_opts.pnotify_title) + pnotify.title_container.html(opts.pnotify_title).show(200); + // Update the text. + if (opts.pnotify_text === false) { + pnotify.text_container.hide("fast"); + } else if (opts.pnotify_text !== old_opts.pnotify_text) { + if (opts.pnotify_insert_brs) + opts.pnotify_text = opts.pnotify_text.replace(/\n/g, "
"); + pnotify.text_container.html(opts.pnotify_text).show(200); + } + pnotify.pnotify_history = opts.pnotify_history; + // Change the notice type. + if (opts.pnotify_type != old_opts.pnotify_type) + pnotify.container.toggleClass("rf-ny-co rf-ny-co-hover"); + if ((opts.pnotify_notice_icon != old_opts.pnotify_notice_icon && opts.pnotify_type == "notice") || + (opts.pnotify_error_icon != old_opts.pnotify_error_icon && opts.pnotify_type == "error") || + (opts.pnotify_type != old_opts.pnotify_type)) { + // Remove any old icon. + pnotify.container.find("div.rf-ny-ic").remove(); + // if ((opts.pnotify_error_icon && opts.pnotify_type == "error") || (opts.pnotify_notice_icon)) { + // Build the new icon. + $14("
", {"class": "rf-ny-ic"}) + .append($14("", {"class": opts.pnotify_type == "error" ? opts.pnotify_error_icon : opts.pnotify_notice_icon})) + .prependTo(pnotify.container); + // } + } + // Update the width. + if (opts.pnotify_width !== old_opts.pnotify_width) + pnotify.animate({width: opts.pnotify_width}); + // Update the minimum height. + if (opts.pnotify_min_height !== old_opts.pnotify_min_height) + pnotify.container.animate({minHeight: opts.pnotify_min_height}); + // Update the opacity. + if (opts.pnotify_opacity !== old_opts.pnotify_opacity) + pnotify.fadeTo(opts.pnotify_animate_speed, opts.pnotify_opacity); + if (!opts.pnotify_hide) + pnotify.pnotify_cancel_remove(); + else if (!old_opts.pnotify_hide) + pnotify.pnotify_queue_remove(); + pnotify.pnotify_queue_position(); + return pnotify; + }; + + // Queue the position function so it doesn't run repeatedly and use + // up resources. + pnotify.pnotify_queue_position = function() { + if (timer) + clearTimeout(timer); + timer = setTimeout($.pnotify_position_all, 10); + }; + + // Display the notice. + pnotify.pnotify_display = function() { + // If the notice is not in the DOM, append it. + if (!pnotify.parent().length) + pnotify.appendTo(body); + // Run callback. + if (opts.pnotify_before_open) { + if (opts.pnotify_before_open(pnotify) === false) + return; + } + pnotify.pnotify_queue_position(); + // First show it, then set its opacity, then hide it. + if (opts.pnotify_animation == "fade" || opts.pnotify_animation.effect_in == "fade") { + // If it's fading in, it should start at 0. + pnotify.show().fadeTo(0, 0).hide(); + } else { + // Or else it should be set to the opacity. + if (opts.pnotify_opacity != 1) + pnotify.show().fadeTo(0, opts.pnotify_opacity).hide(); + } + pnotify.animate_in(function() { + if (opts.pnotify_after_open) + opts.pnotify_after_open(pnotify); + + pnotify.pnotify_queue_position(); + + // Now set it to hide. + if (opts.pnotify_hide) + pnotify.pnotify_queue_remove(); + }); + }; + + // Remove the notice. + pnotify.pnotify_remove = function() { + if (pnotify.timer) { + window.clearTimeout(pnotify.timer); + pnotify.timer = null; + } + // Run callback. + if (opts.pnotify_before_close) { + if (opts.pnotify_before_close(pnotify) === false) + return; + } + pnotify.animate_out(function() { + if (opts.pnotify_after_close) { + if (opts.pnotify_after_close(pnotify) === false) + return; + } + pnotify.pnotify_queue_position(); + // If we're supposed to remove the notice from the DOM, do it. + if (opts.pnotify_remove) { + if (opts.pnotify_history) { + if (pnotify[0].parentNode != null) { + pnotify[0].parentNode.removeChild(pnotify[0]); + } + } else { + pnotify.remove(); + } + } + }); + }; + + // Animate the notice in. + pnotify.animate_in = function(callback) { + // Declare that the notice is animating in. (Or has completed animating in.) + animating = "in"; + var animation; + if (typeof opts.pnotify_animation.effect_in != "undefined") + animation = opts.pnotify_animation.effect_in; + else + animation = opts.pnotify_animation; + if (animation == "none") { + pnotify.show(); + callback(); + } else if (animation == "show") + pnotify.show(opts.pnotify_animate_speed, callback); + else if (animation == "fade") + pnotify.show().fadeTo(opts.pnotify_animate_speed, opts.pnotify_opacity, callback); + else if (animation == "slide") + pnotify.slideDown(opts.pnotify_animate_speed, callback); + else if (typeof animation == "function") + animation("in", callback, pnotify); + else if (pnotify.effect) + pnotify.effect(animation, {}, opts.pnotify_animate_speed, callback); + }; + + // Animate the notice out. + pnotify.animate_out = function(callback) { + // Declare that the notice is animating out. (Or has completed animating out.) + animating = "out"; + var animation; + if (typeof opts.pnotify_animation.effect_out != "undefined") + animation = opts.pnotify_animation.effect_out; + else + animation = opts.pnotify_animation; + if (animation == "none") { + pnotify.hide(); + callback(); + } else if (animation == "show") + pnotify.hide(opts.pnotify_animate_speed, callback); + else if (animation == "fade") + pnotify.fadeOut(opts.pnotify_animate_speed, callback); + else if (animation == "slide") + pnotify.slideUp(opts.pnotify_animate_speed, callback); + else if (typeof animation == "function") + animation("out", callback, pnotify); + else if (pnotify.effect) + pnotify.effect(animation, {}, opts.pnotify_animate_speed, callback); + }; + + // Cancel any pending removal timer. + pnotify.pnotify_cancel_remove = function() { + if (pnotify.timer) + window.clearTimeout(pnotify.timer); + }; + + // Queue a removal timer. + pnotify.pnotify_queue_remove = function() { + // Cancel any current removal timer. + pnotify.pnotify_cancel_remove(); + pnotify.timer = window.setTimeout(function() { + pnotify.pnotify_remove(); + }, (isNaN(opts.pnotify_delay) ? 0 : opts.pnotify_delay)); + }; + + // Provide a button to close the notice. + pnotify.closer = $14("
", { + "class": "rf-ny-cl", + "css": {"cursor": "pointer", "display": "none"}, + "click": function() { + pnotify.pnotify_remove(); + pnotify.closer.hide(); + } + }) + .append($14("", {"class": "rf-ny-cl-ic"})) + .appendTo(pnotify.container); + + // Add the appropriate icon. + // if ((opts.pnotify_error_icon && opts.pnotify_type == "error") || (opts.pnotify_notice_icon)) { + $14("
", {"class": "rf-ny-ic"}) + .append($14("", {"class": opts.pnotify_type == "error" ? opts.pnotify_error_icon : opts.pnotify_notice_icon})) + .appendTo(pnotify.container); + // } + + // Add a title. + pnotify.title_container = $14("
", { + "class": "rf-ny-tl", + "html": opts.pnotify_title + }) + .appendTo(pnotify.container); + if (opts.pnotify_title === false) + pnotify.title_container.hide(); + + // Replace new lines with HTML line breaks. + if (opts.pnotify_insert_brs && typeof opts.pnotify_text == "string") + opts.pnotify_text = opts.pnotify_text.replace(/\n/g, "
"); + // Add text. + pnotify.text_container = $14("
", { + "class": "rf-ny-te", + "html": opts.pnotify_text + }) + .appendTo(pnotify.container); + if (opts.pnotify_text === false) + pnotify.text_container.hide(); + + // Set width and min height. + if (typeof opts.pnotify_width == "string") + pnotify.css("width", opts.pnotify_width); + if (typeof opts.pnotify_min_height == "string") + pnotify.container.css("min-height", opts.pnotify_min_height); + + // The history variable controls whether the notice gets redisplayed + // by the history pull down. + pnotify.pnotify_history = opts.pnotify_history; + + // Add the notice to the notice array. + var body_data = body.data("pnotify"); + if (body_data == null || typeof body_data != "object") + body_data = []; + if (opts.pnotify_stack.push == "top") + body_data = $.merge([pnotify], body_data); + else + body_data = $.merge(body_data, [pnotify]); + body.data("pnotify", body_data); + + // Run callback. + if (opts.pnotify_after_init) + opts.pnotify_after_init(pnotify); + + if (opts.pnotify_history) { + // If there isn't a history pull down, create one. + var body_history = body.data("pnotify_history"); + if (typeof body_history == "undefined") { + body_history = $14("
", { + "class": "rf-ny-hc", + "mouseleave": function() { + body_history.animate({top: "-" + history_handle_top + "px"}, {duration: 100, queue: false}); + } + }) + .append($14("
", {"class": "rf-ny-hh", "text": "Redisplay"})) + .append($14("