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 containing navigation buttons */ +} + +.calendar thead .daynames { /* Row containing the day names */ +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #000; + padding: 2px; + text-align: center; + background: #f4f0e8; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #f00; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + border-top: 2px solid #fff; + border-right: 2px solid #000; + border-bottom: 2px solid #000; + border-left: 2px solid #fff; + padding: 0px; + background-color: #e4e0d8; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + 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; + background-color: #c4c0b8; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #aaa; +} +.calendar tbody .day.othermonth.oweekend { + color: #faa; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #f4f0e8; +} + +.calendar tbody .rowhilite td { + background: #e4e0d8; +} + +.calendar tbody .rowhilite td.wn { + background: #d4d0c8; +} + +.calendar tbody td.hilite { /* Hovered cells */ + padding: 1px 3px 1px 1px; + border-top: 1px solid #fff; + border-right: 1px solid #000; + border-bottom: 1px solid #000; + border-left: 1px solid #fff; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + padding: 2px 2px 0px 2px; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; +} + +.calendar tbody td.selected { /* Cell showing selected date */ + font-weight: bold; + border-top: 1px solid #000; + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + border-left: 1px solid #000; + padding: 2px 2px 0px 2px; + background: #e4e0d8; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #f00; +} + +.calendar tbody td.today { /* Cell showing today date */ + font-weight: bold; + color: #00f; +} + +.calendar tbody .disabled { color: #999; } + +.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */ + visibility: hidden; +} + +.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */ + display: none; +} + +/* The footer part -- status bar and "Close" button */ + +.calendar tfoot .footrow { /* The 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 + ">"; + } 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 + ">"; + } 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 = '' + this.content + '
    '; + return $A(div.childNodes[0].childNodes[0].childNodes); + } +} + +var Insertion = new Object(); + +Insertion.Before = Class.create(); +Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { + initializeRange: function() { + this.range.setStartBefore(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, this.element); + }).bind(this)); + } +}); + +Insertion.Top = Class.create(); +Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(true); + }, + + insertContent: function(fragments) { + fragments.reverse(false).each((function(fragment) { + this.element.insertBefore(fragment, this.element.firstChild); + }).bind(this)); + } +}); + +Insertion.Bottom = Class.create(); +Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.appendChild(fragment); + }).bind(this)); + } +}); + +Insertion.After = Class.create(); +Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { + initializeRange: function() { + this.range.setStartAfter(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, + this.element.nextSibling); + }).bind(this)); + } +}); + +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator) { + this.element.className.split(/\s+/).select(function(name) { + return name.length > 0; + })._each(iterator); + }, + + set: function(className) { + this.element.className = className; + }, + + add: function(classNameToAdd) { + if (this.include(classNameToAdd)) return; + this.set(this.toArray().concat(classNameToAdd).join(' ')); + }, + + remove: function(classNameToRemove) { + if (!this.include(classNameToRemove)) return; + this.set(this.select(function(className) { + return className != classNameToRemove; + }).join(' ')); + }, + + toString: function() { + return this.toArray().join(' '); + } +} + +Object.extend(Element.ClassNames.prototype, Enumerable); +var Selector = Class.create(); +Selector.prototype = { + initialize: function(expression) { + this.params = {classNames: []}; + this.expression = expression.toString().strip(); + this.parseExpression(); + this.compileMatcher(); + }, + + parseExpression: function() { + function abort(message) { throw 'Parse error in selector: ' + message; } + + if (this.expression == '') abort('empty expression'); + + var params = this.params, expr = this.expression, match, modifier, clause, rest; + while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) { + params.attributes = params.attributes || []; + params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''}); + expr = match[1]; + } + + if (expr == '*') return this.params.wildcard = true; + + while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) { + modifier = match[1], clause = match[2], rest = match[3]; + switch (modifier) { + case '#': params.id = clause; break; + case '.': params.classNames.push(clause); break; + case '': + case undefined: params.tagName = clause.toUpperCase(); break; + default: abort(expr.inspect()); + } + expr = rest; + } + + if (expr.length > 0) abort(expr.inspect()); + }, + + buildMatchExpression: function() { + var params = this.params, conditions = [], clause; + + if (params.wildcard) + conditions.push('true'); + if (clause = params.id) + conditions.push('element.id == ' + clause.inspect()); + if (clause = params.tagName) + conditions.push('element.tagName.toUpperCase() == ' + clause.inspect()); + if ((clause = params.classNames).length > 0) + for (var i = 0; i < clause.length; i++) + conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')'); + if (clause = params.attributes) { + clause.each(function(attribute) { + var value = 'element.getAttribute(' + attribute.name.inspect() + ')'; + var splitValueBy = function(delimiter) { + return value + ' && ' + value + '.split(' + delimiter.inspect() + ')'; + } + + switch (attribute.operator) { + case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break; + case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break; + case '|=': conditions.push( + splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect() + ); break; + case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break; + case '': + case undefined: conditions.push(value + ' != null'); break; + default: throw 'Unknown operator ' + attribute.operator + ' in selector'; + } + }); + } + + return conditions.join(' && '); + }, + + compileMatcher: function() { + this.match = new Function('element', 'if (!element.tagName) return false; \ + return ' + this.buildMatchExpression()); + }, + + findElements: function(scope) { + var element; + + if (element = $(this.params.id)) + if (this.match(element)) + if (!scope || Element.childOf(element, scope)) + return [element]; + + scope = (scope || document).getElementsByTagName(this.params.tagName || '*'); + + var results = []; + for (var i = 0; i < scope.length; i++) + if (this.match(element = scope[i])) + results.push(Element.extend(element)); + + return results; + }, + + toString: function() { + return this.expression; + } +} + +function $$() { + return $A(arguments).map(function(expression) { + return expression.strip().split(/\s+/).inject([null], function(results, expr) { + var selector = new Selector(expr); + return results.map(selector.findElements.bind(selector)).flatten(); + }); + }).flatten(); +} +var Field = { + clear: function() { + for (var i = 0; i < arguments.length; i++) + $(arguments[i]).value = ''; + }, + + focus: function(element) { + $(element).focus(); + }, + + present: function() { + for (var i = 0; i < arguments.length; i++) + if ($(arguments[i]).value == '') return false; + return true; + }, + + select: function(element) { + $(element).select(); + }, + + activate: function(element) { + element = $(element); + element.focus(); + if (element.select) + element.select(); + } +} + +/*--------------------------------------------------------------------------*/ + +var Form = { + serialize: function(form) { + var elements = Form.getElements($(form)); + var queryComponents = new Array(); + + for (var i = 0; i < elements.length; i++) { + var queryComponent = Form.Element.serialize(elements[i]); + if (queryComponent) + queryComponents.push(queryComponent); + } + + return queryComponents.join('&'); + }, + + getElements: function(form) { + form = $(form); + var elements = new Array(); + + for (var tagName in Form.Element.Serializers) { + var tagElements = form.getElementsByTagName(tagName); + for (var j = 0; j < tagElements.length; j++) + elements.push(tagElements[j]); + } + return elements; + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) + return inputs; + + var matchingInputs = new Array(); + for (var i = 0; i < inputs.length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || + (name && input.name != name)) + continue; + matchingInputs.push(input); + } + + return matchingInputs; + }, + + disable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.blur(); + element.disabled = 'true'; + } + }, + + enable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.disabled = ''; + } + }, + + findFirstElement: function(form) { + return Form.getElements(form).find(function(element) { + return element.type != 'hidden' && !element.disabled && + ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); + }); + }, + + focusFirstElement: function(form) { + Field.activate(Form.findFirstElement(form)); + }, + + reset: function(form) { + $(form).reset(); + } +} + +Form.Element = { + serialize: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) { + var key = encodeURIComponent(parameter[0]); + if (key.length == 0) return; + + if (parameter[1].constructor != Array) + parameter[1] = [parameter[1]]; + + return parameter[1].map(function(value) { + return key + '=' + encodeURIComponent(value); + }).join('&'); + } + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) + return parameter[1]; + } +} + +Form.Element.Serializers = { + input: function(element) { + switch (element.type.toLowerCase()) { + case 'submit': + case 'hidden': + case 'password': + case 'text': + return Form.Element.Serializers.textarea(element); + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element); + } + return false; + }, + + inputSelector: function(element) { + if (element.checked) + return [element.name, element.value]; + }, + + textarea: function(element) { + return [element.name, element.value]; + }, + + select: function(element) { + return Form.Element.Serializers[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + }, + + selectOne: function(element) { + var value = '', opt, index = element.selectedIndex; + if (index >= 0) { + opt = element.options[index]; + value = opt.value || opt.text; + } + return [element.name, value]; + }, + + selectMany: function(element) { + var value = []; + for (var i = 0; i < element.length; i++) { + var opt = element.options[i]; + if (opt.selected) + value.push(opt.value || opt.text); + } + return [element.name, value]; + } +} + +/*--------------------------------------------------------------------------*/ + +var $F = Form.Element.getValue; + +/*--------------------------------------------------------------------------*/ + +Abstract.TimedObserver = function() {} +Abstract.TimedObserver.prototype = { + initialize: function(element, frequency, callback) { + this.frequency = frequency; + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + } +} + +Form.Element.Observer = Class.create(); +Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(); +Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = function() {} +Abstract.EventObserver.prototype = { + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + var elements = Form.getElements(this.element); + for (var i = 0; i < elements.length; i++) + this.registerCallback(elements[i]); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + case 'password': + case 'text': + case 'textarea': + case 'select-one': + case 'select-multiple': + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +} + +Form.Element.EventObserver = Class.create(); +Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(); +Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); +if (!window.Event) { + var Event = new Object(); +} + +Object.extend(Event, { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + + element: function(event) { + return event.target || event.srcElement; + }, + + isLeftClick: function(event) { + return (((event.which) && (event.which == 1)) || + ((event.button) && (event.button == 1))); + }, + + pointerX: function(event) { + return event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)); + }, + + pointerY: function(event) { + return event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)); + }, + + stop: function(event) { + if (event.preventDefault) { + event.preventDefault(); + event.stopPropagation(); + } else { + event.returnValue = false; + event.cancelBubble = true; + } + }, + + // find the first node with the given tagName, starting from the + // node the event was triggered on; traverses the DOM upwards + findElement: function(event, tagName) { + var element = Event.element(event); + while (element.parentNode && (!element.tagName || + (element.tagName.toUpperCase() != tagName.toUpperCase()))) + element = element.parentNode; + return element; + }, + + observers: false, + + _observeAndCache: function(element, name, observer, useCapture) { + if (!this.observers) this.observers = []; + if (element.addEventListener) { + this.observers.push([element, name, observer, useCapture]); + element.addEventListener(name, observer, useCapture); + } else if (element.attachEvent) { + this.observers.push([element, name, observer, useCapture]); + element.attachEvent('on' + name, observer); + } + }, + + unloadCache: function() { + if (!Event.observers) return; + for (var i = 0; i < Event.observers.length; i++) { + Event.stopObserving.apply(this, Event.observers[i]); + Event.observers[i][0] = null; + } + Event.observers = false; + }, + + observe: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) + || element.attachEvent)) + name = 'keydown'; + + this._observeAndCache(element, name, observer, useCapture); + }, + + stopObserving: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) + || element.detachEvent)) + name = 'keydown'; + + if (element.removeEventListener) { + element.removeEventListener(name, observer, useCapture); + } else if (element.detachEvent) { + element.detachEvent('on' + name, observer); + } + } +}); + +/* prevent memory leaks in IE */ +if (navigator.appVersion.match(/\bMSIE\b/)) + Event.observe(window, 'unload', Event.unloadCache, false); +var Position = { + // set to true if needed, warning: firefox performance problems + // NOT neeeded for page scrolling, only if draggable contained in + // scrollable elements + includeScrollOffsets: false, + + // must be called before calling withinIncludingScrolloffset, every time the + // page is scrolled + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + realOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return [valueL, valueT]; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return [valueL, valueT]; + }, + + positionedOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + p = Element.getStyle(element, 'position'); + if (p == 'relative' || p == 'absolute') break; + } + } while (element); + return [valueL, valueT]; + }, + + offsetParent: function(element) { + if (element.offsetParent) return element.offsetParent; + if (element == document.body) return element; + + while ((element = element.parentNode) && element != document.body) + if (Element.getStyle(element, 'position') != 'static') + return element; + + return document.body; + }, + + // caches x/y coordinate pair to use with overlap + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = this.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = this.realOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = this.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + // within must be called directly before + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + clone: function(source, target) { + source = $(source); + target = $(target); + target.style.position = 'absolute'; + var offsets = this.cumulativeOffset(source); + target.style.top = offsets[1] + 'px'; + target.style.left = offsets[0] + 'px'; + target.style.width = source.offsetWidth + 'px'; + target.style.height = source.offsetHeight + 'px'; + }, + + page: function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + // Safari fix + if (element.offsetParent==document.body) + if (Element.getStyle(element,'position')=='absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } while (element = element.parentNode); + + return [valueL, valueT]; + }, + + clone: function(source, target) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || {}) + + // find page position of source + source = $(source); + var p = Position.page(source); + + // find coordinate system to use + target = $(target); + var delta = [0, 0]; + var parent = null; + // delta [0,0] will do fine with position: fixed elements, + // position:absolute needs offsetParent deltas + if (Element.getStyle(target,'position') == 'absolute') { + parent = Position.offsetParent(target); + delta = Position.page(parent); + } + + // correct by body offsets (fixes Safari) + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + // set position + if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if(options.setWidth) target.style.width = source.offsetWidth + 'px'; + if(options.setHeight) target.style.height = source.offsetHeight + 'px'; + }, + + absolutize: function(element) { + element = $(element); + if (element.style.position == 'absolute') return; + Position.prepare(); + + var offsets = Position.positionedOffset(element); + var top = offsets[1]; + var left = offsets[0]; + var width = element.clientWidth; + var height = element.clientHeight; + + element._originalLeft = left - parseFloat(element.style.left || 0); + element._originalTop = top - parseFloat(element.style.top || 0); + element._originalWidth = element.style.width; + element._originalHeight = element.style.height; + + element.style.position = 'absolute'; + element.style.top = top + 'px';; + element.style.left = left + 'px';; + element.style.width = width + 'px';; + element.style.height = height + 'px';; + }, + + relativize: function(element) { + element = $(element); + if (element.style.position == 'relative') return; + Position.prepare(); + + element.style.position = 'relative'; + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.height = element._originalHeight; + element.style.width = element._originalWidth; + } +} + +// Safari returns margins on body which is incorrect if the child is absolutely +// positioned. For performance reasons, redefine Position.cumulativeOffset for +// KHTML/WebKit only. +if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { + Position.cumulativeOffset = function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) + if (Element.getStyle(element, 'position') == 'absolute') break; + + element = element.offsetParent; + } while (element); + + return [valueL, valueT]; + } +} + 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' + + '' + + '' + + '' + + '
    StatusTestMessage
    '; + 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' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + ' ' + + '' + + '' + + '' + + '' + + '' + + '
    1234567890HIDE
    QWERTYUIOPBACK
    ASDFGHJKLENTER
    CAPSZXCVBNM,.
    SPACE
    ' + + '' + + '
    ' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + ' ' + + '' + + '' + + '' + + '' + + '' + + '
    1234567890HIDE
    qwertyuiopBACK
    asdfghjklENTER
    CAPSzxcvbnm,.
    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