diff --git a/posterita/src/web/css/js-calendar/calendar-win2k-1.css b/posterita/src/web/css/js-calendar/calendar-win2k-1.css
new file mode 100644
index 0000000000..e7c888c6aa
--- /dev/null
+++ b/posterita/src/web/css/js-calendar/calendar-win2k-1.css
@@ -0,0 +1,296 @@
+/**
+ * Product: Posterita Web-Based POS and Adempiere Plugin
+ * Copyright (C) 2007 Posterita Ltd
+ * This file is part of POSterita
+ *
+ * POSterita is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ @author tamak
+ */
+
+
+/* The main calendar widget. DIV containing a table. */
+
+.calendar {
+ position: relative;
+ display: none;
+ border-top: 2px solid #fff;
+ border-right: 2px solid #000;
+ border-bottom: 2px solid #000;
+ border-left: 2px solid #fff;
+ font-size: 11px;
+ color: #000;
+ cursor: default;
+ background: #d4d0c8;
+ font-family: tahoma,verdana,sans-serif;
+}
+
+.calendar table {
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+ font-size: 11px;
+ color: #000;
+ cursor: default;
+ background: #d4d0c8;
+ font-family: tahoma,verdana,sans-serif;
+}
+
+/* Header part -- contains navigation buttons and day names. */
+
+.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */
+ text-align: center;
+ padding: 1px;
+ border-top: 1px solid #fff;
+ border-right: 1px solid #000;
+ border-bottom: 1px solid #000;
+ border-left: 1px solid #fff;
+}
+
+.calendar .nav {
+ background: transparent url(menuarrow.gif) no-repeat 100% 100%;
+}
+
+.calendar thead .title { /* This holds the current "month, year" */
+ font-weight: bold;
+ padding: 1px;
+ border: 1px solid #000;
+ background: #848078;
+ color: #fff;
+ text-align: center;
+}
+
+.calendar thead .headrow { /* Row
in footer (only one right now) */
+}
+
+.calendar tfoot .ttip { /* Tooltip (status bar) cell */
+ background: #f4f0e8;
+ padding: 1px;
+ border: 1px solid #000;
+ background: #848078;
+ color: #fff;
+ text-align: center;
+}
+
+.calendar tfoot .hilite { /* Hover style for buttons in footer */
+ border-top: 1px solid #fff;
+ border-right: 1px solid #000;
+ border-bottom: 1px solid #000;
+ border-left: 1px solid #fff;
+ padding: 1px;
+ background: #e4e0d8;
+}
+
+.calendar tfoot .active { /* Active (pressed) style for buttons in footer */
+ padding: 2px 0px 0px 2px;
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+}
+
+/* Combo boxes (menus that display months/years for direct selection) */
+
+.calendar .combo {
+ position: absolute;
+ display: none;
+ width: 4em;
+ top: 0px;
+ left: 0px;
+ cursor: default;
+ border-top: 1px solid #fff;
+ border-right: 1px solid #000;
+ border-bottom: 1px solid #000;
+ border-left: 1px solid #fff;
+ background: #e4e0d8;
+ font-size: 90%;
+ padding: 1px;
+ z-index: 100;
+}
+
+.calendar .combo .label,
+.calendar .combo .label-IEfix {
+ text-align: center;
+ padding: 1px;
+}
+
+.calendar .combo .label-IEfix {
+ width: 4em;
+}
+
+.calendar .combo .active {
+ background: #c4c0b8;
+ padding: 0px;
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+}
+
+.calendar .combo .hilite {
+ background: #048;
+ color: #fea;
+}
+
+.calendar td.time {
+ border-top: 1px solid #000;
+ padding: 1px 0px;
+ text-align: center;
+ background-color: #f4f0e8;
+}
+
+.calendar td.time .hour,
+.calendar td.time .minute,
+.calendar td.time .ampm {
+ padding: 0px 3px 0px 4px;
+ border: 1px solid #889;
+ font-weight: bold;
+ background-color: #fff;
+}
+
+.calendar td.time .ampm {
+ text-align: center;
+}
+
+.calendar td.time .colon {
+ padding: 0px 2px 0px 3px;
+ font-weight: bold;
+}
+
+.calendar td.time span.hilite {
+ border-color: #000;
+ background-color: #766;
+ color: #fff;
+}
+
+.calendar td.time span.active {
+ border-color: #f00;
+ background-color: #000;
+ color: #0f0;
+}
diff --git a/posterita/src/web/images/1.jpg b/posterita/src/web/images/1.jpg
new file mode 100644
index 0000000000..242a15d3f8
Binary files /dev/null and b/posterita/src/web/images/1.jpg differ
diff --git a/posterita/src/web/images/2.jpg b/posterita/src/web/images/2.jpg
new file mode 100644
index 0000000000..fab96765c0
Binary files /dev/null and b/posterita/src/web/images/2.jpg differ
diff --git a/posterita/src/web/images/BackPriv1.jpg b/posterita/src/web/images/BackPriv1.jpg
new file mode 100644
index 0000000000..ebae0e3504
Binary files /dev/null and b/posterita/src/web/images/BackPriv1.jpg differ
diff --git a/posterita/src/web/images/CARTE DE FIDELITE_110906.jpg b/posterita/src/web/images/CARTE DE FIDELITE_110906.jpg
new file mode 100644
index 0000000000..78e2121571
Binary files /dev/null and b/posterita/src/web/images/CARTE DE FIDELITE_110906.jpg differ
diff --git a/posterita/src/web/images/Fidelite.jpg b/posterita/src/web/images/Fidelite.jpg
new file mode 100644
index 0000000000..d0ca7bc6d8
Binary files /dev/null and b/posterita/src/web/images/Fidelite.jpg differ
diff --git a/posterita/src/web/images/PRIVELEDGE CARD.jpg b/posterita/src/web/images/PRIVELEDGE CARD.jpg
new file mode 100644
index 0000000000..01faeea7d4
Binary files /dev/null and b/posterita/src/web/images/PRIVELEDGE CARD.jpg differ
diff --git a/posterita/src/web/images/backPriv2.jpg b/posterita/src/web/images/backPriv2.jpg
new file mode 100644
index 0000000000..d7bd968425
Binary files /dev/null and b/posterita/src/web/images/backPriv2.jpg differ
diff --git a/posterita/src/web/images/calendar_icon.png b/posterita/src/web/images/calendar_icon.png
new file mode 100644
index 0000000000..92cb163d12
Binary files /dev/null and b/posterita/src/web/images/calendar_icon.png differ
diff --git a/posterita/src/web/images/frontPriv1.jpg b/posterita/src/web/images/frontPriv1.jpg
new file mode 100644
index 0000000000..3c78da8850
Binary files /dev/null and b/posterita/src/web/images/frontPriv1.jpg differ
diff --git a/posterita/src/web/images/frontPriv2.jpg b/posterita/src/web/images/frontPriv2.jpg
new file mode 100644
index 0000000000..66b3a4a045
Binary files /dev/null and b/posterita/src/web/images/frontPriv2.jpg differ
diff --git a/posterita/src/web/images/ico_printer.gif b/posterita/src/web/images/ico_printer.gif
new file mode 100644
index 0000000000..de27b46458
Binary files /dev/null and b/posterita/src/web/images/ico_printer.gif differ
diff --git a/posterita/src/web/images/image1.jpg b/posterita/src/web/images/image1.jpg
new file mode 100644
index 0000000000..0656b6f618
Binary files /dev/null and b/posterita/src/web/images/image1.jpg differ
diff --git a/posterita/src/web/images/image2.jpg b/posterita/src/web/images/image2.jpg
new file mode 100644
index 0000000000..9e23e2b11f
Binary files /dev/null and b/posterita/src/web/images/image2.jpg differ
diff --git a/posterita/src/web/images/image3.jpg b/posterita/src/web/images/image3.jpg
new file mode 100644
index 0000000000..3794a16cec
Binary files /dev/null and b/posterita/src/web/images/image3.jpg differ
diff --git a/posterita/src/web/images/image31.jpg b/posterita/src/web/images/image31.jpg
new file mode 100644
index 0000000000..34f9814b19
Binary files /dev/null and b/posterita/src/web/images/image31.jpg differ
diff --git a/posterita/src/web/images/logo.gif b/posterita/src/web/images/logo.gif
new file mode 100644
index 0000000000..d990762f63
Binary files /dev/null and b/posterita/src/web/images/logo.gif differ
diff --git a/posterita/src/web/images/logo.jpg b/posterita/src/web/images/logo.jpg
new file mode 100644
index 0000000000..77c8b5142c
Binary files /dev/null and b/posterita/src/web/images/logo.jpg differ
diff --git a/posterita/src/web/images/logo1.gif b/posterita/src/web/images/logo1.gif
new file mode 100644
index 0000000000..a41a8dba4e
Binary files /dev/null and b/posterita/src/web/images/logo1.gif differ
diff --git a/posterita/src/web/images/logo2.gif b/posterita/src/web/images/logo2.gif
new file mode 100644
index 0000000000..16a5c23764
Binary files /dev/null and b/posterita/src/web/images/logo2.gif differ
diff --git a/posterita/src/web/images/pc.png b/posterita/src/web/images/pc.png
new file mode 100644
index 0000000000..dadf14881c
Binary files /dev/null and b/posterita/src/web/images/pc.png differ
diff --git a/posterita/src/web/images/pos/-----.gif b/posterita/src/web/images/pos/-----.gif
new file mode 100644
index 0000000000..2030a5f450
Binary files /dev/null and b/posterita/src/web/images/pos/-----.gif differ
diff --git a/posterita/src/web/images/pos/ADMINISTRATION.gif b/posterita/src/web/images/pos/ADMINISTRATION.gif
new file mode 100644
index 0000000000..62bf4be6a9
Binary files /dev/null and b/posterita/src/web/images/pos/ADMINISTRATION.gif differ
diff --git a/posterita/src/web/images/pos/BULLET_reddd.gif b/posterita/src/web/images/pos/BULLET_reddd.gif
new file mode 100644
index 0000000000..88c72711b7
Binary files /dev/null and b/posterita/src/web/images/pos/BULLET_reddd.gif differ
diff --git a/posterita/src/web/images/pos/Contacts 1.gif b/posterita/src/web/images/pos/Contacts 1.gif
new file mode 100644
index 0000000000..e31cfab365
Binary files /dev/null and b/posterita/src/web/images/pos/Contacts 1.gif differ
diff --git a/posterita/src/web/images/pos/Contacts 2.gif b/posterita/src/web/images/pos/Contacts 2.gif
new file mode 100644
index 0000000000..b750e7063f
Binary files /dev/null and b/posterita/src/web/images/pos/Contacts 2.gif differ
diff --git a/posterita/src/web/images/pos/ImageBack.jpg b/posterita/src/web/images/pos/ImageBack.jpg
new file mode 100644
index 0000000000..394390b137
Binary files /dev/null and b/posterita/src/web/images/pos/ImageBack.jpg differ
diff --git a/posterita/src/web/images/pos/LOGOUT.gif b/posterita/src/web/images/pos/LOGOUT.gif
new file mode 100644
index 0000000000..72a91a124c
Binary files /dev/null and b/posterita/src/web/images/pos/LOGOUT.gif differ
diff --git a/posterita/src/web/images/pos/ORDER.gif b/posterita/src/web/images/pos/ORDER.gif
new file mode 100644
index 0000000000..8fe689cd80
Binary files /dev/null and b/posterita/src/web/images/pos/ORDER.gif differ
diff --git a/posterita/src/web/images/pos/POS-LOGIN.gif b/posterita/src/web/images/pos/POS-LOGIN.gif
new file mode 100644
index 0000000000..6555442b2c
Binary files /dev/null and b/posterita/src/web/images/pos/POS-LOGIN.gif differ
diff --git a/posterita/src/web/images/pos/POS-LOGIN_01.gif b/posterita/src/web/images/pos/POS-LOGIN_01.gif
new file mode 100644
index 0000000000..204fd269ea
Binary files /dev/null and b/posterita/src/web/images/pos/POS-LOGIN_01.gif differ
diff --git a/posterita/src/web/images/pos/Printer 2.gif b/posterita/src/web/images/pos/Printer 2.gif
new file mode 100644
index 0000000000..134d4355b3
Binary files /dev/null and b/posterita/src/web/images/pos/Printer 2.gif differ
diff --git a/posterita/src/web/images/pos/REPORTS.gif b/posterita/src/web/images/pos/REPORTS.gif
new file mode 100644
index 0000000000..add87aa820
Binary files /dev/null and b/posterita/src/web/images/pos/REPORTS.gif differ
diff --git a/posterita/src/web/images/pos/Users 1.gif b/posterita/src/web/images/pos/Users 1.gif
new file mode 100644
index 0000000000..255d53f064
Binary files /dev/null and b/posterita/src/web/images/pos/Users 1.gif differ
diff --git a/posterita/src/web/images/pos/button-login.gif b/posterita/src/web/images/pos/button-login.gif
new file mode 100644
index 0000000000..ac11f25cda
Binary files /dev/null and b/posterita/src/web/images/pos/button-login.gif differ
diff --git a/posterita/src/web/images/pos/button_add to cart.gif b/posterita/src/web/images/pos/button_add to cart.gif
new file mode 100644
index 0000000000..0638845afe
Binary files /dev/null and b/posterita/src/web/images/pos/button_add to cart.gif differ
diff --git a/posterita/src/web/images/pos/button_advanced.gif b/posterita/src/web/images/pos/button_advanced.gif
new file mode 100644
index 0000000000..3537dcf733
Binary files /dev/null and b/posterita/src/web/images/pos/button_advanced.gif differ
diff --git a/posterita/src/web/images/pos/button_card.gif b/posterita/src/web/images/pos/button_card.gif
new file mode 100644
index 0000000000..7413c289b2
Binary files /dev/null and b/posterita/src/web/images/pos/button_card.gif differ
diff --git a/posterita/src/web/images/pos/button_cash.gif b/posterita/src/web/images/pos/button_cash.gif
new file mode 100644
index 0000000000..720026d03d
Binary files /dev/null and b/posterita/src/web/images/pos/button_cash.gif differ
diff --git a/posterita/src/web/images/pos/button_checkout.gif b/posterita/src/web/images/pos/button_checkout.gif
new file mode 100644
index 0000000000..f5e023e5f6
Binary files /dev/null and b/posterita/src/web/images/pos/button_checkout.gif differ
diff --git a/posterita/src/web/images/pos/button_cheque.gif b/posterita/src/web/images/pos/button_cheque.gif
new file mode 100644
index 0000000000..6c286714bb
Binary files /dev/null and b/posterita/src/web/images/pos/button_cheque.gif differ
diff --git a/posterita/src/web/images/pos/button_newcustomer.gif b/posterita/src/web/images/pos/button_newcustomer.gif
new file mode 100644
index 0000000000..3a20ccd4cc
Binary files /dev/null and b/posterita/src/web/images/pos/button_newcustomer.gif differ
diff --git a/posterita/src/web/images/pos/buttons/Thumbs.db b/posterita/src/web/images/pos/buttons/Thumbs.db
new file mode 100644
index 0000000000..99b1dbba55
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/Thumbs.db differ
diff --git a/posterita/src/web/images/pos/buttons/browser.gif b/posterita/src/web/images/pos/buttons/browser.gif
new file mode 100644
index 0000000000..561173d83a
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/browser.gif differ
diff --git a/posterita/src/web/images/pos/buttons/bullet_red.gif b/posterita/src/web/images/pos/buttons/bullet_red.gif
new file mode 100644
index 0000000000..88c72711b7
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/bullet_red.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_addtocart.gif b/posterita/src/web/images/pos/buttons/button_addtocart.gif
new file mode 100644
index 0000000000..0638845afe
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_addtocart.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_administration.gif b/posterita/src/web/images/pos/buttons/button_administration.gif
new file mode 100644
index 0000000000..62bf4be6a9
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_administration.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_advanced.gif b/posterita/src/web/images/pos/buttons/button_advanced.gif
new file mode 100644
index 0000000000..3537dcf733
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_advanced.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_background.gif b/posterita/src/web/images/pos/buttons/button_background.gif
new file mode 100644
index 0000000000..c6beb23d71
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_background.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_barchart.gif b/posterita/src/web/images/pos/buttons/button_barchart.gif
new file mode 100644
index 0000000000..2171c739fa
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_barchart.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_blank.gif b/posterita/src/web/images/pos/buttons/button_blank.gif
new file mode 100644
index 0000000000..26707e2aed
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_blank.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_card.gif b/posterita/src/web/images/pos/buttons/button_card.gif
new file mode 100644
index 0000000000..7413c289b2
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_card.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_cart.jpeg b/posterita/src/web/images/pos/buttons/button_cart.jpeg
new file mode 100644
index 0000000000..d88f4c78c2
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_cart.jpeg differ
diff --git a/posterita/src/web/images/pos/buttons/button_cash.gif b/posterita/src/web/images/pos/buttons/button_cash.gif
new file mode 100644
index 0000000000..720026d03d
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_cash.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_checkout.gif b/posterita/src/web/images/pos/buttons/button_checkout.gif
new file mode 100644
index 0000000000..f5e023e5f6
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_checkout.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_cheque.gif b/posterita/src/web/images/pos/buttons/button_cheque.gif
new file mode 100644
index 0000000000..6c286714bb
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_cheque.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_complete.gif b/posterita/src/web/images/pos/buttons/button_complete.gif
new file mode 100644
index 0000000000..9980c9268b
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_complete.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_continue.gif b/posterita/src/web/images/pos/buttons/button_continue.gif
new file mode 100644
index 0000000000..8a00291315
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_continue.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_delete.gif b/posterita/src/web/images/pos/buttons/button_delete.gif
new file mode 100644
index 0000000000..8de32e1fcb
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_delete.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_edit.gif b/posterita/src/web/images/pos/buttons/button_edit.gif
new file mode 100644
index 0000000000..e5df0f5a91
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_edit.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_import.gif b/posterita/src/web/images/pos/buttons/button_import.gif
new file mode 100644
index 0000000000..b73a6b7ef3
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_import.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_login.gif b/posterita/src/web/images/pos/buttons/button_login.gif
new file mode 100644
index 0000000000..ac11f25cda
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_login.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_logout.gif b/posterita/src/web/images/pos/buttons/button_logout.gif
new file mode 100644
index 0000000000..72a91a124c
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_logout.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_middle.gif b/posterita/src/web/images/pos/buttons/button_middle.gif
new file mode 100644
index 0000000000..be4927b44e
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_middle.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_minus.gif b/posterita/src/web/images/pos/buttons/button_minus.gif
new file mode 100644
index 0000000000..2030a5f450
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_minus.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_mixed.gif b/posterita/src/web/images/pos/buttons/button_mixed.gif
new file mode 100644
index 0000000000..e5146729cb
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_mixed.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_newcustomer.gif b/posterita/src/web/images/pos/buttons/button_newcustomer.gif
new file mode 100644
index 0000000000..3a20ccd4cc
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_newcustomer.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_newnote.gif b/posterita/src/web/images/pos/buttons/button_newnote.gif
new file mode 100644
index 0000000000..ea2c0cb576
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_newnote.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_neworder.gif b/posterita/src/web/images/pos/buttons/button_neworder.gif
new file mode 100644
index 0000000000..c1a9d54039
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_neworder.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_order.gif b/posterita/src/web/images/pos/buttons/button_order.gif
new file mode 100644
index 0000000000..8fe689cd80
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_order.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_piechart.gif b/posterita/src/web/images/pos/buttons/button_piechart.gif
new file mode 100644
index 0000000000..bdeb6b06fe
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_piechart.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_plus.gif b/posterita/src/web/images/pos/buttons/button_plus.gif
new file mode 100644
index 0000000000..f14e73fe83
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_plus.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_refresh.gif b/posterita/src/web/images/pos/buttons/button_refresh.gif
new file mode 100644
index 0000000000..554c9e7a7f
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_refresh.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_reports.gif b/posterita/src/web/images/pos/buttons/button_reports.gif
new file mode 100644
index 0000000000..add87aa820
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_reports.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_save.gif b/posterita/src/web/images/pos/buttons/button_save.gif
new file mode 100644
index 0000000000..6c18606669
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_save.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_saveascsv.gif b/posterita/src/web/images/pos/buttons/button_saveascsv.gif
new file mode 100644
index 0000000000..9f099f2905
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_saveascsv.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_search.gif b/posterita/src/web/images/pos/buttons/button_search.gif
new file mode 100644
index 0000000000..bcebc9db78
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_search.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_selectall.gif b/posterita/src/web/images/pos/buttons/button_selectall.gif
new file mode 100644
index 0000000000..03bb547870
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_selectall.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_submit.gif b/posterita/src/web/images/pos/buttons/button_submit.gif
new file mode 100644
index 0000000000..d9b2fcf53c
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_submit.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_tabular.gif b/posterita/src/web/images/pos/buttons/button_tabular.gif
new file mode 100644
index 0000000000..dc93499596
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_tabular.gif differ
diff --git a/posterita/src/web/images/pos/buttons/button_timeseries.gif b/posterita/src/web/images/pos/buttons/button_timeseries.gif
new file mode 100644
index 0000000000..1943bc0f2e
Binary files /dev/null and b/posterita/src/web/images/pos/buttons/button_timeseries.gif differ
diff --git a/posterita/src/web/images/pos/charts/barchart_icon.gif b/posterita/src/web/images/pos/charts/barchart_icon.gif
new file mode 100644
index 0000000000..417a1ae334
Binary files /dev/null and b/posterita/src/web/images/pos/charts/barchart_icon.gif differ
diff --git a/posterita/src/web/images/pos/charts/piechart_icon.gif b/posterita/src/web/images/pos/charts/piechart_icon.gif
new file mode 100644
index 0000000000..4a8b0bb799
Binary files /dev/null and b/posterita/src/web/images/pos/charts/piechart_icon.gif differ
diff --git a/posterita/src/web/images/pos/charts/tabular_icon.gif b/posterita/src/web/images/pos/charts/tabular_icon.gif
new file mode 100644
index 0000000000..e90b927886
Binary files /dev/null and b/posterita/src/web/images/pos/charts/tabular_icon.gif differ
diff --git a/posterita/src/web/images/pos/charts/timeseries_icon.gif b/posterita/src/web/images/pos/charts/timeseries_icon.gif
new file mode 100644
index 0000000000..c7409ab9bf
Binary files /dev/null and b/posterita/src/web/images/pos/charts/timeseries_icon.gif differ
diff --git a/posterita/src/web/images/pos/cross.gif b/posterita/src/web/images/pos/cross.gif
new file mode 100644
index 0000000000..84ef48f084
Binary files /dev/null and b/posterita/src/web/images/pos/cross.gif differ
diff --git a/posterita/src/web/images/pos/delprod.gif b/posterita/src/web/images/pos/delprod.gif
new file mode 100644
index 0000000000..e03a79165b
Binary files /dev/null and b/posterita/src/web/images/pos/delprod.gif differ
diff --git a/posterita/src/web/images/pos/error.gif b/posterita/src/web/images/pos/error.gif
new file mode 100644
index 0000000000..8533edbb08
Binary files /dev/null and b/posterita/src/web/images/pos/error.gif differ
diff --git a/posterita/src/web/images/pos/error.jpg b/posterita/src/web/images/pos/error.jpg
new file mode 100644
index 0000000000..78ec8a06ff
Binary files /dev/null and b/posterita/src/web/images/pos/error.jpg differ
diff --git a/posterita/src/web/images/pos/helpIcon.gif b/posterita/src/web/images/pos/helpIcon.gif
new file mode 100644
index 0000000000..e04ad56f15
Binary files /dev/null and b/posterita/src/web/images/pos/helpIcon.gif differ
diff --git a/posterita/src/web/images/pos/helpIcon2.gif b/posterita/src/web/images/pos/helpIcon2.gif
new file mode 100644
index 0000000000..01389860c4
Binary files /dev/null and b/posterita/src/web/images/pos/helpIcon2.gif differ
diff --git a/posterita/src/web/images/pos/kbicon.gif b/posterita/src/web/images/pos/kbicon.gif
new file mode 100644
index 0000000000..0c5d53e5bc
Binary files /dev/null and b/posterita/src/web/images/pos/kbicon.gif differ
diff --git a/posterita/src/web/images/pos/logo.jpg b/posterita/src/web/images/pos/logo.jpg
new file mode 100644
index 0000000000..c6ab5ebb2a
Binary files /dev/null and b/posterita/src/web/images/pos/logo.jpg differ
diff --git a/posterita/src/web/images/pos/magnifying-glass.gif b/posterita/src/web/images/pos/magnifying-glass.gif
new file mode 100644
index 0000000000..18e046b144
Binary files /dev/null and b/posterita/src/web/images/pos/magnifying-glass.gif differ
diff --git a/posterita/src/web/images/pos/maskBG.png b/posterita/src/web/images/pos/maskBG.png
new file mode 100644
index 0000000000..b89babc2f1
Binary files /dev/null and b/posterita/src/web/images/pos/maskBG.png differ
diff --git a/posterita/src/web/images/pos/plus.gif b/posterita/src/web/images/pos/plus.gif
new file mode 100644
index 0000000000..f14e73fe83
Binary files /dev/null and b/posterita/src/web/images/pos/plus.gif differ
diff --git a/posterita/src/web/images/posterita.jpg b/posterita/src/web/images/posterita.jpg
new file mode 100644
index 0000000000..cab0493e71
Binary files /dev/null and b/posterita/src/web/images/posterita.jpg differ
diff --git a/posterita/src/web/images/spacer.gif b/posterita/src/web/images/spacer.gif
new file mode 100644
index 0000000000..35d42e808f
Binary files /dev/null and b/posterita/src/web/images/spacer.gif differ
diff --git a/posterita/src/web/images/tango/accessories-calculator.png b/posterita/src/web/images/tango/accessories-calculator.png
new file mode 100644
index 0000000000..7de1c447d1
Binary files /dev/null and b/posterita/src/web/images/tango/accessories-calculator.png differ
diff --git a/posterita/src/web/images/tango/accessories-character-map.png b/posterita/src/web/images/tango/accessories-character-map.png
new file mode 100644
index 0000000000..a86c23ee25
Binary files /dev/null and b/posterita/src/web/images/tango/accessories-character-map.png differ
diff --git a/posterita/src/web/images/tango/accessories-text-editor.png b/posterita/src/web/images/tango/accessories-text-editor.png
new file mode 100644
index 0000000000..c6b6285144
Binary files /dev/null and b/posterita/src/web/images/tango/accessories-text-editor.png differ
diff --git a/posterita/src/web/images/tango/address-book-new.png b/posterita/src/web/images/tango/address-book-new.png
new file mode 100644
index 0000000000..420139d307
Binary files /dev/null and b/posterita/src/web/images/tango/address-book-new.png differ
diff --git a/posterita/src/web/images/tango/applications-system.png b/posterita/src/web/images/tango/applications-system.png
new file mode 100644
index 0000000000..565f406dd1
Binary files /dev/null and b/posterita/src/web/images/tango/applications-system.png differ
diff --git a/posterita/src/web/images/tango/appointment-new.png b/posterita/src/web/images/tango/appointment-new.png
new file mode 100644
index 0000000000..85daef3b0b
Binary files /dev/null and b/posterita/src/web/images/tango/appointment-new.png differ
diff --git a/posterita/src/web/images/tango/bookmark-new.png b/posterita/src/web/images/tango/bookmark-new.png
new file mode 100644
index 0000000000..621312a89b
Binary files /dev/null and b/posterita/src/web/images/tango/bookmark-new.png differ
diff --git a/posterita/src/web/images/tango/contact-new.png b/posterita/src/web/images/tango/contact-new.png
new file mode 100644
index 0000000000..8b10c1e9a2
Binary files /dev/null and b/posterita/src/web/images/tango/contact-new.png differ
diff --git a/posterita/src/web/images/tango/document-new.png b/posterita/src/web/images/tango/document-new.png
new file mode 100644
index 0000000000..e6d64bb90b
Binary files /dev/null and b/posterita/src/web/images/tango/document-new.png differ
diff --git a/posterita/src/web/images/tango/document-open.png b/posterita/src/web/images/tango/document-open.png
new file mode 100644
index 0000000000..2b135a17f3
Binary files /dev/null and b/posterita/src/web/images/tango/document-open.png differ
diff --git a/posterita/src/web/images/tango/document-print-preview.png b/posterita/src/web/images/tango/document-print-preview.png
new file mode 100644
index 0000000000..772efe5a8a
Binary files /dev/null and b/posterita/src/web/images/tango/document-print-preview.png differ
diff --git a/posterita/src/web/images/tango/document-print.png b/posterita/src/web/images/tango/document-print.png
new file mode 100644
index 0000000000..3ef393029e
Binary files /dev/null and b/posterita/src/web/images/tango/document-print.png differ
diff --git a/posterita/src/web/images/tango/document-properties.png b/posterita/src/web/images/tango/document-properties.png
new file mode 100644
index 0000000000..fa697db405
Binary files /dev/null and b/posterita/src/web/images/tango/document-properties.png differ
diff --git a/posterita/src/web/images/tango/document-save-as.png b/posterita/src/web/images/tango/document-save-as.png
new file mode 100644
index 0000000000..5c9f6b343b
Binary files /dev/null and b/posterita/src/web/images/tango/document-save-as.png differ
diff --git a/posterita/src/web/images/tango/document-save.png b/posterita/src/web/images/tango/document-save.png
new file mode 100644
index 0000000000..db5c52b769
Binary files /dev/null and b/posterita/src/web/images/tango/document-save.png differ
diff --git a/posterita/src/web/images/tango/edit-clear.png b/posterita/src/web/images/tango/edit-clear.png
new file mode 100644
index 0000000000..5542948bca
Binary files /dev/null and b/posterita/src/web/images/tango/edit-clear.png differ
diff --git a/posterita/src/web/images/tango/edit-copy.png b/posterita/src/web/images/tango/edit-copy.png
new file mode 100644
index 0000000000..3348ee08fd
Binary files /dev/null and b/posterita/src/web/images/tango/edit-copy.png differ
diff --git a/posterita/src/web/images/tango/edit-cut.png b/posterita/src/web/images/tango/edit-cut.png
new file mode 100644
index 0000000000..217663b19c
Binary files /dev/null and b/posterita/src/web/images/tango/edit-cut.png differ
diff --git a/posterita/src/web/images/tango/edit-delete.png b/posterita/src/web/images/tango/edit-delete.png
new file mode 100644
index 0000000000..9becb3e2f3
Binary files /dev/null and b/posterita/src/web/images/tango/edit-delete.png differ
diff --git a/posterita/src/web/images/tango/edit-find-replace.png b/posterita/src/web/images/tango/edit-find-replace.png
new file mode 100644
index 0000000000..0f1b117ff7
Binary files /dev/null and b/posterita/src/web/images/tango/edit-find-replace.png differ
diff --git a/posterita/src/web/images/tango/edit-find.png b/posterita/src/web/images/tango/edit-find.png
new file mode 100644
index 0000000000..5594785d10
Binary files /dev/null and b/posterita/src/web/images/tango/edit-find.png differ
diff --git a/posterita/src/web/images/tango/edit-paste.png b/posterita/src/web/images/tango/edit-paste.png
new file mode 100644
index 0000000000..dd429ced62
Binary files /dev/null and b/posterita/src/web/images/tango/edit-paste.png differ
diff --git a/posterita/src/web/images/tango/edit-redo.png b/posterita/src/web/images/tango/edit-redo.png
new file mode 100644
index 0000000000..3eb7b05c84
Binary files /dev/null and b/posterita/src/web/images/tango/edit-redo.png differ
diff --git a/posterita/src/web/images/tango/edit-select-all.png b/posterita/src/web/images/tango/edit-select-all.png
new file mode 100644
index 0000000000..107fc60741
Binary files /dev/null and b/posterita/src/web/images/tango/edit-select-all.png differ
diff --git a/posterita/src/web/images/tango/edit-undo.png b/posterita/src/web/images/tango/edit-undo.png
new file mode 100644
index 0000000000..61b2ce9a53
Binary files /dev/null and b/posterita/src/web/images/tango/edit-undo.png differ
diff --git a/posterita/src/web/images/tango/folder-new.png b/posterita/src/web/images/tango/folder-new.png
new file mode 100644
index 0000000000..fcd15c0184
Binary files /dev/null and b/posterita/src/web/images/tango/folder-new.png differ
diff --git a/posterita/src/web/images/tango/format-indent-less.png b/posterita/src/web/images/tango/format-indent-less.png
new file mode 100644
index 0000000000..7ced16f725
Binary files /dev/null and b/posterita/src/web/images/tango/format-indent-less.png differ
diff --git a/posterita/src/web/images/tango/format-indent-more.png b/posterita/src/web/images/tango/format-indent-more.png
new file mode 100644
index 0000000000..6a18867ca7
Binary files /dev/null and b/posterita/src/web/images/tango/format-indent-more.png differ
diff --git a/posterita/src/web/images/tango/format-justify-center.png b/posterita/src/web/images/tango/format-justify-center.png
new file mode 100644
index 0000000000..a0db2bb30a
Binary files /dev/null and b/posterita/src/web/images/tango/format-justify-center.png differ
diff --git a/posterita/src/web/images/tango/format-justify-fill.png b/posterita/src/web/images/tango/format-justify-fill.png
new file mode 100644
index 0000000000..2a34a8fd6c
Binary files /dev/null and b/posterita/src/web/images/tango/format-justify-fill.png differ
diff --git a/posterita/src/web/images/tango/format-justify-left.png b/posterita/src/web/images/tango/format-justify-left.png
new file mode 100644
index 0000000000..ba0e914a00
Binary files /dev/null and b/posterita/src/web/images/tango/format-justify-left.png differ
diff --git a/posterita/src/web/images/tango/format-justify-right.png b/posterita/src/web/images/tango/format-justify-right.png
new file mode 100644
index 0000000000..2144cb915e
Binary files /dev/null and b/posterita/src/web/images/tango/format-justify-right.png differ
diff --git a/posterita/src/web/images/tango/format-text-bold.png b/posterita/src/web/images/tango/format-text-bold.png
new file mode 100644
index 0000000000..99ed19c580
Binary files /dev/null and b/posterita/src/web/images/tango/format-text-bold.png differ
diff --git a/posterita/src/web/images/tango/format-text-italic.png b/posterita/src/web/images/tango/format-text-italic.png
new file mode 100644
index 0000000000..87ed6f9f6e
Binary files /dev/null and b/posterita/src/web/images/tango/format-text-italic.png differ
diff --git a/posterita/src/web/images/tango/format-text-strikethrough.png b/posterita/src/web/images/tango/format-text-strikethrough.png
new file mode 100644
index 0000000000..b9b55ab250
Binary files /dev/null and b/posterita/src/web/images/tango/format-text-strikethrough.png differ
diff --git a/posterita/src/web/images/tango/format-text-underline.png b/posterita/src/web/images/tango/format-text-underline.png
new file mode 100644
index 0000000000..0de6b1cfd3
Binary files /dev/null and b/posterita/src/web/images/tango/format-text-underline.png differ
diff --git a/posterita/src/web/images/tango/go-bottom.png b/posterita/src/web/images/tango/go-bottom.png
new file mode 100644
index 0000000000..bf973fedc2
Binary files /dev/null and b/posterita/src/web/images/tango/go-bottom.png differ
diff --git a/posterita/src/web/images/tango/go-down.png b/posterita/src/web/images/tango/go-down.png
new file mode 100644
index 0000000000..dce3f15ef5
Binary files /dev/null and b/posterita/src/web/images/tango/go-down.png differ
diff --git a/posterita/src/web/images/tango/go-first.png b/posterita/src/web/images/tango/go-first.png
new file mode 100644
index 0000000000..5e2a6b1ea8
Binary files /dev/null and b/posterita/src/web/images/tango/go-first.png differ
diff --git a/posterita/src/web/images/tango/go-home.png b/posterita/src/web/images/tango/go-home.png
new file mode 100644
index 0000000000..a3ca103e35
Binary files /dev/null and b/posterita/src/web/images/tango/go-home.png differ
diff --git a/posterita/src/web/images/tango/go-jump.png b/posterita/src/web/images/tango/go-jump.png
new file mode 100644
index 0000000000..34dc4c04e2
Binary files /dev/null and b/posterita/src/web/images/tango/go-jump.png differ
diff --git a/posterita/src/web/images/tango/go-last.png b/posterita/src/web/images/tango/go-last.png
new file mode 100644
index 0000000000..48fe95bd3b
Binary files /dev/null and b/posterita/src/web/images/tango/go-last.png differ
diff --git a/posterita/src/web/images/tango/go-next.png b/posterita/src/web/images/tango/go-next.png
new file mode 100644
index 0000000000..a68e2db775
Binary files /dev/null and b/posterita/src/web/images/tango/go-next.png differ
diff --git a/posterita/src/web/images/tango/go-previous.png b/posterita/src/web/images/tango/go-previous.png
new file mode 100644
index 0000000000..c37bc0414c
Binary files /dev/null and b/posterita/src/web/images/tango/go-previous.png differ
diff --git a/posterita/src/web/images/tango/go-top.png b/posterita/src/web/images/tango/go-top.png
new file mode 100644
index 0000000000..d99552b694
Binary files /dev/null and b/posterita/src/web/images/tango/go-top.png differ
diff --git a/posterita/src/web/images/tango/go-up.png b/posterita/src/web/images/tango/go-up.png
new file mode 100644
index 0000000000..afb307b18c
Binary files /dev/null and b/posterita/src/web/images/tango/go-up.png differ
diff --git a/posterita/src/web/images/tango/help-browser.png b/posterita/src/web/images/tango/help-browser.png
new file mode 100644
index 0000000000..d60425f7ec
Binary files /dev/null and b/posterita/src/web/images/tango/help-browser.png differ
diff --git a/posterita/src/web/images/tango/input-keyboard.png b/posterita/src/web/images/tango/input-keyboard.png
new file mode 100644
index 0000000000..5bb1298cd3
Binary files /dev/null and b/posterita/src/web/images/tango/input-keyboard.png differ
diff --git a/posterita/src/web/images/tango/internet-group-chat.png b/posterita/src/web/images/tango/internet-group-chat.png
new file mode 100644
index 0000000000..9cb1d3be11
Binary files /dev/null and b/posterita/src/web/images/tango/internet-group-chat.png differ
diff --git a/posterita/src/web/images/tango/internet-mail.png b/posterita/src/web/images/tango/internet-mail.png
new file mode 100644
index 0000000000..dc3b9dd3a6
Binary files /dev/null and b/posterita/src/web/images/tango/internet-mail.png differ
diff --git a/posterita/src/web/images/tango/internet-news-reader.png b/posterita/src/web/images/tango/internet-news-reader.png
new file mode 100644
index 0000000000..ebc528fed8
Binary files /dev/null and b/posterita/src/web/images/tango/internet-news-reader.png differ
diff --git a/posterita/src/web/images/tango/internet-web-browser.png b/posterita/src/web/images/tango/internet-web-browser.png
new file mode 100644
index 0000000000..10d2ed4f47
Binary files /dev/null and b/posterita/src/web/images/tango/internet-web-browser.png differ
diff --git a/posterita/src/web/images/tango/list-add.png b/posterita/src/web/images/tango/list-add.png
new file mode 100644
index 0000000000..2acdd8f514
Binary files /dev/null and b/posterita/src/web/images/tango/list-add.png differ
diff --git a/posterita/src/web/images/tango/list-remove.png b/posterita/src/web/images/tango/list-remove.png
new file mode 100644
index 0000000000..c5524f7284
Binary files /dev/null and b/posterita/src/web/images/tango/list-remove.png differ
diff --git a/posterita/src/web/images/tango/mail-forward.png b/posterita/src/web/images/tango/mail-forward.png
new file mode 100644
index 0000000000..bea94ab0c8
Binary files /dev/null and b/posterita/src/web/images/tango/mail-forward.png differ
diff --git a/posterita/src/web/images/tango/mail-mark-junk.png b/posterita/src/web/images/tango/mail-mark-junk.png
new file mode 100644
index 0000000000..0af3006673
Binary files /dev/null and b/posterita/src/web/images/tango/mail-mark-junk.png differ
diff --git a/posterita/src/web/images/tango/mail-mark-not-junk.png b/posterita/src/web/images/tango/mail-mark-not-junk.png
new file mode 100644
index 0000000000..296e92ad13
Binary files /dev/null and b/posterita/src/web/images/tango/mail-mark-not-junk.png differ
diff --git a/posterita/src/web/images/tango/mail-message-new.png b/posterita/src/web/images/tango/mail-message-new.png
new file mode 100644
index 0000000000..9f51246f1e
Binary files /dev/null and b/posterita/src/web/images/tango/mail-message-new.png differ
diff --git a/posterita/src/web/images/tango/mail-reply-all.png b/posterita/src/web/images/tango/mail-reply-all.png
new file mode 100644
index 0000000000..0216e390b6
Binary files /dev/null and b/posterita/src/web/images/tango/mail-reply-all.png differ
diff --git a/posterita/src/web/images/tango/mail-reply-sender.png b/posterita/src/web/images/tango/mail-reply-sender.png
new file mode 100644
index 0000000000..3f248dc3a2
Binary files /dev/null and b/posterita/src/web/images/tango/mail-reply-sender.png differ
diff --git a/posterita/src/web/images/tango/mail-send-receive.png b/posterita/src/web/images/tango/mail-send-receive.png
new file mode 100644
index 0000000000..99349b9aac
Binary files /dev/null and b/posterita/src/web/images/tango/mail-send-receive.png differ
diff --git a/posterita/src/web/images/tango/media-eject.png b/posterita/src/web/images/tango/media-eject.png
new file mode 100644
index 0000000000..b218e7ae98
Binary files /dev/null and b/posterita/src/web/images/tango/media-eject.png differ
diff --git a/posterita/src/web/images/tango/media-playback-pause.png b/posterita/src/web/images/tango/media-playback-pause.png
new file mode 100644
index 0000000000..1e9f4d5357
Binary files /dev/null and b/posterita/src/web/images/tango/media-playback-pause.png differ
diff --git a/posterita/src/web/images/tango/media-playback-start.png b/posterita/src/web/images/tango/media-playback-start.png
new file mode 100644
index 0000000000..66f32d89b5
Binary files /dev/null and b/posterita/src/web/images/tango/media-playback-start.png differ
diff --git a/posterita/src/web/images/tango/media-playback-stop.png b/posterita/src/web/images/tango/media-playback-stop.png
new file mode 100644
index 0000000000..a0947879ee
Binary files /dev/null and b/posterita/src/web/images/tango/media-playback-stop.png differ
diff --git a/posterita/src/web/images/tango/media-record.png b/posterita/src/web/images/tango/media-record.png
new file mode 100644
index 0000000000..43f034b59a
Binary files /dev/null and b/posterita/src/web/images/tango/media-record.png differ
diff --git a/posterita/src/web/images/tango/media-seek-backward.png b/posterita/src/web/images/tango/media-seek-backward.png
new file mode 100644
index 0000000000..535c536157
Binary files /dev/null and b/posterita/src/web/images/tango/media-seek-backward.png differ
diff --git a/posterita/src/web/images/tango/media-seek-forward.png b/posterita/src/web/images/tango/media-seek-forward.png
new file mode 100644
index 0000000000..96ebe01c5f
Binary files /dev/null and b/posterita/src/web/images/tango/media-seek-forward.png differ
diff --git a/posterita/src/web/images/tango/media-skip-backward.png b/posterita/src/web/images/tango/media-skip-backward.png
new file mode 100644
index 0000000000..aa082513b9
Binary files /dev/null and b/posterita/src/web/images/tango/media-skip-backward.png differ
diff --git a/posterita/src/web/images/tango/media-skip-forward.png b/posterita/src/web/images/tango/media-skip-forward.png
new file mode 100644
index 0000000000..52be9420da
Binary files /dev/null and b/posterita/src/web/images/tango/media-skip-forward.png differ
diff --git a/posterita/src/web/images/tango/office-calendar.png b/posterita/src/web/images/tango/office-calendar.png
new file mode 100644
index 0000000000..7817c12b4f
Binary files /dev/null and b/posterita/src/web/images/tango/office-calendar.png differ
diff --git a/posterita/src/web/images/tango/preferences-desktop-accessibility.png b/posterita/src/web/images/tango/preferences-desktop-accessibility.png
new file mode 100644
index 0000000000..adfe247e29
Binary files /dev/null and b/posterita/src/web/images/tango/preferences-desktop-accessibility.png differ
diff --git a/posterita/src/web/images/tango/preferences-desktop-assistive-technology.png b/posterita/src/web/images/tango/preferences-desktop-assistive-technology.png
new file mode 100644
index 0000000000..70adb92b52
Binary files /dev/null and b/posterita/src/web/images/tango/preferences-desktop-assistive-technology.png differ
diff --git a/posterita/src/web/images/tango/preferences-desktop-font.png b/posterita/src/web/images/tango/preferences-desktop-font.png
new file mode 100644
index 0000000000..b4ec434ca1
Binary files /dev/null and b/posterita/src/web/images/tango/preferences-desktop-font.png differ
diff --git a/posterita/src/web/images/tango/preferences-desktop-keyboard-shortcuts.png b/posterita/src/web/images/tango/preferences-desktop-keyboard-shortcuts.png
new file mode 100644
index 0000000000..178dd294c3
Binary files /dev/null and b/posterita/src/web/images/tango/preferences-desktop-keyboard-shortcuts.png differ
diff --git a/posterita/src/web/images/tango/preferences-desktop-locale.png b/posterita/src/web/images/tango/preferences-desktop-locale.png
new file mode 100644
index 0000000000..608a0b1e95
Binary files /dev/null and b/posterita/src/web/images/tango/preferences-desktop-locale.png differ
diff --git a/posterita/src/web/images/tango/preferences-desktop-remote-desktop.png b/posterita/src/web/images/tango/preferences-desktop-remote-desktop.png
new file mode 100644
index 0000000000..6da67c65f7
Binary files /dev/null and b/posterita/src/web/images/tango/preferences-desktop-remote-desktop.png differ
diff --git a/posterita/src/web/images/tango/preferences-desktop-screensaver.png b/posterita/src/web/images/tango/preferences-desktop-screensaver.png
new file mode 100644
index 0000000000..dba245586f
Binary files /dev/null and b/posterita/src/web/images/tango/preferences-desktop-screensaver.png differ
diff --git a/posterita/src/web/images/tango/preferences-desktop-sound.png b/posterita/src/web/images/tango/preferences-desktop-sound.png
new file mode 100644
index 0000000000..56a5662eee
Binary files /dev/null and b/posterita/src/web/images/tango/preferences-desktop-sound.png differ
diff --git a/posterita/src/web/images/tango/preferences-desktop-theme.png b/posterita/src/web/images/tango/preferences-desktop-theme.png
new file mode 100644
index 0000000000..7af777d6e7
Binary files /dev/null and b/posterita/src/web/images/tango/preferences-desktop-theme.png differ
diff --git a/posterita/src/web/images/tango/preferences-desktop-wallpaper.png b/posterita/src/web/images/tango/preferences-desktop-wallpaper.png
new file mode 100644
index 0000000000..4eb744ca14
Binary files /dev/null and b/posterita/src/web/images/tango/preferences-desktop-wallpaper.png differ
diff --git a/posterita/src/web/images/tango/preferences-system-network-proxy.png b/posterita/src/web/images/tango/preferences-system-network-proxy.png
new file mode 100644
index 0000000000..e75f6f769c
Binary files /dev/null and b/posterita/src/web/images/tango/preferences-system-network-proxy.png differ
diff --git a/posterita/src/web/images/tango/preferences-system-session.png b/posterita/src/web/images/tango/preferences-system-session.png
new file mode 100644
index 0000000000..f8c2d112c9
Binary files /dev/null and b/posterita/src/web/images/tango/preferences-system-session.png differ
diff --git a/posterita/src/web/images/tango/preferences-system-windows.png b/posterita/src/web/images/tango/preferences-system-windows.png
new file mode 100644
index 0000000000..517e48ae08
Binary files /dev/null and b/posterita/src/web/images/tango/preferences-system-windows.png differ
diff --git a/posterita/src/web/images/tango/preferences-system.png b/posterita/src/web/images/tango/preferences-system.png
new file mode 100644
index 0000000000..6e52db7cfd
Binary files /dev/null and b/posterita/src/web/images/tango/preferences-system.png differ
diff --git a/posterita/src/web/images/tango/process-stop.png b/posterita/src/web/images/tango/process-stop.png
new file mode 100644
index 0000000000..e7a8d1722f
Binary files /dev/null and b/posterita/src/web/images/tango/process-stop.png differ
diff --git a/posterita/src/web/images/tango/system-file-manager.png b/posterita/src/web/images/tango/system-file-manager.png
new file mode 100644
index 0000000000..1d6ce31510
Binary files /dev/null and b/posterita/src/web/images/tango/system-file-manager.png differ
diff --git a/posterita/src/web/images/tango/system-installer.png b/posterita/src/web/images/tango/system-installer.png
new file mode 100644
index 0000000000..c26576ee8f
Binary files /dev/null and b/posterita/src/web/images/tango/system-installer.png differ
diff --git a/posterita/src/web/images/tango/system-lock-screen.png b/posterita/src/web/images/tango/system-lock-screen.png
new file mode 100644
index 0000000000..2c220fcb62
Binary files /dev/null and b/posterita/src/web/images/tango/system-lock-screen.png differ
diff --git a/posterita/src/web/images/tango/system-log-out.png b/posterita/src/web/images/tango/system-log-out.png
new file mode 100644
index 0000000000..780411d3ea
Binary files /dev/null and b/posterita/src/web/images/tango/system-log-out.png differ
diff --git a/posterita/src/web/images/tango/system-password.png b/posterita/src/web/images/tango/system-password.png
new file mode 100644
index 0000000000..b0109750ac
Binary files /dev/null and b/posterita/src/web/images/tango/system-password.png differ
diff --git a/posterita/src/web/images/tango/system-search.png b/posterita/src/web/images/tango/system-search.png
new file mode 100644
index 0000000000..950d792af5
Binary files /dev/null and b/posterita/src/web/images/tango/system-search.png differ
diff --git a/posterita/src/web/images/tango/system-shutdown.png b/posterita/src/web/images/tango/system-shutdown.png
new file mode 100644
index 0000000000..36acd46bd3
Binary files /dev/null and b/posterita/src/web/images/tango/system-shutdown.png differ
diff --git a/posterita/src/web/images/tango/system-software-update.png b/posterita/src/web/images/tango/system-software-update.png
new file mode 100644
index 0000000000..470b5d46f4
Binary files /dev/null and b/posterita/src/web/images/tango/system-software-update.png differ
diff --git a/posterita/src/web/images/tango/system-users.png b/posterita/src/web/images/tango/system-users.png
new file mode 100644
index 0000000000..749c825e07
Binary files /dev/null and b/posterita/src/web/images/tango/system-users.png differ
diff --git a/posterita/src/web/images/tango/tab-new.png b/posterita/src/web/images/tango/tab-new.png
new file mode 100644
index 0000000000..294d150697
Binary files /dev/null and b/posterita/src/web/images/tango/tab-new.png differ
diff --git a/posterita/src/web/images/tango/utilities-system-monitor.png b/posterita/src/web/images/tango/utilities-system-monitor.png
new file mode 100644
index 0000000000..b62959e4f3
Binary files /dev/null and b/posterita/src/web/images/tango/utilities-system-monitor.png differ
diff --git a/posterita/src/web/images/tango/utilities-terminal.png b/posterita/src/web/images/tango/utilities-terminal.png
new file mode 100644
index 0000000000..f86c784002
Binary files /dev/null and b/posterita/src/web/images/tango/utilities-terminal.png differ
diff --git a/posterita/src/web/images/tango/view-fullscreen.png b/posterita/src/web/images/tango/view-fullscreen.png
new file mode 100644
index 0000000000..00e6b83cc0
Binary files /dev/null and b/posterita/src/web/images/tango/view-fullscreen.png differ
diff --git a/posterita/src/web/images/tango/view-refresh.png b/posterita/src/web/images/tango/view-refresh.png
new file mode 100644
index 0000000000..606ea9eba4
Binary files /dev/null and b/posterita/src/web/images/tango/view-refresh.png differ
diff --git a/posterita/src/web/images/tango/window-new.png b/posterita/src/web/images/tango/window-new.png
new file mode 100644
index 0000000000..e091702e33
Binary files /dev/null and b/posterita/src/web/images/tango/window-new.png differ
diff --git a/posterita/src/web/images/tango/x-office-calendar.png b/posterita/src/web/images/tango/x-office-calendar.png
new file mode 100644
index 0000000000..0c224e9f01
Binary files /dev/null and b/posterita/src/web/images/tango/x-office-calendar.png differ
diff --git a/posterita/src/web/javascripts/builder.js b/posterita/src/web/javascripts/builder.js
new file mode 100644
index 0000000000..5b15ba9397
--- /dev/null
+++ b/posterita/src/web/javascripts/builder.js
@@ -0,0 +1,101 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// See scriptaculous.js for full license.
+
+var Builder = {
+ NODEMAP: {
+ AREA: 'map',
+ CAPTION: 'table',
+ COL: 'table',
+ COLGROUP: 'table',
+ LEGEND: 'fieldset',
+ OPTGROUP: 'select',
+ OPTION: 'select',
+ PARAM: 'object',
+ TBODY: 'table',
+ TD: 'table',
+ TFOOT: 'table',
+ TH: 'table',
+ THEAD: 'table',
+ TR: 'table'
+ },
+ // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
+ // due to a Firefox bug
+ node: function(elementName) {
+ elementName = elementName.toUpperCase();
+
+ // try innerHTML approach
+ var parentTag = this.NODEMAP[elementName] || 'div';
+ var parentElement = document.createElement(parentTag);
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+ parentElement.innerHTML = "<" + elementName + ">" + elementName + ">";
+ } catch(e) {}
+ var element = parentElement.firstChild || null;
+
+ // see if browser added wrapping tags
+ if(element && (element.tagName != elementName))
+ element = element.getElementsByTagName(elementName)[0];
+
+ // fallback to createElement approach
+ if(!element) element = document.createElement(elementName);
+
+ // abort if nothing could be created
+ if(!element) return;
+
+ // attributes (or text)
+ if(arguments[1])
+ if(this._isStringOrNumber(arguments[1]) ||
+ (arguments[1] instanceof Array)) {
+ this._children(element, arguments[1]);
+ } else {
+ var attrs = this._attributes(arguments[1]);
+ if(attrs.length) {
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+ parentElement.innerHTML = "<" +elementName + " " +
+ attrs + ">" + elementName + ">";
+ } catch(e) {}
+ element = parentElement.firstChild || null;
+ // workaround firefox 1.0.X bug
+ if(!element) {
+ element = document.createElement(elementName);
+ for(attr in arguments[1])
+ element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
+ }
+ if(element.tagName != elementName)
+ element = parentElement.getElementsByTagName(elementName)[0];
+ }
+ }
+
+ // text, or array of children
+ if(arguments[2])
+ this._children(element, arguments[2]);
+
+ return element;
+ },
+ _text: function(text) {
+ return document.createTextNode(text);
+ },
+ _attributes: function(attributes) {
+ var attrs = [];
+ for(attribute in attributes)
+ attrs.push((attribute=='className' ? 'class' : attribute) +
+ '="' + attributes[attribute].toString().escapeHTML() + '"');
+ return attrs.join(" ");
+ },
+ _children: function(element, children) {
+ if(typeof children=='object') { // array can hold nodes and text
+ children.flatten().each( function(e) {
+ if(typeof e=='object')
+ element.appendChild(e)
+ else
+ if(Builder._isStringOrNumber(e))
+ element.appendChild(Builder._text(e));
+ });
+ } else
+ if(Builder._isStringOrNumber(children))
+ element.appendChild(Builder._text(children));
+ },
+ _isStringOrNumber: function(param) {
+ return(typeof param=='string' || typeof param=='number');
+ }
+}
\ No newline at end of file
diff --git a/posterita/src/web/javascripts/controls.js b/posterita/src/web/javascripts/controls.js
new file mode 100644
index 0000000000..99a16a6c73
--- /dev/null
+++ b/posterita/src/web/javascripts/controls.js
@@ -0,0 +1,815 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+// (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+// Richard Livsey
+// Rahul Bhargava
+// Rob Wills
+//
+// See scriptaculous.js for full license.
+
+// Autocompleter.Base handles all the autocompletion functionality
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least,
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most
+// useful when one of the tokens is \n (a newline), as it
+// allows smart autocompletion after linebreaks.
+
+var Autocompleter = {}
+Autocompleter.Base = function() {};
+Autocompleter.Base.prototype = {
+ baseInitialize: function(element, update, options) {
+ this.element = $(element);
+ this.update = $(update);
+ this.hasFocus = false;
+ this.changed = false;
+ this.active = false;
+ this.index = 0;
+ this.entryCount = 0;
+
+ if (this.setOptions)
+ this.setOptions(options);
+ else
+ this.options = options || {};
+
+ this.options.paramName = this.options.paramName || this.element.name;
+ this.options.tokens = this.options.tokens || [];
+ this.options.frequency = this.options.frequency || 0.4;
+ this.options.minChars = this.options.minChars || 1;
+ this.options.onShow = this.options.onShow ||
+ function(element, update){
+ if(!update.style.position || update.style.position=='absolute') {
+ update.style.position = 'absolute';
+ Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
+ }
+ Effect.Appear(update,{duration:0.05});
+ };
+ this.options.onHide = this.options.onHide ||
+ function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+ if (typeof(this.options.tokens) == 'string')
+ this.options.tokens = new Array(this.options.tokens);
+
+ this.observer = null;
+
+ this.element.setAttribute('autocomplete','off');
+
+ Element.hide(this.update);
+
+ Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
+ Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
+ },
+
+ show: function() {
+ if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+ if(!this.iefix &&
+ (navigator.appVersion.indexOf('MSIE')>0) &&
+ (navigator.userAgent.indexOf('Opera')<0) &&
+ (Element.getStyle(this.update, 'position')=='absolute')) {
+ new Insertion.After(this.update,
+ '');
+ this.iefix = $(this.update.id+'_iefix');
+ }
+ if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+ },
+
+ fixIEOverlapping: function() {
+ Position.clone(this.update, this.iefix);
+ this.iefix.style.zIndex = 1;
+ this.update.style.zIndex = 2;
+ Element.show(this.iefix);
+ },
+
+ hide: function() {
+ this.stopIndicator();
+ if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+ if(this.iefix) Element.hide(this.iefix);
+ },
+
+ startIndicator: function() {
+ if(this.options.indicator) Element.show(this.options.indicator);
+ },
+
+ stopIndicator: function() {
+ if(this.options.indicator) Element.hide(this.options.indicator);
+ },
+
+ onKeyPress: function(event) {
+ if(this.active)
+ switch(event.keyCode) {
+ case Event.KEY_TAB:
+ case Event.KEY_RETURN:
+ this.selectEntry();
+ Event.stop(event);
+ case Event.KEY_ESC:
+ this.hide();
+ this.active = false;
+ Event.stop(event);
+ return;
+ case Event.KEY_LEFT:
+ case Event.KEY_RIGHT:
+ return;
+ case Event.KEY_UP:
+ this.markPrevious();
+ this.render();
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+ return;
+ case Event.KEY_DOWN:
+ this.markNext();
+ this.render();
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+ return;
+ }
+ else
+ if(event.keyCode==Event.KEY_TAB ||
+ (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
+
+ this.changed = true;
+ this.hasFocus = true;
+
+ if(this.observer) clearTimeout(this.observer);
+ this.observer =
+ setTimeout(this.onObserverEvent.bind(this), this.options.frequency*500);
+ },
+
+ activate: function() {
+ this.changed = false;
+ this.hasFocus = true;
+ this.getUpdatedChoices();
+ },
+
+ onHover: function(event) {
+ var element = Event.findElement(event, 'LI');
+ if(this.index != element.autocompleteIndex)
+ {
+ this.index = element.autocompleteIndex;
+ this.render();
+ }
+ Event.stop(event);
+ },
+
+ onClick: function(event) {
+ var element = Event.findElement(event, 'LI');
+ this.index = element.autocompleteIndex;
+ this.selectEntry();
+ this.hide();
+ },
+
+ onBlur: function(event) {
+ // needed to make click events working
+ setTimeout(this.hide.bind(this), 50);
+ this.hasFocus = false;
+ this.active = false;
+ },
+
+ render: function() {
+ if(this.entryCount > 0) {
+ for (var i = 0; i < this.entryCount; i++)
+ this.index==i ?
+ Element.addClassName(this.getEntry(i),"selected") :
+ Element.removeClassName(this.getEntry(i),"selected");
+
+ if(this.hasFocus) {
+ this.show();
+ this.active = true;
+ }
+ } else {
+ this.active = false;
+ this.hide();
+ }
+ },
+
+ markPrevious: function() {
+ if(this.index > 0) this.index--
+ else this.index = this.entryCount-1;
+ },
+
+ markNext: function() {
+ if(this.index < this.entryCount-1) this.index++
+ else this.index = 0;
+ },
+
+ getEntry: function(index) {
+ return this.update.firstChild.childNodes[index];
+ },
+
+ getCurrentEntry: function() {
+ return this.getEntry(this.index);
+ },
+
+ selectEntry: function() {
+ this.active = false;
+ this.updateElement(this.getCurrentEntry());
+ },
+
+ updateElement: function(selectedElement) {
+ if (this.options.updateElement) {
+ this.options.updateElement(selectedElement);
+ return;
+ }
+ var value = '';
+ if (this.options.select) {
+ var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
+ if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+ } else
+ value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+
+ var lastTokenPos = this.findLastToken();
+ if (lastTokenPos != -1) {
+ var newValue = this.element.value.substr(0, lastTokenPos + 1);
+ var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
+ if (whitespace)
+ newValue += whitespace[0];
+ this.element.value = newValue + value;
+ } else {
+ this.element.value = value;
+ }
+ this.element.focus();
+
+ if (this.options.afterUpdateElement)
+ this.options.afterUpdateElement(this.element, selectedElement);
+ },
+
+ updateChoices: function(choices) {
+ if(!this.changed && this.hasFocus) {
+ this.update.innerHTML = choices;
+ Element.cleanWhitespace(this.update);
+ Element.cleanWhitespace(this.update.firstChild);
+
+ if(this.update.firstChild && this.update.firstChild.childNodes) {
+ this.entryCount =
+ this.update.firstChild.childNodes.length;
+ for (var i = 0; i < this.entryCount; i++) {
+ var entry = this.getEntry(i);
+ entry.autocompleteIndex = i;
+ this.addObservers(entry);
+ }
+ } else {
+ this.entryCount = 0;
+ }
+
+ this.stopIndicator();
+
+ this.index = 0;
+ this.render();
+ }
+ },
+
+ addObservers: function(element) {
+ Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+ Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+ },
+
+ onObserverEvent: function() {
+ this.changed = false;
+ if(this.getToken().length>=this.options.minChars) {
+ this.startIndicator();
+ this.getUpdatedChoices();
+ } else {
+ this.active = false;
+ this.hide();
+ }
+ },
+
+ getToken: function() {
+ var tokenPos = this.findLastToken();
+ if (tokenPos != -1)
+ var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
+ else
+ var ret = this.element.value;
+
+ return /\n/.test(ret) ? '' : ret;
+ },
+
+ findLastToken: function() {
+ var lastTokenPos = -1;
+
+ for (var i=0; i lastTokenPos)
+ lastTokenPos = thisTokenPos;
+ }
+ return lastTokenPos;
+ }
+}
+
+Ajax.Autocompleter = Class.create();
+Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
+ initialize: function(element, update, url, options) {
+ this.baseInitialize(element, update, options);
+ this.options.asynchronous = true;
+ this.options.onComplete = this.onComplete.bind(this);
+ this.options.defaultParams = this.options.parameters || null;
+ this.url = url;
+ },
+
+ getUpdatedChoices: function() {
+ entry = encodeURIComponent(this.options.paramName) + '=' +
+ encodeURIComponent(this.getToken());
+
+ this.options.parameters = this.options.callback ?
+ this.options.callback(this.element, entry) : entry;
+
+ if(this.options.defaultParams)
+ this.options.parameters += '&' + this.options.defaultParams;
+
+ new Ajax.Request(this.url, this.options);
+ },
+
+ onComplete: function(request) {
+ this.updateChoices(request.responseText);
+ }
+
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+// text only at the beginning of strings in the
+// autocomplete array. Defaults to true, which will
+// match text at the beginning of any *word* in the
+// strings in the autocomplete array. If you want to
+// search anywhere in the string, additionally set
+// the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+// a partial match (unlike minChars, which defines
+// how many characters are required to do any match
+// at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+// Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector'
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create();
+Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
+ initialize: function(element, update, array, options) {
+ this.baseInitialize(element, update, options);
+ this.options.array = array;
+ },
+
+ getUpdatedChoices: function() {
+ this.updateChoices(this.options.selector(this));
+ },
+
+ setOptions: function(options) {
+ this.options = Object.extend({
+ choices: 10,
+ partialSearch: true,
+ partialChars: 2,
+ ignoreCase: true,
+ fullSearch: false,
+ selector: function(instance) {
+ var ret = []; // Beginning matches
+ var partial = []; // Inside matches
+ var entry = instance.getToken();
+ var count = 0;
+
+ for (var i = 0; i < instance.options.array.length &&
+ ret.length < instance.options.choices ; i++) {
+
+ var elem = instance.options.array[i];
+ var foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase()) :
+ elem.indexOf(entry);
+
+ while (foundPos != -1) {
+ if (foundPos == 0 && elem.length != entry.length) {
+ ret.push("" + elem.substr(0, entry.length) + " " +
+ elem.substr(entry.length) + " ");
+ break;
+ } else if (entry.length >= instance.options.partialChars &&
+ instance.options.partialSearch && foundPos != -1) {
+ if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+ partial.push("" + elem.substr(0, foundPos) + "" +
+ elem.substr(foundPos, entry.length) + " " + elem.substr(
+ foundPos + entry.length) + " ");
+ break;
+ }
+ }
+
+ foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
+ elem.indexOf(entry, foundPos + 1);
+
+ }
+ }
+ if (partial.length)
+ ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+ return "";
+ }
+ }, options || {});
+ }
+});
+
+// AJAX in-place editor
+//
+// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+ setTimeout(function() {
+ Field.activate(field);
+ }, 1);
+}
+
+Ajax.InPlaceEditor = Class.create();
+Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
+Ajax.InPlaceEditor.prototype = {
+ initialize: function(element, url, options) {
+ this.url = url;
+ this.element = $(element);
+
+ this.options = Object.extend({
+ okButton: true,
+ okText: "ok",
+ cancelLink: true,
+ cancelText: "cancel",
+ savingText: "Saving...",
+ clickToEditText: "Click to edit",
+ okText: "ok",
+ rows: 1,
+ onComplete: function(transport, element) {
+ new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
+ },
+ onFailure: function(transport) {
+ alert("Error communicating with the server: " + transport.responseText.stripTags());
+ },
+ callback: function(form) {
+ return Form.serialize(form);
+ },
+ handleLineBreaks: true,
+ loadingText: 'Loading...',
+ savingClassName: 'inplaceeditor-saving',
+ loadingClassName: 'inplaceeditor-loading',
+ formClassName: 'inplaceeditor-form',
+ highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
+ highlightendcolor: "#FFFFFF",
+ externalControl: null,
+ submitOnBlur: false,
+ ajaxOptions: {},
+ evalScripts: false
+ }, options || {});
+
+ if(!this.options.formId && this.element.id) {
+ this.options.formId = this.element.id + "-inplaceeditor";
+ if ($(this.options.formId)) {
+ // there's already a form with that name, don't specify an id
+ this.options.formId = null;
+ }
+ }
+
+ if (this.options.externalControl) {
+ this.options.externalControl = $(this.options.externalControl);
+ }
+
+ this.originalBackground = Element.getStyle(this.element, 'background-color');
+ if (!this.originalBackground) {
+ this.originalBackground = "transparent";
+ }
+
+ this.element.title = this.options.clickToEditText;
+
+ this.onclickListener = this.enterEditMode.bindAsEventListener(this);
+ this.mouseoverListener = this.enterHover.bindAsEventListener(this);
+ this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
+ Event.observe(this.element, 'click', this.onclickListener);
+ Event.observe(this.element, 'mouseover', this.mouseoverListener);
+ Event.observe(this.element, 'mouseout', this.mouseoutListener);
+ if (this.options.externalControl) {
+ Event.observe(this.options.externalControl, 'click', this.onclickListener);
+ Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
+ Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
+ }
+ },
+ enterEditMode: function(evt) {
+ if (this.saving) return;
+ if (this.editing) return;
+ this.editing = true;
+ this.onEnterEditMode();
+ if (this.options.externalControl) {
+ Element.hide(this.options.externalControl);
+ }
+ Element.hide(this.element);
+ this.createForm();
+ this.element.parentNode.insertBefore(this.form, this.element);
+ Field.scrollFreeActivate(this.editField);
+ // stop the event to avoid a page refresh in Safari
+ if (evt) {
+ Event.stop(evt);
+ }
+ return false;
+ },
+ createForm: function() {
+ this.form = document.createElement("form");
+ this.form.id = this.options.formId;
+ Element.addClassName(this.form, this.options.formClassName)
+ this.form.onsubmit = this.onSubmit.bind(this);
+
+ this.createEditField();
+
+ if (this.options.textarea) {
+ var br = document.createElement("br");
+ this.form.appendChild(br);
+ }
+
+ if (this.options.okButton) {
+ okButton = document.createElement("input");
+ okButton.type = "submit";
+ okButton.value = this.options.okText;
+ okButton.className = 'editor_ok_button';
+ this.form.appendChild(okButton);
+ }
+
+ if (this.options.cancelLink) {
+ cancelLink = document.createElement("a");
+ cancelLink.href = "#";
+ cancelLink.appendChild(document.createTextNode(this.options.cancelText));
+ cancelLink.onclick = this.onclickCancel.bind(this);
+ cancelLink.className = 'editor_cancel';
+ this.form.appendChild(cancelLink);
+ }
+ },
+ hasHTMLLineBreaks: function(string) {
+ if (!this.options.handleLineBreaks) return false;
+ return string.match(/ /i);
+ },
+ convertHTMLLineBreaks: function(string) {
+ return string.replace(/ /gi, "\n").replace(/ /gi, "\n").replace(/<\/p>/gi, "\n").replace(//gi, "");
+ },
+ createEditField: function() {
+ var text;
+ if(this.options.loadTextURL) {
+ text = this.options.loadingText;
+ } else {
+ text = this.getText();
+ }
+
+ var obj = this;
+
+ if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
+ this.options.textarea = false;
+ var textField = document.createElement("input");
+ textField.obj = this;
+ textField.type = "text";
+ textField.name = "value";
+ textField.value = text;
+ textField.style.backgroundColor = this.options.highlightcolor;
+ textField.className = 'editor_field';
+ var size = this.options.size || this.options.cols || 0;
+ if (size != 0) textField.size = size;
+ if (this.options.submitOnBlur)
+ textField.onblur = this.onSubmit.bind(this);
+ this.editField = textField;
+ } else {
+ this.options.textarea = true;
+ var textArea = document.createElement("textarea");
+ textArea.obj = this;
+ textArea.name = "value";
+ textArea.value = this.convertHTMLLineBreaks(text);
+ textArea.rows = this.options.rows;
+ textArea.cols = this.options.cols || 40;
+ textArea.className = 'editor_field';
+ if (this.options.submitOnBlur)
+ textArea.onblur = this.onSubmit.bind(this);
+ this.editField = textArea;
+ }
+
+ if(this.options.loadTextURL) {
+ this.loadExternalText();
+ }
+ this.form.appendChild(this.editField);
+ },
+ getText: function() {
+ return this.element.innerHTML;
+ },
+ loadExternalText: function() {
+ Element.addClassName(this.form, this.options.loadingClassName);
+ this.editField.disabled = true;
+ new Ajax.Request(
+ this.options.loadTextURL,
+ Object.extend({
+ asynchronous: true,
+ onComplete: this.onLoadedExternalText.bind(this)
+ }, this.options.ajaxOptions)
+ );
+ },
+ onLoadedExternalText: function(transport) {
+ Element.removeClassName(this.form, this.options.loadingClassName);
+ this.editField.disabled = false;
+ this.editField.value = transport.responseText.stripTags();
+ },
+ onclickCancel: function() {
+ this.onComplete();
+ this.leaveEditMode();
+ return false;
+ },
+ onFailure: function(transport) {
+ this.options.onFailure(transport);
+ if (this.oldInnerHTML) {
+ this.element.innerHTML = this.oldInnerHTML;
+ this.oldInnerHTML = null;
+ }
+ return false;
+ },
+ onSubmit: function() {
+ // onLoading resets these so we need to save them away for the Ajax call
+ var form = this.form;
+ var value = this.editField.value;
+
+ // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
+ // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
+ // to be displayed indefinitely
+ this.onLoading();
+
+ if (this.options.evalScripts) {
+ new Ajax.Request(
+ this.url, Object.extend({
+ parameters: this.options.callback(form, value),
+ onComplete: this.onComplete.bind(this),
+ onFailure: this.onFailure.bind(this),
+ asynchronous:true,
+ evalScripts:true
+ }, this.options.ajaxOptions));
+ } else {
+ new Ajax.Updater(
+ { success: this.element,
+ // don't update on failure (this could be an option)
+ failure: null },
+ this.url, Object.extend({
+ parameters: this.options.callback(form, value),
+ onComplete: this.onComplete.bind(this),
+ onFailure: this.onFailure.bind(this)
+ }, this.options.ajaxOptions));
+ }
+ // stop the event to avoid a page refresh in Safari
+ if (arguments.length > 1) {
+ Event.stop(arguments[0]);
+ }
+ return false;
+ },
+ onLoading: function() {
+ this.saving = true;
+ this.removeForm();
+ this.leaveHover();
+ this.showSaving();
+ },
+ showSaving: function() {
+ this.oldInnerHTML = this.element.innerHTML;
+ this.element.innerHTML = this.options.savingText;
+ Element.addClassName(this.element, this.options.savingClassName);
+ this.element.style.backgroundColor = this.originalBackground;
+ Element.show(this.element);
+ },
+ removeForm: function() {
+ if(this.form) {
+ if (this.form.parentNode) Element.remove(this.form);
+ this.form = null;
+ }
+ },
+ enterHover: function() {
+ if (this.saving) return;
+ this.element.style.backgroundColor = this.options.highlightcolor;
+ if (this.effect) {
+ this.effect.cancel();
+ }
+ Element.addClassName(this.element, this.options.hoverClassName)
+ },
+ leaveHover: function() {
+ if (this.options.backgroundColor) {
+ this.element.style.backgroundColor = this.oldBackground;
+ }
+ Element.removeClassName(this.element, this.options.hoverClassName)
+ if (this.saving) return;
+ this.effect = new Effect.Highlight(this.element, {
+ startcolor: this.options.highlightcolor,
+ endcolor: this.options.highlightendcolor,
+ restorecolor: this.originalBackground
+ });
+ },
+ leaveEditMode: function() {
+ Element.removeClassName(this.element, this.options.savingClassName);
+ this.removeForm();
+ this.leaveHover();
+ this.element.style.backgroundColor = this.originalBackground;
+ Element.show(this.element);
+ if (this.options.externalControl) {
+ Element.show(this.options.externalControl);
+ }
+ this.editing = false;
+ this.saving = false;
+ this.oldInnerHTML = null;
+ this.onLeaveEditMode();
+ },
+ onComplete: function(transport) {
+ this.leaveEditMode();
+ this.options.onComplete.bind(this)(transport, this.element);
+ },
+ onEnterEditMode: function() {},
+ onLeaveEditMode: function() {},
+ dispose: function() {
+ if (this.oldInnerHTML) {
+ this.element.innerHTML = this.oldInnerHTML;
+ }
+ this.leaveEditMode();
+ Event.stopObserving(this.element, 'click', this.onclickListener);
+ Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
+ Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
+ if (this.options.externalControl) {
+ Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
+ Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
+ Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
+ }
+ }
+};
+
+Ajax.InPlaceCollectionEditor = Class.create();
+Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
+Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
+ createEditField: function() {
+ if (!this.cached_selectTag) {
+ var selectTag = document.createElement("select");
+ var collection = this.options.collection || [];
+ var optionTag;
+ collection.each(function(e,i) {
+ optionTag = document.createElement("option");
+ optionTag.value = (e instanceof Array) ? e[0] : e;
+ if(this.options.value==optionTag.value) optionTag.selected = true;
+ optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
+ selectTag.appendChild(optionTag);
+ }.bind(this));
+ this.cached_selectTag = selectTag;
+ }
+
+ this.editField = this.cached_selectTag;
+ if(this.options.loadTextURL) this.loadExternalText();
+ this.form.appendChild(this.editField);
+ this.options.callback = function(form, value) {
+ return "value=" + encodeURIComponent(value);
+ }
+ }
+});
+
+// Delayed observer, like Form.Element.Observer,
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create();
+Form.Element.DelayedObserver.prototype = {
+ initialize: function(element, delay, callback) {
+ this.delay = delay || 0.5;
+ this.element = $(element);
+ this.callback = callback;
+ this.timer = null;
+ this.lastValue = $F(this.element);
+ Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+ },
+ delayedListener: function(event) {
+ if(this.lastValue == $F(this.element)) return;
+ if(this.timer) clearTimeout(this.timer);
+ this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+ this.lastValue = $F(this.element);
+ },
+ onTimerEvent: function() {
+ this.timer = null;
+ this.callback(this.element, $F(this.element));
+ }
+};
diff --git a/posterita/src/web/javascripts/dragdrop.js b/posterita/src/web/javascripts/dragdrop.js
new file mode 100644
index 0000000000..be2a30f538
--- /dev/null
+++ b/posterita/src/web/javascripts/dragdrop.js
@@ -0,0 +1,915 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
+//
+// See scriptaculous.js for full license.
+
+/*--------------------------------------------------------------------------*/
+
+var Droppables = {
+ drops: [],
+
+ remove: function(element) {
+ this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+ },
+
+ add: function(element) {
+ element = $(element);
+ var options = Object.extend({
+ greedy: true,
+ hoverclass: null,
+ tree: false
+ }, arguments[1] || {});
+
+ // cache containers
+ if(options.containment) {
+ options._containers = [];
+ var containment = options.containment;
+ if((typeof containment == 'object') &&
+ (containment.constructor == Array)) {
+ containment.each( function(c) { options._containers.push($(c)) });
+ } else {
+ options._containers.push($(containment));
+ }
+ }
+
+ if(options.accept) options.accept = [options.accept].flatten();
+
+ Element.makePositioned(element); // fix IE
+ options.element = element;
+
+ this.drops.push(options);
+ },
+
+ findDeepestChild: function(drops) {
+ deepest = drops[0];
+
+ for (i = 1; i < drops.length; ++i)
+ if (Element.isParent(drops[i].element, deepest.element))
+ deepest = drops[i];
+
+ return deepest;
+ },
+
+ isContained: function(element, drop) {
+ var containmentNode;
+ if(drop.tree) {
+ containmentNode = element.treeNode;
+ } else {
+ containmentNode = element.parentNode;
+ }
+ return drop._containers.detect(function(c) { return containmentNode == c });
+ },
+
+ isAffected: function(point, element, drop) {
+ return (
+ (drop.element!=element) &&
+ ((!drop._containers) ||
+ this.isContained(element, drop)) &&
+ ((!drop.accept) ||
+ (Element.classNames(element).detect(
+ function(v) { return drop.accept.include(v) } ) )) &&
+ Position.within(drop.element, point[0], point[1]) );
+ },
+
+ deactivate: function(drop) {
+ if(drop.hoverclass)
+ Element.removeClassName(drop.element, drop.hoverclass);
+ this.last_active = null;
+ },
+
+ activate: function(drop) {
+ if(drop.hoverclass)
+ Element.addClassName(drop.element, drop.hoverclass);
+ this.last_active = drop;
+ },
+
+ show: function(point, element) {
+ if(!this.drops.length) return;
+ var affected = [];
+
+ if(this.last_active) this.deactivate(this.last_active);
+ this.drops.each( function(drop) {
+ if(Droppables.isAffected(point, element, drop))
+ affected.push(drop);
+ });
+
+ if(affected.length>0) {
+ drop = Droppables.findDeepestChild(affected);
+ Position.within(drop.element, point[0], point[1]);
+ if(drop.onHover)
+ drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+
+ Droppables.activate(drop);
+ }
+ },
+
+ fire: function(event, element) {
+ if(!this.last_active) return;
+ Position.prepare();
+
+ if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+ if (this.last_active.onDrop)
+ this.last_active.onDrop(element, this.last_active.element, event);
+ },
+
+ reset: function() {
+ if(this.last_active)
+ this.deactivate(this.last_active);
+ }
+}
+
+var Draggables = {
+ drags: [],
+ observers: [],
+
+ register: function(draggable) {
+ if(this.drags.length == 0) {
+ this.eventMouseUp = this.endDrag.bindAsEventListener(this);
+ this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+ this.eventKeypress = this.keyPress.bindAsEventListener(this);
+
+ Event.observe(document, "mouseup", this.eventMouseUp);
+ Event.observe(document, "mousemove", this.eventMouseMove);
+ Event.observe(document, "keypress", this.eventKeypress);
+ }
+ this.drags.push(draggable);
+ },
+
+ unregister: function(draggable) {
+ this.drags = this.drags.reject(function(d) { return d==draggable });
+ if(this.drags.length == 0) {
+ Event.stopObserving(document, "mouseup", this.eventMouseUp);
+ Event.stopObserving(document, "mousemove", this.eventMouseMove);
+ Event.stopObserving(document, "keypress", this.eventKeypress);
+ }
+ },
+
+ activate: function(draggable) {
+ window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+ this.activeDraggable = draggable;
+ },
+
+ deactivate: function() {
+ this.activeDraggable = null;
+ },
+
+ updateDrag: function(event) {
+ if(!this.activeDraggable) return;
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ // Mozilla-based browsers fire successive mousemove events with
+ // the same coordinates, prevent needless redrawing (moz bug?)
+ if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+ this._lastPointer = pointer;
+ this.activeDraggable.updateDrag(event, pointer);
+ },
+
+ endDrag: function(event) {
+ if(!this.activeDraggable) return;
+ this._lastPointer = null;
+ this.activeDraggable.endDrag(event);
+ this.activeDraggable = null;
+ },
+
+ keyPress: function(event) {
+ if(this.activeDraggable)
+ this.activeDraggable.keyPress(event);
+ },
+
+ addObserver: function(observer) {
+ this.observers.push(observer);
+ this._cacheObserverCallbacks();
+ },
+
+ removeObserver: function(element) { // element instead of observer fixes mem leaks
+ this.observers = this.observers.reject( function(o) { return o.element==element });
+ this._cacheObserverCallbacks();
+ },
+
+ notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
+ if(this[eventName+'Count'] > 0)
+ this.observers.each( function(o) {
+ if(o[eventName]) o[eventName](eventName, draggable, event);
+ });
+ },
+
+ _cacheObserverCallbacks: function() {
+ ['onStart','onEnd','onDrag'].each( function(eventName) {
+ Draggables[eventName+'Count'] = Draggables.observers.select(
+ function(o) { return o[eventName]; }
+ ).length;
+ });
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create();
+Draggable.prototype = {
+ initialize: function(element) {
+ var options = Object.extend({
+ handle: false,
+ starteffect: function(element) {
+ element._opacity = Element.getOpacity(element);
+ new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
+ },
+ reverteffect: function(element, top_offset, left_offset) {
+ var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+ element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur});
+ },
+ endeffect: function(element) {
+ var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0
+ new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity});
+ },
+ zindex: 1000,
+ revert: false,
+ scroll: false,
+ scrollSensitivity: 20,
+ scrollSpeed: 15,
+ snap: false // false, or xy or [x,y] or function(x,y){ return [x,y] }
+ }, arguments[1] || {});
+
+ this.element = $(element);
+
+ if(options.handle && (typeof options.handle == 'string')) {
+ var h = Element.childrenWithClassName(this.element, options.handle, true);
+ if(h.length>0) this.handle = h[0];
+ }
+ if(!this.handle) this.handle = $(options.handle);
+ if(!this.handle) this.handle = this.element;
+
+ if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML)
+ options.scroll = $(options.scroll);
+
+ Element.makePositioned(this.element); // fix IE
+
+ this.delta = this.currentDelta();
+ this.options = options;
+ this.dragging = false;
+
+ this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+ Event.observe(this.handle, "mousedown", this.eventMouseDown);
+
+ Draggables.register(this);
+ },
+
+ destroy: function() {
+ Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+ Draggables.unregister(this);
+ },
+
+ currentDelta: function() {
+ return([
+ parseInt(Element.getStyle(this.element,'left') || '0'),
+ parseInt(Element.getStyle(this.element,'top') || '0')]);
+ },
+
+ initDrag: function(event) {
+ if(Event.isLeftClick(event)) {
+ // abort on form elements, fixes a Firefox issue
+ var src = Event.element(event);
+ if(src.tagName && (
+ src.tagName=='INPUT' ||
+ src.tagName=='SELECT' ||
+ src.tagName=='OPTION' ||
+ src.tagName=='BUTTON' ||
+ src.tagName=='TEXTAREA')) return;
+
+ if(this.element._revert) {
+ this.element._revert.cancel();
+ this.element._revert = null;
+ }
+
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ var pos = Position.cumulativeOffset(this.element);
+ this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+
+ Draggables.activate(this);
+ Event.stop(event);
+ }
+ },
+
+ startDrag: function(event) {
+ this.dragging = true;
+
+ if(this.options.zindex) {
+ this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+ this.element.style.zIndex = this.options.zindex;
+ }
+
+ if(this.options.ghosting) {
+ this._clone = this.element.cloneNode(true);
+ Position.absolutize(this.element);
+ this.element.parentNode.insertBefore(this._clone, this.element);
+ }
+
+ if(this.options.scroll) {
+ if (this.options.scroll == window) {
+ var where = this._getWindowScroll(this.options.scroll);
+ this.originalScrollLeft = where.left;
+ this.originalScrollTop = where.top;
+ } else {
+ this.originalScrollLeft = this.options.scroll.scrollLeft;
+ this.originalScrollTop = this.options.scroll.scrollTop;
+ }
+ }
+
+ Draggables.notify('onStart', this, event);
+ if(this.options.starteffect) this.options.starteffect(this.element);
+ },
+
+ updateDrag: function(event, pointer) {
+ if(!this.dragging) this.startDrag(event);
+ Position.prepare();
+ Droppables.show(pointer, this.element);
+ Draggables.notify('onDrag', this, event);
+ this.draw(pointer);
+ if(this.options.change) this.options.change(this);
+
+ if(this.options.scroll) {
+ this.stopScrolling();
+
+ var p;
+ if (this.options.scroll == window) {
+ with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
+ } else {
+ p = Position.page(this.options.scroll);
+ p[0] += this.options.scroll.scrollLeft;
+ p[1] += this.options.scroll.scrollTop;
+ p.push(p[0]+this.options.scroll.offsetWidth);
+ p.push(p[1]+this.options.scroll.offsetHeight);
+ }
+ var speed = [0,0];
+ if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
+ if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
+ if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
+ if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
+ this.startScrolling(speed);
+ }
+
+ // fix AppleWebKit rendering
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+
+ Event.stop(event);
+ },
+
+ finishDrag: function(event, success) {
+ this.dragging = false;
+
+ if(this.options.ghosting) {
+ Position.relativize(this.element);
+ Element.remove(this._clone);
+ this._clone = null;
+ }
+
+ if(success) Droppables.fire(event, this.element);
+ Draggables.notify('onEnd', this, event);
+
+ var revert = this.options.revert;
+ if(revert && typeof revert == 'function') revert = revert(this.element);
+
+ var d = this.currentDelta();
+ if(revert && this.options.reverteffect) {
+ this.options.reverteffect(this.element,
+ d[1]-this.delta[1], d[0]-this.delta[0]);
+ } else {
+ this.delta = d;
+ }
+
+ if(this.options.zindex)
+ this.element.style.zIndex = this.originalZ;
+
+ if(this.options.endeffect)
+ this.options.endeffect(this.element);
+
+ Draggables.deactivate(this);
+ Droppables.reset();
+ },
+
+ keyPress: function(event) {
+ if(event.keyCode!=Event.KEY_ESC) return;
+ this.finishDrag(event, false);
+ Event.stop(event);
+ },
+
+ endDrag: function(event) {
+ if(!this.dragging) return;
+ this.stopScrolling();
+ this.finishDrag(event, true);
+ Event.stop(event);
+ },
+
+ draw: function(point) {
+ var pos = Position.cumulativeOffset(this.element);
+ var d = this.currentDelta();
+ pos[0] -= d[0]; pos[1] -= d[1];
+
+ if(this.options.scroll && (this.options.scroll != window)) {
+ pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
+ pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
+ }
+
+ var p = [0,1].map(function(i){
+ return (point[i]-pos[i]-this.offset[i])
+ }.bind(this));
+
+ if(this.options.snap) {
+ if(typeof this.options.snap == 'function') {
+ p = this.options.snap(p[0],p[1],this);
+ } else {
+ if(this.options.snap instanceof Array) {
+ p = p.map( function(v, i) {
+ return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
+ } else {
+ p = p.map( function(v) {
+ return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
+ }
+ }}
+
+ var style = this.element.style;
+ if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+ style.left = p[0] + "px";
+ if((!this.options.constraint) || (this.options.constraint=='vertical'))
+ style.top = p[1] + "px";
+ if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+ },
+
+ stopScrolling: function() {
+ if(this.scrollInterval) {
+ clearInterval(this.scrollInterval);
+ this.scrollInterval = null;
+ Draggables._lastScrollPointer = null;
+ }
+ },
+
+ startScrolling: function(speed) {
+ this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
+ this.lastScrolled = new Date();
+ this.scrollInterval = setInterval(this.scroll.bind(this), 10);
+ },
+
+ scroll: function() {
+ var current = new Date();
+ var delta = current - this.lastScrolled;
+ this.lastScrolled = current;
+ if(this.options.scroll == window) {
+ with (this._getWindowScroll(this.options.scroll)) {
+ if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
+ var d = delta / 1000;
+ this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
+ }
+ }
+ } else {
+ this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
+ this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
+ }
+
+ Position.prepare();
+ Droppables.show(Draggables._lastPointer, this.element);
+ Draggables.notify('onDrag', this);
+ Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+ Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+ Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+ if (Draggables._lastScrollPointer[0] < 0)
+ Draggables._lastScrollPointer[0] = 0;
+ if (Draggables._lastScrollPointer[1] < 0)
+ Draggables._lastScrollPointer[1] = 0;
+ this.draw(Draggables._lastScrollPointer);
+
+ if(this.options.change) this.options.change(this);
+ },
+
+ _getWindowScroll: function(w) {
+ var T, L, W, H;
+ with (w.document) {
+ if (w.document.documentElement && documentElement.scrollTop) {
+ T = documentElement.scrollTop;
+ L = documentElement.scrollLeft;
+ } else if (w.document.body) {
+ T = body.scrollTop;
+ L = body.scrollLeft;
+ }
+ if (w.innerWidth) {
+ W = w.innerWidth;
+ H = w.innerHeight;
+ } else if (w.document.documentElement && documentElement.clientWidth) {
+ W = documentElement.clientWidth;
+ H = documentElement.clientHeight;
+ } else {
+ W = body.offsetWidth;
+ H = body.offsetHeight
+ }
+ }
+ return { top: T, left: L, width: W, height: H };
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create();
+SortableObserver.prototype = {
+ initialize: function(element, observer) {
+ this.element = $(element);
+ this.observer = observer;
+ this.lastValue = Sortable.serialize(this.element);
+ },
+
+ onStart: function() {
+ this.lastValue = Sortable.serialize(this.element);
+ },
+
+ onEnd: function() {
+ Sortable.unmark();
+ if(this.lastValue != Sortable.serialize(this.element))
+ this.observer(this.element)
+ }
+}
+
+var Sortable = {
+ sortables: {},
+
+ _findRootElement: function(element) {
+ while (element.tagName != "BODY") {
+ if(element.id && Sortable.sortables[element.id]) return element;
+ element = element.parentNode;
+ }
+ },
+
+ options: function(element) {
+ element = Sortable._findRootElement($(element));
+ if(!element) return;
+ return Sortable.sortables[element.id];
+ },
+
+ destroy: function(element){
+ var s = Sortable.options(element);
+
+ if(s) {
+ Draggables.removeObserver(s.element);
+ s.droppables.each(function(d){ Droppables.remove(d) });
+ s.draggables.invoke('destroy');
+
+ delete Sortable.sortables[s.element.id];
+ }
+ },
+
+ create: function(element) {
+ element = $(element);
+ var options = Object.extend({
+ element: element,
+ tag: 'li', // assumes li children, override with tag: 'tagname'
+ dropOnEmpty: false,
+ tree: false,
+ treeTag: 'ul',
+ overlap: 'vertical', // one of 'vertical', 'horizontal'
+ constraint: 'vertical', // one of 'vertical', 'horizontal', false
+ containment: element, // also takes array of elements (or id's); or false
+ handle: false, // or a CSS class
+ only: false,
+ hoverclass: null,
+ ghosting: false,
+ scroll: false,
+ scrollSensitivity: 20,
+ scrollSpeed: 15,
+ format: /^[^_]*_(.*)$/,
+ onChange: Prototype.emptyFunction,
+ onUpdate: Prototype.emptyFunction
+ }, arguments[1] || {});
+
+ // clear any old sortable with same element
+ this.destroy(element);
+
+ // build options for the draggables
+ var options_for_draggable = {
+ revert: true,
+ scroll: options.scroll,
+ scrollSpeed: options.scrollSpeed,
+ scrollSensitivity: options.scrollSensitivity,
+ ghosting: options.ghosting,
+ constraint: options.constraint,
+ handle: options.handle };
+
+ if(options.starteffect)
+ options_for_draggable.starteffect = options.starteffect;
+
+ if(options.reverteffect)
+ options_for_draggable.reverteffect = options.reverteffect;
+ else
+ if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+ element.style.top = 0;
+ element.style.left = 0;
+ };
+
+ if(options.endeffect)
+ options_for_draggable.endeffect = options.endeffect;
+
+ if(options.zindex)
+ options_for_draggable.zindex = options.zindex;
+
+ // build options for the droppables
+ var options_for_droppable = {
+ overlap: options.overlap,
+ containment: options.containment,
+ tree: options.tree,
+ hoverclass: options.hoverclass,
+ onHover: Sortable.onHover
+ //greedy: !options.dropOnEmpty
+ }
+
+ var options_for_tree = {
+ onHover: Sortable.onEmptyHover,
+ overlap: options.overlap,
+ containment: options.containment,
+ hoverclass: options.hoverclass
+ }
+
+ // fix for gecko engine
+ Element.cleanWhitespace(element);
+
+ options.draggables = [];
+ options.droppables = [];
+
+ // drop on empty handling
+ if(options.dropOnEmpty || options.tree) {
+ Droppables.add(element, options_for_tree);
+ options.droppables.push(element);
+ }
+
+ (this.findElements(element, options) || []).each( function(e) {
+ // handles are per-draggable
+ var handle = options.handle ?
+ Element.childrenWithClassName(e, options.handle)[0] : e;
+ options.draggables.push(
+ new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+ Droppables.add(e, options_for_droppable);
+ if(options.tree) e.treeNode = element;
+ options.droppables.push(e);
+ });
+
+ if(options.tree) {
+ (Sortable.findTreeElements(element, options) || []).each( function(e) {
+ Droppables.add(e, options_for_tree);
+ e.treeNode = element;
+ options.droppables.push(e);
+ });
+ }
+
+ // keep reference
+ this.sortables[element.id] = options;
+
+ // for onupdate
+ Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+ },
+
+ // return all suitable-for-sortable elements in a guaranteed order
+ findElements: function(element, options) {
+ return Element.findChildren(
+ element, options.only, options.tree ? true : false, options.tag);
+ },
+
+ findTreeElements: function(element, options) {
+ return Element.findChildren(
+ element, options.only, options.tree ? true : false, options.treeTag);
+ },
+
+ onHover: function(element, dropon, overlap) {
+ if(Element.isParent(dropon, element)) return;
+
+ if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
+ return;
+ } else if(overlap>0.5) {
+ Sortable.mark(dropon, 'before');
+ if(dropon.previousSibling != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, dropon);
+ if(dropon.parentNode!=oldParentNode)
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon.parentNode).onChange(element);
+ }
+ } else {
+ Sortable.mark(dropon, 'after');
+ var nextElement = dropon.nextSibling || null;
+ if(nextElement != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, nextElement);
+ if(dropon.parentNode!=oldParentNode)
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon.parentNode).onChange(element);
+ }
+ }
+ },
+
+ onEmptyHover: function(element, dropon, overlap) {
+ var oldParentNode = element.parentNode;
+ var droponOptions = Sortable.options(dropon);
+
+ if(!Element.isParent(dropon, element)) {
+ var index;
+
+ var children = Sortable.findElements(dropon, {tag: droponOptions.tag});
+ var child = null;
+
+ if(children) {
+ var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
+
+ for (index = 0; index < children.length; index += 1) {
+ if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
+ offset -= Element.offsetSize (children[index], droponOptions.overlap);
+ } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
+ child = index + 1 < children.length ? children[index + 1] : null;
+ break;
+ } else {
+ child = children[index];
+ break;
+ }
+ }
+ }
+
+ dropon.insertBefore(element, child);
+
+ Sortable.options(oldParentNode).onChange(element);
+ droponOptions.onChange(element);
+ }
+ },
+
+ unmark: function() {
+ if(Sortable._marker) Element.hide(Sortable._marker);
+ },
+
+ mark: function(dropon, position) {
+ // mark on ghosting only
+ var sortable = Sortable.options(dropon.parentNode);
+ if(sortable && !sortable.ghosting) return;
+
+ if(!Sortable._marker) {
+ Sortable._marker = $('dropmarker') || document.createElement('DIV');
+ Element.hide(Sortable._marker);
+ Element.addClassName(Sortable._marker, 'dropmarker');
+ Sortable._marker.style.position = 'absolute';
+ document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+ }
+ var offsets = Position.cumulativeOffset(dropon);
+ Sortable._marker.style.left = offsets[0] + 'px';
+ Sortable._marker.style.top = offsets[1] + 'px';
+
+ if(position=='after')
+ if(sortable.overlap == 'horizontal')
+ Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
+ else
+ Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
+
+ Element.show(Sortable._marker);
+ },
+
+ _tree: function(element, options, parent) {
+ var children = Sortable.findElements(element, options) || [];
+
+ for (var i = 0; i < children.length; ++i) {
+ var match = children[i].id.match(options.format);
+
+ if (!match) continue;
+
+ var child = {
+ id: encodeURIComponent(match ? match[1] : null),
+ element: element,
+ parent: parent,
+ children: new Array,
+ position: parent.children.length,
+ container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
+ }
+
+ /* Get the element containing the children and recurse over it */
+ if (child.container)
+ this._tree(child.container, options, child)
+
+ parent.children.push (child);
+ }
+
+ return parent;
+ },
+
+ /* Finds the first element of the given tag type within a parent element.
+ Used for finding the first LI[ST] within a L[IST]I[TEM].*/
+ _findChildrenElement: function (element, containerTag) {
+ if (element && element.hasChildNodes)
+ for (var i = 0; i < element.childNodes.length; ++i)
+ if (element.childNodes[i].tagName == containerTag)
+ return element.childNodes[i];
+
+ return null;
+ },
+
+ tree: function(element) {
+ element = $(element);
+ var sortableOptions = this.options(element);
+ var options = Object.extend({
+ tag: sortableOptions.tag,
+ treeTag: sortableOptions.treeTag,
+ only: sortableOptions.only,
+ name: element.id,
+ format: sortableOptions.format
+ }, arguments[1] || {});
+
+ var root = {
+ id: null,
+ parent: null,
+ children: new Array,
+ container: element,
+ position: 0
+ }
+
+ return Sortable._tree (element, options, root);
+ },
+
+ /* Construct a [i] index for a particular node */
+ _constructIndex: function(node) {
+ var index = '';
+ do {
+ if (node.id) index = '[' + node.position + ']' + index;
+ } while ((node = node.parent) != null);
+ return index;
+ },
+
+ sequence: function(element) {
+ element = $(element);
+ var options = Object.extend(this.options(element), arguments[1] || {});
+
+ return $(this.findElements(element, options) || []).map( function(item) {
+ return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
+ });
+ },
+
+ setSequence: function(element, new_sequence) {
+ element = $(element);
+ var options = Object.extend(this.options(element), arguments[2] || {});
+
+ var nodeMap = {};
+ this.findElements(element, options).each( function(n) {
+ if (n.id.match(options.format))
+ nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
+ n.parentNode.removeChild(n);
+ });
+
+ new_sequence.each(function(ident) {
+ var n = nodeMap[ident];
+ if (n) {
+ n[1].appendChild(n[0]);
+ delete nodeMap[ident];
+ }
+ });
+ },
+
+ serialize: function(element) {
+ element = $(element);
+ var options = Object.extend(Sortable.options(element), arguments[1] || {});
+ var name = encodeURIComponent(
+ (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
+
+ if (options.tree) {
+ return Sortable.tree(element, arguments[1]).children.map( function (item) {
+ return [name + Sortable._constructIndex(item) + "=" +
+ encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
+ }).flatten().join('&');
+ } else {
+ return Sortable.sequence(element, arguments[1]).map( function(item) {
+ return name + "[]=" + encodeURIComponent(item);
+ }).join('&');
+ }
+ }
+}
+
+/* Returns true if child is contained within element */
+Element.isParent = function(child, element) {
+ if (!child.parentNode || child == element) return false;
+
+ if (child.parentNode == element) return true;
+
+ return Element.isParent(child.parentNode, element);
+}
+
+Element.findChildren = function(element, only, recursive, tagName) {
+ if(!element.hasChildNodes()) return null;
+ tagName = tagName.toUpperCase();
+ if(only) only = [only].flatten();
+ var elements = [];
+ $A(element.childNodes).each( function(e) {
+ if(e.tagName && e.tagName.toUpperCase()==tagName &&
+ (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
+ elements.push(e);
+ if(recursive) {
+ var grandchildren = Element.findChildren(e, only, recursive, tagName);
+ if(grandchildren) elements.push(grandchildren);
+ }
+ });
+
+ return (elements.length>0 ? elements.flatten() : []);
+}
+
+Element.offsetSize = function (element, type) {
+ if (type == 'vertical' || type == 'height')
+ return element.offsetHeight;
+ else
+ return element.offsetWidth;
+}
\ No newline at end of file
diff --git a/posterita/src/web/javascripts/effects.js b/posterita/src/web/javascripts/effects.js
new file mode 100644
index 0000000000..0864323ecd
--- /dev/null
+++ b/posterita/src/web/javascripts/effects.js
@@ -0,0 +1,958 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+// Justin Palmer (http://encytemedia.com/)
+// Mark Pilgrim (http://diveintomark.org/)
+// Martin Bialasinki
+//
+// See scriptaculous.js for full license.
+
+// converts rgb() and #xxx to #xxxxxx format,
+// returns self (or first argument) if not convertable
+String.prototype.parseColor = function() {
+ var color = '#';
+ if(this.slice(0,4) == 'rgb(') {
+ var cols = this.slice(4,this.length-1).split(',');
+ var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
+ } else {
+ if(this.slice(0,1) == '#') {
+ if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
+ if(this.length==7) color = this.toLowerCase();
+ }
+ }
+ return(color.length==7 ? color : (arguments[0] || this));
+}
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {
+ return $A($(element).childNodes).collect( function(node) {
+ return (node.nodeType==3 ? node.nodeValue :
+ (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+ }).flatten().join('');
+}
+
+Element.collectTextNodesIgnoreClass = function(element, className) {
+ return $A($(element).childNodes).collect( function(node) {
+ return (node.nodeType==3 ? node.nodeValue :
+ ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
+ Element.collectTextNodesIgnoreClass(node, className) : ''));
+ }).flatten().join('');
+}
+
+Element.setContentZoom = function(element, percent) {
+ element = $(element);
+ Element.setStyle(element, {fontSize: (percent/100) + 'em'});
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+}
+
+Element.getOpacity = function(element){
+ var opacity;
+ if (opacity = Element.getStyle(element, 'opacity'))
+ return parseFloat(opacity);
+ if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
+ if(opacity[1]) return parseFloat(opacity[1]) / 100;
+ return 1.0;
+}
+
+Element.setOpacity = function(element, value){
+ element= $(element);
+ if (value == 1){
+ Element.setStyle(element, { opacity:
+ (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
+ 0.999999 : null });
+ if(/MSIE/.test(navigator.userAgent))
+ Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
+ } else {
+ if(value < 0.00001) value = 0;
+ Element.setStyle(element, {opacity: value});
+ if(/MSIE/.test(navigator.userAgent))
+ Element.setStyle(element,
+ { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
+ 'alpha(opacity='+value*100+')' });
+ }
+}
+
+Element.getInlineOpacity = function(element){
+ return $(element).style.opacity || '';
+}
+
+Element.childrenWithClassName = function(element, className, findFirst) {
+ var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
+ var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) {
+ return (c.className && c.className.match(classNameRegExp));
+ });
+ if(!results) results = [];
+ return results;
+}
+
+Element.forceRerendering = function(element) {
+ try {
+ element = $(element);
+ var n = document.createTextNode(' ');
+ element.appendChild(n);
+ element.removeChild(n);
+ } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Array.prototype.call = function() {
+ var args = arguments;
+ this.each(function(f){ f.apply(this, args) });
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+ tagifyText: function(element) {
+ var tagifyStyle = 'position:relative';
+ if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
+ element = $(element);
+ $A(element.childNodes).each( function(child) {
+ if(child.nodeType==3) {
+ child.nodeValue.toArray().each( function(character) {
+ element.insertBefore(
+ Builder.node('span',{style: tagifyStyle},
+ character == ' ' ? String.fromCharCode(160) : character),
+ child);
+ });
+ Element.remove(child);
+ }
+ });
+ },
+ multiple: function(element, effect) {
+ var elements;
+ if(((typeof element == 'object') ||
+ (typeof element == 'function')) &&
+ (element.length))
+ elements = element;
+ else
+ elements = $(element).childNodes;
+
+ var options = Object.extend({
+ speed: 0.1,
+ delay: 0.0
+ }, arguments[2] || {});
+ var masterDelay = options.delay;
+
+ $A(elements).each( function(element, index) {
+ new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+ });
+ },
+ PAIRS: {
+ 'slide': ['SlideDown','SlideUp'],
+ 'blind': ['BlindDown','BlindUp'],
+ 'appear': ['Appear','Fade']
+ },
+ toggle: function(element, effect) {
+ element = $(element);
+ effect = (effect || 'appear').toLowerCase();
+ var options = Object.extend({
+ queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+ }, arguments[2] || {});
+ Effect[element.visible() ?
+ Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+ }
+};
+
+var Effect2 = Effect; // deprecated
+
+/* ------------- transitions ------------- */
+
+Effect.Transitions = {}
+
+Effect.Transitions.linear = function(pos) {
+ return pos;
+}
+Effect.Transitions.sinoidal = function(pos) {
+ return (-Math.cos(pos*Math.PI)/2) + 0.5;
+}
+Effect.Transitions.reverse = function(pos) {
+ return 1-pos;
+}
+Effect.Transitions.flicker = function(pos) {
+ return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+}
+Effect.Transitions.wobble = function(pos) {
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+}
+Effect.Transitions.pulse = function(pos) {
+ return (Math.floor(pos*10) % 2 == 0 ?
+ (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
+}
+Effect.Transitions.none = function(pos) {
+ return 0;
+}
+Effect.Transitions.full = function(pos) {
+ return 1;
+}
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create();
+Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
+ initialize: function() {
+ this.effects = [];
+ this.interval = null;
+ },
+ _each: function(iterator) {
+ this.effects._each(iterator);
+ },
+ add: function(effect) {
+ var timestamp = new Date().getTime();
+
+ var position = (typeof effect.options.queue == 'string') ?
+ effect.options.queue : effect.options.queue.position;
+
+ switch(position) {
+ case 'front':
+ // move unstarted effects after this effect
+ this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+ e.startOn += effect.finishOn;
+ e.finishOn += effect.finishOn;
+ });
+ break;
+ case 'end':
+ // start effect after last queued effect has finished
+ timestamp = this.effects.pluck('finishOn').max() || timestamp;
+ break;
+ }
+
+ effect.startOn += timestamp;
+ effect.finishOn += timestamp;
+
+ if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+ this.effects.push(effect);
+
+ if(!this.interval)
+ this.interval = setInterval(this.loop.bind(this), 40);
+ },
+ remove: function(effect) {
+ this.effects = this.effects.reject(function(e) { return e==effect });
+ if(this.effects.length == 0) {
+ clearInterval(this.interval);
+ this.interval = null;
+ }
+ },
+ loop: function() {
+ var timePos = new Date().getTime();
+ this.effects.invoke('loop', timePos);
+ }
+});
+
+Effect.Queues = {
+ instances: $H(),
+ get: function(queueName) {
+ if(typeof queueName != 'string') return queueName;
+
+ if(!this.instances[queueName])
+ this.instances[queueName] = new Effect.ScopedQueue();
+
+ return this.instances[queueName];
+ }
+}
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.DefaultOptions = {
+ transition: Effect.Transitions.sinoidal,
+ duration: 1.0, // seconds
+ fps: 25.0, // max. 25fps due to Effect.Queue implementation
+ sync: false, // true for combining
+ from: 0.0,
+ to: 1.0,
+ delay: 0.0,
+ queue: 'parallel'
+}
+
+Effect.Base = function() {};
+Effect.Base.prototype = {
+ position: null,
+ start: function(options) {
+ this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
+ this.currentFrame = 0;
+ this.state = 'idle';
+ this.startOn = this.options.delay*1000;
+ this.finishOn = this.startOn + (this.options.duration*1000);
+ this.event('beforeStart');
+ if(!this.options.sync)
+ Effect.Queues.get(typeof this.options.queue == 'string' ?
+ 'global' : this.options.queue.scope).add(this);
+ },
+ loop: function(timePos) {
+ if(timePos >= this.startOn) {
+ if(timePos >= this.finishOn) {
+ this.render(1.0);
+ this.cancel();
+ this.event('beforeFinish');
+ if(this.finish) this.finish();
+ this.event('afterFinish');
+ return;
+ }
+ var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
+ var frame = Math.round(pos * this.options.fps * this.options.duration);
+ if(frame > this.currentFrame) {
+ this.render(pos);
+ this.currentFrame = frame;
+ }
+ }
+ },
+ render: function(pos) {
+ if(this.state == 'idle') {
+ this.state = 'running';
+ this.event('beforeSetup');
+ if(this.setup) this.setup();
+ this.event('afterSetup');
+ }
+ if(this.state == 'running') {
+ if(this.options.transition) pos = this.options.transition(pos);
+ pos *= (this.options.to-this.options.from);
+ pos += this.options.from;
+ this.position = pos;
+ this.event('beforeUpdate');
+ if(this.update) this.update(pos);
+ this.event('afterUpdate');
+ }
+ },
+ cancel: function() {
+ if(!this.options.sync)
+ Effect.Queues.get(typeof this.options.queue == 'string' ?
+ 'global' : this.options.queue.scope).remove(this);
+ this.state = 'finished';
+ },
+ event: function(eventName) {
+ if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+ if(this.options[eventName]) this.options[eventName](this);
+ },
+ inspect: function() {
+ return '#';
+ }
+}
+
+Effect.Parallel = Class.create();
+Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
+ initialize: function(effects) {
+ this.effects = effects || [];
+ this.start(arguments[1]);
+ },
+ update: function(position) {
+ this.effects.invoke('render', position);
+ },
+ finish: function(position) {
+ this.effects.each( function(effect) {
+ effect.render(1.0);
+ effect.cancel();
+ effect.event('beforeFinish');
+ if(effect.finish) effect.finish(position);
+ effect.event('afterFinish');
+ });
+ }
+});
+
+Effect.Opacity = Class.create();
+Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ // make this work on IE on elements without 'layout'
+ if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
+ this.element.setStyle({zoom: 1});
+ var options = Object.extend({
+ from: this.element.getOpacity() || 0.0,
+ to: 1.0
+ }, arguments[1] || {});
+ this.start(options);
+ },
+ update: function(position) {
+ this.element.setOpacity(position);
+ }
+});
+
+Effect.Move = Class.create();
+Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ var options = Object.extend({
+ x: 0,
+ y: 0,
+ mode: 'relative'
+ }, arguments[1] || {});
+ this.start(options);
+ },
+ setup: function() {
+ // Bug in Opera: Opera returns the "real" position of a static element or
+ // relative element that does not have top/left explicitly set.
+ // ==> Always set top and left for position relative elements in your stylesheets
+ // (to 0 if you do not need them)
+ this.element.makePositioned();
+ this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+ this.originalTop = parseFloat(this.element.getStyle('top') || '0');
+ if(this.options.mode == 'absolute') {
+ // absolute movement, so we need to calc deltaX and deltaY
+ this.options.x = this.options.x - this.originalLeft;
+ this.options.y = this.options.y - this.originalTop;
+ }
+ },
+ update: function(position) {
+ this.element.setStyle({
+ left: this.options.x * position + this.originalLeft + 'px',
+ top: this.options.y * position + this.originalTop + 'px'
+ });
+ }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+ return new Effect.Move(element,
+ Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
+};
+
+Effect.Scale = Class.create();
+Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
+ initialize: function(element, percent) {
+ this.element = $(element)
+ var options = Object.extend({
+ scaleX: true,
+ scaleY: true,
+ scaleContent: true,
+ scaleFromCenter: false,
+ scaleMode: 'box', // 'box' or 'contents' or {} with provided values
+ scaleFrom: 100.0,
+ scaleTo: percent
+ }, arguments[2] || {});
+ this.start(options);
+ },
+ setup: function() {
+ this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+ this.elementPositioning = this.element.getStyle('position');
+
+ this.originalStyle = {};
+ ['top','left','width','height','fontSize'].each( function(k) {
+ this.originalStyle[k] = this.element.style[k];
+ }.bind(this));
+
+ this.originalTop = this.element.offsetTop;
+ this.originalLeft = this.element.offsetLeft;
+
+ var fontSize = this.element.getStyle('font-size') || '100%';
+ ['em','px','%'].each( function(fontSizeType) {
+ if(fontSize.indexOf(fontSizeType)>0) {
+ this.fontSize = parseFloat(fontSize);
+ this.fontSizeType = fontSizeType;
+ }
+ }.bind(this));
+
+ this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+
+ this.dims = null;
+ if(this.options.scaleMode=='box')
+ this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+ if(/^content/.test(this.options.scaleMode))
+ this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+ if(!this.dims)
+ this.dims = [this.options.scaleMode.originalHeight,
+ this.options.scaleMode.originalWidth];
+ },
+ update: function(position) {
+ var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+ if(this.options.scaleContent && this.fontSize)
+ this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+ this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+ },
+ finish: function(position) {
+ if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+ },
+ setDimensions: function(height, width) {
+ var d = {};
+ if(this.options.scaleX) d.width = width + 'px';
+ if(this.options.scaleY) d.height = height + 'px';
+ if(this.options.scaleFromCenter) {
+ var topd = (height - this.dims[0])/2;
+ var leftd = (width - this.dims[1])/2;
+ if(this.elementPositioning == 'absolute') {
+ if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
+ if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+ } else {
+ if(this.options.scaleY) d.top = -topd + 'px';
+ if(this.options.scaleX) d.left = -leftd + 'px';
+ }
+ }
+ this.element.setStyle(d);
+ }
+});
+
+Effect.Highlight = Class.create();
+Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
+ this.start(options);
+ },
+ setup: function() {
+ // Prevent executing on elements not in the layout flow
+ if(this.element.getStyle('display')=='none') { this.cancel(); return; }
+ // Disable background image during the effect
+ this.oldStyle = {
+ backgroundImage: this.element.getStyle('background-image') };
+ this.element.setStyle({backgroundImage: 'none'});
+ if(!this.options.endcolor)
+ this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+ if(!this.options.restorecolor)
+ this.options.restorecolor = this.element.getStyle('background-color');
+ // init color calculations
+ this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+ this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+ },
+ update: function(position) {
+ this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+ return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
+ },
+ finish: function() {
+ this.element.setStyle(Object.extend(this.oldStyle, {
+ backgroundColor: this.options.restorecolor
+ }));
+ }
+});
+
+Effect.ScrollTo = Class.create();
+Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ this.start(arguments[1] || {});
+ },
+ setup: function() {
+ Position.prepare();
+ var offsets = Position.cumulativeOffset(this.element);
+ if(this.options.offset) offsets[1] += this.options.offset;
+ var max = window.innerHeight ?
+ window.height - window.innerHeight :
+ document.body.scrollHeight -
+ (document.documentElement.clientHeight ?
+ document.documentElement.clientHeight : document.body.clientHeight);
+ this.scrollStart = Position.deltaY;
+ this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
+ },
+ update: function(position) {
+ Position.prepare();
+ window.scrollTo(Position.deltaX,
+ this.scrollStart + (position*this.delta));
+ }
+});
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+ element = $(element);
+ var oldOpacity = element.getInlineOpacity();
+ var options = Object.extend({
+ from: element.getOpacity() || 1.0,
+ to: 0.0,
+ afterFinishInternal: function(effect) {
+ if(effect.options.to!=0) return;
+ effect.element.hide();
+ effect.element.setStyle({opacity: oldOpacity});
+ }}, arguments[1] || {});
+ return new Effect.Opacity(element,options);
+}
+
+Effect.Appear = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+ to: 1.0,
+ // force Safari to render floated elements properly
+ afterFinishInternal: function(effect) {
+ effect.element.forceRerendering();
+ },
+ beforeSetup: function(effect) {
+ effect.element.setOpacity(effect.options.from);
+ effect.element.show();
+ }}, arguments[1] || {});
+ return new Effect.Opacity(element,options);
+}
+
+Effect.Puff = function(element) {
+ element = $(element);
+ var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') };
+ return new Effect.Parallel(
+ [ new Effect.Scale(element, 200,
+ { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
+ Object.extend({ duration: 1.0,
+ beforeSetupInternal: function(effect) {
+ effect.effects[0].element.setStyle({position: 'absolute'}); },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide();
+ effect.effects[0].element.setStyle(oldStyle); }
+ }, arguments[1] || {})
+ );
+}
+
+Effect.BlindUp = function(element) {
+ element = $(element);
+ element.makeClipping();
+ return new Effect.Scale(element, 0,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ restoreAfterFinish: true,
+ afterFinishInternal: function(effect) {
+ effect.element.hide();
+ effect.element.undoClipping();
+ }
+ }, arguments[1] || {})
+ );
+}
+
+Effect.BlindDown = function(element) {
+ element = $(element);
+ var elementDimensions = element.getDimensions();
+ return new Effect.Scale(element, 100,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ scaleFrom: 0,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) {
+ effect.element.makeClipping();
+ effect.element.setStyle({height: '0px'});
+ effect.element.show();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.undoClipping();
+ }
+ }, arguments[1] || {})
+ );
+}
+
+Effect.SwitchOff = function(element) {
+ element = $(element);
+ var oldOpacity = element.getInlineOpacity();
+ return new Effect.Appear(element, {
+ duration: 0.4,
+ from: 0,
+ transition: Effect.Transitions.flicker,
+ afterFinishInternal: function(effect) {
+ new Effect.Scale(effect.element, 1, {
+ duration: 0.3, scaleFromCenter: true,
+ scaleX: false, scaleContent: false, restoreAfterFinish: true,
+ beforeSetup: function(effect) {
+ effect.element.makePositioned();
+ effect.element.makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide();
+ effect.element.undoClipping();
+ effect.element.undoPositioned();
+ effect.element.setStyle({opacity: oldOpacity});
+ }
+ })
+ }
+ });
+}
+
+Effect.DropOut = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: element.getStyle('top'),
+ left: element.getStyle('left'),
+ opacity: element.getInlineOpacity() };
+ return new Effect.Parallel(
+ [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+ Object.extend(
+ { duration: 0.5,
+ beforeSetup: function(effect) {
+ effect.effects[0].element.makePositioned();
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide();
+ effect.effects[0].element.undoPositioned();
+ effect.effects[0].element.setStyle(oldStyle);
+ }
+ }, arguments[1] || {}));
+}
+
+Effect.Shake = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: element.getStyle('top'),
+ left: element.getStyle('left') };
+ return new Effect.Move(element,
+ { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
+ effect.element.undoPositioned();
+ effect.element.setStyle(oldStyle);
+ }}) }}) }}) }}) }}) }});
+}
+
+Effect.SlideDown = function(element) {
+ element = $(element);
+ element.cleanWhitespace();
+ // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+ var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+ var elementDimensions = element.getDimensions();
+ return new Effect.Scale(element, 100, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ scaleFrom: window.opera ? 0 : 1,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) {
+ effect.element.makePositioned();
+ effect.element.firstChild.makePositioned();
+ if(window.opera) effect.element.setStyle({top: ''});
+ effect.element.makeClipping();
+ effect.element.setStyle({height: '0px'});
+ effect.element.show(); },
+ afterUpdateInternal: function(effect) {
+ effect.element.firstChild.setStyle({bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.undoClipping();
+ // IE will crash if child is undoPositioned first
+ if(/MSIE/.test(navigator.userAgent)){
+ effect.element.undoPositioned();
+ effect.element.firstChild.undoPositioned();
+ }else{
+ effect.element.firstChild.undoPositioned();
+ effect.element.undoPositioned();
+ }
+ effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
+ }, arguments[1] || {})
+ );
+}
+
+Effect.SlideUp = function(element) {
+ element = $(element);
+ element.cleanWhitespace();
+ var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+ return new Effect.Scale(element, window.opera ? 0 : 1,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ scaleMode: 'box',
+ scaleFrom: 100,
+ restoreAfterFinish: true,
+ beforeStartInternal: function(effect) {
+ effect.element.makePositioned();
+ effect.element.firstChild.makePositioned();
+ if(window.opera) effect.element.setStyle({top: ''});
+ effect.element.makeClipping();
+ effect.element.show(); },
+ afterUpdateInternal: function(effect) {
+ effect.element.firstChild.setStyle({bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' }); },
+ afterFinishInternal: function(effect) {
+ effect.element.hide();
+ effect.element.undoClipping();
+ effect.element.firstChild.undoPositioned();
+ effect.element.undoPositioned();
+ effect.element.setStyle({bottom: oldInnerBottom}); }
+ }, arguments[1] || {})
+ );
+}
+
+// Bug in opera makes the TD containing this element expand for a instance after finish
+Effect.Squish = function(element) {
+ return new Effect.Scale(element, window.opera ? 1 : 0,
+ { restoreAfterFinish: true,
+ beforeSetup: function(effect) {
+ effect.element.makeClipping(effect.element); },
+ afterFinishInternal: function(effect) {
+ effect.element.hide(effect.element);
+ effect.element.undoClipping(effect.element); }
+ });
+}
+
+Effect.Grow = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ direction: 'center',
+ moveTransition: Effect.Transitions.sinoidal,
+ scaleTransition: Effect.Transitions.sinoidal,
+ opacityTransition: Effect.Transitions.full
+ }, arguments[1] || {});
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ height: element.style.height,
+ width: element.style.width,
+ opacity: element.getInlineOpacity() };
+
+ var dims = element.getDimensions();
+ var initialMoveX, initialMoveY;
+ var moveX, moveY;
+
+ switch (options.direction) {
+ case 'top-left':
+ initialMoveX = initialMoveY = moveX = moveY = 0;
+ break;
+ case 'top-right':
+ initialMoveX = dims.width;
+ initialMoveY = moveY = 0;
+ moveX = -dims.width;
+ break;
+ case 'bottom-left':
+ initialMoveX = moveX = 0;
+ initialMoveY = dims.height;
+ moveY = -dims.height;
+ break;
+ case 'bottom-right':
+ initialMoveX = dims.width;
+ initialMoveY = dims.height;
+ moveX = -dims.width;
+ moveY = -dims.height;
+ break;
+ case 'center':
+ initialMoveX = dims.width / 2;
+ initialMoveY = dims.height / 2;
+ moveX = -dims.width / 2;
+ moveY = -dims.height / 2;
+ break;
+ }
+
+ return new Effect.Move(element, {
+ x: initialMoveX,
+ y: initialMoveY,
+ duration: 0.01,
+ beforeSetup: function(effect) {
+ effect.element.hide();
+ effect.element.makeClipping();
+ effect.element.makePositioned();
+ },
+ afterFinishInternal: function(effect) {
+ new Effect.Parallel(
+ [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+ new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+ new Effect.Scale(effect.element, 100, {
+ scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
+ sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+ ], Object.extend({
+ beforeSetup: function(effect) {
+ effect.effects[0].element.setStyle({height: '0px'});
+ effect.effects[0].element.show();
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.undoClipping();
+ effect.effects[0].element.undoPositioned();
+ effect.effects[0].element.setStyle(oldStyle);
+ }
+ }, options)
+ )
+ }
+ });
+}
+
+Effect.Shrink = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ direction: 'center',
+ moveTransition: Effect.Transitions.sinoidal,
+ scaleTransition: Effect.Transitions.sinoidal,
+ opacityTransition: Effect.Transitions.none
+ }, arguments[1] || {});
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ height: element.style.height,
+ width: element.style.width,
+ opacity: element.getInlineOpacity() };
+
+ var dims = element.getDimensions();
+ var moveX, moveY;
+
+ switch (options.direction) {
+ case 'top-left':
+ moveX = moveY = 0;
+ break;
+ case 'top-right':
+ moveX = dims.width;
+ moveY = 0;
+ break;
+ case 'bottom-left':
+ moveX = 0;
+ moveY = dims.height;
+ break;
+ case 'bottom-right':
+ moveX = dims.width;
+ moveY = dims.height;
+ break;
+ case 'center':
+ moveX = dims.width / 2;
+ moveY = dims.height / 2;
+ break;
+ }
+
+ return new Effect.Parallel(
+ [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+ new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+ new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+ ], Object.extend({
+ beforeStartInternal: function(effect) {
+ effect.effects[0].element.makePositioned();
+ effect.effects[0].element.makeClipping(); },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide();
+ effect.effects[0].element.undoClipping();
+ effect.effects[0].element.undoPositioned();
+ effect.effects[0].element.setStyle(oldStyle); }
+ }, options)
+ );
+}
+
+Effect.Pulsate = function(element) {
+ element = $(element);
+ var options = arguments[1] || {};
+ var oldOpacity = element.getInlineOpacity();
+ var transition = options.transition || Effect.Transitions.sinoidal;
+ var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
+ reverser.bind(transition);
+ return new Effect.Opacity(element,
+ Object.extend(Object.extend({ duration: 3.0, from: 0,
+ afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+ }, options), {transition: reverser}));
+}
+
+Effect.Fold = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ width: element.style.width,
+ height: element.style.height };
+ Element.makeClipping(element);
+ return new Effect.Scale(element, 5, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ afterFinishInternal: function(effect) {
+ new Effect.Scale(element, 1, {
+ scaleContent: false,
+ scaleY: false,
+ afterFinishInternal: function(effect) {
+ effect.element.hide();
+ effect.element.undoClipping();
+ effect.element.setStyle(oldStyle);
+ } });
+ }}, arguments[1] || {}));
+};
+
+['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
+ 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each(
+ function(f) { Element.Methods[f] = Element[f]; }
+);
+
+Element.Methods.visualEffect = function(element, effect, options) {
+ s = effect.gsub(/_/, '-').camelize();
+ effect_class = s.charAt(0).toUpperCase() + s.substring(1);
+ new Effect[effect_class](element, options);
+ return $(element);
+};
+
+Element.addMethods();
\ No newline at end of file
diff --git a/posterita/src/web/javascripts/js-calendar/calendar-en.js b/posterita/src/web/javascripts/js-calendar/calendar-en.js
new file mode 100644
index 0000000000..0dbde793d8
--- /dev/null
+++ b/posterita/src/web/javascripts/js-calendar/calendar-en.js
@@ -0,0 +1,127 @@
+// ** I18N
+
+// Calendar EN language
+// Author: Mihai Bazon,
+// Encoding: any
+// Distributed under the same terms as the calendar itself.
+
+// For translators: please use UTF-8 if possible. We strongly believe that
+// Unicode is the answer to a real internationalized world. Also please
+// include your contact information in the header, as can be seen above.
+
+// full day names
+Calendar._DN = new Array
+("Sunday",
+ "Monday",
+ "Tuesday",
+ "Wednesday",
+ "Thursday",
+ "Friday",
+ "Saturday",
+ "Sunday");
+
+// Please note that the following array of short day names (and the same goes
+// for short month names, _SMN) isn't absolutely necessary. We give it here
+// for exemplification on how one can customize the short day names, but if
+// they are simply the first N letters of the full name you can simply say:
+//
+// Calendar._SDN_len = N; // short day name length
+// Calendar._SMN_len = N; // short month name length
+//
+// If N = 3 then this is not needed either since we assume a value of 3 if not
+// present, to be compatible with translation files that were written before
+// this feature.
+
+// short day names
+Calendar._SDN = new Array
+("Sun",
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat",
+ "Sun");
+
+// First day of the week. "0" means display Sunday first, "1" means display
+// Monday first, etc.
+Calendar._FD = 0;
+
+// full month names
+Calendar._MN = new Array
+("January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December");
+
+// short month names
+Calendar._SMN = new Array
+("Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec");
+
+// tooltips
+Calendar._TT = {};
+Calendar._TT["INFO"] = "About the calendar";
+
+Calendar._TT["ABOUT"] =
+"DHTML Date/Time Selector\n" +
+"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-)
+"For latest version visit: http://www.dynarch.com/projects/calendar/\n" +
+"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." +
+"\n\n" +
+"Date selection:\n" +
+"- Use the \xab, \xbb buttons to select year\n" +
+"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" +
+"- Hold mouse button on any of the above buttons for faster selection.";
+Calendar._TT["ABOUT_TIME"] = "\n\n" +
+"Time selection:\n" +
+"- Click on any of the time parts to increase it\n" +
+"- or Shift-click to decrease it\n" +
+"- or click and drag for faster selection.";
+
+Calendar._TT["PREV_YEAR"] = "Prev. year (hold for menu)";
+Calendar._TT["PREV_MONTH"] = "Prev. month (hold for menu)";
+Calendar._TT["GO_TODAY"] = "Go Today";
+Calendar._TT["NEXT_MONTH"] = "Next month (hold for menu)";
+Calendar._TT["NEXT_YEAR"] = "Next year (hold for menu)";
+Calendar._TT["SEL_DATE"] = "Select date";
+Calendar._TT["DRAG_TO_MOVE"] = "Drag to move";
+Calendar._TT["PART_TODAY"] = " (today)";
+
+// the following is to inform that "%s" is to be the first day of week
+// %s will be replaced with the day name.
+Calendar._TT["DAY_FIRST"] = "Display %s first";
+
+// This may be locale-dependent. It specifies the week-end days, as an array
+// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1
+// means Monday, etc.
+Calendar._TT["WEEKEND"] = "0,6";
+
+Calendar._TT["CLOSE"] = "Close";
+Calendar._TT["TODAY"] = "Today";
+Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value";
+
+// date formats
+Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d";
+Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e";
+
+Calendar._TT["WK"] = "wk";
+Calendar._TT["TIME"] = "Time:";
diff --git a/posterita/src/web/javascripts/js-calendar/calendar-setup.js b/posterita/src/web/javascripts/js-calendar/calendar-setup.js
new file mode 100644
index 0000000000..f2b4854308
--- /dev/null
+++ b/posterita/src/web/javascripts/js-calendar/calendar-setup.js
@@ -0,0 +1,200 @@
+/* Copyright Mihai Bazon, 2002, 2003 | http://dynarch.com/mishoo/
+ * ---------------------------------------------------------------------------
+ *
+ * The DHTML Calendar
+ *
+ * Details and latest version at:
+ * http://dynarch.com/mishoo/calendar.epl
+ *
+ * This script is distributed under the GNU Lesser General Public License.
+ * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
+ *
+ * This file defines helper functions for setting up the calendar. They are
+ * intended to help non-programmers get a working calendar on their site
+ * quickly. This script should not be seen as part of the calendar. It just
+ * shows you what one can do with the calendar, while in the same time
+ * providing a quick and simple method for setting it up. If you need
+ * exhaustive customization of the calendar creation process feel free to
+ * modify this code to suit your needs (this is recommended and much better
+ * than modifying calendar.js itself).
+ */
+
+// $Id: calendar-setup.js,v 1.25 2005/03/07 09:51:33 mishoo Exp $
+
+/**
+ * This function "patches" an input field (or other element) to use a calendar
+ * widget for date selection.
+ *
+ * The "params" is a single object that can have the following properties:
+ *
+ * prop. name | description
+ * -------------------------------------------------------------------------------------------------
+ * inputField | the ID of an input field to store the date
+ * displayArea | the ID of a DIV or other element to show the date
+ * button | ID of a button or other element that will trigger the calendar
+ * eventName | event that will trigger the calendar, without the "on" prefix (default: "click")
+ * ifFormat | date format that will be stored in the input field
+ * daFormat | the date format that will be used to display the date in displayArea
+ * singleClick | (true/false) wether the calendar is in single click mode or not (default: true)
+ * firstDay | numeric: 0 to 6. "0" means display Sunday first, "1" means display Monday first, etc.
+ * align | alignment (default: "Br"); if you don't know what's this see the calendar documentation
+ * range | array with 2 elements. Default: [1900, 2999] -- the range of years available
+ * weekNumbers | (true/false) if it's true (default) the calendar will display week numbers
+ * flat | null or element ID; if not null the calendar will be a flat calendar having the parent with the given ID
+ * flatCallback | function that receives a JS Date object and returns an URL to point the browser to (for flat calendar)
+ * disableFunc | function that receives a JS Date object and should return true if that date has to be disabled in the calendar
+ * onSelect | function that gets called when a date is selected. You don't _have_ to supply this (the default is generally okay)
+ * onClose | function that gets called when the calendar is closed. [default]
+ * onUpdate | function that gets called after the date is updated in the input field. Receives a reference to the calendar.
+ * date | the date that the calendar will be initially displayed to
+ * showsTime | default: false; if true the calendar will include a time selector
+ * timeFormat | the time format; can be "12" or "24", default is "12"
+ * electric | if true (default) then given fields/date areas are updated for each move; otherwise they're updated only on close
+ * step | configures the step of the years in drop-down boxes; default: 2
+ * position | configures the calendar absolute position; default: null
+ * cache | if "true" (but default: "false") it will reuse the same calendar object, where possible
+ * showOthers | if "true" (but default: "false") it will show days from other months too
+ *
+ * None of them is required, they all have default values. However, if you
+ * pass none of "inputField", "displayArea" or "button" you'll get a warning
+ * saying "nothing to setup".
+ */
+Calendar.setup = function (params) {
+ function param_default(pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };
+
+ param_default("inputField", null);
+ param_default("displayArea", null);
+ param_default("button", null);
+ param_default("eventName", "click");
+ param_default("ifFormat", "%Y/%m/%d");
+ param_default("daFormat", "%Y/%m/%d");
+ param_default("singleClick", true);
+ param_default("disableFunc", null);
+ param_default("dateStatusFunc", params["disableFunc"]); // takes precedence if both are defined
+ param_default("dateText", null);
+ param_default("firstDay", null);
+ param_default("align", "Br");
+ param_default("range", [1900, 2999]);
+ param_default("weekNumbers", true);
+ param_default("flat", null);
+ param_default("flatCallback", null);
+ param_default("onSelect", null);
+ param_default("onClose", null);
+ param_default("onUpdate", null);
+ param_default("date", null);
+ param_default("showsTime", false);
+ param_default("timeFormat", "24");
+ param_default("electric", true);
+ param_default("step", 2);
+ param_default("position", null);
+ param_default("cache", false);
+ param_default("showOthers", false);
+ param_default("multiple", null);
+
+ var tmp = ["inputField", "displayArea", "button"];
+ for (var i in tmp) {
+ if (typeof params[tmp[i]] == "string") {
+ params[tmp[i]] = document.getElementById(params[tmp[i]]);
+ }
+ }
+ if (!(params.flat || params.multiple || params.inputField || params.displayArea || params.button)) {
+ alert("Calendar.setup:\n Nothing to setup (no fields found). Please check your code");
+ return false;
+ }
+
+ function onSelect(cal) {
+ var p = cal.params;
+ var update = (cal.dateClicked || p.electric);
+ if (update && p.inputField) {
+ p.inputField.value = cal.date.print(p.ifFormat);
+ if (typeof p.inputField.onchange == "function")
+ p.inputField.onchange();
+ }
+ if (update && p.displayArea)
+ p.displayArea.innerHTML = cal.date.print(p.daFormat);
+ if (update && typeof p.onUpdate == "function")
+ p.onUpdate(cal);
+ if (update && p.flat) {
+ if (typeof p.flatCallback == "function")
+ p.flatCallback(cal);
+ }
+ if (update && p.singleClick && cal.dateClicked)
+ cal.callCloseHandler();
+ };
+
+ if (params.flat != null) {
+ if (typeof params.flat == "string")
+ params.flat = document.getElementById(params.flat);
+ if (!params.flat) {
+ alert("Calendar.setup:\n Flat specified but can't find parent.");
+ return false;
+ }
+ var cal = new Calendar(params.firstDay, params.date, params.onSelect || onSelect);
+ cal.showsOtherMonths = params.showOthers;
+ cal.showsTime = params.showsTime;
+ cal.time24 = (params.timeFormat == "24");
+ cal.params = params;
+ cal.weekNumbers = params.weekNumbers;
+ cal.setRange(params.range[0], params.range[1]);
+ cal.setDateStatusHandler(params.dateStatusFunc);
+ cal.getDateText = params.dateText;
+ if (params.ifFormat) {
+ cal.setDateFormat(params.ifFormat);
+ }
+ if (params.inputField && typeof params.inputField.value == "string") {
+ cal.parseDate(params.inputField.value);
+ }
+ cal.create(params.flat);
+ cal.show();
+ return false;
+ }
+
+ var triggerEl = params.button || params.displayArea || params.inputField;
+ triggerEl["on" + params.eventName] = function() {
+ var dateEl = params.inputField || params.displayArea;
+ var dateFmt = params.inputField ? params.ifFormat : params.daFormat;
+ var mustCreate = false;
+ var cal = window.calendar;
+ if (dateEl)
+ params.date = Date.parseDate(dateEl.value || dateEl.innerHTML, dateFmt);
+ if (!(cal && params.cache)) {
+ window.calendar = cal = new Calendar(params.firstDay,
+ params.date,
+ params.onSelect || onSelect,
+ params.onClose || function(cal) { cal.hide(); });
+ cal.showsTime = params.showsTime;
+ cal.time24 = (params.timeFormat == "24");
+ cal.weekNumbers = params.weekNumbers;
+ mustCreate = true;
+ } else {
+ if (params.date)
+ cal.setDate(params.date);
+ cal.hide();
+ }
+ if (params.multiple) {
+ cal.multiple = {};
+ for (var i = params.multiple.length; --i >= 0;) {
+ var d = params.multiple[i];
+ var ds = d.print("%Y%m%d");
+ cal.multiple[ds] = d;
+ }
+ }
+ cal.showsOtherMonths = params.showOthers;
+ cal.yearStep = params.step;
+ cal.setRange(params.range[0], params.range[1]);
+ cal.params = params;
+ cal.setDateStatusHandler(params.dateStatusFunc);
+ cal.getDateText = params.dateText;
+ cal.setDateFormat(dateFmt);
+ if (mustCreate)
+ cal.create();
+ cal.refresh();
+ if (!params.position)
+ cal.showAtElement(params.button || params.displayArea || params.inputField, params.align);
+ else
+ cal.showAt(params.position[0], params.position[1]);
+ return false;
+ };
+
+ return cal;
+};
diff --git a/posterita/src/web/javascripts/js-calendar/calendar.js b/posterita/src/web/javascripts/js-calendar/calendar.js
new file mode 100644
index 0000000000..9088e0e897
--- /dev/null
+++ b/posterita/src/web/javascripts/js-calendar/calendar.js
@@ -0,0 +1,1806 @@
+/* Copyright Mihai Bazon, 2002-2005 | www.bazon.net/mishoo
+ * -----------------------------------------------------------
+ *
+ * The DHTML Calendar, version 1.0 "It is happening again"
+ *
+ * Details and latest version at:
+ * www.dynarch.com/projects/calendar
+ *
+ * This script is developed by Dynarch.com. Visit us at www.dynarch.com.
+ *
+ * This script is distributed under the GNU Lesser General Public License.
+ * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
+ */
+
+// $Id: calendar.js,v 1.51 2005/03/07 16:44:31 mishoo Exp $
+
+/** The Calendar object constructor. */
+Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose) {
+ // member variables
+ this.activeDiv = null;
+ this.currentDateEl = null;
+ this.getDateStatus = null;
+ this.getDateToolTip = null;
+ this.getDateText = null;
+ this.timeout = null;
+ this.onSelected = onSelected || null;
+ this.onClose = onClose || null;
+ this.dragging = false;
+ this.hidden = false;
+ this.minYear = 1970;
+ this.maxYear = 2050;
+ this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"];
+ this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"];
+ this.isPopup = true;
+ this.weekNumbers = true;
+ this.firstDayOfWeek = typeof firstDayOfWeek == "number" ? firstDayOfWeek : Calendar._FD; // 0 for Sunday, 1 for Monday, etc.
+ this.showsOtherMonths = false;
+ this.dateStr = dateStr;
+ this.ar_days = null;
+ this.showsTime = false;
+ this.time24 = true;
+ this.yearStep = 2;
+ this.hiliteToday = true;
+ this.multiple = null;
+ // HTML elements
+ this.table = null;
+ this.element = null;
+ this.tbody = null;
+ this.firstdayname = null;
+ // Combo boxes
+ this.monthsCombo = null;
+ this.yearsCombo = null;
+ this.hilitedMonth = null;
+ this.activeMonth = null;
+ this.hilitedYear = null;
+ this.activeYear = null;
+ // Information
+ this.dateClicked = false;
+
+ // one-time initializations
+ if (typeof Calendar._SDN == "undefined") {
+ // table of short day names
+ if (typeof Calendar._SDN_len == "undefined")
+ Calendar._SDN_len = 3;
+ var ar = new Array();
+ for (var i = 8; i > 0;) {
+ ar[--i] = Calendar._DN[i].substr(0, Calendar._SDN_len);
+ }
+ Calendar._SDN = ar;
+ // table of short month names
+ if (typeof Calendar._SMN_len == "undefined")
+ Calendar._SMN_len = 3;
+ ar = new Array();
+ for (var i = 12; i > 0;) {
+ ar[--i] = Calendar._MN[i].substr(0, Calendar._SMN_len);
+ }
+ Calendar._SMN = ar;
+ }
+};
+
+// ** constants
+
+/// "static", needed for event handlers.
+Calendar._C = null;
+
+/// detect a special case of "web browser"
+Calendar.is_ie = ( /msie/i.test(navigator.userAgent) &&
+ !/opera/i.test(navigator.userAgent) );
+
+Calendar.is_ie5 = ( Calendar.is_ie && /msie 5\.0/i.test(navigator.userAgent) );
+
+/// detect Opera browser
+Calendar.is_opera = /opera/i.test(navigator.userAgent);
+
+/// detect KHTML-based browsers
+Calendar.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);
+
+// BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate
+// library, at some point.
+
+Calendar.getAbsolutePos = function(el) {
+ var SL = 0, ST = 0;
+ var is_div = /^div$/i.test(el.tagName);
+ if (is_div && el.scrollLeft)
+ SL = el.scrollLeft;
+ if (is_div && el.scrollTop)
+ ST = el.scrollTop;
+ var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
+ if (el.offsetParent) {
+ var tmp = this.getAbsolutePos(el.offsetParent);
+ r.x += tmp.x;
+ r.y += tmp.y;
+ }
+ return r;
+};
+
+Calendar.isRelated = function (el, evt) {
+ var related = evt.relatedTarget;
+ if (!related) {
+ var type = evt.type;
+ if (type == "mouseover") {
+ related = evt.fromElement;
+ } else if (type == "mouseout") {
+ related = evt.toElement;
+ }
+ }
+ while (related) {
+ if (related == el) {
+ return true;
+ }
+ related = related.parentNode;
+ }
+ return false;
+};
+
+Calendar.removeClass = function(el, className) {
+ if (!(el && el.className)) {
+ return;
+ }
+ var cls = el.className.split(" ");
+ var ar = new Array();
+ for (var i = cls.length; i > 0;) {
+ if (cls[--i] != className) {
+ ar[ar.length] = cls[i];
+ }
+ }
+ el.className = ar.join(" ");
+};
+
+Calendar.addClass = function(el, className) {
+ Calendar.removeClass(el, className);
+ el.className += " " + className;
+};
+
+// FIXME: the following 2 functions totally suck, are useless and should be replaced immediately.
+Calendar.getElement = function(ev) {
+ var f = Calendar.is_ie ? window.event.srcElement : ev.currentTarget;
+ while (f.nodeType != 1 || /^div$/i.test(f.tagName))
+ f = f.parentNode;
+ return f;
+};
+
+Calendar.getTargetElement = function(ev) {
+ var f = Calendar.is_ie ? window.event.srcElement : ev.target;
+ while (f.nodeType != 1)
+ f = f.parentNode;
+ return f;
+};
+
+Calendar.stopEvent = function(ev) {
+ ev || (ev = window.event);
+ if (Calendar.is_ie) {
+ ev.cancelBubble = true;
+ ev.returnValue = false;
+ } else {
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+ return false;
+};
+
+Calendar.addEvent = function(el, evname, func) {
+ if (el.attachEvent) { // IE
+ el.attachEvent("on" + evname, func);
+ } else if (el.addEventListener) { // Gecko / W3C
+ el.addEventListener(evname, func, true);
+ } else {
+ el["on" + evname] = func;
+ }
+};
+
+Calendar.removeEvent = function(el, evname, func) {
+ if (el.detachEvent) { // IE
+ el.detachEvent("on" + evname, func);
+ } else if (el.removeEventListener) { // Gecko / W3C
+ el.removeEventListener(evname, func, true);
+ } else {
+ el["on" + evname] = null;
+ }
+};
+
+Calendar.createElement = function(type, parent) {
+ var el = null;
+ if (document.createElementNS) {
+ // use the XHTML namespace; IE won't normally get here unless
+ // _they_ "fix" the DOM2 implementation.
+ el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
+ } else {
+ el = document.createElement(type);
+ }
+ if (typeof parent != "undefined") {
+ parent.appendChild(el);
+ }
+ return el;
+};
+
+// END: UTILITY FUNCTIONS
+
+// BEGIN: CALENDAR STATIC FUNCTIONS
+
+/** Internal -- adds a set of events to make some element behave like a button. */
+Calendar._add_evs = function(el) {
+ with (Calendar) {
+ addEvent(el, "mouseover", dayMouseOver);
+ addEvent(el, "mousedown", dayMouseDown);
+ addEvent(el, "mouseout", dayMouseOut);
+ if (is_ie) {
+ addEvent(el, "dblclick", dayMouseDblClick);
+ el.setAttribute("unselectable", true);
+ }
+ }
+};
+
+Calendar.findMonth = function(el) {
+ if (typeof el.month != "undefined") {
+ return el;
+ } else if (typeof el.parentNode.month != "undefined") {
+ return el.parentNode;
+ }
+ return null;
+};
+
+Calendar.findYear = function(el) {
+ if (typeof el.year != "undefined") {
+ return el;
+ } else if (typeof el.parentNode.year != "undefined") {
+ return el.parentNode;
+ }
+ return null;
+};
+
+Calendar.showMonthsCombo = function () {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ var cal = cal;
+ var cd = cal.activeDiv;
+ var mc = cal.monthsCombo;
+ if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ if (cal.activeMonth) {
+ Calendar.removeClass(cal.activeMonth, "active");
+ }
+ var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];
+ Calendar.addClass(mon, "active");
+ cal.activeMonth = mon;
+ var s = mc.style;
+ s.display = "block";
+ if (cd.navtype < 0)
+ s.left = cd.offsetLeft + "px";
+ else {
+ var mcw = mc.offsetWidth;
+ if (typeof mcw == "undefined")
+ // Konqueror brain-dead techniques
+ mcw = 50;
+ s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px";
+ }
+ s.top = (cd.offsetTop + cd.offsetHeight) + "px";
+};
+
+Calendar.showYearsCombo = function (fwd) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ var cal = cal;
+ var cd = cal.activeDiv;
+ var yc = cal.yearsCombo;
+ if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ if (cal.activeYear) {
+ Calendar.removeClass(cal.activeYear, "active");
+ }
+ cal.activeYear = null;
+ var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
+ var yr = yc.firstChild;
+ var show = false;
+ for (var i = 12; i > 0; --i) {
+ if (Y >= cal.minYear && Y <= cal.maxYear) {
+ yr.innerHTML = Y;
+ yr.year = Y;
+ yr.style.display = "block";
+ show = true;
+ } else {
+ yr.style.display = "none";
+ }
+ yr = yr.nextSibling;
+ Y += fwd ? cal.yearStep : -cal.yearStep;
+ }
+ if (show) {
+ var s = yc.style;
+ s.display = "block";
+ if (cd.navtype < 0)
+ s.left = cd.offsetLeft + "px";
+ else {
+ var ycw = yc.offsetWidth;
+ if (typeof ycw == "undefined")
+ // Konqueror brain-dead techniques
+ ycw = 50;
+ s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px";
+ }
+ s.top = (cd.offsetTop + cd.offsetHeight) + "px";
+ }
+};
+
+// event handlers
+
+Calendar.tableMouseUp = function(ev) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ if (cal.timeout) {
+ clearTimeout(cal.timeout);
+ }
+ var el = cal.activeDiv;
+ if (!el) {
+ return false;
+ }
+ var target = Calendar.getTargetElement(ev);
+ ev || (ev = window.event);
+ Calendar.removeClass(el, "active");
+ if (target == el || target.parentNode == el) {
+ Calendar.cellClick(el, ev);
+ }
+ var mon = Calendar.findMonth(target);
+ var date = null;
+ if (mon) {
+ date = new Date(cal.date);
+ if (mon.month != date.getMonth()) {
+ date.setMonth(mon.month);
+ cal.setDate(date);
+ cal.dateClicked = false;
+ cal.callHandler();
+ }
+ } else {
+ var year = Calendar.findYear(target);
+ if (year) {
+ date = new Date(cal.date);
+ if (year.year != date.getFullYear()) {
+ date.setFullYear(year.year);
+ cal.setDate(date);
+ cal.dateClicked = false;
+ cal.callHandler();
+ }
+ }
+ }
+ with (Calendar) {
+ removeEvent(document, "mouseup", tableMouseUp);
+ removeEvent(document, "mouseover", tableMouseOver);
+ removeEvent(document, "mousemove", tableMouseOver);
+ cal._hideCombos();
+ _C = null;
+ return stopEvent(ev);
+ }
+};
+
+Calendar.tableMouseOver = function (ev) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return;
+ }
+ var el = cal.activeDiv;
+ var target = Calendar.getTargetElement(ev);
+ if (target == el || target.parentNode == el) {
+ Calendar.addClass(el, "hilite active");
+ Calendar.addClass(el.parentNode, "rowhilite");
+ } else {
+ if (typeof el.navtype == "undefined" || (el.navtype != 50 && (el.navtype == 0 || Math.abs(el.navtype) > 2)))
+ Calendar.removeClass(el, "active");
+ Calendar.removeClass(el, "hilite");
+ Calendar.removeClass(el.parentNode, "rowhilite");
+ }
+ ev || (ev = window.event);
+ if (el.navtype == 50 && target != el) {
+ var pos = Calendar.getAbsolutePos(el);
+ var w = el.offsetWidth;
+ var x = ev.clientX;
+ var dx;
+ var decrease = true;
+ if (x > pos.x + w) {
+ dx = x - pos.x - w;
+ decrease = false;
+ } else
+ dx = pos.x - x;
+
+ if (dx < 0) dx = 0;
+ var range = el._range;
+ var current = el._current;
+ var count = Math.floor(dx / 10) % range.length;
+ for (var i = range.length; --i >= 0;)
+ if (range[i] == current)
+ break;
+ while (count-- > 0)
+ if (decrease) {
+ if (--i < 0)
+ i = range.length - 1;
+ } else if ( ++i >= range.length )
+ i = 0;
+ var newval = range[i];
+ el.innerHTML = newval;
+
+ cal.onUpdateTime();
+ }
+ var mon = Calendar.findMonth(target);
+ if (mon) {
+ if (mon.month != cal.date.getMonth()) {
+ if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ Calendar.addClass(mon, "hilite");
+ cal.hilitedMonth = mon;
+ } else if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ } else {
+ if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ var year = Calendar.findYear(target);
+ if (year) {
+ if (year.year != cal.date.getFullYear()) {
+ if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ Calendar.addClass(year, "hilite");
+ cal.hilitedYear = year;
+ } else if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ } else if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ }
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.tableMouseDown = function (ev) {
+ if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) {
+ return Calendar.stopEvent(ev);
+ }
+};
+
+Calendar.calDragIt = function (ev) {
+ var cal = Calendar._C;
+ if (!(cal && cal.dragging)) {
+ return false;
+ }
+ var posX;
+ var posY;
+ if (Calendar.is_ie) {
+ posY = window.event.clientY + document.body.scrollTop;
+ posX = window.event.clientX + document.body.scrollLeft;
+ } else {
+ posX = ev.pageX;
+ posY = ev.pageY;
+ }
+ cal.hideShowCovered();
+ var st = cal.element.style;
+ st.left = (posX - cal.xOffs) + "px";
+ st.top = (posY - cal.yOffs) + "px";
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.calDragEnd = function (ev) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ cal.dragging = false;
+ with (Calendar) {
+ removeEvent(document, "mousemove", calDragIt);
+ removeEvent(document, "mouseup", calDragEnd);
+ tableMouseUp(ev);
+ }
+ cal.hideShowCovered();
+};
+
+Calendar.dayMouseDown = function(ev) {
+ var el = Calendar.getElement(ev);
+ if (el.disabled) {
+ return false;
+ }
+ var cal = el.calendar;
+ cal.activeDiv = el;
+ Calendar._C = cal;
+ if (el.navtype != 300) with (Calendar) {
+ if (el.navtype == 50) {
+ el._current = el.innerHTML;
+ addEvent(document, "mousemove", tableMouseOver);
+ } else
+ addEvent(document, Calendar.is_ie5 ? "mousemove" : "mouseover", tableMouseOver);
+ addClass(el, "hilite active");
+ addEvent(document, "mouseup", tableMouseUp);
+ } else if (cal.isPopup) {
+ cal._dragStart(ev);
+ }
+ if (el.navtype == -1 || el.navtype == 1) {
+ if (cal.timeout) clearTimeout(cal.timeout);
+ cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250);
+ } else if (el.navtype == -2 || el.navtype == 2) {
+ if (cal.timeout) clearTimeout(cal.timeout);
+ cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250);
+ } else {
+ cal.timeout = null;
+ }
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.dayMouseDblClick = function(ev) {
+ Calendar.cellClick(Calendar.getElement(ev), ev || window.event);
+ if (Calendar.is_ie) {
+ document.selection.empty();
+ }
+};
+
+Calendar.dayMouseOver = function(ev) {
+ var el = Calendar.getElement(ev);
+ if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) {
+ return false;
+ }
+ if (el.ttip) {
+ if (el.ttip.substr(0, 1) == "_") {
+ el.ttip = el.caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1);
+ }
+ el.calendar.tooltips.innerHTML = el.ttip;
+ }
+ if (el.navtype != 300) {
+ Calendar.addClass(el, "hilite");
+ if (el.caldate) {
+ Calendar.addClass(el.parentNode, "rowhilite");
+ }
+ }
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.dayMouseOut = function(ev) {
+ with (Calendar) {
+ var el = getElement(ev);
+ if (isRelated(el, ev) || _C || el.disabled)
+ return false;
+ removeClass(el, "hilite");
+ if (el.caldate)
+ removeClass(el.parentNode, "rowhilite");
+ if (el.calendar)
+ el.calendar.tooltips.innerHTML = _TT["SEL_DATE"];
+ return stopEvent(ev);
+ }
+};
+
+/**
+ * A generic "click" handler :) handles all types of buttons defined in this
+ * calendar.
+ */
+Calendar.cellClick = function(el, ev) {
+ var cal = el.calendar;
+ var closing = false;
+ var newdate = false;
+ var date = null;
+ if (typeof el.navtype == "undefined") {
+ if (cal.currentDateEl) {
+ Calendar.removeClass(cal.currentDateEl, "selected");
+ Calendar.addClass(el, "selected");
+ closing = (cal.currentDateEl == el);
+ if (!closing) {
+ cal.currentDateEl = el;
+ }
+ }
+ cal.date.setDateOnly(el.caldate);
+ date = cal.date;
+ var other_month = !(cal.dateClicked = !el.otherMonth);
+ if (!other_month && !cal.currentDateEl)
+ cal._toggleMultipleDate(new Date(date));
+ else
+ newdate = !el.disabled;
+ // a date was clicked
+ if (other_month)
+ cal._init(cal.firstDayOfWeek, date);
+ } else {
+ if (el.navtype == 200) {
+ Calendar.removeClass(el, "hilite");
+ cal.callCloseHandler();
+ return;
+ }
+ date = new Date(cal.date);
+ if (el.navtype == 0)
+ date.setDateOnly(new Date()); // TODAY
+ // unless "today" was clicked, we assume no date was clicked so
+ // the selected handler will know not to close the calenar when
+ // in single-click mode.
+ // cal.dateClicked = (el.navtype == 0);
+ cal.dateClicked = false;
+ var year = date.getFullYear();
+ var mon = date.getMonth();
+ function setMonth(m) {
+ var day = date.getDate();
+ var max = date.getMonthDays(m);
+ if (day > max) {
+ date.setDate(max);
+ }
+ date.setMonth(m);
+ };
+ switch (el.navtype) {
+ case 400:
+ Calendar.removeClass(el, "hilite");
+ var text = Calendar._TT["ABOUT"];
+ if (typeof text != "undefined") {
+ text += cal.showsTime ? Calendar._TT["ABOUT_TIME"] : "";
+ } else {
+ // FIXME: this should be removed as soon as lang files get updated!
+ text = "Help and about box text is not translated into this language.\n" +
+ "If you know this language and you feel generous please update\n" +
+ "the corresponding file in \"lang\" subdir to match calendar-en.js\n" +
+ "and send it back to to get it into the distribution ;-)\n\n" +
+ "Thank you!\n" +
+ "http://dynarch.com/mishoo/calendar.epl\n";
+ }
+ alert(text);
+ return;
+ case -2:
+ if (year > cal.minYear) {
+ date.setFullYear(year - 1);
+ }
+ break;
+ case -1:
+ if (mon > 0) {
+ setMonth(mon - 1);
+ } else if (year-- > cal.minYear) {
+ date.setFullYear(year);
+ setMonth(11);
+ }
+ break;
+ case 1:
+ if (mon < 11) {
+ setMonth(mon + 1);
+ } else if (year < cal.maxYear) {
+ date.setFullYear(year + 1);
+ setMonth(0);
+ }
+ break;
+ case 2:
+ if (year < cal.maxYear) {
+ date.setFullYear(year + 1);
+ }
+ break;
+ case 100:
+ cal.setFirstDayOfWeek(el.fdow);
+ return;
+ case 50:
+ var range = el._range;
+ var current = el.innerHTML;
+ for (var i = range.length; --i >= 0;)
+ if (range[i] == current)
+ break;
+ if (ev && ev.shiftKey) {
+ if (--i < 0)
+ i = range.length - 1;
+ } else if ( ++i >= range.length )
+ i = 0;
+ var newval = range[i];
+ el.innerHTML = newval;
+ cal.onUpdateTime();
+ return;
+ case 0:
+ // TODAY will bring us here
+ if ((typeof cal.getDateStatus == "function") &&
+ cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate())) {
+ return false;
+ }
+ break;
+ }
+ if (!date.equalsTo(cal.date)) {
+ cal.setDate(date);
+ newdate = true;
+ } else if (el.navtype == 0)
+ newdate = closing = true;
+ }
+ if (newdate) {
+ ev && cal.callHandler();
+ }
+ if (closing) {
+ Calendar.removeClass(el, "hilite");
+ ev && cal.callCloseHandler();
+ }
+};
+
+// END: CALENDAR STATIC FUNCTIONS
+
+// BEGIN: CALENDAR OBJECT FUNCTIONS
+
+/**
+ * This function creates the calendar inside the given parent. If _par is
+ * null than it creates a popup calendar inside the BODY element. If _par is
+ * an element, be it BODY, then it creates a non-popup calendar (still
+ * hidden). Some properties need to be set before calling this function.
+ */
+Calendar.prototype.create = function (_par) {
+ var parent = null;
+ if (! _par) {
+ // default parent is the document body, in which case we create
+ // a popup calendar.
+ parent = document.getElementsByTagName("body")[0];
+ this.isPopup = true;
+ } else {
+ parent = _par;
+ this.isPopup = false;
+ }
+ this.date = this.dateStr ? new Date(this.dateStr) : new Date();
+
+ var table = Calendar.createElement("table");
+ this.table = table;
+ table.cellSpacing = 0;
+ table.cellPadding = 0;
+ table.calendar = this;
+ Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown);
+
+ var div = Calendar.createElement("div");
+ this.element = div;
+ div.className = "calendar";
+ if (this.isPopup) {
+ div.style.position = "absolute";
+ div.style.display = "none";
+ }
+ div.appendChild(table);
+
+ var thead = Calendar.createElement("thead", table);
+ var cell = null;
+ var row = null;
+
+ var cal = this;
+ var hh = function (text, cs, navtype) {
+ cell = Calendar.createElement("td", row);
+ cell.colSpan = cs;
+ cell.className = "button";
+ if (navtype != 0 && Math.abs(navtype) <= 2)
+ cell.className += " nav";
+ Calendar._add_evs(cell);
+ cell.calendar = cal;
+ cell.navtype = navtype;
+ cell.innerHTML = "" + text + "
";
+ return cell;
+ };
+
+ row = Calendar.createElement("tr", thead);
+ var title_length = 6;
+ (this.isPopup) && --title_length;
+ (this.weekNumbers) && ++title_length;
+
+ hh("?", 1, 400).ttip = Calendar._TT["INFO"];
+ this.title = hh("", title_length, 300);
+ this.title.className = "title";
+ if (this.isPopup) {
+ this.title.ttip = Calendar._TT["DRAG_TO_MOVE"];
+ this.title.style.cursor = "move";
+ hh("×", 1, 200).ttip = Calendar._TT["CLOSE"];
+ }
+
+ row = Calendar.createElement("tr", thead);
+ row.className = "headrow";
+
+ this._nav_py = hh("«", 1, -2);
+ this._nav_py.ttip = Calendar._TT["PREV_YEAR"];
+
+ this._nav_pm = hh("‹", 1, -1);
+ this._nav_pm.ttip = Calendar._TT["PREV_MONTH"];
+
+ this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0);
+ this._nav_now.ttip = Calendar._TT["GO_TODAY"];
+
+ this._nav_nm = hh("›", 1, 1);
+ this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"];
+
+ this._nav_ny = hh("»", 1, 2);
+ this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"];
+
+ // day names
+ row = Calendar.createElement("tr", thead);
+ row.className = "daynames";
+ if (this.weekNumbers) {
+ cell = Calendar.createElement("td", row);
+ cell.className = "name wn";
+ cell.innerHTML = Calendar._TT["WK"];
+ }
+ for (var i = 7; i > 0; --i) {
+ cell = Calendar.createElement("td", row);
+ if (!i) {
+ cell.navtype = 100;
+ cell.calendar = this;
+ Calendar._add_evs(cell);
+ }
+ }
+ this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild;
+ this._displayWeekdays();
+
+ var tbody = Calendar.createElement("tbody", table);
+ this.tbody = tbody;
+
+ for (i = 6; i > 0; --i) {
+ row = Calendar.createElement("tr", tbody);
+ if (this.weekNumbers) {
+ cell = Calendar.createElement("td", row);
+ }
+ for (var j = 7; j > 0; --j) {
+ cell = Calendar.createElement("td", row);
+ cell.calendar = this;
+ Calendar._add_evs(cell);
+ }
+ }
+
+ if (this.showsTime) {
+ row = Calendar.createElement("tr", tbody);
+ row.className = "time";
+
+ cell = Calendar.createElement("td", row);
+ cell.className = "time";
+ cell.colSpan = 2;
+ cell.innerHTML = Calendar._TT["TIME"] || " ";
+
+ cell = Calendar.createElement("td", row);
+ cell.className = "time";
+ cell.colSpan = this.weekNumbers ? 4 : 3;
+
+ (function(){
+ function makeTimePart(className, init, range_start, range_end) {
+ var part = Calendar.createElement("span", cell);
+ part.className = className;
+ part.innerHTML = init;
+ part.calendar = cal;
+ part.ttip = Calendar._TT["TIME_PART"];
+ part.navtype = 50;
+ part._range = [];
+ if (typeof range_start != "number")
+ part._range = range_start;
+ else {
+ for (var i = range_start; i <= range_end; ++i) {
+ var txt;
+ if (i < 10 && range_end >= 10) txt = '0' + i;
+ else txt = '' + i;
+ part._range[part._range.length] = txt;
+ }
+ }
+ Calendar._add_evs(part);
+ return part;
+ };
+ var hrs = cal.date.getHours();
+ var mins = cal.date.getMinutes();
+ var t12 = !cal.time24;
+ var pm = (hrs > 12);
+ if (t12 && pm) hrs -= 12;
+ var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);
+ var span = Calendar.createElement("span", cell);
+ span.innerHTML = ":";
+ span.className = "colon";
+ var M = makeTimePart("minute", mins, 0, 59);
+ var AP = null;
+ cell = Calendar.createElement("td", row);
+ cell.className = "time";
+ cell.colSpan = 2;
+ if (t12)
+ AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]);
+ else
+ cell.innerHTML = " ";
+
+ cal.onSetTime = function() {
+ var pm, hrs = this.date.getHours(),
+ mins = this.date.getMinutes();
+ if (t12) {
+ pm = (hrs >= 12);
+ if (pm) hrs -= 12;
+ if (hrs == 0) hrs = 12;
+ AP.innerHTML = pm ? "pm" : "am";
+ }
+ H.innerHTML = (hrs < 10) ? ("0" + hrs) : hrs;
+ M.innerHTML = (mins < 10) ? ("0" + mins) : mins;
+ };
+
+ cal.onUpdateTime = function() {
+ var date = this.date;
+ var h = parseInt(H.innerHTML, 10);
+ if (t12) {
+ if (/pm/i.test(AP.innerHTML) && h < 12)
+ h += 12;
+ else if (/am/i.test(AP.innerHTML) && h == 12)
+ h = 0;
+ }
+ var d = date.getDate();
+ var m = date.getMonth();
+ var y = date.getFullYear();
+ date.setHours(h);
+ date.setMinutes(parseInt(M.innerHTML, 10));
+ date.setFullYear(y);
+ date.setMonth(m);
+ date.setDate(d);
+ this.dateClicked = false;
+ this.callHandler();
+ };
+ })();
+ } else {
+ this.onSetTime = this.onUpdateTime = function() {};
+ }
+
+ var tfoot = Calendar.createElement("tfoot", table);
+
+ row = Calendar.createElement("tr", tfoot);
+ row.className = "footrow";
+
+ cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300);
+ cell.className = "ttip";
+ if (this.isPopup) {
+ cell.ttip = Calendar._TT["DRAG_TO_MOVE"];
+ cell.style.cursor = "move";
+ }
+ this.tooltips = cell;
+
+ div = Calendar.createElement("div", this.element);
+ this.monthsCombo = div;
+ div.className = "combo";
+ for (i = 0; i < Calendar._MN.length; ++i) {
+ var mn = Calendar.createElement("div");
+ mn.className = Calendar.is_ie ? "label-IEfix" : "label";
+ mn.month = i;
+ mn.innerHTML = Calendar._SMN[i];
+ div.appendChild(mn);
+ }
+
+ div = Calendar.createElement("div", this.element);
+ this.yearsCombo = div;
+ div.className = "combo";
+ for (i = 12; i > 0; --i) {
+ var yr = Calendar.createElement("div");
+ yr.className = Calendar.is_ie ? "label-IEfix" : "label";
+ div.appendChild(yr);
+ }
+
+ this._init(this.firstDayOfWeek, this.date);
+ parent.appendChild(this.element);
+};
+
+/** keyboard navigation, only for popup calendars */
+Calendar._keyEvent = function(ev) {
+ var cal = window._dynarch_popupCalendar;
+ if (!cal || cal.multiple)
+ return false;
+ (Calendar.is_ie) && (ev = window.event);
+ var act = (Calendar.is_ie || ev.type == "keypress"),
+ K = ev.keyCode;
+ if (ev.ctrlKey) {
+ switch (K) {
+ case 37: // KEY left
+ act && Calendar.cellClick(cal._nav_pm);
+ break;
+ case 38: // KEY up
+ act && Calendar.cellClick(cal._nav_py);
+ break;
+ case 39: // KEY right
+ act && Calendar.cellClick(cal._nav_nm);
+ break;
+ case 40: // KEY down
+ act && Calendar.cellClick(cal._nav_ny);
+ break;
+ default:
+ return false;
+ }
+ } else switch (K) {
+ case 32: // KEY space (now)
+ Calendar.cellClick(cal._nav_now);
+ break;
+ case 27: // KEY esc
+ act && cal.callCloseHandler();
+ break;
+ case 37: // KEY left
+ case 38: // KEY up
+ case 39: // KEY right
+ case 40: // KEY down
+ if (act) {
+ var prev, x, y, ne, el, step;
+ prev = K == 37 || K == 38;
+ step = (K == 37 || K == 39) ? 1 : 7;
+ function setVars() {
+ el = cal.currentDateEl;
+ var p = el.pos;
+ x = p & 15;
+ y = p >> 4;
+ ne = cal.ar_days[y][x];
+ };setVars();
+ function prevMonth() {
+ var date = new Date(cal.date);
+ date.setDate(date.getDate() - step);
+ cal.setDate(date);
+ };
+ function nextMonth() {
+ var date = new Date(cal.date);
+ date.setDate(date.getDate() + step);
+ cal.setDate(date);
+ };
+ while (1) {
+ switch (K) {
+ case 37: // KEY left
+ if (--x >= 0)
+ ne = cal.ar_days[y][x];
+ else {
+ x = 6;
+ K = 38;
+ continue;
+ }
+ break;
+ case 38: // KEY up
+ if (--y >= 0)
+ ne = cal.ar_days[y][x];
+ else {
+ prevMonth();
+ setVars();
+ }
+ break;
+ case 39: // KEY right
+ if (++x < 7)
+ ne = cal.ar_days[y][x];
+ else {
+ x = 0;
+ K = 40;
+ continue;
+ }
+ break;
+ case 40: // KEY down
+ if (++y < cal.ar_days.length)
+ ne = cal.ar_days[y][x];
+ else {
+ nextMonth();
+ setVars();
+ }
+ break;
+ }
+ break;
+ }
+ if (ne) {
+ if (!ne.disabled)
+ Calendar.cellClick(ne);
+ else if (prev)
+ prevMonth();
+ else
+ nextMonth();
+ }
+ }
+ break;
+ case 13: // KEY enter
+ if (act)
+ Calendar.cellClick(cal.currentDateEl, ev);
+ break;
+ default:
+ return false;
+ }
+ return Calendar.stopEvent(ev);
+};
+
+/**
+ * (RE)Initializes the calendar to the given date and firstDayOfWeek
+ */
+Calendar.prototype._init = function (firstDayOfWeek, date) {
+ var today = new Date(),
+ TY = today.getFullYear(),
+ TM = today.getMonth(),
+ TD = today.getDate();
+ this.table.style.visibility = "hidden";
+ var year = date.getFullYear();
+ if (year < this.minYear) {
+ year = this.minYear;
+ date.setFullYear(year);
+ } else if (year > this.maxYear) {
+ year = this.maxYear;
+ date.setFullYear(year);
+ }
+ this.firstDayOfWeek = firstDayOfWeek;
+ this.date = new Date(date);
+ var month = date.getMonth();
+ var mday = date.getDate();
+ var no_days = date.getMonthDays();
+
+ // calendar voodoo for computing the first day that would actually be
+ // displayed in the calendar, even if it's from the previous month.
+ // WARNING: this is magic. ;-)
+ date.setDate(1);
+ var day1 = (date.getDay() - this.firstDayOfWeek) % 7;
+ if (day1 < 0)
+ day1 += 7;
+ date.setDate(-day1);
+ date.setDate(date.getDate() + 1);
+
+ var row = this.tbody.firstChild;
+ var MN = Calendar._SMN[month];
+ var ar_days = this.ar_days = new Array();
+ var weekend = Calendar._TT["WEEKEND"];
+ var dates = this.multiple ? (this.datesCells = {}) : null;
+ for (var i = 0; i < 6; ++i, row = row.nextSibling) {
+ var cell = row.firstChild;
+ if (this.weekNumbers) {
+ cell.className = "day wn";
+ cell.innerHTML = date.getWeekNumber();
+ cell = cell.nextSibling;
+ }
+ row.className = "daysrow";
+ var hasdays = false, iday, dpos = ar_days[i] = [];
+ for (var j = 0; j < 7; ++j, cell = cell.nextSibling, date.setDate(iday + 1)) {
+ iday = date.getDate();
+ var wday = date.getDay();
+ cell.className = "day";
+ cell.pos = i << 4 | j;
+ dpos[j] = cell;
+ var current_month = (date.getMonth() == month);
+ if (!current_month) {
+ if (this.showsOtherMonths) {
+ cell.className += " othermonth";
+ cell.otherMonth = true;
+ } else {
+ cell.className = "emptycell";
+ cell.innerHTML = " ";
+ cell.disabled = true;
+ continue;
+ }
+ } else {
+ cell.otherMonth = false;
+ hasdays = true;
+ }
+ cell.disabled = false;
+ cell.innerHTML = this.getDateText ? this.getDateText(date, iday) : iday;
+ if (dates)
+ dates[date.print("%Y%m%d")] = cell;
+ if (this.getDateStatus) {
+ var status = this.getDateStatus(date, year, month, iday);
+ if (this.getDateToolTip) {
+ var toolTip = this.getDateToolTip(date, year, month, iday);
+ if (toolTip)
+ cell.title = toolTip;
+ }
+ if (status === true) {
+ cell.className += " disabled";
+ cell.disabled = true;
+ } else {
+ if (/disabled/i.test(status))
+ cell.disabled = true;
+ cell.className += " " + status;
+ }
+ }
+ if (!cell.disabled) {
+ cell.caldate = new Date(date);
+ cell.ttip = "_";
+ if (!this.multiple && current_month
+ && iday == mday && this.hiliteToday) {
+ cell.className += " selected";
+ this.currentDateEl = cell;
+ }
+ if (date.getFullYear() == TY &&
+ date.getMonth() == TM &&
+ iday == TD) {
+ cell.className += " today";
+ cell.ttip += Calendar._TT["PART_TODAY"];
+ }
+ if (weekend.indexOf(wday.toString()) != -1)
+ cell.className += cell.otherMonth ? " oweekend" : " weekend";
+ }
+ }
+ if (!(hasdays || this.showsOtherMonths))
+ row.className = "emptyrow";
+ }
+ this.title.innerHTML = Calendar._MN[month] + ", " + year;
+ this.onSetTime();
+ this.table.style.visibility = "visible";
+ this._initMultipleDates();
+ // PROFILE
+ // this.tooltips.innerHTML = "Generated in " + ((new Date()) - today) + " ms";
+};
+
+Calendar.prototype._initMultipleDates = function() {
+ if (this.multiple) {
+ for (var i in this.multiple) {
+ var cell = this.datesCells[i];
+ var d = this.multiple[i];
+ if (!d)
+ continue;
+ if (cell)
+ cell.className += " selected";
+ }
+ }
+};
+
+Calendar.prototype._toggleMultipleDate = function(date) {
+ if (this.multiple) {
+ var ds = date.print("%Y%m%d");
+ var cell = this.datesCells[ds];
+ if (cell) {
+ var d = this.multiple[ds];
+ if (!d) {
+ Calendar.addClass(cell, "selected");
+ this.multiple[ds] = date;
+ } else {
+ Calendar.removeClass(cell, "selected");
+ delete this.multiple[ds];
+ }
+ }
+ }
+};
+
+Calendar.prototype.setDateToolTipHandler = function (unaryFunction) {
+ this.getDateToolTip = unaryFunction;
+};
+
+/**
+ * Calls _init function above for going to a certain date (but only if the
+ * date is different than the currently selected one).
+ */
+Calendar.prototype.setDate = function (date) {
+ if (!date.equalsTo(this.date)) {
+ this._init(this.firstDayOfWeek, date);
+ }
+};
+
+/**
+ * Refreshes the calendar. Useful if the "disabledHandler" function is
+ * dynamic, meaning that the list of disabled date can change at runtime.
+ * Just * call this function if you think that the list of disabled dates
+ * should * change.
+ */
+Calendar.prototype.refresh = function () {
+ this._init(this.firstDayOfWeek, this.date);
+};
+
+/** Modifies the "firstDayOfWeek" parameter (pass 0 for Synday, 1 for Monday, etc.). */
+Calendar.prototype.setFirstDayOfWeek = function (firstDayOfWeek) {
+ this._init(firstDayOfWeek, this.date);
+ this._displayWeekdays();
+};
+
+/**
+ * Allows customization of what dates are enabled. The "unaryFunction"
+ * parameter must be a function object that receives the date (as a JS Date
+ * object) and returns a boolean value. If the returned value is true then
+ * the passed date will be marked as disabled.
+ */
+Calendar.prototype.setDateStatusHandler = Calendar.prototype.setDisabledHandler = function (unaryFunction) {
+ this.getDateStatus = unaryFunction;
+};
+
+/** Customization of allowed year range for the calendar. */
+Calendar.prototype.setRange = function (a, z) {
+ this.minYear = a;
+ this.maxYear = z;
+};
+
+/** Calls the first user handler (selectedHandler). */
+Calendar.prototype.callHandler = function () {
+ if (this.onSelected) {
+ this.onSelected(this, this.date.print(this.dateFormat));
+ }
+};
+
+/** Calls the second user handler (closeHandler). */
+Calendar.prototype.callCloseHandler = function () {
+ if (this.onClose) {
+ this.onClose(this);
+ }
+ this.hideShowCovered();
+};
+
+/** Removes the calendar object from the DOM tree and destroys it. */
+Calendar.prototype.destroy = function () {
+ var el = this.element.parentNode;
+ el.removeChild(this.element);
+ Calendar._C = null;
+ window._dynarch_popupCalendar = null;
+};
+
+/**
+ * Moves the calendar element to a different section in the DOM tree (changes
+ * its parent).
+ */
+Calendar.prototype.reparent = function (new_parent) {
+ var el = this.element;
+ el.parentNode.removeChild(el);
+ new_parent.appendChild(el);
+};
+
+// This gets called when the user presses a mouse button anywhere in the
+// document, if the calendar is shown. If the click was outside the open
+// calendar this function closes it.
+Calendar._checkCalendar = function(ev) {
+ var calendar = window._dynarch_popupCalendar;
+ if (!calendar) {
+ return false;
+ }
+ var el = Calendar.is_ie ? Calendar.getElement(ev) : Calendar.getTargetElement(ev);
+ for (; el != null && el != calendar.element; el = el.parentNode);
+ if (el == null) {
+ // calls closeHandler which should hide the calendar.
+ window._dynarch_popupCalendar.callCloseHandler();
+ return Calendar.stopEvent(ev);
+ }
+};
+
+/** Shows the calendar. */
+Calendar.prototype.show = function () {
+ var rows = this.table.getElementsByTagName("tr");
+ for (var i = rows.length; i > 0;) {
+ var row = rows[--i];
+ Calendar.removeClass(row, "rowhilite");
+ var cells = row.getElementsByTagName("td");
+ for (var j = cells.length; j > 0;) {
+ var cell = cells[--j];
+ Calendar.removeClass(cell, "hilite");
+ Calendar.removeClass(cell, "active");
+ }
+ }
+ this.element.style.display = "block";
+ this.hidden = false;
+ if (this.isPopup) {
+ window._dynarch_popupCalendar = this;
+ Calendar.addEvent(document, "keydown", Calendar._keyEvent);
+ Calendar.addEvent(document, "keypress", Calendar._keyEvent);
+ Calendar.addEvent(document, "mousedown", Calendar._checkCalendar);
+ }
+ this.hideShowCovered();
+};
+
+/**
+ * Hides the calendar. Also removes any "hilite" from the class of any TD
+ * element.
+ */
+Calendar.prototype.hide = function () {
+ if (this.isPopup) {
+ Calendar.removeEvent(document, "keydown", Calendar._keyEvent);
+ Calendar.removeEvent(document, "keypress", Calendar._keyEvent);
+ Calendar.removeEvent(document, "mousedown", Calendar._checkCalendar);
+ }
+ this.element.style.display = "none";
+ this.hidden = true;
+ this.hideShowCovered();
+};
+
+/**
+ * Shows the calendar at a given absolute position (beware that, depending on
+ * the calendar element style -- position property -- this might be relative
+ * to the parent's containing rectangle).
+ */
+Calendar.prototype.showAt = function (x, y) {
+ var s = this.element.style;
+ s.left = x + "px";
+ s.top = y + "px";
+ this.show();
+};
+
+/** Shows the calendar near a given element. */
+Calendar.prototype.showAtElement = function (el, opts) {
+ var self = this;
+ var p = Calendar.getAbsolutePos(el);
+ if (!opts || typeof opts != "string") {
+ this.showAt(p.x, p.y + el.offsetHeight);
+ return true;
+ }
+ function fixPosition(box) {
+ if (box.x < 0)
+ box.x = 0;
+ if (box.y < 0)
+ box.y = 0;
+ var cp = document.createElement("div");
+ var s = cp.style;
+ s.position = "absolute";
+ s.right = s.bottom = s.width = s.height = "0px";
+ document.body.appendChild(cp);
+ var br = Calendar.getAbsolutePos(cp);
+ document.body.removeChild(cp);
+ if (Calendar.is_ie) {
+ br.y += document.body.scrollTop;
+ br.x += document.body.scrollLeft;
+ } else {
+ br.y += window.scrollY;
+ br.x += window.scrollX;
+ }
+ var tmp = box.x + box.width - br.x;
+ if (tmp > 0) box.x -= tmp;
+ tmp = box.y + box.height - br.y;
+ if (tmp > 0) box.y -= tmp;
+ };
+ this.element.style.display = "block";
+ Calendar.continuation_for_the_fucking_khtml_browser = function() {
+ var w = self.element.offsetWidth;
+ var h = self.element.offsetHeight;
+ self.element.style.display = "none";
+ var valign = opts.substr(0, 1);
+ var halign = "l";
+ if (opts.length > 1) {
+ halign = opts.substr(1, 1);
+ }
+ // vertical alignment
+ switch (valign) {
+ case "T": p.y -= h; break;
+ case "B": p.y += el.offsetHeight; break;
+ case "C": p.y += (el.offsetHeight - h) / 2; break;
+ case "t": p.y += el.offsetHeight - h; break;
+ case "b": break; // already there
+ }
+ // horizontal alignment
+ switch (halign) {
+ case "L": p.x -= w; break;
+ case "R": p.x += el.offsetWidth; break;
+ case "C": p.x += (el.offsetWidth - w) / 2; break;
+ case "l": p.x += el.offsetWidth - w; break;
+ case "r": break; // already there
+ }
+ p.width = w;
+ p.height = h + 40;
+ self.monthsCombo.style.display = "none";
+ fixPosition(p);
+ self.showAt(p.x, p.y);
+ };
+ if (Calendar.is_khtml)
+ setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()", 10);
+ else
+ Calendar.continuation_for_the_fucking_khtml_browser();
+};
+
+/** Customizes the date format. */
+Calendar.prototype.setDateFormat = function (str) {
+ this.dateFormat = str;
+};
+
+/** Customizes the tooltip date format. */
+Calendar.prototype.setTtDateFormat = function (str) {
+ this.ttDateFormat = str;
+};
+
+/**
+ * Tries to identify the date represented in a string. If successful it also
+ * calls this.setDate which moves the calendar to the given date.
+ */
+Calendar.prototype.parseDate = function(str, fmt) {
+ if (!fmt)
+ fmt = this.dateFormat;
+ this.setDate(Date.parseDate(str, fmt));
+};
+
+Calendar.prototype.hideShowCovered = function () {
+ if (!Calendar.is_ie && !Calendar.is_opera)
+ return;
+ function getVisib(obj){
+ var value = obj.style.visibility;
+ if (!value) {
+ if (document.defaultView && typeof (document.defaultView.getComputedStyle) == "function") { // Gecko, W3C
+ if (!Calendar.is_khtml)
+ value = document.defaultView.
+ getComputedStyle(obj, "").getPropertyValue("visibility");
+ else
+ value = '';
+ } else if (obj.currentStyle) { // IE
+ value = obj.currentStyle.visibility;
+ } else
+ value = '';
+ }
+ return value;
+ };
+
+ var tags = new Array("applet", "iframe", "select");
+ var el = this.element;
+
+ var p = Calendar.getAbsolutePos(el);
+ var EX1 = p.x;
+ var EX2 = el.offsetWidth + EX1;
+ var EY1 = p.y;
+ var EY2 = el.offsetHeight + EY1;
+
+ for (var k = tags.length; k > 0; ) {
+ var ar = document.getElementsByTagName(tags[--k]);
+ var cc = null;
+
+ for (var i = ar.length; i > 0;) {
+ cc = ar[--i];
+
+ p = Calendar.getAbsolutePos(cc);
+ var CX1 = p.x;
+ var CX2 = cc.offsetWidth + CX1;
+ var CY1 = p.y;
+ var CY2 = cc.offsetHeight + CY1;
+
+ if (this.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
+ if (!cc.__msh_save_visibility) {
+ cc.__msh_save_visibility = getVisib(cc);
+ }
+ cc.style.visibility = cc.__msh_save_visibility;
+ } else {
+ if (!cc.__msh_save_visibility) {
+ cc.__msh_save_visibility = getVisib(cc);
+ }
+ cc.style.visibility = "hidden";
+ }
+ }
+ }
+};
+
+/** Internal function; it displays the bar with the names of the weekday. */
+Calendar.prototype._displayWeekdays = function () {
+ var fdow = this.firstDayOfWeek;
+ var cell = this.firstdayname;
+ var weekend = Calendar._TT["WEEKEND"];
+ for (var i = 0; i < 7; ++i) {
+ cell.className = "day name";
+ var realday = (i + fdow) % 7;
+ if (i) {
+ cell.ttip = Calendar._TT["DAY_FIRST"].replace("%s", Calendar._DN[realday]);
+ cell.navtype = 100;
+ cell.calendar = this;
+ cell.fdow = realday;
+ Calendar._add_evs(cell);
+ }
+ if (weekend.indexOf(realday.toString()) != -1) {
+ Calendar.addClass(cell, "weekend");
+ }
+ cell.innerHTML = Calendar._SDN[(i + fdow) % 7];
+ cell = cell.nextSibling;
+ }
+};
+
+/** Internal function. Hides all combo boxes that might be displayed. */
+Calendar.prototype._hideCombos = function () {
+ this.monthsCombo.style.display = "none";
+ this.yearsCombo.style.display = "none";
+};
+
+/** Internal function. Starts dragging the element. */
+Calendar.prototype._dragStart = function (ev) {
+ if (this.dragging) {
+ return;
+ }
+ this.dragging = true;
+ var posX;
+ var posY;
+ if (Calendar.is_ie) {
+ posY = window.event.clientY + document.body.scrollTop;
+ posX = window.event.clientX + document.body.scrollLeft;
+ } else {
+ posY = ev.clientY + window.scrollY;
+ posX = ev.clientX + window.scrollX;
+ }
+ var st = this.element.style;
+ this.xOffs = posX - parseInt(st.left);
+ this.yOffs = posY - parseInt(st.top);
+ with (Calendar) {
+ addEvent(document, "mousemove", calDragIt);
+ addEvent(document, "mouseup", calDragEnd);
+ }
+};
+
+// BEGIN: DATE OBJECT PATCHES
+
+/** Adds the number of days array to the Date object. */
+Date._MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
+
+/** Constants used for time computations */
+Date.SECOND = 1000 /* milliseconds */;
+Date.MINUTE = 60 * Date.SECOND;
+Date.HOUR = 60 * Date.MINUTE;
+Date.DAY = 24 * Date.HOUR;
+Date.WEEK = 7 * Date.DAY;
+
+Date.parseDate = function(str, fmt) {
+ var today = new Date();
+ var y = 0;
+ var m = -1;
+ var d = 0;
+ var a = str.split(/\W+/);
+ var b = fmt.match(/%./g);
+ var i = 0, j = 0;
+ var hr = 0;
+ var min = 0;
+ for (i = 0; i < a.length; ++i) {
+ if (!a[i])
+ continue;
+ switch (b[i]) {
+ case "%d":
+ case "%e":
+ d = parseInt(a[i], 10);
+ break;
+
+ case "%m":
+ m = parseInt(a[i], 10) - 1;
+ break;
+
+ case "%Y":
+ case "%y":
+ y = parseInt(a[i], 10);
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
+ break;
+
+ case "%b":
+ case "%B":
+ for (j = 0; j < 12; ++j) {
+ if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }
+ }
+ break;
+
+ case "%H":
+ case "%I":
+ case "%k":
+ case "%l":
+ hr = parseInt(a[i], 10);
+ break;
+
+ case "%P":
+ case "%p":
+ if (/pm/i.test(a[i]) && hr < 12)
+ hr += 12;
+ else if (/am/i.test(a[i]) && hr >= 12)
+ hr -= 12;
+ break;
+
+ case "%M":
+ min = parseInt(a[i], 10);
+ break;
+ }
+ }
+ if (isNaN(y)) y = today.getFullYear();
+ if (isNaN(m)) m = today.getMonth();
+ if (isNaN(d)) d = today.getDate();
+ if (isNaN(hr)) hr = today.getHours();
+ if (isNaN(min)) min = today.getMinutes();
+ if (y != 0 && m != -1 && d != 0)
+ return new Date(y, m, d, hr, min, 0);
+ y = 0; m = -1; d = 0;
+ for (i = 0; i < a.length; ++i) {
+ if (a[i].search(/[a-zA-Z]+/) != -1) {
+ var t = -1;
+ for (j = 0; j < 12; ++j) {
+ if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }
+ }
+ if (t != -1) {
+ if (m != -1) {
+ d = m+1;
+ }
+ m = t;
+ }
+ } else if (parseInt(a[i], 10) <= 12 && m == -1) {
+ m = a[i]-1;
+ } else if (parseInt(a[i], 10) > 31 && y == 0) {
+ y = parseInt(a[i], 10);
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
+ } else if (d == 0) {
+ d = a[i];
+ }
+ }
+ if (y == 0)
+ y = today.getFullYear();
+ if (m != -1 && d != 0)
+ return new Date(y, m, d, hr, min, 0);
+ return today;
+};
+
+/** Returns the number of days in the current month */
+Date.prototype.getMonthDays = function(month) {
+ var year = this.getFullYear();
+ if (typeof month == "undefined") {
+ month = this.getMonth();
+ }
+ if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
+ return 29;
+ } else {
+ return Date._MD[month];
+ }
+};
+
+/** Returns the number of day in the year. */
+Date.prototype.getDayOfYear = function() {
+ var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
+ var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
+ var time = now - then;
+ return Math.floor(time / Date.DAY);
+};
+
+/** Returns the number of the week in year, as defined in ISO 8601. */
+Date.prototype.getWeekNumber = function() {
+ var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
+ var DoW = d.getDay();
+ d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
+ var ms = d.valueOf(); // GMT
+ d.setMonth(0);
+ d.setDate(4); // Thu in Week 1
+ return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
+};
+
+/** Checks date and time equality */
+Date.prototype.equalsTo = function(date) {
+ return ((this.getFullYear() == date.getFullYear()) &&
+ (this.getMonth() == date.getMonth()) &&
+ (this.getDate() == date.getDate()) &&
+ (this.getHours() == date.getHours()) &&
+ (this.getMinutes() == date.getMinutes()));
+};
+
+/** Set only the year, month, date parts (keep existing time) */
+Date.prototype.setDateOnly = function(date) {
+ var tmp = new Date(date);
+ this.setDate(1);
+ this.setFullYear(tmp.getFullYear());
+ this.setMonth(tmp.getMonth());
+ this.setDate(tmp.getDate());
+};
+
+/** Prints the date in a string according to the given format. */
+Date.prototype.print = function (str) {
+ var m = this.getMonth();
+ var d = this.getDate();
+ var y = this.getFullYear();
+ var wn = this.getWeekNumber();
+ var w = this.getDay();
+ var s = {};
+ var hr = this.getHours();
+ var pm = (hr >= 12);
+ var ir = (pm) ? (hr - 12) : hr;
+ var dy = this.getDayOfYear();
+ if (ir == 0)
+ ir = 12;
+ var min = this.getMinutes();
+ var sec = this.getSeconds();
+ s["%a"] = Calendar._SDN[w]; // abbreviated weekday name [FIXME: I18N]
+ s["%A"] = Calendar._DN[w]; // full weekday name
+ s["%b"] = Calendar._SMN[m]; // abbreviated month name [FIXME: I18N]
+ s["%B"] = Calendar._MN[m]; // full month name
+ // FIXME: %c : preferred date and time representation for the current locale
+ s["%C"] = 1 + Math.floor(y / 100); // the century number
+ s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
+ s["%e"] = d; // the day of the month (range 1 to 31)
+ // FIXME: %D : american date style: %m/%d/%y
+ // FIXME: %E, %F, %G, %g, %h (man strftime)
+ s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
+ s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
+ s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
+ s["%k"] = hr; // hour, range 0 to 23 (24h format)
+ s["%l"] = ir; // hour, range 1 to 12 (12h format)
+ s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12
+ s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
+ s["%n"] = "\n"; // a newline character
+ s["%p"] = pm ? "PM" : "AM";
+ s["%P"] = pm ? "pm" : "am";
+ // FIXME: %r : the time in am/pm notation %I:%M:%S %p
+ // FIXME: %R : the time in 24-hour notation %H:%M
+ s["%s"] = Math.floor(this.getTime() / 1000);
+ s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
+ s["%t"] = "\t"; // a tab character
+ // FIXME: %T : the time in 24-hour notation (%H:%M:%S)
+ s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
+ s["%u"] = w + 1; // the day of the week (range 1 to 7, 1 = MON)
+ s["%w"] = w; // the day of the week (range 0 to 6, 0 = SUN)
+ // FIXME: %x : preferred date representation for the current locale without the time
+ // FIXME: %X : preferred time representation for the current locale without the date
+ s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)
+ s["%Y"] = y; // year with the century
+ s["%%"] = "%"; // a literal '%' character
+
+ var re = /%./g;
+ if (!Calendar.is_ie5 && !Calendar.is_khtml)
+ return str.replace(re, function (par) { return s[par] || par; });
+
+ var a = str.match(re);
+ for (var i = 0; i < a.length; i++) {
+ var tmp = s[a[i]];
+ if (tmp) {
+ re = new RegExp(a[i], 'g');
+ str = str.replace(re, tmp);
+ }
+ }
+
+ return str;
+};
+
+Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear;
+Date.prototype.setFullYear = function(y) {
+ var d = new Date(this);
+ d.__msh_oldSetFullYear(y);
+ if (d.getMonth() != this.getMonth())
+ this.setDate(28);
+ this.__msh_oldSetFullYear(y);
+};
+
+// END: DATE OBJECT PATCHES
+
+
+// global object that remembers the calendar
+window._dynarch_popupCalendar = null;
diff --git a/posterita/src/web/javascripts/prototype.js b/posterita/src/web/javascripts/prototype.js
new file mode 100644
index 0000000000..83c561ba91
--- /dev/null
+++ b/posterita/src/web/javascripts/prototype.js
@@ -0,0 +1,2059 @@
+/* Prototype JavaScript framework, version 1.5.0_rc0
+ * (c) 2005 Sam Stephenson
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ * For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.5.0_rc0',
+ ScriptFragment: '(?:)((\n|\r|.)*?)(?:<\/script>)',
+
+ emptyFunction: function() {},
+ K: function(x) {return x}
+}
+
+var Class = {
+ create: function() {
+ return function() {
+ this.initialize.apply(this, arguments);
+ }
+ }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+ for (var property in source) {
+ destination[property] = source[property];
+ }
+ return destination;
+}
+
+Object.inspect = function(object) {
+ try {
+ if (object == undefined) return 'undefined';
+ if (object == null) return 'null';
+ return object.inspect ? object.inspect() : object.toString();
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+}
+
+Function.prototype.bind = function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+ var __method = this;
+ return function(event) {
+ return __method.call(object, event || window.event);
+ }
+}
+
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ var digits = this.toString(16);
+ if (this < 16) return '0' + digits;
+ return digits;
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator) {
+ $R(0, this, true).each(iterator);
+ return this;
+ }
+});
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0; i < arguments.length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) {}
+ }
+
+ return returnValue;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.callback();
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+}
+Object.extend(String.prototype, {
+ gsub: function(pattern, replacement) {
+ var result = '', source = this, match;
+ replacement = arguments.callee.prepareReplacement(replacement);
+
+ while (source.length > 0) {
+ if (match = source.match(pattern)) {
+ result += source.slice(0, match.index);
+ result += (replacement(match) || '').toString();
+ source = source.slice(match.index + match[0].length);
+ } else {
+ result += source, source = '';
+ }
+ }
+ return result;
+ },
+
+ sub: function(pattern, replacement, count) {
+ replacement = this.gsub.prepareReplacement(replacement);
+ count = count === undefined ? 1 : count;
+
+ return this.gsub(pattern, function(match) {
+ if (--count < 0) return match[0];
+ return replacement(match);
+ });
+ },
+
+ scan: function(pattern, iterator) {
+ this.gsub(pattern, iterator);
+ return this;
+ },
+
+ truncate: function(length, truncation) {
+ length = length || 30;
+ truncation = truncation === undefined ? '...' : truncation;
+ return this.length > length ?
+ this.slice(0, length - truncation.length) + truncation : this;
+ },
+
+ strip: function() {
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
+ },
+
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ },
+
+ evalScripts: function() {
+ return this.extractScripts().map(function(script) { return eval(script) });
+ },
+
+ escapeHTML: function() {
+ var div = document.createElement('div');
+ var text = document.createTextNode(this);
+ div.appendChild(text);
+ return div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = document.createElement('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+ },
+
+ toQueryParams: function() {
+ var pairs = this.match(/^\??(.*)$/)[1].split('&');
+ return pairs.inject({}, function(params, pairString) {
+ var pair = pairString.split('=');
+ params[pair[0]] = pair[1];
+ return params;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ camelize: function() {
+ var oStringList = this.split('-');
+ if (oStringList.length == 1) return oStringList[0];
+
+ var camelizedString = this.indexOf('-') == 0
+ ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+ : oStringList[0];
+
+ for (var i = 1, len = oStringList.length; i < len; i++) {
+ var s = oStringList[i];
+ camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+ }
+
+ return camelizedString;
+ },
+
+ inspect: function() {
+ return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'";
+ }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+ if (typeof replacement == 'function') return replacement;
+ var template = new Template(replacement);
+ return function(match) { return template.evaluate(match) };
+}
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+var Template = Class.create();
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+Template.prototype = {
+ initialize: function(template, pattern) {
+ this.template = template.toString();
+ this.pattern = pattern || Template.Pattern;
+ },
+
+ evaluate: function(object) {
+ return this.template.gsub(this.pattern, function(match) {
+ var before = match[1];
+ if (before == '\\') return match[2];
+ return before + (object[match[3]] || '').toString();
+ });
+ }
+}
+
+var $break = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+ each: function(iterator) {
+ var index = 0;
+ try {
+ this._each(function(value) {
+ try {
+ iterator(value, index++);
+ } catch (e) {
+ if (e != $continue) throw e;
+ }
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ },
+
+ all: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!(iterator || Prototype.K)(value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ if (result = !!(iterator || Prototype.K)(value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ detect: function (iterator) {
+ var result;
+ this.each(function(value, index) {
+ if (iterator(value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(pattern, iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ var stringValue = value.toString();
+ if (stringValue.match(pattern))
+ results.push((iterator || Prototype.K)(value, index));
+ })
+ return results;
+ },
+
+ include: function(object) {
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inject: function(memo, iterator) {
+ this.each(function(value, index) {
+ memo = iterator(memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.collect(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (result == undefined || value >= result)
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (result == undefined || value < result)
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator) {
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ ((iterator || Prototype.K)(value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator) {
+ return this.collect(function(value, index) {
+ return {value: value, criteria: iterator(value, index)};
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.collect(Prototype.K);
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (typeof args.last() == 'function')
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ return iterator(collections.pluck(index));
+ });
+ },
+
+ inspect: function() {
+ return '#';
+ }
+}
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) {
+ return iterable.toArray();
+ } else {
+ var results = [];
+ for (var i = 0; i < iterable.length; i++)
+ results.push(iterable[i]);
+ return results;
+ }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse)
+ Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0; i < this.length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != undefined || value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(value && value.constructor == Array ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ indexOf: function(object) {
+ for (var i = 0; i < this.length; i++)
+ if (this[i] == object) return i;
+ return -1;
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ }
+});
+var Hash = {
+ _each: function(iterator) {
+ for (var key in this) {
+ var value = this[key];
+ if (typeof value == 'function') continue;
+
+ var pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ merge: function(hash) {
+ return $H(hash).inject($H(this), function(mergedHash, pair) {
+ mergedHash[pair.key] = pair.value;
+ return mergedHash;
+ });
+ },
+
+ toQueryString: function() {
+ return this.map(function(pair) {
+ return pair.map(encodeURIComponent).join('=');
+ }).join('&');
+ },
+
+ inspect: function() {
+ return '#';
+ }
+}
+
+function $H(object) {
+ var hash = Object.extend({}, object || {});
+ Object.extend(hash, Enumerable);
+ Object.extend(hash, Hash);
+ return hash;
+}
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ do {
+ iterator(value);
+ value = value.succ();
+ } while (this.include(value));
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new XMLHttpRequest()},
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+}
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responderToAdd) {
+ if (!this.include(responderToAdd))
+ this.responders.push(responderToAdd);
+ },
+
+ unregister: function(responderToRemove) {
+ this.responders = this.responders.without(responderToRemove);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (responder[callback] && typeof responder[callback] == 'function') {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) {}
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() {
+ Ajax.activeRequestCount++;
+ },
+
+ onComplete: function() {
+ Ajax.activeRequestCount--;
+ }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+ setOptions: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ contentType: 'application/x-www-form-urlencoded',
+ parameters: ''
+ }
+ Object.extend(this.options, options || {});
+ },
+
+ responseIsSuccess: function() {
+ return this.transport.status == undefined
+ || this.transport.status == 0
+ || (this.transport.status >= 200 && this.transport.status < 300);
+ },
+
+ responseIsFailure: function() {
+ return !this.responseIsSuccess();
+ }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(url, options) {
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+ this.request(url);
+ },
+
+ request: function(url) {
+ var parameters = this.options.parameters || '';
+ //alert(this.options.parameters + '=' + decodeURI(parameters));
+ parameters = decodeURI(parameters);
+
+ var parsamp = parameters.split("&");
+ parameters = '';
+ for(i=0;i 0) parameters += '&_=';
+
+
+
+ try {
+ this.url = url;
+ if (this.options.method == 'get' && parameters.length > 0)
+ this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
+
+ Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+ this.transport.open(this.options.method, this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) {
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+ }
+
+ this.setRequestHeaders();
+
+ var body = this.options.postBody ? this.options.postBody : parameters;
+ this.transport.send(this.options.method == 'post' ? body : null);
+
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ convert : function(s){
+ var uniString = "", hexVal, uniChar;
+
+ for(var i = 0; i < s.length; ++i)
+ {
+ //Convert char to hex
+ //hexVal = Number(s.charCodeAt(i)).toString(16);
+ hexVal = Number(s.charCodeAt(i)).toString(10);
+ if(hexVal < 255)
+ {
+ uniString += s.charAt(i);
+ }
+ else
+ {
+ //Convert to unicode by making sure hex is 4 chars long, padding with 0's if less
+ //uniChar = "\\u" + ("000" + hexVal).match(/.{4}$/)[0];
+ uniChar = "#" + ("000" + hexVal).match(/.{4}$/)[0] +";";
+
+ uniString += uniChar;
+ }
+ }
+
+ return uniString;
+},
+
+ setRequestHeaders: function() {
+ var requestHeaders =
+ ['X-Requested-With', 'XMLHttpRequest',
+ 'X-Prototype-Version', Prototype.Version,
+ 'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
+
+ if (this.options.method == 'post') {
+ requestHeaders.push('Content-type', this.options.contentType);
+
+ /* Force "Connection: close" for Mozilla browsers to work around
+ * a bug where XMLHttpReqeuest sends an incorrect Content-length
+ * header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType)
+ requestHeaders.push('Connection', 'close');
+ }
+
+ if (this.options.requestHeaders)
+ requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+
+ for (var i = 0; i < requestHeaders.length; i += 2)
+ this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState != 1)
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ header: function(name) {
+ try {
+ return this.transport.getResponseHeader(name);
+ } catch (e) {}
+ },
+
+ evalJSON: function() {
+ try {
+ return eval('(' + this.header('X-JSON') + ')');
+ } catch (e) {}
+ },
+
+ evalResponse: function() {
+ try {
+ return eval(this.transport.responseText);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ respondToReadyState: function(readyState) {
+ var event = Ajax.Request.Events[readyState];
+ var transport = this.transport, json = this.evalJSON();
+
+ if (event == 'Complete') {
+ try {
+ (this.options['on' + this.transport.status]
+ || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if ((this.header('Content-type') || '').match(/^text\/javascript/i))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
+ Ajax.Responders.dispatch('on' + event, this, transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+ if (event == 'Complete')
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+ initialize: function(container, url, options) {
+ this.containers = {
+ success: container.success ? $(container.success) : $(container),
+ failure: container.failure ? $(container.failure) :
+ (container.success ? null : $(container))
+ }
+
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+
+ var onComplete = this.options.onComplete || Prototype.emptyFunction;
+ this.options.onComplete = (function(transport, object) {
+ this.updateContent();
+ onComplete(transport, object);
+ }).bind(this);
+
+ this.request(url);
+ },
+
+ updateContent: function() {
+ var receiver = this.responseIsSuccess() ?
+ this.containers.success : this.containers.failure;
+ var response = this.transport.responseText;
+
+ if (!this.options.evalScripts)
+ response = response.stripScripts();
+
+ if (receiver) {
+ if (this.options.insertion) {
+ new this.options.insertion(receiver, response);
+ } else {
+ Element.update(receiver, response);
+ }
+ }
+
+ if (this.responseIsSuccess()) {
+ if (this.onComplete)
+ setTimeout(this.onComplete.bind(this), 10);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(container, url, options) {
+ this.setOptions(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = {};
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(request) {
+ if (this.options.decay) {
+ this.decay = (request.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = request.responseText;
+ }
+ this.timer = setTimeout(this.onTimerEvent.bind(this),
+ this.decay * this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+function $() {
+ var results = [], element;
+ for (var i = 0; i < arguments.length; i++) {
+ element = arguments[i];
+ if (typeof element == 'string')
+ element = document.getElementById(element);
+ results.push(Element.extend(element));
+ }
+ return results.length < 2 ? results[0] : results;
+}
+
+document.getElementsByClassName = function(className, parentElement) {
+ var children = ($(parentElement) || document.body).getElementsByTagName('*');
+ return $A(children).inject([], function(elements, child) {
+ if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+ elements.push(Element.extend(child));
+ return elements;
+ });
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element)
+ var Element = new Object();
+
+Element.extend = function(element) {
+ if (!element) return;
+ if (_nativeExtensions) return element;
+
+ if (!element._extended && element.tagName && element != window) {
+ var methods = Element.Methods, cache = Element.extend.cache;
+ for (property in methods) {
+ var value = methods[property];
+ if (typeof value == 'function')
+ element[property] = cache.findOrStore(value);
+ }
+ }
+
+ element._extended = true;
+ return element;
+}
+
+Element.extend.cache = {
+ findOrStore: function(value) {
+ return this[value] = this[value] || function() {
+ return value.apply(null, [this].concat($A(arguments)));
+ }
+ }
+}
+
+Element.Methods = {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ }
+ },
+
+ hide: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display = 'none';
+ }
+ },
+
+ show: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display = '';
+ }
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ },
+
+ update: function(element, html) {
+ $(element).innerHTML = html.stripScripts();
+ setTimeout(function() {html.evalScripts()}, 10);
+ },
+
+ replace: function(element, html) {
+ element = $(element);
+ if (element.outerHTML) {
+ element.outerHTML = html.stripScripts();
+ } else {
+ var range = element.ownerDocument.createRange();
+ range.selectNodeContents(element);
+ element.parentNode.replaceChild(
+ range.createContextualFragment(html.stripScripts()), element);
+ }
+ setTimeout(function() {html.evalScripts()}, 10);
+ },
+
+ getHeight: function(element) {
+ element = $(element);
+ return element.offsetHeight;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).include(className);
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).add(className);
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).remove(className);
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ for (var i = 0; i < element.childNodes.length; i++) {
+ var node = element.childNodes[i];
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ Element.remove(node);
+ }
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.match(/^\s*$/);
+ },
+
+ childOf: function(element, ancestor) {
+ element = $(element), ancestor = $(ancestor);
+ while (element = element.parentNode)
+ if (element == ancestor) return true;
+ return false;
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var x = element.x ? element.x : element.offsetLeft,
+ y = element.y ? element.y : element.offsetTop;
+ window.scrollTo(x, y);
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ var value = element.style[style.camelize()];
+ if (!value) {
+ if (document.defaultView && document.defaultView.getComputedStyle) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css.getPropertyValue(style) : null;
+ } else if (element.currentStyle) {
+ value = element.currentStyle[style.camelize()];
+ }
+ }
+
+ if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+ if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+ return value == 'auto' ? null : value;
+ },
+
+ setStyle: function(element, style) {
+ element = $(element);
+ for (var name in style)
+ element.style[name.camelize()] = style[name];
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ if (Element.getStyle(element, 'display') != 'none')
+ return {width: element.offsetWidth, height: element.offsetHeight};
+
+ // All *Width and *Height properties give 0 on elements with display none,
+ // so enable the element temporarily
+ var els = element.style;
+ var originalVisibility = els.visibility;
+ var originalPosition = els.position;
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = '';
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ els.display = 'none';
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ return {width: originalWidth, height: originalHeight};
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (window.opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return;
+ element._overflow = element.style.overflow;
+ if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+ element.style.overflow = 'hidden';
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return;
+ element.style.overflow = element._overflow;
+ element._overflow = undefined;
+ }
+}
+
+Object.extend(Element, Element.Methods);
+
+var _nativeExtensions = false;
+
+if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+ var HTMLElement = {}
+ HTMLElement.prototype = document.createElement('div').__proto__;
+}
+
+Element.addMethods = function(methods) {
+ Object.extend(Element.Methods, methods || {});
+
+ if(typeof HTMLElement != 'undefined') {
+ var methods = Element.Methods, cache = Element.extend.cache;
+ for (property in methods) {
+ var value = methods[property];
+ if (typeof value == 'function')
+ HTMLElement.prototype[property] = cache.findOrStore(value);
+ }
+ _nativeExtensions = true;
+ }
+}
+
+Element.addMethods();
+
+var Toggle = new Object();
+Toggle.display = Element.toggle;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+ this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+ initialize: function(element, content) {
+ this.element = $(element);
+ this.content = content.stripScripts();
+
+ if (this.adjacency && this.element.insertAdjacentHTML) {
+ try {
+ this.element.insertAdjacentHTML(this.adjacency, this.content);
+ } catch (e) {
+ var tagName = this.element.tagName.toLowerCase();
+ if (tagName == 'tbody' || tagName == 'tr') {
+ this.insertContent(this.contentFromAnonymousTable());
+ } else {
+ throw e;
+ }
+ }
+ } else {
+ this.range = this.element.ownerDocument.createRange();
+ if (this.initializeRange) this.initializeRange();
+ this.insertContent([this.range.createContextualFragment(this.content)]);
+ }
+
+ setTimeout(function() {content.evalScripts()}, 10);
+ },
+
+ contentFromAnonymousTable: function() {
+ var div = document.createElement('div');
+ div.innerHTML = '';
+ return $A(div.childNodes[0].childNodes[0].childNodes);
+ }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+ initializeRange: function() {
+ this.range.setStartBefore(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment, this.element);
+ }).bind(this));
+ }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(true);
+ },
+
+ insertContent: function(fragments) {
+ fragments.reverse(false).each((function(fragment) {
+ this.element.insertBefore(fragment, this.element.firstChild);
+ }).bind(this));
+ }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.appendChild(fragment);
+ }).bind(this));
+ }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+ initializeRange: function() {
+ this.range.setStartAfter(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment,
+ this.element.nextSibling);
+ }).bind(this));
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set(this.toArray().concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set(this.select(function(className) {
+ return className != classNameToRemove;
+ }).join(' '));
+ },
+
+ toString: function() {
+ return this.toArray().join(' ');
+ }
+}
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+var Selector = Class.create();
+Selector.prototype = {
+ initialize: function(expression) {
+ this.params = {classNames: []};
+ this.expression = expression.toString().strip();
+ this.parseExpression();
+ this.compileMatcher();
+ },
+
+ parseExpression: function() {
+ function abort(message) { throw 'Parse error in selector: ' + message; }
+
+ if (this.expression == '') abort('empty expression');
+
+ var params = this.params, expr = this.expression, match, modifier, clause, rest;
+ while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
+ params.attributes = params.attributes || [];
+ params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
+ expr = match[1];
+ }
+
+ if (expr == '*') return this.params.wildcard = true;
+
+ while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
+ modifier = match[1], clause = match[2], rest = match[3];
+ switch (modifier) {
+ case '#': params.id = clause; break;
+ case '.': params.classNames.push(clause); break;
+ case '':
+ case undefined: params.tagName = clause.toUpperCase(); break;
+ default: abort(expr.inspect());
+ }
+ expr = rest;
+ }
+
+ if (expr.length > 0) abort(expr.inspect());
+ },
+
+ buildMatchExpression: function() {
+ var params = this.params, conditions = [], clause;
+
+ if (params.wildcard)
+ conditions.push('true');
+ if (clause = params.id)
+ conditions.push('element.id == ' + clause.inspect());
+ if (clause = params.tagName)
+ conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
+ if ((clause = params.classNames).length > 0)
+ for (var i = 0; i < clause.length; i++)
+ conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
+ if (clause = params.attributes) {
+ clause.each(function(attribute) {
+ var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
+ var splitValueBy = function(delimiter) {
+ return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
+ }
+
+ switch (attribute.operator) {
+ case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break;
+ case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
+ case '|=': conditions.push(
+ splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
+ ); break;
+ case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
+ case '':
+ case undefined: conditions.push(value + ' != null'); break;
+ default: throw 'Unknown operator ' + attribute.operator + ' in selector';
+ }
+ });
+ }
+
+ return conditions.join(' && ');
+ },
+
+ compileMatcher: function() {
+ this.match = new Function('element', 'if (!element.tagName) return false; \
+ return ' + this.buildMatchExpression());
+ },
+
+ findElements: function(scope) {
+ var element;
+
+ if (element = $(this.params.id))
+ if (this.match(element))
+ if (!scope || Element.childOf(element, scope))
+ return [element];
+
+ scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
+
+ var results = [];
+ for (var i = 0; i < scope.length; i++)
+ if (this.match(element = scope[i]))
+ results.push(Element.extend(element));
+
+ return results;
+ },
+
+ toString: function() {
+ return this.expression;
+ }
+}
+
+function $$() {
+ return $A(arguments).map(function(expression) {
+ return expression.strip().split(/\s+/).inject([null], function(results, expr) {
+ var selector = new Selector(expr);
+ return results.map(selector.findElements.bind(selector)).flatten();
+ });
+ }).flatten();
+}
+var Field = {
+ clear: function() {
+ for (var i = 0; i < arguments.length; i++)
+ $(arguments[i]).value = '';
+ },
+
+ focus: function(element) {
+ $(element).focus();
+ },
+
+ present: function() {
+ for (var i = 0; i < arguments.length; i++)
+ if ($(arguments[i]).value == '') return false;
+ return true;
+ },
+
+ select: function(element) {
+ $(element).select();
+ },
+
+ activate: function(element) {
+ element = $(element);
+ element.focus();
+ if (element.select)
+ element.select();
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Form = {
+ serialize: function(form) {
+ var elements = Form.getElements($(form));
+ var queryComponents = new Array();
+
+ for (var i = 0; i < elements.length; i++) {
+ var queryComponent = Form.Element.serialize(elements[i]);
+ if (queryComponent)
+ queryComponents.push(queryComponent);
+ }
+
+ return queryComponents.join('&');
+ },
+
+ getElements: function(form) {
+ form = $(form);
+ var elements = new Array();
+
+ for (var tagName in Form.Element.Serializers) {
+ var tagElements = form.getElementsByTagName(tagName);
+ for (var j = 0; j < tagElements.length; j++)
+ elements.push(tagElements[j]);
+ }
+ return elements;
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name)
+ return inputs;
+
+ var matchingInputs = new Array();
+ for (var i = 0; i < inputs.length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) ||
+ (name && input.name != name))
+ continue;
+ matchingInputs.push(input);
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ var elements = Form.getElements(form);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ element.blur();
+ element.disabled = 'true';
+ }
+ },
+
+ enable: function(form) {
+ var elements = Form.getElements(form);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ element.disabled = '';
+ }
+ },
+
+ findFirstElement: function(form) {
+ return Form.getElements(form).find(function(element) {
+ return element.type != 'hidden' && !element.disabled &&
+ ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ Field.activate(Form.findFirstElement(form));
+ },
+
+ reset: function(form) {
+ $(form).reset();
+ }
+}
+
+Form.Element = {
+ serialize: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter) {
+ var key = encodeURIComponent(parameter[0]);
+ if (key.length == 0) return;
+
+ if (parameter[1].constructor != Array)
+ parameter[1] = [parameter[1]];
+
+ return parameter[1].map(function(value) {
+ return key + '=' + encodeURIComponent(value);
+ }).join('&');
+ }
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter)
+ return parameter[1];
+ }
+}
+
+Form.Element.Serializers = {
+ input: function(element) {
+ switch (element.type.toLowerCase()) {
+ case 'submit':
+ case 'hidden':
+ case 'password':
+ case 'text':
+ return Form.Element.Serializers.textarea(element);
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element);
+ }
+ return false;
+ },
+
+ inputSelector: function(element) {
+ if (element.checked)
+ return [element.name, element.value];
+ },
+
+ textarea: function(element) {
+ return [element.name, element.value];
+ },
+
+ select: function(element) {
+ return Form.Element.Serializers[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ },
+
+ selectOne: function(element) {
+ var value = '', opt, index = element.selectedIndex;
+ if (index >= 0) {
+ opt = element.options[index];
+ value = opt.value || opt.text;
+ }
+ return [element.name, value];
+ },
+
+ selectMany: function(element) {
+ var value = [];
+ for (var i = 0; i < element.length; i++) {
+ var opt = element.options[i];
+ if (opt.selected)
+ value.push(opt.value || opt.text);
+ }
+ return [element.name, value];
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+ initialize: function(element, frequency, callback) {
+ this.frequency = frequency;
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ var elements = Form.getElements(this.element);
+ for (var i = 0; i < elements.length; i++)
+ this.registerCallback(elements[i]);
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ case 'password':
+ case 'text':
+ case 'textarea':
+ case 'select-one':
+ case 'select-multiple':
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) {
+ var Event = new Object();
+}
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+
+ element: function(event) {
+ return event.target || event.srcElement;
+ },
+
+ isLeftClick: function(event) {
+ return (((event.which) && (event.which == 1)) ||
+ ((event.button) && (event.button == 1)));
+ },
+
+ pointerX: function(event) {
+ return event.pageX || (event.clientX +
+ (document.documentElement.scrollLeft || document.body.scrollLeft));
+ },
+
+ pointerY: function(event) {
+ return event.pageY || (event.clientY +
+ (document.documentElement.scrollTop || document.body.scrollTop));
+ },
+
+ stop: function(event) {
+ if (event.preventDefault) {
+ event.preventDefault();
+ event.stopPropagation();
+ } else {
+ event.returnValue = false;
+ event.cancelBubble = true;
+ }
+ },
+
+ // find the first node with the given tagName, starting from the
+ // node the event was triggered on; traverses the DOM upwards
+ findElement: function(event, tagName) {
+ var element = Event.element(event);
+ while (element.parentNode && (!element.tagName ||
+ (element.tagName.toUpperCase() != tagName.toUpperCase())))
+ element = element.parentNode;
+ return element;
+ },
+
+ observers: false,
+
+ _observeAndCache: function(element, name, observer, useCapture) {
+ if (!this.observers) this.observers = [];
+ if (element.addEventListener) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.addEventListener(name, observer, useCapture);
+ } else if (element.attachEvent) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.attachEvent('on' + name, observer);
+ }
+ },
+
+ unloadCache: function() {
+ if (!Event.observers) return;
+ for (var i = 0; i < Event.observers.length; i++) {
+ Event.stopObserving.apply(this, Event.observers[i]);
+ Event.observers[i][0] = null;
+ }
+ Event.observers = false;
+ },
+
+ observe: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+ || element.attachEvent))
+ name = 'keydown';
+
+ this._observeAndCache(element, name, observer, useCapture);
+ },
+
+ stopObserving: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+ || element.detachEvent))
+ name = 'keydown';
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, observer, useCapture);
+ } else if (element.detachEvent) {
+ element.detachEvent('on' + name, observer);
+ }
+ }
+});
+
+/* prevent memory leaks in IE */
+if (navigator.appVersion.match(/\bMSIE\b/))
+ Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ realOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ positionedOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ p = Element.getStyle(element, 'position');
+ if (p == 'relative' || p == 'absolute') break;
+ }
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ offsetParent: function(element) {
+ if (element.offsetParent) return element.offsetParent;
+ if (element == document.body) return element;
+
+ while ((element = element.parentNode) && element != document.body)
+ if (Element.getStyle(element, 'position') != 'static')
+ return element;
+
+ return document.body;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = this.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = this.realOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = this.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
+ },
+
+ clone: function(source, target) {
+ source = $(source);
+ target = $(target);
+ target.style.position = 'absolute';
+ var offsets = this.cumulativeOffset(source);
+ target.style.top = offsets[1] + 'px';
+ target.style.left = offsets[0] + 'px';
+ target.style.width = source.offsetWidth + 'px';
+ target.style.height = source.offsetHeight + 'px';
+ },
+
+ page: function(forElement) {
+ var valueT = 0, valueL = 0;
+
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ // Safari fix
+ if (element.offsetParent==document.body)
+ if (Element.getStyle(element,'position')=='absolute') break;
+
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ } while (element = element.parentNode);
+
+ return [valueL, valueT];
+ },
+
+ clone: function(source, target) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || {})
+
+ // find page position of source
+ source = $(source);
+ var p = Position.page(source);
+
+ // find coordinate system to use
+ target = $(target);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(target,'position') == 'absolute') {
+ parent = Position.offsetParent(target);
+ delta = Position.page(parent);
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
+ if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
+ if(options.setWidth) target.style.width = source.offsetWidth + 'px';
+ if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.style.position == 'absolute') return;
+ Position.prepare();
+
+ var offsets = Position.positionedOffset(element);
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';;
+ element.style.left = left + 'px';;
+ element.style.width = width + 'px';;
+ element.style.height = height + 'px';;
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.style.position == 'relative') return;
+ Position.prepare();
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+ }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned. For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+ Position.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return [valueL, valueT];
+ }
+}
+
diff --git a/posterita/src/web/javascripts/scriptaculous.js b/posterita/src/web/javascripts/scriptaculous.js
new file mode 100644
index 0000000000..f61fc57f74
--- /dev/null
+++ b/posterita/src/web/javascripts/scriptaculous.js
@@ -0,0 +1,47 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var Scriptaculous = {
+ Version: '1.6.1',
+ require: function(libraryName) {
+ // inserting via DOM fails in Safari 2.0, so brute force approach
+ document.write('');
+ },
+ load: function() {
+ if((typeof Prototype=='undefined') ||
+ (typeof Element == 'undefined') ||
+ (typeof Element.Methods=='undefined') ||
+ parseFloat(Prototype.Version.split(".")[0] + "." +
+ Prototype.Version.split(".")[1]) < 1.5)
+ throw("script.aculo.us requires the Prototype JavaScript framework >= 1.5.0");
+
+ $A(document.getElementsByTagName("script")).findAll( function(s) {
+ return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
+ }).each( function(s) {
+ var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
+ var includes = s.src.match(/\?.*load=([a-z,]*)/);
+ (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider').split(',').each(
+ function(include) { Scriptaculous.require(path+include+'.js') });
+ });
+ }
+}
+
+Scriptaculous.load();
\ No newline at end of file
diff --git a/posterita/src/web/javascripts/slider.js b/posterita/src/web/javascripts/slider.js
new file mode 100644
index 0000000000..c0f1fc01bb
--- /dev/null
+++ b/posterita/src/web/javascripts/slider.js
@@ -0,0 +1,283 @@
+// Copyright (c) 2005 Marty Haught, Thomas Fuchs
+//
+// See http://script.aculo.us for more info
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+if(!Control) var Control = {};
+Control.Slider = Class.create();
+
+// options:
+// axis: 'vertical', or 'horizontal' (default)
+//
+// callbacks:
+// onChange(value)
+// onSlide(value)
+Control.Slider.prototype = {
+ initialize: function(handle, track, options) {
+ var slider = this;
+
+ if(handle instanceof Array) {
+ this.handles = handle.collect( function(e) { return $(e) });
+ } else {
+ this.handles = [$(handle)];
+ }
+
+ this.track = $(track);
+ this.options = options || {};
+
+ this.axis = this.options.axis || 'horizontal';
+ this.increment = this.options.increment || 1;
+ this.step = parseInt(this.options.step || '1');
+ this.range = this.options.range || $R(0,1);
+
+ this.value = 0; // assure backwards compat
+ this.values = this.handles.map( function() { return 0 });
+ this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
+ this.options.startSpan = $(this.options.startSpan || null);
+ this.options.endSpan = $(this.options.endSpan || null);
+
+ this.restricted = this.options.restricted || false;
+
+ this.maximum = this.options.maximum || this.range.end;
+ this.minimum = this.options.minimum || this.range.start;
+
+ // Will be used to align the handle onto the track, if necessary
+ this.alignX = parseInt(this.options.alignX || '0');
+ this.alignY = parseInt(this.options.alignY || '0');
+
+ this.trackLength = this.maximumOffset() - this.minimumOffset();
+ this.handleLength = this.isVertical() ? this.handles[0].offsetHeight : this.handles[0].offsetWidth;
+
+ this.active = false;
+ this.dragging = false;
+ this.disabled = false;
+
+ if(this.options.disabled) this.setDisabled();
+
+ // Allowed values array
+ this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
+ if(this.allowedValues) {
+ this.minimum = this.allowedValues.min();
+ this.maximum = this.allowedValues.max();
+ }
+
+ this.eventMouseDown = this.startDrag.bindAsEventListener(this);
+ this.eventMouseUp = this.endDrag.bindAsEventListener(this);
+ this.eventMouseMove = this.update.bindAsEventListener(this);
+
+ // Initialize handles in reverse (make sure first handle is active)
+ this.handles.each( function(h,i) {
+ i = slider.handles.length-1-i;
+ slider.setValue(parseFloat(
+ (slider.options.sliderValue instanceof Array ?
+ slider.options.sliderValue[i] : slider.options.sliderValue) ||
+ slider.range.start), i);
+ Element.makePositioned(h); // fix IE
+ Event.observe(h, "mousedown", slider.eventMouseDown);
+ });
+
+ Event.observe(this.track, "mousedown", this.eventMouseDown);
+ Event.observe(document, "mouseup", this.eventMouseUp);
+ Event.observe(document, "mousemove", this.eventMouseMove);
+
+ this.initialized = true;
+ },
+ dispose: function() {
+ var slider = this;
+ Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
+ Event.stopObserving(document, "mouseup", this.eventMouseUp);
+ Event.stopObserving(document, "mousemove", this.eventMouseMove);
+ this.handles.each( function(h) {
+ Event.stopObserving(h, "mousedown", slider.eventMouseDown);
+ });
+ },
+ setDisabled: function(){
+ this.disabled = true;
+ },
+ setEnabled: function(){
+ this.disabled = false;
+ },
+ getNearestValue: function(value){
+ if(this.allowedValues){
+ if(value >= this.allowedValues.max()) return(this.allowedValues.max());
+ if(value <= this.allowedValues.min()) return(this.allowedValues.min());
+
+ var offset = Math.abs(this.allowedValues[0] - value);
+ var newValue = this.allowedValues[0];
+ this.allowedValues.each( function(v) {
+ var currentOffset = Math.abs(v - value);
+ if(currentOffset <= offset){
+ newValue = v;
+ offset = currentOffset;
+ }
+ });
+ return newValue;
+ }
+ if(value > this.range.end) return this.range.end;
+ if(value < this.range.start) return this.range.start;
+ return value;
+ },
+ setValue: function(sliderValue, handleIdx){
+ if(!this.active) {
+ this.activeHandle = this.handles[handleIdx];
+ this.activeHandleIdx = handleIdx;
+ this.updateStyles();
+ }
+ handleIdx = handleIdx || this.activeHandleIdx || 0;
+ if(this.initialized && this.restricted) {
+ if((handleIdx>0) && (sliderValuethis.values[handleIdx+1]))
+ sliderValue = this.values[handleIdx+1];
+ }
+ sliderValue = this.getNearestValue(sliderValue);
+ this.values[handleIdx] = sliderValue;
+ this.value = this.values[0]; // assure backwards compat
+
+ this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
+ this.translateToPx(sliderValue);
+
+ this.drawSpans();
+ if(!this.dragging || !this.event) this.updateFinished();
+ },
+ setValueBy: function(delta, handleIdx) {
+ this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
+ handleIdx || this.activeHandleIdx || 0);
+ },
+ translateToPx: function(value) {
+ return Math.round(
+ ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
+ (value - this.range.start)) + "px";
+ },
+ translateToValue: function(offset) {
+ return ((offset/(this.trackLength-this.handleLength) *
+ (this.range.end-this.range.start)) + this.range.start);
+ },
+ getRange: function(range) {
+ var v = this.values.sortBy(Prototype.K);
+ range = range || 0;
+ return $R(v[range],v[range+1]);
+ },
+ minimumOffset: function(){
+ return(this.isVertical() ? this.alignY : this.alignX);
+ },
+ maximumOffset: function(){
+ return(this.isVertical() ?
+ this.track.offsetHeight - this.alignY : this.track.offsetWidth - this.alignX);
+ },
+ isVertical: function(){
+ return (this.axis == 'vertical');
+ },
+ drawSpans: function() {
+ var slider = this;
+ if(this.spans)
+ $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
+ if(this.options.startSpan)
+ this.setSpan(this.options.startSpan,
+ $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
+ if(this.options.endSpan)
+ this.setSpan(this.options.endSpan,
+ $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
+ },
+ setSpan: function(span, range) {
+ if(this.isVertical()) {
+ span.style.top = this.translateToPx(range.start);
+ span.style.height = this.translateToPx(range.end - range.start + this.range.start);
+ } else {
+ span.style.left = this.translateToPx(range.start);
+ span.style.width = this.translateToPx(range.end - range.start + this.range.start);
+ }
+ },
+ updateStyles: function() {
+ this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
+ Element.addClassName(this.activeHandle, 'selected');
+ },
+ startDrag: function(event) {
+ if(Event.isLeftClick(event)) {
+ if(!this.disabled){
+ this.active = true;
+
+ var handle = Event.element(event);
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ if(handle==this.track) {
+ var offsets = Position.cumulativeOffset(this.track);
+ this.event = event;
+ this.setValue(this.translateToValue(
+ (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
+ ));
+ var offsets = Position.cumulativeOffset(this.activeHandle);
+ this.offsetX = (pointer[0] - offsets[0]);
+ this.offsetY = (pointer[1] - offsets[1]);
+ } else {
+ // find the handle (prevents issues with Safari)
+ while((this.handles.indexOf(handle) == -1) && handle.parentNode)
+ handle = handle.parentNode;
+
+ this.activeHandle = handle;
+ this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
+ this.updateStyles();
+
+ var offsets = Position.cumulativeOffset(this.activeHandle);
+ this.offsetX = (pointer[0] - offsets[0]);
+ this.offsetY = (pointer[1] - offsets[1]);
+ }
+ }
+ Event.stop(event);
+ }
+ },
+ update: function(event) {
+ if(this.active) {
+ if(!this.dragging) this.dragging = true;
+ this.draw(event);
+ // fix AppleWebKit rendering
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+ Event.stop(event);
+ }
+ },
+ draw: function(event) {
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ var offsets = Position.cumulativeOffset(this.track);
+ pointer[0] -= this.offsetX + offsets[0];
+ pointer[1] -= this.offsetY + offsets[1];
+ this.event = event;
+ this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
+ if(this.initialized && this.options.onSlide)
+ this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
+ },
+ endDrag: function(event) {
+ if(this.active && this.dragging) {
+ this.finishDrag(event, true);
+ Event.stop(event);
+ }
+ this.active = false;
+ this.dragging = false;
+ },
+ finishDrag: function(event, success) {
+ this.active = false;
+ this.dragging = false;
+ this.updateFinished();
+ },
+ updateFinished: function() {
+ if(this.initialized && this.options.onChange)
+ this.options.onChange(this.values.length>1 ? this.values : this.value, this);
+ this.event = null;
+ }
+}
\ No newline at end of file
diff --git a/posterita/src/web/javascripts/sorttable.js b/posterita/src/web/javascripts/sorttable.js
new file mode 100644
index 0000000000..9914fccbc7
--- /dev/null
+++ b/posterita/src/web/javascripts/sorttable.js
@@ -0,0 +1,209 @@
+addEvent(window, "load", sortables_init);
+
+var SORT_COLUMN_INDEX;
+
+function sortables_init() {
+ // Find all tables with class sortable and make them sortable
+ if (!document.getElementsByTagName) return;
+ tbls = document.getElementsByTagName("table");
+ for (ti=0;ti 0) {
+ var firstRow = table.rows[0];
+ }
+ if (!firstRow) return;
+
+ // We have a first row: assume it's the header, and make its contents clickable links
+ for (var i=0;i' +
+ txt+' ';
+ }
+}
+
+function ts_getInnerText(el) {
+ if (typeof el == "string") return el;
+ if (typeof el == "undefined") { return el };
+ if (el.innerText) return el.innerText; //Not needed but it is faster
+ var str = "";
+
+ var cs = el.childNodes;
+ var l = cs.length;
+ for (var i = 0; i < l; i++) {
+ switch (cs[i].nodeType) {
+ case 1: //ELEMENT_NODE
+ str += ts_getInnerText(cs[i]);
+ break;
+ case 3: //TEXT_NODE
+ str += cs[i].nodeValue;
+ break;
+ }
+ }
+ return str;
+}
+
+function ts_resortTable(lnk,clid) {
+ // get the span
+ var span;
+ for (var ci=0;ci' +
+ '' +
+ 'Status Test Message ' +
+ ' ' +
+ '
';
+ this.logsummary = $('logsummary')
+ this.loglines = $('loglines');
+ },
+ _toHTML: function(txt) {
+ return txt.escapeHTML().replace(/\n/g," ");
+ }
+}
+
+Test.Unit.Runner = Class.create();
+Test.Unit.Runner.prototype = {
+ initialize: function(testcases) {
+ this.options = Object.extend({
+ testLog: 'testlog'
+ }, arguments[1] || {});
+ this.options.resultsURL = this.parseResultsURLQueryParameter();
+ if (this.options.testLog) {
+ this.options.testLog = $(this.options.testLog) || null;
+ }
+ if(this.options.tests) {
+ this.tests = [];
+ for(var i = 0; i < this.options.tests.length; i++) {
+ if(/^test/.test(this.options.tests[i])) {
+ this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
+ }
+ }
+ } else {
+ if (this.options.test) {
+ this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
+ } else {
+ this.tests = [];
+ for(var testcase in testcases) {
+ if(/^test/.test(testcase)) {
+ this.tests.push(new Test.Unit.Testcase(testcase, testcases[testcase], testcases["setup"], testcases["teardown"]));
+ }
+ }
+ }
+ }
+ this.currentTest = 0;
+ this.logger = new Test.Unit.Logger(this.options.testLog);
+ setTimeout(this.runTests.bind(this), 1000);
+ },
+ parseResultsURLQueryParameter: function() {
+ return window.location.search.parseQuery()["resultsURL"];
+ },
+ // Returns:
+ // "ERROR" if there was an error,
+ // "FAILURE" if there was a failure, or
+ // "SUCCESS" if there was neither
+ getResult: function() {
+ var hasFailure = false;
+ for(var i=0;i 0) {
+ return "ERROR";
+ }
+ if (this.tests[i].failures > 0) {
+ hasFailure = true;
+ }
+ }
+ if (hasFailure) {
+ return "FAILURE";
+ } else {
+ return "SUCCESS";
+ }
+ },
+ postResults: function() {
+ if (this.options.resultsURL) {
+ new Ajax.Request(this.options.resultsURL,
+ { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
+ }
+ },
+ runTests: function() {
+ var test = this.tests[this.currentTest];
+ if (!test) {
+ // finished!
+ this.postResults();
+ this.logger.summary(this.summary());
+ return;
+ }
+ if(!test.isWaiting) {
+ this.logger.start(test.name);
+ }
+ test.run();
+ if(test.isWaiting) {
+ this.logger.message("Waiting for " + test.timeToWait + "ms");
+ setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
+ } else {
+ this.logger.finish(test.status(), test.summary());
+ this.currentTest++;
+ // tail recursive, hopefully the browser will skip the stackframe
+ this.runTests();
+ }
+ },
+ summary: function() {
+ var assertions = 0;
+ var failures = 0;
+ var errors = 0;
+ var messages = [];
+ for(var i=0;i 0) return 'failed';
+ if (this.errors > 0) return 'error';
+ return 'passed';
+ },
+ assert: function(expression) {
+ var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
+ try { expression ? this.pass() :
+ this.fail(message); }
+ catch(e) { this.error(e); }
+ },
+ assertEqual: function(expected, actual) {
+ var message = arguments[2] || "assertEqual";
+ try { (expected == actual) ? this.pass() :
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
+ '", actual "' + Test.Unit.inspect(actual) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertEnumEqual: function(expected, actual) {
+ var message = arguments[2] || "assertEnumEqual";
+ try { $A(expected).length == $A(actual).length &&
+ expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
+ this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) +
+ ', actual ' + Test.Unit.inspect(actual)); }
+ catch(e) { this.error(e); }
+ },
+ assertNotEqual: function(expected, actual) {
+ var message = arguments[2] || "assertNotEqual";
+ try { (expected != actual) ? this.pass() :
+ this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertNull: function(obj) {
+ var message = arguments[1] || 'assertNull'
+ try { (obj==null) ? this.pass() :
+ this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertHidden: function(element) {
+ var message = arguments[1] || 'assertHidden';
+ this.assertEqual("none", element.style.display, message);
+ },
+ assertNotNull: function(object) {
+ var message = arguments[1] || 'assertNotNull';
+ this.assert(object != null, message);
+ },
+ assertInstanceOf: function(expected, actual) {
+ var message = arguments[2] || 'assertInstanceOf';
+ try {
+ (actual instanceof expected) ? this.pass() :
+ this.fail(message + ": object was not an instance of the expected type"); }
+ catch(e) { this.error(e); }
+ },
+ assertNotInstanceOf: function(expected, actual) {
+ var message = arguments[2] || 'assertNotInstanceOf';
+ try {
+ !(actual instanceof expected) ? this.pass() :
+ this.fail(message + ": object was an instance of the not expected type"); }
+ catch(e) { this.error(e); }
+ },
+ _isVisible: function(element) {
+ element = $(element);
+ if(!element.parentNode) return true;
+ this.assertNotNull(element);
+ if(element.style && Element.getStyle(element, 'display') == 'none')
+ return false;
+
+ return this._isVisible(element.parentNode);
+ },
+ assertNotVisible: function(element) {
+ this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
+ },
+ assertVisible: function(element) {
+ this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
+ },
+ benchmark: function(operation, iterations) {
+ var startAt = new Date();
+ (iterations || 1).times(operation);
+ var timeTaken = ((new Date())-startAt);
+ this.info((arguments[2] || 'Operation') + ' finished ' +
+ iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+ return timeTaken;
+ }
+}
+
+Test.Unit.Testcase = Class.create();
+Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
+ initialize: function(name, test, setup, teardown) {
+ Test.Unit.Assertions.prototype.initialize.bind(this)();
+ this.name = name;
+ this.test = test || function() {};
+ this.setup = setup || function() {};
+ this.teardown = teardown || function() {};
+ this.isWaiting = false;
+ this.timeToWait = 1000;
+ },
+ wait: function(time, nextPart) {
+ this.isWaiting = true;
+ this.test = nextPart;
+ this.timeToWait = time;
+ },
+ run: function() {
+ try {
+ try {
+ if (!this.isWaiting) this.setup.bind(this)();
+ this.isWaiting = false;
+ this.test.bind(this)();
+ } finally {
+ if(!this.isWaiting) {
+ this.teardown.bind(this)();
+ }
+ }
+ }
+ catch(e) { this.error(e); }
+ }
+});
diff --git a/posterita/src/web/js/createPOSOrder.js b/posterita/src/web/js/createPOSOrder.js
new file mode 100644
index 0000000000..d1a63277b4
--- /dev/null
+++ b/posterita/src/web/js/createPOSOrder.js
@@ -0,0 +1,193 @@
+
diff --git a/posterita/src/web/js/customer.js b/posterita/src/web/js/customer.js
new file mode 100644
index 0000000000..3e81a9cfb7
--- /dev/null
+++ b/posterita/src/web/js/customer.js
@@ -0,0 +1,160 @@
+/**
+ * Product: Posterita Web-Based POS and Adempiere Plugin
+ * Copyright (C) 2007 Posterita Ltd
+ * This file is part of POSterita
+ *
+ * POSterita is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ @author praveen
+ */
+
+
+//----------------------------------FUNCTION DECLARATIONS---------------------------------------------
+//create new customer
+function createCustomer()
+{
+ window.location = "InitCreatePOSCustomer.do?action=initCreatePOSCustomer&creatingFromOrder=true";
+}
+
+//sets the current customer to default
+function defaultCustomer()
+{
+ try
+ {
+ var bpartner = document.getElementsByName('bpartnerId')[0];
+ var bpartnerName = document.getElementsByName('partnerName')[0];
+
+ bpartner.value = "";
+ bpartnerName.value = "";
+ }
+ catch(e)
+ {
+ toConsole('Unable to set default customer');
+ toConsole(e);
+ }
+
+}
+
+//gets the custom name.
+function getCustomerName()
+{
+ var customerID = $FElement('bpartnerId').value;
+ var url = 'POSCustomerAction.do';
+ var pars = 'action=getNameByID&bpartnerId=' + customerID;
+
+ toConsole('Requesting: ' + url + '?' + pars);
+
+ var myAjax = new Ajax.Request( url,
+ {
+ method: 'get',
+ parameters: pars,
+ onSuccess: evaluateResponse,
+ onFailure: reportError
+ });
+
+}
+
+//set customer name
+function setCustomerName(name)
+{
+ $FElement('partnerName').value = name;
+}
+
+function evaluateResponse(request)
+{
+ var script = request.responseText;
+ eval(script);
+}
+
+function reportError(request)
+{
+ alert('Some error occured while communicating with the server. Please try again.');
+}
+//------------------------------REGISTERING SOME EVENTS OR INITIALISATION-----------------------------
+
+$FElement('bpartnerId').onkeyup = function(e)
+{
+ try
+ {
+ var event = e||window.event;
+
+ if(event.keyCode == 13)
+ {
+ //request client name
+ getCustomerName();
+ this.blur();
+ }
+ }
+ catch (e)
+ {
+ toConsole(e);
+ }
+};
+
+$('customerQuery').Autocompleter = new Ajax.Autocompleter('customerQuery','customerSearchResult','SearchCustomerAction.do',{
+paramName:'customerQuery',
+frequency:TROTTLE_TIME,
+afterUpdateElement:function(e1,e2){
+ var bpartner = $FElement('bpartnerId');
+ var bpartnerName = $FElement('partnerName');
+
+
+ if(e2.value != '-1')
+ {
+ toConsole('1');
+ if(e2.value)
+ {
+ toConsole('1.1.1');
+ bpartner.value = e2.value;
+ }
+ else
+ {
+ toConsole('1.1.2');
+ bpartner.value = '';
+ }
+
+ if(bpartnerName)
+ {
+ toConsole('1.2');
+ bpartnerName.value = e2.getAttribute('name');
+ }//if
+ }
+ else
+ {
+ toConsole('2');
+
+ bpartner.value = "";
+ if(bpartnerName)
+ {
+ toConsole('2.1');
+ bpartnerName.value = "";
+ }//if
+ }//if
+
+ toConsole('Setting customer-->' + bpartnerName.value);
+
+ $('customerQuery').value = "";
+
+ //focusBarcode();
+
+
+ }
+
+});
+
+var bpartner = $FElement('bpartnerId');
+bpartner.initialValue = bpartner.value;
\ No newline at end of file
diff --git a/posterita/src/web/js/customer2.js b/posterita/src/web/js/customer2.js
new file mode 100644
index 0000000000..6216276a8b
--- /dev/null
+++ b/posterita/src/web/js/customer2.js
@@ -0,0 +1,161 @@
+/**
+ * Product: Posterita Web-Based POS and Adempiere Plugin
+ * Copyright (C) 2007 Posterita Ltd
+ * This file is part of POSterita
+ *
+ * POSterita is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ @author praveen
+ */
+
+
+//----------------------------------FUNCTION DECLARATIONS---------------------------------------------
+//create new customer
+function createCustomer()
+{
+ window.location = "InitCreatePOSCustomer.do?action=initCreatePOSCustomer&creatingFromOrder=true";
+}
+
+//sets the current customer to default
+function defaultCustomer()
+{
+ try
+ {
+ var bpartner = document.getElementsByName('bpartnerId')[0];
+ var bpartnerName = document.getElementsByName('partnerName')[0];
+
+ bpartner.value = "";
+ bpartnerName.value = "";
+ }
+ catch(e)
+ {
+ toConsole('Unable to set default customer');
+ toConsole(e);
+ }
+
+}
+
+//gets the custom name.
+function getCustomerName()
+{
+ var customerID = $FElement('bpartnerId').value;
+ var url = 'POSCustomerAction.do';
+ var pars = 'action=getNameByID&bpartnerId=' + customerID;
+
+ toConsole('Requesting: ' + url + '?' + pars);
+
+ var myAjax = new Ajax.Request( url,
+ {
+ method: 'get',
+ parameters: pars,
+ onSuccess: evaluateResponse,
+ onFailure: reportError
+ });
+
+}
+
+//set customer name
+function setCustomerName(name)
+{
+ $FElement('partnerName').value = name;
+ document.forms[0].submit();
+}
+
+function evaluateResponse(request)
+{
+ var script = request.responseText;
+ eval(script);
+}
+
+function reportError(request)
+{
+ alert('Some error occured while communicating with the server. Please try again.');
+}
+//------------------------------REGISTERING SOME EVENTS OR INITIALISATION-----------------------------
+
+$FElement('bpartnerId').onkeyup = function(e)
+{
+ try
+ {
+ var event = e||window.event;
+
+ if(event.keyCode == 13)
+ {
+ //request client name
+ getCustomerName();
+ }
+ }
+ catch (e)
+ {
+ toConsole(e);
+ }
+};
+
+$('customerQuery').Autocompleter = new Ajax.Autocompleter('customerQuery','customerSearchResult','SearchCustomerAction.do',{
+paramName:'customerQuery',
+frequency:TROTTLE_TIME,
+afterUpdateElement:function(e1,e2){
+ var bpartner = $FElement('bpartnerId');
+ var bpartnerName = $FElement('partnerName');
+
+
+ if(e2.value != '-1')
+ {
+ toConsole('1');
+ if(e2.value)
+ {
+ toConsole('1.1.1');
+ bpartner.value = e2.value;
+ }
+ else
+ {
+ toConsole('1.1.2');
+ bpartner.value = '';
+ }
+
+ if(bpartnerName)
+ {
+ toConsole('1.2');
+ bpartnerName.value = e2.getAttribute('name');
+ }//if
+ }
+ else
+ {
+ toConsole('2');
+
+ bpartner.value = "";
+ if(bpartnerName)
+ {
+ toConsole('2.1');
+ bpartnerName.value = "";
+ }//if
+ }//if
+
+ toConsole('Setting customer-->' + bpartnerName.value);
+ document.forms[0].submit();
+
+ $('customerQuery').value = "";
+
+ //focusBarcode();
+
+
+ }
+
+});
+
+var bpartner = $FElement('bpartnerId');
+bpartner.initialValue = bpartner.value;
\ No newline at end of file
diff --git a/posterita/src/web/js/dom-drag.js b/posterita/src/web/js/dom-drag.js
new file mode 100644
index 0000000000..0cd39c3e76
--- /dev/null
+++ b/posterita/src/web/js/dom-drag.js
@@ -0,0 +1,121 @@
+/**************************************************
+ * dom-drag.js
+ * 09.25.2001
+ * www.youngpup.net
+ **************************************************
+ * 10.28.2001 - fixed minor bug where events
+ * sometimes fired off the handle, not the root.
+ **************************************************/
+
+var Drag = {
+
+ obj : null,
+
+ init : function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper)
+ {
+ o.onmousedown = Drag.start;
+
+ o.hmode = bSwapHorzRef ? false : true ;
+ o.vmode = bSwapVertRef ? false : true ;
+
+ o.root = oRoot && oRoot != null ? oRoot : o ;
+
+ if (o.hmode && isNaN(parseInt(o.root.style.left ))) o.root.style.left = "0px";
+ if (o.vmode && isNaN(parseInt(o.root.style.top ))) o.root.style.top = "0px";
+ if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right = "0px";
+ if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px";
+
+ o.minX = typeof minX != 'undefined' ? minX : null;
+ o.minY = typeof minY != 'undefined' ? minY : null;
+ o.maxX = typeof maxX != 'undefined' ? maxX : null;
+ o.maxY = typeof maxY != 'undefined' ? maxY : null;
+
+ o.xMapper = fXMapper ? fXMapper : null;
+ o.yMapper = fYMapper ? fYMapper : null;
+
+ o.root.onDragStart = new Function();
+ o.root.onDragEnd = new Function();
+ o.root.onDrag = new Function();
+ },
+
+ start : function(e)
+ {
+ var o = Drag.obj = this;
+ e = Drag.fixE(e);
+ var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom);
+ var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
+ o.root.onDragStart(x, y);
+
+ o.lastMouseX = e.clientX;
+ o.lastMouseY = e.clientY;
+
+ if (o.hmode) {
+ if (o.minX != null) o.minMouseX = e.clientX - x + o.minX;
+ if (o.maxX != null) o.maxMouseX = o.minMouseX + o.maxX - o.minX;
+ } else {
+ if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x;
+ if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x;
+ }
+
+ if (o.vmode) {
+ if (o.minY != null) o.minMouseY = e.clientY - y + o.minY;
+ if (o.maxY != null) o.maxMouseY = o.minMouseY + o.maxY - o.minY;
+ } else {
+ if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y;
+ if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y;
+ }
+
+ document.onmousemove = Drag.drag;
+ document.onmouseup = Drag.end;
+
+ return false;
+ },
+
+ drag : function(e)
+ {
+ e = Drag.fixE(e);
+ var o = Drag.obj;
+
+ var ey = e.clientY;
+ var ex = e.clientX;
+ var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom);
+ var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
+ var nx, ny;
+
+ if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);
+ if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);
+ if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);
+ if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);
+
+ nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
+ ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
+
+ if (o.xMapper) nx = o.xMapper(y)
+ else if (o.yMapper) ny = o.yMapper(x)
+
+ Drag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
+ Drag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
+ Drag.obj.lastMouseX = ex;
+ Drag.obj.lastMouseY = ey;
+
+ Drag.obj.root.onDrag(nx, ny);
+ return false;
+ },
+
+ end : function()
+ {
+ document.onmousemove = null;
+ document.onmouseup = null;
+ Drag.obj.root.onDragEnd( parseInt(Drag.obj.root.style[Drag.obj.hmode ? "left" : "right"]),
+ parseInt(Drag.obj.root.style[Drag.obj.vmode ? "top" : "bottom"]));
+ Drag.obj = null;
+ },
+
+ fixE : function(e)
+ {
+ if (typeof e == 'undefined') e = window.event;
+ if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
+ if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
+ return e;
+ }
+};
\ No newline at end of file
diff --git a/posterita/src/web/js/draftedPOSOrder.js b/posterita/src/web/js/draftedPOSOrder.js
new file mode 100644
index 0000000000..d9ceb801ef
--- /dev/null
+++ b/posterita/src/web/js/draftedPOSOrder.js
@@ -0,0 +1,174 @@
+
diff --git a/posterita/src/web/js/enableButton.js b/posterita/src/web/js/enableButton.js
new file mode 100644
index 0000000000..42ab5ee023
--- /dev/null
+++ b/posterita/src/web/js/enableButton.js
@@ -0,0 +1,405 @@
+/**
+ * Product: Posterita Web-Based POS and Adempiere Plugin
+ * Copyright (C) 2007 Posterita Ltd
+ * This file is part of POSterita
+ *
+ * POSterita is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ @author tamak
+ */
+
+
+window.status="";
+function refreshList(varDoc)
+{
+ window.location="NatisReleaseAction.do?action=prepareRelease&tradeInDetailMake=" + varDoc.tradeInDetailMake.value + "&assetID=" + varDoc.assetID.value + "&financeOptionId=" + varDoc.financeOptionId.value + "&licensingOptionId=" + varDoc.licensingOptionId.value + "&userSalesRepId=" + varDoc.userSalesRepId.value;
+}
+
+function cancelRelease()
+{
+ window.location="NatisReleaseAction.do?action=home";
+}
+
+function updateAttributeList(form,id)
+{
+ form.updateAttrValueId.value = id;
+ form.action.value = "updateProductAttributeValue";
+ form.submit();
+}
+
+function setActionMethod(form,action)
+{
+ toConsole("setting action dynamically");
+ toConsole("Form :" + form);
+ toConsole("action :" + action);
+
+ try
+ {
+ document.getElementsByName('action')[0].value = action;
+ form.submit();
+ }
+ catch(e)
+ {
+ toConsole("Unable to set Action " + e);
+ }
+
+}
+
+
+/*function setAction(form,action,method)
+{
+ form.action = action;
+ form.action.value = method;
+ //form.bpartnerId.value=100;
+ //alert("Calling action: " + action + "/n Method="+method);
+ form.submit();
+
+}
+*/
+function setAction(form,action,method)
+{
+ var elements = form.elements;
+
+ var getStr = action + "?action=" + method;
+
+ for(i=0;i 9) ? d : ('0' + d);
+ m = (m > 9) ? m : ('0' + m);
+
+ $('startDate').value = d + '/' + m + '/' + y;
+ $FElement('fromDate').value = $('startDate').value;
+ }
+
+ function initStartCalendar()
+ {
+ $FElement('startDay').onchange = updateStartDate;
+ $FElement('startMonth').onchange = updateStartDate;
+ $FElement('startYear').onchange = updateStartDate;
+
+ Calendar.setup(
+ { inputField : "startDate",
+ ifFormat : "%d/%m/%Y",
+ showTime : true,
+ button : "startDateBtn",
+ onUpdate : setStartDate
+ }
+ );
+
+ $('startDate').value = $FElement('fromDate').value;
+ }
\ No newline at end of file
diff --git a/posterita/src/web/js/keyboard.js b/posterita/src/web/js/keyboard.js
new file mode 100644
index 0000000000..9c1ca9910f
--- /dev/null
+++ b/posterita/src/web/js/keyboard.js
@@ -0,0 +1,422 @@
+/**
+ * Product: Posterita Web-Based POS and Adempiere Plugin
+ * Copyright (C) 2007 Posterita Ltd
+ * This file is part of POSterita
+ *
+ * POSterita is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ @author praveen
+ */
+
+
+function insertAtCursor(myField, myValue)
+{
+ //IE support
+ if (document.selection)
+ {
+ myField.focus();
+ sel = document.selection.createRange();
+ sel.text = myValue;
+ }
+ //MOZILLA/NETSCAPE support
+ else if (myField.selectionStart || myField.selectionStart == 0)
+ {
+ var startPos = myField.selectionStart;
+ var endPos = myField.selectionEnd;
+ myField.value = myField.value.substring(0, startPos) + myValue + myField.value.substring(endPos, myField.value.length);
+ }
+ else
+ {
+ myField.value += myValue;
+ }
+}
+
+function init()
+{
+ var textfield = this.output;
+ var keyboard = document.getElementById('keyboard');
+ var keys = keyboard.getElementsByTagName('td');
+
+ for(var i=0; i' +
+ '' +
+ '' +
+ '1 ' +
+ '2 ' +
+ '3 ' +
+ '4 ' +
+ '5 ' +
+ '6 ' +
+ '7 ' +
+ '8 ' +
+ '9 ' +
+ '0 ' +
+ 'HIDE ' +
+ ' ' +
+ '' +
+ 'Q ' +
+ 'W ' +
+ 'E ' +
+ 'R ' +
+ 'T ' +
+ 'Y ' +
+ 'U ' +
+ 'I ' +
+ 'O ' +
+ 'P ' +
+ 'BACK ' +
+ ' ' +
+ '' +
+ 'A ' +
+ 'S ' +
+ 'D ' +
+ 'F ' +
+ 'G ' +
+ 'H ' +
+ 'J ' +
+ 'K ' +
+ 'L ' +
+ 'ENTER ' +
+ ' ' +
+ '' +
+ 'CAPS ' +
+ 'Z ' +
+ 'X ' +
+ 'C ' +
+ 'V ' +
+ 'B ' +
+ 'N ' +
+ 'M ' +
+ ', ' +
+ '. ' +
+ ' ' +
+ '' +
+ 'SPACE ' +
+ ' ' +
+ '
' +
+ '' +
+ '' +
+ '
' +
+ '' +
+ '1 ' +
+ '2 ' +
+ '3 ' +
+ '4 ' +
+ '5 ' +
+ '6 ' +
+ '7 ' +
+ '8 ' +
+ '9 ' +
+ '0 ' +
+ 'HIDE ' +
+ ' ' +
+ '' +
+ 'q ' +
+ 'w ' +
+ 'e ' +
+ 'r ' +
+ 't ' +
+ 'y ' +
+ 'u ' +
+ 'i ' +
+ 'o ' +
+ 'p ' +
+ 'BACK ' +
+ ' ' +
+ '' +
+ 'a ' +
+ 's ' +
+ 'd ' +
+ 'f ' +
+ 'g ' +
+ 'h ' +
+ 'j ' +
+ 'k ' +
+ 'l ' +
+ 'ENTER ' +
+ ' ' +
+ '' +
+ 'CAPS ' +
+ 'z ' +
+ 'x ' +
+ 'c ' +
+ 'v ' +
+ 'b ' +
+ 'n ' +
+ 'm ' +
+ ', ' +
+ '. ' +
+ ' ' +
+ '' +
+ 'SPACE ' +
+ ' ' +
+ '
' +
+ '
' +
+ '
';
+
+}
+
+Keyboard.prototype.show = function(e){
+
+ if(this.hidden)
+ {
+ this.keypadpanel.style.visibility = 'hidden';
+ this.keypadpanel.style.display = 'block';
+ //this.keypadpanel.style.top = '10px';
+ //this.keypadpanel.style.left = '10px';
+ this.hidden = false;
+
+ try
+ {
+ //setting keyboard state in cookie
+ createCookie('pos_keyboard_on',"true",7);
+ }
+ catch(e)
+ {
+ toConsole(e);
+ }
+
+ }
+};
+
+Keyboard.prototype.hide = function(e){
+ this.keypadpanel.style.display = 'none';
+ this.hidden = true;
+
+ try
+ {toConsole
+ //setting keyboard state in cookie
+ createCookie('pos_keyboard_on',"false",7);
+ }
+ catch(e)
+ {
+ toConsole(e);
+ }
+};
+
+Keyboard.prototype.setCaps = function(e){
+
+ var uppercasekeyboard = document.getElementById('uppercase');
+ var lowercasekeyboard = document.getElementById('lowercase');
+
+ //toConsole(uppercasekeyboard.style.display);
+ //toConsole(lowercasekeyboard.style.display);
+
+ if(this.caps)
+ {
+ uppercasekeyboard.style.display = 'block';
+ lowercasekeyboard.style.display = 'none';
+ }
+ else
+ {
+ uppercasekeyboard.style.display = 'none';
+ lowercasekeyboard.style.display = 'block';
+ }
+
+
+};
+
+Keyboard.prototype.init = init;
+
+function showKeyboard()
+{
+ keyboard.show();
+ keyboard.init();
+ keyboard.setCaps();
+
+ kH = parseInt(keyboard.keypadpanel.scrollHeight);
+ kW = parseInt(keyboard.keypadpanel.scrollWidth);
+
+ sH = parseInt(getViewportHeight());
+ sW = parseInt(getViewportWidth());
+
+ cH = (sH - kH)/2;
+ cW = (sW - kW)/2;
+
+ //centering the keyboard
+ keyboard.keypadpanel.style.top = cH + "px";
+ keyboard.keypadpanel.style.left = cW + "px";
+ keyboard.keypadpanel.style.visibility = 'visible';
+
+}
+
+function bindKeyboard(output)
+{
+ keyboard.output = output;
+ keyboard.init();
+ keyboard.setCaps();
+}
+
+function enableVirtualKeyboard()
+{
+ keyboard = new Keyboard();
+
+ var keyboardicon = document.getElementById('keyboardicon');
+
+ keyboardicon.style.display = 'inline';
+ keyboardicon.onclick = function(e){
+ showKeyboard();
+ };
+
+ var textfields = document.getElementsByTagName('input');
+
+ for(var i=0; i");
+ d.write(" ");
+ d.write("");
+ d.write(printDiv.innerHTML);
+ d.write("");
+ d.close();
+}
+
+function enableDelete()
+{
+ var btn = document.getElementsByName("deleteBtn");
+
+ btn[0].disabled = enableButton();
+}
+
+function enableButton()
+{
+ /*
+ var chk = document.getElementsByName("posOrderLineIds");
+ for(i=0;i "+msg);
+
+ //console.appendChild(text);
+ //document.body.appendChild(console);
+ }
+ catch(e)
+ {
+ alert(e);
+ }
+
+}
+
+function $focus(element)
+{
+ var el = document.getElementsByName(element);
+
+ if(el)
+ {
+ el[0].focus();
+ toConsole('Setting focus to :' + element);
+ }
+ else
+ {
+ toConsole('Unable to set focus to :' + element);
+ }
+
+}
+
+function $FElement(element)
+{
+ var el = document.getElementsByName(element)[0];
+ return el;
+
+}
+
+function focusBarcode()
+{
+ $FElement('barCode').focus();
+ toConsole("Setting focus to barcode");
+}
+
+function openCashDrawer()
+{
+ var url = 'CompletePOSOrderAction.do';
+ var param = 'action=openCashDrawer';
+
+ var success = function(request){alert('Opening drawer!');};
+ var failure = function(request){alert('Oop some problem occured while communicating with server!');};
+
+ var myAjax = new Ajax.Request( url,
+ {
+ method: 'get',
+ parameters: param,
+ onSuccess: success,
+ onFailure: failure
+ });
+}
+
+function fullScreen(theURL)
+{
+ window.open(theURL, '', 'fullscreen=yes, scrollbars=auto');
+}
+
+function disableButtons()
+{
+ try
+ {
+ var elements = document.getElementsByTagName('input');
+
+ for(var i = 0; i < elements.length; i++)
+ {
+ if(elements[i].name != 'action')
+ {
+ elements[i].disabled = true;
+ elements[i].style.cursor = 'wait';
+ }
+ }
+
+ toConsole('Disable ' + elements.length + ' buttons');
+
+ return true;
+
+ }
+ catch(e)
+ {
+ toConsole(e);
+ }
+}
+
+
+function initDisableButtons()
+{
+ toConsole('initDisableButtons');
+
+ var elements = document.getElementsByTagName('input');
+
+ for(var i = 0; i < elements.length; i++)
+ {
+ var type = elements[i].type;
+
+ if(type == 'button')
+ {
+ var button = elements[i];
+ var accessKey = button.accessKey;
+
+ if(accessKey == 'c')
+ {
+ //button.onclick = disableButtons;
+ var action = button.onclick;
+ button.onclick = function(e){
+
+ };
+
+ toConsole('Found 1 button with accesskey: C');
+ }
+ }
+ }
+}
+
+function setNumericInputMask(e)
+{
+ var evtobj = window.event? event : e; //distinguish between IE's explicit event object (window.event) and Firefox's implicit.
+ var unicode = evtobj.charCode? evtobj.charCode : evtobj.keyCode;
+
+ var validKeys = [8,9,13,20,27,37,38,39,40,45,46,48,49,50,51,52,53,54,55,56,57];
+
+ for(var i = 0; i < validKeys.length; i++)
+ {
+ if(validKeys[i] == unicode)
+ {
+ return true;
+ }
+ }
+
+ alert('Accepts numeric values only');
+ return false;
+
+}
+
+function showDocumentPDF(id)
+{
+ window.open('ViewDocumentPDFAction.do?action=viewOrderDocumentPDF&documentId=' + id, '_blank', 'width=800, height=600, status=no, toolbar=no, location=no, resizable=yes');
+}
+
+function showInvoiceDocumentPDF(id)
+{
+ window.open('ViewDocumentPDFAction.do?action=viewInvoiceDocumentPDF&documentId=' + id, '_blank', 'width=800, height=600, status=no, toolbar=no, location=no, resizable=yes');
+}
+
+function showShipmentDocumentPDF(id)
+{
+ window.open('ViewDocumentPDFAction.do?action=viewShipmentDocumentPDF&documentId=' + id, '_blank', 'width=800, height=600, status=no, toolbar=no, location=no, resizable=yes');
+}
+
+function showPaymentDocumentPDF(id)
+{
+ window.open('ViewDocumentPDFAction.do?action=viewPaymentDocumentPDF&documentId=' + id, '_blank', 'width=800, height=600, status=no, toolbar=no, location=no, resizable=yes');
+}
+
+
+
+function enableVirtualKeyboard()
+{
+ if(!ENABLE_VIRTUAL_KEYBOARD)
+ {
+ return;
+ }
+
+ var textfields = document.getElementsByTagName('input');
+
+ for(var i=0; i= 0)
+ {
+ cashInput.value = newValue;
+ cardInput.previousAmount = newValue;
+
+ }
+ else
+ {
+ var max = cashInput.initialAmount - chequeInput.value;
+ alert('Card Amount should not exceed ' + max);
+
+ cardInput.value = 0.0;
+
+ newValue = new Number(cashInput.initialAmount - cardInput.value - chequeInput.value).toFixed(2);
+ cashInput.value = newValue;
+ }
+ }
+ catch(e)
+ {
+ toConsole(e);
+ }
+
+ }
+
+ chequeInput.onkeyup = function()
+ {
+ try
+ {
+ var newValue = new Number(cashInput.initialAmount - cardInput.value - chequeInput.value).toFixed(2);
+
+ if(newValue >= 0)
+ {
+ cashInput.value = newValue;
+ chequeInput.previousAmount = newValue;
+
+ }
+ else
+ {
+ var max = cashInput.initialAmount - cardInput.value;
+ alert('Cheque Amount should not exceed ' + max);
+
+ chequeInput.value = 0.0;
+
+ newValue = new Number(cashInput.initialAmount - cardInput.value - chequeInput.value).toFixed(2);
+ cashInput.value = newValue;
+ }
+ }
+ catch(e)
+ {
+ toConsole(e);
+ }
+ }
+
+ return false;
+}
+
+function showCheque()
+{
+ show("chequeNoLabel");show("chequeNoTxt");
+
+ hide("cashLabel");hide("cashTxt");
+ hide("cardLabel");hide("cardTxt");
+ hide("cardNoLabel");hide("cardNoTxt");
+ hide("chequeLabel");hide("chequeTxt");
+
+ setTrxType("Cheque");
+
+ var chequeTf = document.getElementsByName('chequeNo')[0];
+
+ chequeTf.focus();
+
+ return false;
+}
+
+function showCard()
+{
+ show("cardNoLabel");show("cardNoTxt");
+
+ hide("cashLabel");hide("cashTxt");
+ hide("cardLabel");hide("cardTxt");
+ hide("chequeLabel");hide("chequeTxt");
+ hide("chequeNoLabel");hide("chequeNoTxt");
+
+ setTrxType("Card");
+
+ var cardTf = document.getElementsByName('creditCardNumber')[0];
+
+ cardTf.focus();
+
+ return false;
+}
+
+function showCash()
+{
+ hide('cashLabel');hide('cashTxt');
+ hide('cardLabel');hide('cardTxt');
+ hide('cardNoLabel');hide('cardNoTxt');
+ hide('chequeLabel');hide('chequeTxt');
+ hide('chequeNoLabel');hide('chequeNoTxt');
+
+ setTrxType("Cash");
+
+ return false;
+}
+
+function setTrxType(type)
+{
+ document.forms[0].trxType.value = type;
+}
+
+
+
+//adding behaviour to discount&actual price textfields
+var price = document.getElementsByName('price');
+var discount = document.getElementsByName('discountPercent');
+var actualPrice = document.getElementsByName('actualPrice');
+
+/*
+var totalDiscount = $('totalDiscount');
+var totalPrice = $('totalPrice');
+
+totalPrice.initialAmount = 0;
+for(var i=0;i discountAllowed)
+ {
+ var errormsg = 'You are authorised to give ' + discountAllowed + '% discount only!';
+
+ this.className = 'text medium error';
+ this.title = errormsg;
+
+ totalDiscount.className = 'text medium error';
+ totalDiscount.title = errormsg;
+
+ //alert(errormsg);
+ }
+ else
+ {
+ var className = 'text medium';
+
+
+ this.className = className;
+ this.title = '';
+
+ totalDiscount.className = className;
+ totalDiscount.title = '';
+ }
+};
+
+totalDiscount.onkeyup = function()
+{
+ var newprice = ((100-this.value)*totalPrice.initialAmount)/100;
+ newprice = new Number(newprice).toFixed(2); //rounding calculate price
+ totalPrice.value = newprice;
+
+ if(this.value > discountAllowed)
+ {
+ var errormsg = 'You are authorised to give ' + discountAllowed + '% discount only!';
+
+ this.className = 'text medium error';
+ this.title = errormsg;
+
+ totalPrice.className = 'text medium error';
+ totalPrice.title = errormsg;
+
+ //alert(errormsg);
+ }
+ else
+ {
+ var className = 'text medium';
+
+ this.className = className;
+ this.title = '';
+
+ totalPrice.className = className;
+ totalPrice.title = '';
+ }
+};
+*/
+
+for(var i=0;i discountAllowed)
+ {
+ var errormsg = 'You are authorised to give ' + discountAllowed + '% discount only!';
+
+ this.className = 'text medium error';
+ this.title = errormsg;
+
+ actualPrice[index].className = 'text medium error';
+ actualPrice[index].title = errormsg;
+
+ //alert(errormsg);
+ }
+ else if(this.value < 0)
+ {
+ var errormsg = 'Discount cannot be negative!';
+
+ this.className = 'text medium error';
+ this.title = errormsg;
+
+ actualPrice[index].className = 'text medium error';
+ actualPrice[index].title = errormsg;
+
+ //alert(errormsg);
+ }
+ else
+ {
+ var className = 'text medium';
+
+ this.className = className;
+ this.title = '';
+
+ actualPrice[index].className = className;
+ actualPrice[index].title = '';
+ }
+
+ }
+
+ element = actualPrice[i];
+ element.position = i; //adding attribute position
+
+ element.onkeypress = setNumericInputMask;
+ element.onkeyup = function(e)
+ {
+ var index = this.position;
+
+ var calculateDiscount = 100-((this.value*100)/price[index].value);
+ calculateDiscount = new Number(calculateDiscount).toFixed(2); //rounding calculate discount
+
+ discount[index].value = calculateDiscount;
+ updateTotal();
+
+ if(calculateDiscount > discountAllowed)
+ {
+ var errormsg = 'You are authorised to give ' + discountAllowed + '% discount only!';
+
+ this.className = 'text medium error';
+ this.title = errormsg;
+
+ discount[index].className = 'text medium error';
+ discount[index].title = errormsg;
+
+ //alert(errormsg);
+ }
+ else if(calculateDiscount < 0)
+ {
+ var errormsg = 'Discount cannot be negative!';
+
+ this.className = 'text medium error';
+ this.title = errormsg;
+
+ discount[index].className = 'text medium error';
+ discount[index].title = errormsg;
+
+ //alert(errormsg);
+ }
+ else
+ {
+ var className = 'text medium';
+
+
+ this.className = className;
+ this.title = '';
+
+ discount[index].className = className;
+ discount[index].title = '';
+ }
+ }
+
+}
+
+function updateTotal()
+{
+ init();
+
+ var cardInput = document.getElementsByName('paymentByCard')[0].value="";
+ var chequeInput = document.getElementsByName('paymentByChq')[0].value="";
+}
+
+function init()
+{
+ var total = 0;
+
+ for(var i=0;i discountAllowed)
+ {
+ actualPrice[i].className = 'error medium';
+ actualPrice[i].title = errormsg;
+
+ discount[i].className = 'error medium';
+ discount[i].title = errormsg;
+ }
+ else if(discount[i].value < 0)
+ {
+ actualPrice[i].className = 'error medium';
+ actualPrice[i].title = errormsg2;
+
+ discount[i].className = 'error medium';
+ discount[i].title = errormsg2;
+ }
+ else
+ {
+ actualPrice[i].className = 'text medium';
+ actualPrice[i].title = "";
+
+ discount[i].className = 'text medium';
+ discount[i].title = "";
+ }
+ }
+
+ if(discountTotal.value > discountAllowed)
+ {
+ grandTotal.className = 'error medium';
+ grandTotal.title = errormsg;
+
+ discountTotal.className = 'error medium';
+ discountTotal.title = errormsg;
+ }
+ else if(discountTotal.value < 0)
+ {
+ grandTotal.className = 'error medium';
+ grandTotal.title = errormsg2;
+
+ discountTotal.className = 'error medium';
+ discountTotal.title = errormsg2;
+ }
+ else
+ {
+ grandTotal.className = 'text medium';
+ grandTotal.title = "";
+
+ discountTotal.className = 'text medium';
+ discountTotal.title = "";
+ }
+
+
+}
+
+
+var discountTotal = null;
+var grandTotal = null;
+var priceTotal = null;
+
+var discountAllowed = ;
+var errorCount = 0;
+
+
+
+
+
diff --git a/posterita/src/web/js/product.js b/posterita/src/web/js/product.js
new file mode 100644
index 0000000000..2aec86f906
--- /dev/null
+++ b/posterita/src/web/js/product.js
@@ -0,0 +1,338 @@
+/**
+ * Product: Posterita Web-Based POS and Adempiere Plugin
+ * Copyright (C) 2007 Posterita Ltd
+ * This file is part of POSterita
+ *
+ * POSterita is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ @author praveen
+ */
+
+
+//autocomplete for revenue recognition
+copyValueInto('revenueRecognition','revenue_recognition');
+new Ajax.Autocompleter('revenue_recognition','revenue_recognitionDiv','SearchPOSGarmentAttributes.do',{
+ paramName:'revenue_recognition',
+ frequency:TROTTLE_TIME,
+ afterUpdateElement:function(e1,e2){
+ $FF('revenueRecognition').value = e1.value;
+ }
+ });
+
+
+function $FF(elementName)
+{
+ return document.getElementsByName(elementName)[0];
+}
+
+function copyValueInto(e1,e2)
+{
+ toConsole("Copying " + e1 + " into " + e2);
+
+ try
+ {
+ $FF(e2).value = $FF(e1).value;
+ }
+ catch(e)
+ {
+ toConsole("Copying failed!");
+ toConsole(e);
+ }
+
+
+}
+
+function setValues(form)
+{
+ try
+ {
+ toConsole('Going to submit form');
+ copyValueInto('revenue_recognition','revenueRecognition');
+
+ form.submit();
+ }
+ catch(e)
+ {
+ toConsole("Submit failed!");
+ toConsole(e);
+
+ toConsole(form);
+ }
+}
+
+//------------------------------------------------------
+// Validation of barcode & product name using AJAX
+//------------------------------------------------------
+function validateBarcode()
+{
+ if($FF('barCode').initialValue == $FF('barCode').value) return;
+
+ var barcode = $FF('barCode').value;
+
+ var url = 'ValidatePOSProductAction.do';
+ var pars = 'action=validateProductBarcode&barCode=' + barcode;
+
+ var myAjax = new Ajax.Request( url,
+ {
+ method: 'get',
+ parameters: pars,
+ onSuccess: doesBarcodeExists,
+ onFailure: reportError
+ });
+}
+
+function validateProductName()
+{
+ if($FF('productName').initialValue == $FF('productName').value) return;
+
+ var productName = $FF('productName').value;
+ var url = 'ValidatePOSProductAction.do';
+ var pars = 'action=validateProductName&productName=' + productName;
+
+ var myAjax = new Ajax.Request( url,
+ {
+ method: 'get',
+ parameters: pars,
+ onSuccess: doesProductExists,
+ onFailure: reportError
+ });
+}
+
+function doesBarcodeExists(request)
+{
+ try
+ {
+ var response = request.responseText;
+
+ if(response == 'true')
+ {
+ Element.show('barcodeError');
+ $FF('barCode').className = 'error';
+ }
+ else
+ {
+ Element.hide('barcodeError');
+ $FF('barCode').className = 'text';
+ }
+
+ }
+ catch(e)
+ {
+ toConsole(e);
+ }
+}
+
+function doesProductExists(request)
+{
+ try
+ {
+ var response = request.responseText;
+
+ if(response == 'true')
+ {
+ Element.show('productError');
+ $FF('productName').className = 'error';
+ }
+ else
+ {
+ Element.hide('productError');
+ $FF('productName').className = 'text';
+ }
+
+ }
+ catch(e)
+ {
+ toConsole(e);
+ }
+}
+
+function reportError(request)
+{
+ alert('Some error occured while communicating with the server. Please try again.');
+}
+
+//------------------------------------------------------
+var barcode = $FF('barCode');
+var productName = $FF('productName');
+var taxCategory = $FF('taxCategoryId');
+
+var purchasePriceStandard = $FF('purchasePriceStandard');
+var salesPriceStandard = $FF('salesPriceStandard');
+var salesPriceList = $FF('salesPriceList');
+var salesPriceLimit = $FF('salesPriceLimit');
+
+var purchasePriceStandardTax = $FF('purchasePriceStandardTax');
+var salesPriceStandardTax = $FF('salesPriceStandardTax');
+var salesPriceListTax = $FF('salesPriceListTax');
+var salesPriceLimitTax = $FF('salesPriceLimitTax');
+
+/**
+ * Calculates price including tax
+ * @param price without tax
+ * @return price with tax
+ */
+function getPriceIncludingTax(price)
+{
+ var tax = getTax();
+ var priceWithoutTax = price || 0;
+ var priceWithTax = ((100+tax)/100)*priceWithoutTax;
+
+ priceWithTax = new Number(priceWithTax).toFixed(2);
+
+ return priceWithTax;
+
+}//getPriceIncludingTax(price)
+
+/**
+ * Calculates price excluding tax
+ * @param price with tax
+ * @return price without tax
+ */
+function getPriceExcludingTax(price)
+{
+ var tax = getTax();
+ var priceWithTax = price || 0;
+ var priceWithoutTax = (100*priceWithTax)/(100+tax);
+
+ priceWithoutTax = new Number(priceWithoutTax).toFixed(2);
+
+ return priceWithoutTax;
+
+}//getPriceExcludingTax(price)
+
+/**
+ * Gets the current tax
+ */
+function getTax()
+{
+ var select = taxCategory;
+ var index = select.selectedIndex;
+
+ if(index == -1) index = 0;
+
+ var option = select.options[select.selectedIndex];
+ var v = option.label || option.text;
+
+ var s = v.split('%');
+
+ if(s.length < 2) return 0;
+
+ var tax = parseFloat(s[1]);
+
+ return tax;
+
+}//getTax()
+
+function setPriceIncludingTax()
+{
+ purchasePriceStandardTax.value = getPriceIncludingTax(purchasePriceStandard.value);
+ salesPriceLimitTax.value = getPriceIncludingTax(salesPriceLimit.value);
+ salesPriceListTax.value = getPriceIncludingTax(salesPriceList.value);
+ salesPriceStandardTax.value = getPriceIncludingTax(salesPriceStandard.value);
+
+}//setPriceIncludingTax()
+
+//--------------------------------------------------------------------------
+// Initialising components & adding behaviour
+//--------------------------------------------------------------------------
+
+//update all prices
+taxCategory.onchange = setPriceIncludingTax;
+
+purchasePriceStandard.onkeyup = function(e){
+ purchasePriceStandardTax.value = getPriceIncludingTax(this.value);
+};
+purchasePriceStandardTax.onkeyup = function(e){
+ purchasePriceStandard.value = getPriceExcludingTax(this.value);
+};
+
+salesPriceLimit.onkeyup = function(e){
+ salesPriceLimitTax.value = getPriceIncludingTax(this.value);
+};
+salesPriceLimitTax.onkeyup = function(e){
+ salesPriceLimit.value = getPriceExcludingTax(this.value);
+};
+
+salesPriceStandard.onkeyup = function(e){
+ salesPriceStandardTax.value = getPriceIncludingTax(this.value);
+};
+salesPriceStandardTax.onkeyup = function(e){
+ salesPriceStandard.value = getPriceExcludingTax(this.value);
+};
+
+salesPriceList.onkeyup = function(e){
+ salesPriceListTax.value = getPriceIncludingTax(this.value);
+
+ //overwrite the other prices
+ salesPriceLimit.value = this.value;
+ salesPriceStandard.value = this.value;
+ salesPriceLimitTax.value = getPriceIncludingTax(this.value);
+ salesPriceStandardTax.value = getPriceIncludingTax(this.value);
+};
+
+salesPriceListTax.onkeyup = function(e){
+ salesPriceList.value = getPriceExcludingTax(this.value) ;
+};
+
+barcode.onblur = validateBarcode;
+barcode.initialValue = barcode.value;
+barcode.onkeyup = function(e)
+{
+ try
+ {
+ if(this.initialValue == this.value) return;
+
+ var event = e || window.event;
+
+ if(event.keyCode == 13)
+ {
+ toConsole('validating barcode');
+ validateBarcode();
+ }
+ }
+ catch (e)
+ {
+ toConsole(e);
+ }
+};
+
+productName.onblur = validateProductName;
+productName.initialValue = productName.value;
+productName.onkeyup = function(e)
+{
+ try
+ {
+ if(this.initialValue == this.value) return;
+
+ var event = e || window.event;
+
+ if(event.keyCode == 13)
+ {
+ toConsole('validating product name');
+ validateProductName();
+ }
+ }
+ catch (e)
+ {
+ toConsole(e);
+ }
+};
+
+$('barcodeError').style.display = 'none';
+$('productError').style.display = 'none';
+
+setPriceIncludingTax();
\ No newline at end of file
diff --git a/posterita/src/web/js/product2.js b/posterita/src/web/js/product2.js
new file mode 100644
index 0000000000..5eeff0b148
--- /dev/null
+++ b/posterita/src/web/js/product2.js
@@ -0,0 +1,147 @@
+/**
+ * Product: Posterita Web-Based POS and Adempiere Plugin
+ * Copyright (C) 2007 Posterita Ltd
+ * This file is part of POSterita
+ *
+ * POSterita is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ @author praveen
+ */
+
+
+//------------------------------------------------------
+var taxCategory = $FF('taxCategoryId');
+
+var purchasePriceStandard = $FF('purchasePriceStandard');
+var salesPriceStandard = $FF('salesPriceStandard');
+var salesPriceList = $FF('salesPriceList');
+var salesPriceLimit = $FF('salesPriceLimit');
+
+var purchasePriceStandardTax = $FF('purchasePriceStandardTax');
+var salesPriceStandardTax = $FF('salesPriceStandardTax');
+var salesPriceListTax = $FF('salesPriceListTax');
+var salesPriceLimitTax = $FF('salesPriceLimitTax');
+
+/**
+ * Calculates price including tax
+ * @param price without tax
+ * @return price with tax
+ */
+function getPriceIncludingTax(price)
+{
+ var tax = getTax();
+ var priceWithoutTax = price || 0;
+ var priceWithTax = ((100+tax)/100)*priceWithoutTax;
+
+ priceWithTax = new Number(priceWithTax).toFixed(2);
+
+ return priceWithTax;
+
+}//getPriceIncludingTax(price)
+
+/**
+ * Calculates price excluding tax
+ * @param price with tax
+ * @return price without tax
+ */
+function getPriceExcludingTax(price)
+{
+ var tax = getTax();
+ var priceWithTax = price || 0;
+ var priceWithoutTax = (100*priceWithTax)/(100+tax);
+
+ priceWithoutTax = new Number(priceWithoutTax).toFixed(2);
+
+ return priceWithoutTax;
+
+}//getPriceExcludingTax(price)
+
+/**
+ * Gets the current tax
+ */
+function getTax()
+{
+ var select = taxCategory;
+ var index = select.selectedIndex;
+
+ if(index == -1) index = 0;
+
+ var option = select.options[select.selectedIndex];
+ var v = option.label || option.text;
+
+ var s = v.split('%');
+
+ if(s.length < 2) return 0;
+
+ var tax = parseFloat(s[1]);
+
+ return tax;
+
+}//getTax()
+
+function setPriceIncludingTax()
+{
+ purchasePriceStandardTax.value = getPriceIncludingTax(purchasePriceStandard.value);
+ salesPriceLimitTax.value = getPriceIncludingTax(salesPriceLimit.value);
+ salesPriceListTax.value = getPriceIncludingTax(salesPriceList.value);
+ salesPriceStandardTax.value = getPriceIncludingTax(salesPriceStandard.value);
+
+}//setPriceIncludingTax()
+
+//--------------------------------------------------------------------------
+// Initialising components & adding behaviour
+//--------------------------------------------------------------------------
+
+//update all prices
+taxCategory.onchange = setPriceIncludingTax;
+
+purchasePriceStandard.onkeyup = function(e){
+ purchasePriceStandardTax.value = getPriceIncludingTax(this.value);
+};
+purchasePriceStandardTax.onkeyup = function(e){
+ purchasePriceStandard.value = getPriceExcludingTax(this.value);
+};
+
+salesPriceLimit.onkeyup = function(e){
+ salesPriceLimitTax.value = getPriceIncludingTax(this.value);
+};
+salesPriceLimitTax.onkeyup = function(e){
+ salesPriceLimit.value = getPriceExcludingTax(this.value);
+};
+
+salesPriceStandard.onkeyup = function(e){
+ salesPriceStandardTax.value = getPriceIncludingTax(this.value);
+};
+salesPriceStandardTax.onkeyup = function(e){
+ salesPriceStandard.value = getPriceExcludingTax(this.value);
+};
+
+salesPriceList.onkeyup = function(e){
+ salesPriceListTax.value = getPriceIncludingTax(this.value);
+
+ //overwrite the other prices
+ salesPriceLimit.value = this.value;
+ salesPriceStandard.value = this.value;
+ salesPriceLimitTax.value = getPriceIncludingTax(this.value);
+ salesPriceStandardTax.value = getPriceIncludingTax(this.value);
+};
+
+salesPriceListTax.onkeyup = function(e){
+ salesPriceList.value = getPriceExcludingTax(this.value) ;
+};
+
+setPriceIncludingTax();
\ No newline at end of file
diff --git a/posterita/src/web/js/shoppingCart.js b/posterita/src/web/js/shoppingCart.js
new file mode 100644
index 0000000000..30bfa964dd
--- /dev/null
+++ b/posterita/src/web/js/shoppingCart.js
@@ -0,0 +1,153 @@
+/**
+ * Product: Posterita Web-Based POS and Adempiere Plugin
+ * Copyright (C) 2007 Posterita Ltd
+ * This file is part of POSterita
+ *
+ * POSterita is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ @author praveen
+ */
+
+
+//----------------------------------FUNCTION DECLARATIONS---------------------------------------------
+//scrolls to the bottom of the cart
+function scrollDownCart()
+{
+ if($('items'))
+ {
+ $('items').scrollTop = $('items').scrollHeight;
+ }
+}
+
+//incrementing, decrementing and deleting the shopping cart items using ajax
+// 1.Increment the qty for the product
+function incrementCart(productId)
+{
+
+ try
+ {
+
+ var url = 'AddToPOSShoppingCartAction.do';
+ var pars = 'action=incrementQty&productId='+productId+ '&ifAdd=true'+ '&orderType=' + $FElement('orderType').value;
+
+ var myAjax = new Ajax.Request( url,
+ {
+ method: 'get',
+ parameters: pars,
+ onSuccess: refreshShoppingCart,
+ onFailure: reportShoppingCartError
+ });
+
+
+ }
+ catch(e)
+ {
+ toConsole(e);
+ }
+}
+
+//2.Decrement the qty for the product
+function decrementCart(productId)
+{
+
+ try
+ {
+
+
+ var url = 'AddToPOSShoppingCartAction.do';
+ var pars = 'action=decrementQty&productId='+productId+ '&ifAdd=false'+ '&orderType=' + $FElement('orderType').value;
+
+ var myAjax = new Ajax.Request( url,
+ {
+ method: 'get',
+ parameters: pars,
+ onSuccess: refreshShoppingCart,
+ onFailure: reportShoppingCartError
+ });
+
+ }
+ catch(e)
+ {
+ toConsole(e);
+ }
+}
+
+//3.Delete all the products with the ID from the cart
+function deleteItemFromCart(productId)
+{
+ try
+ {
+ var url = 'DeleteFromShoppingCartAction.do';
+ var pars = 'action=deleteFromPOSCart&productId='+productId+ '&orderType=' + $FElement('orderType').value;
+
+ var myAjax = new Ajax.Request( url,
+ {
+ method: 'get',
+ parameters: pars,
+ onSuccess: refreshShoppingCart,
+ onFailure: reportShoppingCartError
+ });
+ }
+ catch(e)
+ {
+ toConsole(e);
+ }
+}
+
+//Refreshs the content of the shopping cart
+function refreshShoppingCart(request)
+{
+ //var top = $('items').scrollTop;
+ $('shoppingCart').innerHTML = request.responseText;
+ //$('items').scrollTop = top;
+ //scrollDownCart();
+ requestIndicator.hide();
+}
+
+//Reports an error
+function reportShoppingCartError(request)
+{
+ alert('Some error occured while communicating with the server. Please try again.');
+ alert(request.responseText);
+ requestIndicator.hide();
+ var win = window.open();
+ win.document.write(request.responseText);
+ win.document.close();
+}
+
+addRequiredLibrary('js/test.js');
+var requestIndicator;
+
+/*
+var ShoppingCart = {
+
+ init : function(){
+ //this.indicator = new AJAXIndicator('Please wait...');
+ alert('Shopping Cart');
+ },
+
+ indicator : null
+};
+*/
+
+var init = function(){
+ requestIndicator = new AJAXIndicator('Please wait...');
+ };
+
+Event.observe(window,'load',init,false);
+//---------------------------------------------------------------------------------------
+//calling methods
\ No newline at end of file
diff --git a/posterita/src/web/js/timer.js b/posterita/src/web/js/timer.js
new file mode 100644
index 0000000000..477b1a0696
--- /dev/null
+++ b/posterita/src/web/js/timer.js
@@ -0,0 +1,36 @@
+var rTimer;
+var sds ;
+
+TimeTick();
+
+function TimeTick()
+{
+ var d = new Date();
+
+ var sds = new Date();
+
+ var hours = d.getHours();
+ var minutes = d.getMinutes();
+ var seconds = d.getSeconds();
+ var month = d.getMonth()+1;
+ var date= d.getDate();
+ var year = d.getFullYear();
+
+
+ hours = (hours < 10)? '0'+hours : hours;
+ minutes = (minutes < 10)? '0'+minutes : minutes;
+ seconds = (seconds < 10)? '0'+seconds : seconds;
+ date =(date <10) ? '0'+date : date;
+ month =(month<10) ? '0'+ month : month;
+
+
+ $('timer').innerHTML= date+"/"+ month +"/"+year+" "+hours+":"+minutes+":"+seconds;
+ if(rTimer)
+ {
+ clearTimeout(rTimer);
+ }
+
+ rTimer = setTimeout('TimeTick()', 1000);
+}
+
+
diff --git a/posterita/src/web/js/tooltip.js b/posterita/src/web/js/tooltip.js
new file mode 100644
index 0000000000..d707fcc866
--- /dev/null
+++ b/posterita/src/web/js/tooltip.js
@@ -0,0 +1,113 @@
+/**
+ * Product: Posterita Web-Based POS and Adempiere Plugin
+ * Copyright (C) 2007 Posterita Ltd
+ * This file is part of POSterita
+ *
+ * POSterita is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ @author praveen
+ */
+
+
+//some methods for tooltip
+function showTooltip(e)
+{
+ //Remove previous tooltips
+ if($('tooltipdiv'))
+ {
+ document.body.removeChild($('tooltipdiv'));
+ }
+
+ var tip = this.getAttribute('tooltip');
+ if(!tip)
+ {
+ return;
+ }
+
+ var tooltip = document.createElement('div');
+ var newAttr = document.createAttribute('id');
+ newAttr.nodeValue = "tooltipdiv"
+ tooltip.setAttributeNode(newAttr);
+
+ tooltip.onclick = function(){
+ document.body.removeChild(this);
+ };
+
+ var event = window.event || e;
+
+ var xcoor = Event.pointerX(event);
+ var ycoor = Event.pointerY(event);
+
+ //tooltip.style.left = 10 + xcoor + "px";
+ //tooltip.style.top = 10 + ycoor + "px";
+ tooltip.style.visibility = 'hidden';
+ tooltip.className = "tooltip";
+ tooltip.innerHTML = tip;
+ document.body.appendChild(tooltip);
+
+ var sWidth = getViewportWidth();
+ var sHeigth = getViewportHeight();
+
+ var ttLeft = 10 + xcoor;
+ var ttTop = 10 + ycoor;
+ var ttWidth = tooltip.scrollWidth;
+ var ttHeight = tooltip.scrollHeight;
+
+ if((ttLeft+ttWidth)>sWidth){
+ ttLeft = sWidth - (10 + ttWidth);
+ }
+
+ if((ttTop+ttHeight)>sHeigth){
+ ttTop = sHeigth - (10 + ttHeight);
+ }
+
+ tooltip.style.left = ttLeft + "px";
+ tooltip.style.top = ttTop + "px";
+ tooltip.style.visibility = 'visible';
+
+ //toConsole('Width:' + tooltip.scrollWidth);
+ //toConsole('Height:' + tooltip.scrollHeight);
+
+}
+
+var initTooltip = function()
+{
+ var help = document.getElementsByName('help');
+ for(var i=0; i