From ecaebbeb67fee65860539cde031a62fba19819d4 Mon Sep 17 00:00:00 2001 From: Naomi Aro Date: Mon, 23 Jan 2012 19:07:07 +0100 Subject: [PATCH] CC-3174 : Show builder, working on date choice pickers, make sure they accomodate client/server timezones. --- .../controllers/ScheduleController.php | 19 +- .../controllers/ShowbuilderController.php | 57 +- .../application/layouts/scripts/builder.phtml | 2 +- airtime_mvc/application/models/Schedule.php | 42 +- .../views/scripts/showbuilder/builder.phtml | 9 +- .../public/css/jquery-ui-timepicker.css | 28 - .../public/css/jquery.ui.timepicker.css | 69 + .../library/events/library_playlistbuilder.js | 16 + .../library/events/library_showbuilder.js | 17 + .../public/js/airtime/library/library.js | 16 - .../public/js/airtime/showbuilder/builder.js | 242 ++- .../timepicker/jquery.ui.timepicker-0.0.6.js | 849 ----------- .../js/timepicker/jquery.ui.timepicker.js | 1345 +++++++++++++++++ 13 files changed, 1715 insertions(+), 996 deletions(-) delete mode 100644 airtime_mvc/public/css/jquery-ui-timepicker.css create mode 100644 airtime_mvc/public/css/jquery.ui.timepicker.css delete mode 100644 airtime_mvc/public/js/timepicker/jquery.ui.timepicker-0.0.6.js create mode 100644 airtime_mvc/public/js/timepicker/jquery.ui.timepicker.js diff --git a/airtime_mvc/application/controllers/ScheduleController.php b/airtime_mvc/application/controllers/ScheduleController.php index c78dcb270..27e547863 100644 --- a/airtime_mvc/application/controllers/ScheduleController.php +++ b/airtime_mvc/application/controllers/ScheduleController.php @@ -41,33 +41,34 @@ class ScheduleController extends Zend_Controller_Action $baseUrl = $request->getBaseUrl(); $this->view->headScript()->appendFile($baseUrl.'/js/contextmenu/jjmenu.js','text/javascript'); - $this->view->headScript()->appendFile($baseUrl.'/js/datatables/js/jquery.dataTables.js','text/javascript'); - $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.pluginAPI.js','text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/datatables/js/jquery.dataTables.js','text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.pluginAPI.js','text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/fullcalendar/fullcalendar.js','text/javascript'); - $this->view->headScript()->appendFile($baseUrl.'/js/timepicker/jquery.ui.timepicker-0.0.6.js','text/javascript'); - $this->view->headScript()->appendFile($baseUrl.'/js/colorpicker/js/colorpicker.js','text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/timepicker/jquery.ui.timepicker.js','text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/colorpicker/js/colorpicker.js','text/javascript'); //full-calendar-functions.js requires this variable, so that datePicker widget can be offset to server time instead of client time $this->view->headScript()->appendScript("var timezoneOffset = ".date("Z")."; //in seconds"); - $this->view->headScript()->appendFile($baseUrl.'/js/airtime/schedule/full-calendar-functions.js','text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/airtime/schedule/full-calendar-functions.js','text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/airtime/schedule/add-show.js','text/javascript'); - $this->view->headScript()->appendFile($baseUrl.'/js/airtime/schedule/schedule.js','text/javascript'); - $this->view->headScript()->appendFile($baseUrl.'/js/meioMask/jquery.meio.mask.js','text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/airtime/schedule/schedule.js','text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/meioMask/jquery.meio.mask.js','text/javascript'); - $this->view->headLink()->appendStylesheet($baseUrl.'/css/jquery-ui-timepicker.css'); + $this->view->headLink()->appendStylesheet($baseUrl.'/css/jquery.ui.timepicker.css'); $this->view->headLink()->appendStylesheet($baseUrl.'/css/fullcalendar.css'); $this->view->headLink()->appendStylesheet($baseUrl.'/css/colorpicker/css/colorpicker.css'); $this->view->headLink()->appendStylesheet($baseUrl.'/css/add-show.css'); $this->view->headLink()->appendStylesheet($baseUrl.'/css/contextmenu.css'); + Application_Model_Schedule::createNewFormSections($this->view); $userInfo = Zend_Auth::getInstance()->getStorage()->read(); $user = new Application_Model_User($userInfo->id); $this->view->isAdmin = $user->isAdmin(); $this->view->isProgramManager = $user->isUserType('P'); - + $this->view->headScript()->appendScript("var weekStart = ".Application_Model_Preference::GetWeekStartDay().";"); } diff --git a/airtime_mvc/application/controllers/ShowbuilderController.php b/airtime_mvc/application/controllers/ShowbuilderController.php index 674ea827d..85d491fde 100644 --- a/airtime_mvc/application/controllers/ShowbuilderController.php +++ b/airtime_mvc/application/controllers/ShowbuilderController.php @@ -7,36 +7,22 @@ class ShowbuilderController extends Zend_Controller_Action { $ajaxContext = $this->_helper->getHelper('AjaxContext'); $ajaxContext->addActionContext('schedule', 'json') + ->addActionContext('builder-feed', 'json') ->initContext(); } public function indexAction() { - $request = $this->getRequest(); + $this->_helper->layout->setLayout('builder'); - $user_id = $request->getParam("uid", 0); - $show_instance_id = $request->getParam("sid", 0); + $this->view->headScript()->appendFile($this->view->baseUrl('/js/airtime/library/events/library_showbuilder.js'),'text/javascript'); - try { - $user = new Application_Model_User($user_id); - $show_instance = new Application_Model_ShowInstance($show_instance_id); - } - catch(Exception $e) { - $this->_helper->redirector('denied', 'error'); - } + $this->_helper->actionStack('library', 'library'); + $this->_helper->actionStack('builder', 'showbuilder'); //user is allowed to schedule this show. - if ($user->isAdmin() || $user->isHost($show_instance->getShowId())) { - $this->_helper->layout->setLayout('builder'); - - $this->view->headScript()->appendFile($this->view->baseUrl('/js/airtime/library/events/library_showbuilder.js'),'text/javascript'); - - $this->_helper->actionStack('library', 'library'); - $this->_helper->actionStack('builder', 'showbuilder'); - } - else { - $this->_helper->redirector('denied', 'error'); - } + //if ($user->isAdmin() || $user->isHost($show_instance->getShowId())) { + //} } public function builderAction() { @@ -44,16 +30,39 @@ class ShowbuilderController extends Zend_Controller_Action $request = $this->getRequest(); $baseUrl = $request->getBaseUrl(); - $this->view->headScript()->appendFile($baseUrl.'/js/fullcalendar/fullcalendar_orig.js','text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/timepicker/jquery.ui.timepicker.js','text/javascript'); + + $this->view->headScript()->appendScript("var serverTimezoneOffset = ".date("Z")."; //in seconds"); + $this->view->headScript()->appendFile($baseUrl.'/js/datatables/js/jquery.dataTables.js','text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColVis.js','text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColReorderResize.js','text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/airtime/showbuilder/builder.js','text/javascript'); - $this->view->headLink()->appendStylesheet($baseUrl.'/css/fullcalendar.css'); + $this->view->headLink()->appendStylesheet($baseUrl.'/css/jquery.ui.timepicker.css'); $this->_helper->viewRenderer->setResponseSegment('builder'); } - public function eventFeedAction() { + public function builderFeedAction() { + $request = $this->getRequest(); + $current_time = microtime(true); + + $starts_epoch = $request->getParam("start", $current_time); + //default ends is 24 hours after starts. + $ends_epoch = $request->getParam("end", $current_time + (60*60*24)); + + $startsDT = DateTime::createFromFormat("U.u", $starts_epoch, new DateTimeZone("UTC")); + $endsDT = DateTime::createFromFormat("U.u", $ends_epoch, new DateTimeZone("UTC")); + + $scheduled_items = Application_Model_Schedule::GetItems($startsDT->format("Y-m-d H:i:s.u"), $endsDT->format("Y-m-d H:i:s.u"), false); + + foreach ($scheduled_items as &$item) { + $itemStartsDT = DateTime::createFromFormat("Y-m-d H:i:s.u", $item["starts"], new DateTimeZone("UTC")); + $itemEndsDT = DateTime::createFromFormat("Y-m-d H:i:s.u", $item["ends"], new DateTimeZone("UTC")); + } + + $this->view->schedule = $scheduled_items; } public function scheduleAction() { diff --git a/airtime_mvc/application/layouts/scripts/builder.phtml b/airtime_mvc/application/layouts/scripts/builder.phtml index e7c4dbf0a..3efba392c 100644 --- a/airtime_mvc/application/layouts/scripts/builder.phtml +++ b/airtime_mvc/application/layouts/scripts/builder.phtml @@ -22,7 +22,7 @@
layout()->library ?>
-
layout()->builder ?>
+
layout()->builder ?>
diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index 725578348..a0c8f3a5e 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -155,7 +155,7 @@ class Application_Model_Schedule { "nextShow"=>Application_Model_Show::GetNextShows($utcTimeNow, 1), "timezone"=> date("T"), "timezoneOffset"=> date("Z")); - + return $range; } @@ -294,6 +294,38 @@ class Application_Model_Schedule { return $rows; } + /* + * + * @param DateTime $p_startDateTime + * + * @param DateTime $p_endDateTime + * + * @return array $scheduledItems + * + */ + public static function GetScheduleDetailItems($p_startDateTime, $p_endDateTime) + { + global $CC_CONFIG, $CC_DBC; + + $sql = "SELECT DISTINCT + + showt.name, showt.color, showt.background_color, + si.starts, si.ends, si.time_filled, si.record, si.rebroadcast, + sched.starts, sched.ends, + ft.track_title, ft.artist_name, ft.album_title, ft.length + + FROM + ((cc_schedule AS sched JOIN cc_files AS ft ON (sched.file_id = ft.id) + RIGHT OUTER JOIN cc_show_instances AS si ON (si.id = sched.instance_id)) + JOIN cc_show AS showt ON (showt.id = si.show_id) + ) + + ORDER BY sched.starts;"; + + $rows = $CC_DBC->GetAll($sql); + return $rows; + } + public static function GetShowInstanceItems($instance_id) { global $CC_CONFIG, $CC_DBC; @@ -533,7 +565,7 @@ class Application_Model_Schedule { public static function createNewFormSections($p_view){ $isSaas = Application_Model_Preference::GetPlanLevel() == 'disabled'?false:true; - + $formWhat = new Application_Form_AddShowWhat(); $formWho = new Application_Form_AddShowWho(); $formWhen = new Application_Form_AddShowWhen(); @@ -560,16 +592,16 @@ class Application_Model_Schedule { 'add_show_duration' => '1h')); $formRepeats->populate(array('add_show_end_date' => date("Y-m-d"))); - + if(!$isSaas){ $formRecord = new Application_Form_AddShowRR(); $formAbsoluteRebroadcast = new Application_Form_AddShowAbsoluteRebroadcastDates(); $formRebroadcast = new Application_Form_AddShowRebroadcastDates(); - + $formRecord->removeDecorator('DtDdWrapper'); $formAbsoluteRebroadcast->removeDecorator('DtDdWrapper'); $formRebroadcast->removeDecorator('DtDdWrapper'); - + $p_view->rr = $formRecord; $p_view->absoluteRebroadcast = $formAbsoluteRebroadcast; $p_view->rebroadcast = $formRebroadcast; diff --git a/airtime_mvc/application/views/scripts/showbuilder/builder.phtml b/airtime_mvc/application/views/scripts/showbuilder/builder.phtml index 8284050c2..8a0834363 100644 --- a/airtime_mvc/application/views/scripts/showbuilder/builder.phtml +++ b/airtime_mvc/application/views/scripts/showbuilder/builder.phtml @@ -1 +1,8 @@ -
\ No newline at end of file +
+ + + + + +
+
\ No newline at end of file diff --git a/airtime_mvc/public/css/jquery-ui-timepicker.css b/airtime_mvc/public/css/jquery-ui-timepicker.css deleted file mode 100644 index 926f56692..000000000 --- a/airtime_mvc/public/css/jquery-ui-timepicker.css +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Timepicker stylesheet - * Highly inspired from datepicker - * FG - Nov 2010 - Web3R - * - * version 0.0.3 : Fixed some settings, more dynamic - * version 0.0.4 : Removed width:100% on tables - */ - -.ui-timepicker-inline { display: inline } - -#ui-timepicker-div { padding: 0.2em; z-index: 10 !important } -.ui-timepicker-table { display: inline-table } -.ui-timepicker-table table { margin:0.15em 0 0 0; border-collapse: collapse; } - -.ui-timepicker-hours, .ui-timepicker-minutes { padding: 0.2em; } - -.ui-timepicker-table .ui-timepicker-title { line-height: 1.8em; text-align: center; } -.ui-timepicker-table td { padding: 0.1em; width: 2.2em; } -.ui-timepicker-table th.periods { padding: 0.1em; width: 2.2em; } -.ui-timepicker-table td a { - display:block; - padding:0.2em 0.3em 0.2em 0.5em; - width: 1.2em; - - text-align:right; - text-decoration:none; -} diff --git a/airtime_mvc/public/css/jquery.ui.timepicker.css b/airtime_mvc/public/css/jquery.ui.timepicker.css new file mode 100644 index 000000000..08b442a7e --- /dev/null +++ b/airtime_mvc/public/css/jquery.ui.timepicker.css @@ -0,0 +1,69 @@ +/* + * Timepicker stylesheet + * Highly inspired from datepicker + * FG - Nov 2010 - Web3R + * + * version 0.0.3 : Fixed some settings, more dynamic + * version 0.0.4 : Removed width:100% on tables + * version 0.1.1 : set width 0 on tables to fix an ie6 bug + */ + +.ui-timepicker-inline { display: inline; } + +#ui-timepicker-div { padding: 0.2em } +.ui-timepicker-table { display: inline-table; width: 0; } +.ui-timepicker-table table { margin:0.15em 0 0 0; border-collapse: collapse; } + +.ui-timepicker-hours, .ui-timepicker-minutes { padding: 0.2em; } + +.ui-timepicker-table .ui-timepicker-title { line-height: 1.8em; text-align: center; } +.ui-timepicker-table td { padding: 0.1em; width: 2.2em; } +.ui-timepicker-table th.periods { padding: 0.1em; width: 2.2em; } + +/* span for disabled cells */ +.ui-timepicker-table td span { + display:block; + padding:0.2em 0.3em 0.2em 0.5em; + width: 1.2em; + + text-align:right; + text-decoration:none; +} +/* anchors for clickable cells */ +.ui-timepicker-table td a { + display:block; + padding:0.2em 0.3em 0.2em 0.5em; + width: 1.2em; + cursor: pointer; + text-align:right; + text-decoration:none; +} + + +/* buttons and button pane styling */ +.ui-timepicker .ui-timepicker-buttonpane { + background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; +} +.ui-timepicker .ui-timepicker-buttonpane button { margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +/* The close button */ +.ui-timepicker .ui-timepicker-close { float: right } + +/* the now button */ +.ui-timepicker .ui-timepicker-now { float: left; } + +/* the deselect button */ +.ui-timepicker .ui-timepicker-deselect { float: left; } + + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-timepicker-cover { + display: none; /*sorry for IE5*/ + display/**/: block; /*sorry for IE5*/ + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +} \ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/library/events/library_playlistbuilder.js b/airtime_mvc/public/js/airtime/library/events/library_playlistbuilder.js index 3935d8c23..87c70d1cb 100644 --- a/airtime_mvc/public/js/airtime/library/events/library_playlistbuilder.js +++ b/airtime_mvc/public/js/airtime/library/events/library_playlistbuilder.js @@ -12,6 +12,22 @@ function dtDrawCallback() { setupGroupActions(); } +function addLibraryItemEvents() { + + $('#library_display tr[id ^= "au"]') + .draggable({ + helper: 'clone', + cursor: 'pointer' + }); + + $('#library_display tbody tr td').not('[class=library_checkbox]') + .jjmenu("click", + [{get:"/Library/context-menu/format/json/id/#id#/type/#type#"}], + {id: getId, type: getType}, + {xposition: "mouse", yposition: "mouse"}); + +} + function setupLibraryToolbar() { $("div.library_toolbar").html('Reset Order' + 'Delete' + diff --git a/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js b/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js index f5a1c6d18..2070f9add 100644 --- a/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js +++ b/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js @@ -11,4 +11,21 @@ function dtDrawCallback() { addLibraryItemEvents(); //addMetadataQtip(); //setupGroupActions(); +} + +function addLibraryItemEvents() { + + $('#library_display tr') + .draggable({ + helper: 'clone', + cursor: 'pointer', + connectToSortable: '#show_builder_table' + }); + + $('#library_display tbody tr td').not('[class=library_checkbox]') + .jjmenu("click", + [{get:"/Library/context-menu/format/json/id/#id#/type/#type#"}], + {id: getId, type: getType}, + {xposition: "mouse", yposition: "mouse"}); + } \ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index 65dc7c54b..31b776f59 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -104,22 +104,6 @@ function deletePlaylist(json) { } //end callbacks called by jjmenu -function addLibraryItemEvents() { - - $('#library_display tr[id ^= "au"]') - .draggable({ - helper: 'clone', - cursor: 'pointer' - }); - - $('#library_display tbody tr td').not('[class=library_checkbox]') - .jjmenu("click", - [{get:"/Library/context-menu/format/json/id/#id#/type/#type#"}], - {id: getId, type: getType}, - {xposition: "mouse", yposition: "mouse"}); - -} - function addProgressIcon(id) { if($("#au_"+id).find("td.library_title").find("span").length > 0){ $("#au_"+id).find("td.library_title").find("span").removeClass(); diff --git a/airtime_mvc/public/js/airtime/showbuilder/builder.js b/airtime_mvc/public/js/airtime/showbuilder/builder.js index 0038d4a40..02359e0d5 100644 --- a/airtime_mvc/public/js/airtime/showbuilder/builder.js +++ b/airtime_mvc/public/js/airtime/showbuilder/builder.js @@ -1,76 +1,192 @@ -function getFullCalendarEvents(start, end, callback) { - var url, unix_ts_start, unix_ts_end; +function tpStartOnHourShowCallback(hour) { + var tpEndHour = $('#show_builder_timepicker_end').timepicker('getHour'); + + // Check if proposed hour is prior or equal to selected end time hour + if (hour <= tpEndHour) { return true; } + // if hour did not match, it can not be selected + return false; +} - unix_ts_start = Math.round(start.getTime() / 1000), - unix_ts_end = Math.round(end.getTime() / 1000); +function tpStartOnMinuteShowCallback(hour, minute) { + var tpEndHour = $('#show_builder_timepicker_end').timepicker('getHour'), + tpEndMinute = $('#show_builder_timepicker_end').timepicker('getMinute'); + + // Check if proposed hour is prior to selected end time hour + if (hour < tpEndHour) { return true; } + // Check if proposed hour is equal to selected end time hour and minutes is prior + if ( (hour == tpEndHour) && (minute < tpEndMinute) ) { return true; } + // if minute did not match, it can not be selected + return false; +} - url = '/Schedule/event-feed'; +function tpEndOnHourShowCallback(hour) { + var tpStartHour = $('#show_builder_timepicker_start').timepicker('getHour'); + + // Check if proposed hour is after or equal to selected start time hour + if (hour >= tpStartHour) { return true; } + // if hour did not match, it can not be selected + return false; +} - var d = new Date(); +function tpEndOnMinuteShowCallback(hour, minute) { + var tpStartHour = $('#show_builder_timepicker_start').timepicker('getHour'), + tpStartMinute = $('#show_builder_timepicker_start').timepicker('getMinute'); + + // Check if proposed hour is after selected start time hour + if (hour > tpStartHour) { return true; } + // Check if proposed hour is equal to selected start time hour and minutes is after + if ( (hour == tpStartHour) && (minute > tpStartMinute) ) { return true; } + // if minute did not match, it can not be selected + return false; +} - $.get(url, {format: "json", start: unix_ts_start, end: unix_ts_end, cachep: d.getTime()}, function(json){ - callback(json.events); - }); +/* + * Get the schedule range start in unix timestamp form (in seconds). + * defaults to NOW if nothing is selected. + * + * @param String sDatePickerId + * + * @param String sTimePickerId + * + * @return Number iTime + */ +function fnGetUIPickerUnixTimestamp(sDatePickerId, sTimePickerId) { + var oDate, + oTimePicker = $( sTimePickerId ), + iTime, + iHour, + iMin, + iClientOffset, + iServerOffset; + + oDate = $( sDatePickerId ).datepicker( "getDate" ); + + //nothing has been selected from this datepicker. + if (oDate === null) { + oDate = new Date(); + } + else { + iHour = oTimePicker.timepicker('getHour'); + iMin = oTimePicker.timepicker('getMinute'); + + oDate.setHours(iHour, iMin); + } + + iTime = oDate.getTime(); //value is in millisec. + iTime = Math.round(iTime / 1000); + iClientOffset = -(oDate.getTimezoneOffset() * 60); //offset is returned in minutes. + iServerOffset = serverTimezoneOffset; + + return iTime; +} + +function fnGetScheduleRange() { + var iStart, + iEnd, + iRange; + + iStart = fnGetUIPickerUnixTimestamp("#show_builder_datepicker_start", "#show_builder_timepicker_start"); + iEnd = fnGetUIPickerUnixTimestamp("#show_builder_datepicker_end", "#show_builder_timepicker_end"); + + iRange = iEnd - iStart; +} + +function fnServerData( sSource, aoData, fnCallback ) { + aoData.push( { name: "format", value: "json"} ); + + $.ajax( { + "dataType": "json", + "type": "GET", + "url": sSource, + "data": aoData, + "success": fnCallback + } ); } $(document).ready(function() { - $('#show_builder').fullCalendar({ - header: { - left: '', - center: '', - right: '' - }, - defaultView: 'agendaDay', - allDaySlot: false, - theme: true, + var dTable; + + dTable = $('#show_builder_table').dataTable( { + "aoColumns": [ + /* starts */{"mDataProp": "starts", "sTitle": "starts"}, + /* ends */{"mDataProp": "ends", "sTitle": "ends"}, + /* title */{"mDataProp": "file_id", "sTitle": "file_id"} + ], + + "asStripClasses": [ 'odd' ], + + "bJQueryUI": true, + "bSort": false, + "bFilter": false, + "bProcessing": true, + "bServerSide": true, + "bInfo": false, + + "fnServerData": fnServerData, - events: getFullCalendarEvents, + // R = ColReorder, C = ColVis, see datatables doc for others + "sDom": 'Rr<"H"C>t<"F">', + + //options for infinite scrolling + //"bScrollInfinite": true, + //"bScrollCollapse": true, + "sScrollY": "400px", + + "sAjaxDataProp": "schedule", + "sAjaxSource": "/showbuilder/builder-feed" - axisFormat: 'H:mm', - slotMinutes: 1, - timeFormat: { - agenda: 'H:mm:ss{ - H:mm:ss}' - }, - - minTime: '17:00', - maxTime: '18:00', - - droppable: true, // this allows things to be dropped onto the calendar !!! - drop: function(date, allDay) { // this function is called when something is dropped - - // retrieve the dropped element's stored Event Object - //var originalEventObject = $(this).data('eventObject'); + }); + + $( "#show_builder_datepicker_start" ).datepicker({ + dateFormat: '@', + onSelect: function(sDate, oDatePicker) { + var oDate; - // we need to copy it, so that multiple events don't have a reference to the same object - //var copiedEventObject = $.extend({}, originalEventObject); - var copiedEventObject = {}; - var data = $(this).data("show_builder"); - - $.ajax({url: "/showbuilder/schedule", - data:{format: "json", sid:"", schedule_start: date}, - dataType:"json", - success:function(json){ - var x; - }, - error:function(jqXHR, textStatus, errorThrown){ - var x; - } - }); - - // assign it the date that was reported - copiedEventObject.title = "test title"; - copiedEventObject.start = date; - var end = new Date(date.getTime()); - end.setMinutes(end.getMinutes() + 5); - end.setSeconds(end.getSeconds() + 5); - copiedEventObject.end = end; - copiedEventObject.allDay = allDay; - - // render the event on the calendar - // the last `true` argument determines if the event "sticks" (http://arshaw.com/fullcalendar/docs/event_rendering/renderEvent/) - //$('#show_builder').fullCalendar('renderEvent', copiedEventObject, true); - - $("#schedule_calendar").fullCalendar( 'refetchEvents' ); + oDate = new Date(parseInt(sDate, 10)); + $(this).val(oDate.toDateString()); } }); + + $( "#show_builder_timepicker_start" ).timepicker({ + showPeriodLabels: false, + showCloseButton: true, + showLeadingZero: false + }); + + $( "#show_builder_datepicker_end" ).datepicker({ + dateFormat: '@', + onSelect: function(sDate, oDatePicker) { + var oDate; + + oDate = new Date(parseInt(sDate, 10)); + $(this).val(oDate.toDateString()); + } + }); + + $( "#show_builder_timepicker_end" ).timepicker({ + showPeriodLabels: false, + showCloseButton: true, + showLeadingZero: false + }); + + $( "#show_builder_timerange_button" ).click(function(ev){ + var oTable, oSettings, iStartDate, iEndDate, iStartTime, iEndTime; + + fnGetScheduleRange(); + + oTable = $('#show_builder_table').dataTable({"bRetrieve": true}); + oSettings = oTable.fnSettings(); + oSettings["_iDisplayStart"] = 1050; + + oTable.fnDraw(); + }); + + $( "#show_builder_table" ).sortable({ + placeholder: "ui-state-highlight", + items: 'tr', + receive: function(event, ui) { + var x; + } + }); + }); diff --git a/airtime_mvc/public/js/timepicker/jquery.ui.timepicker-0.0.6.js b/airtime_mvc/public/js/timepicker/jquery.ui.timepicker-0.0.6.js deleted file mode 100644 index a7a0d2b07..000000000 --- a/airtime_mvc/public/js/timepicker/jquery.ui.timepicker-0.0.6.js +++ /dev/null @@ -1,849 +0,0 @@ -/* - * jQuery UI Timepicker 0.0.5 - * - * Copyright 2010-2011, Francois Gelinas - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://fgelinas.com - * - * Depends: - * jquery.ui.core.js - */ - - /* - * As it is a timepicker, I inspired most of the code from the datepicker - * Francois Gelinas - Nov 2010 - */ - - /* - * Release 0.0.2 - Jan 6, 2011 - * Updated to include common display options for USA users - * Stephen Commisso - Jan 2011 - */ - - /* - * release 0.0.3 - Jan 8, 2011 - * Re-added a display:none on the main div (fix a small empty div visible at the bottom of the page before timepicker is called) (Thanks Gertjan van Roekel) - * Fixed a problem where the timepicker was never displayed with jquery ui 1.8.7 css, - * the problem was the class ui-helper-hidden-accessible, witch I removed. - * Thanks Alexander Fietz and StackOverflow : http://stackoverflow.com/questions/4522274/jquery-timepicker-and-jqueryui-1-8-7-conflict - */ - - /* - * Release 0.0.4 - jan 10, 2011 - * changed showLeadingZero to affect only hours, added showMinutesLeadingZero for minutes display - * Removed width:100% for tables in css - */ - - /* - * Release 0.0.5 - Jan 18, 2011 - * Now updating time picker selected value when manually typing in the text field (thanks Rasmus Schultz) - * Fixed : with showPeriod: true and showLeadingZero: true, PM hours did not show leading zeros (thanks Chandler May) - * Fixed : with showPeriod: true and showLeadingZero: true, Selecting 12 AM shows as 00 AM in the input field, also parsing 12AM did not work correctly (thanks Rasmus Schultz) - */ - - /* - * Release 0.0.6 - Jan 19, 2011 - * Added standard "change" event being triggered on the input when the content changes. (Thanks Rasmus Schultz) - * Added support for inline timePicker, attached to div or span - * Added altField that receive the parsed time value when selected time changes - * Added defaultTime value to use when input field is missing (inline) or input value is empty - * if defaultTime is missing, current time is used - * - */ - -(function ($, undefined) { - - $.extend($.ui, { timepicker: { version: "0.0.1"} }); - - var PROP_NAME = 'timepicker'; - var tpuuid = new Date().getTime(); - - /* Time picker manager. - Use the singleton instance of this class, $.timepicker, to interact with the time picker. - Settings for (groups of) time pickers are maintained in an instance object, - allowing multiple different settings on the same page. */ - - function Timepicker() { - this.debug = true; // Change this to true to start debugging - this._curInst = null; // The current instance in use - this._isInline = false; // true if the instance is displayed inline - this._disabledInputs = []; // List of time picker inputs that have been disabled - this._timepickerShowing = false; // True if the popup picker is showing , false if not - this._inDialog = false; // True if showing within a "dialog", false if not - this._dialogClass = 'ui-timepicker-dialog'; // The name of the dialog marker class - this._mainDivId = 'ui-timepicker-div'; // The ID of the main timepicker division - this._inlineClass = 'ui-timepicker-inline'; // The name of the inline marker class - this._currentClass = 'ui-timepicker-current'; // The name of the current hour / minutes marker class - this._dayOverClass = 'ui-timepicker-days-cell-over'; // The name of the day hover marker class - - this.regional = []; // Available regional settings, indexed by language code - this.regional[''] = { // Default regional settings - hourText: 'Hour', // Display text for hours section - minuteText: 'Minute', // Display text for minutes link - amPmText: ['AM', 'PM'] // Display text for AM PM - }; - this._defaults = { // Global defaults for all the time picker instances - showOn: 'focus', // 'focus' for popup on focus, - // 'button' for trigger button, or 'both' for either (not yet implemented) - showAnim: 'fadeIn', // Name of jQuery animation for popup - showOptions: {}, // Options for enhanced animations - appendText: '', // Display text following the input box, e.g. showing the format - onSelect: null, // Define a callback function when a hour / minutes is selected - onClose: null, // Define a callback function when the timepicker is closed - timeSeparator: ':', // The caracter to use to separate hours and minutes. - showPeriod: false, // Define whether or not to show AM/PM with selected time - showLeadingZero: true, // Define whether or not to show a leading zero for hours < 10. [true/false] - showMinutesLeadingZero: true, // Define whether or not to show a leading zero for minutes < 10. - altField: '', // Selector for an alternate field to store selected time into - defaultTime: '' // Used as default time when input field is empty or for inline timePicker - }; - $.extend(this._defaults, this.regional['']); - - this.tpDiv = $(''); - } - - $.extend(Timepicker.prototype, { - /* Class name added to elements to indicate already configured with a time picker. */ - markerClassName: 'hasTimepicker', - - /* Debug logging (if enabled). */ - log: function () { - if (this.debug) - console.log.apply('', arguments); - }, - - // TODO rename to "widget" when switching to widget factory - _widgetTimepicker: function () { - return this.tpDiv; - }, - - /* Override the default settings for all instances of the time picker. - @param settings object - the new settings to use as defaults (anonymous object) - @return the manager object */ - setDefaults: function (settings) { - extendRemove(this._defaults, settings || {}); - return this; - }, - - /* Attach the time picker to a jQuery selection. - @param target element - the target input field or division or span - @param settings object - the new settings to use for this time picker instance (anonymous) */ - _attachTimepicker: function (target, settings) { - // check for settings on the control itself - in namespace 'time:' - var inlineSettings = null; - for (var attrName in this._defaults) { - var attrValue = target.getAttribute('time:' + attrName); - if (attrValue) { - inlineSettings = inlineSettings || {}; - try { - inlineSettings[attrName] = eval(attrValue); - } catch (err) { - inlineSettings[attrName] = attrValue; - } - } - } - var nodeName = target.nodeName.toLowerCase(); - var inline = (nodeName == 'div' || nodeName == 'span'); - - if (!target.id) { - this.uuid += 1; - target.id = 'tp' + this.uuid; - } - var inst = this._newInst($(target), inline); - inst.settings = $.extend({}, settings || {}, inlineSettings || {}); - if (nodeName == 'input') { - this._connectTimepicker(target, inst); - } else if (inline) { - this._inlineTimepicker(target, inst); - } - }, - - /* Create a new instance object. */ - _newInst: function (target, inline) { - var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars - return { id: id, input: target, // associated target - selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection - drawMonth: 0, drawYear: 0, // month being drawn - inline: inline, // is timepicker inline or not : - tpDiv: (!inline ? this.tpDiv : // presentation div - $('
')) - }; - }, - - /* Attach the time picker to an input field. */ - _connectTimepicker: function (target, inst) { - var input = $(target); - inst.append = $([]); - inst.trigger = $([]); - if (input.hasClass(this.markerClassName)) { return; } - this._attachments(input, inst); - input.addClass(this.markerClassName). - keydown(this._doKeyDown). - keyup(this._doKeyUp). - bind("setData.timepicker", function (event, key, value) { - inst.settings[key] = value; - }). - bind("getData.timepicker", function (event, key) { - return this._get(inst, key); - }); - //this._autoSize(inst); - $.data(target, PROP_NAME, inst); - }, - - /* Handle keystrokes. */ - _doKeyDown: function (event) { - var inst = $.timepicker._getInst(event.target); - var handled = true; - inst._keyEvent = true; - if ($.timepicker._timepickerShowing) { - switch (event.keyCode) { - case 9: $.timepicker._hideTimepicker(); - handled = false; - break; // hide on tab out - case 27: $.timepicker._hideTimepicker(); - break; // hide on escape - default: handled = false; - } - } - else if (event.keyCode == 36 && event.ctrlKey) { // display the time picker on ctrl+home - $.timepicker._showTimepicker(this); - } - else { - handled = false; - } - if (handled) { - event.preventDefault(); - event.stopPropagation(); - } - }, - - /* Update selected time on keyUp */ - /* Added verion 0.0.5 */ - _doKeyUp: function (event) { - var inst = $.timepicker._getInst(event.target); - $.timepicker._setTimeFromField(inst); - $.timepicker._updateTimepicker(inst); - }, - - /* Make attachments based on settings. */ - _attachments: function (input, inst) { - var appendText = this._get(inst, 'appendText'); - var isRTL = this._get(inst, 'isRTL'); - if (inst.append) { inst.append.remove(); } - if (appendText) { - inst.append = $('' + appendText + ''); - input[isRTL ? 'before' : 'after'](inst.append); - } - input.unbind('focus', this._showTimepicker); - if (inst.trigger) { inst.trigger.remove(); } - var showOn = this._get(inst, 'showOn'); - if (showOn == 'focus' || showOn == 'both') { // pop-up time picker when in the marked field - input.focus(this._showTimepicker); - } - if (showOn == 'button' || showOn == 'both') { // pop-up time picker when button clicked - var buttonText = this._get(inst, 'buttonText'); - var buttonImage = this._get(inst, 'buttonImage'); - inst.trigger = $(this._get(inst, 'buttonImageOnly') ? - $('').addClass(this._triggerClass). - attr({ src: buttonImage, alt: buttonText, title: buttonText }) : - $('').addClass(this._triggerClass). - html(buttonImage == '' ? buttonText : $('').attr( - { src: buttonImage, alt: buttonText, title: buttonText }))); - input[isRTL ? 'before' : 'after'](inst.trigger); - inst.trigger.click(function () { - if ($.timepicker._timepickerShowing && $.timepicker._lastInput == input[0]) { $.timepicker._hideTimepicker(); } - else { $.timepicker._showTimepicker(input[0]); } - return false; - }); - } - }, - - - /* Attach an inline time picker to a div. */ - _inlineTimepicker: function(target, inst) { - var divSpan = $(target); - if (divSpan.hasClass(this.markerClassName)) - return; - divSpan.addClass(this.markerClassName).append(inst.tpDiv). - bind("setData.timepicker", function(event, key, value){ - inst.settings[key] = value; - }).bind("getData.timepicker", function(event, key){ - return this._get(inst, key); - }); - $.data(target, PROP_NAME, inst); - this._setTimeFromField(inst); - this._updateTimepicker(inst); - inst.tpDiv.show(); - }, - - /* Pop-up the time picker for a given input field. - @param input element - the input field attached to the time picker or - event - if triggered by focus */ - _showTimepicker: function (input) { - input = input.target || input; - if (input.nodeName.toLowerCase() != 'input') { input = $('input', input.parentNode)[0]; } // find from button/image trigger - if ($.timepicker._isDisabledTimepicker(input) || $.timepicker._lastInput == input) { return; } // already here - - var inst = $.timepicker._getInst(input); - if ($.timepicker._curInst && $.timepicker._curInst != inst) { - $.timepicker._curInst.tpDiv.stop(true, true); - } - var beforeShow = $.timepicker._get(inst, 'beforeShow'); - extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {})); - inst.lastVal = null; - $.timepicker._lastInput = input; - - $.timepicker._setTimeFromField(inst); - if ($.timepicker._inDialog) { input.value = ''; } // hide cursor - if (!$.timepicker._pos) { // position below input - $.timepicker._pos = $.timepicker._findPos(input); - $.timepicker._pos[1] += input.offsetHeight; // add the height - } - var isFixed = false; - $(input).parents().each(function () { - isFixed |= $(this).css('position') == 'fixed'; - return !isFixed; - }); - if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled - $.timepicker._pos[0] -= document.documentElement.scrollLeft; - $.timepicker._pos[1] -= document.documentElement.scrollTop; - } - var offset = { left: $.timepicker._pos[0], top: $.timepicker._pos[1] }; - $.timepicker._pos = null; - // determine sizing offscreen - inst.tpDiv.css({ position: 'absolute', display: 'block', top: '-1000px' }); - $.timepicker._updateTimepicker(inst); - - // reset clicked state - inst._hoursClicked = false; - inst._minutesClicked = false; - - // fix width for dynamic number of time pickers - // and adjust position before showing - offset = $.timepicker._checkOffset(inst, offset, isFixed); - inst.tpDiv.css({ position: ($.timepicker._inDialog && $.blockUI ? - 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none', - left: offset.left + 'px', top: offset.top + 'px' - }); - if (!inst.inline) { - var showAnim = $.timepicker._get(inst, 'showAnim'); - var duration = $.timepicker._get(inst, 'duration'); - var postProcess = function () { - $.timepicker._timepickerShowing = true; - var borders = $.timepicker._getBorders(inst.tpDiv); - inst.tpDiv.find('iframe.ui-timepicker-cover'). // IE6- only - css({ left: -borders[0], top: -borders[1], - width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight() - }); - }; - inst.tpDiv.zIndex($(input).zIndex() + 1); - if ($.effects && $.effects[showAnim]) { - inst.tpDiv.show(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess); - } - else { - inst.tpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess); - } - if (!showAnim || !duration) { postProcess(); } - if (inst.input.is(':visible') && !inst.input.is(':disabled')) { inst.input.focus(); } - $.timepicker._curInst = inst; - } - }, - - /* Generate the time picker content. */ - _updateTimepicker: function (inst) { - var self = this; - var borders = $.timepicker._getBorders(inst.tpDiv); - inst.tpDiv.empty().append(this._generateHTML(inst)) - .find('iframe.ui-timepicker-cover') // IE6- only - .css({ left: -borders[0], top: -borders[1], - width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight() - }) - .end() - .find('.ui-timepicker td a') - .bind('mouseout', function () { - $(this).removeClass('ui-state-hover'); - if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).removeClass('ui-timepicker-prev-hover'); - if (this.className.indexOf('ui-timepicker-next') != -1) $(this).removeClass('ui-timepicker-next-hover'); - }) - .bind('mouseover', function () { - if (!self._isDisabledTimepicker(inst.inline ? inst.tpDiv.parent()[0] : inst.input[0])) { - $(this).parents('.ui-timepicker-calendar').find('a').removeClass('ui-state-hover'); - $(this).addClass('ui-state-hover'); - if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).addClass('ui-timepicker-prev-hover'); - if (this.className.indexOf('ui-timepicker-next') != -1) $(this).addClass('ui-timepicker-next-hover'); - } - }) - .end() - .find('.' + this._dayOverClass + ' a') - .trigger('mouseover') - .end(); - }, - - /* Generate the HTML for the current state of the date picker. */ - _generateHTML: function (inst) { - var h, m, html = ''; - var showPeriod = (this._get(inst, 'showPeriod') == true); - var showLeadingZero = (this._get(inst, 'showLeadingZero') == true); - var showMinutesLeadingZero = (this._get(inst, 'showMinutesLeadingZero') == true); - var amPmText = this._get(inst, 'amPmText'); - - - html = '' + - '' + // Close the Hour td - '
' + - '
' + - this._get(inst, 'hourText') + - '
' + - ''; - - // AM - html += ''; - for (h = 0; h <= 5; h++) { - html += this._generateHTMLHourCell(inst, h, showPeriod, showLeadingZero); - } - - html += ''; - for (h = 6; h <= 11; h++) { - html += this._generateHTMLHourCell(inst, h, showPeriod, showLeadingZero); - } - - // PM - html += ''; - for (h = 12; h <= 17; h++) { - html += this._generateHTMLHourCell(inst, h, showPeriod, showLeadingZero); - } - - html += ''; - for (h = 18; h <= 23; h++) { - html += this._generateHTMLHourCell(inst, h, showPeriod, showLeadingZero); - } - html += '
' + amPmText[0] + '
' + amPmText[1] + '
' + // Close the hours cells table - '
' + // open minutes td - /* Add the minutes */ - '
' + - this._get(inst, 'minuteText') + - '
' + - '' + - ''; - for (m = 0; m < 15; m += 5) { - html += this._generateHTMLMinuteCell(inst, m, (m < 10) && showMinutesLeadingZero ? "0" + m.toString() : m.toString()); - } - html += ''; - for (m = 15; m < 30; m += 5) { - html += this._generateHTMLMinuteCell(inst, m, m.toString()); - } - html += ''; - for (m = 30; m < 45; m += 5) { - html += this._generateHTMLMinuteCell(inst, m, m.toString()); - } - html += ''; - for (m = 45; m < 60; m += 5) { - html += this._generateHTMLMinuteCell(inst, m, m.toString()); - } - html += '
' + - '
'; - return html; - }, - - /* Generate the content of a "Hour" cell */ - _generateHTMLHourCell: function (inst, hour, showPeriod, showLeadingZero) { - - var displayHour = hour; - if ((hour > 12) && showPeriod) { - displayHour = hour - 12; - } - if ((displayHour == 0) && showPeriod) { - displayHour = 12; - } - if ((displayHour < 10) && showLeadingZero) { - displayHour = '0' + displayHour; - } - - - var html = '' + - '' + - displayHour.toString() + - ''; - return html; - }, - /* Generate the content of a "Hour" cell */ - _generateHTMLMinuteCell: function (inst, minute, displayText) { - var html = '' + - '' + - displayText + - ''; - return html; - }, - - /* Is the first field in a jQuery collection disabled as a timepicker? - @param target element - the target input field or division or span - @return boolean - true if disabled, false if enabled */ - _isDisabledTimepicker: function (target) { - if (!target) { return false; } - for (var i = 0; i < this._disabledInputs.length; i++) { - if (this._disabledInputs[i] == target) { return true; } - } - return false; - }, - - /* Check positioning to remain on screen. */ - _checkOffset: function (inst, offset, isFixed) { - var tpWidth = inst.tpDiv.outerWidth(); - var tpHeight = inst.tpDiv.outerHeight(); - var inputWidth = inst.input ? inst.input.outerWidth() : 0; - var inputHeight = inst.input ? inst.input.outerHeight() : 0; - var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft(); - var viewHeight = document.documentElement.clientHeight + $(document).scrollTop(); - - offset.left -= (this._get(inst, 'isRTL') ? (tpWidth - inputWidth) : 0); - offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0; - offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0; - - // now check if datepicker is showing outside window viewport - move to a better place if so. - offset.left -= Math.min(offset.left, (offset.left + tpWidth > viewWidth && viewWidth > tpWidth) ? - Math.abs(offset.left + tpWidth - viewWidth) : 0); - offset.top -= Math.min(offset.top, (offset.top + tpHeight > viewHeight && viewHeight > tpHeight) ? - Math.abs(tpHeight + inputHeight) : 0); - - return offset; - }, - - /* Find an object's position on the screen. */ - _findPos: function (obj) { - var inst = this._getInst(obj); - var isRTL = this._get(inst, 'isRTL'); - while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) { - obj = obj[isRTL ? 'previousSibling' : 'nextSibling']; - } - var position = $(obj).offset(); - return [position.left, position.top]; - }, - - /* Retrieve the size of left and top borders for an element. - @param elem (jQuery object) the element of interest - @return (number[2]) the left and top borders */ - _getBorders: function (elem) { - var convert = function (value) { - return { thin: 1, medium: 2, thick: 3}[value] || value; - }; - return [parseFloat(convert(elem.css('border-left-width'))), - parseFloat(convert(elem.css('border-top-width')))]; - }, - - - /* Close time picker if clicked elsewhere. */ - _checkExternalClick: function (event) { - if (!$.timepicker._curInst) { return; } - var $target = $(event.target); - if ($target[0].id != $.timepicker._mainDivId && - $target.parents('#' + $.timepicker._mainDivId).length == 0 && - !$target.hasClass($.timepicker.markerClassName) && - !$target.hasClass($.timepicker._triggerClass) && - $.timepicker._timepickerShowing && !($.timepicker._inDialog && $.blockUI)) - $.timepicker._hideTimepicker(); - }, - - /* Hide the time picker from view. - @param input element - the input field attached to the time picker */ - _hideTimepicker: function (input) { - var inst = this._curInst; - if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; } - if (this._timepickerShowing) { - var showAnim = this._get(inst, 'showAnim'); - var duration = this._get(inst, 'duration'); - var postProcess = function () { - $.timepicker._tidyDialog(inst); - this._curInst = null; - }; - if ($.effects && $.effects[showAnim]) { - inst.tpDiv.hide(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess); - } - else { - inst.tpDiv[(showAnim == 'slideDown' ? 'slideUp' : - (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess); - } - if (!showAnim) { postProcess(); } - var onClose = this._get(inst, 'onClose'); - if (onClose) { - onClose.apply( - (inst.input ? inst.input[0] : null), - [(inst.input ? inst.input.val() : ''), inst]); // trigger custom callback - } - this._timepickerShowing = false; - this._lastInput = null; - if (this._inDialog) { - this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' }); - if ($.blockUI) { - $.unblockUI(); - $('body').append(this.tpDiv); - } - } - this._inDialog = false; - } - }, - - /* Tidy up after a dialog display. */ - _tidyDialog: function (inst) { - inst.tpDiv.removeClass(this._dialogClass).unbind('.ui-timepicker'); - }, - - /* Retrieve the instance data for the target control. - @param target element - the target input field or division or span - @return object - the associated instance data - @throws error if a jQuery problem getting data */ - _getInst: function (target) { - try { - return $.data(target, PROP_NAME); - } - catch (err) { - throw 'Missing instance data for this timepicker'; - } - }, - - /* Get a setting value, defaulting if necessary. */ - _get: function (inst, name) { - return inst.settings[name] !== undefined ? - inst.settings[name] : this._defaults[name]; - }, - - /* Parse existing time and initialise time picker. */ - _setTimeFromField: function (inst) { - if (inst.input.val() == inst.lastVal) { return; } - var defaultTime = this._get(inst, 'defaultTime'); - - - var timeToParse = this._getCurrentTimeRounded(inst); - if (defaultTime != '') { timeToParse = defaultTime } - if ((inst.inline == false) && (inst.input.val() != '')) { timeToParse = inst.input.val() } - - //var timeVal = inst.lastVal = inst.inline == false ? inst.input.val() : - // defaultTime ? defaultTime : - // getHours(new Date()) + timeSeparator + getMinutes(new Date()); - var timeVal = inst.lastVal = timeToParse; - //alert ('inst.input ' + inst.input.attr('id')); - var time = this.parseTime(inst, timeVal); - inst.hours = time.hours; - inst.minutes = time.minutes; - }, - - /* Return the current time, ready to be parsed, rounded to the closest 5 minute */ - _getCurrentTimeRounded: function (inst) { - var currentTime = new Date(); - var timeSeparator = this._get(inst, 'timeSeparator'); - // setting selected time , least priority first - var currentMinutes = currentTime.getMinutes() - // round to closest 5 - currentMinutes = Math.round( currentMinutes / 5 ) * 5; - - return currentTime.getHours().toString() + timeSeparator + currentMinutes.toString(); - }, - - /* - * Pase a time string into hours and minutes - */ - parseTime: function (inst, timeVal) { - var retVal = new Object(); - retVal.hours = -1; - retVal.minutes = -1; - - var timeSeparator = this._get(inst, 'timeSeparator'); - var amPmText = this._get(inst, 'amPmText'); - var p = timeVal.indexOf(timeSeparator); - if (p == -1) { return retVal; } - - retVal.hours = parseInt(timeVal.substr(0, p), 10); - retVal.minutes = parseInt(timeVal.substr(p + 1), 10); - - var showPeriod = (this._get(inst, 'showPeriod') == true); - var timeValUpper = timeVal.toUpperCase(); - if ((retVal.hours < 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[1].toUpperCase()) != -1)) { - retVal.hours += 12; - } - // fix for 12 AM - if ((retVal.hours == 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[0].toUpperCase()) != -1)) { - retVal.hours = 0; - } - - return retVal; - }, - - - - selectHours: function (id, newHours, td, fromDoubleClick) { - var target = $(id); - var inst = this._getInst(target[0]); - $(td).parents('.ui-timepicker-hours:first').find('a').removeClass('ui-state-active'); - //inst.tpDiv.children('.ui-timepicker-hours a').removeClass('ui-state-active'); - $(td).children('a').addClass('ui-state-active'); - - inst.hours = newHours; - this._updateSelectedValue(inst); - - inst._hoursClicked = true; - if ((inst._minutesClicked) || (fromDoubleClick)) { $.timepicker._hideTimepicker(); } - }, - - selectMinutes: function (id, newMinutes, td, fromDoubleClick) { - var target = $(id); - var inst = this._getInst(target[0]); - $(td).parents('.ui-timepicker-minutes:first').find('a').removeClass('ui-state-active'); - $(td).children('a').addClass('ui-state-active'); - - inst.minutes = newMinutes; - this._updateSelectedValue(inst); - - inst._minutesClicked = true; - if ((inst._hoursClicked) || (fromDoubleClick)) { $.timepicker._hideTimepicker(); } - }, - - _updateSelectedValue: function (inst) { - if ((inst.hours < 0) || (inst.hours > 23)) { inst.hours = 12; } - if ((inst.minutes < 0) || (inst.minutes > 59)) { inst.minutes = 0; } - - var period = ""; - var showPeriod = (this._get(inst, 'showPeriod') == true); - var showLeadingZero = (this._get(inst, 'showLeadingZero') == true); - var amPmText = this._get(inst, 'amPmText'); - var selectedHours = inst.hours ? inst.hours : 0; - var selectedMinutes = inst.minutes ? inst.minutes : 0; - - var displayHours = selectedHours; - if ( ! displayHours) { - displayHoyrs = 0; - } - - - if (showPeriod) { - if (inst.hours == 0) { - displayHours = 12; - } - if (inst.hours < 12) { - period = amPmText[0]; - } - else { - period = amPmText[1]; - if (displayHours > 12) { - displayHours -= 12; - } - } - } - - var h = displayHours.toString(); - if (showLeadingZero && (displayHours < 10)) { h = '0' + h; } - - - var m = selectedMinutes.toString(); - if (selectedMinutes < 10) { m = '0' + m; } - - var newTime = h + this._get(inst, 'timeSeparator') + m; - if (period.length > 0) { newTime += " " + period; } - - if (inst.input) { - inst.input.val(newTime); - inst.input.trigger('change'); - } - - var onSelect = this._get(inst, 'onSelect'); - if (onSelect) { onSelect.apply((inst.input ? inst.input[0] : null), [newTime, inst]); } // trigger custom callback - - this._updateAlternate(inst, newTime); - - return newTime; - }, - - /* Update any alternate field to synchronise with the main field. */ - _updateAlternate: function(inst, newTime) { - var altField = this._get(inst, 'altField'); - if (altField) { // update alternate field too - $(altField).each(function() { $(this).val(newTime); }); - } - } - }); - - - - /* Invoke the timepicker functionality. - @param options string - a command, optionally followed by additional parameters or - Object - settings for attaching new datepicker functionality - @return jQuery object */ - $.fn.timepicker = function (options) { - - /* Initialise the date picker. */ - if (!$.timepicker.initialized) { - $(document).mousedown($.timepicker._checkExternalClick). - find('body').append($.timepicker.tpDiv); - $.timepicker.initialized = true; - } - - var otherArgs = Array.prototype.slice.call(arguments, 1); - if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate' || options == 'widget')) - return $.timepicker['_' + options + 'Datepicker']. - apply($.timepicker, [this[0]].concat(otherArgs)); - if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string') - return $.timepicker['_' + options + 'Datepicker']. - apply($.timepicker, [this[0]].concat(otherArgs)); - return this.each(function () { - typeof options == 'string' ? - $.timepicker['_' + options + 'Datepicker']. - apply($.timepicker, [this].concat(otherArgs)) : - $.timepicker._attachTimepicker(this, options); - }); - }; - - /* jQuery extend now ignores nulls! */ - function extendRemove(target, props) { - $.extend(target, props); - for (var name in props) - if (props[name] == null || props[name] == undefined) - target[name] = props[name]; - return target; - }; - - $.timepicker = new Timepicker(); // singleton instance - $.timepicker.initialized = false; - $.timepicker.uuid = new Date().getTime(); - $.timepicker.version = "1.8.6"; - - // Workaround for #4055 - // Add another global to avoid noConflict issues with inline event handlers - window['TP_jQuery_' + tpuuid] = $; - -})(jQuery); - -/* - ____ - ___ .-~. /_"-._ - `-._~-. / /_ "~o\ :Y - \ \ / : \~x. ` ') - ] Y / | Y< ~-.__j - / ! _.--~T : l l< /.-~ - / / ____.--~ . ` l /~\ \<|Y - / / .-~~" /| . ',-~\ \L| - / / / .^ \ Y~Y \.^>/l_ "--' - / Y .-"( . l__ j_j l_/ /~_.-~ . - Y l / \ ) ~~~." / `/"~ / \.__/l_ - | \ _.-" ~-{__ l : l._Z~-.___.--~ - | ~---~ / ~~"---\_ ' __[> - l . _.^ ___ _>-y~ - \ \ . .-~ .-~ ~>--" / - \ ~---" / ./ _.-' - "-.,_____.,_ _.--~\ _.-~ - ~~ ( _} -Row - `. ~( - ) \ - /,`--'~\--'~\ - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ->T-Rex<- -*/ \ No newline at end of file diff --git a/airtime_mvc/public/js/timepicker/jquery.ui.timepicker.js b/airtime_mvc/public/js/timepicker/jquery.ui.timepicker.js new file mode 100644 index 000000000..d086b674b --- /dev/null +++ b/airtime_mvc/public/js/timepicker/jquery.ui.timepicker.js @@ -0,0 +1,1345 @@ +/* + * jQuery UI Timepicker 0.2.9 + * + * Copyright 2010-2011, Francois Gelinas + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://fgelinas.com/code/timepicker + * + * Depends: + * jquery.ui.core.js + * jquery.ui.position.js (only if position settngs are used) + * + * Change version 0.1.0 - moved the t-rex up here + * + ____ + ___ .-~. /_"-._ + `-._~-. / /_ "~o\ :Y + \ \ / : \~x. ` ') + ] Y / | Y< ~-.__j + / ! _.--~T : l l< /.-~ + / / ____.--~ . ` l /~\ \<|Y + / / .-~~" /| . ',-~\ \L| + / / / .^ \ Y~Y \.^>/l_ "--' + / Y .-"( . l__ j_j l_/ /~_.-~ . + Y l / \ ) ~~~." / `/"~ / \.__/l_ + | \ _.-" ~-{__ l : l._Z~-.___.--~ + | ~---~ / ~~"---\_ ' __[> + l . _.^ ___ _>-y~ + \ \ . .-~ .-~ ~>--" / + \ ~---" / ./ _.-' + "-.,_____.,_ _.--~\ _.-~ + ~~ ( _} -Row + `. ~( + ) \ + /,`--'~\--'~\ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ->T-Rex<- +*/ + +(function ($, undefined) { + + $.extend($.ui, { timepicker: { version: "0.2.9"} }); + + var PROP_NAME = 'timepicker'; + var tpuuid = new Date().getTime(); + + /* Time picker manager. + Use the singleton instance of this class, $.timepicker, to interact with the time picker. + Settings for (groups of) time pickers are maintained in an instance object, + allowing multiple different settings on the same page. */ + + function Timepicker() { + this.debug = true; // Change this to true to start debugging + this._curInst = null; // The current instance in use + this._isInline = false; // true if the instance is displayed inline + this._disabledInputs = []; // List of time picker inputs that have been disabled + this._timepickerShowing = false; // True if the popup picker is showing , false if not + this._inDialog = false; // True if showing within a "dialog", false if not + this._dialogClass = 'ui-timepicker-dialog'; // The name of the dialog marker class + this._mainDivId = 'ui-timepicker-div'; // The ID of the main timepicker division + this._inlineClass = 'ui-timepicker-inline'; // The name of the inline marker class + this._currentClass = 'ui-timepicker-current'; // The name of the current hour / minutes marker class + this._dayOverClass = 'ui-timepicker-days-cell-over'; // The name of the day hover marker class + + this.regional = []; // Available regional settings, indexed by language code + this.regional[''] = { // Default regional settings + hourText: 'Hour', // Display text for hours section + minuteText: 'Minute', // Display text for minutes link + amPmText: ['AM', 'PM'], // Display text for AM PM + closeButtonText: 'Done', // Text for the confirmation button (ok button) + nowButtonText: 'Now', // Text for the now button + deselectButtonText: 'Deselect' // Text for the deselect button + }; + this._defaults = { // Global defaults for all the time picker instances + showOn: 'focus', // 'focus' for popup on focus, + // 'button' for trigger button, or 'both' for either (not yet implemented) + button: null, // 'button' element that will trigger the timepicker + showAnim: 'fadeIn', // Name of jQuery animation for popup + showOptions: {}, // Options for enhanced animations + appendText: '', // Display text following the input box, e.g. showing the format + + beforeShow: null, // Define a callback function executed before the timepicker is shown + onSelect: null, // Define a callback function when a hour / minutes is selected + onClose: null, // Define a callback function when the timepicker is closed + + timeSeparator: ':', // The character to use to separate hours and minutes. + periodSeparator: ' ', // The character to use to separate the time from the time period. + showPeriod: false, // Define whether or not to show AM/PM with selected time + showPeriodLabels: true, // Show the AM/PM labels on the left of the time picker + showLeadingZero: true, // Define whether or not to show a leading zero for hours < 10. [true/false] + showMinutesLeadingZero: true, // Define whether or not to show a leading zero for minutes < 10. + altField: '', // Selector for an alternate field to store selected time into + defaultTime: 'now', // Used as default time when input field is empty or for inline timePicker + // (set to 'now' for the current time, '' for no highlighted time) + myPosition: 'left top', // Position of the dialog relative to the input. + // see the position utility for more info : http://jqueryui.com/demos/position/ + atPosition: 'left bottom', // Position of the input element to match + // Note : if the position utility is not loaded, the timepicker will attach left top to left bottom + //NEW: 2011-02-03 + onHourShow: null, // callback for enabling / disabling on selectable hours ex : function(hour) { return true; } + onMinuteShow: null, // callback for enabling / disabling on time selection ex : function(hour,minute) { return true; } + + hours: { + starts: 0, // first displayed hour + ends: 23 // last displayed hour + }, + minutes: { + starts: 0, // first displayed minute + ends: 55, // last displayed minute + interval: 5 // interval of displayed minutes + }, + rows: 4, // number of rows for the input tables, minimum 2, makes more sense if you use multiple of 2 + // 2011-08-05 0.2.4 + showHours: true, // display the hours section of the dialog + showMinutes: true, // display the minute section of the dialog + optionalMinutes: false, // optionally parse inputs of whole hours with minutes omitted + + // buttons + showCloseButton: false, // shows an OK button to confirm the edit + showNowButton: false, // Shows the 'now' button + showDeselectButton: false // Shows the deselect time button + + }; + $.extend(this._defaults, this.regional['']); + + this.tpDiv = $(''); + } + + $.extend(Timepicker.prototype, { + /* Class name added to elements to indicate already configured with a time picker. */ + markerClassName: 'hasTimepicker', + + /* Debug logging (if enabled). */ + log: function () { + if (this.debug) + console.log.apply('', arguments); + }, + + _widgetTimepicker: function () { + return this.tpDiv; + }, + + /* Override the default settings for all instances of the time picker. + @param settings object - the new settings to use as defaults (anonymous object) + @return the manager object */ + setDefaults: function (settings) { + extendRemove(this._defaults, settings || {}); + return this; + }, + + /* Attach the time picker to a jQuery selection. + @param target element - the target input field or division or span + @param settings object - the new settings to use for this time picker instance (anonymous) */ + _attachTimepicker: function (target, settings) { + // check for settings on the control itself - in namespace 'time:' + var inlineSettings = null; + for (var attrName in this._defaults) { + var attrValue = target.getAttribute('time:' + attrName); + if (attrValue) { + inlineSettings = inlineSettings || {}; + try { + inlineSettings[attrName] = eval(attrValue); + } catch (err) { + inlineSettings[attrName] = attrValue; + } + } + } + var nodeName = target.nodeName.toLowerCase(); + var inline = (nodeName == 'div' || nodeName == 'span'); + + if (!target.id) { + this.uuid += 1; + target.id = 'tp' + this.uuid; + } + var inst = this._newInst($(target), inline); + inst.settings = $.extend({}, settings || {}, inlineSettings || {}); + if (nodeName == 'input') { + this._connectTimepicker(target, inst); + // init inst.hours and inst.minutes from the input value + this._setTimeFromField(inst); + } else if (inline) { + this._inlineTimepicker(target, inst); + } + + + }, + + /* Create a new instance object. */ + _newInst: function (target, inline) { + var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars + return { + id: id, input: target, // associated target + inline: inline, // is timepicker inline or not : + tpDiv: (!inline ? this.tpDiv : // presentation div + $('
')) + }; + }, + + /* Attach the time picker to an input field. */ + _connectTimepicker: function (target, inst) { + var input = $(target); + inst.append = $([]); + inst.trigger = $([]); + if (input.hasClass(this.markerClassName)) { return; } + this._attachments(input, inst); + input.addClass(this.markerClassName). + keydown(this._doKeyDown). + keyup(this._doKeyUp). + bind("setData.timepicker", function (event, key, value) { + inst.settings[key] = value; + }). + bind("getData.timepicker", function (event, key) { + return this._get(inst, key); + }); + $.data(target, PROP_NAME, inst); + }, + + /* Handle keystrokes. */ + _doKeyDown: function (event) { + var inst = $.timepicker._getInst(event.target); + var handled = true; + inst._keyEvent = true; + if ($.timepicker._timepickerShowing) { + switch (event.keyCode) { + case 9: $.timepicker._hideTimepicker(); + handled = false; + break; // hide on tab out + case 13: + $.timepicker._updateSelectedValue(inst); + $.timepicker._hideTimepicker(); + + return false; // don't submit the form + break; // select the value on enter + case 27: $.timepicker._hideTimepicker(); + break; // hide on escape + default: handled = false; + } + } + else if (event.keyCode == 36 && event.ctrlKey) { // display the time picker on ctrl+home + $.timepicker._showTimepicker(this); + } + else { + handled = false; + } + if (handled) { + event.preventDefault(); + event.stopPropagation(); + } + }, + + /* Update selected time on keyUp */ + /* Added verion 0.0.5 */ + _doKeyUp: function (event) { + var inst = $.timepicker._getInst(event.target); + $.timepicker._setTimeFromField(inst); + $.timepicker._updateTimepicker(inst); + }, + + /* Make attachments based on settings. */ + _attachments: function (input, inst) { + var appendText = this._get(inst, 'appendText'); + var isRTL = this._get(inst, 'isRTL'); + if (inst.append) { inst.append.remove(); } + if (appendText) { + inst.append = $('' + appendText + ''); + input[isRTL ? 'before' : 'after'](inst.append); + } + input.unbind('focus.timepicker', this._showTimepicker); + if (inst.trigger) { inst.trigger.remove(); } + + var showOn = this._get(inst, 'showOn'); + if (showOn == 'focus' || showOn == 'both') { // pop-up time picker when in the marked field + input.bind("focus.timepicker", this._showTimepicker); + } + if (showOn == 'button' || showOn == 'both') { // pop-up time picker when 'button' element is clicked + var button = this._get(inst, 'button'); + $(button).bind("click.timepicker", function () { + if ($.timepicker._timepickerShowing && $.timepicker._lastInput == input[0]) { $.timepicker._hideTimepicker(); } + else { $.timepicker._showTimepicker(input[0]); } + return false; + }); + + } + }, + + + /* Attach an inline time picker to a div. */ + _inlineTimepicker: function(target, inst) { + var divSpan = $(target); + if (divSpan.hasClass(this.markerClassName)) + return; + divSpan.addClass(this.markerClassName).append(inst.tpDiv). + bind("setData.timepicker", function(event, key, value){ + inst.settings[key] = value; + }).bind("getData.timepicker", function(event, key){ + return this._get(inst, key); + }); + $.data(target, PROP_NAME, inst); + + this._setTimeFromField(inst); + this._updateTimepicker(inst); + inst.tpDiv.show(); + }, + + /* Pop-up the time picker for a given input field. + @param input element - the input field attached to the time picker or + event - if triggered by focus */ + _showTimepicker: function (input) { + input = input.target || input; + if (input.nodeName.toLowerCase() != 'input') { input = $('input', input.parentNode)[0]; } // find from button/image trigger + if ($.timepicker._isDisabledTimepicker(input) || $.timepicker._lastInput == input) { return; } // already here + + // fix v 0.0.8 - close current timepicker before showing another one + $.timepicker._hideTimepicker(); + + var inst = $.timepicker._getInst(input); + if ($.timepicker._curInst && $.timepicker._curInst != inst) { + $.timepicker._curInst.tpDiv.stop(true, true); + } + var beforeShow = $.timepicker._get(inst, 'beforeShow'); + extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {})); + inst.lastVal = null; + $.timepicker._lastInput = input; + + $.timepicker._setTimeFromField(inst); + + // calculate default position + if ($.timepicker._inDialog) { input.value = ''; } // hide cursor + if (!$.timepicker._pos) { // position below input + $.timepicker._pos = $.timepicker._findPos(input); + $.timepicker._pos[1] += input.offsetHeight; // add the height + } + var isFixed = false; + $(input).parents().each(function () { + isFixed |= $(this).css('position') == 'fixed'; + return !isFixed; + }); + if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled + $.timepicker._pos[0] -= document.documentElement.scrollLeft; + $.timepicker._pos[1] -= document.documentElement.scrollTop; + } + + var offset = { left: $.timepicker._pos[0], top: $.timepicker._pos[1] }; + + $.timepicker._pos = null; + // determine sizing offscreen + inst.tpDiv.css({ position: 'absolute', display: 'block', top: '-1000px' }); + $.timepicker._updateTimepicker(inst); + + + // position with the ui position utility, if loaded + if ( ( ! inst.inline ) && ( typeof $.ui.position == 'object' ) ) { + inst.tpDiv.position({ + of: inst.input, + my: $.timepicker._get( inst, 'myPosition' ), + at: $.timepicker._get( inst, 'atPosition' ), + // offset: $( "#offset" ).val(), + // using: using, + collision: 'flip' + }); + var offset = inst.tpDiv.offset(); + $.timepicker._pos = [offset.top, offset.left]; + } + + + // reset clicked state + inst._hoursClicked = false; + inst._minutesClicked = false; + + // fix width for dynamic number of time pickers + // and adjust position before showing + offset = $.timepicker._checkOffset(inst, offset, isFixed); + inst.tpDiv.css({ position: ($.timepicker._inDialog && $.blockUI ? + 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none', + left: offset.left + 'px', top: offset.top + 'px' + }); + if ( ! inst.inline ) { + var showAnim = $.timepicker._get(inst, 'showAnim'); + var duration = $.timepicker._get(inst, 'duration'); + + var postProcess = function () { + $.timepicker._timepickerShowing = true; + var borders = $.timepicker._getBorders(inst.tpDiv); + inst.tpDiv.find('iframe.ui-timepicker-cover'). // IE6- only + css({ left: -borders[0], top: -borders[1], + width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight() + }); + }; + + // Fixed the zIndex problem for real (I hope) - FG - v 0.2.9 + inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1); + + if ($.effects && $.effects[showAnim]) { + inst.tpDiv.show(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess); + } + else { + inst.tpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess); + } + if (!showAnim || !duration) { postProcess(); } + if (inst.input.is(':visible') && !inst.input.is(':disabled')) { inst.input.focus(); } + $.timepicker._curInst = inst; + } + }, + + // This is a copy of the zIndex function of UI core 1.8.?? + // Copied in the timepicker to stay backward compatible. + _getZIndex: function (target) { + var elem = $( target ), position, value; + while ( elem.length && elem[ 0 ] !== document ) { + position = elem.css( "position" ); + if ( position === "absolute" || position === "relative" || position === "fixed" ) { + value = parseInt( elem.css( "zIndex" ), 10 ); + if ( !isNaN( value ) && value !== 0 ) { + return value; + } + } + elem = elem.parent(); + } + }, + + /* Generate the time picker content. */ + _updateTimepicker: function (inst) { + inst.tpDiv.empty().append(this._generateHTML(inst)); + this._rebindDialogEvents(inst); + + }, + + _rebindDialogEvents: function (inst) { + var borders = $.timepicker._getBorders(inst.tpDiv), + self = this; + inst.tpDiv + .find('iframe.ui-timepicker-cover') // IE6- only + .css({ left: -borders[0], top: -borders[1], + width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight() + }) + .end() + // after the picker html is appended bind the click & double click events (faster in IE this way + // then letting the browser interpret the inline events) + // the binding for the minute cells also exists in _updateMinuteDisplay + .find('.ui-timepicker-minute-cell') + .unbind() + .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this)) + .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this)) + .end() + .find('.ui-timepicker-hour-cell') + .unbind() + .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectHours, this)) + .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectHours, this)) + .end() + .find('.ui-timepicker td a') + .unbind() + .bind('mouseout', function () { + $(this).removeClass('ui-state-hover'); + if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).removeClass('ui-timepicker-prev-hover'); + if (this.className.indexOf('ui-timepicker-next') != -1) $(this).removeClass('ui-timepicker-next-hover'); + }) + .bind('mouseover', function () { + if ( ! self._isDisabledTimepicker(inst.inline ? inst.tpDiv.parent()[0] : inst.input[0])) { + $(this).parents('.ui-timepicker-calendar').find('a').removeClass('ui-state-hover'); + $(this).addClass('ui-state-hover'); + if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).addClass('ui-timepicker-prev-hover'); + if (this.className.indexOf('ui-timepicker-next') != -1) $(this).addClass('ui-timepicker-next-hover'); + } + }) + .end() + .find('.' + this._dayOverClass + ' a') + .trigger('mouseover') + .end() + .find('.ui-timepicker-now').bind("click",function(e) { + $.timepicker.selectNow(e); + }).end() + .find('.ui-timepicker-deselect').bind("click",function(e) { + $.timepicker.deselectTime(e); + }).end() + .find('.ui-timepicker-close').bind("click",function(e) { + $.timepicker._hideTimepicker(); + }).end(); + }, + + /* Generate the HTML for the current state of the time picker. */ + _generateHTML: function (inst) { + + var h, m, row, col, html, hoursHtml, minutesHtml = '', + showPeriod = (this._get(inst, 'showPeriod') == true), + showPeriodLabels = (this._get(inst, 'showPeriodLabels') == true), + showLeadingZero = (this._get(inst, 'showLeadingZero') == true), + showHours = (this._get(inst, 'showHours') == true), + showMinutes = (this._get(inst, 'showMinutes') == true), + amPmText = this._get(inst, 'amPmText'), + rows = this._get(inst, 'rows'), + amRows = 0, + pmRows = 0, + amItems = 0, + pmItems = 0, + amFirstRow = 0, + pmFirstRow = 0, + hours = Array(), + hours_options = this._get(inst, 'hours'), + hoursPerRow = null, + hourCounter = 0, + hourLabel = this._get(inst, 'hourText'), + showCloseButton = this._get(inst, 'showCloseButton'), + closeButtonText = this._get(inst, 'closeButtonText'), + showNowButton = this._get(inst, 'showNowButton'), + nowButtonText = this._get(inst, 'nowButtonText'), + showDeselectButton = this._get(inst, 'showDeselectButton'), + deselectButtonText = this._get(inst, 'deselectButtonText'), + showButtonPanel = showCloseButton || showNowButton || showDeselectButton; + + + + // prepare all hours and minutes, makes it easier to distribute by rows + for (h = hours_options.starts; h <= hours_options.ends; h++) { + hours.push (h); + } + hoursPerRow = Math.ceil(hours.length / rows); // always round up + + if (showPeriodLabels) { + for (hourCounter = 0; hourCounter < hours.length; hourCounter++) { + if (hours[hourCounter] < 12) { + amItems++; + } + else { + pmItems++; + } + } + hourCounter = 0; + + amRows = Math.floor(amItems / hours.length * rows); + pmRows = Math.floor(pmItems / hours.length * rows); + + // assign the extra row to the period that is more densly populated + if (rows != amRows + pmRows) { + // Make sure: AM Has Items and either PM Does Not, AM has no rows yet, or AM is more dense + if (amItems && (!pmItems || !amRows || (pmRows && amItems / amRows >= pmItems / pmRows))) { + amRows++; + } else { + pmRows++; + } + } + amFirstRow = Math.min(amRows, 1); + pmFirstRow = amRows + 1; + hoursPerRow = Math.ceil(Math.max(amItems / amRows, pmItems / pmRows)); + } + + + html = ''; + + if (showHours) { + + html += ''; // Close the Hour td + } + + if (showMinutes) { + html += ''; + } + + html += ''; + + + if (showButtonPanel) { + var buttonPanel = ''; + } + html += '
' + + '
' + + hourLabel + + '
' + + ''; + + for (row = 1; row <= rows; row++) { + html += ''; + // AM + if (row == amFirstRow && showPeriodLabels) { + html += ''; + } + // PM + if (row == pmFirstRow && showPeriodLabels) { + html += ''; + } + for (col = 1; col <= hoursPerRow; col++) { + if (showPeriodLabels && row < pmFirstRow && hours[hourCounter] >= 12) { + html += this._generateHTMLHourCell(inst, undefined, showPeriod, showLeadingZero); + } else { + html += this._generateHTMLHourCell(inst, hours[hourCounter], showPeriod, showLeadingZero); + hourCounter++; + } + } + html += ''; + } + html += '
' + amPmText[0] + '' + amPmText[1] + '
' + // Close the hours cells table + '
'; + html += this._generateHTMLMinutes(inst); + html += '
'; + if (showNowButton) { + buttonPanel += ''; + } + if (showDeselectButton) { + buttonPanel += ''; + } + if (showCloseButton) { + buttonPanel += ''; + } + + html += buttonPanel + '
'; + + /* IE6 IFRAME FIX (taken from datepicker 1.5.3, fixed in 0.1.2 */ + html += ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ? + '' : ''); + + return html; + }, + + /* Special function that update the minutes selection in currently visible timepicker + * called on hour selection when onMinuteShow is defined */ + _updateMinuteDisplay: function (inst) { + var newHtml = this._generateHTMLMinutes(inst); + inst.tpDiv.find('td.ui-timepicker-minutes').html(newHtml); + this._rebindDialogEvents(inst); + // after the picker html is appended bind the click & double click events (faster in IE this way + // then letting the browser interpret the inline events) + // yes I know, duplicate code, sorry +/* .find('.ui-timepicker-minute-cell') + .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this)) + .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this)); +*/ + + }, + + /* + * Generate the minutes table + * This is separated from the _generateHTML function because is can be called separately (when hours changes) + */ + _generateHTMLMinutes: function (inst) { + + var m, row, html = '', + rows = this._get(inst, 'rows'), + minutes = Array(), + minutes_options = this._get(inst, 'minutes'), + minutesPerRow = null, + minuteCounter = 0, + showMinutesLeadingZero = (this._get(inst, 'showMinutesLeadingZero') == true), + onMinuteShow = this._get(inst, 'onMinuteShow'), + minuteLabel = this._get(inst, 'minuteText'); + + if ( ! minutes_options.starts) { + minutes_options.starts = 0; + } + if ( ! minutes_options.ends) { + minutes_options.ends = 59; + } + for (m = minutes_options.starts; m <= minutes_options.ends; m += minutes_options.interval) { + minutes.push(m); + } + minutesPerRow = Math.round(minutes.length / rows + 0.49); // always round up + + /* + * The minutes table + */ + // if currently selected minute is not enabled, we have a problem and need to select a new minute. + if (onMinuteShow && + (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours , inst.minutes]) == false) ) { + // loop minutes and select first available + for (minuteCounter = 0; minuteCounter < minutes.length; minuteCounter += 1) { + m = minutes[minuteCounter]; + if (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours, m])) { + inst.minutes = m; + break; + } + } + } + + + + html += '
' + + minuteLabel + + '
' + + ''; + + minuteCounter = 0; + for (row = 1; row <= rows; row++) { + html += ''; + while (minuteCounter < row * minutesPerRow) { + var m = minutes[minuteCounter]; + var displayText = ''; + if (m !== undefined ) { + displayText = (m < 10) && showMinutesLeadingZero ? "0" + m.toString() : m.toString(); + } + html += this._generateHTMLMinuteCell(inst, m, displayText); + minuteCounter++; + } + html += ''; + } + + html += '
'; + + return html; + }, + + /* Generate the content of a "Hour" cell */ + _generateHTMLHourCell: function (inst, hour, showPeriod, showLeadingZero) { + + var displayHour = hour; + if ((hour > 12) && showPeriod) { + displayHour = hour - 12; + } + if ((displayHour == 0) && showPeriod) { + displayHour = 12; + } + if ((displayHour < 10) && showLeadingZero) { + displayHour = '0' + displayHour; + } + + var html = ""; + var enabled = true; + var onHourShow = this._get(inst, 'onHourShow'); //custom callback + + if (hour == undefined) { + html = ' '; + return html; + } + + if (onHourShow) { + enabled = onHourShow.apply((inst.input ? inst.input[0] : null), [hour]); + } + + if (enabled) { + html = '' + + '' + + displayHour.toString() + + ''; + } + else { + html = + '' + + '' + + displayHour.toString() + + '' + + ''; + } + return html; + }, + + /* Generate the content of a "Hour" cell */ + _generateHTMLMinuteCell: function (inst, minute, displayText) { + var html = ""; + var enabled = true; + var onMinuteShow = this._get(inst, 'onMinuteShow'); //custom callback + if (onMinuteShow) { + //NEW: 2011-02-03 we should give the hour as a parameter as well! + enabled = onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours,minute]); //trigger callback + } + + if (minute == undefined) { + html = ' '; + return html; + } + + if (enabled) { + html = '' + + '' + + displayText + + ''; + } + else { + + html = '' + + '' + + displayText + + '' + + ''; + } + return html; + }, + + + /* Enable the date picker to a jQuery selection. + @param target element - the target input field or division or span */ + _enableTimepicker: function(target) { + var $target = $(target), + target_id = $target.attr('id'), + inst = $.data(target, PROP_NAME); + + if (!$target.hasClass(this.markerClassName)) { + return; + } + var nodeName = target.nodeName.toLowerCase(); + if (nodeName == 'input') { + target.disabled = false; + inst.trigger.filter('button'). + each(function() { this.disabled = false; }).end(); + } + else if (nodeName == 'div' || nodeName == 'span') { + var inline = $target.children('.' + this._inlineClass); + inline.children().removeClass('ui-state-disabled'); + } + this._disabledInputs = $.map(this._disabledInputs, + function(value) { return (value == target_id ? null : value); }); // delete entry + }, + + /* Disable the time picker to a jQuery selection. + @param target element - the target input field or division or span */ + _disableTimepicker: function(target) { + var $target = $(target); + var inst = $.data(target, PROP_NAME); + if (!$target.hasClass(this.markerClassName)) { + return; + } + var nodeName = target.nodeName.toLowerCase(); + if (nodeName == 'input') { + target.disabled = true; + + inst.trigger.filter('button'). + each(function() { this.disabled = true; }).end(); + + } + else if (nodeName == 'div' || nodeName == 'span') { + var inline = $target.children('.' + this._inlineClass); + inline.children().addClass('ui-state-disabled'); + } + this._disabledInputs = $.map(this._disabledInputs, + function(value) { return (value == target ? null : value); }); // delete entry + this._disabledInputs[this._disabledInputs.length] = $target.attr('id'); + }, + + /* Is the first field in a jQuery collection disabled as a timepicker? + @param target_id element - the target input field or division or span + @return boolean - true if disabled, false if enabled */ + _isDisabledTimepicker: function (target_id) { + if ( ! target_id) { return false; } + for (var i = 0; i < this._disabledInputs.length; i++) { + if (this._disabledInputs[i] == target_id) { return true; } + } + return false; + }, + + /* Check positioning to remain on screen. */ + _checkOffset: function (inst, offset, isFixed) { + var tpWidth = inst.tpDiv.outerWidth(); + var tpHeight = inst.tpDiv.outerHeight(); + var inputWidth = inst.input ? inst.input.outerWidth() : 0; + var inputHeight = inst.input ? inst.input.outerHeight() : 0; + var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft(); + var viewHeight = document.documentElement.clientHeight + $(document).scrollTop(); + + offset.left -= (this._get(inst, 'isRTL') ? (tpWidth - inputWidth) : 0); + offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0; + offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0; + + // now check if datepicker is showing outside window viewport - move to a better place if so. + offset.left -= Math.min(offset.left, (offset.left + tpWidth > viewWidth && viewWidth > tpWidth) ? + Math.abs(offset.left + tpWidth - viewWidth) : 0); + offset.top -= Math.min(offset.top, (offset.top + tpHeight > viewHeight && viewHeight > tpHeight) ? + Math.abs(tpHeight + inputHeight) : 0); + + return offset; + }, + + /* Find an object's position on the screen. */ + _findPos: function (obj) { + var inst = this._getInst(obj); + var isRTL = this._get(inst, 'isRTL'); + while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) { + obj = obj[isRTL ? 'previousSibling' : 'nextSibling']; + } + var position = $(obj).offset(); + return [position.left, position.top]; + }, + + /* Retrieve the size of left and top borders for an element. + @param elem (jQuery object) the element of interest + @return (number[2]) the left and top borders */ + _getBorders: function (elem) { + var convert = function (value) { + return { thin: 1, medium: 2, thick: 3}[value] || value; + }; + return [parseFloat(convert(elem.css('border-left-width'))), + parseFloat(convert(elem.css('border-top-width')))]; + }, + + + /* Close time picker if clicked elsewhere. */ + _checkExternalClick: function (event) { + if (!$.timepicker._curInst) { return; } + var $target = $(event.target); + if ($target[0].id != $.timepicker._mainDivId && + $target.parents('#' + $.timepicker._mainDivId).length == 0 && + !$target.hasClass($.timepicker.markerClassName) && + !$target.hasClass($.timepicker._triggerClass) && + $.timepicker._timepickerShowing && !($.timepicker._inDialog && $.blockUI)) + $.timepicker._hideTimepicker(); + }, + + /* Hide the time picker from view. + @param input element - the input field attached to the time picker */ + _hideTimepicker: function (input) { + var inst = this._curInst; + if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; } + if (this._timepickerShowing) { + var showAnim = this._get(inst, 'showAnim'); + var duration = this._get(inst, 'duration'); + var postProcess = function () { + $.timepicker._tidyDialog(inst); + this._curInst = null; + }; + if ($.effects && $.effects[showAnim]) { + inst.tpDiv.hide(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess); + } + else { + inst.tpDiv[(showAnim == 'slideDown' ? 'slideUp' : + (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess); + } + if (!showAnim) { postProcess(); } + var onClose = this._get(inst, 'onClose'); + if (onClose) { + onClose.apply( + (inst.input ? inst.input[0] : null), + [(inst.input ? inst.input.val() : ''), inst]); // trigger custom callback + } + this._timepickerShowing = false; + this._lastInput = null; + if (this._inDialog) { + this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' }); + if ($.blockUI) { + $.unblockUI(); + $('body').append(this.tpDiv); + } + } + this._inDialog = false; + } + }, + + + + /* Tidy up after a dialog display. */ + _tidyDialog: function (inst) { + inst.tpDiv.removeClass(this._dialogClass).unbind('.ui-timepicker'); + }, + + /* Retrieve the instance data for the target control. + @param target element - the target input field or division or span + @return object - the associated instance data + @throws error if a jQuery problem getting data */ + _getInst: function (target) { + try { + return $.data(target, PROP_NAME); + } + catch (err) { + throw 'Missing instance data for this timepicker'; + } + }, + + /* Get a setting value, defaulting if necessary. */ + _get: function (inst, name) { + return inst.settings[name] !== undefined ? + inst.settings[name] : this._defaults[name]; + }, + + /* Parse existing time and initialise time picker. */ + _setTimeFromField: function (inst) { + if (inst.input.val() == inst.lastVal) { return; } + var defaultTime = this._get(inst, 'defaultTime'); + + var timeToParse = defaultTime == 'now' ? this._getCurrentTimeRounded(inst) : defaultTime; + if ((inst.inline == false) && (inst.input.val() != '')) { timeToParse = inst.input.val() } + + if (timeToParse instanceof Date) { + inst.hours = timeToParse.getHours(); + inst.minutes = timeToParse.getMinutes(); + } else { + var timeVal = inst.lastVal = timeToParse; + if (timeToParse == '') { + inst.hours = -1; + inst.minutes = -1; + } else { + var time = this.parseTime(inst, timeVal); + inst.hours = time.hours; + inst.minutes = time.minutes; + } + } + + + $.timepicker._updateTimepicker(inst); + }, + + /* Update or retrieve the settings for an existing time picker. + @param target element - the target input field or division or span + @param name object - the new settings to update or + string - the name of the setting to change or retrieve, + when retrieving also 'all' for all instance settings or + 'defaults' for all global defaults + @param value any - the new value for the setting + (omit if above is an object or to retrieve a value) */ + _optionTimepicker: function(target, name, value) { + var inst = this._getInst(target); + if (arguments.length == 2 && typeof name == 'string') { + return (name == 'defaults' ? $.extend({}, $.timepicker._defaults) : + (inst ? (name == 'all' ? $.extend({}, inst.settings) : + this._get(inst, name)) : null)); + } + var settings = name || {}; + if (typeof name == 'string') { + settings = {}; + settings[name] = value; + } + if (inst) { + if (this._curInst == inst) { + this._hideTimepicker(); + } + extendRemove(inst.settings, settings); + this._updateTimepicker(inst); + } + }, + + + /* Set the time for a jQuery selection. + @param target element - the target input field or division or span + @param time String - the new time */ + _setTimeTimepicker: function(target, time) { + var inst = this._getInst(target); + if (inst) { + this._setTime(inst, time); + this._updateTimepicker(inst); + this._updateAlternate(inst, time); + } + }, + + /* Set the time directly. */ + _setTime: function(inst, time, noChange) { + var origHours = inst.hours; + var origMinutes = inst.minutes; + var time = this.parseTime(inst, time); + inst.hours = time.hours; + inst.minutes = time.minutes; + + if ((origHours != inst.hours || origMinutes != inst.minuts) && !noChange) { + inst.input.trigger('change'); + } + this._updateTimepicker(inst); + this._updateSelectedValue(inst); + }, + + /* Return the current time, ready to be parsed, rounded to the closest 5 minute */ + _getCurrentTimeRounded: function (inst) { + var currentTime = new Date(), + currentMinutes = currentTime.getMinutes(), + // round to closest 5 + adjustedMinutes = Math.round( currentMinutes / 5 ) * 5; + currentTime.setMinutes(adjustedMinutes); + return currentTime; + }, + + /* + * Parse a time string into hours and minutes + */ + parseTime: function (inst, timeVal) { + var retVal = new Object(); + retVal.hours = -1; + retVal.minutes = -1; + + var timeSeparator = this._get(inst, 'timeSeparator'), + amPmText = this._get(inst, 'amPmText'), + showHours = this._get(inst, 'showHours'), + showMinutes = this._get(inst, 'showMinutes'), + optionalMinutes = this._get(inst, 'optionalMinutes'), + showPeriod = (this._get(inst, 'showPeriod') == true), + p = timeVal.indexOf(timeSeparator); + + // check if time separator found + if (p != -1) { + retVal.hours = parseInt(timeVal.substr(0, p), 10); + retVal.minutes = parseInt(timeVal.substr(p + 1), 10); + } + // check for hours only + else if ( (showHours) && ( !showMinutes || optionalMinutes ) ) { + retVal.hours = parseInt(timeVal, 10); + } + // check for minutes only + else if ( ( ! showHours) && (showMinutes) ) { + retVal.minutes = parseInt(timeVal, 10); + } + + if (showHours) { + var timeValUpper = timeVal.toUpperCase(); + if ((retVal.hours < 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[1].toUpperCase()) != -1)) { + retVal.hours += 12; + } + // fix for 12 AM + if ((retVal.hours == 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[0].toUpperCase()) != -1)) { + retVal.hours = 0; + } + } + + return retVal; + }, + + selectNow: function(e) { + + var id = $(e.target).attr("data-timepicker-instance-id"), + $target = $(id), + inst = this._getInst($target[0]); + + //if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; } + var currentTime = new Date(); + inst.hours = currentTime.getHours(); + inst.minutes = currentTime.getMinutes(); + this._updateSelectedValue(inst); + this._updateTimepicker(inst); + this._hideTimepicker(); + }, + + deselectTime: function(e) { + var id = $(e.target).attr("data-timepicker-instance-id"), + $target = $(id), + inst = this._getInst($target[0]); + inst.hours = -1; + inst.minutes = -1; + this._updateSelectedValue(inst); + this._hideTimepicker(); + }, + + + selectHours: function (event) { + var $td = $(event.currentTarget), + id = $td.attr("data-timepicker-instance-id"), + newHours = $td.attr("data-hour"), + fromDoubleClick = event.data.fromDoubleClick, + $target = $(id), + inst = this._getInst($target[0]), + showMinutes = (this._get(inst, 'showMinutes') == true); + + // don't select if disabled + if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false } + + $td.parents('.ui-timepicker-hours:first').find('a').removeClass('ui-state-active'); + $td.children('a').addClass('ui-state-active'); + inst.hours = newHours; + + // added for onMinuteShow callback + var onMinuteShow = this._get(inst, 'onMinuteShow'); + if (onMinuteShow) { + // this will trigger a callback on selected hour to make sure selected minute is allowed. + this._updateMinuteDisplay(inst); + } + + this._updateSelectedValue(inst); + + inst._hoursClicked = true; + if ((inst._minutesClicked) || (fromDoubleClick) || (showMinutes == false)) { + $.timepicker._hideTimepicker(); + } + // return false because if used inline, prevent the url to change to a hashtag + return false; + }, + + selectMinutes: function (event) { + var $td = $(event.currentTarget), + id = $td.attr("data-timepicker-instance-id"), + newMinutes = $td.attr("data-minute"), + fromDoubleClick = event.data.fromDoubleClick, + $target = $(id), + inst = this._getInst($target[0]), + showHours = (this._get(inst, 'showHours') == true); + + // don't select if disabled + if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false } + + $td.parents('.ui-timepicker-minutes:first').find('a').removeClass('ui-state-active'); + $td.children('a').addClass('ui-state-active'); + + inst.minutes = newMinutes; + this._updateSelectedValue(inst); + + inst._minutesClicked = true; + if ((inst._hoursClicked) || (fromDoubleClick) || (showHours == false)) { + $.timepicker._hideTimepicker(); + // return false because if used inline, prevent the url to change to a hashtag + return false; + } + + // return false because if used inline, prevent the url to change to a hashtag + return false; + }, + + _updateSelectedValue: function (inst) { + var newTime = this._getParsedTime(inst); + if (inst.input) { + inst.input.val(newTime); + inst.input.trigger('change'); + } + var onSelect = this._get(inst, 'onSelect'); + if (onSelect) { onSelect.apply((inst.input ? inst.input[0] : null), [newTime, inst]); } // trigger custom callback + this._updateAlternate(inst, newTime); + return newTime; + }, + + /* this function process selected time and return it parsed according to instance options */ + _getParsedTime: function(inst) { + + if (inst.hours == -1 && inst.minutes == -1) { + return ''; + } + + if ((inst.hours < 0) || (inst.hours > 23)) { inst.hours = 12; } + if ((inst.minutes < 0) || (inst.minutes > 59)) { inst.minutes = 0; } + + var period = "", + showPeriod = (this._get(inst, 'showPeriod') == true), + showLeadingZero = (this._get(inst, 'showLeadingZero') == true), + showHours = (this._get(inst, 'showHours') == true), + showMinutes = (this._get(inst, 'showMinutes') == true), + optionalMinutes = (this._get(inst, 'optionalMinutes') == true), + amPmText = this._get(inst, 'amPmText'), + selectedHours = inst.hours ? inst.hours : 0, + selectedMinutes = inst.minutes ? inst.minutes : 0, + displayHours = selectedHours ? selectedHours : 0, + parsedTime = ''; + + if (showPeriod) { + if (inst.hours == 0) { + displayHours = 12; + } + if (inst.hours < 12) { + period = amPmText[0]; + } + else { + period = amPmText[1]; + if (displayHours > 12) { + displayHours -= 12; + } + } + } + + var h = displayHours.toString(); + if (showLeadingZero && (displayHours < 10)) { h = '0' + h; } + + var m = selectedMinutes.toString(); + if (selectedMinutes < 10) { m = '0' + m; } + + if (showHours) { + parsedTime += h; + } + if (showHours && showMinutes && (!optionalMinutes || m != 0)) { + parsedTime += this._get(inst, 'timeSeparator'); + } + if (showMinutes && (!optionalMinutes || m != 0)) { + parsedTime += m; + } + if (showHours) { + if (period.length > 0) { parsedTime += this._get(inst, 'periodSeparator') + period; } + } + + return parsedTime; + }, + + /* Update any alternate field to synchronise with the main field. */ + _updateAlternate: function(inst, newTime) { + var altField = this._get(inst, 'altField'); + if (altField) { // update alternate field too + $(altField).each(function(i,e) { + $(e).val(newTime); + }); + } + }, + + /* This might look unused but it's called by the $.fn.timepicker function with param getTime */ + /* added v 0.2.3 - gitHub issue #5 - Thanks edanuff */ + _getTimeTimepicker : function(input) { + var inst = this._getInst(input); + return this._getParsedTime(inst); + }, + _getHourTimepicker: function(input) { + var inst = this._getInst(input); + if ( inst == undefined) { return -1; } + return inst.hours; + }, + _getMinuteTimepicker: function(input) { + var inst= this._getInst(input); + if ( inst == undefined) { return -1; } + return inst.minutes; + } + + }); + + + + /* Invoke the timepicker functionality. + @param options string - a command, optionally followed by additional parameters or + Object - settings for attaching new timepicker functionality + @return jQuery object */ + $.fn.timepicker = function (options) { + + /* Initialise the time picker. */ + if (!$.timepicker.initialized) { + $(document).mousedown($.timepicker._checkExternalClick). + find('body').append($.timepicker.tpDiv); + $.timepicker.initialized = true; + } + + var otherArgs = Array.prototype.slice.call(arguments, 1); + if (typeof options == 'string' && (options == 'getTime' || options == 'getHour' || options == 'getMinute' )) + return $.timepicker['_' + options + 'Timepicker']. + apply($.timepicker, [this[0]].concat(otherArgs)); + if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string') + return $.timepicker['_' + options + 'Timepicker']. + apply($.timepicker, [this[0]].concat(otherArgs)); + return this.each(function () { + typeof options == 'string' ? + $.timepicker['_' + options + 'Timepicker']. + apply($.timepicker, [this].concat(otherArgs)) : + $.timepicker._attachTimepicker(this, options); + }); + }; + + /* jQuery extend now ignores nulls! */ + function extendRemove(target, props) { + $.extend(target, props); + for (var name in props) + if (props[name] == null || props[name] == undefined) + target[name] = props[name]; + return target; + }; + + $.timepicker = new Timepicker(); // singleton instance + $.timepicker.initialized = false; + $.timepicker.uuid = new Date().getTime(); + $.timepicker.version = "0.2.9"; + + // Workaround for #4055 + // Add another global to avoid noConflict issues with inline event handlers + window['TP_jQuery_' + tpuuid] = $; + +})(jQuery);