From fa902db66b11b4400103d2058b1b5c55d9d79f61 Mon Sep 17 00:00:00 2001 From: r-a-y Date: Mon, 2 Nov 2015 17:33:03 -0800 Subject: [PATCH 1/2] Trim whitespace. --- includes/event-organiser-register.php | 86 +++++++++++++-------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/includes/event-organiser-register.php b/includes/event-organiser-register.php index 97f9fbcb..bfabbd44 100644 --- a/includes/event-organiser-register.php +++ b/includes/event-organiser-register.php @@ -14,9 +14,9 @@ function eventorganiser_register_script() { $ext = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min'; $rtl = is_rtl() ? '-rtl' : ''; - /* Moment.js */ + /* Moment.js */ wp_register_script( 'eo_momentjs', EVENT_ORGANISER_URL."js/moment{$ext}.js", '2.9.0', true ); - + /* FullCalendar */ wp_register_script( 'eo_fullcalendar', EVENT_ORGANISER_URL."js/fullcalendar{$ext}.js",array( 'jquery', @@ -25,11 +25,11 @@ function eventorganiser_register_script() { 'jquery-ui-widget', 'jquery-ui-button', ),$version,true); - + /* Google Maps */ $protocal = is_ssl() ? 'https://' : 'http://'; wp_register_script( 'eo_GoogleMap', $protocal.'maps.googleapis.com/maps/api/js?sensor=false&language='.substr( get_locale(), 0, 2 ) ); - + /* Front-end script */ wp_register_script( 'eo_front', EVENT_ORGANISER_URL."js/frontend{$ext}.js",array( 'jquery','eo_qtip2', @@ -40,7 +40,7 @@ function eventorganiser_register_script() { 'eo_fullcalendar', 'eo-wp-js-hooks', ),$version,true); - + /* Add js variables to frontend script */ $category = get_taxonomy( 'event-category' ); $venue = get_taxonomy( 'event-venue' ); @@ -72,14 +72,14 @@ function eventorganiser_register_script() { /* WP-JS-Hooks */ wp_register_script( 'eo-wp-js-hooks', EVENT_ORGANISER_URL."js/event-manager{$ext}.js",array('jquery'),$version,true); - + /* Q-Tip */ wp_register_script( 'eo_qtip2', EVENT_ORGANISER_URL.'js/qtip2.js',array('jquery'),$version,true); /* Styles */ eo_register_style( 'eo_calendar-style', EVENT_ORGANISER_URL . "css/fullcalendar{$ext}.css", array(), $version ); eo_register_style( 'eo_front', EVENT_ORGANISER_URL . "css/eventorganiser-front-end{$rtl}{$ext}.css", array() , $version ); -} +} add_action( 'init', 'eventorganiser_register_script' ); /** @@ -99,25 +99,25 @@ function eventorganiser_register_scripts(){ 'jquery', 'eo_GoogleMap' ),$version,true); - + /* Venue script for venue edit */ wp_register_script( 'eo-venue-admin', EVENT_ORGANISER_URL."js/venue-admin{$ext}.js",array( 'jquery', 'eo-venue-util' ),$version,true); - + /* Script for event edit page. (Legacy version) */ wp_register_script( 'eo-time-picker', EVENT_ORGANISER_URL."js/time-picker{$ext}.js",array( 'jquery', - 'jquery-ui-datepicker', + 'jquery-ui-datepicker', ),$version,true); - + /* New version - prefixed timepicker */ wp_register_script( 'eo-timepicker', EVENT_ORGANISER_URL."js/jquery-ui-eo-timepicker{$ext}.js",array( 'jquery', 'jquery-ui-datepicker', ),$version,true); - + wp_register_script( 'eo_event', EVENT_ORGANISER_URL."js/event{$ext}.js",array( 'jquery', 'jquery-ui-datepicker', @@ -129,7 +129,7 @@ function eventorganiser_register_scripts(){ 'jquery-ui-button', 'jquery-ui-position', ),$version,true); - + wp_register_script( 'eo-edit-event-controller', EVENT_ORGANISER_URL."js/edit-event-controller{$ext}.js",array( 'jquery', 'eo_event', @@ -149,7 +149,7 @@ function eventorganiser_register_scripts(){ /* Pick and register jQuery UI style */ wp_register_style( 'eventorganiser-jquery-ui-style', EVENT_ORGANISER_URL."css/eventorganiser-jquery-ui{$rtl}{$ext}.css", array(), $version ); - + /* Admin styling */ wp_register_style( 'eventorganiser-style', EVENT_ORGANISER_URL."css/eventorganiser-admin-style{$rtl}{$ext}.css", array( 'eventorganiser-jquery-ui-style' ), $version ); @@ -176,9 +176,9 @@ function eventorganiser_cgmp_workaround(){ remove_action( 'wp_head', 'cgmp_google_map_deregister_scripts', 200 ); } add_action( 'wp_head', 'eventorganiser_cgmp_workaround', 1 ); - + /** - * Check the export and event creation (from Calendar view) actions. + * Check the export and event creation (from Calendar view) actions. * These cannot be called later. Most other actions are only called when * the appropriate page is loading. * @@ -206,7 +206,7 @@ function eventorganiser_public_export(){ add_action('init','eventorganiser_public_export'); /** - * Queues up the javascript / style scripts for Events custom page type + * Queues up the javascript / style scripts for Events custom page type * Hooked onto admin_enqueue_scripts * * @since 1.0.0 @@ -217,10 +217,10 @@ function eventorganiser_add_admin_scripts( $hook ) { global $post,$current_screen,$wp_locale; if ( $hook == 'post-new.php' || $hook == 'post.php') { - if( $post->post_type == 'event' ) { + if( $post->post_type == 'event' ) { wp_enqueue_script( 'eo-edit-event-controller' ); - wp_localize_script( 'eo_event', 'EO_Ajax_Event', array( + wp_localize_script( 'eo_event', 'EO_Ajax_Event', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'wpversion' => get_bloginfo( 'version' ), 'startday' => intval( get_option( 'start_of_week' ) ), @@ -278,7 +278,7 @@ function eventorganiser_add_admin_scripts( $hook ) { /** - * Perform database and WP version checks. Display appropriate error messages. + * Perform database and WP version checks. Display appropriate error messages. * Triggered on update. * * @since 1.4.0 @@ -314,7 +314,7 @@ function eventorganiser_db_checks(){

Event Organiser requires WordPress 3.8 to function properly. Your version is .

- %s ', @@ -369,7 +369,7 @@ function eventorganiser_plugin_settings_link($links, $file) { } return $links; } -add_filter('plugin_action_links', 'eventorganiser_plugin_settings_link', 10, 2); +add_filter('plugin_action_links', 'eventorganiser_plugin_settings_link', 10, 2); /** * Schedules cron job for automatically deleting expired events @@ -423,35 +423,35 @@ function eventorganiser_get_next_cron_time( $cron_name ){ function eventorganiser_delete_expired_events(){ //Get expired events $events = eo_get_events(array('showrepeats'=>0,'showpastevents'=>1,'eo_interval'=>'expired')); - + /** * Filters how long (in seconds) after an event as finished it should be considered expired. - * + * * If enabled in *Settings > Event Organiser > General*, expired events are trashed. - * - * @param int $time_until_expired Time (in seconds) to wait after an event has finished. Defaults to 24 hours. + * + * @param int $time_until_expired Time (in seconds) to wait after an event has finished. Defaults to 24 hours. */ $time_until_expired = (int) apply_filters( 'eventorganiser_events_expire_time', 24*60*60 ); $time_until_expired = max( $time_until_expired, 0 ); if($events): $now = new DateTime('now', eo_get_blog_timezone()); - + foreach($events as $event): - + $start = eo_get_the_start( DATETIMEOBJ, $event->ID, null, $event->occurrence_id ); $end = eo_get_the_end( DATETIMEOBJ, $event->ID, null, $event->occurrence_id ); - + $expired = round(abs($end->format('U')-$start->format('U'))) + $time_until_expired; //Duration + expire time - + $finished = eo_get_schedule_last( DATETIMEOBJ, $event->ID ); $finished->modify("+$expired seconds");//[Expired time] after the last occurrence finishes - + //Delete if [expired time] has passed if( $finished <= $now ){ wp_trash_post((int) $event->ID); } - + endforeach; endif; } @@ -492,7 +492,7 @@ function eventorganiser_admin_print_scripts() { add_action( 'admin_print_styles', 'eventorganiser_admin_print_scripts' ); -/** +/** * Purge the occurrences cache * Hooked onto eventorganiser_save_event and eventorganiser_delete_event * @@ -533,7 +533,7 @@ function _eventorganiser_delete_calendar_cache() { $hooks = array( 'eventorganiser_save_event', 'eventorganiser_delete_event', 'wp_trash_post','update_option_gmt_offset', /* obvious */ 'update_option_start_of_week', /* Start of week is used for calendars */ - 'update_option_rewrite_rules', /* If permalinks updated - links on fullcalendar might now be invalid */ + 'update_option_rewrite_rules', /* If permalinks updated - links on fullcalendar might now be invalid */ 'delete_option_rewrite_rules', 'update_option_siteurl', 'update_option_home', @@ -588,16 +588,16 @@ function eventorganiser_pointer_load( $hook_suffix ) { } //No valid pointers? Stop here. - if( empty($valid_pointers) ) + if( empty($valid_pointers) ) return; - // Add pointers style to queue. + // Add pointers style to queue. wp_enqueue_style( 'wp-pointer' ); - + // Add pointers script to queue. Add custom script. wp_enqueue_script('eventorganiser-pointer',EVENT_ORGANISER_URL.'js/eventorganiser-pointer.js',array('wp-pointer','eo_event')); - // Add pointer options to script. + // Add pointer options to script. wp_localize_script('eventorganiser-pointer', 'eventorganiserPointer', $valid_pointers); } add_action( 'admin_enqueue_scripts', 'eventorganiser_pointer_load',99999); @@ -630,7 +630,7 @@ function _eventorganiser_upgrade_admin_notice() { $message = sprintf( "

" . sprintf( esc_html__('Your site timezone is %s','eventorganiser'), "{$tzstring}" ). '

' - ."

" . __( "Is this correct? Using a fixed offset from UTC may cause unexpected behaviour, particular if you observe Daylight Savings Time.", 'eventorganiser' ) . '
' + ."

" . __( "Is this correct? Using a fixed offset from UTC may cause unexpected behaviour, particular if you observe Daylight Savings Time.", 'eventorganiser' ) . '
' .__( "It is strongly recommended you select a city instead, even if you don't observe Daylight Savings Time.",'eventorganiser') . '

' ."

" . __( "You can change your timezone settings here.",'eventorganiser') . '

', admin_url( 'options-general.php' ).'#default_role' @@ -645,7 +645,7 @@ function _eventorganiser_upgrade_admin_notice() { * Handles city auto-fill request. * * Hooked onto admin_post_eo-autofillcity. Triggered when user clicks 'autofill' link. - * This routine goes through all the venues, reverse geocodes to find their city and + * This routine goes through all the venues, reverse geocodes to find their city and * autofills the city field (added in 1.7). * *@ignore @@ -671,7 +671,7 @@ function _eventorganiser_autofill_city(){ If( eo_get_venue_meta($venue_id,'_city',true) ) continue; - $response=wp_remote_get("http://open.mapquestapi.com/nominatim/v1/reverse?format=json&lat={$lat}&lon={$lng}&osm_type=N&limit=1"); + $response=wp_remote_get("http://open.mapquestapi.com/nominatim/v1/reverse?format=json&lat={$lat}&lon={$lng}&osm_type=N&limit=1"); $geo = json_decode(wp_remote_retrieve_body( $response )); if( isset($geo->address->city) ){ $cities[$venue_id] = $geo->address->city; @@ -689,7 +689,7 @@ function _eventorganiser_autofill_city(){ eo_update_venue_meta($venue_id, '_state', $geo->address->state); } } - } + } wp_safe_redirect(admin_url('edit.php?post_type=event&page=venues')); } From fbf85fae321ece856a6a9339f547e4533de3e67e Mon Sep 17 00:00:00 2001 From: r-a-y Date: Mon, 2 Nov 2015 20:41:51 -0800 Subject: [PATCH 2/2] Use Datepair.js and jquery-timepicker for selecting time ranges. This replaces the existing time range selection solution, which uses jQuery UI Timepicker, with Datepair.js and jquery-timepicker. Datepair.js automatically updates the end time when the start time is changed. This makes for a more user-friendly experience. --- css/jquery.timepicker.css | 72 ++ includes/event-organiser-register.php | 36 +- js/datepair.js | 353 ++++++ js/event.js | 91 +- js/jquery-ui-eo-timepicker.js | 1588 ------------------------- js/jquery.datepair.js | 46 + js/jquery.timepicker.js | 1197 +++++++++++++++++++ 7 files changed, 1718 insertions(+), 1665 deletions(-) create mode 100644 css/jquery.timepicker.css create mode 100644 js/datepair.js delete mode 100644 js/jquery-ui-eo-timepicker.js create mode 100644 js/jquery.datepair.js create mode 100644 js/jquery.timepicker.js diff --git a/css/jquery.timepicker.css b/css/jquery.timepicker.css new file mode 100644 index 00000000..cd75f13f --- /dev/null +++ b/css/jquery.timepicker.css @@ -0,0 +1,72 @@ +.ui-timepicker-wrapper { + overflow-y: auto; + height: 150px; + width: 6.5em; + background: #fff; + border: 1px solid #ddd; + -webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2); + -moz-box-shadow:0 5px 10px rgba(0,0,0,0.2); + box-shadow:0 5px 10px rgba(0,0,0,0.2); + outline: none; + z-index: 10001; + margin: 0; +} + +.ui-timepicker-wrapper.ui-timepicker-with-duration { + width: 13em; +} + +.ui-timepicker-wrapper.ui-timepicker-with-duration.ui-timepicker-step-30, +.ui-timepicker-wrapper.ui-timepicker-with-duration.ui-timepicker-step-60 { + width: 11em; +} + +.ui-timepicker-list { + margin: 0; + padding: 0; + list-style: none; +} + +.ui-timepicker-duration { + margin-left: 5px; color: #888; +} + +.ui-timepicker-list:hover .ui-timepicker-duration { + color: #888; +} + +.ui-timepicker-list li { + padding: 3px 0 3px 5px; + cursor: pointer; + white-space: nowrap; + color: #000; + list-style: none; + margin: 0; +} + +.ui-timepicker-list:hover .ui-timepicker-selected { + background: #fff; color: #000; +} + +li.ui-timepicker-selected, +.ui-timepicker-list li:hover, +.ui-timepicker-list .ui-timepicker-selected:hover { + background: #1980EC; color: #fff; +} + +li.ui-timepicker-selected .ui-timepicker-duration, +.ui-timepicker-list li:hover .ui-timepicker-duration { + color: #ccc; +} + +.ui-timepicker-list li.ui-timepicker-disabled, +.ui-timepicker-list li.ui-timepicker-disabled:hover, +.ui-timepicker-list li.ui-timepicker-selected.ui-timepicker-disabled { + color: #888; + cursor: default; +} + +.ui-timepicker-list li.ui-timepicker-disabled:hover, +.ui-timepicker-list li.ui-timepicker-selected.ui-timepicker-disabled { + background: #f2f2f2; +} diff --git a/includes/event-organiser-register.php b/includes/event-organiser-register.php index bfabbd44..b497eac2 100644 --- a/includes/event-organiser-register.php +++ b/includes/event-organiser-register.php @@ -106,23 +106,33 @@ function eventorganiser_register_scripts(){ 'eo-venue-util' ),$version,true); - /* Script for event edit page. (Legacy version) */ - wp_register_script( 'eo-time-picker', EVENT_ORGANISER_URL."js/time-picker{$ext}.js",array( + /** + * jquery-timepicker + * + * @license MIT + * @link https://github.com/jonthornton/jquery-timepicker + */ + wp_register_script( 'eo-timepicker', EVENT_ORGANISER_URL . "js/jquery.timepicker{$ext}.js", array( 'jquery', - 'jquery-ui-datepicker', - ),$version,true); + ), $version, true ); - /* New version - prefixed timepicker */ - wp_register_script( 'eo-timepicker', EVENT_ORGANISER_URL."js/jquery-ui-eo-timepicker{$ext}.js",array( + /** + * Datepair.js + * + * @license MIT + * @link https://github.com/jonthornton/Datepair.js + */ + wp_register_script( 'eo-datepair', EVENT_ORGANISER_URL . "js/datepair{$ext}.js", array(), $version, true ); + wp_register_script( 'eo-jquery-datepair', EVENT_ORGANISER_URL . "js/jquery.datepair{$ext}.js", array( + 'eo-datepair', 'jquery', - 'jquery-ui-datepicker', - ),$version,true); + ), $version, true ); wp_register_script( 'eo_event', EVENT_ORGANISER_URL."js/event{$ext}.js",array( 'jquery', 'jquery-ui-datepicker', 'eo-timepicker', - 'eo-time-picker',//Deprecated remove in 3.0? + 'eo-jquery-datepair', 'eo-venue-util', 'jquery-ui-autocomplete', 'jquery-ui-widget', @@ -150,8 +160,14 @@ function eventorganiser_register_scripts(){ /* Pick and register jQuery UI style */ wp_register_style( 'eventorganiser-jquery-ui-style', EVENT_ORGANISER_URL."css/eventorganiser-jquery-ui{$rtl}{$ext}.css", array(), $version ); + // Timepicker CSS + wp_register_style( 'eo-timepicker', EVENT_ORGANISER_URL . "css/jquery.timepicker{$ext}.css", array(), $version ); + /* Admin styling */ - wp_register_style( 'eventorganiser-style', EVENT_ORGANISER_URL."css/eventorganiser-admin-style{$rtl}{$ext}.css", array( 'eventorganiser-jquery-ui-style' ), $version ); + wp_register_style( 'eventorganiser-style', EVENT_ORGANISER_URL."css/eventorganiser-admin-style{$rtl}{$ext}.css", array( + 'eventorganiser-jquery-ui-style', + 'eo-timepicker' + ), $version ); /* Inline Help */ wp_register_script( 'eo-inline-help', EVENT_ORGANISER_URL.'js/inline-help.js',array( 'jquery', 'eo_qtip2' ), $version, true ); diff --git a/js/datepair.js b/js/datepair.js new file mode 100644 index 00000000..14a2d463 --- /dev/null +++ b/js/datepair.js @@ -0,0 +1,353 @@ +/*! + * datepair.js v0.4.12 - A javascript plugin for intelligently selecting date and time ranges inspired by Google Calendar. + * Copyright (c) 2015 Jon Thornton - http://jonthornton.github.com/Datepair.js + * License: MIT + */ + +(function(window, document) { + + 'use strict'; + + var _ONE_DAY = 86400000; + var jq = window.Zepto || window.jQuery; + + function simpleExtend(obj1, obj2) { + var out = obj2 || {}; + + for (var i in obj1) { + if (!(i in out)) { + out[i] = obj1[i]; + } + } + + return out; + } + + // IE's custom event support is totally borked. + // Use jQuery if possible + function triggerSimpleCustomEvent(el, eventName) { + if (jq) { + jq(el).trigger(eventName); + } else { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent(eventName, true, true, {}); + el.dispatchEvent(event); + } + } + + // el.classList not supported by < IE10 + // use jQuery if available + function hasClass(el, className) { + if (jq) { + return jq(el).hasClass(className); + } else { + return el.classList.contains(className); + } + } + + function Datepair(container, options) { + this.dateDelta = null; + this.timeDelta = null; + this._defaults = { + startClass: 'start', + endClass: 'end', + timeClass: 'time', + dateClass: 'date', + defaultDateDelta: 0, + defaultTimeDelta: 3600000, + anchor: 'start', + + // defaults for jquery-timepicker; override when using other input widgets + parseTime: function(input){ + return jq(input).timepicker('getTime'); + }, + updateTime: function(input, dateObj){ + jq(input).timepicker('setTime', dateObj); + }, + setMinTime: function(input, dateObj){ + jq(input).timepicker('option', 'minTime', dateObj); + }, + + // defaults for bootstrap datepicker; override when using other input widgets + parseDate: function(input){ + return input.value && jq(input).datepicker('getDate'); + }, + updateDate: function(input, dateObj){ + jq(input).datepicker('update', dateObj); + } + }; + + this.container = container; + this.settings = simpleExtend(this._defaults, options); + + this.startDateInput = this.container.querySelector('.'+this.settings.startClass+'.'+this.settings.dateClass); + this.endDateInput = this.container.querySelector('.'+this.settings.endClass+'.'+this.settings.dateClass); + this.startTimeInput = this.container.querySelector('.'+this.settings.startClass+'.'+this.settings.timeClass); + this.endTimeInput = this.container.querySelector('.'+this.settings.endClass+'.'+this.settings.timeClass); + + // initialize date and time deltas + this.refresh(); + this._updateEndMintime(); + + // init starts here + this._bindChangeHandler(); + } + + Datepair.prototype = { + constructor: Datepair, + + option: function(key, value) + { + if (typeof key == 'object') { + this.settings = simpleExtend(this.settings, key); + + } else if (typeof key == 'string' && typeof value != 'undefined') { + this.settings[key] = value; + + } else if (typeof key == 'string') { + return this.settings[key]; + } + + this._updateEndMintime(); + }, + + getTimeDiff: function() + { + // due to the fact that times can wrap around, timeDiff for any + // time-only pair will always be >= 0 + var delta = this.dateDelta + this.timeDelta; + if (delta < 0 && (!this.startDateInput || !this.endDateInput) ) { + delta += _ONE_DAY; + } + + return delta; + }, + + refresh: function() + { + if (this.startDateInput && this.startDateInput.value && this.endDateInput && this.endDateInput.value) { + var startDate = this.settings.parseDate(this.startDateInput); + var endDate = this.settings.parseDate(this.endDateInput); + if (startDate && endDate) { + this.dateDelta = endDate.getTime() - startDate.getTime(); + } + } + if (this.startTimeInput && this.startTimeInput.value && this.endTimeInput && this.endTimeInput.value) { + var startTime = this.settings.parseTime(this.startTimeInput); + var endTime = this.settings.parseTime(this.endTimeInput); + if (startTime && endTime) { + this.timeDelta = endTime.getTime() - startTime.getTime(); + } + } + }, + + remove: function() + { + this._unbindChangeHandler(); + }, + + _bindChangeHandler: function(){ + // addEventListener doesn't work with synthetic "change" events + // fired by jQuery's trigger() functioin. If jQuery is present, + // use that for event binding + if (jq) { + jq(this.container).on('change.datepair', jq.proxy(this.handleEvent, this)); + } else { + this.container.addEventListener('change', this, false); + } + }, + + _unbindChangeHandler: function(){ + if (jq) { + jq(this.container).off('change.datepair'); + } else { + this.container.removeEventListener('change', this, false); + } + }, + + // This function will be called when passing 'this' to addEventListener + handleEvent: function(e){ + // temporarily unbind the change handler to prevent triggering this + // if we update other inputs + this._unbindChangeHandler(); + + if (hasClass(e.target, this.settings.dateClass)) { + if (e.target.value != '') { + this._dateChanged(e.target); + this._timeChanged(e.target); + } else { + this.dateDelta = null; + } + + } else if (hasClass(e.target, this.settings.timeClass)) { + if (e.target.value != '') { + this._timeChanged(e.target); + } else { + this.timeDelta = null; + } + } + + this._validateRanges(); + this._updateEndMintime(); + this._bindChangeHandler(); + }, + + _dateChanged: function(target){ + if (!this.startDateInput || !this.endDateInput) { + return; + } + + var startDate = this.settings.parseDate(this.startDateInput); + var endDate = this.settings.parseDate(this.endDateInput); + + if (!startDate || !endDate) { + if (this.settings.defaultDateDelta !== null) { + if (startDate) { + var newEnd = new Date(startDate.getTime() + this.settings.defaultDateDelta * _ONE_DAY); + this.settings.updateDate(this.endDateInput, newEnd); + + } else if (endDate) { + var newStart = new Date(endDate.getTime() - this.settings.defaultDateDelta * _ONE_DAY); + this.settings.updateDate(this.startDateInput, newStart); + } + + this.dateDelta = this.settings.defaultDateDelta * _ONE_DAY; + } else { + this.dateDelta = null; + } + + return; + } + + if (this.settings.anchor == 'start' && hasClass(target, this.settings.startClass)) { + var newDate = new Date(startDate.getTime() + this.dateDelta); + this.settings.updateDate(this.endDateInput, newDate); + } else if (this.settings.anchor == 'end' && hasClass(target, this.settings.endClass)) { + var newDate = new Date(endDate.getTime() - this.dateDelta); + this.settings.updateDate(this.startDateInput, newDate); + } else { + if (endDate < startDate) { + var otherInput = hasClass(target, this.settings.startClass) ? this.endDateInput : this.startDateInput; + var selectedDate = this.settings.parseDate(target); + this.dateDelta = 0; + this.settings.updateDate(otherInput, selectedDate); + } else { + this.dateDelta = endDate.getTime() - startDate.getTime(); + } + } + }, + + _timeChanged: function(target){ + if (!this.startTimeInput || !this.endTimeInput) { + return; + } + + var startTime = this.settings.parseTime(this.startTimeInput); + var endTime = this.settings.parseTime(this.endTimeInput); + + if (!startTime || !endTime) { + if (this.settings.defaultTimeDelta !== null) { + if (startTime) { + var newEnd = new Date(startTime.getTime() + this.settings.defaultTimeDelta); + this.settings.updateTime(this.endTimeInput, newEnd); + } else if (endTime) { + var newStart = new Date(endTime.getTime() - this.settings.defaultTimeDelta); + this.settings.updateTime(this.startTimeInput, newStart); + } + + this.timeDelta = this.settings.defaultTimeDelta; + } else { + this.timeDelta = null; + } + + return; + } + + if (this.settings.anchor == 'start' && hasClass(target, this.settings.startClass)) { + var newTime = new Date(startTime.getTime() + this.timeDelta); + this.settings.updateTime(this.endTimeInput, newTime); + endTime = this.settings.parseTime(this.endTimeInput); + + this._doMidnightRollover(startTime, endTime); + } else if (this.settings.anchor == 'end' && hasClass(target, this.settings.endClass)) { + var newTime = new Date(endTime.getTime() - this.timeDelta); + this.settings.updateTime(this.startTimeInput, newTime); + startTime = this.settings.parseTime(this.startTimeInput); + + this._doMidnightRollover(startTime, endTime); + } else { + this._doMidnightRollover(startTime, endTime); + + var startDate = this.settings.parseDate(this.startDateInput); + var endDate = this.settings.parseDate(this.endDateInput); + if ((+startDate == +endDate) && (endTime < startTime)) { + var thisInput = hasClass(target, this.settings.endClass) ? this.endTimeInput : this.startTimeInput; + var otherInput = hasClass(target, this.settings.startClass) ? this.endTimeInput : this.startTimeInput; + var selectedTime = this.settings.parseTime(thisInput); + this.timeDelta = 0; + this.settings.updateTime(otherInput, selectedTime); + } else { + this.timeDelta = endTime.getTime() - startTime.getTime(); + } + } + + + }, + + _doMidnightRollover: function(startTime, endTime) { + var endDate = this.settings.parseDate(this.endDateInput); + var startDate = this.settings.parseDate(this.startDateInput); + var newDelta = endTime.getTime() - startTime.getTime(); + var offset = (endTime < startTime) ? _ONE_DAY : -1 * _ONE_DAY; + + if (this.dateDelta !== null + && this.dateDelta + this.timeDelta <= _ONE_DAY + && this.dateDelta + newDelta != 0 + && (offset > 0 || this.dateDelta != 0) + && ((newDelta >= 0 && this.timeDelta < 0) || (newDelta < 0 && this.timeDelta >= 0))) { + + if (this.settings.anchor == 'start') { + this.settings.updateDate(this.endDateInput, new Date(endDate.getTime() + offset)); + this._dateChanged(this.endDateInput); + } else if (this.settings.anchor == 'end') { + this.settings.updateDate(this.startDateInput, new Date(startDate.getTime() - offset)); + this._dateChanged(this.startDateInput); + } + } + this.timeDelta = newDelta; + }, + + _updateEndMintime: function(){ + if (typeof this.settings.setMinTime != 'function') return; + + var baseTime = null; + if (this.settings.anchor == 'start' && (!this.dateDelta || this.dateDelta < _ONE_DAY || (this.timeDelta && this.dateDelta + this.timeDelta < _ONE_DAY))) { + baseTime = this.settings.parseTime(this.startTimeInput); + } + + this.settings.setMinTime(this.endTimeInput, baseTime); + }, + + _validateRanges: function(){ + if (this.startTimeInput && this.endTimeInput && this.timeDelta === null) { + triggerSimpleCustomEvent(this.container, 'rangeIncomplete'); + return; + } + + if (this.startDateInput && this.endDateInput && this.dateDelta === null) { + triggerSimpleCustomEvent(this.container, 'rangeIncomplete'); + return; + } + + // due to the fact that times can wrap around, any time-only pair will be considered valid + if (!this.startDateInput || !this.endDateInput || this.dateDelta + this.timeDelta >= 0) { + triggerSimpleCustomEvent(this.container, 'rangeSelected'); + } else { + triggerSimpleCustomEvent(this.container, 'rangeError'); + } + } + }; + + window.Datepair = Datepair; + +}(window, document)); \ No newline at end of file diff --git a/js/event.js b/js/event.js index 9d309f7c..9e508917 100644 --- a/js/event.js +++ b/js/event.js @@ -191,6 +191,7 @@ window.eventOrganiserSchedulePicker = { this.schedule = options.schedule; this.set_up_datepickers(); this.set_up_timepickers(); + this.set_up_datepair(); //On input, update form $(".event-date :input, .eo-all-day-toggle").change(function(o) { @@ -219,8 +220,8 @@ window.eventOrganiserSchedulePicker = { var locale = this.options.locale; //Init data - var start = $(views.start_date).data('eo-datepicker','start'); - var end = $(views.end_date).data('eo-datepicker','end'); + var start = $(views.start_date).addClass( 'start' ).data('eo-datepicker','start'); + var end = $(views.end_date).addClass( 'end' ).data('eo-datepicker','end'); $(views.is_all_day).addClass('eo-all-day-toggle'); //Date pickers @@ -271,9 +272,11 @@ window.eventOrganiserSchedulePicker = { var startDate = $( views.start_date ).datepicker( 'getDate' ); var endDate = $( views.end_date ).datepicker( 'getDate' ); + + // @todo what is this used for? if( startDate.getTime() != endDate.getTime() ){ var time = { hour: null, minute: null }; - $( views.start_time + ", " + views.end_time ).eotimepicker('option', { maxTime: time, minTime: time }); + //$( views.start_time + ", " + views.end_time ).eotimepicker('option', { maxTime: time, minTime: time }); } //Replace with do_action @@ -365,72 +368,26 @@ window.eventOrganiserSchedulePicker = { }, set_up_timepickers: function(){ - - - var options = this.options; var views = this.options.views; - //Time pickers - $( views.start_time ).data( 'eo-event-data', 'start-time' ); - $( views.end_time ).data( 'eo-event-data', 'end-time' ); - $( views.start_time + ', ' + views.end_time).eotimepicker({ - showPeriodLabels: !options.is24hour, - showPeriod: !this.options.is24hour, - showLeadingZero: options.is24hour, - periodSeparator: '', - amPmText: options.locale.meridian, - hourText: options.locale.hour, - minuteText: options.locale.minute, - isRTL: options.locale.isrtl, - onSelect: function( timeString, timePickerInst ){ - var startDate = $( views.start_date ).datepicker( 'getDate' ); - var endDate = $( views.end_date ).datepicker( 'getDate' ); - - //If dates are the same update timepicker... - if( startDate.getTime() == endDate.getTime() ){ - - var time = { - hour: timePickerInst.hours, - minute: timePickerInst.minutes, - }; - - if( 'start-time' == $( timePickerInst.input ).data( 'eo-event-data' ) ){ - //We have selected start time, update end time - $( views.end_time ).eotimepicker('option', { minTime: time }); - - /* - var endTime = { - hour: $( views.end_time ).eotimepicker( 'getHour' ), - minute: $( views.end_time ).eotimepicker( 'getMinute' ), - }; - if( time.hour > endTime.hour || ( ( time.hour = endTime.hour ) && time.minute > endTime.minute ) ){ - //Correct end: - var newEndTime = new Date('2012-11-24 ' + endTime.hour +":"+endTime.minute ); - $( views.end_time ).eotimepicker( 'setTime', newEndTime ); - }*/ - - }else{ - $( views.start_time ).eotimepicker('option', { maxTime: time }); - /* - var startTime = { - hour: $( views.end_time ).eotimepicker( 'getHour' ), - minute: $( views.end_time ).eotimepicker( 'getMinute' ), - }; - - if( time.hour < startTime.hour || ( ( time.hour = startTime.hour ) && time.minute < startTime.minute ) ){ - console.log( 'end before start!'); - //Correct start: - var newStartTime = new Date('2012-11-24 ' + startTime.hour +":"+startTime.minute ); - $( views.end_time ).eotimepicker( 'setTime', newStartTime ); - } - */ - } - - } - - } - }).addClass('eo-time-picker'); + + $( views.start_time ).addClass( 'start' ).data( 'eo-event-data', 'start-time' ); + $( views.end_time ).addClass( 'end' ).data( 'eo-event-data', 'end-time' ); + + $( '#eventorganiser_detail .eo_time' ).timepicker({ + 'showDuration': true, + }).addClass('eo-time-picker'); }, - + + set_up_datepair: function() { + $( '#eventorganiser_detail' ).datepair({ + 'startClass': 'start', + 'endClass': 'end', + 'timeClass': 'eo_time', + 'dateClass': 'hasDatepicker', + 'defaultTimeDelta': 1800000 // default to 30 min. increments + }); + }, + /** * @this; */ diff --git a/js/jquery-ui-eo-timepicker.js b/js/jquery-ui-eo-timepicker.js deleted file mode 100644 index 1730d0b8..00000000 --- a/js/jquery-ui-eo-timepicker.js +++ /dev/null @@ -1,1588 +0,0 @@ -/* - * Forked from jQuery UI Timepicker 0.3.3. - * - * Changes: - * * Prepend an 'eo' prefix to prevent namespace clashes. - * * Add href="#" to hour/minute anchor tags - * * Keyboard navigation of hours & minutes -*/ -/* - * jQuery UI Timepicker 0.3.3 - * - * Copyright 2010-2013, Francois Gelinas - * Dual licensed under the MIT or GPL Version 2 licenses. - * https://jquery.org/license - * - * http://fgelinas.com/code/timepicker - * - * Depends: - * jquery.ui.core.js - * jquery.ui.position.js (only if position settings are used) - * - * Change version 0.1.0 - moved the t-rex up here - * -*/ - -(function ($) { - - $.extend($.ui, { eotimepicker: { version: "0.3.3"} }); - - var PROP_NAME = 'eotimepicker', - tpuuid = new Date().getTime(); - - /* Time picker manager. - Use the singleton instance of this class, $.eotimepicker, 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 eoTimepicker() { - this.debug = true; // Change this to true to start debugging - this._curInst = null; // The current instance in use - 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 : https://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 - manual: [] // optional extra manual entries for 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 - - maxTime: { - hour: null, - minute: null - }, - minTime: { - hour: null, - minute: null - } - - }; - $.extend(this._defaults, this.regional['']); - - this.tpDiv = $(''); - } - - $.extend(eoTimepicker.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.eotimepicker", function (event, key, value) { - inst.settings[key] = value; - }). - bind("getData.eotimepicker", function (event, key) { - return this._get(inst, key); - }); - $.data(target, PROP_NAME, inst); - }, - - _getHours: function( inst ){ - var hours = Array(),hours_options = this._get(inst, 'hours'); - for (h = hours_options.starts; h <= hours_options.ends; h++) { - hours.push (h); - } - return hours; - - }, - _getMinutes: function( inst ){ - - var minutes_options = this._get(inst, 'minutes'); - var minutes = Array(); - - if ( ! minutes_options.starts) { - minutes_options.starts = 0; - } - if ( ! minutes_options.ends) { - minutes_options.ends = 59; - } - if ( ! minutes_options.manual) { - minutes_options.manual = []; - } - for (m = minutes_options.starts; m <= minutes_options.ends; m += minutes_options.interval) { - minutes.push(m); - } - for (i = 0; i < minutes_options.manual.length;i++) { - var currMin = minutes_options.manual[i]; - - // Validate & filter duplicates of manual minute input - if (typeof currMin != 'number' || currMin < 0 || currMin > 59 || $.inArray(currMin, minutes) >= 0) { - continue; - } - minutes.push(currMin); - } - - // Sort to get correct order after adding manual minutes - // Use compare function to sort by number, instead of string (default) - minutes.sort(function(a, b) { - return a-b; - }); - - return minutes; - }, - - /* Function to fix JavaScript modulo behaviour: ensures returned value is positive */ - _modulo: function ( value, modulo ){ - return ( ( value % modulo ) + modulo ) % modulo; - }, - - /* Handle keystrokes. */ - _doKeyDown: function (event) { - var inst = $.eotimepicker._getInst(event.target); - var handled = true; - inst._keyEvent = true; - if ($.eotimepicker._timepickerShowing) { - - var ctrlOrCmd = ( event.ctrlKey || event.metaKey ); - var shift = event.shiftKey; - - var hours = $.eotimepicker._getHours( inst ), - minutes = $.eotimepicker._getMinutes( inst ), - rows = $.eotimepicker._get(inst, 'rows'); - - var hoursPerRow = Math.ceil(hours.length / rows); - var minutesPerRow = Math.round(minutes.length / rows + 0.49); - - switch (event.keyCode) { - case 9: $.eotimepicker._hideTimepicker(); - handled = false; - break; // hide on tab out - - case 13: - inst.hours = inst.hlHours; - inst.minutes = inst.hlMinutes; - $.eotimepicker._updateSelectedValue(inst); - $.eotimepicker._hideTimepicker(); - return false; // don't submit the form - break; // select the value on enter - - case 27: $.eotimepicker._hideTimepicker(); - break; // hide on escape - - case 37: - handled = ctrlOrCmd || shift; - if( shift ) { - $.eotimepicker._moveHighlight( inst, -1, 'minute' ); - } else if ( ctrlOrCmd ) { - $.eotimepicker._moveHighlight( inst, -1, 'hour' ); - } - break; //Move up hour/minute; - - case 38: - handled = ctrlOrCmd || shift; - if( shift ) { - $.eotimepicker._moveHighlight( inst, -minutesPerRow, 'minute' ); - } else if ( ctrlOrCmd ) { - $.eotimepicker._moveHighlight( inst, -hoursPerRow, 'hour' ); - } - break; // Move left hour/minute - - case 39: - handled = ctrlOrCmd || shift; - if( shift ) { - $.eotimepicker._moveHighlight( inst, +1, 'minute' ); - } else if ( ctrlOrCmd ) { - $.eotimepicker._moveHighlight( inst, +1, 'hour' ); - } - break; //Move right hour/minute - - case 40: - handled = ctrlOrCmd || shift; - if( shift ) { - $.eotimepicker._moveHighlight( inst, minutesPerRow, 'minute' ); - } else if ( ctrlOrCmd ) { - $.eotimepicker._moveHighlight( inst, hoursPerRow, 'hour' ); - } - break; //Move down hour/minute - - default: - handled = false; - - } - } - else if (event.keyCode == 36 && event.ctrlKey) { // display the time picker on ctrl+home - $.eotimepicker._showTimepicker(this); - } - else { - handled = false; - } - if (handled) { - event.preventDefault(); - event.stopPropagation(); - } - }, - - _moveHighlight: function( inst, offset, period ){ - - var minutes = this._getMinutes( inst ); - var hours = this._getHours( inst ); - var maxTime = this._get(inst, 'maxTime'); - var minTime = this._get(inst, 'minTime'); - - if ( period.toLowerCase() == 'hour' ){ - var index = $.inArray( inst.hlHours, hours ); - inst.hlHours = hours[ this._modulo( index + offset, hours.length )]; - } else{ - var index = $.inArray( inst.hlMinutes, minutes ); - inst.hlMinutes = minutes[ this._modulo( index + offset, minutes.length )]; - } - - //Make sure we don't go over the max, or under the minimum time - if( minTime.hour ) { - inst.hlHours = Math.max( inst.hlHours, minTime.hour ); - } - if( maxTime.hour ) { - inst.hlHours = Math.min( inst.hlHours, maxTime.hour ); - } - - if( minTime.minute && inst.hlHours <= minTime.hour ) { - inst.hlMinutes = Math.max( inst.hlMinutes, minTime.minute ); - } - if( maxTime.minute && inst.hlHours >= maxTime.hour ) { - inst.hlMinutes = Math.min( inst.hlMinutes, maxTime.minute ); - } - - }, - - /* Update selected time on keyUp */ - /* Added verion 0.0.5 */ - _doKeyUp: function (event) { - var inst = $.eotimepicker._getInst(event.target); - $.eotimepicker._setTimeFromField(inst); - $.eotimepicker._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.eotimepicker', this._showTimepicker); - input.unbind('click.eotimepicker', this._adjustZIndex); - - 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.eotimepicker", this._showTimepicker); - input.bind("click.eotimepicker", this._adjustZIndex); - } - if (showOn == 'button' || showOn == 'both') { // pop-up time picker when 'button' element is clicked - var button = this._get(inst, 'button'); - - // Add button if button element is not set - if(button == null) { - button = $(''); - input.after(button); - } - - $(button).bind("click.eotimepicker", function () { - if ($.eotimepicker._timepickerShowing && $.eotimepicker._lastInput == input[0]) { - $.eotimepicker._hideTimepicker(); - } else if (!inst.input.is(':disabled')) { - $.eotimepicker._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.eotimepicker", function(event, key, value){ - inst.settings[key] = value; - }).bind("getData.eotimepicker", function(event, key){ - return this._get(inst, key); - }); - $.data(target, PROP_NAME, inst); - - this._setTimeFromField(inst); - this._updateTimepicker(inst); - inst.tpDiv.show(); - }, - - _adjustZIndex: function(input) { - input = input.target || input; - var inst = $.eotimepicker._getInst(input); - inst.tpDiv.css('zIndex', $.eotimepicker._getZIndex(input) +1); - }, - - /* 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 ($.eotimepicker._isDisabledTimepicker(input) || $.eotimepicker._lastInput == input) { return; } // already here - - // fix v 0.0.8 - close current timepicker before showing another one - $.eotimepicker._hideTimepicker(); - - var inst = $.eotimepicker._getInst(input); - if ($.eotimepicker._curInst && $.eotimepicker._curInst != inst) { - $.eotimepicker._curInst.tpDiv.stop(true, true); - } - var beforeShow = $.eotimepicker._get(inst, 'beforeShow'); - extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {})); - inst.lastVal = null; - $.eotimepicker._lastInput = input; - - $.eotimepicker._setTimeFromField(inst); - - //set highlighted cells to current time - inst.hlMinutes = inst.minutes; - inst.hlHours = inst.hours; - - // calculate default position - if ($.eotimepicker._inDialog) { input.value = ''; } // hide cursor - if (!$.eotimepicker._pos) { // position below input - $.eotimepicker._pos = $.eotimepicker._findPos(input); - $.eotimepicker._pos[1] += input.offsetHeight; // add the height - } - var isFixed = false; - $(input).parents().each(function () { - isFixed |= $(this).css('position') == 'fixed'; - return !isFixed; - }); - - var offset = { left: $.eotimepicker._pos[0], top: $.eotimepicker._pos[1] }; - - $.eotimepicker._pos = null; - // determine sizing offscreen - inst.tpDiv.css({ position: 'absolute', display: 'block', top: '-1000px' }); - $.eotimepicker._updateTimepicker(inst); - - - // position with the ui position utility, if loaded - if ( ( ! inst.inline ) && ( typeof $.ui.position == 'object' ) ) { - inst.tpDiv.position({ - of: inst.input, - my: $.eotimepicker._get( inst, 'myPosition' ), - at: $.eotimepicker._get( inst, 'atPosition' ), - // offset: $( "#offset" ).val(), - // using: using, - collision: 'flip' - }); - var offset = inst.tpDiv.offset(); - $.eotimepicker._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 = $.eotimepicker._checkOffset(inst, offset, isFixed); - inst.tpDiv.css({ position: ($.eotimepicker._inDialog && $.blockUI ? - 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none', - left: offset.left + 'px', top: offset.top + 'px' - }); - if ( ! inst.inline ) { - var showAnim = $.eotimepicker._get(inst, 'showAnim'); - var duration = $.eotimepicker._get(inst, 'duration'); - - var postProcess = function () { - $.eotimepicker._timepickerShowing = true; - var borders = $.eotimepicker._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 - $.eotimepicker._adjustZIndex(input); - //inst.tpDiv.css('zIndex', $.eotimepicker._getZIndex(input) +1); - - if ($.effects && $.effects[showAnim]) { - inst.tpDiv.show(showAnim, $.eotimepicker._get(inst, 'showOptions'), duration, postProcess); - } - else { - inst.tpDiv.show((showAnim ? duration : null), postProcess); - } - if (!showAnim || !duration) { postProcess(); } - if (inst.input.is(':visible') && !inst.input.is(':disabled')) { inst.input.focus(); } - $.eotimepicker._curInst = inst; - } - }, - - // This is an enhanced copy of the zIndex function of UI core 1.8.?? For backward compatibility. - // Enhancement returns maximum zindex value discovered while traversing parent elements, - // rather than the first zindex value found. Ensures the timepicker popup will be in front, - // even in funky scenarios like non-jq dialog containers with large fixed zindex values and - // nested zindex-influenced elements of their own. - _getZIndex: function (target) { - var elem = $(target); - var maxValue = 0; - var 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) { - if (value > maxValue) { maxValue = value; } - } - } - elem = elem.parent(); - } - - return maxValue; - }, - - /* Refresh the time picker - @param target element - The target input field or inline container element. */ - _refreshTimepicker: function(target) { - var inst = this._getInst(target); - if (inst) { - this._updateTimepicker(inst); - } - }, - - - /* Generate the time picker content. */ - _updateTimepicker: function (inst) { - inst.tpDiv.empty().append(this._generateHTML(inst)); - this._rebindDialogEvents(inst); - - }, - - _rebindDialogEvents: function (inst) { - var borders = $.eotimepicker._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($.eotimepicker.selectMinutes, this)) - .bind("dblclick", { fromDoubleClick:true }, $.proxy($.eotimepicker.selectMinutes, this)) - .end() - .find('.ui-timepicker-hour-cell') - .unbind() - .bind("click", { fromDoubleClick:false }, $.proxy($.eotimepicker.selectHours, this)) - .bind("dblclick", { fromDoubleClick:true }, $.proxy($.eotimepicker.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) { - $.eotimepicker.selectNow(e); - }).end() - .find('.ui-timepicker-deselect').bind("click",function(e) { - $.eotimepicker.deselectTime(e); - }).end() - .find('.ui-timepicker-close').bind("click",function(e) { - $.eotimepicker._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(), - 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; - - hours = this._getHours( inst ); - 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 densely 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; - - if (amRows == 0) { - hoursPerRow = Math.ceil(pmItems / pmRows); - } else if (pmRows == 0) { - hoursPerRow = Math.ceil(amItems / amRows); - } else { - 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 + '
'; - - 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($.eotimepicker.selectMinutes, this)) - .bind("dblclick", { fromDoubleClick:true }, $.proxy($.eotimepicker.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(), - minutesPerRow = null, - minuteCounter = 0, - showMinutesLeadingZero = (this._get(inst, 'showMinutesLeadingZero') == true), - onMinuteShow = this._get(inst, 'onMinuteShow'), - minuteLabel = this._get(inst, 'minuteText'); - - minutes = this._getMinutes( inst ); - 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 - var maxTime = this._get(inst, 'maxTime'); - var minTime = this._get(inst, 'minTime'); - - if (hour == undefined) { - html = ' '; - return html; - } - - if (onHourShow) { - enabled = onHourShow.apply((inst.input ? inst.input[0] : null), [hour]); - } - - if (enabled) { - if ( !isNaN(parseInt(maxTime.hour)) && hour > maxTime.hour ) enabled = false; - if ( !isNaN(parseInt(minTime.hour)) && hour < minTime.hour ) enabled = false; - } - - 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 hour = inst.hlHours; - var onMinuteShow = this._get(inst, 'onMinuteShow'); //custom callback - var maxTime = this._get(inst, 'maxTime'); - var minTime = this._get(inst, 'minTime'); - - 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 && hour !== null) { - if ( !isNaN(parseInt(maxTime.hour)) && !isNaN(parseInt(maxTime.minute)) && hour >= maxTime.hour && minute > maxTime.minute ) enabled = false; - if ( !isNaN(parseInt(minTime.hour)) && !isNaN(parseInt(minTime.minute)) && hour <= minTime.hour && minute < minTime.minute ) enabled = false; - } - - if (enabled) { - html = '' + - '' + - displayText + - ''; - } - else { - - html = '' + - '' + - displayText + - '' + - ''; - } - return html; - }, - - - /* Detach a timepicker from its control. - @param target element - the target input field or division or span */ - _destroyTimepicker: function(target) { - var $target = $(target); - var inst = $.data(target, PROP_NAME); - if (!$target.hasClass(this.markerClassName)) { - return; - } - var nodeName = target.nodeName.toLowerCase(); - $.removeData(target, PROP_NAME); - if (nodeName == 'input') { - inst.append.remove(); - inst.trigger.remove(); - $target.removeClass(this.markerClassName) - .unbind('focus.eotimepicker', this._showTimepicker) - .unbind('click.eotimepicker', this._adjustZIndex); - } else if (nodeName == 'div' || nodeName == 'span') - $target.removeClass(this.markerClassName).empty(); - }, - - /* 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; - var button = this._get(inst, 'button'); - $(button).removeClass('ui-state-disabled').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'); - inline.find('button').each( - function() { this.disabled = false } - ) - } - 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') { - var button = this._get(inst, 'button'); - - $(button).addClass('ui-state-disabled').disabled = true; - 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'); - inline.find('button').each( - function() { this.disabled = true } - ) - - } - 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 timepicker 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 (!$.eotimepicker._curInst) { return; } - var $target = $(event.target); - if ($target[0].id != $.eotimepicker._mainDivId && - $target.parents('#' + $.eotimepicker._mainDivId).length == 0 && - !$target.hasClass($.eotimepicker.markerClassName) && - !$target.hasClass($.eotimepicker._triggerClass) && - $.eotimepicker._timepickerShowing && !($.eotimepicker._inDialog && $.blockUI)) - $.eotimepicker._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 () { - $.eotimepicker._tidyDialog(inst); - this._curInst = null; - }; - if ($.effects && $.effects[showAnim]) { - inst.tpDiv.hide(showAnim, $.eotimepicker._get(inst, 'showOptions'), duration, postProcess); - } - else { - inst.tpDiv[(showAnim == 'slideDown' ? 'slideUp' : - (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess); - } - if (!showAnim) { postProcess(); } - - 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; - - 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 - } - - } - }, - - - - /* 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; - } - } - - - $.eotimepicker._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({}, $.eotimepicker._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) { - extendRemove(inst.settings, settings); - if (this._curInst == inst) { - this._hideTimepicker(); - this._updateTimepicker(inst); - } - if (inst.inline) { - 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; - if (time instanceof Date) { - inst.hours = time.getHours(); - inst.minutes = time.getMinutes(); - } else { - var time = this.parseTime(inst, time); - inst.hours = time.hours; - inst.minutes = time.minutes; - } - - if ((origHours != inst.hours || origMinutes != inst.minutes) && !noChange) { - inst.input.trigger('change'); - } - this._updateTimepicker(inst); - this._updateSelectedValue(inst); - }, - - /* Return the current time, ready to be parsed, rounded to the closest minute by interval */ - _getCurrentTimeRounded: function (inst) { - var currentTime = new Date(), - currentMinutes = currentTime.getMinutes(), - minutes_options = this._get(inst, 'minutes'), - // round to closest interval - adjustedMinutes = Math.round(currentMinutes / minutes_options.interval) * minutes_options.interval; - 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; - - if(!timeVal) - return ''; - - 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(event) { - var id = $(event.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(event) { - var id = $(event.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 = parseInt($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 ( $.eotimepicker._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'), - maxTime = this._get(inst, 'maxTime'), - minTime = this._get(inst, 'minTime'); - if (onMinuteShow || maxTime.minute || minTime.minute) { - // 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)) { - $.eotimepicker._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 = parseInt($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 ( $.eotimepicker._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)) { - $.eotimepicker._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 ''; - } - - // default to 0 AM if hours is not valid - if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends )) { inst.hours = 0; } - // default to 0 minutes if minute is not valid - if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) { 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 = ''; - - // fix some display problem when hours or minutes are not selected yet - if (displayHours == -1) { displayHours = 0 } - if (selectedMinutes == -1) { selectedMinutes = 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; } - - 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); - }); - } - }, - - _getTimeAsDateTimepicker: function(input) { - var inst = this._getInst(input); - if (inst.hours == -1 && inst.minutes == -1) { - return ''; - } - - // default to 0 AM if hours is not valid - if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends )) { inst.hours = 0; } - // default to 0 minutes if minute is not valid - if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) { inst.minutes = 0; } - - return new Date(0, 0, 0, inst.hours, inst.minutes, 0); - }, - /* This might look unused but it's called by the $.fn.eotimepicker 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.eotimepicker = function (options) { - /* Initialise the time picker. */ - if (!$.eotimepicker.initialized) { - $(document).mousedown($.eotimepicker._checkExternalClick); - $.eotimepicker.initialized = true; - } - - /* Append timepicker main container to body if not exist. */ - if ($("#"+$.eotimepicker._mainDivId).length === 0) { - $('body').append($.eotimepicker.tpDiv); - } - - var otherArgs = Array.prototype.slice.call(arguments, 1); - if (typeof options == 'string' && (options == 'getTime' || options == 'getTimeAsDate' || options == 'getHour' || options == 'getMinute' )) - return $.eotimepicker['_' + options + 'Timepicker']. - apply($.eotimepicker, [this[0]].concat(otherArgs)); - if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string') - return $.eotimepicker['_' + options + 'Timepicker']. - apply($.eotimepicker, [this[0]].concat(otherArgs)); - return this.each(function () { - typeof options == 'string' ? - $.eotimepicker['_' + options + 'Timepicker']. - apply($.eotimepicker, [this].concat(otherArgs)) : - $.eotimepicker._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; - }; - - $.eotimepicker = new eoTimepicker(); // singleton instance - $.eotimepicker.initialized = false; - $.eotimepicker.uuid = new Date().getTime(); - $.eotimepicker.version = "0.3.3"; - - // Workaround for #4055 - // Add another global to avoid noConflict issues with inline event handlers - window['TP_jQuery_' + tpuuid] = $; - -})(jQuery); \ No newline at end of file diff --git a/js/jquery.datepair.js b/js/jquery.datepair.js new file mode 100644 index 00000000..f3d07c22 --- /dev/null +++ b/js/jquery.datepair.js @@ -0,0 +1,46 @@ +/*! + * datepair.js v0.4.12 - A javascript plugin for intelligently selecting date and time ranges inspired by Google Calendar. + * Copyright (c) 2015 Jon Thornton - http://jonthornton.github.com/Datepair.js + * License: MIT + */ + +(function($) { + + if(!$) { + return; + } + + //////////// + // Plugin // + //////////// + + $.fn.datepair = function(option) { + var out; + this.each(function() { + var $this = $(this); + var data = $this.data('datepair'); + var options = typeof option === 'object' && option; + + if (!data) { + data = new Datepair(this, options); + $this.data('datepair', data); + } + + if (typeof option === 'string') { + out = data[option](); + } + }); + + return out || this; + }; + + ////////////// + // Data API // + ////////////// + + $('[data-datepair]').each(function() { + var $this = $(this); + $this.datepair($this.data()); + }); + +}(window.Zepto || window.jQuery)); \ No newline at end of file diff --git a/js/jquery.timepicker.js b/js/jquery.timepicker.js new file mode 100644 index 00000000..0ba05526 --- /dev/null +++ b/js/jquery.timepicker.js @@ -0,0 +1,1197 @@ +/*! + * jquery-timepicker v1.8.5 - A jQuery timepicker plugin inspired by Google Calendar. It supports both mouse and keyboard navigation. + * Copyright (c) 2015 Jon Thornton - http://jonthornton.github.com/jquery-timepicker/ + * License: MIT + */ + + +(function (factory) { + if (typeof exports === "object" && exports && + typeof module === "object" && module && module.exports === exports) { + // Browserify. Attach to jQuery module. + factory(require("jquery")); + } else if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else { + // Browser globals + factory(jQuery); + } +}(function ($) { + var _baseDate = _generateBaseDate(); + var _ONE_DAY = 86400; + var _lang = { + am: 'am', + pm: 'pm', + AM: 'AM', + PM: 'PM', + decimal: '.', + mins: 'mins', + hr: 'hr', + hrs: 'hrs' + }; + + var methods = { + init: function(options) + { + return this.each(function() + { + var self = $(this); + + // pick up settings from data attributes + var attributeOptions = []; + for (var key in $.fn.timepicker.defaults) { + if (self.data(key)) { + attributeOptions[key] = self.data(key); + } + } + + var settings = $.extend({}, $.fn.timepicker.defaults, attributeOptions, options); + + if (settings.lang) { + _lang = $.extend(_lang, settings.lang); + } + + settings = _parseSettings(settings); + self.data('timepicker-settings', settings); + self.addClass('ui-timepicker-input'); + + if (settings.useSelect) { + _render(self); + } else { + self.prop('autocomplete', 'off'); + if (settings.showOn) { + for (i in settings.showOn) { + self.on(settings.showOn[i]+'.timepicker', methods.show); + } + } + self.on('change.timepicker', _formatValue); + self.on('keydown.timepicker', _keydownhandler); + self.on('keyup.timepicker', _keyuphandler); + if (settings.disableTextInput) { + self.on('keypress.timepicker', function(e) { e.preventDefault(); }); + } + + _formatValue.call(self.get(0)); + } + }); + }, + + show: function(e) + { + var self = $(this); + var settings = self.data('timepicker-settings'); + + if (e) { + e.preventDefault(); + } + + if (settings.useSelect) { + self.data('timepicker-list').focus(); + return; + } + + if (_hideKeyboard(self)) { + // block the keyboard on mobile devices + self.blur(); + } + + var list = self.data('timepicker-list'); + + // check if input is readonly + if (self.prop('readonly')) { + return; + } + + // check if list needs to be rendered + if (!list || list.length === 0 || typeof settings.durationTime === 'function') { + _render(self); + list = self.data('timepicker-list'); + } + + if (_isVisible(list)) { + return; + } + + self.data('ui-timepicker-value', self.val()); + _setSelected(self, list); + + // make sure other pickers are hidden + methods.hide(); + + // position the dropdown relative to the input + list.show(); + var listOffset = {}; + + if (settings.orientation.match(/r/)) { + // right-align the dropdown + listOffset.left = self.offset().left + self.outerWidth() - list.outerWidth() + parseInt(list.css('marginLeft').replace('px', ''), 10); + } else { + // left-align the dropdown + listOffset.left = self.offset().left + parseInt(list.css('marginLeft').replace('px', ''), 10); + } + + var verticalOrientation; + if (settings.orientation.match(/t/)) { + verticalOrientation = 't'; + } else if (settings.orientation.match(/b/)) { + verticalOrientation = 'b'; + } else if ((self.offset().top + self.outerHeight(true) + list.outerHeight()) > $(window).height() + $(window).scrollTop()) { + verticalOrientation = 't'; + } else { + verticalOrientation = 'b'; + } + + if (verticalOrientation == 't') { + // position the dropdown on top + list.addClass('ui-timepicker-positioned-top'); + listOffset.top = self.offset().top - list.outerHeight() + parseInt(list.css('marginTop').replace('px', ''), 10); + } else { + // put it under the input + list.removeClass('ui-timepicker-positioned-top'); + listOffset.top = self.offset().top + self.outerHeight() + parseInt(list.css('marginTop').replace('px', ''), 10); + } + + list.offset(listOffset); + + // position scrolling + var selected = list.find('.ui-timepicker-selected'); + + if (!selected.length) { + var timeInt = _time2int(_getTimeValue(self)); + if (timeInt !== null) { + selected = _findRow(self, list, timeInt); + } else if (settings.scrollDefault) { + selected = _findRow(self, list, settings.scrollDefault()); + } + } + + if (selected && selected.length) { + var topOffset = list.scrollTop() + selected.position().top - selected.outerHeight(); + list.scrollTop(topOffset); + } else { + list.scrollTop(0); + } + + // prevent scroll propagation + if(settings.stopScrollPropagation) { + $(document).on('wheel.ui-timepicker', '.ui-timepicker-wrapper', function(e){ + e.preventDefault(); + var currentScroll = $(this).scrollTop(); + $(this).scrollTop(currentScroll + e.originalEvent.deltaY); + }); + } + + // attach close handlers + $(document).on('touchstart.ui-timepicker mousedown.ui-timepicker', _closeHandler); + $(window).on('resize.ui-timepicker', _closeHandler); + if (settings.closeOnWindowScroll) { + $(document).on('scroll.ui-timepicker', _closeHandler); + } + + self.trigger('showTimepicker'); + + return this; + }, + + hide: function(e) + { + var self = $(this); + var settings = self.data('timepicker-settings'); + + if (settings && settings.useSelect) { + self.blur(); + } + + $('.ui-timepicker-wrapper').each(function() { + var list = $(this); + if (!_isVisible(list)) { + return; + } + + var self = list.data('timepicker-input'); + var settings = self.data('timepicker-settings'); + + if (settings && settings.selectOnBlur) { + _selectValue(self); + } + + list.hide(); + self.trigger('hideTimepicker'); + }); + + return this; + }, + + option: function(key, value) + { + return this.each(function(){ + var self = $(this); + var settings = self.data('timepicker-settings'); + var list = self.data('timepicker-list'); + + if (typeof key == 'object') { + settings = $.extend(settings, key); + + } else if (typeof key == 'string' && typeof value != 'undefined') { + settings[key] = value; + + } else if (typeof key == 'string') { + return settings[key]; + } + + settings = _parseSettings(settings); + + self.data('timepicker-settings', settings); + + if (list) { + list.remove(); + self.data('timepicker-list', false); + } + + if (settings.useSelect) { + _render(self); + } + }); + }, + + getSecondsFromMidnight: function() + { + return _time2int(_getTimeValue(this)); + }, + + getTime: function(relative_date) + { + var self = this; + + var time_string = _getTimeValue(self); + if (!time_string) { + return null; + } + + var offset = _time2int(time_string); + if (offset === null) { + return null; + } + + if (!relative_date) { + relative_date = _baseDate; + } + + // construct a Date from relative date, and offset's time + var time = new Date(relative_date); + time.setHours(offset / 3600); + time.setMinutes(offset % 3600 / 60); + time.setSeconds(offset % 60); + time.setMilliseconds(0); + + return time; + }, + + setTime: function(value) + { + var self = this; + var settings = self.data('timepicker-settings'); + + if (settings.forceRoundTime) { + var prettyTime = _roundAndFormatTime(_time2int(value), settings) + } else { + var prettyTime = _int2time(_time2int(value), settings); + } + + if (value && prettyTime === null && settings.noneOption) { + prettyTime = value; + } + + _setTimeValue(self, prettyTime); + if (self.data('timepicker-list')) { + _setSelected(self, self.data('timepicker-list')); + } + + return this; + }, + + remove: function() + { + var self = this; + + // check if this element is a timepicker + if (!self.hasClass('ui-timepicker-input')) { + return; + } + + var settings = self.data('timepicker-settings'); + self.removeAttr('autocomplete', 'off'); + self.removeClass('ui-timepicker-input'); + self.removeData('timepicker-settings'); + self.off('.timepicker'); + + // timepicker-list won't be present unless the user has interacted with this timepicker + if (self.data('timepicker-list')) { + self.data('timepicker-list').remove(); + } + + if (settings.useSelect) { + self.show(); + } + + self.removeData('timepicker-list'); + + return this; + } + }; + + // private methods + + function _isVisible(elem) + { + var el = elem[0]; + return el.offsetWidth > 0 && el.offsetHeight > 0; + } + + function _parseSettings(settings) + { + if (settings.minTime) { + settings.minTime = _time2int(settings.minTime); + } + + if (settings.maxTime) { + settings.maxTime = _time2int(settings.maxTime); + } + + if (settings.durationTime && typeof settings.durationTime !== 'function') { + settings.durationTime = _time2int(settings.durationTime); + } + + if (settings.scrollDefault == 'now') { + settings.scrollDefault = function() { + return settings.roundingFunction(_time2int(new Date()), settings); + } + } else if (settings.scrollDefault && typeof settings.scrollDefault != 'function') { + var val = settings.scrollDefault; + settings.scrollDefault = function() { + return settings.roundingFunction(_time2int(val), settings); + } + } else if (settings.minTime) { + settings.scrollDefault = function() { + return settings.roundingFunction(settings.minTime, settings); + } + } + + if ($.type(settings.timeFormat) === "string" && settings.timeFormat.match(/[gh]/)) { + settings._twelveHourTime = true; + } + + if (settings.showOnFocus === false && settings.showOn.indexOf('focus') != -1) { + settings.showOn.splice(settings.showOn.indexOf('focus'), 1); + } + + if (settings.disableTimeRanges.length > 0) { + // convert string times to integers + for (var i in settings.disableTimeRanges) { + settings.disableTimeRanges[i] = [ + _time2int(settings.disableTimeRanges[i][0]), + _time2int(settings.disableTimeRanges[i][1]) + ]; + } + + // sort by starting time + settings.disableTimeRanges = settings.disableTimeRanges.sort(function(a, b){ + return a[0] - b[0]; + }); + + // merge any overlapping ranges + for (var i = settings.disableTimeRanges.length-1; i > 0; i--) { + if (settings.disableTimeRanges[i][0] <= settings.disableTimeRanges[i-1][1]) { + settings.disableTimeRanges[i-1] = [ + Math.min(settings.disableTimeRanges[i][0], settings.disableTimeRanges[i-1][0]), + Math.max(settings.disableTimeRanges[i][1], settings.disableTimeRanges[i-1][1]) + ]; + settings.disableTimeRanges.splice(i, 1); + } + } + } + + return settings; + } + + function _render(self) + { + var settings = self.data('timepicker-settings'); + var list = self.data('timepicker-list'); + + if (list && list.length) { + list.remove(); + self.data('timepicker-list', false); + } + + if (settings.useSelect) { + list = $('