<commit_before>/* This file is part of Indico.
 * Copyright (C) 2002 - 2016 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

/* global countWords:false */

(function(global) {
    'use strict';

    function validatePasswordConfirmation(passwordField, confirmField) {
        if ('setCustomValidity' in confirmField[0]) {
            passwordField.add(confirmField).on('change input', function() {
                if (passwordField.val() != confirmField.val()) {
                    confirmField[0].setCustomValidity($T('The passwords do not match.'));
                } else {
                    confirmField[0].setCustomValidity('');
                }
            });
        }
    }

    function hideFieldUnless(field, conditionField, requiredValues, checkedOnly) {
        conditionField.on('change', function() {
            var value = checkedOnly ? conditionField.filter(':checked').val() || false : conditionField.val();
            var active = !!((!requiredValues.length && value) ||
                            (requiredValues.length && _.contains(requiredValues, value)));
            field.closest('.form-group').toggle(active);
            if (!field.is(':input')) {
                field.find(':input').prop('disabled', !active);
            } else if (!field.data('initiallyDisabled')) {
                field.prop('disabled', !active);
            }
        });
    }

    function validateLength($field) {
        if (!('setCustomValidity' in $field[0])) {
            return;
        }

        var minChars = $field.data('min-length');
        var maxChars = $field.data('max-length');
        var minWords = $field.data('min-words');
        var maxWords = $field.data('max-words');

        $field.on('change input', function() {
            var msg = '';
            var charCount = $field.val().trim().length;
            var wordCount = countWords($field.val());

            if ((minChars && charCount < minChars) || (maxChars && charCount > maxChars)) {
                if (!maxChars) {
                    msg = $T.ngettext('Field must be at least {0} character long.',
                                      'Field must be at least {0} characters long.', minChars).format(minChars);
                } else if (!minChars) {
                    msg = $T.ngettext('Field cannot be longer than {0} character.',
                                      'Field cannot be longer than {0} characters.', maxChars).format(maxChars);
                } else {
                    msg = $T.gettext('Field must be between {0} and {1} characters long.').format(minChars, maxChars);
                }
            } else if ((minWords && wordCount < minWords) || (maxWords && wordCount > maxWords)) {
                if (!maxWords) {
                    msg = $T.ngettext('Field must contain at least {0} word.',
                                      'Field must contain at least {0} words.', minWords).format(minWords);
                } else if (!minWords) {
                    msg = $T.ngettext('Field cannot contain more than {0} word.',
                                      'Field cannot contain more than {0} words.', maxWords).format(maxWords);
                } else {
                    msg = $T.gettext('Field must contain between {0} and {1} words.').format(minWords, maxWords);
                }
            }
            $field[0].setCustomValidity(msg);
        });
    }

    function isElementInView(elem) {
        var docViewTop = $(window).scrollTop();
        var docViewBottom = docViewTop + $(window).height();
        var elemTop = $(elem).offset().top;
        var elemBottom = elemTop + $(elem).height();
        return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
    }

    function showSaveCornerMessage($form) {
        cornerMessage({
            actionLabel: $T.gettext('Save now'),
            progressMessage: $T.gettext('Saving...'),
            message: $T.gettext('Do not forget to save your changes!'),
            class: 'highlight save-corner-message',
            actionCallback: function() {
                $form.submit();
            }
        });
    }

    function hideSaveCornerMessage($cornerMessage) {
        $cornerMessage.fadeOut(300, function() {
            $cornerMessage.remove();
        });
    }

    global.initForms = function initForms(forms) {
        // ConfirmPassword validator
        forms.find('input[data-confirm-password]').each(function() {
            var confirmField = $(this);
            var passwordField = $(this.form).find('input[name="' + confirmField.data('confirmPassword') + '"]');
            validatePasswordConfirmation(passwordField, confirmField);
        });

        // HiddenUnless validator
        forms.find(':input[data-hidden-unless]:disabled').data('initiallyDisabled', true);
        forms.find(':input[data-hidden-unless]').each(function() {
            var field = $(this);
            var data = field.data('hidden-unless');
            var conditionField = $(this.form).find(':input[name="{0}"]'.format(data.field));
            hideFieldUnless(field, conditionField, data.values, data.checked_only);
            conditionField.triggerHandler('change');
        });

        // SoftLength/WordCount validators
        var selectors = '[data-min-words], [data-max-words], [data-min-length], [data-max-length]';
        forms.find('input, textarea').filter(selectors).each(function() {
            validateLength($(this));
        });

        // track modifications
        forms.find('[data-disabled-until-change]').prop('disabled', true);
        forms.each(function() {
            var $this = $(this);
            function _resetData() {
                $this.data('initialData', $this.serialize());
                $this.data('fieldsChanged', false);
            }
            _resetData();

            $this.on('indico:fieldsSaved', function() {
                _resetData();
                $this.find('[data-disabled-until-change]').prop('disabled', true);
            });

            $this.on('indico:beforeConfirmed', '[data-confirm-if-change]', function(evt) {
                if (!$this.data('fieldsChanged')) {
                    evt.preventDefault();
                }
            });
        }).on('change input', function() {
            var $this = $(this);
            var untouched = $this.serialize() === $this.data('initialData');
            var $cornerMessage = $('.save-corner-message');
            $this.find('[data-disabled-until-change]').prop('disabled', untouched);
            $this.closest('form').data('fieldsChanged', !untouched);
            if ($this.find('[data-save-reminder]').length) {
                if (!isElementInView($this.find('[data-save-reminder]'))
                    && !untouched && !$cornerMessage.length) {
                    showSaveCornerMessage($this);
                } else if (untouched) {
                    hideSaveCornerMessage($cornerMessage);
                }
            }
        });

        // Remove corner message when 'Save' button is visible on screen
        $(window).on('scroll', _.debounce(function() {
            var $form = forms.find('[data-save-reminder]').closest('form');
            if ($form.length) {
                var $cornerMessage = $('.save-corner-message');
                var untouched = $form.serialize() === $form.data('initialData');
                if (isElementInView($form.find('[data-save-reminder]'))) {
                    hideSaveCornerMessage($cornerMessage);
                } else if (!untouched && !$cornerMessage.length) {
                    showSaveCornerMessage($form);
                }
            }
        }, 100));

        forms.find('fieldset.collapsible > legend').on('click', function(evt) {
            evt.preventDefault();
            var $this = $(this),
                collapseIcon = $this.find('div > span');
            $this.next('.fieldset-content').slideToggle();
            collapseIcon.toggleClass('icon-next icon-expand');
        });

        forms.find('fieldset.collapsible.initially-collapsed').each(function() {
            var $this = $(this);
            if ($this.find('div.form-field[data-error]').length) {
                $this.find('legend').trigger('click');
            }
        });
    };

    global.toggleAclField = function toogleAclField(aclField, state) {
        aclField.closest('.form-group')
                .find('input.i-button').prop('disabled', state).end()
                .find('a.i-button').toggleClass('disabled', state).end()
                .find('.PluginOptionPeopleListDiv').toggleClass('disabled', state);
    };

    global.aclIfProtected = function aclIfProtected(protectionField, aclField, selfProtection, inheritedProtection, folderField, folderProtection) {
        protectionField.on('change', function() {
            toggleAclField(aclField, !this.checked);

            if (selfProtection && inheritedProtection) {
                selfProtection.toggle(this.checked);
                inheritedProtection.toggle(!this.checked);
                if (folderField) {
                    folderField.triggerHandler('change');
                }
                else {
                    folderProtection.hide();
                }
            }
        });
        _.defer(function() {
            protectionField.triggerHandler('change');
            if (folderField) {
                folderField.triggerHandler('change');
            }
        });
    };

    global.messageIfFolderProtected = function messageIfFolderProtected(protectionField, folderField, protectionInfo, selfProtection, inheritedProtection, folderProtection) {
        folderField.on('change', function() {
            var selectedFolder = $(this);
            if (protectionInfo[selectedFolder.val()] && !protectionField.prop('checked')) {
                selfProtection.hide();
                inheritedProtection.hide();
                folderProtection.find('.folder-name').html(selectedFolder.children('option:selected').text());
                folderProtection.show();
            }
            else {
                folderProtection.hide();
                selfProtection.toggle(protectionField.prop('checked'));
                inheritedProtection.toggle(!protectionField.prop('checked'));
            }
        });
        _.defer(function() {
            folderField.triggerHandler('change');
        });
    };

    /* Enable or disable elements (typically buttons) based on the state of checkboxes.
     *
     * - checkboxContainer: Element or jQuery object containing the checkboxes to consider
     * - checkboxSelector: Selector to find the checkboxes inside the containers
     * - buttonSelector: Selector, jQuery object or element corresponding to the button(s) to be enabled/disabled
     * - extraCheckCallback: Function called with the checkboxes as argument. If it returns false the buttons will be disabled.
     */
    global.enableIfChecked = function enableIfChecked(checkboxContainer, checkboxSelector, buttonSelector, extraCheckCallback) {
        function _update(force) {
            var $checkboxes = $(checkboxContainer).find(checkboxSelector).filter(':checked');
            var checked = force || !!$checkboxes.length;
            if (extraCheckCallback && extraCheckCallback($checkboxes) === false) {
                checked = false;
            }
            $(buttonSelector).prop('disabled', !checked).toggleClass('disabled', !checked);
        }

        $(checkboxContainer).on('change', checkboxSelector, function() {
            _update(this.checked);
        }).on('indico:syncEnableIfChecked', function() {
            _update();
        });

        _update();
    };

    /* Provide a "select really everything" option for paginated lists.
     * When selecting all rows in such a list a message is shown indicating
     * that only the current page is selected with the option to select all
     * items (no matter on which page).
     */
    global.paginatedSelectAll = function paginatedSelectAll(options) {
        options = $.extend(true, {
            // the container element used as context for all other selectors.
            // in case of dynamically updated rows, this must remain the same
            // all the time as events are bound to it
            containerSelector: null,
            // the selector for the row selection checkboxes. used within the
            // context of containerSelector
            checkboxSelector: null,
            // the selector for the (usually hidden) all-items-on-all-pages-selected
            // checkbox. used within the context of containerSelector
            allSelectedSelector: null,
            // the selector for the element where the message whether all items
            // on the current page or really all items are selected.
            // used within the global context, i.e. not restricted by the given
            // containerSelector.
            selectionMessageSelector: null,
            // the total number of rows. may also be a function.
            totalRows: 0,
            messages: {
                // message shown when all items on all pages are selected
                allSelected: function(total) {
                    // never used with total == 1. empty string is invalid so we use `*` instead
                    return $T.ngettext('*', 'All {0} rows are currently selected.').format(total);
                },
                // message shown when all items on the current page are selected
                pageSelected: function(selected, total) {
                    // never used with total == 1
                    return $T.ngettext('Only {0} out of {1} rows is currently selected',
                                       'Only {0} out of {1} rows are currently selected.',
                                       selected).format(selected, total);
                }
            }
        }, options);

        var container = $(options.containerSelector);
        container.on('change', options.checkboxSelector, _update);
        _update();

        function _update() {
            var messageContainer = $(options.selectionMessageSelector).empty();
            var numChecked = container.find(options.checkboxSelector + ':checked').length;
            var numUnchecked = container.find(options.checkboxSelector + ':not(:checked)').length;
            var numRows = numChecked + numUnchecked;

            if (numChecked < numRows) {
                // not everything selected
                messageContainer.hide();
                _setAllSelected(false);
            } else if (numChecked === options.totalRows) {
                // everything selected and only one page
                messageContainer.hide();
                _setAllSelected(true);
            } else if (numChecked === numRows) {
                // all rows selected
                if (_getAllSelected()) {
                    $('<span>', {
                        html: options.messages.allSelected(options.totalRows)
                    }).appendTo(messageContainer);
                    messageContainer.append(' ');
                    $('<a>', {
                        href: '#',
                        text: $T.gettext('Select only the current page.'),
                        click: function(evt) {
                            evt.preventDefault();
                            _setAllSelected(false);
                            _update();
                        }
                    }).appendTo(messageContainer);
                } else {
                    $('<span>', {
                        html: options.messages.pageSelected(numChecked, options.totalRows)
                    }).appendTo(messageContainer);
                    messageContainer.append(' ');
                    $('<a>', {
                        href: '#',
                        text: $T.gettext('Click here to select them all.'),
                        click: function(evt) {
                            evt.preventDefault();
                            _setAllSelected(true);
                            _update();
                        }
                    }).appendTo(messageContainer);
                }
                messageContainer.show();
            }
        }

        function _getAllSelected() {
            return container.find(options.allSelectedSelector).prop('checked');
        }

        function _setAllSelected(selected) {
            container.find(options.allSelectedSelector).prop('checked', selected).trigger('change');
        }

        return {
            isEverythingSelected: _getAllSelected
        };
    };

    $(document).ready(function() {
        initForms($('form'));
    });
})(window);
<commit_msg>Only bind scroll event once<commit_after>/* This file is part of Indico.
 * Copyright (C) 2002 - 2016 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

/* global countWords:false */

(function(global) {
    'use strict';

    function validatePasswordConfirmation(passwordField, confirmField) {
        if ('setCustomValidity' in confirmField[0]) {
            passwordField.add(confirmField).on('change input', function() {
                if (passwordField.val() != confirmField.val()) {
                    confirmField[0].setCustomValidity($T('The passwords do not match.'));
                } else {
                    confirmField[0].setCustomValidity('');
                }
            });
        }
    }

    function hideFieldUnless(field, conditionField, requiredValues, checkedOnly) {
        conditionField.on('change', function() {
            var value = checkedOnly ? conditionField.filter(':checked').val() || false : conditionField.val();
            var active = !!((!requiredValues.length && value) ||
                            (requiredValues.length && _.contains(requiredValues, value)));
            field.closest('.form-group').toggle(active);
            if (!field.is(':input')) {
                field.find(':input').prop('disabled', !active);
            } else if (!field.data('initiallyDisabled')) {
                field.prop('disabled', !active);
            }
        });
    }

    function validateLength($field) {
        if (!('setCustomValidity' in $field[0])) {
            return;
        }

        var minChars = $field.data('min-length');
        var maxChars = $field.data('max-length');
        var minWords = $field.data('min-words');
        var maxWords = $field.data('max-words');

        $field.on('change input', function() {
            var msg = '';
            var charCount = $field.val().trim().length;
            var wordCount = countWords($field.val());

            if ((minChars && charCount < minChars) || (maxChars && charCount > maxChars)) {
                if (!maxChars) {
                    msg = $T.ngettext('Field must be at least {0} character long.',
                                      'Field must be at least {0} characters long.', minChars).format(minChars);
                } else if (!minChars) {
                    msg = $T.ngettext('Field cannot be longer than {0} character.',
                                      'Field cannot be longer than {0} characters.', maxChars).format(maxChars);
                } else {
                    msg = $T.gettext('Field must be between {0} and {1} characters long.').format(minChars, maxChars);
                }
            } else if ((minWords && wordCount < minWords) || (maxWords && wordCount > maxWords)) {
                if (!maxWords) {
                    msg = $T.ngettext('Field must contain at least {0} word.',
                                      'Field must contain at least {0} words.', minWords).format(minWords);
                } else if (!minWords) {
                    msg = $T.ngettext('Field cannot contain more than {0} word.',
                                      'Field cannot contain more than {0} words.', maxWords).format(maxWords);
                } else {
                    msg = $T.gettext('Field must contain between {0} and {1} words.').format(minWords, maxWords);
                }
            }
            $field[0].setCustomValidity(msg);
        });
    }

    function isElementInView(elem) {
        var docViewTop = $(window).scrollTop();
        var docViewBottom = docViewTop + $(window).height();
        var elemTop = $(elem).offset().top;
        var elemBottom = elemTop + $(elem).height();
        return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
    }

    function showSaveCornerMessage($form) {
        cornerMessage({
            actionLabel: $T.gettext('Save now'),
            progressMessage: $T.gettext('Saving...'),
            message: $T.gettext('Do not forget to save your changes!'),
            class: 'highlight save-corner-message',
            actionCallback: function() {
                $form.submit();
            }
        });
    }

    function hideSaveCornerMessage($cornerMessage) {
        $cornerMessage.fadeOut(300, function() {
            $cornerMessage.remove();
        });
    }

    global.initForms = function initForms(forms) {
        // ConfirmPassword validator
        forms.find('input[data-confirm-password]').each(function() {
            var confirmField = $(this);
            var passwordField = $(this.form).find('input[name="' + confirmField.data('confirmPassword') + '"]');
            validatePasswordConfirmation(passwordField, confirmField);
        });

        // HiddenUnless validator
        forms.find(':input[data-hidden-unless]:disabled').data('initiallyDisabled', true);
        forms.find(':input[data-hidden-unless]').each(function() {
            var field = $(this);
            var data = field.data('hidden-unless');
            var conditionField = $(this.form).find(':input[name="{0}"]'.format(data.field));
            hideFieldUnless(field, conditionField, data.values, data.checked_only);
            conditionField.triggerHandler('change');
        });

        // SoftLength/WordCount validators
        var selectors = '[data-min-words], [data-max-words], [data-min-length], [data-max-length]';
        forms.find('input, textarea').filter(selectors).each(function() {
            validateLength($(this));
        });

        // track modifications
        forms.find('[data-disabled-until-change]').prop('disabled', true);
        forms.each(function() {
            var $this = $(this);
            function _resetData() {
                $this.data('initialData', $this.serialize());
                $this.data('fieldsChanged', false);
            }
            _resetData();

            $this.on('indico:fieldsSaved', function() {
                _resetData();
                $this.find('[data-disabled-until-change]').prop('disabled', true);
            });

            $this.on('indico:beforeConfirmed', '[data-confirm-if-change]', function(evt) {
                if (!$this.data('fieldsChanged')) {
                    evt.preventDefault();
                }
            });
        }).on('change input', function() {
            var $this = $(this);
            var untouched = $this.serialize() === $this.data('initialData');
            var $cornerMessage = $('.save-corner-message');
            $this.find('[data-disabled-until-change]').prop('disabled', untouched);
            $this.closest('form').data('fieldsChanged', !untouched);
            if ($this.find('[data-save-reminder]').length) {
                if (!isElementInView($this.find('[data-save-reminder]'))
                    && !untouched && !$cornerMessage.length) {
                    showSaveCornerMessage($this);
                } else if (untouched) {
                    hideSaveCornerMessage($cornerMessage);
                }
            }
        });

        // Remove corner message when 'Save' button is visible on screen
        $(window).off('scroll.initForms').on('scroll.initForms', _.debounce(function() {
            var $form = forms.find('[data-save-reminder]').closest('form');
            if ($form.length) {
                var $cornerMessage = $('.save-corner-message');
                var untouched = $form.serialize() === $form.data('initialData');
                if (isElementInView($form.find('[data-save-reminder]'))) {
                    hideSaveCornerMessage($cornerMessage);
                } else if (!untouched && !$cornerMessage.length) {
                    showSaveCornerMessage($form);
                }
            }
        }, 100));

        forms.find('fieldset.collapsible > legend').on('click', function(evt) {
            evt.preventDefault();
            var $this = $(this),
                collapseIcon = $this.find('div > span');
            $this.next('.fieldset-content').slideToggle();
            collapseIcon.toggleClass('icon-next icon-expand');
        });

        forms.find('fieldset.collapsible.initially-collapsed').each(function() {
            var $this = $(this);
            if ($this.find('div.form-field[data-error]').length) {
                $this.find('legend').trigger('click');
            }
        });
    };

    global.toggleAclField = function toogleAclField(aclField, state) {
        aclField.closest('.form-group')
                .find('input.i-button').prop('disabled', state).end()
                .find('a.i-button').toggleClass('disabled', state).end()
                .find('.PluginOptionPeopleListDiv').toggleClass('disabled', state);
    };

    global.aclIfProtected = function aclIfProtected(protectionField, aclField, selfProtection, inheritedProtection, folderField, folderProtection) {
        protectionField.on('change', function() {
            toggleAclField(aclField, !this.checked);

            if (selfProtection && inheritedProtection) {
                selfProtection.toggle(this.checked);
                inheritedProtection.toggle(!this.checked);
                if (folderField) {
                    folderField.triggerHandler('change');
                }
                else {
                    folderProtection.hide();
                }
            }
        });
        _.defer(function() {
            protectionField.triggerHandler('change');
            if (folderField) {
                folderField.triggerHandler('change');
            }
        });
    };

    global.messageIfFolderProtected = function messageIfFolderProtected(protectionField, folderField, protectionInfo, selfProtection, inheritedProtection, folderProtection) {
        folderField.on('change', function() {
            var selectedFolder = $(this);
            if (protectionInfo[selectedFolder.val()] && !protectionField.prop('checked')) {
                selfProtection.hide();
                inheritedProtection.hide();
                folderProtection.find('.folder-name').html(selectedFolder.children('option:selected').text());
                folderProtection.show();
            }
            else {
                folderProtection.hide();
                selfProtection.toggle(protectionField.prop('checked'));
                inheritedProtection.toggle(!protectionField.prop('checked'));
            }
        });
        _.defer(function() {
            folderField.triggerHandler('change');
        });
    };

    /* Enable or disable elements (typically buttons) based on the state of checkboxes.
     *
     * - checkboxContainer: Element or jQuery object containing the checkboxes to consider
     * - checkboxSelector: Selector to find the checkboxes inside the containers
     * - buttonSelector: Selector, jQuery object or element corresponding to the button(s) to be enabled/disabled
     * - extraCheckCallback: Function called with the checkboxes as argument. If it returns false the buttons will be disabled.
     */
    global.enableIfChecked = function enableIfChecked(checkboxContainer, checkboxSelector, buttonSelector, extraCheckCallback) {
        function _update(force) {
            var $checkboxes = $(checkboxContainer).find(checkboxSelector).filter(':checked');
            var checked = force || !!$checkboxes.length;
            if (extraCheckCallback && extraCheckCallback($checkboxes) === false) {
                checked = false;
            }
            $(buttonSelector).prop('disabled', !checked).toggleClass('disabled', !checked);
        }

        $(checkboxContainer).on('change', checkboxSelector, function() {
            _update(this.checked);
        }).on('indico:syncEnableIfChecked', function() {
            _update();
        });

        _update();
    };

    /* Provide a "select really everything" option for paginated lists.
     * When selecting all rows in such a list a message is shown indicating
     * that only the current page is selected with the option to select all
     * items (no matter on which page).
     */
    global.paginatedSelectAll = function paginatedSelectAll(options) {
        options = $.extend(true, {
            // the container element used as context for all other selectors.
            // in case of dynamically updated rows, this must remain the same
            // all the time as events are bound to it
            containerSelector: null,
            // the selector for the row selection checkboxes. used within the
            // context of containerSelector
            checkboxSelector: null,
            // the selector for the (usually hidden) all-items-on-all-pages-selected
            // checkbox. used within the context of containerSelector
            allSelectedSelector: null,
            // the selector for the element where the message whether all items
            // on the current page or really all items are selected.
            // used within the global context, i.e. not restricted by the given
            // containerSelector.
            selectionMessageSelector: null,
            // the total number of rows. may also be a function.
            totalRows: 0,
            messages: {
                // message shown when all items on all pages are selected
                allSelected: function(total) {
                    // never used with total == 1. empty string is invalid so we use `*` instead
                    return $T.ngettext('*', 'All {0} rows are currently selected.').format(total);
                },
                // message shown when all items on the current page are selected
                pageSelected: function(selected, total) {
                    // never used with total == 1
                    return $T.ngettext('Only {0} out of {1} rows is currently selected',
                                       'Only {0} out of {1} rows are currently selected.',
                                       selected).format(selected, total);
                }
            }
        }, options);

        var container = $(options.containerSelector);
        container.on('change', options.checkboxSelector, _update);
        _update();

        function _update() {
            var messageContainer = $(options.selectionMessageSelector).empty();
            var numChecked = container.find(options.checkboxSelector + ':checked').length;
            var numUnchecked = container.find(options.checkboxSelector + ':not(:checked)').length;
            var numRows = numChecked + numUnchecked;

            if (numChecked < numRows) {
                // not everything selected
                messageContainer.hide();
                _setAllSelected(false);
            } else if (numChecked === options.totalRows) {
                // everything selected and only one page
                messageContainer.hide();
                _setAllSelected(true);
            } else if (numChecked === numRows) {
                // all rows selected
                if (_getAllSelected()) {
                    $('<span>', {
                        html: options.messages.allSelected(options.totalRows)
                    }).appendTo(messageContainer);
                    messageContainer.append(' ');
                    $('<a>', {
                        href: '#',
                        text: $T.gettext('Select only the current page.'),
                        click: function(evt) {
                            evt.preventDefault();
                            _setAllSelected(false);
                            _update();
                        }
                    }).appendTo(messageContainer);
                } else {
                    $('<span>', {
                        html: options.messages.pageSelected(numChecked, options.totalRows)
                    }).appendTo(messageContainer);
                    messageContainer.append(' ');
                    $('<a>', {
                        href: '#',
                        text: $T.gettext('Click here to select them all.'),
                        click: function(evt) {
                            evt.preventDefault();
                            _setAllSelected(true);
                            _update();
                        }
                    }).appendTo(messageContainer);
                }
                messageContainer.show();
            }
        }

        function _getAllSelected() {
            return container.find(options.allSelectedSelector).prop('checked');
        }

        function _setAllSelected(selected) {
            container.find(options.allSelectedSelector).prop('checked', selected).trigger('change');
        }

        return {
            isEverythingSelected: _getAllSelected
        };
    };

    $(document).ready(function() {
        initForms($('form'));
    });
})(window);
<|endoftext|>


<commit_before>/**
 * 返回给客户端的状态、消息和json数据
 */
export default async (ctx, next) => {
  ctx.error = (message, data = {}, code = 500) => {
    ctx.body = {
      code: 500,
      message,
      data
    }
  }
  ctx.notFound = (message, data = {}) => {
    ctx.body = {
      code: 404,
      message,
      data
    }
  }
  ctx.success = (data, message = '') => {
    ctx.body = {
      code: 200,
      message,
      data
    }
  }
  await next()
}
<commit_msg>增加默认message<commit_after>/**
 * 返回给客户端的状态、消息和json数据
 */
export default async (ctx, next) => {
  ctx.error = (message = '服务器出错', data = {}, code = 500) => {
    ctx.body = {
      code,
      message,
      data
    }
  }
  ctx.notFound = (message, data = {}) => {
    ctx.body = {
      code: 404,
      message,
      data
    }
  }
  ctx.success = (data, message = 'ok') => {
    ctx.body = {
      code: 200,
      message,
      data
    }
  }
  await next()
}
<|endoftext|>


<commit_before>import Integration from './../Integration.js';
import deleteProperty from './../functions/deleteProperty';
import cleanObject from './../functions/cleanObject';
import { getProp } from './../functions/dotProp';
import {
  VIEWED_PAGE,
  VIEWED_PRODUCT_DETAIL,
  ADDED_PRODUCT,
  REMOVED_PRODUCT,
  COMPLETED_TRANSACTION,
} from './../events';

const SEMANTIC_EVENTS = [
  VIEWED_PAGE,
  VIEWED_PRODUCT_DETAIL,
  ADDED_PRODUCT,
  REMOVED_PRODUCT,
  COMPLETED_TRANSACTION,
];

/* eslint-disable new-cap */

class Ofsys extends Integration {

  constructor(digitalData, options) {
    const optionsWithDefaults = Object.assign({
      accountKey: '',
    }, options);

    super(digitalData, optionsWithDefaults);

    this.addTag({
      type: 'script',
      attr: {
        src: `https://t.ofsys.com/js/Journey/1/${options.accountKey}/DI.Journey-min.js`,
      },
    });
  }

  getSemanticEvents() {
    return SEMANTIC_EVENTS;
  }

  getEnrichableEventProps(event) {
    let enrichableProps = [];
    switch (event.name) {
    case VIEWED_PAGE:
      enrichableProps = [
        'user.email',
        'cart',
      ];
      break;
    case VIEWED_PRODUCT_DETAIL:
      enrichableProps = [
        'product.id',
      ];
      break;
    case COMPLETED_TRANSACTION:
      enrichableProps = [
        'transaction',
      ];
      break;
    default:
      // do nothing
    }

    return enrichableProps;
  }

  initialize() {
    this.asyncQueue = [];

    // emulate async queue for Ofsys sync script
    let invervalCounter = 0;
    const invervalId = setInterval(() => {
      invervalCounter++;
      if (this.isLoaded()) {
        this.flushQueue();
        clearInterval(invervalId);
      } else if (invervalCounter > 10) {
        clearInterval(invervalId);
      }
    }, 100);

    this.load(this.onLoad);
  }

  flushQueue() {
    let handler = this.asyncQueue.shift();
    while (handler && typeof handler === 'function') {
      handler();
      handler = this.asyncQueue.shift();
    }
    this.asyncQueue.push = (callback) => {
      callback();
    };
  }

  isLoaded() {
    return !!(window.DI && window.DI.Journey);
  }

  reset() {
    deleteProperty(window, 'OFSYS_Tracking_onload');
    deleteProperty(window, 'DI');
  }

  createCart(cart) {
    if (!cart || !cart.id) return;

    this.cartId = cart.id;
    this.asyncQueue.push(() => {
      window.DI.Journey.ECommerce.AddCart(cleanObject({ // eslint-disable-line new-cap
        idCart: cart.id,
        status: cart.checkoutStep,
        TotalPrice: cart.total,
      }));
    });

    if (cart.lineItems && Array.isArray(cart.lineItems)) {
      this.asyncQueue.push(() => {
        for (const lineItem of cart.lineItems) {
          window.DI.Journey.ECommerce.AddCartItem({
            idCart: cart.id,
            idProduct: getProp(lineItem, 'product.id'),
            productName: getProp(lineItem, 'product.name'),
            priceunit: getProp(lineItem, 'product.unitSalePrice') || getProp(lineItem, 'product.unitPrice'),
            quantity: lineItem.quantity || 1,
          });
        }
      });
    }

    this.asyncQueue.push(() => {
      window.DI.Journey.ECommerce.SubmitCart();
    });
  }

  trackEvent(event) {
    const methods = {
      [VIEWED_PAGE]: 'onViewedPage',
      [VIEWED_PRODUCT_DETAIL]: 'onViewedProductDetail',
      [ADDED_PRODUCT]: 'onAddedProduct',
      [REMOVED_PRODUCT]: 'onRemovedProduct',
      [COMPLETED_TRANSACTION]: 'onCompletedTransaction',
    };

    const method = methods[event.name];
    if (method) {
      this[method](event);
    }
  }

  onViewedPage(event) {
    const email = getProp(event, 'user.email');
    if (email) {
      this.asyncQueue.push(() => {
        window.DI.Journey.Set('f_EMail', email);
        window.DI.Journey.Track();
      });
    }

    this.createCart(this.cart);
  }

  onViewedProductDetail(event) {
    const product = event.product;
    if (!product || !product.id) return;

    this.asyncQueue.push(() => {
      window.DI.Journey.ECommerce.ProductView(product.id);
    });
  }

  onAddedProduct(event) {
    const product = event.product;
    if (!product || !product.id || !this.cartId) return;

    this.asyncQueue.push(() => {
      window.DI.Journey.ECommerce.AddCartItem({
        idCart: this.cartId,
        idProduct: product.id,
        productName: product.name,
        priceunit: product.unitSalePrice || product.unitPrice,
        quantity: event.quantity || 1,
      });
      window.DI.Journey.ECommerce.SubmitCart();
    });
  }

  onRemovedProduct(event) {
    const product = event.product;
    if (!product || !product.id || !this.cartId) return;

    this.asyncQueue.push(() => {
      window.DI.Journey.ECommerce.RemoveCartItem({
        idCart: '43234',
        idProduct: '54368',
      });
      window.DI.Journey.ECommerce.SubmitCart();
    });
  }

  onCompletedTransaction(event) {
    const transaction = event.transaction;
    if (!transaction || !transaction.orderId) return;

    this.asyncQueue.push(() => {
      window.DI.Journey.ECommerce.AddTransaction(cleanObject({
        idTransaction: transaction.orderId,
        affiliation: transaction.affiliation,
        revenue: transaction.total,
        tax: transaction.tax,
        shipping: transaction.shippingCost,
      }));
    });

    if (transaction.lineItems && Array.isArray(transaction.lineItems)) {
      this.asyncQueue.push(() => {
        for (const lineItem of transaction.lineItems) {
          const unitSalePrice = getProp(lineItem, 'product.unitSalePrice') || getProp(lineItem, 'product.unitPrice');
          const quantity = lineItem.quantity || 1;
          window.DI.Journey.ECommerce.AddItem({
            idTransaction: transaction.cartId,
            idProduct: getProp(lineItem, 'product.id'),
            productName: getProp(lineItem, 'product.name'),
            Price_unit: unitSalePrice,
            Price_total: lineItem.subtotal || unitSalePrice * quantity,
            quantity: quantity,
          });
        }
      });
    }

    this.asyncQueue.push(() => {
      window.DI.Journey.ECommerce.SubmitTransaction();
    });
  }
}

export default Ofsys;
<commit_msg>fixed onRemovedProduct method<commit_after>import Integration from './../Integration.js';
import deleteProperty from './../functions/deleteProperty';
import cleanObject from './../functions/cleanObject';
import { getProp } from './../functions/dotProp';
import {
  VIEWED_PAGE,
  VIEWED_PRODUCT_DETAIL,
  ADDED_PRODUCT,
  REMOVED_PRODUCT,
  COMPLETED_TRANSACTION,
} from './../events';

const SEMANTIC_EVENTS = [
  VIEWED_PAGE,
  VIEWED_PRODUCT_DETAIL,
  ADDED_PRODUCT,
  REMOVED_PRODUCT,
  COMPLETED_TRANSACTION,
];

/* eslint-disable new-cap */

class Ofsys extends Integration {

  constructor(digitalData, options) {
    const optionsWithDefaults = Object.assign({
      accountKey: '',
    }, options);

    super(digitalData, optionsWithDefaults);

    this.addTag({
      type: 'script',
      attr: {
        src: `https://t.ofsys.com/js/Journey/1/${options.accountKey}/DI.Journey-min.js`,
      },
    });
  }

  getSemanticEvents() {
    return SEMANTIC_EVENTS;
  }

  getEnrichableEventProps(event) {
    let enrichableProps = [];
    switch (event.name) {
    case VIEWED_PAGE:
      enrichableProps = [
        'user.email',
        'cart',
      ];
      break;
    case VIEWED_PRODUCT_DETAIL:
      enrichableProps = [
        'product.id',
      ];
      break;
    case COMPLETED_TRANSACTION:
      enrichableProps = [
        'transaction',
      ];
      break;
    default:
      // do nothing
    }

    return enrichableProps;
  }

  initialize() {
    this.asyncQueue = [];

    // emulate async queue for Ofsys sync script
    let invervalCounter = 0;
    const invervalId = setInterval(() => {
      invervalCounter++;
      if (this.isLoaded()) {
        this.flushQueue();
        clearInterval(invervalId);
      } else if (invervalCounter > 10) {
        clearInterval(invervalId);
      }
    }, 100);

    this.load(this.onLoad);
  }

  flushQueue() {
    let handler = this.asyncQueue.shift();
    while (handler && typeof handler === 'function') {
      handler();
      handler = this.asyncQueue.shift();
    }
    this.asyncQueue.push = (callback) => {
      callback();
    };
  }

  isLoaded() {
    return !!(window.DI && window.DI.Journey);
  }

  reset() {
    deleteProperty(window, 'OFSYS_Tracking_onload');
    deleteProperty(window, 'DI');
  }

  createCart(cart) {
    if (!cart || !cart.id) return;

    this.cartId = cart.id;
    this.asyncQueue.push(() => {
      window.DI.Journey.ECommerce.AddCart(cleanObject({ // eslint-disable-line new-cap
        idCart: cart.id,
        status: cart.checkoutStep,
        TotalPrice: cart.total,
      }));
    });

    if (cart.lineItems && Array.isArray(cart.lineItems)) {
      this.asyncQueue.push(() => {
        for (const lineItem of cart.lineItems) {
          window.DI.Journey.ECommerce.AddCartItem({
            idCart: cart.id,
            idProduct: getProp(lineItem, 'product.id'),
            productName: getProp(lineItem, 'product.name'),
            priceunit: getProp(lineItem, 'product.unitSalePrice') || getProp(lineItem, 'product.unitPrice'),
            quantity: lineItem.quantity || 1,
          });
        }
      });
    }

    this.asyncQueue.push(() => {
      window.DI.Journey.ECommerce.SubmitCart();
    });
  }

  trackEvent(event) {
    const methods = {
      [VIEWED_PAGE]: 'onViewedPage',
      [VIEWED_PRODUCT_DETAIL]: 'onViewedProductDetail',
      [ADDED_PRODUCT]: 'onAddedProduct',
      [REMOVED_PRODUCT]: 'onRemovedProduct',
      [COMPLETED_TRANSACTION]: 'onCompletedTransaction',
    };

    const method = methods[event.name];
    if (method) {
      this[method](event);
    }
  }

  onViewedPage(event) {
    const email = getProp(event, 'user.email');
    if (email) {
      this.asyncQueue.push(() => {
        window.DI.Journey.Set('f_EMail', email);
        window.DI.Journey.Track();
      });
    }

    this.createCart(this.cart);
  }

  onViewedProductDetail(event) {
    const product = event.product;
    if (!product || !product.id) return;

    this.asyncQueue.push(() => {
      window.DI.Journey.ECommerce.ProductView(product.id);
    });
  }

  onAddedProduct(event) {
    const product = event.product;
    if (!product || !product.id || !this.cartId) return;

    this.asyncQueue.push(() => {
      window.DI.Journey.ECommerce.AddCartItem({
        idCart: this.cartId,
        idProduct: product.id,
        productName: product.name,
        priceunit: product.unitSalePrice || product.unitPrice,
        quantity: event.quantity || 1,
      });
      window.DI.Journey.ECommerce.SubmitCart();
    });
  }

  onRemovedProduct(event) {
    const product = event.product;
    if (!product || !product.id || !this.cartId) return;

    this.asyncQueue.push(() => {
      window.DI.Journey.ECommerce.RemoveCartItem({
        idCart: this.cartId,
        idProduct: product.id,
      });
      window.DI.Journey.ECommerce.SubmitCart();
    });
  }

  onCompletedTransaction(event) {
    const transaction = event.transaction;
    if (!transaction || !transaction.orderId) return;

    this.asyncQueue.push(() => {
      window.DI.Journey.ECommerce.AddTransaction(cleanObject({
        idTransaction: transaction.orderId,
        affiliation: transaction.affiliation,
        revenue: transaction.total,
        tax: transaction.tax,
        shipping: transaction.shippingCost,
      }));
    });

    if (transaction.lineItems && Array.isArray(transaction.lineItems)) {
      this.asyncQueue.push(() => {
        for (const lineItem of transaction.lineItems) {
          const unitSalePrice = getProp(lineItem, 'product.unitSalePrice') || getProp(lineItem, 'product.unitPrice');
          const quantity = lineItem.quantity || 1;
          window.DI.Journey.ECommerce.AddItem({
            idTransaction: transaction.cartId,
            idProduct: getProp(lineItem, 'product.id'),
            productName: getProp(lineItem, 'product.name'),
            Price_unit: unitSalePrice,
            Price_total: lineItem.subtotal || unitSalePrice * quantity,
            quantity: quantity,
          });
        }
      });
    }

    this.asyncQueue.push(() => {
      window.DI.Journey.ECommerce.SubmitTransaction();
    });
  }
}

export default Ofsys;
<|endoftext|>


<commit_before>"use strict";
var Marks = {
  activateCreateMode: function() {
    handlerStack.push({
      keydown: this.onKeydown,
      keyup: handlerStack.StopProp,
      keypress: this._create,
      _this: this
    });
    VHUD.show("Create mark ...");
  },
  activateGotoMode: function() {
    handlerStack.push({
      keydown: this.onKeydown,
      keyup: handlerStack.StopProp,
      keypress: this._goto,
      _this: this
    });
    VHUD.show("Go to mark ...");
  },
  onKeydown: function() {
    if (event.keyCode === KeyCodes.esc && KeyboardUtils.isPlain(event)) {
      handlerStack.remove();
      VHUD.hide();
      return false;
    }
    return -1;
  },
  getBaseUrl: function() {
    return window.location.href.split('#', 1)[0];
  },
  getLocationKey: function(keyChar) {
    return "vimiumMark|" + this.getBaseUrl() + "|" + keyChar;
  },
  getMarkString: function() {
    return ;
  },
  // previousPositionRegisters: ["`", "'"],
  _previous: null,
  setPreviousPosition: function() {
    this._previous = {
      scrollX: window.scrollX,
      scrollY: window.scrollY
    };
  },
  _create: function(event) {
    var keyChar = String.fromCharCode(event.charCode);
    handlerStack.remove();
    if (event.shiftKey) {
      this.CreateGlobalMark({markName: keyChar});
      VHUD.hide();
      return;
    }
    if (keyChar === "`" || keyChar === "'") {
      this.setPreviousPosition();
    } else {
      localStorage[this.getLocationKey(keyChar)] = JSON.stringify({
        scrollX: window.scrollX,
        scrollY: window.scrollY
      });
    }
    VHUD.showForDuration("Created local mark '" + keyChar + "'", 1000);
    return false;
  },
  _goto: function(event) {
    var keyChar = String.fromCharCode(event.charCode), markString, position;
    handlerStack.remove();
    if (event.shiftKey) {
      MainPort.postMessage({
        handler: "Marks.gotoMark",
        markName: keyChar
      }, function(req) {
        if (req === false) {
          VHUD.showForDuration("Global mark not set '" + keyChar + "'", 1500);
        }
      });
      VHUD.hide();
      return false;
    }
    if (keyChar === "`" || keyChar === "'") {
      if (!(position = this._previous)) {
        this.setPreviousPosition();
        VHUD.showForDuration("Created local mark '" + keyChar + "'", 1000);
        return;
      }
    } else if (markString = localStorage[this.getLocationKey(keyChar)]) {
      position = JSON.parse(markString);
    }
    if (position) {
      this.setPreviousPosition();
      window.scrollTo(position.scrollX, position.scrollY);
      VHUD.showForDuration("Jumped to local mark ' " + keyChar + " '", 1000);
    } else {
      VHUD.showForDuration("Local mark not set '" + keyChar + "'", 2000);
    }
    return false;
  },
  CreateGlobalMark: function(request) {
    var keyChar = request.markName;
    if (window.top !== window && !request.force) {
      MainPort.postMessage({handler: 'Marks.createMark', markName: keyChar});
      return;
    }
    MainPort.postMessage({
      handler: 'Marks.createMark',
      markName: keyChar,
      url: Marks.getBaseUrl(),
      scroll: [window.scrollX, window.scrollY]
    });
    VHUD.showForDuration("Created global mark '" + keyChar + "'", 1000);
  },
  Goto: function(request) {
    var scroll = request.scroll;
    if (!document.body || document.body.nodeName.toLowerCase() !== "frameset") {
      window.focus();
    }
    if (request.markName) {
      Marks.setPreviousPosition();
      window.scrollTo(scroll[0], scroll[1]);
      VHUD.showForDuration("Jumped to global mark '" + request.markName + "'", 2000);
    } else {
      window.scrollTo(scroll[0], scroll[1]);
    }
  }
};<commit_msg>small changes<commit_after>"use strict";
var Marks = {
  activateCreateMode: function() {
    handlerStack.push({
      keydown: this.onKeydown,
      keyup: handlerStack.StopProp,
      keypress: this._create,
      _this: this
    });
    VHUD.show("Create mark ...");
  },
  activateGotoMode: function() {
    handlerStack.push({
      keydown: this.onKeydown,
      keyup: handlerStack.StopProp,
      keypress: this._goto,
      _this: this
    });
    VHUD.show("Go to mark ...");
  },
  onKeydown: function() {
    if (event.keyCode === KeyCodes.esc && KeyboardUtils.isPlain(event)) {
      handlerStack.remove();
      VHUD.hide();
      return false;
    }
    return -1;
  },
  getBaseUrl: function() {
    return window.location.href.split('#', 1)[0];
  },
  getLocationKey: function(keyChar) {
    return "vimiumMark|" + this.getBaseUrl() + "|" + keyChar;
  },
  // previousPositionRegisters: ["`", "'"],
  _previous: null,
  setPreviousPosition: function() {
    this._previous = {
      scrollX: window.scrollX,
      scrollY: window.scrollY
    };
  },
  _create: function(event) {
    var keyChar = String.fromCharCode(event.charCode);
    handlerStack.remove();
    if (event.shiftKey) {
      this.CreateGlobalMark({markName: keyChar});
      return;
    }
    if (keyChar === "`" || keyChar === "'") {
      this.setPreviousPosition();
    } else {
      localStorage[this.getLocationKey(keyChar)] = JSON.stringify({
        scrollX: window.scrollX,
        scrollY: window.scrollY
      });
    }
    VHUD.showForDuration("Created local mark '" + keyChar + "'", 1000);
    return false;
  },
  _goto: function(event) {
    var keyChar = String.fromCharCode(event.charCode), markString, position;
    handlerStack.remove();
    if (event.shiftKey) {
      MainPort.postMessage({
        handler: "Marks.gotoMark",
        markName: keyChar
      }, function(req) {
        if (req === false) {
          VHUD.showForDuration("Global mark not set '" + keyChar + "'", 1500);
        }
      });
      VHUD.hide();
      return false;
    }
    if (keyChar === "`" || keyChar === "'") {
      if (!(position = this._previous)) {
        this.setPreviousPosition();
        VHUD.showForDuration("Created local mark '" + keyChar + "'", 1000);
        return;
      }
    } else if (markString = localStorage[this.getLocationKey(keyChar)]) {
      position = JSON.parse(markString);
    }
    if (position) {
      this.setPreviousPosition();
      window.scrollTo(position.scrollX, position.scrollY);
      VHUD.showForDuration("Jumped to local mark ' " + keyChar + " '", 1000);
    } else {
      VHUD.showForDuration("Local mark not set '" + keyChar + "'", 2000);
    }
    return false;
  },
  CreateGlobalMark: function(request) {
    var keyChar = request.markName;
    if (window.top !== window && !request.force) {
      MainPort.postMessage({handler: 'Marks.createMark', markName: keyChar});
      VHUD.hide();
      return;
    }
    MainPort.postMessage({
      handler: 'Marks.createMark',
      markName: keyChar,
      url: Marks.getBaseUrl(),
      scroll: [window.scrollX, window.scrollY]
    });
    VHUD.showForDuration("Created global mark '" + keyChar + "'", 1000);
  },
  Goto: function(request) {
    var scroll = request.scroll;
    if (!document.body || document.body.nodeName.toLowerCase() !== "frameset") {
      window.focus();
    }
    if (request.markName) {
      Marks.setPreviousPosition();
      window.scrollTo(scroll[0], scroll[1]);
      VHUD.showForDuration("Jumped to global mark '" + request.markName + "'", 2000);
    } else {
      window.scrollTo(scroll[0], scroll[1]);
    }
  }
};<|endoftext|>


<commit_before>var buttonHandler = {};
var selectHandler = {};
var buttonBar = 0;
var summary = 0;
var exportModule = 0;
var importModule = 0;

$(document).ready(function() {
    renderContainer(template_data);

    startSubModules();
    checkIfExported();

    $('#summary').scrollToFixed();
});

function renderContainer(data) {
    dust.render('container', template_data,

    function(err, out) {
        if (err) {
            console.log(err);
        }
        $('.container').html(out);
    });
};

function checkIfExported() {
    var vars = $.getUrlVars();
    if(!$.isEmptyObject(vars)) {
        importModule.start(vars);
    }
};

function startSubModules() {
    for (var i = 0; i < template_data.slots.length; i++) {
        startDistributionButtonHandler(template_data.slots[i].id_prefix);
        startSelectHandler(template_data.slots[i].id_prefix);
    }
    startButtonBar();
    startSummary();
    startExportModule();
    startImportModule();
};

function startDistributionButtonHandler(slotId) {
    buttonHandler[slotId] = new DistributionButtonHandler(slotId);
    buttonHandler[slotId].initiate();
};

function startSelectHandler(slotId) {
    selectHandler[slotId] = new SelectHandler(slotId);
    selectHandler[slotId].initiate();
};

function startButtonBar() {
    buttonBar = new ButtonBar();
    buttonBar.initiate();
};

function startSummary() {
    summary = new Summary();
};

function startExportModule() {
    exportModule = new Export();
    exportModule.initiate();
};

function startImportModule() {
    importModule = new Import();
};<commit_msg>Added hash when there is none, so export doesn't output an unusable URL<commit_after>var buttonHandler = {};
var selectHandler = {};
var buttonBar = 0;
var summary = 0;
var exportModule = 0;
var importModule = 0;

$(document).ready(function() {
    renderContainer(template_data);

    addHash();
    startSubModules();
    checkIfExported();

    $('#summary').scrollToFixed();
});

function renderContainer(data) {
    dust.render('container', template_data,

    function(err, out) {
        if (err) {
            console.log(err);
        }
        $('.container').html(out);
    });
};

function checkIfExported() {
    var vars = $.getUrlVars();
    if(!$.isEmptyObject(vars) && Object.keys(vars).length == 8) {
        importModule.start(vars);
    }
};

function startSubModules() {
    for (var i = 0; i < template_data.slots.length; i++) {
        startDistributionButtonHandler(template_data.slots[i].id_prefix);
        startSelectHandler(template_data.slots[i].id_prefix);
    }
    startButtonBar();
    startSummary();
    startExportModule();
    startImportModule();
};

function addHash() {
    if(location.hash == '') {
        location.hash = ' ';
    }
};

function startDistributionButtonHandler(slotId) {
    buttonHandler[slotId] = new DistributionButtonHandler(slotId);
    buttonHandler[slotId].initiate();
};

function startSelectHandler(slotId) {
    selectHandler[slotId] = new SelectHandler(slotId);
    selectHandler[slotId].initiate();
};

function startButtonBar() {
    buttonBar = new ButtonBar();
    buttonBar.initiate();
};

function startSummary() {
    summary = new Summary();
};

function startExportModule() {
    exportModule = new Export();
    exportModule.initiate();
};

function startImportModule() {
    importModule = new Import();
};<|endoftext|>


<commit_before>'use strict';

var async = require('async');

module.exports = {
  loadPriority:  430,
  initialize: function(api, next){

    api.actionProcessor = function(connection, callback){
      if(!connection){
        throw new Error('data.connection is required');
      }

      this.connection      = connection;
      this.action          = null;
      this.toProcess       = true;
      this.toRender        = true;
      this.messageCount    = connection.messageCount;
      this.params          = connection.params;
      this.callback        = callback;
      this.missingParams   = [];
      this.validatorErrors = [];
      this.actionStartTime = null;
      this.actionTemplate  = null;
      this.working         = false;
      this.response        = {};
      this.duration        = null;
      this.actionStatus    = null;
    };

    api.actionProcessor.prototype.incrementTotalActions = function(count){
      var self = this;
      if(!count){ count = 1; }
      self.connection.totalActions = self.connection.totalActions + count;
    };

    api.actionProcessor.prototype.incrementPendingActions = function(count){
      var self = this;
      if(!count){ count = 1; }
      self.connection.pendingActions = self.connection.pendingActions + count;
    };

    api.actionProcessor.prototype.getPendingActionCount = function(){
      var self = this;
      return self.connection.pendingActions;
    };

    api.actionProcessor.prototype.completeAction = function(status){
      var self = this;
      var error = null;
      self.actionStatus = String(status);

      if(status instanceof Error){
        error = status;
      }else if(status === 'server_shutting_down'){
        error = api.config.errors.serverShuttingDown(self);
      }else if(status === 'too_many_requests'){
        error = api.config.errors.tooManyPendingActions(self);
      }else if(status === 'unknown_action'){
        error = api.config.errors.unknownAction(self);
      }else if(status === 'unsupported_server_type'){
        error = api.config.errors.unsupportedServerType(self);
      }else if(status === 'missing_params'){
        error = api.config.errors.missingParams(self, self.missingParams);
      }else if(status === 'validator_errors'){
        error = api.config.errors.invalidParams(self, self.validatorErrors);
      }else if(status){
        error = status;
      }

      if(error && typeof error === 'string'){
        error = new Error(error);
      }
      if(error && !self.response.error){
        self.response.error = error;
      }

      self.incrementPendingActions(-1);
      self.duration = new Date().getTime() - self.actionStartTime;

      process.nextTick(function(){
        if(typeof self.callback === 'function'){
          self.callback(self);
        }
      });

      self.working = false;
      self.logAction(error);
    };

    api.actionProcessor.prototype.logAction = function(error){
      var self = this;

      // logging
      var logLevel = 'info';
      if(self.actionTemplate && self.actionTemplate.logLevel){
        logLevel = self.actionTemplate.logLevel;
      }

      var filteredParams = {};
      for(var i in self.params){
        if(api.config.general.filteredParams && api.config.general.filteredParams.indexOf(i) >= 0){
          filteredParams[i] = '[FILTERED]';
        }else if(typeof self.params[i] === 'string'){
          filteredParams[i] = self.params[i].substring(0, api.config.logger.maxLogStringLength);
        }else{
          filteredParams[i] = self.params[i];
        }
      }

      var logLine = {
        to: self.connection.remoteIP,
        action: self.action,
        params: JSON.stringify(filteredParams),
        duration: self.duration,
      };

      if(error){
        if(error instanceof Error){
          logLine.error = String(error);
        }else{
          try{
            logLine.error = JSON.stringify(error);
          }catch(e){
            logLine.error = String(error);
          }
        }
      }

      api.log(['[ action @ %s ]', self.connection.type], logLevel, logLine);
    };

    api.actionProcessor.prototype.preProcessAction = function(callback){
      var self = this;
      var processors     = [];
      var processorNames = api.actions.globalMiddleware.slice(0);

      if(self.actionTemplate.middleware){
        self.actionTemplate.middleware.forEach(function(m){ processorNames.push(m); });
      }

      processorNames.forEach(function(name){
        if(typeof api.actions.middleware[name].preProcessor === 'function'){
          processors.push(function(next){ api.actions.middleware[name].preProcessor(self, next); });
        }
      });

      async.series(processors, callback);
    };

    api.actionProcessor.prototype.postProcessAction = function(callback){
      var self = this;
      var processors     = [];
      var processorNames = api.actions.globalMiddleware.slice(0);

      if(self.actionTemplate.middleware){
        self.actionTemplate.middleware.forEach(function(m){ processorNames.push(m); });
      }

      processorNames.forEach(function(name){
        if(typeof api.actions.middleware[name].postProcessor === 'function'){
          processors.push(function(next){ api.actions.middleware[name].postProcessor(self, next); });
        }
      });

      async.series(processors, callback);
    };

    api.actionProcessor.prototype.reduceParams = function(){
      var self = this;
      var inputNames = [];
      if(self.actionTemplate.inputs){
        inputNames = Object.keys(self.actionTemplate.inputs);
      }

      if(api.config.general.disableParamScrubbing !== true){
        for(var p in self.params){
          if(api.params.globalSafeParams.indexOf(p) < 0 && inputNames.indexOf(p) < 0){
            delete self.params[p];
          }
        }
      }
    };

    api.actionProcessor.prototype.validateParams = function(){
      var self = this;

      for(var key in self.actionTemplate.inputs){
        var props = self.actionTemplate.inputs[key];

        // default
        if(self.params[key] === undefined && props['default'] !== undefined){
          if(typeof props['default'] === 'function'){
            self.params[key] = props['default'].call(api, self.params[key], self);
          }else{
            self.params[key] = props['default'];
          }
        }

        // formatter
        if(self.params[key] !== undefined && typeof props.formatter === 'function'){
          self.params[key] = props.formatter.call(api, self.params[key], self);
        }

        // validator
        if(self.params[key] !== undefined && typeof props.validator === 'function'){
          var validatorResponse = props.validator.call(api, self.params[key], self);
          if(validatorResponse !== true){
            self.validatorErrors.push(validatorResponse);
          }
        }

        // required
        if(props.required === true){
          if(api.config.general.missingParamChecks.indexOf(self.params[key]) >= 0){
            self.missingParams.push(key);
          }
        }
      }
    };

    api.actionProcessor.prototype.processAction = function(){
      var self = this;
      self.actionStartTime = new Date().getTime();
      self.working = true;
      self.incrementTotalActions();
      self.incrementPendingActions();
      self.action = self.params.action;

      if(api.actions.versions[self.action]){
        if(!self.params.apiVersion){
          self.params.apiVersion = api.actions.versions[self.action][api.actions.versions[self.action].length - 1];
        }
        self.actionTemplate = api.actions.actions[self.action][self.params.apiVersion];
      }

      if(api.running !== true){
        self.completeAction('server_shutting_down');
      }else if(self.getPendingActionCount(self.connection) > api.config.general.simultaneousActions){
        self.completeAction('too_many_requests');
      }else if(!self.action || !self.actionTemplate){
        self.completeAction('unknown_action');
      }else if(self.actionTemplate.blockedConnectionTypes && self.actionTemplate.blockedConnectionTypes.indexOf(self.connection.type) >= 0){
        self.completeAction('unsupported_server_type');
      }else{
        self.runAction();
      }
    };

    api.actionProcessor.prototype.runAction = function(){
      var self = this;

      self.preProcessAction(function(error){
        self.reduceParams();
        self.validateParams();

        if(error){
          self.completeAction(error);
        }else if(self.missingParams.length > 0){
          self.completeAction('missing_params');
        }else if(self.validatorErrors.length > 0){
          self.completeAction('validator_errors');
        }else if(self.toProcess === true && !error){
          self.actionTemplate.run(api, self, function(error){
            if(error){
              self.completeAction(error);
            }else{
              self.postProcessAction(function(error){
                self.completeAction(error);
              });
            }
          });
        }else{
          self.completeAction();
        }
      });
    };

    next();
  }
};
<commit_msg>validators and formatters can be passed as function names<commit_after>'use strict';

var async = require('async');

module.exports = {
  loadPriority:  430,
  initialize: function(api, next){

    api.actionProcessor = function(connection, callback){
      if(!connection){
        throw new Error('data.connection is required');
      }

      this.connection      = connection;
      this.action          = null;
      this.toProcess       = true;
      this.toRender        = true;
      this.messageCount    = connection.messageCount;
      this.params          = connection.params;
      this.callback        = callback;
      this.missingParams   = [];
      this.validatorErrors = [];
      this.actionStartTime = null;
      this.actionTemplate  = null;
      this.working         = false;
      this.response        = {};
      this.duration        = null;
      this.actionStatus    = null;
    };

    api.actionProcessor.prototype.incrementTotalActions = function(count){
      var self = this;
      if(!count){ count = 1; }
      self.connection.totalActions = self.connection.totalActions + count;
    };

    api.actionProcessor.prototype.incrementPendingActions = function(count){
      var self = this;
      if(!count){ count = 1; }
      self.connection.pendingActions = self.connection.pendingActions + count;
    };

    api.actionProcessor.prototype.getPendingActionCount = function(){
      var self = this;
      return self.connection.pendingActions;
    };

    api.actionProcessor.prototype.completeAction = function(status){
      var self = this;
      var error = null;
      self.actionStatus = String(status);

      if(status instanceof Error){
        error = status;
      }else if(status === 'server_shutting_down'){
        error = api.config.errors.serverShuttingDown(self);
      }else if(status === 'too_many_requests'){
        error = api.config.errors.tooManyPendingActions(self);
      }else if(status === 'unknown_action'){
        error = api.config.errors.unknownAction(self);
      }else if(status === 'unsupported_server_type'){
        error = api.config.errors.unsupportedServerType(self);
      }else if(status === 'missing_params'){
        error = api.config.errors.missingParams(self, self.missingParams);
      }else if(status === 'validator_errors'){
        error = api.config.errors.invalidParams(self, self.validatorErrors);
      }else if(status){
        error = status;
      }

      if(error && typeof error === 'string'){
        error = new Error(error);
      }
      if(error && !self.response.error){
        self.response.error = error;
      }

      self.incrementPendingActions(-1);
      self.duration = new Date().getTime() - self.actionStartTime;

      process.nextTick(function(){
        if(typeof self.callback === 'function'){
          self.callback(self);
        }
      });

      self.working = false;
      self.logAction(error);
    };

    api.actionProcessor.prototype.logAction = function(error){
      var self = this;

      // logging
      var logLevel = 'info';
      if(self.actionTemplate && self.actionTemplate.logLevel){
        logLevel = self.actionTemplate.logLevel;
      }

      var filteredParams = {};
      for(var i in self.params){
        if(api.config.general.filteredParams && api.config.general.filteredParams.indexOf(i) >= 0){
          filteredParams[i] = '[FILTERED]';
        }else if(typeof self.params[i] === 'string'){
          filteredParams[i] = self.params[i].substring(0, api.config.logger.maxLogStringLength);
        }else{
          filteredParams[i] = self.params[i];
        }
      }

      var logLine = {
        to: self.connection.remoteIP,
        action: self.action,
        params: JSON.stringify(filteredParams),
        duration: self.duration,
      };

      if(error){
        if(error instanceof Error){
          logLine.error = String(error);
        }else{
          try{
            logLine.error = JSON.stringify(error);
          }catch(e){
            logLine.error = String(error);
          }
        }
      }

      api.log(['[ action @ %s ]', self.connection.type], logLevel, logLine);
    };

    api.actionProcessor.prototype.preProcessAction = function(callback){
      var self = this;
      var processors     = [];
      var processorNames = api.actions.globalMiddleware.slice(0);

      if(self.actionTemplate.middleware){
        self.actionTemplate.middleware.forEach(function(m){ processorNames.push(m); });
      }

      processorNames.forEach(function(name){
        if(typeof api.actions.middleware[name].preProcessor === 'function'){
          processors.push(function(next){ api.actions.middleware[name].preProcessor(self, next); });
        }
      });

      async.series(processors, callback);
    };

    api.actionProcessor.prototype.postProcessAction = function(callback){
      var self = this;
      var processors     = [];
      var processorNames = api.actions.globalMiddleware.slice(0);

      if(self.actionTemplate.middleware){
        self.actionTemplate.middleware.forEach(function(m){ processorNames.push(m); });
      }

      processorNames.forEach(function(name){
        if(typeof api.actions.middleware[name].postProcessor === 'function'){
          processors.push(function(next){ api.actions.middleware[name].postProcessor(self, next); });
        }
      });

      async.series(processors, callback);
    };

    api.actionProcessor.prototype.reduceParams = function(){
      var self = this;
      var inputNames = [];
      if(self.actionTemplate.inputs){
        inputNames = Object.keys(self.actionTemplate.inputs);
      }

      if(api.config.general.disableParamScrubbing !== true){
        for(var p in self.params){
          if(api.params.globalSafeParams.indexOf(p) < 0 && inputNames.indexOf(p) < 0){
            delete self.params[p];
          }
        }
      }
    };

    api.actionProcessor.prototype.validateParams = function(){
      var self = this;

      for(var key in self.actionTemplate.inputs){
        var props = self.actionTemplate.inputs[key];

        // default
        if(self.params[key] === undefined && props['default'] !== undefined){
          if(typeof props['default'] === 'function'){
            self.params[key] = props['default'].call(api, self.params[key], self);
          }else{
            self.params[key] = props['default'];
          }
        }

        // formatter
        if(self.params[key] !== undefined && props.formatter !== undefined){
          if(!Array.isArray(props.formatter)){ props.formatter = [props.formatter]; }

          props.formatter.forEach(function(formatter){
            if(typeof formatter === 'function'){
              self.params[key] = formatter.call(api, self.params[key], self);
            }else{
              var method = api.utils.stringToHash(formatter);
              self.params[key] = method.call(api, self.params[key], self);
            }
          });
        }

        // validator
        if(self.params[key] !== undefined && props.validator !== undefined){
          if(!Array.isArray(props.validator)){ props.validator = [props.validator]; }

          props.validator.forEach(function(validator){
            var validatorResponse;
            if(typeof validator === 'function'){
              validatorResponse = validator.call(api, self.params[key], self);
            }else{
              var method = api.utils.stringToHash(validator);
              validatorResponse = method.call(api, self.params[key], self);
            }
            if(validatorResponse !== true){ self.validatorErrors.push(validatorResponse); }
          });
        }

        // required
        if(props.required === true){
          if(api.config.general.missingParamChecks.indexOf(self.params[key]) >= 0){
            self.missingParams.push(key);
          }
        }
      }
    };

    api.actionProcessor.prototype.processAction = function(){
      var self = this;
      self.actionStartTime = new Date().getTime();
      self.working = true;
      self.incrementTotalActions();
      self.incrementPendingActions();
      self.action = self.params.action;

      if(api.actions.versions[self.action]){
        if(!self.params.apiVersion){
          self.params.apiVersion = api.actions.versions[self.action][api.actions.versions[self.action].length - 1];
        }
        self.actionTemplate = api.actions.actions[self.action][self.params.apiVersion];
      }

      if(api.running !== true){
        self.completeAction('server_shutting_down');
      }else if(self.getPendingActionCount(self.connection) > api.config.general.simultaneousActions){
        self.completeAction('too_many_requests');
      }else if(!self.action || !self.actionTemplate){
        self.completeAction('unknown_action');
      }else if(self.actionTemplate.blockedConnectionTypes && self.actionTemplate.blockedConnectionTypes.indexOf(self.connection.type) >= 0){
        self.completeAction('unsupported_server_type');
      }else{
        self.runAction();
      }
    };

    api.actionProcessor.prototype.runAction = function(){
      var self = this;

      self.preProcessAction(function(error){
        self.reduceParams();
        self.validateParams();

        if(error){
          self.completeAction(error);
        }else if(self.missingParams.length > 0){
          self.completeAction('missing_params');
        }else if(self.validatorErrors.length > 0){
          self.completeAction('validator_errors');
        }else if(self.toProcess === true && !error){
          self.actionTemplate.run(api, self, function(error){
            if(error){
              self.completeAction(error);
            }else{
              self.postProcessAction(function(error){
                self.completeAction(error);
              });
            }
          });
        }else{
          self.completeAction();
        }
      });
    };

    next();
  }
};
<|endoftext|>


<commit_before>import url         from 'url';
import fs          from 'fs';
import yaml        from 'js-yaml';
import querystring from 'querystring';
import path        from 'path';
import request     from 'superagent';
import SendMessage from './SendMessage';
import Auth        from './Auth';

/**
 * Do the job selected by the branch of git on jenkins.
 *
 */

// the type of irc service.
const type       = process.env.HUBOT_IRC_TYPE;

// the configuration file for me.
const configFile = process.env.JENKINS_JOBSELECTOR_CONFIG_FILE;

// the flag whether tell message to irc service.
const sendFlag   = process.env.JENKINS_JOBSELECTOR_SEND_MESSAGE;
const icon       = process.env.JENKINS_JOBSELECTOR_ICON   || null;
const color      = process.env.JENKINS_JOBSELECTOR_COLOR  || '#aaaaaa';
const debug      = process.env.JENKINS_JOBSELECTOR_DEBUG  || process.env.DEBUG || null;
const allow      = process.env.JENKINS_JOBSELECTOR_ALLOW  || '';
const deny       = process.env.JENKINS_JOBSELECTOR_DENY   || '';
const apikey     = process.env.JENKINS_JOBSELECTOR_APIKEY || '';
const ircType    = process.env.HUBOT_IRC_TYPE;
const urlpath    = '/hubot/jenkins-jobselector';
const name       = 'JobSelector';

module.exports = (robot) => {
  const sm   = new SendMessage(robot, type);
  const log  = sm.robot;

  if (!configFile) {
    log.debug(`${name}> no config file.`);
    return;
  }

  const conf = yaml.safeLoad(fs.readFileSync(configFile));
  let service;
  let gitUrl;
  let auth;

  robot.router.post(urlpath, (req, res) => {
    const auth  = new Auth(req, allow, deny, apikey);

    if (!auth.checkRequest(res)) {
      console.log(`not allowed: ${auth.remoteIp}`);
      return;
    }

    log.debug(`data: ${JSON.stringify(req.body)}`);
    const query = querystring.parse(url.parse(req.url).query);
    res.end('OK');

    if (!req.body) {
      throw new Error(`${name}> no body: ${JSON.stringify(req)}`);
    }

    const body = req.body;
    let payload;
    if (body.payload) {
       payload = JSON.parse(body.payload);
    }

    // gitlab
    if (body.repository && body.repository.homepage) {
      gitUrl = body.repository.homepage;
      service = 'gitlab';
    }
    log.debug(`payload.repository: ${JSON.stringify(payload.repository)}`);

    // github
    if (payload && payload.repository && payload.repository.url) {
      log.debug(`github pattern 1.`);
      gitUrl = payload.repository.url;
      service = 'github';
    }
    if (body.repository && body.repository.html_url) {
      log.debug(`github pattern 2.`);
      gitUrl = body.repository.html_url;
      service = 'github';
    }

    if (!gitUrl) {
      throw new Error(`${name}> Unknown git repository.`);
    }

    log.debug(`${name}> git url: ${gitUrl}`);

//    if (!(body.ref || payload.ref)) {
//      return;
//    }

    let branch = '';
    if (service == 'github') {
      if (payload) {
        branch = payload.ref.replace(/refs\/heads\//, '');
      } else {
        branch = body.ref.replace(/refs\/heads\//, '');
      }
    } else {
        branch = body.ref.replace(/refs\/heads\//, '');
    }

    const gitInfo = conf[gitUrl];
    let authInfo;

    if (gitInfo['auth']) {
      authInfo = {};
      authInfo.user = gitInfo['auth']['id'];
      authInfo.pass = gitInfo['auth']['password'];
    } else {
      log.debug(`${name}> No auth infomation.`);
    }

    const jobUrl = gitInfo['jobs'][branch];
    const jobRequest = (url) => {

      if (authInfo) {
        request
          .post(url)
          .auth(authInfo.user, authInfo.pass)
          .end((err, res) => {
            if (err) {
              log.error(`${name}> request error: ${err}`);
              return;
            }
          });
      } else {
        request
          .post(url)
          .end((err, res) => {
            if (err) {
              log.error(`${name}> request error: ${err}`);
              return;
            }
          });
      }

      const info = { color: color };

      if (icon) {
        info.icon_url = icon;
      }

      sendFlag && sm.send(gitInfo['target'], `[Jenkins]: The job has started on ${sm.bold(branch)} branch at ${gitUrl}.`, info, (err, res) => {
        if (err) {
          log.error(`${name}> send error: ${err}.`);
        } else {
          log.debug(`${name}> send: ${res}`);
        }
      });
    };

    if (sm.robot.checkType('Array', jobUrl)) {
      jobUrl.map((v, i) => jobRequest(v));
    } else if (sm.robot.checkType('String', jobUrl)) {
      jobRequest(jobUrl);
    } else {
      log.debug(`${name}> no jobUrl for ${branch}`);
    }

  });
};
<commit_msg>fix jenkins.<commit_after>import url         from 'url';
import fs          from 'fs';
import yaml        from 'js-yaml';
import querystring from 'querystring';
import path        from 'path';
import request     from 'superagent';
import SendMessage from './SendMessage';
import Auth        from './Auth';

/**
 * Do the job selected by the branch of git on jenkins.
 *
 */

// the type of irc service.
const type       = process.env.HUBOT_IRC_TYPE;

// the configuration file for me.
const configFile = process.env.JENKINS_JOBSELECTOR_CONFIG_FILE;

// the flag whether tell message to irc service.
const sendFlag   = process.env.JENKINS_JOBSELECTOR_SEND_MESSAGE;
const icon       = process.env.JENKINS_JOBSELECTOR_ICON   || null;
const color      = process.env.JENKINS_JOBSELECTOR_COLOR  || '#aaaaaa';
const debug      = process.env.JENKINS_JOBSELECTOR_DEBUG  || process.env.DEBUG || null;
const allow      = process.env.JENKINS_JOBSELECTOR_ALLOW  || '';
const deny       = process.env.JENKINS_JOBSELECTOR_DENY   || '';
const apikey     = process.env.JENKINS_JOBSELECTOR_APIKEY || '';
const ircType    = process.env.HUBOT_IRC_TYPE;
const urlpath    = '/hubot/jenkins-jobselector';
const name       = 'JobSelector';

module.exports = (robot) => {
  const sm   = new SendMessage(robot, type);
  const log  = sm.robot;

  if (!configFile) {
    log.debug(`${name}> no config file.`);
    return;
  }

  const conf = yaml.safeLoad(fs.readFileSync(configFile));
  let service;
  let gitUrl;
  let auth;

  robot.router.post(urlpath, (req, res) => {
    const auth  = new Auth(req, allow, deny, apikey);

    if (!auth.checkRequest(res)) {
      console.log(`not allowed: ${auth.remoteIp}`);
      return;
    }

    log.debug(`data: ${JSON.stringify(req.body)}`);
    const query = querystring.parse(url.parse(req.url).query);
    res.end('OK');

    if (!req.body) {
      throw new Error(`${name}> no body: ${JSON.stringify(req)}`);
    }

    const body = req.body;
    let payload;
    if (body.payload) {
       payload = JSON.parse(body.payload);
    }

    // gitlab
    if (body.repository && body.repository.homepage) {
      gitUrl = body.repository.homepage;
      service = 'gitlab';
    }

    // github
    if (payload && payload.repository && payload.repository.url) {
      log.debug(`payload.repository: ${JSON.stringify(payload.repository)}`);
      log.debug(`github pattern 1.`);
      gitUrl = payload.repository.url;
      service = 'github';
    }
    if (body.repository && body.repository.html_url) {
      log.debug(`github pattern 2.`);
      gitUrl = body.repository.html_url;
      service = 'github';
    }

    if (!gitUrl) {
      throw new Error(`${name}> Unknown git repository.`);
    }

    log.debug(`${name}> git url: ${gitUrl}`);

//    if (!(body.ref || payload.ref)) {
//      return;
//    }

    let branch = '';
    if (service == 'github') {
      if (payload) {
        branch = payload.ref.replace(/refs\/heads\//, '');
      } else {
        branch = body.ref.replace(/refs\/heads\//, '');
      }
    } else {
        branch = body.ref.replace(/refs\/heads\//, '');
    }

    const gitInfo = conf[gitUrl];
    let authInfo;

    if (gitInfo['auth']) {
      authInfo = {};
      authInfo.user = gitInfo['auth']['id'];
      authInfo.pass = gitInfo['auth']['password'];
    } else {
      log.debug(`${name}> No auth infomation.`);
    }

    const jobUrl = gitInfo['jobs'][branch];
    const jobRequest = (url) => {

      if (authInfo) {
        request
          .post(url)
          .auth(authInfo.user, authInfo.pass)
          .end((err, res) => {
            if (err) {
              log.error(`${name}> request error: ${err}`);
              return;
            }
          });
      } else {
        request
          .post(url)
          .end((err, res) => {
            if (err) {
              log.error(`${name}> request error: ${err}`);
              return;
            }
          });
      }

      const info = { color: color };

      if (icon) {
        info.icon_url = icon;
      }

      sendFlag && sm.send(gitInfo['target'], `[Jenkins]: The job has started on ${sm.bold(branch)} branch at ${gitUrl}.`, info, (err, res) => {
        if (err) {
          log.error(`${name}> send error: ${err}.`);
        } else {
          log.debug(`${name}> send: ${res}`);
        }
      });
    };

    if (sm.robot.checkType('Array', jobUrl)) {
      jobUrl.map((v, i) => jobRequest(v));
    } else if (sm.robot.checkType('String', jobUrl)) {
      jobRequest(jobUrl);
    } else {
      log.debug(`${name}> no jobUrl for ${branch}`);
    }

  });
};
<|endoftext|>