<commit_before>/*global require, module, window*/
var Calendar = function (properties) {
    'use strict';
    properties = properties || {};
    var calendar = {},
        m = window.m || require("mithril"),
        actual = new Date(),
        functionType = Object.prototype.toString.call(function () {});

    properties.time = properties.time === undefined ? true : properties.time;

    calendar.actual = m.prop(new Date(actual.getFullYear(), actual.getMonth(), actual.getDate()));

    calendar.now = properties.now ? m.prop(properties.now) : m.prop(new Date());

    calendar.mindate = properties.mindate;
    if (calendar.mindate) {
        calendar.mindate_nt = new Date(calendar.mindate.getFullYear(), calendar.mindate.getMonth(), calendar.mindate.getDate());
    }
    calendar.maxdate = properties.maxdate;
    if (calendar.maxdate) {
        calendar.maxdate_nt = new Date(calendar.maxdate.getFullYear(), calendar.maxdate.getMonth(), calendar.maxdate.getDate());
    }

    if (calendar.mindate && +calendar.now() < +calendar.mindate) {
        calendar.now(calendar.mindate);
    }
    if (calendar.maxdate && +calendar.now() > +calendar.maxdate) {
        calendar.now(calendar.maxdate);
    }

    function daysInMonth(month, year) {
        return new Date(year, month, 0).getDate();
    }

    function merge(obj1, obj2) {
        for (var p in obj2) {
            try {
                // Property in destination object set; update its value.
                if (obj2[p].constructor == Object) {
                    obj1[p] = merge(obj1[p], obj2[p]);

                } else {
                    obj1[p] = obj2[p];

                }

            } catch (e) {
                // Property in destination object not set; create it and set its value.
                obj1[p] = obj2[p];

            }
        }
        return obj1;
    }

    calendar.editYear = function(date) {
        var cal = this,
            year = date().getFullYear();
        return m('span', [
            m('span', {
                onclick: function () {
                    calendar.editingYear(true);
                },
                style: calendar.editingYear() ? 'display:none;' : 'cursor: pointer;text-decoration: underline;'
            }, year),
            m('input', {
                value: year,
                maxlength: 4,
                type: 'text',
                required: 'required',
                style: !calendar.editingYear() ? 'display:none;' : 'width: 40px;padding: 1px;',
                config: function (element) {
                    if (calendar.editingYear()) {
                        element.focus();
                        element.selectionStart = element.value.length;
                    }
                },
                onblur: function () {
                    if (+this.value < 1800 || +this.value > 2999) {
                        return;
                    }
                    if (cal.mindate && this.value < cal.mindate.getFullYear()) {
                        return;
                    }
                    if (cal.maxdate && this.value > cal.maxdate.getFullYear()) {
                        return;
                    }
                    cal.date().setFullYear(this.value);
                    cal.editingYear(false);
                }
            }),
        ]);
    };

    calendar.editingYear = m.prop(false);

    calendar.i18n = {
        monthsLong: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
        months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
        daysLong: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
        days: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
    };

    calendar.small = properties.small;

    calendar.i18n = merge(calendar.i18n, properties.i18n);

    calendar.className = {
        wrapperClass: '',
        prevBtnClass: '',
        prevBtnITagClass: '',
        dateClass: '',
        nextBtnClass: '',
        nextBtnITagClass: '',
        tableWrapperClass: '',
        tableClass: '',
        timeClass: ''
    };

    calendar.className = merge(calendar.className, properties.className);

    calendar.formatCell = properties.formatCell || function (date) {
        var style = {
            cursor: 'pointer'
        };
        var claz = '',
            cal = this;
        if (+date === +this.actual()) {
            style['background-color'] = '#00b5ad';
            style.color = '#fff';
        }
        if (+date === +this.value()) {
            style['background-color'] = '#5bbd72';
            style.color = '#fff';
        }
        if ((cal.mindate_nt && date < cal.mindate_nt) || (cal.maxdate_nt && date > cal.maxdate_nt)) {
            claz = '.disabled';
        }
        return m('td.center.aligned' + claz, {
            style: style,
            onclick: function (e) {
                e.preventDefault();
                cal.value(date);
                if (properties.onclick) {
                    properties.onclick(cal.getDate());
                }
            }
        }, [
            claz.indexOf('.disabled') < 0 ?
                m('a[href="#"]', {
                    style: style,
                    onclick: function (e) {
                        e.preventDefault();
                    }
                }, date.getDate()) :
                date.getDate()
        ]);
    };

    calendar.goToDate = function (date) {
        if (Object.prototype.toString.call(date) === functionType) {
            date = date.date();
        }
        calendar.now(date);
        if (calendar.mindate && +date < +calendar.mindate) {
            calendar.now(calendar.mindate);
        }
        if (calendar.maxdate && +date > +calendar.maxdate) {
            calendar.now(calendar.maxdate);
        }
        calendar.now = m.prop(new Date(calendar.now().getFullYear(), calendar.now().getMonth(), calendar.now().getDate()));
        calendar.date = m.prop(new Date(calendar.now().getFullYear(), calendar.now().getMonth(), 1));
        calendar.hours = m.prop(date.getHours());
        calendar.minutes = m.prop(date.getMinutes());
        calendar.value = m.prop(calendar.now());
    };

    calendar.getDate = function () {
        return new Date(this.value().getFullYear(), this.value().getMonth(), this.value().getDate(), this.hours(), this.minutes());
    };

    calendar.hours = properties.time ? m.prop(calendar.now().getHours()) : m.prop(0);
    calendar.minutes = properties.time ? m.prop(calendar.now().getMinutes()) : m.prop(0);
    calendar.now = m.prop(new Date(calendar.now().getFullYear(), calendar.now().getMonth(), calendar.now().getDate()));
    calendar.date = m.prop(new Date(calendar.now().getFullYear(), calendar.now().getMonth(), 1));
    calendar.goToDate(properties.value || calendar.now());//m.prop(calendar.now());

    calendar.setMaxDate = function (date) {
        m.startComputation();
        calendar.maxdate = date || null;
        if (calendar.maxdate) {
            calendar.maxdate_nt = new Date(calendar.maxdate.getFullYear(), calendar.maxdate.getMonth(), calendar.maxdate.getDate());
        }
        if (calendar.maxdate && +calendar.now() > +calendar.maxdate) {
            calendar.now(calendar.maxdate);
        }
        m.endComputation();
    };

    calendar.setMinDate = function (date) {
        m.startComputation();
        calendar.mindate = date || null;
        if (date) {
            calendar.mindate_nt = new Date(calendar.mindate.getFullYear(), calendar.mindate.getMonth(), calendar.mindate.getDate());
        }
        if (calendar.mindate && +calendar.now() < +calendar.mindate) {
            calendar.now(calendar.mindate);
        }
        m.endComputation();
    };

    calendar.view = function () {
        var date,
            dates,
            out = [],
            all = [],
            cal = this,
            next = true,
            year,
            previous = true,
            className = this.className;
        //add dates
        //create data view
        date = new Date(cal.date().getFullYear(), cal.date().getMonth(), 1);
        year = date.getFullYear();

        if (calendar.mindate && (cal.date().getFullYear() <= cal.mindate_nt.getFullYear())) {
            year = cal.mindate.getFullYear();
            cal.date().setFullYear(year);
            if (cal.date().getMonth() - 1 < cal.mindate.getMonth()) {
                date.setMonth(cal.mindate.getMonth());
                previous = false;
            }
            if (cal.date().getMonth() < cal.mindate.getMonth()) {
                cal.date().setMonth(cal.mindate.getMonth());
            }
        }

        if (calendar.maxdate && (cal.date().getFullYear() >= cal.maxdate_nt.getFullYear())) {
            year = cal.maxdate.getFullYear();
            cal.date().setFullYear(year);
            if (cal.date().getMonth() + 1 > cal.maxdate.getMonth()) {
                date.setMonth(cal.maxdate.getMonth());
                cal.date().setMonth(cal.maxdate.getMonth());
                next = false;
            }
        }

        date.setFullYear(year);
        date.setDate(date.getDate() - date.getDay());
        for (var i = 1; i < 43; i += 1) {
            var d = date.getDate();
            out.push(new Date(date));
            date.setDate(d + 1);
            if (i % 7 === 0) {
                all.push(out);
                out = [];
            }
        }
        dates = m.prop(all);

        return m('.sm-calendar' + className.wrapperClass + (cal.small ? '.sm-calendar-small' : ''), {
            config: function (el, init) {
                if (!init) {
                    if (el.parentNode.className.indexOf('grid') < 0) {
                        el.className += " grid";
                        if (properties.pageclass) {
                            el.className += ' page';
                        }
                        el.className = el.className.replace('row','');
                    }
                }
            }
        }, [
            m('.column' + className.prevBtnClass, {
                style: 'padding-bottom: 0;'
            }, [
                previous ? m('a[href=#].sm-calendar-arrow', {
                    onclick: function (e) {
                        e.preventDefault();
                        cal.date().setDate(cal.date().getDate() - daysInMonth(cal.date().getMonth(), cal.date().getFullYear()));
                    }
                }, [
                    m('i.sm-calendar-arrow' + className.prevBtnITagClass),
                    !cal.small ? m('span', cal.i18n.monthsLong[cal.date().getMonth() - 1  < 0 ? cal.i18n.months.length - 1 : cal.date().getMonth() - 1]) : ''
                ]) : ''
            ]),
            m('.column' + className.dataClass, {
                style: 'padding-bottom: 0;'
            }, [
                m('select',  {
                    style: 'border: 0;background: transparent;padding: 0 3px;cursor: pointer;-webkit-appearance: none;-moz-appearance: none;appearance: none;text-decoration: underline;display: inline;width: auto;',
                    value: cal.date().getMonth(),
                    config: function (el) {
                        el.value = cal.date().getMonth();
                    },
                    onchange: function () {
                        cal.date().setMonth(this.value);
                    }
                }, cal.i18n.months.map(function (item, idx) {
                    if (cal.mindate && (+cal.date().getFullYear() <= +cal.mindate_nt.getFullYear()) && idx < cal.mindate.getMonth()) {
                        return '';
                    }
                    if (cal.maxdate && (+cal.date().getFullYear() >= +cal.maxdate_nt.getFullYear()) && idx > cal.maxdate.getMonth()) {
                        return '';
                    }
                    return m('option[value=' + idx + ']', !cal.small ? cal.i18n.monthsLong[idx] : cal.i18n.months[idx]);
                })),
                calendar.editYear (cal.date)
            ]),
            m('.column' + className.nextBtnClass, {
                style: 'padding-bottom: 0;'
            }, [
                next ? m('a[href=#].sm-calendar-arrow', {
                    onclick: function (e) {
                        e.preventDefault();
                        cal.date().setMonth(cal.date().getMonth() + 1);
                    }
                }, [
                    !cal.small ? m('span', cal.i18n.monthsLong[cal.date().getMonth() + 1  >= cal.i18n.months.length ? 0 : cal.date().getMonth() + 1]) : '',
                    m('i.sm-calendar-arrow' + className.nextBtnITagClass)
                ]) : ''
            ]),
            m('.column' + className.tableWrapperClass, [
                m('table' + className.tableClass, [
                    m('thead', [
                        m('tr', cal.i18n.days.map(function (item) {
                            return m('th', {
                                style: cal.small ? 'padding: 0;' : ''
                            }, item);
                        }))
                    ]),
                    m('tbody', dates().map(function (row) {
                        return m('tr', row.map(cal.formatCell.bind(cal)));
                    }))
                ])
            ]),
            m('.column' + className.timeClass, {
                style: 'padding-top: 0;'
            }, properties.time ? [
                m('select',  {
                    style: 'border: 0;background: transparent;padding: 0 3px;cursor: pointer;-webkit-appearance: none;-moz-appearance: none;appearance: none;text-decoration: underline;display: inline;width: auto;',
                    value: cal.hours(),
                    onchange: m.withAttr('value', cal.hours),
                    config: function (element) {
                        cal.hours(element.value);
                    }
                }, Array.apply(null, new Array(24)).map(function (item, idx) {
                    idx += 1;
                    if (cal.mindate && (+cal.value() <= +cal.mindate_nt) && idx < cal.mindate.getHours()) {
                        return;
                    }
                    if (cal.maxdate && (+cal.value() >= +cal.maxdate_nt) && idx > cal.maxdate.getHours()) {
                        return;
                    }
                    return m('option[value=' + idx + ']', idx < 10 ? ('0' + idx) : idx);
                })),
                ':',
                m('select',  {
                    style: 'border: 0;background: transparent;padding: 0 3px;cursor: pointer;-webkit-appearance: none;-moz-appearance: none;appearance: none;text-decoration: underline;display: inline;width: auto;',
                    value: cal.minutes(),
                    onchange: m.withAttr('value', cal.minutes),
                    config: function (element) {
                        cal.minutes(element.value);
                    }
                }, Array.apply(null, new Array(60)).map(function (item, idx) {
                    if (cal.mindate && (+cal.value() <= +cal.mindate_nt) && idx < cal.mindate.getMinutes()) {
                        return;
                    }
                    if (cal.maxdate && (+cal.value() >= +cal.maxdate_nt) && idx > cal.maxdate.getMinutes()) {
                        return;
                    }
                    return m('option[value=' + idx + ']', idx < 10 ? ('0' + idx) : idx);
                })),
                m('a[href="#"]', {
                    style: 'padding: 0 3px;float:right;',
                    onclick: function (e) {
                        e.preventDefault();
                        cal.goToDate(new Date());
                    }
                }, 'Today')
            ] : [
                m('a[href="#"]', {
                    style: 'padding: 0 3px;float:right;',
                    onclick: function (e) {
                        e.preventDefault();
                        cal.goToDate(new Date());
                    }
                }, 'Today')
            ])
        ]);
    };
    return calendar;
};

if (typeof module !== 'undefined' && module.exports) {
    module.exports = Calendar;
}
<commit_msg>add i18n text in today button<commit_after>/*global require, module, window*/
var Calendar = function (properties) {
    'use strict';
    properties = properties || {};
    var calendar = {},
        m = window.m || require("mithril"),
        actual = new Date(),
        functionType = Object.prototype.toString.call(function () {});

    properties.time = properties.time === undefined ? true : properties.time;

    calendar.actual = m.prop(new Date(actual.getFullYear(), actual.getMonth(), actual.getDate()));

    calendar.now = properties.now ? m.prop(properties.now) : m.prop(new Date());

    calendar.mindate = properties.mindate;
    if (calendar.mindate) {
        calendar.mindate_nt = new Date(calendar.mindate.getFullYear(), calendar.mindate.getMonth(), calendar.mindate.getDate());
    }
    calendar.maxdate = properties.maxdate;
    if (calendar.maxdate) {
        calendar.maxdate_nt = new Date(calendar.maxdate.getFullYear(), calendar.maxdate.getMonth(), calendar.maxdate.getDate());
    }

    if (calendar.mindate && +calendar.now() < +calendar.mindate) {
        calendar.now(calendar.mindate);
    }
    if (calendar.maxdate && +calendar.now() > +calendar.maxdate) {
        calendar.now(calendar.maxdate);
    }

    function daysInMonth(month, year) {
        return new Date(year, month, 0).getDate();
    }

    function merge(obj1, obj2) {
        for (var p in obj2) {
            try {
                // Property in destination object set; update its value.
                if (obj2[p].constructor == Object) {
                    obj1[p] = merge(obj1[p], obj2[p]);

                } else {
                    obj1[p] = obj2[p];

                }

            } catch (e) {
                // Property in destination object not set; create it and set its value.
                obj1[p] = obj2[p];

            }
        }
        return obj1;
    }

    calendar.editYear = function(date) {
        var cal = this,
            year = date().getFullYear();
        return m('span', [
            m('span', {
                onclick: function () {
                    calendar.editingYear(true);
                },
                style: calendar.editingYear() ? 'display:none;' : 'cursor: pointer;text-decoration: underline;'
            }, year),
            m('input', {
                value: year,
                maxlength: 4,
                type: 'text',
                required: 'required',
                style: !calendar.editingYear() ? 'display:none;' : 'width: 40px;padding: 1px;',
                config: function (element) {
                    if (calendar.editingYear()) {
                        element.focus();
                        element.selectionStart = element.value.length;
                    }
                },
                onblur: function () {
                    if (+this.value < 1800 || +this.value > 2999) {
                        return;
                    }
                    if (cal.mindate && this.value < cal.mindate.getFullYear()) {
                        return;
                    }
                    if (cal.maxdate && this.value > cal.maxdate.getFullYear()) {
                        return;
                    }
                    cal.date().setFullYear(this.value);
                    cal.editingYear(false);
                }
            }),
        ]);
    };

    calendar.editingYear = m.prop(false);

    calendar.i18n = {
        monthsLong: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
        months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
        daysLong: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
        days: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
        today: 'Today'
    };

    calendar.small = properties.small;

    calendar.i18n = merge(calendar.i18n, properties.i18n);

    calendar.className = {
        wrapperClass: '',
        prevBtnClass: '',
        prevBtnITagClass: '',
        dateClass: '',
        nextBtnClass: '',
        nextBtnITagClass: '',
        tableWrapperClass: '',
        tableClass: '',
        timeClass: ''
    };

    calendar.className = merge(calendar.className, properties.className);

    calendar.formatCell = properties.formatCell || function (date) {
        var style = {
            cursor: 'pointer'
        };
        var claz = '',
            cal = this;
        if (+date === +this.actual()) {
            style['background-color'] = '#00b5ad';
            style.color = '#fff';
        }
        if (+date === +this.value()) {
            style['background-color'] = '#5bbd72';
            style.color = '#fff';
        }
        if ((cal.mindate_nt && date < cal.mindate_nt) || (cal.maxdate_nt && date > cal.maxdate_nt)) {
            claz = '.disabled';
        }
        return m('td.center.aligned' + claz, {
            style: style,
            onclick: function (e) {
                e.preventDefault();
                cal.value(date);
                if (properties.onclick) {
                    properties.onclick(cal.getDate());
                }
            }
        }, [
            claz.indexOf('.disabled') < 0 ?
                m('a[href="#"]', {
                    style: style,
                    onclick: function (e) {
                        e.preventDefault();
                    }
                }, date.getDate()) :
            date.getDate()
        ]);
    };

    calendar.goToDate = function (date) {
        if (Object.prototype.toString.call(date) === functionType) {
            date = date.date();
        }
        calendar.now(date);
        if (calendar.mindate && +date < +calendar.mindate) {
            calendar.now(calendar.mindate);
        }
        if (calendar.maxdate && +date > +calendar.maxdate) {
            calendar.now(calendar.maxdate);
        }
        calendar.now = m.prop(new Date(calendar.now().getFullYear(), calendar.now().getMonth(), calendar.now().getDate()));
        calendar.date = m.prop(new Date(calendar.now().getFullYear(), calendar.now().getMonth(), 1));
        calendar.hours = m.prop(date.getHours());
        calendar.minutes = m.prop(date.getMinutes());
        calendar.value = m.prop(calendar.now());
    };

    calendar.getDate = function () {
        return new Date(this.value().getFullYear(), this.value().getMonth(), this.value().getDate(), this.hours(), this.minutes());
    };

    calendar.hours = properties.time ? m.prop(calendar.now().getHours()) : m.prop(0);
    calendar.minutes = properties.time ? m.prop(calendar.now().getMinutes()) : m.prop(0);
    calendar.now = m.prop(new Date(calendar.now().getFullYear(), calendar.now().getMonth(), calendar.now().getDate()));
    calendar.date = m.prop(new Date(calendar.now().getFullYear(), calendar.now().getMonth(), 1));
    calendar.goToDate(properties.value || calendar.now());//m.prop(calendar.now());

    calendar.setMaxDate = function (date) {
        m.startComputation();
        calendar.maxdate = date || null;
        if (calendar.maxdate) {
            calendar.maxdate_nt = new Date(calendar.maxdate.getFullYear(), calendar.maxdate.getMonth(), calendar.maxdate.getDate());
        }
        if (calendar.maxdate && +calendar.now() > +calendar.maxdate) {
            calendar.now(calendar.maxdate);
        }
        m.endComputation();
    };

    calendar.setMinDate = function (date) {
        m.startComputation();
        calendar.mindate = date || null;
        if (date) {
            calendar.mindate_nt = new Date(calendar.mindate.getFullYear(), calendar.mindate.getMonth(), calendar.mindate.getDate());
        }
        if (calendar.mindate && +calendar.now() < +calendar.mindate) {
            calendar.now(calendar.mindate);
        }
        m.endComputation();
    };

    calendar.view = function () {
        var date,
            dates,
            out = [],
            all = [],
            cal = this,
            next = true,
            year,
            previous = true,
            className = this.className;
        //add dates
        //create data view
        date = new Date(cal.date().getFullYear(), cal.date().getMonth(), 1);
        year = date.getFullYear();

        if (calendar.mindate && (cal.date().getFullYear() <= cal.mindate_nt.getFullYear())) {
            year = cal.mindate.getFullYear();
            cal.date().setFullYear(year);
            if (cal.date().getMonth() - 1 < cal.mindate.getMonth()) {
                date.setMonth(cal.mindate.getMonth());
                previous = false;
            }
            if (cal.date().getMonth() < cal.mindate.getMonth()) {
                cal.date().setMonth(cal.mindate.getMonth());
            }
        }

        if (calendar.maxdate && (cal.date().getFullYear() >= cal.maxdate_nt.getFullYear())) {
            year = cal.maxdate.getFullYear();
            cal.date().setFullYear(year);
            if (cal.date().getMonth() + 1 > cal.maxdate.getMonth()) {
                date.setMonth(cal.maxdate.getMonth());
                cal.date().setMonth(cal.maxdate.getMonth());
                next = false;
            }
        }

        date.setFullYear(year);
        date.setDate(date.getDate() - date.getDay());
        for (var i = 1; i < 43; i += 1) {
            var d = date.getDate();
            out.push(new Date(date));
            date.setDate(d + 1);
            if (i % 7 === 0) {
                all.push(out);
                out = [];
            }
        }
        dates = m.prop(all);

        return m('.sm-calendar' + className.wrapperClass + (cal.small ? '.sm-calendar-small' : ''), {
            config: function (el, init) {
                if (!init) {
                    if (el.parentNode.className.indexOf('grid') < 0) {
                        el.className += " grid";
                        if (properties.pageclass) {
                            el.className += ' page';
                        }
                        el.className = el.className.replace('row','');
                    }
                }
            }
        }, [
            m('.column' + className.prevBtnClass, {
                style: 'padding-bottom: 0;'
            }, [
                previous ? m('a[href=#].sm-calendar-arrow', {
                    onclick: function (e) {
                        e.preventDefault();
                        cal.date().setDate(cal.date().getDate() - daysInMonth(cal.date().getMonth(), cal.date().getFullYear()));
                    }
                }, [
                    m('i.sm-calendar-arrow' + className.prevBtnITagClass),
                    !cal.small ? m('span', cal.i18n.monthsLong[cal.date().getMonth() - 1  < 0 ? cal.i18n.months.length - 1 : cal.date().getMonth() - 1]) : ''
                ]) : ''
            ]),
            m('.column' + className.dataClass, {
                style: 'padding-bottom: 0;'
            }, [
                m('select',  {
                    style: 'border: 0;background: transparent;padding: 0 3px;cursor: pointer;-webkit-appearance: none;-moz-appearance: none;appearance: none;text-decoration: underline;display: inline;width: auto;',
                    value: cal.date().getMonth(),
                    config: function (el) {
                        el.value = cal.date().getMonth();
                    },
                    onchange: function () {
                        cal.date().setMonth(this.value);
                    }
                }, cal.i18n.months.map(function (item, idx) {
                    if (cal.mindate && (+cal.date().getFullYear() <= +cal.mindate_nt.getFullYear()) && idx < cal.mindate.getMonth()) {
                        return '';
                    }
                    if (cal.maxdate && (+cal.date().getFullYear() >= +cal.maxdate_nt.getFullYear()) && idx > cal.maxdate.getMonth()) {
                        return '';
                    }
                    return m('option[value=' + idx + ']', !cal.small ? cal.i18n.monthsLong[idx] : cal.i18n.months[idx]);
                })),
                calendar.editYear (cal.date)
            ]),
            m('.column' + className.nextBtnClass, {
                style: 'padding-bottom: 0;'
            }, [
                next ? m('a[href=#].sm-calendar-arrow', {
                    onclick: function (e) {
                        e.preventDefault();
                        cal.date().setMonth(cal.date().getMonth() + 1);
                    }
                }, [
                    !cal.small ? m('span', cal.i18n.monthsLong[cal.date().getMonth() + 1  >= cal.i18n.months.length ? 0 : cal.date().getMonth() + 1]) : '',
                    m('i.sm-calendar-arrow' + className.nextBtnITagClass)
                ]) : ''
            ]),
            m('.column' + className.tableWrapperClass, [
                m('table' + className.tableClass, [
                    m('thead', [
                        m('tr', cal.i18n.days.map(function (item) {
                            return m('th', {
                                style: cal.small ? 'padding: 0;' : ''
                            }, item);
                        }))
                    ]),
                    m('tbody', dates().map(function (row) {
                        return m('tr', row.map(cal.formatCell.bind(cal)));
                    }))
                ])
            ]),
            m('.column' + className.timeClass, {
                style: 'padding-top: 0;'
            }, properties.time ? [
                m('select',  {
                    style: 'border: 0;background: transparent;padding: 0 3px;cursor: pointer;-webkit-appearance: none;-moz-appearance: none;appearance: none;text-decoration: underline;display: inline;width: auto;',
                    value: cal.hours(),
                    onchange: m.withAttr('value', cal.hours),
                    config: function (element) {
                        cal.hours(element.value);
                    }
                }, Array.apply(null, new Array(24)).map(function (item, idx) {
                    idx += 1;
                    if (cal.mindate && (+cal.value() <= +cal.mindate_nt) && idx < cal.mindate.getHours()) {
                        return;
                    }
                    if (cal.maxdate && (+cal.value() >= +cal.maxdate_nt) && idx > cal.maxdate.getHours()) {
                        return;
                    }
                    return m('option[value=' + idx + ']', idx < 10 ? ('0' + idx) : idx);
                })),
                ':',
                m('select',  {
                    style: 'border: 0;background: transparent;padding: 0 3px;cursor: pointer;-webkit-appearance: none;-moz-appearance: none;appearance: none;text-decoration: underline;display: inline;width: auto;',
                    value: cal.minutes(),
                    onchange: m.withAttr('value', cal.minutes),
                    config: function (element) {
                        cal.minutes(element.value);
                    }
                }, Array.apply(null, new Array(60)).map(function (item, idx) {
                    if (cal.mindate && (+cal.value() <= +cal.mindate_nt) && idx < cal.mindate.getMinutes()) {
                        return;
                    }
                    if (cal.maxdate && (+cal.value() >= +cal.maxdate_nt) && idx > cal.maxdate.getMinutes()) {
                        return;
                    }
                    return m('option[value=' + idx + ']', idx < 10 ? ('0' + idx) : idx);
                })),
                m('a[href="#"]', {
                    style: 'padding: 0 3px;float:right;',
                    onclick: function (e) {
                        e.preventDefault();
                        cal.goToDate(new Date());
                    }
                }, cal.i18n.today)
            ] : [
                m('a[href="#"]', {
                    style: 'padding: 0 3px;float:right;',
                    onclick: function (e) {
                        e.preventDefault();
                        cal.goToDate(new Date());
                    }
                }, cal.i18n.today)
            ])
        ]);
    };
    return calendar;
};

if (typeof module !== 'undefined' && module.exports) {
    module.exports = Calendar;
}
<|endoftext|>


<commit_before>// @flow
import type { HKT2 } from './HKT'
import type { Semigroupoid } from './Semigroupoid'

export interface Category<C> extends Semigroupoid<C> {
  id(): HKT2<C, any, any>
}
<commit_msg>the monoid of endomorphisms<commit_after>// @flow
import type { HKT2 } from './HKT'
import type { Semigroupoid } from './Semigroupoid'
import type { Monoid } from './Monoid'

export interface Category<C> extends Semigroupoid<C> {
  id(): HKT2<C, any, any>
}

// the set of arrows from A to B
export type Hom<C, A, B> = HKT2<C, A, B>;

// the set of arrows from A to A
export type Endo<C, A> = HKT2<C, A, A>;

// for any object A in any category C, the set of arrows from A to A
// is a monoid under the composition operation of C
export function getMonoid<C, A>(category: Category<C>): Monoid<Endo<C, A>> {
  return {
    empty(): Endo<C, A> {
      return category.id()
    },
    concat(x: Endo<C, A>, y: Endo<C, A>): Endo<C, A> {
      return category.compose(x, y)
    }
  }
}
<|endoftext|>


<commit_before>import _        from "underscore";
import $        from "jquery";
import BaseView from "../BaseView";

export default class CheckboxField extends BaseView {

	className() {
		return 'ui-input ui-input-checkbox';
	}

	constructor(options) {
		super(options);

		this.setDefaultsOptions({
			fieldId:    null,
			field:      null,
			label:      null,
			trueValue:  'true',
			falseValue: 'false',
			disabled:   false,
			checked:    false,
			invalid:    null
		});

		if (!this.options.field)
			throw new Error('CheckboxField field option is required');

		this.addEvents({
			'change input': 'onChange'
		});
	}

	onRender(rendered) {
		if (rendered) return;

		/*

				<input type="checkbox" id="newsletterSignup" data-field="newsletter" data-true-value="true" data-false-value="false" />

<%= __('Registrati alla newsletter') %>

		*/

		this.$el.empty();
		this.cache.$check    = $('
').addClass('check');
		this.cache.$checkbox = $('<input>').attr({
			'type':             'checkbox',
			'id':               this.options.fieldId,
			'data-field':       this.options.field,
			'data-true-value':  this.options.trueValue,
			'data-false-value': this.options.falseValue
		});
		this.cache.$checkLabel = $('').attr({
			'for': this.options.fieldId
		});
		this.cache.$label = $('').attr({
			'for': this.options.fieldId
		}).text(this.options.label);

		this.cache.$check.append(
			this.cache.$checkbox,
			this.cache.$checkLabel
		);

		this.$el.append(
			this.cache.$check,
			this.cache.$label
		);

		if(this.options.invalid)
			this.$el.append($('
').addClass('ui-input-invalid').attr('data-invalid-field', this.options.field));

		if (this.options.checked) {
			this.setValue(this.options.trueValue);
		}

		if (this.options.disabled) this.setEnabled(false);

		return this;
	}

	toggle() {
		let newValue = '';
		if (this.cache.$checkbox.is(':checked')) {
			newValue = this.options.trueValue;
		} else {
			newValue = this.options.falseValue;
		}
		this.setValue(newValue);
	}

	setValue(newValue, options) {
		options = _.defaults(options || {}, {
			silent: false
		});
		this.options.checked = newValue;
		let isActive = this.options.checked === this.options.trueValue;
		this.cache.$checkbox.prop('checked', isActive );
		if (isActive) {
			this.$el.addClass('active');
		} else {
			this.$el.removeClass('active');
		}
		if (!options.silent) this.trigger('change', this.options.checked);
	}

	getValue() {
		return this.options.checked;
	}

	onChange($ev, newValue) {
		this.toggle();
	}

	setEnabled(enabled) {
		// Enable
		if (enabled && this.disabled) {
			this.$el.removeClass('disabled');
			this.disabled = false;
			if (this.cache.$checkbox) this.cache.$checkbox.removeAttr('disabled');
		}
		// Disable
		if (!enabled && !this.disabled) {
			this.$el.addClass('disabled');
			this.disabled = true;
			if (this.cache.$checkbox) this.cache.$checkbox.attr('disabled', 'disabled');
		}
	}
}
<commit_msg>Added callback function to generate an html label for CheckboxField<commit_after>import _        from "underscore";
import $        from "jquery";
import BaseView from "../BaseView";

export default class CheckboxField extends BaseView {

	className() {
		return 'ui-input ui-input-checkbox';
	}

	constructor(options) {
		super(options);

		this.setDefaultsOptions({
			fieldId:    null,
			field:      null,
			label:      null,
			trueValue:  'true',
			falseValue: 'false',
			disabled:   false,
			checked:    false,
			invalid:    null
		});

		if (!this.options.field)
			throw new Error('CheckboxField field option is required');

		this.addEvents({
			'change input': 'onChange'
		});
	}

	onRender(rendered) {
		if (rendered) return;

		/*

				<input type="checkbox" id="newsletterSignup" data-field="newsletter" data-true-value="true" data-false-value="false" />

<%= __('Registrati alla newsletter') %>

		*/

		this.$el.empty();
		this.cache.$check    = $('
').addClass('check');
		this.cache.$checkbox = $('<input>').attr({
			'type':             'checkbox',
			'id':               this.options.fieldId,
			'data-field':       this.options.field,
			'data-true-value':  this.options.trueValue,
			'data-false-value': this.options.falseValue
		});
		this.cache.$checkLabel = $('').attr({
			'for': this.options.fieldId
		});
		this.cache.$label = $('').attr({
			'for': this.options.fieldId
		});

		if (_.isFunction(this.options.label)) {
			this.cache.$label.html(this.options.label());
		} else {
			this.cache.$label.text(this.options.label);
		}

		this.cache.$check.append(
			this.cache.$checkbox,
			this.cache.$checkLabel
		);

		this.$el.append(
			this.cache.$check,
			this.cache.$label
		);

		if(this.options.invalid)
			this.$el.append($('
').addClass('ui-input-invalid').attr('data-invalid-field', this.options.field));

		if (this.options.checked) {
			this.setValue(this.options.trueValue);
		}

		if (this.options.disabled) this.setEnabled(false);

		return this;
	}

	toggle() {
		let newValue = '';
		if (this.cache.$checkbox.is(':checked')) {
			newValue = this.options.trueValue;
		} else {
			newValue = this.options.falseValue;
		}
		this.setValue(newValue);
	}

	setValue(newValue, options) {
		options = _.defaults(options || {}, {
			silent: false
		});
		this.options.checked = newValue;
		let isActive = this.options.checked === this.options.trueValue;
		this.cache.$checkbox.prop('checked', isActive );
		if (isActive) {
			this.$el.addClass('active');
		} else {
			this.$el.removeClass('active');
		}
		if (!options.silent) this.trigger('change', this.options.checked);
	}

	getValue() {
		return this.options.checked;
	}

	onChange($ev, newValue) {
		this.toggle();
	}

	setEnabled(enabled) {
		// Enable
		if (enabled && this.disabled) {
			this.$el.removeClass('disabled');
			this.disabled = false;
			if (this.cache.$checkbox) this.cache.$checkbox.removeAttr('disabled');
		}
		// Disable
		if (!enabled && !this.disabled) {
			this.$el.addClass('disabled');
			this.disabled = true;
			if (this.cache.$checkbox) this.cache.$checkbox.attr('disabled', 'disabled');
		}
	}
}
<|endoftext|>


<commit_before>/**
 * Created by Administrator on 15-7-26.
 */

(function(window,undefined )
{
    function makeData(data,template,forIndex)
    {
        var html=[];
        var tpl = template;
        if( Breeze.isObject(data,true)  && !Breeze.isEmpty(data) )
        {
            var flag=false;
            for(var i in data )
            {
               if( Breeze.isObject(data[i],true)  )
               {
                   var val = makeData(data[i],template,i);
                   if( val.length > 0 )
                   {
                       html.push( val ) ;
                   }
                   flag=true;

               }else
               {
                    tpl = tpl.replace(new RegExp('{'+ i.replace(/\s+/,'')+'}','ig'),data[i]);
                    tpl = tpl.replace(/\{column\}/ig,i);
               }
            }
            if( !flag  )
            {
                tpl=tpl.replace(/\{forIndex\}/ig,forIndex);
                tpl=tpl.replace(/\{.*?\}/,'');
                html.push( tpl )
            }
        }
        return html;
    }

    function merge_option(target,column,option)
    {
        var opt={}
        if( typeof column === 'string' )
        {
            if( Breeze.isFunction(option) )
            {
                opt.column=column;
                opt.callback=option;

            }else if( Breeze.isObject(option) )
            {
                opt=option;
            }

        }else if( Breeze.isObject(column) )
        {
            opt=column;
        }
        return Breeze.extend( target, opt  );
    }

    function DataGrid( target )
    {

        var theadTemplate='';
        var tbodyTemplate='';
        var makeHtml=[];
        var dataRender = null;
        var thead="{value}	";
        var tbody="{value}	";
        var container="
{value}
";
        var template="
\r\n
{theadTemplate}
\r\n
{tbodyTemplate}
\r\n
";
        var columnItem={};
        var plus_data={
            'template':{},
            'option':{}
        }

        /**
         * 根据数据项编译成html
         */
        var doMake=function()
        {
            if( dataRender )
            {
                if( target instanceof Breeze )
                {
                    var html=  template.replace('{theadTemplate}', theadTemplate )
                    html = html.replace('{tbodyTemplate}',  tbodyTemplate );

                    html = Breeze( html );
                    target.html( html )
                    bindAction( html );

                    target.find('[data-bind]').each(function(elem){

                        var index = this.property('data-index');
                        var name = this.property('data-bind');
                        var item = dataRender.indexToItem( index );
                        var bind = new BindData()
                            bind.bind(item,name);
                          this.addEventListener(BreezeEvent.BLUR,function(event){

                              var name = this.property('data-bind');
                              var value = this.property('value')
                                  bind.setProperty(name,value)
                          })
                    })

                }
            }
        }

        // 为每行绑定动作行为
        var bindAction=function( target )
        {
            target.find('[data-action]').each(function(elem,index){

                var action = this.property('data-action');
                if( plus_data.option[ action ] )
                {
                    var option = plus_data.option[ action ];
                    if( option.cursor )
                        this.style('cursor', option.cursor );

                    this.addEventListener( option.eventType , function(event)
                    {
                        var index =  this.property('data-index');
                        if( typeof option.callback ==='function' )
                        {
                            option.callback.call(this,index, dataRender, event );
                        }
                    })
                }
            })
        }

        this.plus=function(action,column,defualt,option)
        {
            option = merge_option(defualt,column,option || {} );
            option.template=option.template.replace(/(\<\s*(\w+))/ig,function(){

                var attr = [];
                var tag = RegExp.$2.toLowerCase();
                if( option.bindable )
                {
                    attr.push('data-bind="{column}"');
                }
                if( tag==='input' )
                {
                    attr.push('value="{value}" name="{column}"');

                }else if( tag==='select' || tag==='textarea' )
                {
                    attr.push('name="{column}"');
                }
                return RegExp.$1+' data-index="{key}" data-action="'+action+'" '+attr.join(' ');
            });

            if( !plus_data.template[ column ] )
            {
                plus_data.template[ column ]=[];
            }
            plus_data.template[ column ].push( option.template );
            plus_data.option[ action ]= option;
        }

        /**
         * 编译一个显示列名行的模板
         * @param columns
         * @param thead
         * @param tbody
         * @returns {DataGrid}
         */
        this.makeTemplate= function(columns,thead,tbody)
        {
            if( Breeze.isObject(columns,true) && ( theadTemplate==='' || tbodyTemplate==='' ) )
            {
                theadTemplate='';
                tbodyTemplate='';

                for( var i in  columns )
                {
                    var field = columns[i];
                    i = i.replace(/\s+/,'');

                    if( Breeze.isObject( field ) && ( item.icon || item.label ) )
                    {
                        field='';
                        field+=''+( item.label || '' )+'';
                    }

                   var tb=tbody;
                   if( plus_data.template[ i ] )
                   {
                       tb = tbody.replace('{value}', plus_data.template[ i ].join('\r\n') );
                   }

                   tb = tb.replace('{value}', '{item.'+ i.replace(/\s+/,'')+'}' );
                   tbodyTemplate += tb.replace(/\{column\}/g, i );
                   theadTemplate += thead.replace('{value}', field ).replace(/\{column\}/g, i );
                }

                theadTemplate = container.replace('{value}', theadTemplate );
                tbodyTemplate = container.replace('{value}', tbodyTemplate );
                tbodyTemplate='<? foreach(data as key item){ ?>'+tbodyTemplate+'<? } ?>';
            }
            return this;
        }

        /**
         * 指定一个自定义列名行的模板
         * @param template
         * @returns {DataGrid}
         */
        this.theadTemplate=function( template )
        {
            theadTemplate = template;
            return this;
        }

        /**
         * 指定一个自定义内容行的模板
         * @param template
         * @returns {DataGrid}
         */
        this.tbodyTemplate=function( template )
        {
            tbodyTemplate = template;
            return this;
        }

        /**
         * 在模板中添加需要编辑的操作
         * @param column
         * @param option
         */
        this.edit=function(column,option)
        {
            this.plus('edit',column,{
                'template':'编辑',
                'callback':null,
                'eventType':MouseEvent.CLICK,
                'cursor':'pointer'
            }, option );
            return this;
        }

        /**
         * 在模板中添加需要移除的操作
         * @param column
         * @param option
         */
        this.remove=function(column,option)
        {
            this.plus('remove',column,{
                'template':'删除',
                'callback':null,
                'eventType':MouseEvent.CLICK,
                'cursor':'pointer'
            },option);
            return this;
        }

        /**
         * 设置指定的列名
         * @param columns
         * @returns {DataGrid}
         */
        this.columns=function( columns )
        {
            columnItem = columns;
            if ( Breeze.isString(columns) )
            {
                columnItem = columns.split(',')
            }
            return this;
        }

        /**
         * 设置指定列中需要设置的组件
         * @param column
         * @param option
         * @returns {DataGrid}
         */
        this.component=function(column,option)
        {
            this.plus('component',column,{
                'eventType':[MouseEvent.CLICK],
                'template':'<input />',
                'dataGroup':[],
                'property':{},
                'callback':null,
                'bindable':false
            }, option );
            return this;
        }

        /**
         * 设置数据属性
         * @param data
         * @returns {DataGrid}
         */
        this.dataProfile=function(data)
        {
            this.makeTemplate(columnItem, thead, tbody);
            this.dataRender().source( data );
            return this;
        }

        /**
         * 获取数据渲染项
         * @returns {DataRender}
         */
        this.dataRender=function()
        {
            if( !dataRender )
            {
                dataRender=new DataRender();
                dataRender.addEventListener(DataRenderEvent.ITEM_ADD,function(event){

                    if( !isNaN(event.index) )
                    {
                        var list = Breeze('[data-row]:gt('+event.index+')', target )
                        Breeze('[data-row="'+event.index+'"]',target).removeElement();
                        list.each(function(elem){
                            var val= this.property('data-row');
                            this.property('data-row', val-1 );
                            Breeze('[data-index]', elem ).property('data-index',  val-1 )
                        })

                    }else
                    {
                        var html =  template.replace('{theadTemplate}', theadTemplate ).replace('{tbodyTemplate}',tbodyTemplate );
                        var  TPL = new Template();
                        TPL.assign('data', dataRender.toArray() )
                        html = TPL.render( html , true )

                        html = Breeze( html );
                        target.html( html )
                        bindAction( html );

                    }
                });
            }
            return dataRender
        }

    }

   window.DataGrid= DataGrid;

})( window )
<commit_msg>index<commit_after>/**
 * Created by Administrator on 15-7-26.
 */

(function(window,undefined )
{

    function merge_option(target,column,option)
    {
        var opt={}
        if( typeof column === 'string' )
        {
            if( Breeze.isFunction(option) )
            {
                opt.column=column;
                opt.callback=option;

            }else if( Breeze.isObject(option) )
            {
                opt=option;
            }

        }else if( Breeze.isObject(column) )
        {
            opt=column;
        }
        return Breeze.extend( target, opt  );
    }

    function DataGrid( target )
    {

        var theadTemplate='';
        var tbodyTemplate='';
        var dataRender = null;
        var thead="{value}	";
        var tbody="{value}	";
        var container="
{value}
";
        var template="
\r\n
{theadTemplate}
\r\n
{tbodyTemplate}
\r\n
";
        var columnItem={};
        var plus_data={
            'template':{},
            'option':{}
        }

        // 为每行绑定动作行为
        var bindAction=function( target )
        {
            target.find('[data-action]').each(function(elem,index){

                var action = this.property('data-action');
                if( plus_data.option[ action ] )
                {
                    var option = plus_data.option[ action ];
                    if( option.cursor )
                        this.style('cursor', option.cursor );

                    this.addEventListener( option.eventType , function(event)
                    {
                        var index =  this.property('data-index');
                        if( typeof option.callback ==='function' )
                        {
                            option.callback.call(this,index, dataRender, event );
                        }
                    })
                }
            })

            target.find('[data-bind]').each(function(elem){

                var index = this.property('data-index');
                var name = this.property('data-bind');
                var item = dataRender.indexToItem( index );
                var bind = new BindData()
                bind.bind(item,name);
                this.addEventListener(BreezeEvent.BLUR,function(event){

                    var name = this.property('data-bind');
                    var value = this.property('value')
                    bind.setProperty(name,value)
                })
            })
        }

        this.plus=function(action,column,defualt,option)
        {
            option = merge_option(defualt,column,option || {} );
            option.template=option.template.replace(/(\<\s*(\w+))/ig,function(){

                var attr = [];
                var tag = RegExp.$2.toLowerCase();
                if( option.bindable )
                {
                    attr.push('data-bind="{column}"');
                }
                if( tag==='input' )
                {
                    attr.push('value="{value}" name="{column}"');

                }else if( tag==='select' || tag==='textarea' )
                {
                    attr.push('name="{column}"');
                }
                return RegExp.$1+' data-index="{key}" data-action="'+action+'" '+attr.join(' ');
            });

            if( !plus_data.template[ column ] )
            {
                plus_data.template[ column ]=[];
            }
            plus_data.template[ column ].push( option.template );
            plus_data.option[ action ]= option;
        }

        /**
         * 编译一个显示列名行的模板
         * @param columns
         * @param thead
         * @param tbody
         * @returns {DataGrid}
         */
        this.makeTemplate= function(columns,thead,tbody)
        {
            if( Breeze.isObject(columns,true) && ( theadTemplate==='' || tbodyTemplate==='' ) )
            {
                theadTemplate='';
                tbodyTemplate='';

                for( var i in  columns )
                {
                    var field = columns[i];
                    i = i.replace(/\s+/,'');

                    if( Breeze.isObject( field ) && ( item.icon || item.label ) )
                    {
                        field='';
                        field+=''+( item.label || '' )+'';
                    }

                   var tb=tbody;
                   if( plus_data.template[ i ] )
                   {
                       tb = tbody.replace('{value}', plus_data.template[ i ].join('\r\n') );
                   }

                   tb = tb.replace('{value}', '{item.'+ i.replace(/\s+/,'')+'}' );
                   tbodyTemplate += tb.replace(/\{column\}/g, i );
                   theadTemplate += thead.replace('{value}', field ).replace(/\{column\}/g, i );
                }

                theadTemplate = container.replace('{value}', theadTemplate );
                tbodyTemplate = container.replace('{value}', tbodyTemplate );
                tbodyTemplate='<? foreach(data as key item){ ?>'+tbodyTemplate+'<? } ?>';
            }
            return this;
        }

        /**
         * 指定一个自定义列名行的模板
         * @param template
         * @returns {DataGrid}
         */
        this.theadTag=function( tag )
        {
            var match =  tag.match(/<?(\w+)(\s+[^>])\/?>?/);
            thead = '<'+match[1]+match[2]+'>{value}</'+match[1]+'>';
            return this;
        }

        /**
         * 指定一个自定义内容行的模板
         * @param template
         * @returns {DataGrid}
         */
        this.tbodyTag=function( tag )
        {
            var match =  tag.match(/<?(\w+)(\s+[^>])\/?>?/);
            tbody = '<'+match[1]+match[2]+'>{value}</'+match[1]+'>';
            return this;
        }

        /**
         * 在模板中添加需要编辑的操作
         * @param column
         * @param option
         */
        this.edit=function(column,option)
        {
            this.plus('edit',column,{
                'template':'编辑',
                'callback':null,
                'eventType':MouseEvent.CLICK,
                'cursor':'pointer'
            }, option );
            return this;
        }

        /**
         * 在模板中添加需要移除的操作
         * @param column
         * @param option
         */
        this.remove=function(column,option)
        {
            this.plus('remove',column,{
                'template':'删除',
                'callback':null,
                'eventType':MouseEvent.CLICK,
                'cursor':'pointer'
            },option);
            return this;
        }

        /**
         * 设置指定的列名
         * @param columns
         * @returns {DataGrid}
         */
        this.columns=function( columns )
        {
            columnItem = columns;
            if ( Breeze.isString(columns) )
            {
                columnItem = columns.split(',')
            }
            return this;
        }

        /**
         * 设置指定列中需要设置的组件
         * @param column
         * @param option
         * @returns {DataGrid}
         */
        this.component=function(column,option)
        {
            this.plus('component',column,{
                'eventType':[MouseEvent.CLICK],
                'template':'<input />',
                'dataGroup':[],
                'property':{},
                'callback':null,
                'bindable':false
            }, option );
            return this;
        }

        /**
         * 设置数据属性
         * @param data
         * @returns {DataGrid}
         */
        this.dataProfile=function(data)
        {
            this.makeTemplate(columnItem, thead, tbody);
            this.dataRender().source( data );
            return this;
        }

        /**
         * 获取数据渲染项
         * @returns {DataRender}
         */
        this.dataRender=function()
        {
            if( !dataRender )
            {
                dataRender=new DataRender();
                dataRender.addEventListener(DataRenderEvent.ITEM_ADD,function(event){

                    if( !isNaN(event.index) )
                    {
                        var list = Breeze('[data-row]:gt('+event.index+')', target )
                        Breeze('[data-row="'+event.index+'"]',target).removeElement();
                        list.each(function(elem){
                            var val= this.property('data-row');
                            this.property('data-row', val-1 );
                            Breeze('[data-index]', elem ).property('data-index',  val-1 )
                        })

                    }else
                    {
                        var html =  template.replace('{theadTemplate}', theadTemplate ).replace('{tbodyTemplate}',tbodyTemplate );
                        var  TPL = new Template();
                        TPL.assign('data', dataRender.toArray() )
                        html = TPL.render( html , true )
                        html = Breeze( html );
                        target.html( html )
                        bindAction( html );
                    }
                });
            }
            return dataRender
        }

    }

   window.DataGrid= DataGrid;

})( window )
<|endoftext|>


<commit_before>var extend = require('util')._extend;
var util = require('../../util');
var config = util.config;
var agentConfig = {maxSockets: config.maxSockets || 10};
var httpAgent = new (require('http').Agent)(agentConfig);
var httpsAgent = new (require('https').Agent)(agentConfig);

module.exports = function(req, res, next) {
	var protocol = req.options && req.options.protocol;
	if (!util.isWebProtocol(protocol)) {
		next();
		return;
	}

	var options = extend({}, req.options);
	var headers = extend({}, req.headers);
	if (options.proxy) {
		headers['x-from-proxy'] = config.name;
		headers.Host = headers.host;//tencent的服务器不识别小写的host
		if (protocol == 'https:') {
			headers[config.remoteAddrHead] = options.host;
		}
	} else {
		headers.host = headers.Host = options.hostname + (options.port ? ':' + options.port : '');
	}
	options.agent = protocol == 'https:' ? httpsAgent : httpAgent;
	options.rejectUnauthorized = false;
	options.headers = headers;
	options.method = req.method;
	options.hostname = null;
	req.request(options);
};<commit_msg>https-proxy<commit_after>var extend = require('util')._extend;
var util = require('../../util');
var config = util.config;
var agentConfig = {maxSockets: config.maxSockets || 10};
var httpAgent = new (require('http').Agent)(agentConfig);
var httpsAgent = new (require('https').Agent)(agentConfig);

module.exports = function(req, res, next) {
	var protocol = req.options && req.options.protocol;
	if (!util.isWebProtocol(protocol)) {
		next();
		return;
	}

	var options = extend({}, req.options);
	var headers = extend({}, req.headers);
	if (options.proxy) {
		headers['x-from-proxy'] = config.name;
		headers.Host = headers.host;//tencent的服务器不识别小写的host
		if (protocol == 'https:') {
			headers[config.remoteAddrHead] = options.host;
			options.protocol = 'http:';
		}
	} else {
		headers.host = headers.Host = options.hostname + (options.port ? ':' + options.port : '');
	}
	options.agent = protocol == 'https:' ? httpsAgent : httpAgent;
	options.rejectUnauthorized = false;
	options.headers = headers;
	options.method = req.method;
	options.hostname = null;
	req.request(options);
};<|endoftext|>


<commit_before>import 'babel-polyfill';

//async function enter() {
//  handle1();
//  await handle2();
//  handle3();
//}
//
//function handle1() {
//  console.log('handle1 enter');
//}
//
//function handle2() {
//  console.log('handle2 enter');
//  return new Promise((resolve) => {
//    setTimeout(() => {
//      resolve();
//    }, 5000);
//  });
//}
//
//function handle3() {
//  console.log('handle3 enter');
//}
//
//enter();

//----------------------------------------------------------------------

function enter(str1, str2) {
  return new Promise((resolve, reject) => {
    let random = Math.random();
    if (random > 0.5) {
      reject(new Error('failed, try again'));
    } else {
      resolve(str1 + str2 + ' : ' + random);
    }
  });
}

function retry(fn, args, retryLeft = 3) {
  console.log('args: ', args);
  return fn.apply(null, args).catch(function(err) {
    console.log(fn.name + ' failed: ', err, 'retry left: ', retryLeft);
    if (retryLeft <= 0) {
      console.log('max retry approach');
      throw err;
    }
    return retry(fn, args, retryLeft - 1);
  });
}

function enterRetry() {
  return retry(enter, arguments);
}

enterRetry('name is ', 'jonathan').then((_) => {
  console.log('final', _);
}, (err) => console.log('wrapped err: ', err));<commit_msg>Save.<commit_after><|endoftext|>


<commit_before>//only method, sub and unsub are valid messages
//so following fields would only required
var WAITON_MESSAGE_FIELDS = ['msg', 'id', 'method', 'name'];

wrapSession = function(sessionProto) {

  //store the currently running DDP message per session
  //we have to add this to the top of the waiting message
  var currentlyProcessingDDPMessage = {};

  var originalProcessMessage = sessionProto.processMessage;
  sessionProto.processMessage = function(msg) {
    if(Apm.connected) {
      //only add apmInfo if it is connected
      var apmInfo = {
        session: this.id,
        userId: this.userId
      };

      if(msg.msg == 'method' || msg.msg == 'sub') {
        apmInfo.trace = Apm.tracer.start(this, msg);

        var waitOnMessages = this.inQueue.map(function(msg) {
          return _.pick(msg, WAITON_MESSAGE_FIELDS);
        });

        //add currently processing ddp message if exists
        if(this.workerRunning) {
          waitOnMessages.unshift(_.pick(currentlyProcessingDDPMessage[this.id], WAITON_MESSAGE_FIELDS));
        }

        var startData = { userId: this.userId };
        Apm.tracer.event(apmInfo.trace, 'start', startData);
        var waitEventId = Apm.tracer.event(apmInfo.trace, 'wait', {waitOn: waitOnMessages}, apmInfo);
        msg._waitEventId = waitEventId;
        msg.__apmInfo = apmInfo;
      }
    }

    return originalProcessMessage.call(this, msg);
  };

  //adding the method context to the current fiber
  var originalMethodHandler = sessionProto.protocol_handlers.method;
  sessionProto.protocol_handlers.method = function(msg, unblock) {
    currentlyProcessingDDPMessage[this.id] = msg;
    //add context
    var apmInfo = msg.__apmInfo;
    Apm._setInfo(apmInfo);

    Apm.tracer.eventEnd(apmInfo.trace, msg._waitEventId);

    return originalMethodHandler.call(this, msg, unblock);
  };

  //to capture the currently processing message
  var orginalSubHandler = sessionProto.protocol_handlers.sub;
  sessionProto.protocol_handlers.sub = function(msg, unblock) {
    currentlyProcessingDDPMessage[this.id] = msg;

    //add context
    var apmInfo = msg.__apmInfo;
    Apm._setInfo(apmInfo);

    Apm.tracer.eventEnd(apmInfo.trace, msg._waitEventId);

    Apm.models.pubsub._trackSub(this, msg);
    return orginalSubHandler.call(this, msg, unblock);
  };

  //to capture the currently processing message
  var orginalUnSubHandler = sessionProto.protocol_handlers.unsub;
  sessionProto.protocol_handlers.unsub = function(msg, unblock) {
    currentlyProcessingDDPMessage[this.id] = msg;
    return orginalUnSubHandler.call(this, msg, unblock);
  };

  //we need to clear currentlyProcessingDDPMessage just after the session destroyed
  //otherwise, it will leads to a potential memory leaks
  var originalDestroy = sessionProto.destroy;
  sessionProto.destroy = function() {
    delete currentlyProcessingDDPMessage[this.id];
    return originalDestroy.call(this);
  };

  //track method ending (to get the result of error)
  var originalSend = sessionProto.send;
  sessionProto.send = function(msg) {
    if(msg.msg == 'result') {
      var apmInfo = Apm._getInfo();
      if(msg.error) {
        var error = msg.error;

        //pick the error from the __apmInfo if setted with
        //DDPServer._CurrentWriteFence.withValue hijack
        if(apmInfo && apmInfo.currentError) {
          error = apmInfo.currentError;
        }
        error = _.pick(error, ['message', 'stack']);

        Apm.tracer.endLastEvent(apmInfo.trace);
        Apm.tracer.event(apmInfo.trace, 'error', {error: error});
      } else {
        Apm.tracer.event(apmInfo.trace, 'complete');
      }

      if(apmInfo) {
        //processing the message
        var trace = Apm.tracer.buildTrace(apmInfo.trace);
        Apm.models.methods.processMethod(trace);

        //clean and make sure, fiber is clean
        //not sure we need to do this, but a preventive measure
        Apm._setInfo(null);
      }
    }

    return originalSend.call(this, msg);
  };

  //for the pub/sub data-impact calculation
  ['sendAdded', 'sendChanged', 'sendRemoved'].forEach(function(funcName) {
    var originalFunc = sessionProto[funcName];
    sessionProto[funcName] = function(collectionName, id, fields) {
      //fields is not relevant for `sendRemoved`, but does make any harm
      var eventName = funcName.substring(4).toLowerCase();
      var subscription = Apm.env.currentSub.get();

      if(subscription) {
        var session = this;
        Apm.models.pubsub._trackNetworkImpact(session, subscription, eventName, collectionName, id, fields);
      }

      return originalFunc.call(this, collectionName, id, fields);
    };
  });
};

//We need this hijack to get the correct exception from the method
//otherwise, what we get from the session.send is something customized for the client

var originalWithValue = DDPServer._CurrentWriteFence.withValue;
DDPServer._CurrentWriteFence.withValue = function(value, func) {
  try {
    return originalWithValue.call(DDPServer._CurrentWriteFence, value, func);
  } catch(ex) {
    if(Apm._getInfo()) {
      Apm._getInfo().currentError = ex;
    }
    throw ex;
  }
};
<commit_msg>start tracking subscription, moved into the processMessage<commit_after>//only method, sub and unsub are valid messages
//so following fields would only required
var WAITON_MESSAGE_FIELDS = ['msg', 'id', 'method', 'name'];

wrapSession = function(sessionProto) {

  //store the currently running DDP message per session
  //we have to add this to the top of the waiting message
  var currentlyProcessingDDPMessage = {};

  var originalProcessMessage = sessionProto.processMessage;
  sessionProto.processMessage = function(msg) {
    if(Apm.connected) {
      //only add apmInfo if it is connected
      var apmInfo = {
        session: this.id,
        userId: this.userId
      };

      if(msg.msg == 'method' || msg.msg == 'sub') {
        apmInfo.trace = Apm.tracer.start(this, msg);

        var waitOnMessages = this.inQueue.map(function(msg) {
          return _.pick(msg, WAITON_MESSAGE_FIELDS);
        });

        //add currently processing ddp message if exists
        if(this.workerRunning) {
          waitOnMessages.unshift(_.pick(currentlyProcessingDDPMessage[this.id], WAITON_MESSAGE_FIELDS));
        }

        var startData = { userId: this.userId };
        Apm.tracer.event(apmInfo.trace, 'start', startData);
        var waitEventId = Apm.tracer.event(apmInfo.trace, 'wait', {waitOn: waitOnMessages}, apmInfo);
        msg._waitEventId = waitEventId;
        msg.__apmInfo = apmInfo;

        if(msg.msg == 'sub') {
          // start tracking inside processMessage allows us to indicate
          // wait time as well
          Apm.models.pubsub._trackSub(this, msg);
        }
      }
    }

    return originalProcessMessage.call(this, msg);
  };

  //adding the method context to the current fiber
  var originalMethodHandler = sessionProto.protocol_handlers.method;
  sessionProto.protocol_handlers.method = function(msg, unblock) {
    currentlyProcessingDDPMessage[this.id] = msg;
    //add context
    var apmInfo = msg.__apmInfo;
    Apm._setInfo(apmInfo);

    Apm.tracer.eventEnd(apmInfo.trace, msg._waitEventId);

    return originalMethodHandler.call(this, msg, unblock);
  };

  //to capture the currently processing message
  var orginalSubHandler = sessionProto.protocol_handlers.sub;
  sessionProto.protocol_handlers.sub = function(msg, unblock) {
    currentlyProcessingDDPMessage[this.id] = msg;

    //add context
    var apmInfo = msg.__apmInfo;
    Apm._setInfo(apmInfo);

    Apm.tracer.eventEnd(apmInfo.trace, msg._waitEventId);

    return orginalSubHandler.call(this, msg, unblock);
  };

  //to capture the currently processing message
  var orginalUnSubHandler = sessionProto.protocol_handlers.unsub;
  sessionProto.protocol_handlers.unsub = function(msg, unblock) {
    currentlyProcessingDDPMessage[this.id] = msg;
    return orginalUnSubHandler.call(this, msg, unblock);
  };

  //we need to clear currentlyProcessingDDPMessage just after the session destroyed
  //otherwise, it will leads to a potential memory leaks
  var originalDestroy = sessionProto.destroy;
  sessionProto.destroy = function() {
    delete currentlyProcessingDDPMessage[this.id];
    return originalDestroy.call(this);
  };

  //track method ending (to get the result of error)
  var originalSend = sessionProto.send;
  sessionProto.send = function(msg) {
    if(msg.msg == 'result') {
      var apmInfo = Apm._getInfo();
      if(msg.error) {
        var error = msg.error;

        //pick the error from the __apmInfo if setted with
        //DDPServer._CurrentWriteFence.withValue hijack
        if(apmInfo && apmInfo.currentError) {
          error = apmInfo.currentError;
        }
        error = _.pick(error, ['message', 'stack']);

        Apm.tracer.endLastEvent(apmInfo.trace);
        Apm.tracer.event(apmInfo.trace, 'error', {error: error});
      } else {
        Apm.tracer.event(apmInfo.trace, 'complete');
      }

      if(apmInfo) {
        //processing the message
        var trace = Apm.tracer.buildTrace(apmInfo.trace);
        Apm.models.methods.processMethod(trace);

        //clean and make sure, fiber is clean
        //not sure we need to do this, but a preventive measure
        Apm._setInfo(null);
      }
    }

    return originalSend.call(this, msg);
  };

  //for the pub/sub data-impact calculation
  ['sendAdded', 'sendChanged', 'sendRemoved'].forEach(function(funcName) {
    var originalFunc = sessionProto[funcName];
    sessionProto[funcName] = function(collectionName, id, fields) {
      //fields is not relevant for `sendRemoved`, but does make any harm
      var eventName = funcName.substring(4).toLowerCase();
      var subscription = Apm.env.currentSub.get();

      if(subscription) {
        var session = this;
        Apm.models.pubsub._trackNetworkImpact(session, subscription, eventName, collectionName, id, fields);
      }

      return originalFunc.call(this, collectionName, id, fields);
    };
  });
};

//We need this hijack to get the correct exception from the method
//otherwise, what we get from the session.send is something customized for the client

var originalWithValue = DDPServer._CurrentWriteFence.withValue;
DDPServer._CurrentWriteFence.withValue = function(value, func) {
  try {
    return originalWithValue.call(DDPServer._CurrentWriteFence, value, func);
  } catch(ex) {
    if(Apm._getInfo()) {
      Apm._getInfo().currentError = ex;
    }
    throw ex;
  }
};
<|endoftext|>


<commit_before>module.exports = function(sails) {

	/**
	 * Module dependencies.
	 */

	var util = require('sails-util');

	/**
	 * Global access to middleware
	 * (useful as helpers)
	 */

	sails._mixinLocals = _mixinLocals;
	sails._mixinServerMetadata = _mixinServerMetadata;
	sails._mixinReqQualifiers = _mixinReqQualifiers;

	/**
	 * Extend middleware req/res for this route w/ new methods / qualifiers.
	 */

	return {

		defaults: {

			// Default 404 (not found) handler
			404: function notFound (req, res) {
				res.send(404);
			},

			// Default 500 (server error) handler
			500: function (errors, req, res) {
				res.send(errors || undefined, 500);
			},

			// Default 403 (forbidden) handler
			403: function (message, req, res) {
				res.send(message || undefined, 403);
			},

			// Default 400 (bad request) handler
			400: function (errors, redirectTo, req, res) {
				res.send(errors || undefined, 400);
			}
		},

		// Don't allow sails to lift until ready
		// is explicitly set below
		ready: false,

		/**
		 * Bind req/res syntactic sugar before applying any app-level routes
		 */

		initialize: function(cb) {

			var self = this;

			sails.on('router:before', function() {

				sails.router.bind('/*', function (req, res, next) {

					// Run connect-flash middleware to support flash messages
					// TODO: potential optimization-- pull this out so it only runs once
					var flashMiddleware = (require('connect-flash')());
					flashMiddleware(req, res, function (err) {
						if (err) return next(err);

						// Add a few extra `res.locals` for convenience when using views
						_mixinLocals(req, res);

						// Add content-negotiating behaviors for common response scenarios
						_mixinResServerError(req, res);
						_mixinResNotFound(req, res);
						_mixinResForbidden(req, res);
						_mixinResBadRequest(req, res);

						// Add information about the server to the request context
						_mixinServerMetadata(req, res);

						// Only apply HTTP middleware if it makes sense
						// (i.e. if this is an HTTP request)
						if (req.protocol === 'http' || req.protocol === 'https') {
							_mixinReqQualifiers(req, res);
						}

						next();
					});

				});

				self.ready = true;
			});

			// Bind an event handler to inject logic before running each middleware within a route/request
			sails.on('router:route', function(requestState) {
				var req = requestState.req;
				var res = requestState.res;
				var next = requestState.next;

				_mixinReqParamsAll(req, res);
			});

			cb();
		}
	};

	/**
	 * Always share some basic metadata with views
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinLocals(req, res) {
		util.extend(res.locals, {
			_: util,
			util: util,
			session: req.session,
			title: sails.config.appName + (req.param('action') ? (' | ' + util.str.capitalize(req.param('action'))) : ''),
			req: req,
			res: res,
			sails: sails
		});
	}

	// TODO:
	// We could differentiate between 500 (generic error message)
	// and 504 (gateway did not receive response from upstream server) which could describe an IO problem
	// This is worth having a think about, since there are 2 fundamentally different kinds of "server errors":
	// (a) An infrastructural issue (e.g. MySQL database randomly crashed or Twitter is down)
	// (b) Unexpected bug in app code (e.g. `req.session.user.id`, but `req.session.user` doesn't exist)

	/**
	 * res.serverError()
	 * (i.e. 500: Server Error)
	 *
	 * Respond as defined in `config/500.js`
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinResServerError(req, res) {
		res.serverError = function respond500(err) {
			sails.config[500](err, req, res, function fallback(e) {
				res.send(e || err || undefined, 500);
			});
		};

		// Also add res.error and res.servererror as synonyms
		res.error = res.serverError;
		res.servererror = res.serverError;
	}

	/**
	 * res.notFound()
	 * (i.e. 404: Not Found)
	 *
	 * Respond as defined in `config/404.js`
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinResNotFound(req, res) {
		res.notFound = function respond404() {

			// Call default 404 handler
			sails.config[404](req, res, function fallback(e) {
				res.send(e || err || undefined, 404);
			});
		};

		// Also add res.unknown and res.notfound as synonyms
		res.unknown = res.notFound;
		res.notfound = res.notFound;
	}

	/**
	 * res.forbidden()
	 * (i.e. 403: Forbidden or 401: Unauthorized)
	 *
	 * Respond as defined in `config/403.js`
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinResForbidden(req, res) {
		res.forbidden = function respond403(err) {
			sails.config[403](err, req, res, function fallback(e) {
				res.send(e || err || undefined, 403);
			});
		};
	}

	/**
	 * res.invalid()
	 * (i.e. 400: Bad Request)
	 *
	 * Respond as defined in `config/400.js`
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinResBadRequest(req, res) {

		/**
		 * @param {Array} errors
		 * @param {String} previous
		 */

		res.badRequest = function respond400(errors, previous) {
			sails.config[400](errors, previous, req, res, function fallback(e) {
				res.send(e || err || undefined, 400);
			});
		};

		// Also add res.badrequest as synonym
		res.badrequest = res.badRequest;
	}

	/**
	 * `req.params.all()`
	 *
	 * Mixes in a convenience method to grab all parameters,
	 * whether they're in the path (req.params), query string (req.query),
	 * or request body (req.body).
	 *
	 * Note: this has to be applied per-route, not per request,
	 * in order to refer to the proper route/path parameters
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinReqParamsAll (req, res) {

		////////////////////////////////////////////////////////////////////
		// The check below is deprecatable since action blueprints
		// will no longer automatically receive the :id? param in their route.
		//
		// // Make sure `id` is omitted if it's undefined
		// // (since action blueprint routes name an optional :id)
		// if (typeof req.param('id') === 'undefined') {
		// 	delete req.params.id;
		// }
		//////////////////////////////////////////////////////////////////

		// Combines parameters from the query string, and encoded request body
		// to compose a monolithic object of named parameters, irrespective of source
		var queryParams = util.clone(req.query) || {};
		var bodyParams = util.clone(req.body) || {};
		var allParams = util.extend({}, queryParams, bodyParams);

		// Mixin route params
		util.each(Object.keys(req.params), function(paramName) {
			allParams[paramName] = req.params[paramName];
		});

		// Define a new non-enuerable function: req.params.all()
		// (but only if an `:all` route parameter doesn't exist!)
		if (!req.params.all) {
			Object.defineProperty(req.params, 'all', {
				value: function getAllParams() {
					return allParams;
				}
			});
		}

		// TODO: make req.params() do the same thing as req.params.all() ??

	}

	/**
	 * Mix in convenience flags about this request
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinReqQualifiers(req, res) {
		var accept = req.get('Accept') || '';

		// Flag indicating whether HTML was explicitly mentioned in the Accepts header
		req.explicitlyAcceptsHTML = (accept.indexOf('html') !== -1);

		// Flag indicating whether a request would like to receive a JSON response
		req.wantsJSON = req.xhr;
		req.wantsJSON = req.wantsJSON || !req.explicitlyAcceptsHTML;
		req.wantsJSON = req.wantsJSON || (req.is('json') && req.get('Accept'));
	}

	/**
	 * Host, port, etc.
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinServerMetadata(req, res) {

		// Access to server port, if available
		var nodeHTTPServer = sails.hooks.http.server;
		var nodeHTTPServerAddress = (nodeHTTPServer && nodeHTTPServer.address()) || {};
		req.port = req.port || nodeHTTPServerAddress.port || 80;

		// Add access to full base url for convenience
		req.baseUrl = req.protocol + '://' + req.host + (req.port == 80 || req.port == 443 ? '' : ':' + req.port);
	}

};<commit_msg>Removed bad todo.<commit_after>module.exports = function(sails) {

	/**
	 * Module dependencies.
	 */

	var util = require('sails-util');

	/**
	 * Global access to middleware
	 * (useful as helpers)
	 */

	sails._mixinLocals = _mixinLocals;
	sails._mixinServerMetadata = _mixinServerMetadata;
	sails._mixinReqQualifiers = _mixinReqQualifiers;

	/**
	 * Extend middleware req/res for this route w/ new methods / qualifiers.
	 */

	return {

		defaults: {

			// Default 404 (not found) handler
			404: function notFound (req, res) {
				res.send(404);
			},

			// Default 500 (server error) handler
			500: function (errors, req, res) {
				res.send(errors || undefined, 500);
			},

			// Default 403 (forbidden) handler
			403: function (message, req, res) {
				res.send(message || undefined, 403);
			},

			// Default 400 (bad request) handler
			400: function (errors, redirectTo, req, res) {
				res.send(errors || undefined, 400);
			}
		},

		// Don't allow sails to lift until ready
		// is explicitly set below
		ready: false,

		/**
		 * Bind req/res syntactic sugar before applying any app-level routes
		 */

		initialize: function(cb) {

			var self = this;

			sails.on('router:before', function() {

				sails.router.bind('/*', function (req, res, next) {

					// Run connect-flash middleware to support flash messages
					// TODO: potential optimization-- pull this out so it only runs once
					var flashMiddleware = (require('connect-flash')());
					flashMiddleware(req, res, function (err) {
						if (err) return next(err);

						// Add a few extra `res.locals` for convenience when using views
						_mixinLocals(req, res);

						// Add content-negotiating behaviors for common response scenarios
						_mixinResServerError(req, res);
						_mixinResNotFound(req, res);
						_mixinResForbidden(req, res);
						_mixinResBadRequest(req, res);

						// Add information about the server to the request context
						_mixinServerMetadata(req, res);

						// Only apply HTTP middleware if it makes sense
						// (i.e. if this is an HTTP request)
						if (req.protocol === 'http' || req.protocol === 'https') {
							_mixinReqQualifiers(req, res);
						}

						next();
					});

				});

				self.ready = true;
			});

			// Bind an event handler to inject logic before running each middleware within a route/request
			sails.on('router:route', function(requestState) {
				var req = requestState.req;
				var res = requestState.res;
				var next = requestState.next;

				_mixinReqParamsAll(req, res);
			});

			cb();
		}
	};

	/**
	 * Always share some basic metadata with views
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinLocals(req, res) {
		util.extend(res.locals, {
			_: util,
			util: util,
			session: req.session,
			title: sails.config.appName + (req.param('action') ? (' | ' + util.str.capitalize(req.param('action'))) : ''),
			req: req,
			res: res,
			sails: sails
		});
	}

	// TODO:
	// We could differentiate between 500 (generic error message)
	// and 504 (gateway did not receive response from upstream server) which could describe an IO problem
	// This is worth having a think about, since there are 2 fundamentally different kinds of "server errors":
	// (a) An infrastructural issue (e.g. MySQL database randomly crashed or Twitter is down)
	// (b) Unexpected bug in app code (e.g. `req.session.user.id`, but `req.session.user` doesn't exist)

	/**
	 * res.serverError()
	 * (i.e. 500: Server Error)
	 *
	 * Respond as defined in `config/500.js`
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinResServerError(req, res) {
		res.serverError = function respond500(err) {
			sails.config[500](err, req, res, function fallback(e) {
				res.send(e || err || undefined, 500);
			});
		};

		// Also add res.error and res.servererror as synonyms
		res.error = res.serverError;
		res.servererror = res.serverError;
	}

	/**
	 * res.notFound()
	 * (i.e. 404: Not Found)
	 *
	 * Respond as defined in `config/404.js`
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinResNotFound(req, res) {
		res.notFound = function respond404() {

			// Call default 404 handler
			sails.config[404](req, res, function fallback(e) {
				res.send(e || err || undefined, 404);
			});
		};

		// Also add res.unknown and res.notfound as synonyms
		res.unknown = res.notFound;
		res.notfound = res.notFound;
	}

	/**
	 * res.forbidden()
	 * (i.e. 403: Forbidden or 401: Unauthorized)
	 *
	 * Respond as defined in `config/403.js`
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinResForbidden(req, res) {
		res.forbidden = function respond403(err) {
			sails.config[403](err, req, res, function fallback(e) {
				res.send(e || err || undefined, 403);
			});
		};
	}

	/**
	 * res.invalid()
	 * (i.e. 400: Bad Request)
	 *
	 * Respond as defined in `config/400.js`
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinResBadRequest(req, res) {

		/**
		 * @param {Array} errors
		 * @param {String} previous
		 */

		res.badRequest = function respond400(errors, previous) {
			sails.config[400](errors, previous, req, res, function fallback(e) {
				res.send(e || err || undefined, 400);
			});
		};

		// Also add res.badrequest as synonym
		res.badrequest = res.badRequest;
	}

	/**
	 * `req.params.all()`
	 *
	 * Mixes in a convenience method to grab all parameters,
	 * whether they're in the path (req.params), query string (req.query),
	 * or request body (req.body).
	 *
	 * Note: this has to be applied per-route, not per request,
	 * in order to refer to the proper route/path parameters
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinReqParamsAll (req, res) {

		////////////////////////////////////////////////////////////////////
		// The check below is deprecatable since action blueprints
		// will no longer automatically receive the :id? param in their route.
		//
		// // Make sure `id` is omitted if it's undefined
		// // (since action blueprint routes name an optional :id)
		// if (typeof req.param('id') === 'undefined') {
		// 	delete req.params.id;
		// }
		//////////////////////////////////////////////////////////////////

		// Combines parameters from the query string, and encoded request body
		// to compose a monolithic object of named parameters, irrespective of source
		var queryParams = util.clone(req.query) || {};
		var bodyParams = util.clone(req.body) || {};
		var allParams = util.extend({}, queryParams, bodyParams);

		// Mixin route params
		util.each(Object.keys(req.params), function(paramName) {
			allParams[paramName] = req.params[paramName];
		});

		// Define a new non-enuerable function: req.params.all()
		// (but only if an `:all` route parameter doesn't exist!)
		if (!req.params.all) {
			Object.defineProperty(req.params, 'all', {
				value: function getAllParams() {
					return allParams;
				}
			});
		}
	}

	/**
	 * Mix in convenience flags about this request
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinReqQualifiers(req, res) {
		var accept = req.get('Accept') || '';

		// Flag indicating whether HTML was explicitly mentioned in the Accepts header
		req.explicitlyAcceptsHTML = (accept.indexOf('html') !== -1);

		// Flag indicating whether a request would like to receive a JSON response
		req.wantsJSON = req.xhr;
		req.wantsJSON = req.wantsJSON || !req.explicitlyAcceptsHTML;
		req.wantsJSON = req.wantsJSON || (req.is('json') && req.get('Accept'));
	}

	/**
	 * Host, port, etc.
	 *
	 * @param {Request} req
	 * @param {Response} res
	 * @api private
	 */

	function _mixinServerMetadata(req, res) {

		// Access to server port, if available
		var nodeHTTPServer = sails.hooks.http.server;
		var nodeHTTPServerAddress = (nodeHTTPServer && nodeHTTPServer.address()) || {};
		req.port = req.port || nodeHTTPServerAddress.port || 80;

		// Add access to full base url for convenience
		req.baseUrl = req.protocol + '://' + req.host + (req.port == 80 || req.port == 443 ? '' : ':' + req.port);
	}

};<|endoftext|>