HOWTH TEAM BUILDING TOURS

Corporate Events

Outdoor Team Building Events & Corporate Activities.

Fun Howth outdoor team building events & corporate activities, eco-friendly adventures e-biking, kayaking, hiking, sailing & walking, experiences and special events on the Howth Peninsula in Dublin.

We provide outdoor adventure experiences and special events on the Howth Peninsula in Dublin. Outdoor adventures are the safest and best way to reconnect with colleagues. 

Our walking, hiking and electric bike tours are operating, and all recommended COVID-19 guidelines are tried and tested.

Our outdoor adventures will keep you safe and sound. Experienced and trusted with businesses, incentive groups and tour operators, we create unforgettable experiences combining the perfect adventure on land or sea with top class food and beverages.

Shared and cherished authentic lifetime memories are imprinted on our clients along with strong feelings of belonging. We take exceptional personal care of every client....and they notice.

Only 30 minutes from the City Centre or Airport and within the Dublin Bay UNESCO biosphere.  

The Secret Garden

Explore the celebrated Howth Cliff Walk Path with alternating views of Howth Harbour, Ireland’s Eye and Lambay Island, even the Rockabill Lighthouse and the Mountains of Mourne on a clear day. 

Follow the trails to the great Baily Lighthouse, Dalkey Island and the Wicklow mountains, getting close to Doldrum Bay, the Needles and the old Jameson and Guinness’s summer playground.   

Immerse yourself in the nature of this UNESCO Biosphere, the busy familiar home of remarkable seabirds, porpoises, seals, sea otters and Ireland’s only native lizards.

Savour a pause in a very private and unique ‘Secret Garden’. 

This precious oasis has been home to an old Howth family for over 500 years whose ancestors were Howth people of soil and salt, living at Kitestown Cottage for centuries. 

Their home was the loneliest outpost of the old Kitestown townland, only accessible across the moors on a rocky trail.  As times and values have changed they are preserving the wild freedom and gentle friendship with nature of an older Ireland and want to share it with you.    

Eco Tours

On our guided hikes, electric bike tours and water experiences, we share access to the intimate, local and exclusive coastal community of Howth, its exquisite nature and history.

We offer more adventurous hikes of the iconic cliffs, hillsides and woodlands and cultured walks exploring the harbour and village.

Our Panoramic E-Bike Tours take you to the most spectacular views in Ireland without getting you out of breath.

Exclusive on the water experiences, sailing, Ireland's Eye boat trips and tours are enjoyed on their own or combined with hikes, walks and electric bikes tours.

Time in nature together deepens relationships, increases attention span and  inspires creative problem-solving. Reboot your business and your teams as individuals and groups. Create  a specialised experience to broaden, energize and inspire with simultaneous lightness and depth.

Howth's famous hospitality guarantees the most satisfying and delicious options for picnics, refreshments, breakfast, lunch or dinner. Blend the perfect Entrée or Apres, À la carte or Al fresco to set you off on the right foot, keep you going en route, or wrap it up in the warm glow of shared celebration.

1.5 Hour Howth Cliff Path & Harbour Tour

Fun Team Building Events In Dublin

Team Building Activities Dublin

 

1.5 Hour Howth Cliff Path and Harbour Tour is a breath of fresh air scenic cliff path walk with a nature and cultural focus. 

Visit Howth Harbour and be immersed in local life reliving the great tales of history.

Take in the world famous breathtaking views on the cliff path.   

This is a journey through time in one of the most significant ports of Ireland which has played its role in both Irish and European history.

This experience leaves our guests feeling like locals, in a home away from home.

2.5 hour Howth Hill Experience

Team Building Events Dublin

Team Building Events Dublin

 

2.5 hour Howth Hill Experience is perfect for most groups with reasonable fitness

Travel through time with our expert local guides while being given Howth’s amazing history surrounded by beautiful marine wildlife

Come with us off the beaten track to secret trails where you will be rewarded with the most breathtaking views in Ireland

All will be revealed on the day!  

Some of the not so secret famous landmarks we will encounter include Howth Castle, Martello Towers, Aideen's Grave & the Baily Lighthouse

Awaken your senses to the fragrances, sights and sounds of this delightful peninsula

2.5 Hour Panoramic Electric Bike Tour

Team Building Ideas

Team Building Ideas Dublin

 

2.5 hour Panoramic Electric Bike Tour

Cycle to the most breath taking views in Ireland without getting out of breath.

Our e-bikes are great fun, easy, safe and trusty on the tarmac or the trails. 

We mix the insider tracks and tales of this truly amazing peninsula on a 2.5 hour tour while taking you to unrivalled panoramic views away from the crowds surrounded by nature and beauty.

2-3 hour Howth Water Experience

Team Building North County Dublin

 

2-3 hour Howth on the Water Experience 

Get a taste of sailing with qualified instructors in a fun, safe and exhilarating experience aboard a fleet of J80 Sportsboats or RIBs at Howth Yacht Club.

First class sailing tuition and sail around Ireland's Eye

Have a fun race and mini regatta before returning on land to the world class hospitality facilities

Wine and dine into the evening

4 Hour Howth Safari Experience

Team Building Activities Dublin

 

4 hour Howth Safari Experience is for the adventurous teams who will hike through the diverse and magical habitats of Howth

Explore a range of habitats from Irish woodlands and famous heathlands to epic cliffs and coastal views

This is the most exciting and refreshing hike close to Dublin city

Escape the stress of work and city, awaken the senses, reconnect with nature

Revitalise

When the tides allow, come rockhopping and explore the caves

 

Team Building Inquiry Form

Name

email

Phone Number

Date of Tour

Ok This website uses cookies in order to offer you the most relevant information. Please OK cookies for optimal performance.

 Push Notifications are disabled

hide

Howth Tours

 Add to homescreen

hide
//! moment.js locale configuration //! locale : English (United Kingdom) [en-gb] //! author : Chris Gedrim : https://github.com/chrisgedrim ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) }(this, (function (moment) { 'use strict'; var enGb = moment.defineLocale('en-gb', { months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), longDateFormat : { LT : 'HH:mm', LTS : 'HH:mm:ss', L : 'DD/MM/YYYY', LL : 'D MMMM YYYY', LLL : 'D MMMM YYYY HH:mm', LLLL : 'dddd, D MMMM YYYY HH:mm' }, calendar : { sameDay : '[Today at] LT', nextDay : '[Tomorrow at] LT', nextWeek : 'dddd [at] LT', lastDay : '[Yesterday at] LT', lastWeek : '[Last] dddd [at] LT', sameElse : 'L' }, relativeTime : { future : 'in %s', past : '%s ago', s : 'a few seconds', m : 'a minute', mm : '%d minutes', h : 'an hour', hh : '%d hours', d : 'a day', dd : '%d days', M : 'a month', MM : '%d months', y : 'a year', yy : '%d years' }, dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, ordinal : function (number) { var b = number % 10, output = (~~(number % 100 / 10) === 1) ? 'th' : (b === 1) ? 'st' : (b === 2) ? 'nd' : (b === 3) ? 'rd' : 'th'; return number + output; }, week : { dow : 1, // Monday is the first day of the week. doy : 4 // The week that contains Jan 4th is the first week of the year. } }); return enGb; }))); /*! version : 4.15.35 ========================================================= bootstrap-datetimejs https://github.com/Eonasdan/bootstrap-datetimepicker Copyright (c) 2015 Jonathan Peterson ========================================================= */ /* The MIT License (MIT) Copyright (c) 2015 Jonathan Peterson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*global define:false */ /*global exports:false */ /*global require:false */ /*global jQuery:false */ /*global moment:false */ (function (factory) { 'use strict'; if (typeof define === 'function' && define.amd) { // AMD is used - Register as an anonymous module. define(['jquery', 'moment'], factory); } else if (typeof exports === 'object') { factory(require('jquery'), require('moment')); } else { // Neither AMD nor CommonJS used. Use global variables. if (typeof jQuery === 'undefined') { throw 'bootstrap-datetimepicker requires jQuery to be loaded first'; } if (typeof moment === 'undefined') { throw 'bootstrap-datetimepicker requires Moment.js to be loaded first'; } factory(jQuery, moment); } }(function ($, moment) { 'use strict'; if (!moment) { throw new Error('bootstrap-datetimepicker requires Moment.js to be loaded first'); } var dateTimePicker = function (element, options) { var picker = {}, date = moment().startOf('d'), viewDate = date.clone(), unset = true, input, component = false, widget = false, use24Hours, minViewModeNumber = 0, actualFormat, parseFormats, currentViewMode, datePickerModes = [ { clsName: 'days', navFnc: 'M', navStep: 1 }, { clsName: 'months', navFnc: 'y', navStep: 1 }, { clsName: 'years', navFnc: 'y', navStep: 10 }, { clsName: 'decades', navFnc: 'y', navStep: 100 } ], viewModes = ['days', 'months', 'years', 'decades'], verticalModes = ['top', 'bottom', 'auto'], horizontalModes = ['left', 'right', 'auto'], toolbarPlacements = ['default', 'top', 'bottom'], keyMap = { 'up': 38, 38: 'up', 'down': 40, 40: 'down', 'left': 37, 37: 'left', 'right': 39, 39: 'right', 'tab': 9, 9: 'tab', 'escape': 27, 27: 'escape', 'enter': 13, 13: 'enter', 'pageUp': 33, 33: 'pageUp', 'pageDown': 34, 34: 'pageDown', 'shift': 16, 16: 'shift', 'control': 17, 17: 'control', 'space': 32, 32: 'space', 't': 84, 84: 't', 'delete': 46, 46: 'delete' }, keyState = {}, /******************************************************************************** * * Private functions * ********************************************************************************/ isEnabled = function (granularity) { if (typeof granularity !== 'string' || granularity.length > 1) { throw new TypeError('isEnabled expects a single character string parameter'); } switch (granularity) { case 'y': return actualFormat.indexOf('Y') !== -1; case 'M': return actualFormat.indexOf('M') !== -1; case 'd': return actualFormat.toLowerCase().indexOf('d') !== -1; case 'h': case 'H': return actualFormat.toLowerCase().indexOf('h') !== -1; case 'm': return actualFormat.indexOf('m') !== -1; case 's': return actualFormat.indexOf('s') !== -1; default: return false; } }, hasTime = function () { return (isEnabled('h') || isEnabled('m') || isEnabled('s')); }, hasDate = function () { return (isEnabled('y') || isEnabled('M') || isEnabled('d')); }, getDatePickerTemplate = function () { var headTemplate = $('') .append($('') .append($('').addClass('prev').attr('data-action', 'previous') .append($('').addClass(options.icons.previous)) ) .append($('').addClass('picker-switch').attr('data-action', 'pickerSwitch').attr('colspan', (options.calendarWeeks ? '6' : '5'))) .append($('').addClass('next').attr('data-action', 'next') .append($('').addClass(options.icons.next)) ) ), contTemplate = $('') .append($('') .append($('').attr('colspan', (options.calendarWeeks ? '8' : '7'))) ); return [ $('
').addClass('datepicker-days') .append($('').addClass('table-condensed') .append(headTemplate) .append($('')) ), $('
').addClass('datepicker-months') .append($('
').addClass('table-condensed') .append(headTemplate.clone()) .append(contTemplate.clone()) ), $('
').addClass('datepicker-years') .append($('
').addClass('table-condensed') .append(headTemplate.clone()) .append(contTemplate.clone()) ), $('
').addClass('datepicker-decades') .append($('
').addClass('table-condensed') .append(headTemplate.clone()) .append(contTemplate.clone()) ) ]; }, getTimePickerMainTemplate = function () { var topRow = $(''), middleRow = $(''), bottomRow = $(''); if (isEnabled('h')) { topRow.append($('
') .append($('').attr({href: '#', tabindex: '-1', 'title':'Increment Hour'}).addClass('btn').attr('data-action', 'incrementHours') .append($('').addClass(options.icons.up)))); middleRow.append($('') .append($('').addClass('timepicker-hour').attr({'data-time-component':'hours', 'title':'Pick Hour'}).attr('data-action', 'showHours'))); bottomRow.append($('') .append($('').attr({href: '#', tabindex: '-1', 'title':'Decrement Hour'}).addClass('btn').attr('data-action', 'decrementHours') .append($('').addClass(options.icons.down)))); } if (isEnabled('m')) { if (isEnabled('h')) { topRow.append($('').addClass('separator')); middleRow.append($('').addClass('separator').html(':')); bottomRow.append($('').addClass('separator')); } topRow.append($('') .append($('').attr({href: '#', tabindex: '-1', 'title':'Increment Minute'}).addClass('btn').attr('data-action', 'incrementMinutes') .append($('').addClass(options.icons.up)))); middleRow.append($('') .append($('').addClass('timepicker-minute').attr({'data-time-component': 'minutes', 'title':'Pick Minute'}).attr('data-action', 'showMinutes'))); bottomRow.append($('') .append($('').attr({href: '#', tabindex: '-1', 'title':'Decrement Minute'}).addClass('btn').attr('data-action', 'decrementMinutes') .append($('').addClass(options.icons.down)))); } if (isEnabled('s')) { if (isEnabled('m')) { topRow.append($('').addClass('separator')); middleRow.append($('').addClass('separator').html(':')); bottomRow.append($('').addClass('separator')); } topRow.append($('') .append($('').attr({href: '#', tabindex: '-1', 'title':'Increment Second'}).addClass('btn').attr('data-action', 'incrementSeconds') .append($('').addClass(options.icons.up)))); middleRow.append($('') .append($('').addClass('timepicker-second').attr({'data-time-component': 'seconds', 'title':'Pick Second'}).attr('data-action', 'showSeconds'))); bottomRow.append($('') .append($('').attr({href: '#', tabindex: '-1', 'title':'Decrement Second'}).addClass('btn').attr('data-action', 'decrementSeconds') .append($('').addClass(options.icons.down)))); } if (!use24Hours) { topRow.append($('').addClass('separator')); middleRow.append($('') .append($('').addClass('separator')); } return $('
').addClass('timepicker-picker') .append($('').addClass('table-condensed') .append([topRow, middleRow, bottomRow])); }, getTimePickerTemplate = function () { var hoursView = $('
').addClass('timepicker-hours') .append($('
').addClass('table-condensed')), minutesView = $('
').addClass('timepicker-minutes') .append($('
').addClass('table-condensed')), secondsView = $('
').addClass('timepicker-seconds') .append($('
').addClass('table-condensed')), ret = [getTimePickerMainTemplate()]; if (isEnabled('h')) { ret.push(hoursView); } if (isEnabled('m')) { ret.push(minutesView); } if (isEnabled('s')) { ret.push(secondsView); } return ret; }, getToolbar = function () { var row = []; if (options.showTodayButton) { row.push($('
').append($('').attr({'data-action':'today', 'title': options.tooltips.today}).append($('').addClass(options.icons.today)))); } if (!options.sideBySide && hasDate() && hasTime()) { row.push($('').append($('').attr({'data-action':'togglePicker', 'title':'Select Time'}).append($('').addClass(options.icons.time)))); } if (options.showClear) { row.push($('').append($('').attr({'data-action':'clear', 'title': options.tooltips.clear}).append($('').addClass(options.icons.clear)))); } if (options.showClose) { row.push($('').append($('').attr({'data-action':'close', 'title': options.tooltips.close}).append($('').addClass(options.icons.close)))); } return $('').addClass('table-condensed').append($('').append($('').append(row))); }, getTemplate = function () { var template = $('
').addClass('bootstrap-datetimepicker-widget dropdown-menu'), dateView = $('
').addClass('datepicker').append(getDatePickerTemplate()), timeView = $('
').addClass('timepicker').append(getTimePickerTemplate()), content = $('
    ').addClass('list-unstyled'), toolbar = $('
  • ').addClass('picker-switch' + (options.collapse ? ' accordion-toggle' : '')).append(getToolbar()); if (options.inline) { template.removeClass('dropdown-menu'); } if (use24Hours) { template.addClass('usetwentyfour'); } if (isEnabled('s') && !use24Hours) { template.addClass('wider'); } if (options.sideBySide && hasDate() && hasTime()) { template.addClass('timepicker-sbs'); if (options.toolbarPlacement === 'top') { template.append(toolbar); } template.append( $('
    ').addClass('row') .append(dateView.addClass('col-md-6')) .append(timeView.addClass('col-md-6')) ); if (options.toolbarPlacement === 'bottom') { template.append(toolbar); } return template; } if (options.toolbarPlacement === 'top') { content.append(toolbar); } if (hasDate()) { content.append($('
  • ').addClass((options.collapse && hasTime() ? 'collapse in' : '')).append(dateView)); } if (options.toolbarPlacement === 'default') { content.append(toolbar); } if (hasTime()) { content.append($('
  • ').addClass((options.collapse && hasDate() ? 'collapse' : '')).append(timeView)); } if (options.toolbarPlacement === 'bottom') { content.append(toolbar); } return template.append(content); }, dataToOptions = function () { var eData, dataOptions = {}; if (element.is('input') || options.inline) { eData = element.data(); } else { eData = element.find('input').data(); } if (eData.dateOptions && eData.dateOptions instanceof Object) { dataOptions = $.extend(true, dataOptions, eData.dateOptions); } $.each(options, function (key) { var attributeName = 'date' + key.charAt(0).toUpperCase() + key.slice(1); if (eData[attributeName] !== undefined) { dataOptions[key] = eData[attributeName]; } }); return dataOptions; }, place = function () { var position = (component || element).position(), offset = (component || element).offset(), vertical = options.widgetPositioning.vertical, horizontal = options.widgetPositioning.horizontal, parent; if (options.widgetParent) { parent = options.widgetParent.append(widget); } else if (element.is('input')) { parent = element.after(widget).parent(); } else if (options.inline) { parent = element.append(widget); return; } else { parent = element; element.children().first().after(widget); } // Top and bottom logic if (vertical === 'auto') { if (offset.top + widget.height() * 1.5 >= $(window).height() + $(window).scrollTop() && widget.height() + element.outerHeight() < offset.top) { vertical = 'top'; } else { vertical = 'bottom'; } } // Left and right logic if (horizontal === 'auto') { if (parent.width() < offset.left + widget.outerWidth() / 2 && offset.left + widget.outerWidth() > $(window).width()) { horizontal = 'right'; } else { horizontal = 'left'; } } if (vertical === 'top') { widget.addClass('top').removeClass('bottom'); } else { widget.addClass('bottom').removeClass('top'); } if (horizontal === 'right') { widget.addClass('pull-right'); } else { widget.removeClass('pull-right'); } // find the first parent element that has a relative css positioning if (parent.css('position') !== 'relative') { parent = parent.parents().filter(function () { return $(this).css('position') === 'relative'; }).first(); } if (parent.length === 0) { throw new Error('datetimepicker component should be placed within a relative positioned container'); } widget.css({ top: vertical === 'top' ? 'auto' : position.top + element.outerHeight(), bottom: vertical === 'top' ? position.top + element.outerHeight() : 'auto', left: horizontal === 'left' ? (parent === element ? 0 : position.left) : 'auto', right: horizontal === 'left' ? 'auto' : parent.outerWidth() - element.outerWidth() - (parent === element ? 0 : position.left) }); }, notifyEvent = function (e) { if (e.type === 'dp.change' && ((e.date && e.date.isSame(e.oldDate)) || (!e.date && !e.oldDate))) { return; } element.trigger(e); }, viewUpdate = function (e) { if (e === 'y') { e = 'YYYY'; } notifyEvent({ type: 'dp.update', change: e, viewDate: viewDate.clone() }); }, showMode = function (dir) { if (!widget) { return; } if (dir) { currentViewMode = Math.max(minViewModeNumber, Math.min(3, currentViewMode + dir)); } widget.find('.datepicker > div').hide().filter('.datepicker-' + datePickerModes[currentViewMode].clsName).show(); }, fillDow = function () { var row = $('
'), currentDate = viewDate.clone().startOf('w').startOf('d'); if (options.calendarWeeks === true) { row.append($(''); if (options.calendarWeeks) { row.append(''); } html.push(row); } clsName = ''; if (currentDate.isBefore(viewDate, 'M')) { clsName += ' old'; } if (currentDate.isAfter(viewDate, 'M')) { clsName += ' new'; } if (currentDate.isSame(date, 'd') && !unset) { clsName += ' active'; } if (!isValid(currentDate, 'd')) { clsName += ' disabled'; } if (currentDate.isSame(moment(), 'd')) { clsName += ' today'; } if (currentDate.day() === 0 || currentDate.day() === 6) { clsName += ' weekend'; } row.append(''); currentDate.add(1, 'd'); } daysView.find('tbody').empty().append(html); updateMonths(); updateYears(); updateDecades(); }, fillHours = function () { var table = widget.find('.timepicker-hours table'), currentHour = viewDate.clone().startOf('d'), html = [], row = $(''); if (viewDate.hour() > 11 && !use24Hours) { currentHour.hour(12); } while (currentHour.isSame(viewDate, 'd') && (use24Hours || (viewDate.hour() < 12 && currentHour.hour() < 12) || viewDate.hour() > 11)) { if (currentHour.hour() % 4 === 0) { row = $(''); html.push(row); } row.append(''); currentHour.add(1, 'h'); } table.empty().append(html); }, fillMinutes = function () { var table = widget.find('.timepicker-minutes table'), currentMinute = viewDate.clone().startOf('h'), html = [], row = $(''), step = options.stepping === 1 ? 5 : options.stepping; while (viewDate.isSame(currentMinute, 'h')) { if (currentMinute.minute() % (step * 4) === 0) { row = $(''); html.push(row); } row.append(''); currentMinute.add(step, 'm'); } table.empty().append(html); }, fillSeconds = function () { var table = widget.find('.timepicker-seconds table'), currentSecond = viewDate.clone().startOf('m'), html = [], row = $(''); while (viewDate.isSame(currentSecond, 'm')) { if (currentSecond.second() % 20 === 0) { row = $(''); html.push(row); } row.append(''); currentSecond.add(5, 's'); } table.empty().append(html); }, fillTime = function () { var toggle, newDate, timeComponents = widget.find('.timepicker span[data-time-component]'); if (!use24Hours) { toggle = widget.find('.timepicker [data-action=togglePeriod]'); newDate = date.clone().add((date.hours() >= 12) ? -12 : 12, 'h'); toggle.text(date.format('A')); if (isValid(newDate, 'h')) { toggle.removeClass('disabled'); } else { toggle.addClass('disabled'); } } timeComponents.filter('[data-time-component=hours]').text(date.format(use24Hours ? 'HH' : 'hh')); timeComponents.filter('[data-time-component=minutes]').text(date.format('mm')); timeComponents.filter('[data-time-component=seconds]').text(date.format('ss')); fillHours(); fillMinutes(); fillSeconds(); }, update = function () { if (!widget) { return; } fillDate(); fillTime(); }, setValue = function (targetMoment) { var oldDate = unset ? null : date; // case of calling setValue(null or false) if (!targetMoment) { unset = true; input.val(''); element.data('date', ''); notifyEvent({ type: 'dp.change', date: false, oldDate: oldDate }); update(); return; } targetMoment = targetMoment.clone().locale(options.locale); if (options.stepping !== 1) { targetMoment.minutes((Math.round(targetMoment.minutes() / options.stepping) * options.stepping) % 60).seconds(0); } if (isValid(targetMoment)) { date = targetMoment; viewDate = date.clone(); input.val(date.format(actualFormat)); element.data('date', date.format(actualFormat)); unset = false; update(); notifyEvent({ type: 'dp.change', date: date.clone(), oldDate: oldDate }); } else { if (!options.keepInvalid) { input.val(unset ? '' : date.format(actualFormat)); } notifyEvent({ type: 'dp.error', date: targetMoment }); } }, hide = function () { ///Hides the widget. Possibly will emit dp.hide var transitioning = false; if (!widget) { return picker; } // Ignore event if in the middle of a picker transition widget.find('.collapse').each(function () { var collapseData = $(this).data('collapse'); if (collapseData && collapseData.transitioning) { transitioning = true; return false; } return true; }); if (transitioning) { return picker; } if (component && component.hasClass('btn')) { component.toggleClass('active'); } widget.hide(); $(window).off('resize', place); widget.off('click', '[data-action]'); widget.off('mousedown', false); widget.remove(); widget = false; notifyEvent({ type: 'dp.hide', date: date.clone() }); input.blur(); return picker; }, clear = function () { setValue(null); }, /******************************************************************************** * * Widget UI interaction functions * ********************************************************************************/ actions = { next: function () { var navFnc = datePickerModes[currentViewMode].navFnc; viewDate.add(datePickerModes[currentViewMode].navStep, navFnc); fillDate(); viewUpdate(navFnc); }, previous: function () { var navFnc = datePickerModes[currentViewMode].navFnc; viewDate.subtract(datePickerModes[currentViewMode].navStep, navFnc); fillDate(); viewUpdate(navFnc); }, pickerSwitch: function () { showMode(1); }, selectMonth: function (e) { var month = $(e.target).closest('tbody').find('span').index($(e.target)); viewDate.month(month); if (currentViewMode === minViewModeNumber) { setValue(date.clone().year(viewDate.year()).month(viewDate.month())); if (!options.inline) { hide(); } } else { showMode(-1); fillDate(); } viewUpdate('M'); }, selectYear: function (e) { var year = parseInt($(e.target).text(), 10) || 0; viewDate.year(year); if (currentViewMode === minViewModeNumber) { setValue(date.clone().year(viewDate.year())); if (!options.inline) { hide(); } } else { showMode(-1); fillDate(); } viewUpdate('YYYY'); }, selectDecade: function (e) { var year = parseInt($(e.target).data('selection'), 10) || 0; viewDate.year(year); if (currentViewMode === minViewModeNumber) { setValue(date.clone().year(viewDate.year())); if (!options.inline) { hide(); } } else { showMode(-1); fillDate(); } viewUpdate('YYYY'); }, selectDay: function (e) { var day = viewDate.clone(); if ($(e.target).is('.old')) { day.subtract(1, 'M'); } if ($(e.target).is('.new')) { day.add(1, 'M'); } setValue(day.date(parseInt($(e.target).text(), 10))); if (!hasTime() && !options.keepOpen && !options.inline) { hide(); } }, incrementHours: function () { var newDate = date.clone().add(1, 'h'); if (isValid(newDate, 'h')) { setValue(newDate); } }, incrementMinutes: function () { var newDate = date.clone().add(options.stepping, 'm'); if (isValid(newDate, 'm')) { setValue(newDate); } }, incrementSeconds: function () { var newDate = date.clone().add(1, 's'); if (isValid(newDate, 's')) { setValue(newDate); } }, decrementHours: function () { var newDate = date.clone().subtract(1, 'h'); if (isValid(newDate, 'h')) { setValue(newDate); } }, decrementMinutes: function () { var newDate = date.clone().subtract(options.stepping, 'm'); if (isValid(newDate, 'm')) { setValue(newDate); } }, decrementSeconds: function () { var newDate = date.clone().subtract(1, 's'); if (isValid(newDate, 's')) { setValue(newDate); } }, togglePeriod: function () { setValue(date.clone().add((date.hours() >= 12) ? -12 : 12, 'h')); }, togglePicker: function (e) { var $this = $(e.target), $parent = $this.closest('ul'), expanded = $parent.find('.in'), closed = $parent.find('.collapse:not(.in)'), collapseData; if (expanded && expanded.length) { collapseData = expanded.data('collapse'); if (collapseData && collapseData.transitioning) { return; } if (expanded.collapse) { // if collapse plugin is available through bootstrap.js then use it expanded.collapse('hide'); closed.collapse('show'); } else { // otherwise just toggle in class on the two views expanded.removeClass('in'); closed.addClass('in'); } if ($this.is('span')) { $this.toggleClass(options.icons.time + ' ' + options.icons.date); } else { $this.find('span').toggleClass(options.icons.time + ' ' + options.icons.date); } // NOTE: uncomment if toggled state will be restored in show() //if (component) { // component.find('span').toggleClass(options.icons.time + ' ' + options.icons.date); //} } }, showPicker: function () { widget.find('.timepicker > div:not(.timepicker-picker)').hide(); widget.find('.timepicker .timepicker-picker').show(); }, showHours: function () { widget.find('.timepicker .timepicker-picker').hide(); widget.find('.timepicker .timepicker-hours').show(); }, showMinutes: function () { widget.find('.timepicker .timepicker-picker').hide(); widget.find('.timepicker .timepicker-minutes').show(); }, showSeconds: function () { widget.find('.timepicker .timepicker-picker').hide(); widget.find('.timepicker .timepicker-seconds').show(); }, selectHour: function (e) { var hour = parseInt($(e.target).text(), 10); if (!use24Hours) { if (date.hours() >= 12) { if (hour !== 12) { hour += 12; } } else { if (hour === 12) { hour = 0; } } } setValue(date.clone().hours(hour)); actions.showPicker.call(picker); }, selectMinute: function (e) { setValue(date.clone().minutes(parseInt($(e.target).text(), 10))); actions.showPicker.call(picker); }, selectSecond: function (e) { setValue(date.clone().seconds(parseInt($(e.target).text(), 10))); actions.showPicker.call(picker); }, clear: clear, today: function () { if (isValid(moment(), 'd')) { setValue(moment()); } }, close: hide }, doAction = function (e) { if ($(e.currentTarget).is('.disabled')) { return false; } actions[$(e.currentTarget).data('action')].apply(picker, arguments); return false; }, show = function () { ///Shows the widget. Possibly will emit dp.show and dp.change var currentMoment, useCurrentGranularity = { 'year': function (m) { return m.month(0).date(1).hours(0).seconds(0).minutes(0); }, 'month': function (m) { return m.date(1).hours(0).seconds(0).minutes(0); }, 'day': function (m) { return m.hours(0).seconds(0).minutes(0); }, 'hour': function (m) { return m.seconds(0).minutes(0); }, 'minute': function (m) { return m.seconds(0); } }; if (input.prop('disabled') || (!options.ignoreReadonly && input.prop('readonly')) || widget) { return picker; } if (input.val() !== undefined && input.val().trim().length !== 0) { setValue(parseInputDate(input.val().trim())); } else if (options.useCurrent && unset && ((input.is('input') && input.val().trim().length === 0) || options.inline)) { currentMoment = moment(); if (typeof options.useCurrent === 'string') { currentMoment = useCurrentGranularity[options.useCurrent](currentMoment); } setValue(currentMoment); } widget = getTemplate(); fillDow(); fillMonths(); widget.find('.timepicker-hours').hide(); widget.find('.timepicker-minutes').hide(); widget.find('.timepicker-seconds').hide(); update(); showMode(); $(window).on('resize', place); widget.on('click', '[data-action]', doAction); // this handles clicks on the widget widget.on('mousedown', false); if (component && component.hasClass('btn')) { component.toggleClass('active'); } widget.show(); place(); if (options.focusOnShow && !input.is(':focus')) { input.focus(); } notifyEvent({ type: 'dp.show' }); return picker; }, toggle = function () { /// Shows or hides the widget return (widget ? hide() : show()); }, parseInputDate = function (inputDate) { if (options.parseInputDate === undefined) { if (moment.isMoment(inputDate) || inputDate instanceof Date) { inputDate = moment(inputDate); } else { inputDate = moment(inputDate, parseFormats, options.useStrict); } } else { inputDate = options.parseInputDate(inputDate); } inputDate.locale(options.locale); return inputDate; }, keydown = function (e) { var handler = null, index, index2, pressedKeys = [], pressedModifiers = {}, currentKey = e.which, keyBindKeys, allModifiersPressed, pressed = 'p'; keyState[currentKey] = pressed; for (index in keyState) { if (keyState.hasOwnProperty(index) && keyState[index] === pressed) { pressedKeys.push(index); if (parseInt(index, 10) !== currentKey) { pressedModifiers[index] = true; } } } for (index in options.keyBinds) { if (options.keyBinds.hasOwnProperty(index) && typeof (options.keyBinds[index]) === 'function') { keyBindKeys = index.split(' '); if (keyBindKeys.length === pressedKeys.length && keyMap[currentKey] === keyBindKeys[keyBindKeys.length - 1]) { allModifiersPressed = true; for (index2 = keyBindKeys.length - 2; index2 >= 0; index2--) { if (!(keyMap[keyBindKeys[index2]] in pressedModifiers)) { allModifiersPressed = false; break; } } if (allModifiersPressed) { handler = options.keyBinds[index]; break; } } } } if (handler) { handler.call(picker, widget); e.stopPropagation(); e.preventDefault(); } }, keyup = function (e) { keyState[e.which] = 'r'; e.stopPropagation(); e.preventDefault(); }, change = function (e) { var val = $(e.target).val().trim(), parsedDate = val ? parseInputDate(val) : null; setValue(parsedDate); e.stopImmediatePropagation(); return false; }, attachDatePickerElementEvents = function () { input.on({ 'change': change, 'blur': options.debug ? '' : hide, 'keydown': keydown, 'keyup': keyup, 'focus': options.allowInputToggle ? show : '' }); if (element.is('input')) { input.on({ 'focus': show }); } else if (component) { component.on('click', toggle); component.on('mousedown', false); } }, detachDatePickerElementEvents = function () { input.off({ 'change': change, 'blur': blur, 'keydown': keydown, 'keyup': keyup, 'focus': options.allowInputToggle ? hide : '' }); if (element.is('input')) { input.off({ 'focus': show }); } else if (component) { component.off('click', toggle); component.off('mousedown', false); } }, indexGivenDates = function (givenDatesArray) { // Store given enabledDates and disabledDates as keys. // This way we can check their existence in O(1) time instead of looping through whole array. // (for example: options.enabledDates['2014-02-27'] === true) var givenDatesIndexed = {}; $.each(givenDatesArray, function () { var dDate = parseInputDate(this); if (dDate.isValid()) { givenDatesIndexed[dDate.format('YYYY-MM-DD')] = true; } }); return (Object.keys(givenDatesIndexed).length) ? givenDatesIndexed : false; }, indexGivenHours = function (givenHoursArray) { // Store given enabledHours and disabledHours as keys. // This way we can check their existence in O(1) time instead of looping through whole array. // (for example: options.enabledHours['2014-02-27'] === true) var givenHoursIndexed = {}; $.each(givenHoursArray, function () { givenHoursIndexed[this] = true; }); return (Object.keys(givenHoursIndexed).length) ? givenHoursIndexed : false; }, initFormatting = function () { var format = options.format || 'L LT'; actualFormat = format.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput) { var newinput = date.localeData().longDateFormat(formatInput) || formatInput; return newinput.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput2) { //temp fix for #740 return date.localeData().longDateFormat(formatInput2) || formatInput2; }); }); parseFormats = options.extraFormats ? options.extraFormats.slice() : []; if (parseFormats.indexOf(format) < 0 && parseFormats.indexOf(actualFormat) < 0) { parseFormats.push(actualFormat); } use24Hours = (actualFormat.toLowerCase().indexOf('a') < 1 && actualFormat.replace(/\[.*?\]/g, '').indexOf('h') < 1); if (isEnabled('y')) { minViewModeNumber = 2; } if (isEnabled('M')) { minViewModeNumber = 1; } if (isEnabled('d')) { minViewModeNumber = 0; } currentViewMode = Math.max(minViewModeNumber, currentViewMode); if (!unset) { setValue(date); } }; /******************************************************************************** * * Public API functions * ===================== * * Important: Do not expose direct references to private objects or the options * object to the outer world. Always return a clone when returning values or make * a clone when setting a private variable. * ********************************************************************************/ picker.destroy = function () { ///Destroys the widget and removes all attached event listeners hide(); detachDatePickerElementEvents(); element.removeData('DateTimePicker'); element.removeData('date'); }; picker.toggle = toggle; picker.show = show; picker.hide = hide; picker.disable = function () { ///Disables the input element, the component is attached to, by adding a disabled="true" attribute to it. ///If the widget was visible before that call it is hidden. Possibly emits dp.hide hide(); if (component && component.hasClass('btn')) { component.addClass('disabled'); } input.prop('disabled', true); return picker; }; picker.enable = function () { ///Enables the input element, the component is attached to, by removing disabled attribute from it. if (component && component.hasClass('btn')) { component.removeClass('disabled'); } input.prop('disabled', false); return picker; }; picker.ignoreReadonly = function (ignoreReadonly) { if (arguments.length === 0) { return options.ignoreReadonly; } if (typeof ignoreReadonly !== 'boolean') { throw new TypeError('ignoreReadonly () expects a boolean parameter'); } options.ignoreReadonly = ignoreReadonly; return picker; }; picker.options = function (newOptions) { if (arguments.length === 0) { return $.extend(true, {}, options); } if (!(newOptions instanceof Object)) { throw new TypeError('options() options parameter should be an object'); } $.extend(true, options, newOptions); $.each(options, function (key, value) { if (picker[key] !== undefined) { picker[key](value); } else { throw new TypeError('option ' + key + ' is not recognized!'); } }); return picker; }; picker.date = function (newDate) { /// ///Returns the component's model current date, a moment object or null if not set. ///date.clone() /// /// ///Sets the components model current moment to it. Passing a null value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the options.format and options.useStrict components configuration. ///Takes string, Date, moment, null parameter. /// if (arguments.length === 0) { if (unset) { return null; } return date.clone(); } if (newDate !== null && typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) { throw new TypeError('date() parameter must be one of [null, string, moment or Date]'); } setValue(newDate === null ? null : parseInputDate(newDate)); return picker; }; picker.format = function (newFormat) { ///test su ///info about para ///returns foo if (arguments.length === 0) { return options.format; } if ((typeof newFormat !== 'string') && ((typeof newFormat !== 'boolean') || (newFormat !== false))) { throw new TypeError('format() expects a sting or boolean:false parameter ' + newFormat); } options.format = newFormat; if (actualFormat) { initFormatting(); // reinit formatting } return picker; }; picker.dayViewHeaderFormat = function (newFormat) { if (arguments.length === 0) { return options.dayViewHeaderFormat; } if (typeof newFormat !== 'string') { throw new TypeError('dayViewHeaderFormat() expects a string parameter'); } options.dayViewHeaderFormat = newFormat; return picker; }; picker.extraFormats = function (formats) { if (arguments.length === 0) { return options.extraFormats; } if (formats !== false && !(formats instanceof Array)) { throw new TypeError('extraFormats() expects an array or false parameter'); } options.extraFormats = formats; if (parseFormats) { initFormatting(); // reinit formatting } return picker; }; picker.disabledDates = function (dates) { /// ///Returns an array with the currently set disabled dates on the component. ///options.disabledDates /// /// ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of ///options.enabledDates if such exist. ///Takes an [ string or Date or moment ] of values and allows the user to select only from those days. /// if (arguments.length === 0) { return (options.disabledDates ? $.extend({}, options.disabledDates) : options.disabledDates); } if (!dates) { options.disabledDates = false; update(); return picker; } if (!(dates instanceof Array)) { throw new TypeError('disabledDates() expects an array parameter'); } options.disabledDates = indexGivenDates(dates); options.enabledDates = false; update(); return picker; }; picker.enabledDates = function (dates) { /// ///Returns an array with the currently set enabled dates on the component. ///options.enabledDates /// /// ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of options.disabledDates if such exist. ///Takes an [ string or Date or moment ] of values and allows the user to select only from those days. /// if (arguments.length === 0) { return (options.enabledDates ? $.extend({}, options.enabledDates) : options.enabledDates); } if (!dates) { options.enabledDates = false; update(); return picker; } if (!(dates instanceof Array)) { throw new TypeError('enabledDates() expects an array parameter'); } options.enabledDates = indexGivenDates(dates); options.disabledDates = false; update(); return picker; }; picker.daysOfWeekDisabled = function (daysOfWeekDisabled) { if (arguments.length === 0) { return options.daysOfWeekDisabled.splice(0); } if ((typeof daysOfWeekDisabled === 'boolean') && !daysOfWeekDisabled) { options.daysOfWeekDisabled = false; update(); return picker; } if (!(daysOfWeekDisabled instanceof Array)) { throw new TypeError('daysOfWeekDisabled() expects an array parameter'); } options.daysOfWeekDisabled = daysOfWeekDisabled.reduce(function (previousValue, currentValue) { currentValue = parseInt(currentValue, 10); if (currentValue > 6 || currentValue < 0 || isNaN(currentValue)) { return previousValue; } if (previousValue.indexOf(currentValue) === -1) { previousValue.push(currentValue); } return previousValue; }, []).sort(); if (options.useCurrent && !options.keepInvalid) { var tries = 0; while (!isValid(date, 'd')) { date.add(1, 'd'); if (tries === 7) { throw 'Tried 7 times to find a valid date'; } tries++; } setValue(date); } update(); return picker; }; picker.maxDate = function (maxDate) { if (arguments.length === 0) { return options.maxDate ? options.maxDate.clone() : options.maxDate; } if ((typeof maxDate === 'boolean') && maxDate === false) { options.maxDate = false; update(); return picker; } if (typeof maxDate === 'string') { if (maxDate === 'now' || maxDate === 'moment') { maxDate = moment(); } } var parsedDate = parseInputDate(maxDate); if (!parsedDate.isValid()) { throw new TypeError('maxDate() Could not parse date parameter: ' + maxDate); } if (options.minDate && parsedDate.isBefore(options.minDate)) { throw new TypeError('maxDate() date parameter is before options.minDate: ' + parsedDate.format(actualFormat)); } options.maxDate = parsedDate; if (options.useCurrent && !options.keepInvalid && date.isAfter(maxDate)) { setValue(options.maxDate); } if (viewDate.isAfter(parsedDate)) { viewDate = parsedDate.clone().subtract(options.stepping, 'm'); } update(); return picker; }; picker.minDate = function (minDate) { if (arguments.length === 0) { return options.minDate ? options.minDate.clone() : options.minDate; } if ((typeof minDate === 'boolean') && minDate === false) { options.minDate = false; update(); return picker; } if (typeof minDate === 'string') { if (minDate === 'now' || minDate === 'moment') { minDate = moment(); } } var parsedDate = parseInputDate(minDate); if (!parsedDate.isValid()) { throw new TypeError('minDate() Could not parse date parameter: ' + minDate); } if (options.maxDate && parsedDate.isAfter(options.maxDate)) { throw new TypeError('minDate() date parameter is after options.maxDate: ' + parsedDate.format(actualFormat)); } options.minDate = parsedDate; if (options.useCurrent && !options.keepInvalid && date.isBefore(minDate)) { setValue(options.minDate); } if (viewDate.isBefore(parsedDate)) { viewDate = parsedDate.clone().add(options.stepping, 'm'); } update(); return picker; }; picker.defaultDate = function (defaultDate) { /// ///Returns a moment with the options.defaultDate option configuration or false if not set ///date.clone() /// /// ///Will set the picker's inital date. If a boolean:false value is passed the options.defaultDate parameter is cleared. ///Takes a string, Date, moment, boolean:false /// if (arguments.length === 0) { return options.defaultDate ? options.defaultDate.clone() : options.defaultDate; } if (!defaultDate) { options.defaultDate = false; return picker; } if (typeof defaultDate === 'string') { if (defaultDate === 'now' || defaultDate === 'moment') { defaultDate = moment(); } } var parsedDate = parseInputDate(defaultDate); if (!parsedDate.isValid()) { throw new TypeError('defaultDate() Could not parse date parameter: ' + defaultDate); } if (!isValid(parsedDate)) { throw new TypeError('defaultDate() date passed is invalid according to component setup validations'); } options.defaultDate = parsedDate; if (options.defaultDate && options.inline || (input.val().trim() === '' && input.attr('placeholder') === undefined)) { setValue(options.defaultDate); } return picker; }; picker.locale = function (locale) { if (arguments.length === 0) { return options.locale; } if (!moment.localeData(locale)) { throw new TypeError('locale() locale ' + locale + ' is not loaded from moment locales!'); } options.locale = locale; date.locale(options.locale); viewDate.locale(options.locale); if (actualFormat) { initFormatting(); // reinit formatting } if (widget) { hide(); show(); } return picker; }; picker.stepping = function (stepping) { if (arguments.length === 0) { return options.stepping; } stepping = parseInt(stepping, 10); if (isNaN(stepping) || stepping < 1) { stepping = 1; } options.stepping = stepping; return picker; }; picker.useCurrent = function (useCurrent) { var useCurrentOptions = ['year', 'month', 'day', 'hour', 'minute']; if (arguments.length === 0) { return options.useCurrent; } if ((typeof useCurrent !== 'boolean') && (typeof useCurrent !== 'string')) { throw new TypeError('useCurrent() expects a boolean or string parameter'); } if (typeof useCurrent === 'string' && useCurrentOptions.indexOf(useCurrent.toLowerCase()) === -1) { throw new TypeError('useCurrent() expects a string parameter of ' + useCurrentOptions.join(', ')); } options.useCurrent = useCurrent; return picker; }; picker.collapse = function (collapse) { if (arguments.length === 0) { return options.collapse; } if (typeof collapse !== 'boolean') { throw new TypeError('collapse() expects a boolean parameter'); } if (options.collapse === collapse) { return picker; } options.collapse = collapse; if (widget) { hide(); show(); } return picker; }; picker.icons = function (icons) { if (arguments.length === 0) { return $.extend({}, options.icons); } if (!(icons instanceof Object)) { throw new TypeError('icons() expects parameter to be an Object'); } $.extend(options.icons, icons); if (widget) { hide(); show(); } return picker; }; picker.tooltips = function (tooltips) { if (arguments.length === 0) { return $.extend({}, options.tooltips); } if (!(tooltips instanceof Object)) { throw new TypeError('tooltips() expects parameter to be an Object'); } $.extend(options.tooltips, tooltips); if (widget) { hide(); show(); } return picker; }; picker.useStrict = function (useStrict) { if (arguments.length === 0) { return options.useStrict; } if (typeof useStrict !== 'boolean') { throw new TypeError('useStrict() expects a boolean parameter'); } options.useStrict = useStrict; return picker; }; picker.sideBySide = function (sideBySide) { if (arguments.length === 0) { return options.sideBySide; } if (typeof sideBySide !== 'boolean') { throw new TypeError('sideBySide() expects a boolean parameter'); } options.sideBySide = sideBySide; if (widget) { hide(); show(); } return picker; }; picker.viewMode = function (viewMode) { if (arguments.length === 0) { return options.viewMode; } if (typeof viewMode !== 'string') { throw new TypeError('viewMode() expects a string parameter'); } if (viewModes.indexOf(viewMode) === -1) { throw new TypeError('viewMode() parameter must be one of (' + viewModes.join(', ') + ') value'); } options.viewMode = viewMode; currentViewMode = Math.max(viewModes.indexOf(viewMode), minViewModeNumber); showMode(); return picker; }; picker.toolbarPlacement = function (toolbarPlacement) { if (arguments.length === 0) { return options.toolbarPlacement; } if (typeof toolbarPlacement !== 'string') { throw new TypeError('toolbarPlacement() expects a string parameter'); } if (toolbarPlacements.indexOf(toolbarPlacement) === -1) { throw new TypeError('toolbarPlacement() parameter must be one of (' + toolbarPlacements.join(', ') + ') value'); } options.toolbarPlacement = toolbarPlacement; if (widget) { hide(); show(); } return picker; }; picker.widgetPositioning = function (widgetPositioning) { if (arguments.length === 0) { return $.extend({}, options.widgetPositioning); } if (({}).toString.call(widgetPositioning) !== '[object Object]') { throw new TypeError('widgetPositioning() expects an object variable'); } if (widgetPositioning.horizontal) { if (typeof widgetPositioning.horizontal !== 'string') { throw new TypeError('widgetPositioning() horizontal variable must be a string'); } widgetPositioning.horizontal = widgetPositioning.horizontal.toLowerCase(); if (horizontalModes.indexOf(widgetPositioning.horizontal) === -1) { throw new TypeError('widgetPositioning() expects horizontal parameter to be one of (' + horizontalModes.join(', ') + ')'); } options.widgetPositioning.horizontal = widgetPositioning.horizontal; } if (widgetPositioning.vertical) { if (typeof widgetPositioning.vertical !== 'string') { throw new TypeError('widgetPositioning() vertical variable must be a string'); } widgetPositioning.vertical = widgetPositioning.vertical.toLowerCase(); if (verticalModes.indexOf(widgetPositioning.vertical) === -1) { throw new TypeError('widgetPositioning() expects vertical parameter to be one of (' + verticalModes.join(', ') + ')'); } options.widgetPositioning.vertical = widgetPositioning.vertical; } update(); return picker; }; picker.calendarWeeks = function (calendarWeeks) { if (arguments.length === 0) { return options.calendarWeeks; } if (typeof calendarWeeks !== 'boolean') { throw new TypeError('calendarWeeks() expects parameter to be a boolean value'); } options.calendarWeeks = calendarWeeks; update(); return picker; }; picker.showTodayButton = function (showTodayButton) { if (arguments.length === 0) { return options.showTodayButton; } if (typeof showTodayButton !== 'boolean') { throw new TypeError('showTodayButton() expects a boolean parameter'); } options.showTodayButton = showTodayButton; if (widget) { hide(); show(); } return picker; }; picker.showClear = function (showClear) { if (arguments.length === 0) { return options.showClear; } if (typeof showClear !== 'boolean') { throw new TypeError('showClear() expects a boolean parameter'); } options.showClear = showClear; if (widget) { hide(); show(); } return picker; }; picker.widgetParent = function (widgetParent) { if (arguments.length === 0) { return options.widgetParent; } if (typeof widgetParent === 'string') { widgetParent = $(widgetParent); } if (widgetParent !== null && (typeof widgetParent !== 'string' && !(widgetParent instanceof $))) { throw new TypeError('widgetParent() expects a string or a jQuery object parameter'); } options.widgetParent = widgetParent; if (widget) { hide(); show(); } return picker; }; picker.keepOpen = function (keepOpen) { if (arguments.length === 0) { return options.keepOpen; } if (typeof keepOpen !== 'boolean') { throw new TypeError('keepOpen() expects a boolean parameter'); } options.keepOpen = keepOpen; return picker; }; picker.focusOnShow = function (focusOnShow) { if (arguments.length === 0) { return options.focusOnShow; } if (typeof focusOnShow !== 'boolean') { throw new TypeError('focusOnShow() expects a boolean parameter'); } options.focusOnShow = focusOnShow; return picker; }; picker.inline = function (inline) { if (arguments.length === 0) { return options.inline; } if (typeof inline !== 'boolean') { throw new TypeError('inline() expects a boolean parameter'); } options.inline = inline; return picker; }; picker.clear = function () { clear(); return picker; }; picker.keyBinds = function (keyBinds) { options.keyBinds = keyBinds; return picker; }; picker.debug = function (debug) { if (typeof debug !== 'boolean') { throw new TypeError('debug() expects a boolean parameter'); } options.debug = debug; return picker; }; picker.allowInputToggle = function (allowInputToggle) { if (arguments.length === 0) { return options.allowInputToggle; } if (typeof allowInputToggle !== 'boolean') { throw new TypeError('allowInputToggle() expects a boolean parameter'); } options.allowInputToggle = allowInputToggle; return picker; }; picker.showClose = function (showClose) { if (arguments.length === 0) { return options.showClose; } if (typeof showClose !== 'boolean') { throw new TypeError('showClose() expects a boolean parameter'); } options.showClose = showClose; return picker; }; picker.keepInvalid = function (keepInvalid) { if (arguments.length === 0) { return options.keepInvalid; } if (typeof keepInvalid !== 'boolean') { throw new TypeError('keepInvalid() expects a boolean parameter'); } options.keepInvalid = keepInvalid; return picker; }; picker.datepickerInput = function (datepickerInput) { if (arguments.length === 0) { return options.datepickerInput; } if (typeof datepickerInput !== 'string') { throw new TypeError('datepickerInput() expects a string parameter'); } options.datepickerInput = datepickerInput; return picker; }; picker.parseInputDate = function (parseInputDate) { if (arguments.length === 0) { return options.parseInputDate; } if (typeof parseInputDate !== 'function') { throw new TypeError('parseInputDate() sholud be as function'); } options.parseInputDate = parseInputDate; return picker; }; picker.disabledTimeIntervals = function (disabledTimeIntervals) { /// ///Returns an array with the currently set disabled dates on the component. ///options.disabledTimeIntervals /// /// ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of ///options.enabledDates if such exist. ///Takes an [ string or Date or moment ] of values and allows the user to select only from those days. /// if (arguments.length === 0) { return (options.disabledTimeIntervals ? $.extend({}, options.disabledTimeIntervals) : options.disabledTimeIntervals); } if (!disabledTimeIntervals) { options.disabledTimeIntervals = false; update(); return picker; } if (!(disabledTimeIntervals instanceof Array)) { throw new TypeError('disabledTimeIntervals() expects an array parameter'); } options.disabledTimeIntervals = disabledTimeIntervals; update(); return picker; }; picker.disabledHours = function (hours) { /// ///Returns an array with the currently set disabled hours on the component. ///options.disabledHours /// /// ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of ///options.enabledHours if such exist. ///Takes an [ int ] of values and disallows the user to select only from those hours. /// if (arguments.length === 0) { return (options.disabledHours ? $.extend({}, options.disabledHours) : options.disabledHours); } if (!hours) { options.disabledHours = false; update(); return picker; } if (!(hours instanceof Array)) { throw new TypeError('disabledHours() expects an array parameter'); } options.disabledHours = indexGivenHours(hours); options.enabledHours = false; if (options.useCurrent && !options.keepInvalid) { var tries = 0; while (!isValid(date, 'h')) { date.add(1, 'h'); if (tries === 24) { throw 'Tried 24 times to find a valid date'; } tries++; } setValue(date); } update(); return picker; }; picker.enabledHours = function (hours) { /// ///Returns an array with the currently set enabled hours on the component. ///options.enabledHours /// /// ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of options.disabledHours if such exist. ///Takes an [ int ] of values and allows the user to select only from those hours. /// if (arguments.length === 0) { return (options.enabledHours ? $.extend({}, options.enabledHours) : options.enabledHours); } if (!hours) { options.enabledHours = false; update(); return picker; } if (!(hours instanceof Array)) { throw new TypeError('enabledHours() expects an array parameter'); } options.enabledHours = indexGivenHours(hours); options.disabledHours = false; if (options.useCurrent && !options.keepInvalid) { var tries = 0; while (!isValid(date, 'h')) { date.add(1, 'h'); if (tries === 24) { throw 'Tried 24 times to find a valid date'; } tries++; } setValue(date); } update(); return picker; }; picker.viewDate = function (newDate) { /// ///Returns the component's model current viewDate, a moment object or null if not set. ///viewDate.clone() /// /// ///Sets the components model current moment to it. Passing a null value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the options.format and options.useStrict components configuration. ///Takes string, viewDate, moment, null parameter. /// if (arguments.length === 0) { return viewDate.clone(); } if (!newDate) { viewDate = date.clone(); return picker; } if (typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) { throw new TypeError('viewDate() parameter must be one of [string, moment or Date]'); } viewDate = parseInputDate(newDate); viewUpdate(); return picker; }; // initializing element and component attributes if (element.is('input')) { input = element; } else { input = element.find(options.datepickerInput); if (input.size() === 0) { input = element.find('input'); } else if (!input.is('input')) { throw new Error('CSS class "' + options.datepickerInput + '" cannot be applied to non input element'); } } if (element.hasClass('input-group')) { // in case there is more then one 'input-group-addon' Issue #48 if (element.find('.datepickerbutton').size() === 0) { component = element.find('.input-group-addon'); } else { component = element.find('.datepickerbutton'); } } if (!options.inline && !input.is('input')) { throw new Error('Could not initialize DateTimePicker without an input element'); } $.extend(true, options, dataToOptions()); picker.options(options); initFormatting(); attachDatePickerElementEvents(); if (input.prop('disabled')) { picker.disable(); } if (input.is('input') && input.val().trim().length !== 0) { setValue(parseInputDate(input.val().trim())); } else if (options.defaultDate && input.attr('placeholder') === undefined) { setValue(options.defaultDate); } if (options.inline) { show(); } return picker; }; /******************************************************************************** * * jQuery plugin constructor and defaults object * ********************************************************************************/ $.fn.datetimepicker = function (options) { return this.each(function () { var $this = $(this); if (!$this.data('DateTimePicker')) { // create a private copy of the defaults object options = $.extend(true, {}, $.fn.datetimepicker.defaults, options); $this.data('DateTimePicker', dateTimePicker($this, options)); } }); }; $.fn.datetimepicker.defaults = { format: false, dayViewHeaderFormat: 'MMMM YYYY', extraFormats: false, stepping: 1, minDate: false, maxDate: false, useCurrent: true, collapse: true, locale: moment.locale(), defaultDate: false, disabledDates: false, enabledDates: false, icons: { time: 'glyphicon glyphicon-time', date: 'glyphicon glyphicon-calendar', up: 'glyphicon glyphicon-chevron-up', down: 'glyphicon glyphicon-chevron-down', previous: 'glyphicon glyphicon-chevron-left', next: 'glyphicon glyphicon-chevron-right', today: 'glyphicon glyphicon-screenshot', clear: 'glyphicon glyphicon-trash', close: 'glyphicon glyphicon-remove' }, tooltips: { today: 'Go to today', clear: 'Clear selection', close: 'Close the picker', selectMonth: 'Select Month', prevMonth: 'Previous Month', nextMonth: 'Next Month', selectYear: 'Select Year', prevYear: 'Previous Year', nextYear: 'Next Year', selectDecade: 'Select Decade', prevDecade: 'Previous Decade', nextDecade: 'Next Decade', prevCentury: 'Previous Century', nextCentury: 'Next Century' }, useStrict: false, sideBySide: false, daysOfWeekDisabled: false, calendarWeeks: false, viewMode: 'days', toolbarPlacement: 'default', showTodayButton: false, showClear: false, showClose: false, widgetPositioning: { horizontal: 'auto', vertical: 'auto' }, widgetParent: null, ignoreReadonly: false, keepOpen: false, focusOnShow: true, inline: false, keepInvalid: false, datepickerInput: '.datepickerinput', keyBinds: { up: function (widget) { if (!widget) { return; } var d = this.date() || moment(); if (widget.find('.datepicker').is(':visible')) { this.date(d.clone().subtract(7, 'd')); } else { this.date(d.clone().add(this.stepping(), 'm')); } }, down: function (widget) { if (!widget) { this.show(); return; } var d = this.date() || moment(); if (widget.find('.datepicker').is(':visible')) { this.date(d.clone().add(7, 'd')); } else { this.date(d.clone().subtract(this.stepping(), 'm')); } }, 'control up': function (widget) { if (!widget) { return; } var d = this.date() || moment(); if (widget.find('.datepicker').is(':visible')) { this.date(d.clone().subtract(1, 'y')); } else { this.date(d.clone().add(1, 'h')); } }, 'control down': function (widget) { if (!widget) { return; } var d = this.date() || moment(); if (widget.find('.datepicker').is(':visible')) { this.date(d.clone().add(1, 'y')); } else { this.date(d.clone().subtract(1, 'h')); } }, left: function (widget) { if (!widget) { return; } var d = this.date() || moment(); if (widget.find('.datepicker').is(':visible')) { this.date(d.clone().subtract(1, 'd')); } }, right: function (widget) { if (!widget) { return; } var d = this.date() || moment(); if (widget.find('.datepicker').is(':visible')) { this.date(d.clone().add(1, 'd')); } }, pageUp: function (widget) { if (!widget) { return; } var d = this.date() || moment(); if (widget.find('.datepicker').is(':visible')) { this.date(d.clone().subtract(1, 'M')); } }, pageDown: function (widget) { if (!widget) { return; } var d = this.date() || moment(); if (widget.find('.datepicker').is(':visible')) { this.date(d.clone().add(1, 'M')); } }, enter: function () { this.hide(); }, escape: function () { this.hide(); }, //tab: function (widget) { //this break the flow of the form. disabling for now // var toggle = widget.find('.picker-switch a[data-action="togglePicker"]'); // if(toggle.length > 0) toggle.click(); //}, 'control space': function (widget) { if (widget.find('.timepicker').is(':visible')) { widget.find('.btn[data-action="togglePeriod"]').click(); } }, t: function () { this.date(moment()); }, 'delete': function () { this.clear(); } }, debug: false, allowInputToggle: false, disabledTimeIntervals: false, disabledHours: false, enabledHours: false, viewDate: false }; }));
').addClass('cw').text('#')); } while (currentDate.isBefore(viewDate.clone().endOf('w'))) { row.append($('').addClass('dow').text(currentDate.format('dd'))); currentDate.add(1, 'd'); } widget.find('.datepicker-days thead').append(row); }, isInDisabledDates = function (testDate) { return options.disabledDates[testDate.format('YYYY-MM-DD')] === true; }, isInEnabledDates = function (testDate) { return options.enabledDates[testDate.format('YYYY-MM-DD')] === true; }, isInDisabledHours = function (testDate) { return options.disabledHours[testDate.format('H')] === true; }, isInEnabledHours = function (testDate) { return options.enabledHours[testDate.format('H')] === true; }, isValid = function (targetMoment, granularity) { if (!targetMoment.isValid()) { return false; } if (options.disabledDates && granularity === 'd' && isInDisabledDates(targetMoment)) { return false; } if (options.enabledDates && granularity === 'd' && !isInEnabledDates(targetMoment)) { return false; } if (options.minDate && targetMoment.isBefore(options.minDate, granularity)) { return false; } if (options.maxDate && targetMoment.isAfter(options.maxDate, granularity)) { return false; } if (options.daysOfWeekDisabled && granularity === 'd' && options.daysOfWeekDisabled.indexOf(targetMoment.day()) !== -1) { return false; } if (options.disabledHours && (granularity === 'h' || granularity === 'm' || granularity === 's') && isInDisabledHours(targetMoment)) { return false; } if (options.enabledHours && (granularity === 'h' || granularity === 'm' || granularity === 's') && !isInEnabledHours(targetMoment)) { return false; } if (options.disabledTimeIntervals && (granularity === 'h' || granularity === 'm' || granularity === 's')) { var found = false; $.each(options.disabledTimeIntervals, function () { if (targetMoment.isBetween(this[0], this[1])) { found = true; return false; } }); if (found) { return false; } } return true; }, fillMonths = function () { var spans = [], monthsShort = viewDate.clone().startOf('y').startOf('d'); while (monthsShort.isSame(viewDate, 'y')) { spans.push($('').attr('data-action', 'selectMonth').addClass('month').text(monthsShort.format('MMM'))); monthsShort.add(1, 'M'); } widget.find('.datepicker-months td').empty().append(spans); }, updateMonths = function () { var monthsView = widget.find('.datepicker-months'), monthsViewHeader = monthsView.find('th'), months = monthsView.find('tbody').find('span'); monthsViewHeader.eq(0).find('span').attr('title', options.tooltips.prevYear); monthsViewHeader.eq(1).attr('title', options.tooltips.selectYear); monthsViewHeader.eq(2).find('span').attr('title', options.tooltips.nextYear); monthsView.find('.disabled').removeClass('disabled'); if (!isValid(viewDate.clone().subtract(1, 'y'), 'y')) { monthsViewHeader.eq(0).addClass('disabled'); } monthsViewHeader.eq(1).text(viewDate.year()); if (!isValid(viewDate.clone().add(1, 'y'), 'y')) { monthsViewHeader.eq(2).addClass('disabled'); } months.removeClass('active'); if (date.isSame(viewDate, 'y') && !unset) { months.eq(date.month()).addClass('active'); } months.each(function (index) { if (!isValid(viewDate.clone().month(index), 'M')) { $(this).addClass('disabled'); } }); }, updateYears = function () { var yearsView = widget.find('.datepicker-years'), yearsViewHeader = yearsView.find('th'), startYear = viewDate.clone().subtract(5, 'y'), endYear = viewDate.clone().add(6, 'y'), html = ''; yearsViewHeader.eq(0).find('span').attr('title', options.tooltips.nextDecade); yearsViewHeader.eq(1).attr('title', options.tooltips.selectDecade); yearsViewHeader.eq(2).find('span').attr('title', options.tooltips.prevDecade); yearsView.find('.disabled').removeClass('disabled'); if (options.minDate && options.minDate.isAfter(startYear, 'y')) { yearsViewHeader.eq(0).addClass('disabled'); } yearsViewHeader.eq(1).text(startYear.year() + '-' + endYear.year()); if (options.maxDate && options.maxDate.isBefore(endYear, 'y')) { yearsViewHeader.eq(2).addClass('disabled'); } while (!startYear.isAfter(endYear, 'y')) { html += '' + startYear.year() + ''; startYear.add(1, 'y'); } yearsView.find('td').html(html); }, updateDecades = function () { var decadesView = widget.find('.datepicker-decades'), decadesViewHeader = decadesView.find('th'), startDecade = viewDate.isBefore(moment({y: 1999})) ? moment({y: 1899}) : moment({y: 1999}), endDecade = startDecade.clone().add(100, 'y'), html = ''; decadesViewHeader.eq(0).find('span').attr('title', options.tooltips.prevCentury); decadesViewHeader.eq(2).find('span').attr('title', options.tooltips.nextCentury); decadesView.find('.disabled').removeClass('disabled'); if (startDecade.isSame(moment({y: 1900})) || (options.minDate && options.minDate.isAfter(startDecade, 'y'))) { decadesViewHeader.eq(0).addClass('disabled'); } decadesViewHeader.eq(1).text(startDecade.year() + '-' + endDecade.year()); if (startDecade.isSame(moment({y: 2000})) || (options.maxDate && options.maxDate.isBefore(endDecade, 'y'))) { decadesViewHeader.eq(2).addClass('disabled'); } while (!startDecade.isAfter(endDecade, 'y')) { html += '' + (startDecade.year() + 1) + ' - ' + (startDecade.year() + 12) + ''; startDecade.add(12, 'y'); } html += ''; //push the dangling block over, at least this way it's even decadesView.find('td').html(html); }, fillDate = function () { var daysView = widget.find('.datepicker-days'), daysViewHeader = daysView.find('th'), currentDate, html = [], row, clsName, i; if (!hasDate()) { return; } daysViewHeader.eq(0).find('span').attr('title', options.tooltips.prevMonth); daysViewHeader.eq(1).attr('title', options.tooltips.selectMonth); daysViewHeader.eq(2).find('span').attr('title', options.tooltips.nextMonth); daysView.find('.disabled').removeClass('disabled'); daysViewHeader.eq(1).text(viewDate.format(options.dayViewHeaderFormat)); if (!isValid(viewDate.clone().subtract(1, 'M'), 'M')) { daysViewHeader.eq(0).addClass('disabled'); } if (!isValid(viewDate.clone().add(1, 'M'), 'M')) { daysViewHeader.eq(2).addClass('disabled'); } currentDate = viewDate.clone().startOf('M').startOf('w').startOf('d'); for (i = 0; i < 42; i++) { //always display 42 days (should show 6 weeks) if (currentDate.weekday() === 0) { row = $('
' + currentDate.week() + '' + currentDate.date() + '
' + currentHour.format(use24Hours ? 'HH' : 'hh') + '
' + currentMinute.format('mm') + '
' + currentSecond.format('ss') + '