Source: datepicker.js

import core from 'scalejs.core';
import ko from 'knockout';
import Pikaday from 'pikaday';
import moment from 'moment';

/**
 * TODO - description
 * @module datepicker
 */

    var clone = core.object.clone;

    window.ko = ko

    function getTimezoneOffset(date) {
        date.setTime(date.getTime() + date.getTimezoneOffset()*60*1000);
        return date;
    }

    function parseDateExpression(ex) {
        if (!ex) { return; }
        var date;

        if (Date.parse(ex)) {
            date = new Date(ex);
        }

        if (ex === 'currentDate') {
            date = new Date();
        }

        if (ex.indexOf('currentDate') !== -1) {
            var expression = ex.replace('currentDate', 'new Date().getDate()')
                .replace('yr', '*365'),
                date = new Date(),
                ret = eval(expression);

            date.setDate(ret);
            date;
        }
        date.setHours(0,0,0,0);
        return date;
    }

    function convertDateFormat(d) {
        return d ? d.substring(0,2) + '/' +
                   d.substring(2,4) + '/' +
                   d.substring(4)
                 : '';
    }


    ko.bindingHandlers.datepicker = {
        init: function (element, valueAccessor) {
            var date = clone(valueAccessor()),
                errorObservable = date.errorObservable,
                errorMessage = date.errorMessage || 'Date is disabled',
                yearRange, format, rawFormat, data, raw,
                maxDate = ko.unwrap(date.maxDate),
                minDate = ko.unwrap(date.minDate),
                picker,
                previousValue,
                disableDayFn,
                disableWeekends,
                day;

            if (!ko.isObservable(date.data)) {
                console.error('Datepicker data must be bound to an observable');
                return;
            }

            data = date.data

            if (!ko.isObservable(date.raw)) {
                raw = date.data
            } else {
                raw = date.raw
            }

            date['field'] = element;

            //defaults
            date['format'] = date['format'] || 'ddd, MMM DD YYYY';
            date['yearRange'] = date['yearRange'] || 100;
            //date['maxDate'] = date['maxDate'];
            //date['minDate'] = date['minDate'];
            rawFormat = date['rawFormat'] || undefined;
            date['disableDayFn'] = date['enabledDate'];
            date['disableWeekends'] = date['disableWeekends'];
            // because date object gets muated by pikaday we lose configurations
            // disableWeekday flag needed for manual user input
            disableWeekends = date.disableWeekends;
            //binding data to observable
            date['onSelect'] = function (d) {
                //data(d);
                raw(this.getMoment().format(rawFormat));
                errorObservable && errorObservable(null);
            }

            minDate = parseDateExpression(minDate);
            maxDate = parseDateExpression(maxDate);

            // remove minDate/maxDate from date picker as it wipes it out (use disableDatFn instead)
            delete date.minDate;
            delete date.maxDate;

            function dateInRange(d) {
                var valid = true;
                if (minDate) {
                    valid = minDate <= d;
                }
                if (maxDate) {
                    valid = valid && maxDate >= d;
                }
                return !valid;
            }

            if (date['disableDayFn']) {
                day = date['disableDayFn'];
                disableDayFn = function (d){
                    if (d.getDate() == day) {
                        return dateInRange(d);
                    } else {
                        return true;
                    }
                }
                date['disableDayFn'] = disableDayFn;
            } else {
                if (minDate || maxDate) {
                    disableDayFn = dateInRange;
                }
                date['disableDayFn'] = dateInRange;
            }

        /* observable setting of min/max date disabled for now till refactor
            if (ko.isObservable(maxDate)) {
                date.maxDate = maxDate();
                maxDate.subscribe(function (mx) {
                    var newDate = parseDateExpression(mx);
                    if (picker.getDate() > newDate) {
                        picker.setDate(null);
                    }
                    picker.setMaxDate(newDate);
                });
            }
            if (ko.isObservable(minDate)) {
                date.minDate = minDate();
                minDate.subscribe(function (mn) {
                    var newDate = parseDateExpression(mn);
                    if (picker.getDate() < newDate) {
                        picker.setDate(null);
                    }
                    picker.setMinDate(newDate);
                });
            }
            */

            picker = new Pikaday( date )

            if (typeof data() === 'string') {
                // need to account for timezone offset before
                // date object returns from pikaday, else it's
                // off by one day
                var date = new Date(data());
                    date = getTimezoneOffset(date);
                    picker.setDate(date, true);
            } else {
                picker.setDate(data(),true)
            }

            raw.subscribe(function (d) {
                if (typeof d === 'string') {
                    var date = new Date(d);
                    date = getTimezoneOffset(date);
                    picker.setDate(date, true);
                } else {
                    picker.setDate(d, true);
                }
            });

            // this "workaround" is necessary for touch screens as pikaday has an issue with it
            // https://github.com/dbushell/Pikaday/issues/406
            if ('ontouchend' in document) {
                picker.el.addEventListener('mousedown', picker._onMouseDown, true);
            }

            //prevent user keypresses
            // element.onkeydown = function (event) {
            //     event.preventDefault();
            // }

            element.addEventListener('blur',function (event) {
                var dateObject;
                // prevent issues with selecting dropdown value on pikaday control
                // by returning if the user did not input a different value
                if(element.value === previousValue) { return; }
                // if the user removes the date, update the input value to blank
                if(element.value === '') { data(''); }
                dateObject = new Date(element.value);
                // if the user enters a disabled date on the datepicker, set the customError messahe
                if((disableDayFn && disableDayFn(dateObject)) || (disableWeekends && [0,6].indexOf(dateObject.getDay()) !== -1)) {
                    //element.value = previousValue; //uncomment if you want disable date to be removed automatically
                    errorObservable && errorObservable(errorMessage);
                    return;
                } else {
                    errorObservable && errorObservable(null);
                }
                // store previous value, fixes bug with selecting year from dropdown
                previousValue = element.value;
                // finally, the user has updated the date and needs to be set
                picker.setDate(element.value);
            });

            ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                picker.destroy();
            });
        }
    }