From c8c4c5176dbef270ffaa9099909c497f2c749609 Mon Sep 17 00:00:00 2001 From: Deon George Date: Fri, 22 Jul 2011 11:04:20 +1000 Subject: [PATCH] Fixes for live environment and updates to admin/service/update --- application/media/css/dhtml.calendar.css | 232 +++ application/media/css/dhtml.menuarrow.gif | Bin 0 -> 68 bytes application/media/img/calendar.png | Bin 0 -> 478 bytes application/media/js/dhtml.calendar-en.js | 9 + application/media/js/dhtml.calendar-setup.js | 200 ++ application/media/js/dhtml.calendar.js | 1806 +++++++++++++++++ application/media/js/dhtml.date_selector.js | 76 + modules/account/classes/model/account.php | 2 +- modules/adsl/classes/model/adsl/supplier.php | 5 +- .../adsl/classes/model/adsl/supplier/plan.php | 2 +- modules/core/database_join_fields.inc.php | 4 +- modules/email_template/email_template.inc.php | 2 +- .../classes/controller/admin/export.php | 5 +- modules/export/classes/export.php | 2 +- modules/export/classes/export/quicken.php | 4 +- .../PDF/pdf_invoice_itemised-tcpdf.inc.php | 5 +- modules/invoice/invoice.inc.php | 3 +- modules/payment/classes/model/payment.php | 12 +- modules/product/classes/model/product.php | 9 + modules/product/views/product/view.php | 8 +- .../classes/controller/admin/service.php | 50 +- .../classes/controller/user/service.php | 4 +- modules/service/classes/model/service.php | 2 +- .../service/classes/model/service/adsl.php | 22 +- .../views/service/admin/adsl/update.php | 56 +- .../{ => admin}/list/adslbilling_body.php | 0 .../{ => admin}/list/adslbilling_foot.php | 0 .../{ => admin}/list/adslbilling_head.php | 0 .../{ => admin}/list/adslbilling_summary.php | 0 .../list/adslbilling_summary_exception.php | 0 .../{ => admin}/list/adslservices_body.php | 2 +- .../{ => admin}/list/adslservices_header.php | 0 .../{ => admin}/list/bycheckout_body.php | 0 .../{ => admin}/list/bycheckout_header.php | 0 .../{ => admin}/list/bycheckout_subtotal.php | 0 .../service/views/service/admin/update.php | 10 +- .../adsl/table_traffic.php} | 0 .../views/service/{ => user}/adsl/view.php | 3 +- .../service/views/service/{ => user}/list.php | 2 +- .../service/views/service/{ => user}/view.php | 0 .../static_page/classes/model/staticpage.php | 2 +- 41 files changed, 2457 insertions(+), 82 deletions(-) create mode 100644 application/media/css/dhtml.calendar.css create mode 100644 application/media/css/dhtml.menuarrow.gif create mode 100644 application/media/img/calendar.png create mode 100644 application/media/js/dhtml.calendar-en.js create mode 100644 application/media/js/dhtml.calendar-setup.js create mode 100644 application/media/js/dhtml.calendar.js create mode 100644 application/media/js/dhtml.date_selector.js rename modules/service/views/service/{ => admin}/list/adslbilling_body.php (100%) rename modules/service/views/service/{ => admin}/list/adslbilling_foot.php (100%) rename modules/service/views/service/{ => admin}/list/adslbilling_head.php (100%) rename modules/service/views/service/{ => admin}/list/adslbilling_summary.php (100%) rename modules/service/views/service/{ => admin}/list/adslbilling_summary_exception.php (100%) rename modules/service/views/service/{ => admin}/list/adslservices_body.php (83%) rename modules/service/views/service/{ => admin}/list/adslservices_header.php (100%) rename modules/service/views/service/{ => admin}/list/bycheckout_body.php (100%) rename modules/service/views/service/{ => admin}/list/bycheckout_header.php (100%) rename modules/service/views/service/{ => admin}/list/bycheckout_subtotal.php (100%) rename modules/service/views/service/{view_detail_adsl_traffic.php => user/adsl/table_traffic.php} (100%) rename modules/service/views/service/{ => user}/adsl/view.php (91%) rename modules/service/views/service/{ => user}/list.php (92%) rename modules/service/views/service/{ => user}/view.php (100%) diff --git a/application/media/css/dhtml.calendar.css b/application/media/css/dhtml.calendar.css new file mode 100644 index 00000000..bbab528c --- /dev/null +++ b/application/media/css/dhtml.calendar.css @@ -0,0 +1,232 @@ +/* The main calendar widget. DIV containing a table. */ + +div.calendar { position: relative; } + +.calendar, .calendar table { + border: 1px solid #556; + font-size: 11px; + color: #000; + cursor: default; + background: #eef; + font-family: tahoma,verdana,sans-serif; +} + +/* Header part -- contains navigation buttons and day names. */ + +.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */ + text-align: center; /* They are the navigation buttons */ + padding: 2px; /* Make the buttons seem like they're pressing */ +} + +.calendar .nav { + background: #778 url(dhtml.menuarrow.gif) no-repeat 100% 100%; +} + +.calendar thead .title { /* This holds the current "month, year" */ + font-weight: bold; /* Pressing it will take you to the current date */ + text-align: center; + background: #fff; + color: #000; + padding: 2px; +} + +.calendar thead .headrow { /* Row containing navigation buttons */ + background: #778; + color: #fff; +} + +.calendar thead .daynames { /* Row containing the day names */ + background: #bdf; +} + +.calendar thead .name { /* Cells containing the day names */ + border-bottom: 1px solid #556; + padding: 2px; + text-align: center; + color: #000; +} + +.calendar thead .weekend { /* How a weekend day name shows in header */ + color: #a66; +} + +.calendar thead .hilite { /* How do the buttons in header appear when hover */ + background-color: #aaf; + color: #000; + border: 1px solid #04f; + padding: 1px; +} + +.calendar thead .active { /* Active (pressed) buttons in header */ + background-color: #77c; + padding: 2px 0px 0px 2px; +} + +/* The body part -- contains all the days in month. */ + +.calendar tbody .day { /* Cells containing month days dates */ + width: 2em; + color: #456; + text-align: right; + padding: 2px 4px 2px 2px; +} +.calendar tbody .day.othermonth { + font-size: 80%; + color: #bbb; +} +.calendar tbody .day.othermonth.oweekend { + color: #fbb; +} + +.calendar table .wn { + padding: 2px 3px 2px 2px; + border-right: 1px solid #000; + background: #bdf; +} + +.calendar tbody .rowhilite td { + background: #def; +} + +.calendar tbody .rowhilite td.wn { + background: #eef; +} + +.calendar tbody td.hilite { /* Hovered cells */ + background: #def; + padding: 1px 3px 1px 1px; + border: 1px solid #bbb; +} + +.calendar tbody td.active { /* Active (pressed) cells */ + background: #cde; + padding: 2px 2px 0px 2px; +} + +.calendar tbody td.selected { /* Cell showing today date */ + font-weight: bold; + border: 1px solid #000; + padding: 1px 3px 1px 1px; + background: #fff; + color: #000; +} + +.calendar tbody td.weekend { /* Cells showing weekend days */ + color: #a66; +} + +.calendar tbody td.today { /* Cell showing selected 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) */ + text-align: center; + background: #556; + color: #fff; +} + +.calendar tfoot .ttip { /* Tooltip (status bar) cell */ + background: #fff; + color: #445; + border-top: 1px solid #556; + padding: 1px; +} + +.calendar tfoot .hilite { /* Hover style for buttons in footer */ + background: #aaf; + border: 1px solid #04f; + color: #000; + padding: 1px; +} + +.calendar tfoot .active { /* Active (pressed) style for buttons in footer */ + background: #77c; + padding: 2px 0px 0px 2px; +} + +/* Combo boxes (menus that display months/years for direct selection) */ + +.calendar .combo { + position: absolute; + display: none; + top: 0px; + left: 0px; + width: 4em; + cursor: default; + border: 1px solid #655; + background: #def; + color: #000; + font-size: 90%; + z-index: 100; +} + +.calendar .combo .label, +.calendar .combo .label-IEfix { + text-align: center; + padding: 1px; +} + +.calendar .combo .label-IEfix { + width: 4em; +} + +.calendar .combo .hilite { + background: #acf; +} + +.calendar .combo .active { + border-top: 1px solid #46a; + border-bottom: 1px solid #46a; + background: #eef; + font-weight: bold; +} + +.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: #667; + color: #fff; +} + +.calendar td.time span.active { + border-color: #f00; + background-color: #000; + color: #0f0; +} diff --git a/application/media/css/dhtml.menuarrow.gif b/application/media/css/dhtml.menuarrow.gif new file mode 100644 index 0000000000000000000000000000000000000000..ed2dee0e6368d201bf5d32833884c7c1da6c7424 GIT binary patch literal 68 zcmZ?wbhEHbWM^Py_{hZ2(9rPi-LwDy|1&T!DE?$&WME)o&;ha;K=KSsqAVN&i(DE6 VRJ~d>0vEY-3aNTcS;53$4FHxP5xxKb literal 0 HcmV?d00001 diff --git a/application/media/img/calendar.png b/application/media/img/calendar.png new file mode 100644 index 0000000000000000000000000000000000000000..c3cdf73ce89740cbc988bcf23757347bdef43a08 GIT binary patch literal 478 zcmV<40U`d0P)xp#diF-0gO0wOY#8YPCu(m;08N&*uTGut5+6l*{Fh zwn89;KuU>W7~xpgbw;BRj^m)~dg%B2eO%WKYlIM_(`f)}1&^j_;l$-;pS?|W3xA>5 z;-%N?0T2WMQpztQ+7{CgQKeE*aQMEDWmyW2!C*itl~U^3?KYn0DRqfNVinQywlz(Q z#1ZAKAd*sscsAj%E7u&|-l$%-zys&eh-$T(l7#VijN>>8j`+nH?&XQHNT+m9{OV4r zvu%47(Yhq8!?yNB?Sjbn{m8`F*#eYQL{HfQkBvq&qI$iqBw;ull1wHQY@JRA&+`-< z&1REKCZo(XO>;A%r~|ZX+7v|F`0k?^Fau73<2kaQ(hD$KTsX!7e%mwP_3z)w@3TO; UdX3#eDgXcg07*qoM6N<$f@_Q1TL1t6 literal 0 HcmV?d00001 diff --git a/application/media/js/dhtml.calendar-en.js b/application/media/js/dhtml.calendar-en.js new file mode 100644 index 00000000..1736202e --- /dev/null +++ b/application/media/js/dhtml.calendar-en.js @@ -0,0 +1,9 @@ +/* + * Calendar EN language + * Author: Mihai Bazon, + */ +Calendar._DN=new Array +("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday");Calendar._SDN=new Array +("Sun","Mon","Tue","Wed","Thu","Fri","Sat","Sun");Calendar._FD=0;Calendar._MN=new Array +("January","February","March","April","May","June","July","August","September","October","November","December");Calendar._SMN=new Array +("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");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"+"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)";Calendar._TT["DAY_FIRST"]="Display %s first";Calendar._TT["WEEKEND"]="0,6";Calendar._TT["CLOSE"]="Close";Calendar._TT["TODAY"]="Today";Calendar._TT["TIME_PART"]="(Shift-)Click or drag to change value";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/application/media/js/dhtml.calendar-setup.js b/application/media/js/dhtml.calendar-setup.js new file mode 100644 index 00000000..c269f7dd --- /dev/null +++ b/application/media/js/dhtml.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$ + +/** + * 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/application/media/js/dhtml.calendar.js b/application/media/js/dhtml.calendar.js new file mode 100644 index 00000000..f6c13981 --- /dev/null +++ b/application/media/js/dhtml.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$ + +/** 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/application/media/js/dhtml.date_selector.js b/application/media/js/dhtml.date_selector.js new file mode 100644 index 00000000..3050c624 --- /dev/null +++ b/application/media/js/dhtml.date_selector.js @@ -0,0 +1,76 @@ +var defaults = new Array(); +function dateSelector(id) { + var el = document.getElementById(id); + var format = gettype(el.id); + var epoch; + var parse = false; + + var cal = new Calendar(0, null, onSelect, onClose); + + if (defaults['f_time_'+id]) { + cal.showsTime = true; + } else { + cal.showsTime = false; + } + + cal.weekNumbers = true; + cal.showsOtherMonths = true; + cal.create(); + + // convert to milliseconds (Epoch is usually expressed in seconds, but Javascript uses Milliseconds) + switch (format) { + case '%es' : epoch = el.value * 86400 * 1000; + format = '%s'; + parse = true; + break; + case '%s' : epoch = el.value * 1000; + parse = true; + break; + } + + // Convert the value to the date so that the calendar will display it + if (parse) { + var dDate = new Date(); + dDate.setTime(epoch); + cal.setDateFormat('%a, %d %b %Y'); // set the specified date format + cal.parseDate(dDate.toString()); // try to parse the text in field + cal.setDateFormat(format); // set the specified date format + } else { + cal.setDateFormat(format); // set the specified date format + cal.parseDate(el.value); // try to parse the text in field + } + + cal.sel = el; // inform it what input field we use + cal.showAtElement(el, 'BR'); // show the calendar +} + +function onSelect(calendar,date) { + switch (gettype(calendar.sel.id)) { + case '%es' : date = Math.round(date / 86400); + break; + } + + calendar.sel.value = date; + if (calendar.dateClicked) + onClose(calendar); +} + +function onClose(calendar,date) { + calendar.hide(); +} + +function gettype(attr) { + if (typeof defaults == "undefined") { + return '%s'; + } + + if (typeof defaults[attr] == "undefined") { + if (typeof default_date_format == "undefined") { + return '%s'; + } else { + return default_date_format; + } + } else { + return defaults[attr]; + } +} diff --git a/modules/account/classes/model/account.php b/modules/account/classes/model/account.php index c42650c1..69463b18 100644 --- a/modules/account/classes/model/account.php +++ b/modules/account/classes/model/account.php @@ -15,7 +15,7 @@ class Model_Account extends Model_Auth_UserDefault { 'group' => array('through' => 'account_group'), 'invoice' => array('far_key'=>'id'), 'payment'=>array(), - 'service' => array(), + 'service' => array('far_key'=>'id'), ); /** diff --git a/modules/adsl/classes/model/adsl/supplier.php b/modules/adsl/classes/model/adsl/supplier.php index a369b1ae..d1eb51cd 100644 --- a/modules/adsl/classes/model/adsl/supplier.php +++ b/modules/adsl/classes/model/adsl/supplier.php @@ -13,7 +13,7 @@ class Model_ADSL_Supplier extends ORMOSB { // Relationships protected $_has_many = array( - 'adsl_supplier_plan'=>array('foreign_key'=>'supplier_id'), + 'adsl_supplier_plan'=>array('foreign_key'=>'supplier_id','far_key'=>'id'), ); protected $_updated_column = FALSE; @@ -32,7 +32,8 @@ class Model_ADSL_Supplier extends ORMOSB { foreach ($aspo->adsl_plan->find_all() as $apo) // Find all the services who use this plan foreach ($apo->service->find_all() as $so) - array_push($services,$so); + if (! $active OR $so->active) + array_push($services,$so); return $services; } diff --git a/modules/adsl/classes/model/adsl/supplier/plan.php b/modules/adsl/classes/model/adsl/supplier/plan.php index 2d4f9aa1..36ba2d48 100644 --- a/modules/adsl/classes/model/adsl/supplier/plan.php +++ b/modules/adsl/classes/model/adsl/supplier/plan.php @@ -13,7 +13,7 @@ class Model_ADSL_Supplier_Plan extends ORMOSB { // Relationships protected $_has_many = array( - 'adsl_plan'=>array(), + 'adsl_plan'=>array('far_key'=>'id'), ); protected $_belongs_to = array( 'adsl_supplier'=>array('foreign_key'=>'supplier_id'), diff --git a/modules/core/database_join_fields.inc.php b/modules/core/database_join_fields.inc.php index 8bb06fbd..3e0edf64 100644 --- a/modules/core/database_join_fields.inc.php +++ b/modules/core/database_join_fields.inc.php @@ -71,7 +71,7 @@ function CORE_database_join_fields($result, $linked) while (!$rss->EOF) { for ($ii=0; $ii < count($result); $ii++ ) { if($result[$ii][$field] == $rss->fields[id]) { - if(ereg(',',$link_field)) { + if(preg_match('/,/',$link_field)) { $fields = explode(',',$link_field); for($iii=0; $iii \ No newline at end of file +?> diff --git a/modules/email_template/email_template.inc.php b/modules/email_template/email_template.inc.php index aac54b07..831b09fb 100644 --- a/modules/email_template/email_template.inc.php +++ b/modules/email_template/email_template.inc.php @@ -56,7 +56,7 @@ class email_template extends OSB_module { $template = $db->Execute(sqlselect($db,'email_template','*',array('name'=>$template_name))); # If the template is not active, return. - if (! $template || $template->fields['status'] != '1') + if (! $template || $template->fields['active'] != '1') return; # Setup our Email diff --git a/modules/export/classes/controller/admin/export.php b/modules/export/classes/controller/admin/export.php index e298f726..c075a003 100644 --- a/modules/export/classes/controller/admin/export.php +++ b/modules/export/classes/controller/admin/export.php @@ -37,10 +37,13 @@ class Controller_Admin_Export extends Controller_TemplateDefault { * This is the main call to export, providing a list of items to export and * setting up the page to call the export plugin when submitted. */ - public function action_index($daysago=30) { + public function action_index($daysago) { // @todo this should come from a file list $TBRexportplugins = array('quicken'=>'Export to Quicken'); + if (! $daysago) + $daysago = 30; + $payments = ORM::factory('payment') ->export($daysago); diff --git a/modules/export/classes/export.php b/modules/export/classes/export.php index 38ce8971..ca2fbfc9 100644 --- a/modules/export/classes/export.php +++ b/modules/export/classes/export.php @@ -16,7 +16,7 @@ class Export { public function __construct() { $this->plugin = preg_replace('/^'.get_parent_class($this).'_/','',get_class($this)); - $this->request = Request::current(); + $this->response = Response::factory(); } } ?> diff --git a/modules/export/classes/export/quicken.php b/modules/export/classes/export/quicken.php index 26039f16..0d52b15f 100644 --- a/modules/export/classes/export/quicken.php +++ b/modules/export/classes/export/quicken.php @@ -190,9 +190,9 @@ class Export_Quicken extends Export { } if (! empty($qo)) - $this->request->response = $qo->export(); + $this->response->body($qo->export()); - $this->request->send_file(TRUE,'quicken-import.iif'); + $this->response->send_file(TRUE,'quicken-import.iif',array('mime_type'=>'text/plain')); } } ?> diff --git a/modules/invoice/PDF/pdf_invoice_itemised-tcpdf.inc.php b/modules/invoice/PDF/pdf_invoice_itemised-tcpdf.inc.php index f9b1069f..4e2e4a34 100644 --- a/modules/invoice/PDF/pdf_invoice_itemised-tcpdf.inc.php +++ b/modules/invoice/PDF/pdf_invoice_itemised-tcpdf.inc.php @@ -203,7 +203,7 @@ class pdf_invoice_overview extends pdf_invoice_base { $this->SetXY($x,$y); $this->Cell(0,0,'This invoice can also be paid by:'); $y += 4; # Direct Credit - $logo = sprintf('%s/%s',PATH_THEMES.DEFAULT_THEME,'invoice/invoice-payment-dd.png'); + $logo = sprintf('%s/%s','/afs/local/home/gh/.web/au.net.graytech.www/osb/modules/invoice/media/img/','invoice-payment-dd.png'); $this->Image($logo,$x+1,$y,8); $this->SetFont('helvetica','B',8); $this->SetXY($x+10,$y); $this->Cell(0,0,'Direct Credit to our Bank Account'); $y += 3; @@ -231,7 +231,8 @@ class pdf_invoice_overview extends pdf_invoice_base { # Paypal $y += 3; - $logo = sprintf('%s/%s',PATH_THEMES.DEFAULT_THEME,'invoice/invoice-payment-pp.png'); +// $logo = sprintf('%s/%s',PATH_THEMES.DEFAULT_THEME,'invoice/invoice-payment-pp.png'); + $logo = sprintf('%s/%s','/afs/local/home/gh/.web/au.net.graytech.www/osb/modules/invoice/media/img/','invoice-payment-pp.png'); $this->Image($logo,$x+1,$y,8); $this->SetFont('helvetica','B',8); $this->SetXY($x+10,$y); $this->Cell(0,0,'Pay Pal/Credit Card'); $y += 3; diff --git a/modules/invoice/invoice.inc.php b/modules/invoice/invoice.inc.php index d0ac6077..693fd575 100644 --- a/modules/invoice/invoice.inc.php +++ b/modules/invoice/invoice.inc.php @@ -618,7 +618,7 @@ class invoice extends OSB_module { $account_where = ''; if (is_null($fields)) - $fields = 'DISTINCT service.id AS sid,account.id AS account_id,invoice.id AS iid,FROM_UNIXTIME(service.date_next_invoice,\'%Y-%m-%d\') AS invoice_date,service.sku AS sku,service.price AS price,account.first_name AS first_name,account.last_name AS last_name,account.currency_id AS billed_currency_id,service.date_orig AS date_orig'; + $fields = 'DISTINCT service.id AS sid,account.id AS account_id,FROM_UNIXTIME(service.date_next_invoice,\'%Y-%m-%d\') AS invoice_date,service.sku AS sku,service.price AS price,account.first_name AS first_name,account.last_name AS last_name,account.currency_id AS billed_currency_id,service.date_orig AS date_orig'; if (is_null($orderby)) $orderby = 'ORDER BY account_id,invoice_date,sid'; @@ -638,7 +638,6 @@ class invoice extends OSB_module { SELECT %s FROM {p}service AS service JOIN {p}account AS account ON (service.account_id=account.id AND account.site_id={s}) -LEFT JOIN {p}invoice AS invoice ON (service.invoice_id=invoice.id AND invoice.site_id={s}) %s WHERE service.site_id={s} AND service.active=1 diff --git a/modules/payment/classes/model/payment.php b/modules/payment/classes/model/payment.php index b66cd605..c39714ed 100644 --- a/modules/payment/classes/model/payment.php +++ b/modules/payment/classes/model/payment.php @@ -13,7 +13,7 @@ class Model_Payment extends ORMOSB { // Relationships protected $_has_many = array( - 'payment_item'=>array(), + 'payment_item'=>array('far_key'=>'id'), 'invoice'=>array('through'=>'payment_item'), ); protected $_belongs_to = array( @@ -23,9 +23,13 @@ class Model_Payment extends ORMOSB { protected $_sorting = array('date_payment'=>'DESC'); - protected $_formats = array( - 'date_payment'=>array('Model_Invoice::_filters'=>array('date','d-m-Y')), - 'total_amt'=>array('Currency::display'=>array()), + protected $_display_filters = array( + 'date_payment'=>array( + array('Config::date',array(':value')), + ), + 'total_amt'=>array( + array('Currency::display',array(':value')), + ), ); /** diff --git a/modules/product/classes/model/product.php b/modules/product/classes/model/product.php index e8abb1d5..d97fb147 100644 --- a/modules/product/classes/model/product.php +++ b/modules/product/classes/model/product.php @@ -144,5 +144,14 @@ class Model_Product extends ORMOSB { else return FALSE; } + + public function show_thumb() { + $mediapath = Route::get('default/media'); + + $thumb = $mediapath->uri(array('file'=>'img/thumbnails/'.$this->thumbnail)); + + // @todo Change the ALT to the product name. + echo HTML::image($thumb,array('alt'=>_('Thumb Nail'))); + } } ?> diff --git a/modules/product/views/product/view.php b/modules/product/views/product/view.php index df26f841..2fe45370 100644 --- a/modules/product/views/product/view.php +++ b/modules/product/views/product/view.php @@ -27,14 +27,16 @@ echo Form::open('cart/add'); - + + + + prod_plugin && method_exists($record->prod_plugin_file,'product_view')) { $pio = new $record->prod_plugin_file; - echo ''; + echo ''; } ?> - prod_plugin && method_exists($record->prod_plugin_file,'contract_view')) { $pio = new $record->prod_plugin_file; echo ''; diff --git a/modules/service/classes/controller/admin/service.php b/modules/service/classes/controller/admin/service.php index 4d9fc7c2..8381e0a7 100644 --- a/modules/service/classes/controller/admin/service.php +++ b/modules/service/classes/controller/admin/service.php @@ -9,7 +9,6 @@ * @author Deon George * @copyright (c) 2010 Open Source Billing * @license http://dev.osbill.net/license.html - * @todo Replace View::factory files to use $this->viewpath() */ class Controller_Admin_Service extends Controller_TemplateDefault { protected $control = array('Services'=>'services'); @@ -51,14 +50,14 @@ ORDER BY c.id,s.recur_schedule,c.name,a.company,a.last_name,a.first_name if (($si != $last_account) AND $last_account) { if ($sc > 1) - $output .= View::factory('service/list/bycheckout_subtotal') + $output .= View::factory('service/admin/list/bycheckout_subtotal') ->set('subtotal',Currency::display($st)) ->set('i',$i++%2); $sc = $st = 0; } if (($service['cid'] != $last_checkout) OR (! is_null($last_checkout) AND ! $last_checkout)) { - $output .= View::factory('service/list/bycheckout_header') + $output .= View::factory('service/admin/list/bycheckout_header') ->set('checkout_name',$service['checkout_plugin_name']) ->set('last_checkout',$last_checkout); } @@ -69,14 +68,14 @@ ORDER BY c.id,s.recur_schedule,c.name,a.company,a.last_name,a.first_name $st += round($so->price+$so->tax(),2); $sc++; - $output .= View::factory('service/list/bycheckout_body') + $output .= View::factory('service/admin/list/bycheckout_body') ->set('service',$so) ->set('i',$i++%2); } // Last subtotal if ($sc > 1) - $output .= View::factory('service/list/bycheckout_subtotal') + $output .= View::factory('service/admin/list/bycheckout_subtotal') ->set('subtotal',$st) ->set('i',$i++%2); @@ -124,13 +123,13 @@ ORDER BY C.last_name,B.account_id,A.service_number if ($i) $output .= ''; - $output .= View::factory('service/list/adslservices_header') + $output .= View::factory('service/admin/list/adslservices_header') ->set('service',$so); $last_account = $so->account_id; } - $output .= View::factory('service/list/adslservices_body') + $output .= View::factory('service/admin/list/adslservices_body') ->set('service',$so) ->set('i',$i++%2); } @@ -218,13 +217,13 @@ ORDER BY C.last_name,B.account_id,A.service_number if ($i) $output .= ''; - $output .= View::factory('service/list/adslservices_header') + $output .= View::factory('service/admin/list/adslservices_header') ->set('service',$so); $last_account = $so->account_id; } - $output .= View::factory('service/list/adslservices_body') + $output .= View::factory('service/admin/list/adslservices_body') ->set('service',$so) ->set('i',$i++%2); } @@ -294,7 +293,7 @@ GROUP BY DATE_FORMAT(DATE,"%%Y-%%m"),SID $i = $j = 0; $total = 0; $summary = ''; - $output = View::factory('service/list/adslbilling_head'); + $output = View::factory('service/admin/list/adslbilling_head'); $output .= '
description_full; ?>description_full; ?>thumbnail) $record->show_thumb(); ?>
'.$pio->product_view($record->prod_plugin_data).''.$pio->product_view($record->prod_plugin_data).'
  '.$pio->contract_view($record->prod_plugin_data,$record->price_base,$record->price_setup).'
 
 
'; foreach ($aso->services(TRUE) as $so) { // Reset our uploaded data @@ -307,7 +306,7 @@ GROUP BY DATE_FORMAT(DATE,"%%Y-%%m"),SID // Record the the exception if the cost is not expected if (round($so->service_adsl->adsl_plan->adsl_supplier_plan->base_cost+$so->service_adsl->adsl_plan->adsl_supplier_plan->tax(),2) != $uploaded['amount']) { - $summary .= View::factory('service/list/adslbilling_summary') + $summary .= View::factory('service/admin/list/adslbilling_summary') ->set('service',$so) ->set('amount',$uploaded['amount']) ->set('i',$j++%2); @@ -325,7 +324,7 @@ GROUP BY DATE_FORMAT(DATE,"%%Y-%%m"),SID } $total += $uploaded['amount']; - $output .= View::factory('service/list/adslbilling_body') + $output .= View::factory('service/admin/list/adslbilling_body') ->set('service',$so) ->set('checked',$uploaded['checked']) ->set('amount',$uploaded['amount']) @@ -333,7 +332,7 @@ GROUP BY DATE_FORMAT(DATE,"%%Y-%%m"),SID ->set('i',$i++%2); } - $output .= View::factory('service/list/adslbilling_foot') + $output .= View::factory('service/admin/list/adslbilling_foot') ->set('total',$total); $output .= '
'; @@ -341,7 +340,7 @@ GROUP BY DATE_FORMAT(DATE,"%%Y-%%m"),SID // Summary Report of remaining CSV items. if (! empty($csv)) foreach ($csv as $service => $item) { - $summary .= View::factory('service/list/adslbilling_summary_exception') + $summary .= View::factory('service/admin/list/adslbilling_summary_exception') ->set('service',$service) ->set('item',$item) ->set('i',$j++%2); @@ -433,8 +432,31 @@ GROUP BY DATE_FORMAT(DATE,"%%Y-%%m"),SID 'title'=>sprintf('%s %s:%s',_('Update Service'),$so->id(),$so->name()), 'body'=>View::factory($so->viewpath()) ->set('so',$so) + ->set('mediapath',Route::get('default/media')) ->set('plugin_form',$so->admin_update()), )); + + // @todo Investigate a better way of preparing for jscalendar + Script::add(array( + 'type'=>'file', + 'data'=>'js/dhtml.calendar.js', + )); + Script::add(array( + 'type'=>'file', + 'data'=>'js/dhtml.calendar-setup.js', + )); + Script::add(array( + 'type'=>'file', + 'data'=>'js/dhtml.calendar-en.js', + )); + Script::add(array( + 'type'=>'file', + 'data'=>'js/dhtml.date_selector.js', + )); + Style::add(array( + 'type'=>'file', + 'data'=>'css/dhtml.calendar.css', + )); } } ?> diff --git a/modules/service/classes/controller/user/service.php b/modules/service/classes/controller/user/service.php index cbd96e05..9045c0d9 100644 --- a/modules/service/classes/controller/user/service.php +++ b/modules/service/classes/controller/user/service.php @@ -32,7 +32,7 @@ class Controller_User_Service extends Controller_TemplateDefault { public function action_list() { Block::add(array( 'title'=>sprintf('%s: %s - %s',_('Services For'),$this->ao->accnum(),$this->ao->name(TRUE)), - 'body'=>View::factory('service/list') + 'body'=>View::factory('service/user/list') ->set('services',$this->ao->service->find_all()), )); } @@ -47,7 +47,7 @@ class Controller_User_Service extends Controller_TemplateDefault { Block::add(array( 'title'=>sprintf('%s: %s',$so->id(),$so->product->name()), - 'body'=>View::factory('service/view') + 'body'=>View::factory('service/user/view') ->set('so',$so), )); } diff --git a/modules/service/classes/model/service.php b/modules/service/classes/model/service.php index 0d19e9ce..c87f73b1 100644 --- a/modules/service/classes/model/service.php +++ b/modules/service/classes/model/service.php @@ -76,7 +76,7 @@ class Model_Service extends ORMOSB { /** * Return the object of the product plugin */ - public function plugin() { + private function plugin() { if (! $this->product->prod_plugin_file) return NULL; diff --git a/modules/service/classes/model/service/adsl.php b/modules/service/classes/model/service/adsl.php index 38967ef4..9c7db348 100644 --- a/modules/service/classes/model/service/adsl.php +++ b/modules/service/classes/model/service/adsl.php @@ -183,7 +183,8 @@ class Model_Service_ADSL extends Model_Service { } public function traffic_lastmonth($string=TRUE) { - return $this->traffic_month(strtotime('last month'),$string); + // We need it to be last month as of yesterday + return $this->traffic_month(strtotime('last month')-86400,$string); } public function traffic_thismonth($string=TRUE) { @@ -225,8 +226,8 @@ class Model_Service_ADSL extends Model_Service { $traffic_type = $this->get_traffic_data_daily($period,TRUE); $day = count($traffic_type) ? max(array_keys($traffic_type)) : 1; - $date = mktime(0,0,0,date('m',$period),$day,date('Y',$period)); - $daysleft = date('d',strtotime('last day ',$date))-$day; + $date = mktime(0,0,0,date('n',$period),$day,date('Y',$period)); + $daysleft = date('d',strtotime('last day of',$date))-$day; $google = GoogleChart::factory('vertical_bar'); $google->title = sprintf('DSL traffic usage as at %s',Config::date($date)); @@ -248,7 +249,7 @@ class Model_Service_ADSL extends Model_Service { switch ($item) { case 'MONTH_GRAPH': $value = (string)$google; break; case 'MONTH_TABLE': $value = $google->html_table(FALSE,array( - 'table'=>'style="border: 1px solid #bebcb7; padding: 5px 5px; background: none repeat scroll 0% 0% #f8f7f5;"', + 'table'=>'style="border: 1px solid #bebcb7; padding: 5px 5px; background: none repeat scroll 0% 0% #f8f7f5; font-size: 70%;"', )); break; case 'OFFPEAK_ALLOWANCE': $value = isset($allowance['base_down_offpeak']) ? $allowance['base_down_offpeak'].' MB' : '-'; break; @@ -331,7 +332,7 @@ class Model_Service_ADSL extends Model_Service { } protected function _service_view() { - return View::factory('service/adsl/view') + return View::factory($this->viewpath(strtolower($this->service->prod_plugin_name))) ->set('so',$this); } @@ -356,13 +357,14 @@ class Model_Service_ADSL extends Model_Service { protected function _admin_update() { return View::factory($this->viewpath(strtolower($this->service->prod_plugin_name))) + ->set('mediapath',Route::get('default/media')) ->set('so',$this); } /** * Render a google chart of traffic */ - public function graph_traffic($month=null) { + public function graph_traffic($month=NULL) { $google = GoogleChart::factory('vertical_bar'); // If we came in via a post to show a particular month, then show that, otherwise show the yearly result @@ -382,9 +384,6 @@ class Model_Service_ADSL extends Model_Service { 'axis'=>'r', 'data'=>array((isset($friendly['cumulative'.$k]) ? $friendly['cumulative'.$k] : 'cumulative'.$k)=>$this->cumulative($traffic_data[$k])))); - $graph_data = View::factory('service/view_detail_adsl_traffic') - ->set('traffic',$this->traffic_month(strtotime($_POST['month'].'-01'),FALSE)); - } else { // @todo Change the date to the last record date $google->title = sprintf('Monthly DSL traffic usage as at %s',Config::date(strtotime('yesterday'))); @@ -399,5 +398,10 @@ class Model_Service_ADSL extends Model_Service { return (string)$google; } + + public function table_traffic($month=NULL) { + return View::factory('service/user/adsl/table_traffic') + ->set('traffic',$this->traffic_month((! is_null($month) AND trim($month)) ? strtotime($month.'-01') : NULL,FALSE)); + } } ?> diff --git a/modules/service/views/service/admin/adsl/update.php b/modules/service/views/service/admin/adsl/update.php index ce66d54f..b41b588a 100644 --- a/modules/service/views/service/admin/adsl/update.php +++ b/modules/service/views/service/admin/adsl/update.php @@ -1,36 +1,36 @@ - + - + + + + + + + + + + + + + + + + + + + + +
Plugin DetailsPlugin Details
- - - - - - - - - - - - - - - - - - - - - - - - - -
Service Numberservice_number); ?>
Service Addressservice_address); ?>
Service Connect Dateservice_connect_date); ?>
Service Usernameservice_username); ?>
Service Passwordservice_password); ?>
Service IP Addressipaddress); ?>
+
Service Numberservice_number); ?>
Service Addressservice_address); ?>
Service Connect Date + service_connect_date,array('id'=>'service_connect_date')); ?> + uri(array('file'=>'img/calendar.png')),array('alt'=>_('Calendar'),'style'=>'cursor: pointer;')), + array('title'=>'Click to popup a dialog to select a date graphically','onclick'=>"dateSelector('service_connect_date')")); ?> +
Service Usernameservice_username); ?>
Service Passwordservice_password); ?>
Service IP Addressipaddress); ?>
diff --git a/modules/service/views/service/list/adslbilling_body.php b/modules/service/views/service/admin/list/adslbilling_body.php similarity index 100% rename from modules/service/views/service/list/adslbilling_body.php rename to modules/service/views/service/admin/list/adslbilling_body.php diff --git a/modules/service/views/service/list/adslbilling_foot.php b/modules/service/views/service/admin/list/adslbilling_foot.php similarity index 100% rename from modules/service/views/service/list/adslbilling_foot.php rename to modules/service/views/service/admin/list/adslbilling_foot.php diff --git a/modules/service/views/service/list/adslbilling_head.php b/modules/service/views/service/admin/list/adslbilling_head.php similarity index 100% rename from modules/service/views/service/list/adslbilling_head.php rename to modules/service/views/service/admin/list/adslbilling_head.php diff --git a/modules/service/views/service/list/adslbilling_summary.php b/modules/service/views/service/admin/list/adslbilling_summary.php similarity index 100% rename from modules/service/views/service/list/adslbilling_summary.php rename to modules/service/views/service/admin/list/adslbilling_summary.php diff --git a/modules/service/views/service/list/adslbilling_summary_exception.php b/modules/service/views/service/admin/list/adslbilling_summary_exception.php similarity index 100% rename from modules/service/views/service/list/adslbilling_summary_exception.php rename to modules/service/views/service/admin/list/adslbilling_summary_exception.php diff --git a/modules/service/views/service/list/adslservices_body.php b/modules/service/views/service/admin/list/adslservices_body.php similarity index 83% rename from modules/service/views/service/list/adslservices_body.php rename to modules/service/views/service/admin/list/adslservices_body.php index fc5b2467..f93d337e 100644 --- a/modules/service/views/service/list/adslservices_body.php +++ b/modules/service/views/service/admin/list/adslservices_body.php @@ -2,7 +2,7 @@ service_adsl->display('service_number'); ?> service_adsl->ipaddress(); ?> name(),$service->id); ?> - service_adsl->adsl_plan->allowance(); ?> + product->prod_plugin_file ? $service->product->plugin()->allowance() : 'No Details'; ?> service_adsl->traffic_thismonth(); ?> service_adsl->traffic_lastmonth(); ?> display('price'); ?> diff --git a/modules/service/views/service/list/adslservices_header.php b/modules/service/views/service/admin/list/adslservices_header.php similarity index 100% rename from modules/service/views/service/list/adslservices_header.php rename to modules/service/views/service/admin/list/adslservices_header.php diff --git a/modules/service/views/service/list/bycheckout_body.php b/modules/service/views/service/admin/list/bycheckout_body.php similarity index 100% rename from modules/service/views/service/list/bycheckout_body.php rename to modules/service/views/service/admin/list/bycheckout_body.php diff --git a/modules/service/views/service/list/bycheckout_header.php b/modules/service/views/service/admin/list/bycheckout_header.php similarity index 100% rename from modules/service/views/service/list/bycheckout_header.php rename to modules/service/views/service/admin/list/bycheckout_header.php diff --git a/modules/service/views/service/list/bycheckout_subtotal.php b/modules/service/views/service/admin/list/bycheckout_subtotal.php similarity index 100% rename from modules/service/views/service/list/bycheckout_subtotal.php rename to modules/service/views/service/admin/list/bycheckout_subtotal.php diff --git a/modules/service/views/service/admin/update.php b/modules/service/views/service/admin/update.php index facd6a10..62f802da 100644 --- a/modules/service/views/service/admin/update.php +++ b/modules/service/views/service/admin/update.php @@ -3,7 +3,7 @@ - + diff --git a/modules/service/views/service/view.php b/modules/service/views/service/user/view.php similarity index 100% rename from modules/service/views/service/view.php rename to modules/service/views/service/user/view.php diff --git a/modules/static_page/classes/model/staticpage.php b/modules/static_page/classes/model/staticpage.php index b9f0d439..611daba6 100644 --- a/modules/static_page/classes/model/staticpage.php +++ b/modules/static_page/classes/model/staticpage.php @@ -18,7 +18,7 @@ class Model_StaticPage extends ORMOSB { ); protected $_has_many = array( - 'staticpage_translate'=>array('foreign_key'=>'static_page_id'), + 'staticpage_translate'=>array('foreign_key'=>'static_page_id','far_key'=>'id'), ); protected $_belongs_to = array(
- +
@@ -22,7 +22,13 @@ - + diff --git a/modules/service/views/service/view_detail_adsl_traffic.php b/modules/service/views/service/user/adsl/table_traffic.php similarity index 100% rename from modules/service/views/service/view_detail_adsl_traffic.php rename to modules/service/views/service/user/adsl/table_traffic.php diff --git a/modules/service/views/service/adsl/view.php b/modules/service/views/service/user/adsl/view.php similarity index 91% rename from modules/service/views/service/adsl/view.php rename to modules/service/views/service/user/adsl/view.php index 31613f7a..74db3ee0 100644 --- a/modules/service/views/service/adsl/view.php +++ b/modules/service/views/service/user/adsl/view.php @@ -67,6 +67,7 @@ -
Service Active active); ?>
Date Next Invoicedisplay('date_next_invoice'); ?> + date_next_invoice,array('id'=>'date_next_invoice')); ?> + uri(array('file'=>'img/calendar.png')),array('alt'=>_('Calendar'),'style'=>'cursor: pointer;')), + array('title'=>'Click to popup a dialog to select a date graphically','onclick'=>"dateSelector('date_next_invoice')")); ?> + +
Taxable
graph_traffic(isset($_POST['month']) ? $_POST['month'] : ''); ?> + graph_traffic(isset($_POST['month']) ? $_POST['month'] : ''); ?> + table_traffic(isset($_POST['month']) ? $_POST['month'] : ''); ?>
diff --git a/modules/service/views/service/list.php b/modules/service/views/service/user/list.php similarity index 92% rename from modules/service/views/service/list.php rename to modules/service/views/service/user/list.php index 15534b3b..17b1e283 100644 --- a/modules/service/views/service/list.php +++ b/modules/service/views/service/user/list.php @@ -12,7 +12,7 @@
id,$service->id); ?> display('type'); ?>invoice_display(); ?>name(); ?> display('recur_schedule');?> display('price'); ?> display('active'); ?>