<commit_before>/**
 * @author Michael Ziminsky <mgziminsky@gmail.com>
 * @version 0.1.0
 * @license MIT
 * @source https://github.com/mgziminsky/jquery-enhanced-select
 *
 * Based on multiple-select plugin by Zhixin Wen v1.1.0
 * http://wenzhixin.net.cn/p/multiple-select/
 *
 * Uses much of the original code as a base, with some fundamental changes:
 *   - Implemented as jQuery UI widget rather than raw jQuery plugin
 *   - Widget behaves as a proxy to original input, it doesn't create a separate form submission value
 *   - Some functionality that either no longer applied, or I didn't see as useful, has been removed
 */

(function($) {

    'use strict';

    $.widget('mz.enhancedSelect', {
        version: '0.1.0',
        options: {
            keepOpen: false,
            placeholder: null,
            selectAll: true,
            selectAllText: '[Select All]',
            selectAllValue: undefined,
            allSelectedText: 'All selected',
            delimiter: ', ',
            displayValues: false,
            minimumCountSelected: 3,
            countSelectedText: '# of % selected',
            noMatchesText: 'No matches found',
            multiple: undefined,
            stacked: false,
            stackedWidth: 80,
            filter: true,
            width: undefined,
            maxHeight: 250,
            container: undefined,
            position: 'bottom',

            styler: function(value) {
                return false;
            }
        },

        _init: function() {
            if (this.options.multiple === undefined) {
                this.options.multiple = this.element.prop('multiple');
            } else {
                this.element.prop('multiple', this.options.multiple);
            }
            if (this.element.prop('disabled')) {
                this.disable();
            } else {
                this.enable();
            }

            if (this.isCreated) {
                this.refresh();
            }
        },

        _create: function() {
            this.element.hide();

            this.$parent = $('

');
            this.$placeholder = $('');
            this.$choice = $('<button type="button" class="ms-choice">

</button>').prepend(this.$placeholder).appendTo(this.$parent);
            this.$drop = $('

').appendTo(this.$parent);
            this.$opts = $('

');

            this.$filter = $('

');
            this.$searchInput = $('<input type="text" autocomplete="off" autocorrect="off" autocapitilize="off" spellcheck="false">').appendTo(this.$filter);
            this.$selectAll = $('<input type="checkbox" />');

            this.$parent.insertAfter(this.element);

            this._createEvents();
            this.refresh();

            this.isCreated = true;
        },

        _destroy: function() {
            this.$parent.remove();
            this.isCreated = false;
            this.element.show();
        },

        _createEvents: function() {
            var self = this;

            function toggleOpen(e) {
                e.preventDefault();
                self[self.isOpen ? 'close' : 'open']();
            }

            var label = this.element.closest('label')[0] || $('label[for=' + this.element.attr('id') + ']')[0];
            if (label) {
                this._on(label, {
                    click: function(e) {
                        toggleOpen(e);
                        if (!(this.options.filter || this.isOpen)) {
                            this.focus();
                        }
                        e.stopPropagation(); // Causes lost focus otherwise
                    }
                });
            }

            this._on(this.$choice, {
                click: toggleOpen
            });

            this._on(this.$parent, {
                keydown: function(e) {
                    switch (e.which) {
                        case 27: // esc key
                            self.close();
                            self.$choice.focus();
                            break;
                        case 13: // enter
                            if (this.options.multiple) {
                                e.preventDefault();
                                this.$selectAll.click();
                            }
                            break;
                    }
                }
            });

            this._on(this.$searchInput, {
                keydown: function(e) {
                    if (e.keyCode === 9 && e.shiftKey) { // Ensure shift-tab causes lost focus from filter as with clicking away
                        this.close();
                    }
                },
                keyup: function(e) {
                    this.filter(this.$searchInput.val().trim());
                }
            });
        },

        _optionToListItem: function(elm, group, groupDisabled) {
            var $elm = $(elm),
                disabled,
                type = this.options.multiple ? 'checkbox' : 'radio';

            var item = $('

'),
                label = $('').appendTo(item),
                input = $('<input />').attr('type', type).appendTo(label);

            if ($elm.is('option')) {
                var value = $elm.val();
                disabled = groupDisabled || $elm.prop('disabled');

                item.css(this.options.styler(value) || {});
                label.append($elm.text());
                input.addClass('es-item')
                    .attr('value', value).attr('data-group', group)
                    .prop('checked', $elm.prop('selected')).prop('disabled', disabled);

                if (this.options.stacked) {
                    item.addClass('stacked');
                }
            } else if (!group && $elm.is('optgroup')) {
                var _group = $elm.attr('label');
                disabled = $elm.prop('disabled');

                item.addClass('group');
                label.addClass('optgroup').attr('data-group', _group).append(_group);
                if (this.options.multiple) {
                    input.addClass('es-group').prop('disabled', disabled);
                } else {
                    input.remove(); // No group selector for single selects
                }

                var self = this;
                $elm.children().each(function(i, elm) {
                    item = item.add(self._optionToListItem(elm, _group, disabled));
                });
            }

            if (disabled) {
                label.addClass('disabled');
            }

            return item;
        },

        _getSelects: function() {
            var self = this,
                texts = [],
                values = [];

            var items = this.$allItems;

            items.filter(':checked').each(function() {
                texts.push($(this).parent().text());
                values.push(this.value);
            });

            if (this.options.multiple && this.$selectGroups.length) {
                texts = [];
                this.$selectGroups.each(function() {
                    var html = [],
                        text = $(this).text().trim(),
                        group = $(this).data('group'),
                        $children = items.filter('[data-group=' + group + ']'),
                        $selected = $children.filter(':checked');

                    if ($selected.length === 0) {
                        return;
                    }

                    html.push('[');
                    html.push(text);
                    if ($children.length > $selected.length) {
                        var list = [];
                        $selected.each(function() {
                            list.push($(this).parent().text());
                        });
                        html.push(': ' + list.join(self.options.delimiter));
                    }
                    html.push(']');
                    texts.push(html.join(''));
                });
            }

            return {
                texts: texts,
                values: values,
                count: values.length
            };
        },

        _updateHelperChecks: function() {
            var items = this.$enabledItems;
            if (this.isOpen) {
                items = items.filter(':visible');
            }
            var checked = items.filter(':checked');

            if (items.length === 0) {
                return;
            } else if (items.length === checked.length) {
                this.$selectAll.prop('checked', true);
                $('.es-group', this.$selectGroups).prop('checked', true);
            } else if (this.$selectGroups.length) {
                this.$selectAll.prop('checked', false);
                this.$selectGroups.filter(':visible').each(function() {
                    var grp = '[data-group=' + $(this).text() + ']';
                    $('.es-group', this).prop('checked', items.filter(grp).length === checked.filter(grp).length);
                });
            }
        },

        _update: function(skipTrigger) {
            this.$placeholder.removeClass('placeholder');
            this._updateHelperChecks();
            var selects = this._getSelects(),
                allSelected = (selects.count === this.$allItems.length),
                display = (this.options.displayValues ? selects.values : selects.texts);

            if (selects.count === 0) {
                this.$placeholder.addClass('placeholder').text(this.options.placeholder);
            } else if (this.options.allSelectedText && allSelected) {
                this.$placeholder.text(this.options.allSelectedText);
            } else if (this.options.countSelectedText && selects.count < this.options.minimumCountSelected) {
                this.$placeholder.text(display.join(this.options.delimiter));
            } else if ((this.options.countSelectedText || this.options.etcaetera) && selects.count > this.options.minimumCountSelected) {
                if (this.options.etcaetera) {
                    this.$placeholder.text(display.slice(0, this.options.minimumCountSelected).join(this.options.delimiter) + '...');
                } else {
                    this.$placeholder.text(this.options.countSelectedText
                        .replace('#', selects.count)
                        .replace('%', this.$allItems.length));
                }
            } else {
                this.$placeholder.text(display.join(this.options.delimiter));
            }

            // add selected class to selected li
            this.$opts.find('li').removeClass('selected');
            this.$opts.find(':input:checked').closest('li').addClass('selected');

            this.element.val((allSelected && this.options.selectAllValue) ? null : selects.values);
            if (!skipTrigger) {
                this._trigger('change');
            }
        },

        _updateChecks: function(check, ignoreFilter, group) {
            var items = this.$enabledItems;
            if (!ignoreFilter) {
                items = items.filter(':visible');
            }
            if (group) {
                items = items.filter('[data-group=' + group + ']');
            }
            items.prop('checked', check);
            this._update();
        },

        // Public Methods

        refresh: function() {
            var self = this;

            this.$drop.attr('class', 'ms-drop').addClass(this.options.position);
            this.$parent.css('width', this.options.width);
            this.$parent.css('min-width', this.element.outerWidth());

            this.$drop.children().detach();

            if (this.isCreated) {
                this._off(this.$selectGroups, 'change');
                this._off(this.$enabledItems, 'change');
                this._off(this.$selectAll, 'change');
                this._off($(document), 'click');
            }

            if (this.options.filter) {
                this.$filter.prependTo(this.$drop);
            }

            this.$opts.empty();
            this.element.children().each(function(i, e) {
                self._optionToListItem(e).appendTo(self.$opts);
            });
            this.$noResults = $('

').text(this.options.noMatchesText);
            this.$opts.append(this.$noResults);
            this.$opts.css('max-height', this.options.maxHeight + 'px');

            this.submitAllValue = false;
            if (this.options.selectAll && this.options.multiple) {
                if (this.options.selectAllValue !== undefined) {
                    this.$selectAll.attr('value', this.options.selectAllValue).attr('name', this.element.attr('name'));
                    this.submitAllValue = true;
                } else {
                    this.$selectAll.removeAttr('value').removeAttr('name');
                }

                var label = $('').text(this.options.selectAllText).prepend(this.$selectAll);
                this.$opts.prepend($('

').append(label));

                // Why doesn't this work when defined in _createEvents???
                this._on(this.$selectAll, {
                    change: function(e) {
                        var checked = $(e.target).is(':checked');
                        if (checked) {
                            this.selectAll();
                        } else {
                            this.unselectAll();
                        }
                    }
                });
            }

            this.$opts.find('.multiple').css('width', this.options.stackedWidth);

            this.$selectGroups = this.$opts.find('label.optgroup');
            this.$allItems = this.$opts.find(':input.es-item');
            this.$enabledItems = this.$allItems.filter(':enabled');
            this.$disabledItems = this.$allItems.filter(':disabled');

            this.$opts.appendTo(this.$drop);

            this._on(this.$selectGroups, {
                change: function(e) {
                    var target = $(e.target),
                        group = target.parent().data('group');
                    if (target.is(':checked')) {
                        this.selectGroup(group);
                    } else {
                        this.unselectGroup(group);
                    }
                }
            });

            this._on(this.$enabledItems, {
                change: function(e) {
                    if (!this.options.multiple && $(e.target).is(':checked')) {
                        this.$enabledItems.not(e.target).prop('checked', false);
                    }
                    if (this.isOpen && !(this.options.keepOpen || this.options.multiple)) {
                        this.close();
                    }
                    this._update();
                }
            });

            if (!this.options.keepOpen) {
                this._on($(document), {
                    click: function(e) {
                        if (this.isOpen && $(e.target).closest('div.ms-parent')[0] !== this.$parent[0]) {
                            this.close();
                        }
                    }
                });
            }
            this._update(true);
        },

        open: function() {
            this.$choice.find('>div').addClass('open');
            this.$drop.show();

            if (this.element.children().length === 0) {
                this.$selectAll.closest('li.ms-select-all').hide();
                this.$noResults.show();
            } else {
                this.$selectAll.closest('li.ms-select-all').show();
                this.$noResults.hide();
            }

            if (this.options.container) {
                var offset = this.$drop.offset();
                this.$drop.appendTo($(this.options.container));
                this.$drop.offset({
                    top: offset.top,
                    left: offset.left
                });
            }
            if (this.options.filter) {
                this.$searchInput.val('');
                this.$searchInput.focus();
                this.filter();
            }
            this._trigger('open');
            this.isOpen = true;
        },

        close: function() {
            this.$choice.find('>div').removeClass('open');
            this.$drop.hide();
            if (this.options.container) {
                this.$parent.append(this.$drop);
                this.$drop.css({
                    'top': 'auto',
                    'left': 'auto'
                });
            }
            this._trigger('close');
            this.isOpen = false;
        },

        filter: function(searchVal) {
            var self = this;

            if (!searchVal) {
                this.$allItems.parent().show();
                this.$selectGroups.show();
            } else {
                this.$disabledItems.parent().hide();
                this.$enabledItems.each(function() {
                    var $parent = $(this).parent();
                    $parent[$parent.text().toLowerCase().indexOf(searchVal) < 0 ? 'hide' : 'show']();
                });

                this.$selectGroups.each(function() {
                    var group = $(this).data('group'),
                        $items = self.$enabledItems.filter(':visible');
                    $(this)[$items.filter('[data-group=' + group + ']').length ? 'show' : 'hide']();
                });

            }
            //Check if no matches found
            if (this.$enabledItems.filter(':visible').length) {
                this.$selectAll.parent().show();
                this.$noResults.hide();
            } else {
                this.$selectAll.parent().hide();
                this.$noResults.show();
            }
            this._updateHelperChecks();
        },

        selectAll: function(ignoreFilter) {
            this._trigger('selectAll');
            this._updateChecks(true, ignoreFilter);
        },

        unselectAll: function(ignoreFilter) {
            this._trigger('unselectAll');
            this._updateChecks(false, ignoreFilter);
        },

        selectGroup: function(group, ignoreFilter) {
            if (!this.options.multiple) {
                return;
            } // NOOP for single mode
            this._trigger('selectGroup', null, group);
            this._updateChecks(true, ignoreFilter, group);
        },

        unselectGroup: function(group, ignoreFilter) {
            if (!this.options.multiple) {
                return;
            } // NOOP for single mode
            this._trigger('unselectGroup', null, group);
            this._updateChecks(false, ignoreFilter, group);
        }
    });
})(jQuery);<commit_msg>Add search delay option defaulted to 350<commit_after>/**
 * @author Michael Ziminsky <mgziminsky@gmail.com>
 * @version 0.1.0
 * @license MIT
 * @source https://github.com/mgziminsky/jquery-enhanced-select
 *
 * Based on multiple-select plugin by Zhixin Wen v1.1.0
 * http://wenzhixin.net.cn/p/multiple-select/
 *
 * Uses much of the original code as a base, with some fundamental changes:
 *   - Implemented as jQuery UI widget rather than raw jQuery plugin
 *   - Widget behaves as a proxy to original input, it doesn't create a separate form submission value
 *   - Some functionality that either no longer applied, or I didn't see as useful, has been removed
 */

(function($) {

    'use strict';

    $.widget('mz.enhancedSelect', {
        version: '0.1.0',
        options: {
            keepOpen: false,
            placeholder: null,
            selectAll: true,
            selectAllText: '[Select All]',
            selectAllValue: undefined,
            allSelectedText: 'All selected',
            delimiter: ', ',
            displayValues: false,
            minimumCountSelected: 3,
            countSelectedText: '# of % selected',
            noMatchesText: 'No matches found',
            multiple: undefined,
            stacked: false,
            stackedWidth: 80,
            filter: true,
            width: undefined,
            maxHeight: 250,
            container: undefined,
            position: 'bottom',
            searchDelay: 350,

            styler: function(value) {
                return false;
            }
        },

        _init: function() {
            if (this.options.multiple === undefined) {
                this.options.multiple = this.element.prop('multiple');
            } else {
                this.element.prop('multiple', this.options.multiple);
            }
            if (this.element.prop('disabled')) {
                this.disable();
            } else {
                this.enable();
            }

            if (this.isCreated) {
                this.refresh();
            }
        },

        _create: function() {
            this.element.hide();

            this.$parent = $('

');
            this.$placeholder = $('');
            this.$choice = $('<button type="button" class="ms-choice">

</button>').prepend(this.$placeholder).appendTo(this.$parent);
            this.$drop = $('

').appendTo(this.$parent);
            this.$opts = $('

');

            this.$filter = $('

');
            this.$searchInput = $('<input type="text" autocomplete="off" autocorrect="off" autocapitilize="off" spellcheck="false">').appendTo(this.$filter);
            this.$selectAll = $('<input type="checkbox" />');

            this.$parent.insertAfter(this.element);

            this._createEvents();
            this.refresh();

            this.isCreated = true;
        },

        _destroy: function() {
            this.$parent.remove();
            this.isCreated = false;
            this.element.show();
        },

        _createEvents: function() {
            var self = this;

            function toggleOpen(e) {
                e.preventDefault();
                self[self.isOpen ? 'close' : 'open']();
            }

            var label = this.element.closest('label')[0] || $('label[for=' + this.element.attr('id') + ']')[0];
            if (label) {
                this._on(label, {
                    click: function(e) {
                        toggleOpen(e);
                        if (!(this.options.filter || this.isOpen)) {
                            this.focus();
                        }
                        e.stopPropagation(); // Causes lost focus otherwise
                    }
                });
            }

            this._on(this.$choice, {
                click: toggleOpen
            });

            this._on(this.$parent, {
                keydown: function(e) {
                    switch (e.which) {
                        case 27: // esc key
                            self.close();
                            self.$choice.focus();
                            break;
                        case 13: // enter
                            if (this.options.multiple) {
                                e.preventDefault();
                                this.$selectAll.click();
                            }
                            break;
                    }
                }
            });

            var searchTimer;
            this._on(this.$searchInput, {
                keydown: function(e) {
                    if (e.keyCode === 9 && e.shiftKey) { // Ensure shift-tab causes lost focus from filter as with clicking away
                        this.close();
                    }
                },
                keyup: function(e) {
                    if (searchTimer) clearTimeout(searchTimer);
                    searchTimer = setTimeout(function() { this.filter(this.$searchInput.val().trim()); }, this.options.searchDelay);
                }
            });
        },

        _optionToListItem: function(elm, group, groupDisabled) {
            var $elm = $(elm),
                disabled,
                type = this.options.multiple ? 'checkbox' : 'radio';

            var item = $('

'),
                label = $('').appendTo(item),
                input = $('<input />').attr('type', type).appendTo(label);

            if ($elm.is('option')) {
                var value = $elm.val();
                disabled = groupDisabled || $elm.prop('disabled');

                item.css(this.options.styler(value) || {});
                label.append($elm.text());
                input.addClass('es-item')
                    .attr('value', value).attr('data-group', group)
                    .prop('checked', $elm.prop('selected')).prop('disabled', disabled);

                if (this.options.stacked) {
                    item.addClass('stacked');
                }
            } else if (!group && $elm.is('optgroup')) {
                var _group = $elm.attr('label');
                disabled = $elm.prop('disabled');

                item.addClass('group');
                label.addClass('optgroup').attr('data-group', _group).append(_group);
                if (this.options.multiple) {
                    input.addClass('es-group').prop('disabled', disabled);
                } else {
                    input.remove(); // No group selector for single selects
                }

                var self = this;
                $elm.children().each(function(i, elm) {
                    item = item.add(self._optionToListItem(elm, _group, disabled));
                });
            }

            if (disabled) {
                label.addClass('disabled');
            }

            return item;
        },

        _getSelects: function() {
            var self = this,
                texts = [],
                values = [];

            var items = this.$allItems;

            items.filter(':checked').each(function() {
                texts.push($(this).parent().text());
                values.push(this.value);
            });

            if (this.options.multiple && this.$selectGroups.length) {
                texts = [];
                this.$selectGroups.each(function() {
                    var html = [],
                        text = $(this).text().trim(),
                        group = $(this).data('group'),
                        $children = items.filter('[data-group=' + group + ']'),
                        $selected = $children.filter(':checked');

                    if ($selected.length === 0) {
                        return;
                    }

                    html.push('[');
                    html.push(text);
                    if ($children.length > $selected.length) {
                        var list = [];
                        $selected.each(function() {
                            list.push($(this).parent().text());
                        });
                        html.push(': ' + list.join(self.options.delimiter));
                    }
                    html.push(']');
                    texts.push(html.join(''));
                });
            }

            return {
                texts: texts,
                values: values,
                count: values.length
            };
        },

        _updateHelperChecks: function() {
            var items = this.$enabledItems;
            if (this.isOpen) {
                items = items.filter(':visible');
            }
            var checked = items.filter(':checked');

            if (items.length === 0) {
                return;
            } else if (items.length === checked.length) {
                this.$selectAll.prop('checked', true);
                $('.es-group', this.$selectGroups).prop('checked', true);
            } else if (this.$selectGroups.length) {
                this.$selectAll.prop('checked', false);
                this.$selectGroups.filter(':visible').each(function() {
                    var grp = '[data-group=' + $(this).text() + ']';
                    $('.es-group', this).prop('checked', items.filter(grp).length === checked.filter(grp).length);
                });
            }
        },

        _update: function(skipTrigger) {
            this.$placeholder.removeClass('placeholder');
            this._updateHelperChecks();
            var selects = this._getSelects(),
                allSelected = (selects.count === this.$allItems.length),
                display = (this.options.displayValues ? selects.values : selects.texts);

            if (selects.count === 0) {
                this.$placeholder.addClass('placeholder').text(this.options.placeholder);
            } else if (this.options.allSelectedText && allSelected) {
                this.$placeholder.text(this.options.allSelectedText);
            } else if (this.options.countSelectedText && selects.count < this.options.minimumCountSelected) {
                this.$placeholder.text(display.join(this.options.delimiter));
            } else if ((this.options.countSelectedText || this.options.etcaetera) && selects.count > this.options.minimumCountSelected) {
                if (this.options.etcaetera) {
                    this.$placeholder.text(display.slice(0, this.options.minimumCountSelected).join(this.options.delimiter) + '...');
                } else {
                    this.$placeholder.text(this.options.countSelectedText
                        .replace('#', selects.count)
                        .replace('%', this.$allItems.length));
                }
            } else {
                this.$placeholder.text(display.join(this.options.delimiter));
            }

            // add selected class to selected li
            this.$opts.find('li').removeClass('selected');
            this.$opts.find(':input:checked').closest('li').addClass('selected');

            this.element.val((allSelected && this.options.selectAllValue) ? null : selects.values);
            if (!skipTrigger) {
                this._trigger('change');
            }
        },

        _updateChecks: function(check, ignoreFilter, group) {
            var items = this.$enabledItems;
            if (!ignoreFilter) {
                items = items.filter(':visible');
            }
            if (group) {
                items = items.filter('[data-group=' + group + ']');
            }
            items.prop('checked', check);
            this._update();
        },

        // Public Methods

        refresh: function() {
            var self = this;

            this.$drop.attr('class', 'ms-drop').addClass(this.options.position);
            this.$parent.css('width', this.options.width);
            this.$parent.css('min-width', this.element.outerWidth());

            this.$drop.children().detach();

            if (this.isCreated) {
                this._off(this.$selectGroups, 'change');
                this._off(this.$enabledItems, 'change');
                this._off(this.$selectAll, 'change');
                this._off($(document), 'click');
            }

            if (this.options.filter) {
                this.$filter.prependTo(this.$drop);
            }

            this.$opts.empty();
            this.element.children().each(function(i, e) {
                self._optionToListItem(e).appendTo(self.$opts);
            });
            this.$noResults = $('

').text(this.options.noMatchesText);
            this.$opts.append(this.$noResults);
            this.$opts.css('max-height', this.options.maxHeight + 'px');

            if (this.options.selectAll && this.options.multiple) {
                if (this.options.selectAllValue !== undefined) {
                    this.$selectAll.attr('value', this.options.selectAllValue).attr('name', this.element.attr('name'));
                } else {
                    this.$selectAll.removeAttr('value').removeAttr('name');
                }

                var label = $('').text(this.options.selectAllText).prepend(this.$selectAll);
                this.$opts.prepend($('

').append(label));

                // Why doesn't this work when defined in _createEvents???
                this._on(this.$selectAll, {
                    change: function(e) {
                        var checked = $(e.target).is(':checked');
                        if (checked) {
                            this.selectAll();
                        } else {
                            this.unselectAll();
                        }
                    }
                });
            }

            this.$opts.find('.multiple').css('width', this.options.stackedWidth);

            this.$selectGroups = this.$opts.find('label.optgroup');
            this.$allItems = this.$opts.find(':input.es-item');
            this.$enabledItems = this.$allItems.filter(':enabled');
            this.$disabledItems = this.$allItems.filter(':disabled');

            this.$opts.appendTo(this.$drop);

            this._on(this.$selectGroups, {
                change: function(e) {
                    var target = $(e.target),
                        group = target.parent().data('group');
                    if (target.is(':checked')) {
                        this.selectGroup(group);
                    } else {
                        this.unselectGroup(group);
                    }
                }
            });

            this._on(this.$enabledItems, {
                change: function(e) {
                    if (!this.options.multiple && $(e.target).is(':checked')) {
                        this.$enabledItems.not(e.target).prop('checked', false);
                    }
                    if (this.isOpen && !(this.options.keepOpen || this.options.multiple)) {
                        this.close();
                    }
                    this._update();
                }
            });

            if (!this.options.keepOpen) {
                this._on($(document), {
                    click: function(e) {
                        if (this.isOpen && $(e.target).closest('div.ms-parent')[0] !== this.$parent[0]) {
                            this.close();
                        }
                    }
                });
            }
            this._update(true);
        },

        open: function() {
            this.$choice.find('>div').addClass('open');
            this.$drop.show();

            if (this.element.children().length === 0) {
                this.$selectAll.closest('li.ms-select-all').hide();
                this.$noResults.show();
            } else {
                this.$selectAll.closest('li.ms-select-all').show();
                this.$noResults.hide();
            }

            if (this.options.container) {
                var offset = this.$drop.offset();
                this.$drop.appendTo($(this.options.container));
                this.$drop.offset({
                    top: offset.top,
                    left: offset.left
                });
            }
            if (this.options.filter) {
                this.$searchInput.val('');
                this.$searchInput.focus();
                this.filter();
            }
            this._trigger('open');
            this.isOpen = true;
        },

        close: function() {
            this.$choice.find('>div').removeClass('open');
            this.$drop.hide();
            if (this.options.container) {
                this.$parent.append(this.$drop);
                this.$drop.css({
                    'top': 'auto',
                    'left': 'auto'
                });
            }
            this._trigger('close');
            this.isOpen = false;
        },

        filter: function(searchVal) {
            var self = this;

            if (!searchVal) {
                this.$allItems.parent().show();
                this.$selectGroups.show();
            } else {
                this.$disabledItems.parent().hide();
                this.$enabledItems.each(function() {
                    var $parent = $(this).parent();
                    $parent[$parent.text().toLowerCase().indexOf(searchVal) < 0 ? 'hide' : 'show']();
                });

                this.$selectGroups.each(function() {
                    var group = $(this).data('group'),
                        $items = self.$enabledItems.filter(':visible');
                    $(this)[$items.filter('[data-group=' + group + ']').length ? 'show' : 'hide']();
                });

            }
            //Check if no matches found
            if (this.$enabledItems.filter(':visible').length) {
                this.$selectAll.parent().show();
                this.$noResults.hide();
            } else {
                this.$selectAll.parent().hide();
                this.$noResults.show();
            }
            this._updateHelperChecks();
        },

        selectAll: function(ignoreFilter) {
            this._trigger('selectAll');
            this._updateChecks(true, ignoreFilter);
        },

        unselectAll: function(ignoreFilter) {
            this._trigger('unselectAll');
            this._updateChecks(false, ignoreFilter);
        },

        selectGroup: function(group, ignoreFilter) {
            if (!this.options.multiple) {
                return;
            } // NOOP for single mode
            this._trigger('selectGroup', null, group);
            this._updateChecks(true, ignoreFilter, group);
        },

        unselectGroup: function(group, ignoreFilter) {
            if (!this.options.multiple) {
                return;
            } // NOOP for single mode
            this._trigger('unselectGroup', null, group);
            this._updateChecks(false, ignoreFilter, group);
        }
    });
})(jQuery);<|endoftext|>


<commit_before>var Gn={"&":"&","<":"<",">":">",'"':""","'":"'"}

function isNumeric(value) {
  return /^\d+$/.test(value);
}

function parsePath(path) {
  var originalPath = path;
  var steps = [];
  var error = false;
  var firstKey = path.substr(0, path.indexOf('['));
  if (!firstKey.length) {
    error = true;
  } else {
    path = path.substr(path.indexOf('['), path.length);
    steps.push({
      key: firstKey,
      last: !path.length,
      type:'object'
    });
  }

  var key;
  key = path.substr(1, path.indexOf(']')-1);

  while (path.length && !error) {
    if (path[0] === '[' && path[1] === ']') {
      steps.push({
        append: true,
        type: 'array'
      });
      path = path.substr(2, path.length);
      error = path.length !== 0;
    } else if (isNumeric(key = path.substr(1, path.indexOf(']')-1))) {
      key = parseInt(key, 10);
      path = path.substr(path.indexOf(']')+1, path.length);
      steps.push({
        key: key,
        type: 'array'
      })
    } else if ((key = path.substr(1, path.indexOf(']')-1)) && key.indexOf('[') === -1) {
      path = path.substr(path.indexOf(']')+1, path.length);
      steps.push({
        key: key,
        type: 'object'
      });
    } else {
      error = true;
    }
  }

  if (error) {
    steps = [{
      key: originalPath,
      last: true,
      type: 'object'
    }];
  } else {
    for (var i = 0; i < steps.length; i++) {
      var step = steps[i];
      var nextStep = steps[i+1];
      if (nextStep) {
        step.nextType = nextStep.type;
      } else {
        step.last = true;
      }
    }
  }

  return steps;
}

function setValue(context, step, currentValue, entryValue, isFile) {
  if (isFile) {
    entryValue = {
      name: 'filename',
      type: 'filetype',
      body: 'filebody'
    }
  }
  if (step.last) {
    if (typeof currentValue === 'undefined') {
      if (step.append) {
        context.push(entryValue);
      } else {
        context[step.key] = entryValue;
      }
    } else if (currentValue.constructor == Array) {
      context[step.key].push(entryValue);
    } else if (currentValue.constructor == Object && !isFile) {
      return setValue(currentValue, {key:'', last:true, type:'object'}, currentValue[''], entryValue, isFile);

    } else {
      context[step.key] = [currentValue, entryValue];
    }
    return context;
  }

  if (typeof currentValue === 'undefined') {
    if (step.nextType === 'array') {
      context[step.key] = [];
    } else {
      context[step.key] = {};
    }
    return context[step.key];
  } else if (currentValue.constructor === Object) {
    return context[step.key];
  } else if (currentValue.constructor === Array) {
    if (step.nextType === 'array') {
      return currentValue;
    } else {
      var object = {};
      currentValue.forEach(function(item, i) {
        if (typeof item !== 'undefined') {
          object[i] = item;
        } else {
          context[step.key] = object;
        }
      });
      return object;
    }
  } else {
    var object = {'': currentValue};
    context[step.key] = object;
    return object;
  }
}

function JSONencode(inputs) {
  var resultingObject = {};

  inputs.forEach(function(input) {
    var isFile = input.value && input.value.body !== undefined;
    var steps = parsePath(input.name);
    var context = resultingObject;
    for (var i = 0; i < steps.length; i++) {
      var step = steps[i];
      var currentValue = context[step.key];
      context = setValue(context, step, currentValue, input.value, isFile);
    }
  });

  return resultingObject;
  var result = JSON.stringify(resultingObject);
  return result;
}

$.fn.JSONencode = function() {
  var e;
  var entries = $(this).map(function() {
    e = $.prop(this, 'elements');
    if (e) {
      return $.makeArray(e);
    } else {
      return this;
    }
  }).filter(function() {
    e = this.type;
    return this.name && !$(this).is(':disabled');
  }).map(function(e, t) {
    var n;
    n = $(this).val();
    if (this.type === 'number') {
      n = parseInt(n, 10);
      return {
        name: t.name,
        value: n
      };
    }
    if (this.type === 'checkbox') {
      n = $(this).is(':checked');
      return {
        name: t.name,
        value: n
      };
    }
    if (n === null) {
      return null;
    } else {
      if ($.isArray(n)) {
        return $.map(n, function(e) {
          return {
            name: t.name,
            value: e.replace(Gn, "\r\n")
          };
        });
      } else {
        return {
          name: t.name,
          value: n.replace(Gn, "\r\n")
        };
      }
    }
  }).get();
  return JSONencode(entries);
};

function JSONformSubmitHandler(e) {
  e.preventDefault();
  $.ajax({
    contentType: 'application/json; charset=utf-8',
    data: JSON.stringify($(this).JSONencode()),
    dataType: 'json',
    type: 'post',
    url: this.action,
    success: function(data, textStatus, request){
      var redirect = request.getResponseHeader('Location') || data.redirect;
      if (redirect) window.location.href = redirect;
    }
  });
};

$.JSONforms = {
  enable: function() {
    $('body').on('submit', 'form[enctype="application/json"]', JSONformSubmitHandler);
  },
  disable: function() {
    $('body').off('submit', 'form[enctype="application/json"]', JSONformSubmitHandler);
  }
}

$(document).ready(function() {
  $.JSONforms.enable();
});
<commit_msg>wrap code in iife and enable strict mode<commit_after>(function() {
  'use strict';

  var Gn={"&":"&","<":"<",">":">",'"':""","'":"'"}

  function isNumeric(value) {
    return /^\d+$/.test(value);
  }

  function parsePath(path) {
    var originalPath = path;
    var steps = [];
    var error = false;
    var firstKey = path.substr(0, path.indexOf('['));
    if (!firstKey.length) {
      error = true;
    } else {
      path = path.substr(path.indexOf('['), path.length);
      steps.push({
        key: firstKey,
        last: !path.length,
        type:'object'
      });
    }

    var key;
    key = path.substr(1, path.indexOf(']')-1);

    while (path.length && !error) {
      if (path[0] === '[' && path[1] === ']') {
        steps.push({
          append: true,
          type: 'array'
        });
        path = path.substr(2, path.length);
        error = path.length !== 0;
      } else if (isNumeric(key = path.substr(1, path.indexOf(']')-1))) {
        key = parseInt(key, 10);
        path = path.substr(path.indexOf(']')+1, path.length);
        steps.push({
          key: key,
          type: 'array'
        })
      } else if ((key = path.substr(1, path.indexOf(']')-1)) && key.indexOf('[') === -1) {
        path = path.substr(path.indexOf(']')+1, path.length);
        steps.push({
          key: key,
          type: 'object'
        });
      } else {
        error = true;
      }
    }

    if (error) {
      steps = [{
        key: originalPath,
        last: true,
        type: 'object'
      }];
    } else {
      for (var i = 0; i < steps.length; i++) {
        var step = steps[i];
        var nextStep = steps[i+1];
        if (nextStep) {
          step.nextType = nextStep.type;
        } else {
          step.last = true;
        }
      }
    }

    return steps;
  }

  function setValue(context, step, currentValue, entryValue, isFile) {
    if (isFile) {
      entryValue = {
        name: 'filename',
        type: 'filetype',
        body: 'filebody'
      }
    }
    if (step.last) {
      if (typeof currentValue === 'undefined') {
        if (step.append) {
          context.push(entryValue);
        } else {
          context[step.key] = entryValue;
        }
      } else if (currentValue.constructor == Array) {
        context[step.key].push(entryValue);
      } else if (currentValue.constructor == Object && !isFile) {
        return setValue(currentValue, {key:'', last:true, type:'object'}, currentValue[''], entryValue, isFile);

      } else {
        context[step.key] = [currentValue, entryValue];
      }
      return context;
    }

    if (typeof currentValue === 'undefined') {
      if (step.nextType === 'array') {
        context[step.key] = [];
      } else {
        context[step.key] = {};
      }
      return context[step.key];
    } else if (currentValue.constructor === Object) {
      return context[step.key];
    } else if (currentValue.constructor === Array) {
      if (step.nextType === 'array') {
        return currentValue;
      } else {
        var object = {};
        currentValue.forEach(function(item, i) {
          if (typeof item !== 'undefined') {
            object[i] = item;
          } else {
            context[step.key] = object;
          }
        });
        return object;
      }
    } else {
      var object = {'': currentValue};
      context[step.key] = object;
      return object;
    }
  }

  function JSONencode(inputs) {
    var resultingObject = {};

    inputs.forEach(function(input) {
      var isFile = input.value && input.value.body !== undefined;
      var steps = parsePath(input.name);
      var context = resultingObject;
      for (var i = 0; i < steps.length; i++) {
        var step = steps[i];
        var currentValue = context[step.key];
        context = setValue(context, step, currentValue, input.value, isFile);
      }
    });

    return resultingObject;
    var result = JSON.stringify(resultingObject);
    return result;
  }

  $.fn.JSONencode = function() {
    var e;
    var entries = $(this).map(function() {
      e = $.prop(this, 'elements');
      if (e) {
        return $.makeArray(e);
      } else {
        return this;
      }
    }).filter(function() {
      e = this.type;
      return this.name && !$(this).is(':disabled');
    }).map(function(e, t) {
      var n;
      n = $(this).val();
      if (this.type === 'number') {
        n = parseInt(n, 10);
        return {
          name: t.name,
          value: n
        };
      }
      if (this.type === 'checkbox') {
        n = $(this).is(':checked');
        return {
          name: t.name,
          value: n
        };
      }
      if (n === null) {
        return null;
      } else {
        if ($.isArray(n)) {
          return $.map(n, function(e) {
            return {
              name: t.name,
              value: e.replace(Gn, "\r\n")
            };
          });
        } else {
          return {
            name: t.name,
            value: n.replace(Gn, "\r\n")
          };
        }
      }
    }).get();
    return JSONencode(entries);
  };

  function JSONformSubmitHandler(e) {
    e.preventDefault();
    $.ajax({
      contentType: 'application/json; charset=utf-8',
      data: JSON.stringify($(this).JSONencode()),
      dataType: 'json',
      type: 'post',
      url: this.action,
      success: function(data, textStatus, request){
        var redirect = request.getResponseHeader('Location') || data.redirect;
        if (redirect) window.location.href = redirect;
      }
    });
  };

  $.JSONforms = {
    enable: function() {
      $('body').on('submit', 'form[enctype="application/json"]', JSONformSubmitHandler);
    },
    disable: function() {
      $('body').off('submit', 'form[enctype="application/json"]', JSONformSubmitHandler);
    }
  }

  $(document).ready(function() {
    $.JSONforms.enable();
  });
})();
<|endoftext|>


<commit_before>var util = require('util')
var crypto = require('crypto')
var EventEmitter = require('events').EventEmitter

// CHILD Testers MUST:
// * NOT emit an error (instead, self.state.fail with the error)
// * NOT throw an exception, under any circumstances
// * Have a timeout, significantly less than the interval if applicable
// * Assert any bad programmatic input. Operation errors must not be checked via assertion!

// TODO: auto set icon based on pass/fail (check/times) if not defined?

// Represents the state of an entity. Can be serialised, so that client can
// render and reference by a UID. Patches can be emitted to keep server/client
// states/views in sync.
var State = function(initial) {
	if (!(this instanceof State)) return new State()
	EventEmitter.call(this)

	if (!arguments[0])
		initial = {}

	console.assert(initial.entity,   'Tester State: entity must be defined')
	console.assert(initial.name,     'Tester State: name must be defined')
	console.assert(initial.category, 'Tester State: category must be defined')

	// Reference object in method context
	var self = this

	var consecutive_failures = 0
	var consecutive_passes   = 0

	// default, for edge detection + filter
	this.fail_threshold = 3
	this.pass_threshold = 3

	// static stuff
	for (var i in initial)
		this[i] = initial[i]

	// DOM ID safe
	// TODO: evaluate using category-name
	var generate_id = function() {
		var hash = crypto.createHash('sha1')
		hash.update(initial.name+initial.category)
		return hash.digest('hex')
	}

	this.id = generate_id()

	// null/false/true, via test
	// undefined is better, as null serialises to false
	this.healthy = undefined

	// don't do this, access attributes directly
	//this.get = function(key) { }

	this.set = function(key,val) {
		if (self[key] == val && key != 'healthy') return
		self[key] = val
		self.emit('set',key,val)
	}

	this.append = function(key,val) {
		if (!val) return
		self[key].append(val)
		self.emit('append',self.id,key,val)
	}

	this.fail = function(err) {
		// patch a remote state
		self.set('healthy',false)

		consecutive_failures++

		// error object, probably with err.message
		if (typeof err == 'object') {
			if (err.message)
				self.set('error',err.message)
			else
				self.set('error',err.toString())
		} else if (arguments[0])
			self.set('error',err)

		if (consecutive_failures == 1)
			self.lastfailedge = new Date()
		else if (consecutive_failures == self.fail_threshold
				&& consecutive_passes > self.pass_threshold) {
			// failed after passing for a while
			pushEvent()
			consecutive_passes = 0
		}

	}

	this.pass = function() {
		// patch a remote state
		self.set('healthy',true)

		consecutive_passes++

		if (consecutive_passes == 1)
			self.lastpassedge = new Date()
		else if (consecutive_passes == self.pass_threshold
				&& consecutive_failures > self.fail_threshold) {
			// passed after failing for a while
			pushEvent()
			consecutive_failures = 0
		}

	}

	var pushEvent = function() {
		// frozen-in-time clone
		var snapshot = JSON.parse(JSON.stringify(self))
		// STRUCTURED LOG EVENT
		//
		// Events are distinct from a state update and are suitable to record in a log
		// or push notification events happen when something has broken or fixed
		// (defined as n consecutive failures or passes)
		// Events are backdated to when the first fail/pass happened in the series.
		//
		// Format: dict/hash of key:vals
		// Must contain 'class' which defines the programatic type of event
		// Must contain 'message' which describes to a human what happened to what
		// Must contain 'date' which is a ISO8601 date (ends with Z) of
		// when the event happened, not when the event was posted.
		// Can contain any other key:val associated to the 'class'. For
		// example, any associated data such as username.
		//
		// If the event contains sensitive/excessive info, everything but the
		// date, class and message can be stripped to display a log.
		//
		// Severity/level is associated with the class, and is
		// application specific. If the log event is to be used in
		// global context, it must be associated with a 'level' of
		// RFC5424. This can be achieved with a simple mapping of class
		// to level.
		//
		// Example classes: fixed, broken, heartbeat, boot, halt
		//
		// The events can be distributed by push publishers, eg Amazon SNS, DSPA.
		e = {
			date : snapshot.healthy? snapshot.lastpassedge : snapshot.lastfailedge,
			name : snapshot.name,
			message : snapshot.healthy? 'System healthy' : snapshot.error,
			category : snapshot.category,
			class : snapshot.healthy? 'fixed':'broken',
			tester_id : self.id,
		}
		self.emit('event',e)
	}

	// I don't want these attributes (from eventemitter) when serialising.
	// I hope this won't break anything. It does not appear to.
	delete this._maxListeners
	delete this.domain
	//delete this._events (_events comes back like a zombie)
}

// Inheritance
util.inherits(State, EventEmitter)

// base class for an entity (may be generated by an enumerator)
// defines interface to tests, attributes etc
var Tester = function(config) {
	if (!(this instanceof Tester)) return new Tester()

	// parent constructor
	EventEmitter.call(this)

	// reference instance in method context
	var self = this

	this.state = new State(config)
	this.id = this.state.id

	// css class/controller
	// default -- simple or base testers will have similar class names
	// config should specify entity, which should choose class
	// Best to set it explicitly anyway
	this.state.class = this.state.entity

	// instance of a test module, for example, HTTP status, ping, mail, DNS
	// must just cause state changes. Can change stat of .error
	// no parameters or callback required, even on error. Update the state instead.
	this.test = function(){}

	// default kind-of-sensible polling interval
	this.interval = 30
	this.timeout  = this.interval/2

	// enumerator/rpc
	//this.update

	// setInterval number
	this.timer = null

	// number of checks this tester makes
	this.assertions = 0
}

// Inheritance
util.inherits(Tester, EventEmitter)
// Exposure
module.exports = Tester

// test
//var foo = new Tester({class:'bar',name:'john',category:'beans'})
//
//
// SEPARATE ENTITY AND STATE TBC
<commit_msg>fix test for fail/pass threshold<commit_after>var util = require('util')
var crypto = require('crypto')
var EventEmitter = require('events').EventEmitter

// CHILD Testers MUST:
// * NOT emit an error (instead, self.state.fail with the error)
// * NOT throw an exception, under any circumstances
// * Have a timeout, significantly less than the interval if applicable
// * Assert any bad programmatic input. Operation errors must not be checked via assertion!

// TODO: auto set icon based on pass/fail (check/times) if not defined?

// Represents the state of an entity. Can be serialised, so that client can
// render and reference by a UID. Patches can be emitted to keep server/client
// states/views in sync.
var State = function(initial) {
	if (!(this instanceof State)) return new State()
	EventEmitter.call(this)

	if (!arguments[0])
		initial = {}

	console.assert(initial.entity,   'Tester State: entity must be defined')
	console.assert(initial.name,     'Tester State: name must be defined')
	console.assert(initial.category, 'Tester State: category must be defined')

	// Reference object in method context
	var self = this

	var consecutive_failures = 0
	var consecutive_passes   = 0

	// default, for edge detection + filter
	this.fail_threshold = 3
	this.pass_threshold = 3

	// static stuff
	for (var i in initial)
		this[i] = initial[i]

	// DOM ID safe
	// TODO: evaluate using category-name
	var generate_id = function() {
		var hash = crypto.createHash('sha1')
		hash.update(initial.name+initial.category)
		return hash.digest('hex')
	}

	this.id = generate_id()

	// null/false/true, via test
	// undefined is better, as null serialises to false
	this.healthy = undefined

	// don't do this, access attributes directly
	//this.get = function(key) { }

	this.set = function(key,val) {
		if (self[key] == val && key != 'healthy') return
		self[key] = val
		self.emit('set',key,val)
	}

	this.append = function(key,val) {
		if (!val) return
		self[key].append(val)
		self.emit('append',self.id,key,val)
	}

	this.fail = function(err) {
		// patch a remote state
		self.set('healthy',false)

		consecutive_failures++

		// error object, probably with err.message
		if (typeof err == 'object') {
			if (err.message)
				self.set('error',err.message)
			else
				self.set('error',err.toString())
		} else if (arguments[0])
			self.set('error',err)

		if (consecutive_failures == 1)
			self.lastfailedge = new Date()
		else if (consecutive_failures == self.fail_threshold
				&& consecutive_passes >= self.pass_threshold) {
			// failed after passing for a while
			pushEvent()
			consecutive_passes = 0
		}

	}

	this.pass = function() {
		// patch a remote state
		self.set('healthy',true)

		consecutive_passes++

		if (consecutive_passes == 1)
			self.lastpassedge = new Date()
		else if (consecutive_passes == self.pass_threshold
				&& consecutive_failures >= self.fail_threshold) {
			// passed after failing for a while
			pushEvent()
			consecutive_failures = 0
		}

	}

	var pushEvent = function() {
		// frozen-in-time clone
		var snapshot = JSON.parse(JSON.stringify(self))
		// STRUCTURED LOG EVENT
		//
		// Events are distinct from a state update and are suitable to record in a log
		// or push notification events happen when something has broken or fixed
		// (defined as n consecutive failures or passes)
		// Events are backdated to when the first fail/pass happened in the series.
		//
		// Format: dict/hash of key:vals
		// Must contain 'class' which defines the programatic type of event
		// Must contain 'message' which describes to a human what happened to what
		// Must contain 'date' which is a ISO8601 date (ends with Z) of
		// when the event happened, not when the event was posted.
		// Can contain any other key:val associated to the 'class'. For
		// example, any associated data such as username.
		//
		// If the event contains sensitive/excessive info, everything but the
		// date, class and message can be stripped to display a log.
		//
		// Severity/level is associated with the class, and is
		// application specific. If the log event is to be used in
		// global context, it must be associated with a 'level' of
		// RFC5424. This can be achieved with a simple mapping of class
		// to level.
		//
		// Example classes: fixed, broken, heartbeat, boot, halt
		//
		// The events can be distributed by push publishers, eg Amazon SNS, DSPA.
		e = {
			date : snapshot.healthy? snapshot.lastpassedge : snapshot.lastfailedge,
			name : snapshot.name,
			message : snapshot.healthy? 'System healthy' : snapshot.error,
			category : snapshot.category,
			class : snapshot.healthy? 'fixed':'broken',
			tester_id : self.id,
		}
		self.emit('event',e)
	}

	// I don't want these attributes (from eventemitter) when serialising.
	// I hope this won't break anything. It does not appear to.
	delete this._maxListeners
	delete this.domain
	//delete this._events (_events comes back like a zombie)
}

// Inheritance
util.inherits(State, EventEmitter)

// base class for an entity (may be generated by an enumerator)
// defines interface to tests, attributes etc
var Tester = function(config) {
	if (!(this instanceof Tester)) return new Tester()

	// parent constructor
	EventEmitter.call(this)

	// reference instance in method context
	var self = this

	this.state = new State(config)
	this.id = this.state.id

	// css class/controller
	// default -- simple or base testers will have similar class names
	// config should specify entity, which should choose class
	// Best to set it explicitly anyway
	this.state.class = this.state.entity

	// instance of a test module, for example, HTTP status, ping, mail, DNS
	// must just cause state changes. Can change stat of .error
	// no parameters or callback required, even on error. Update the state instead.
	this.test = function(){}

	// default kind-of-sensible polling interval
	this.interval = 30
	this.timeout  = this.interval/2

	// enumerator/rpc
	//this.update

	// setInterval number
	this.timer = null

	// number of checks this tester makes
	this.assertions = 0
}

// Inheritance
util.inherits(Tester, EventEmitter)
// Exposure
module.exports = Tester

// test
//var foo = new Tester({class:'bar',name:'john',category:'beans'})
//
//
// SEPARATE ENTITY AND STATE TBC
<|endoftext|>


<commit_before>(function() {
  'use strict';

  var shell = require('shelljs');
  var venvPath = process.env.npm_package_config_virtualenvs_path;

  shell.exec(__dirname + '/dev-config.sh ' + venvPath);
})();
<commit_msg>Fixed path.<commit_after>(function() {
  'use strict';

  var shell = require('shelljs');
  var path = require('path');
  var venvPath = process.env.npm_package_config_virtualenvs_path;

  shell.exec(path.join(__dirname, 'dev-config.sh') + '  ' + venvPath);
})();
<|endoftext|>


<commit_before>// Copyright (c) 2012-2015, Matt Godbolt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

(function () {

    var props = require('../properties.js'),
        path = require('path'),
        fs = require('fs');

    var sourcePath = props.get('builtin', 'sourcepath', './examples/c++');
    var sourceMatch = new RegExp(props.get('builtin', 'extensionRe', '.*\.cpp$'));
    var examples = fs.readdirSync(sourcePath)
        .filter(function (file) {
            return file.match(sourceMatch);
        })
        .map(function (file) {
            var nicename = file.replace(/\.cpp$/, '');
            return {urlpart: nicename, name: nicename.replace(/_/g, ' '), path: path.join(sourcePath, file)};
        }).sort(function (x, y) {
            return y.name < x.name;
        });
    var byUrlpart = {};
    examples.forEach(function (e) {
        byUrlpart[e.urlpart] = e.path
    });

    function load(filename, callback) {
        var path = byUrlpart[filename];
        if (!path) {
            callback("No such path");
            return;
        }
        fs.readFile(path, 'utf-8', function (err, res) {
            if (err) {
                callback(err);
                return;
            }
            callback(null, {file: res});
        });
    }

    function list(callback) {
        callback(null, examples.map(function (example) {
            return {urlpart: example.urlpart, name: example.name};
        }));
    }

    exports.load = load;
    exports.save = null;
    exports.list = list;
    exports.name = "Examples";
    exports.urlpart = "builtin";

}).call(this);
<commit_msg>Fix sorting of examples<commit_after>// Copyright (c) 2012-2015, Matt Godbolt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

(function () {

    var props = require('../properties.js'),
        path = require('path'),
        fs = require('fs');

    var sourcePath = props.get('builtin', 'sourcepath', './examples/c++');
    var sourceMatch = new RegExp(props.get('builtin', 'extensionRe', '.*\.cpp$'));
    var examples = fs.readdirSync(sourcePath)
        .filter(function (file) {
            return file.match(sourceMatch);
        })
        .map(function (file) {
            var nicename = file.replace(/\.cpp$/, '');
            return {urlpart: nicename, name: nicename.replace(/_/g, ' '), path: path.join(sourcePath, file)};
        }).sort(function (x, y) {
            return x.name.localeCompare(y.name);
        });
    var byUrlpart = {};
    examples.forEach(function (e) {
        byUrlpart[e.urlpart] = e.path
    });

    function load(filename, callback) {
        var path = byUrlpart[filename];
        if (!path) {
            callback("No such path");
            return;
        }
        fs.readFile(path, 'utf-8', function (err, res) {
            if (err) {
                callback(err);
                return;
            }
            callback(null, {file: res});
        });
    }

    function list(callback) {
        callback(null, examples.map(function (example) {
            return {urlpart: example.urlpart, name: example.name};
        }));
    }

    exports.load = load;
    exports.save = null;
    exports.list = list;
    exports.name = "Examples";
    exports.urlpart = "builtin";

}).call(this);
<|endoftext|>


<commit_before>var fail = function(message, origin) {
	var o = origin || "fail";
	throw new Error("["+o+"] " + message);
}

var assert = function(message, expression, origin) {
	var m = "Assertion failed";
	if (message) { m += " ("+message+")"; }
	if (!expression) { fail(m, "assert"); }
}

var assertEquals = function(message, expected, actual) {
	if (arguments.length > 2) {
		var m = arguments[0];
		var e = arguments[1];
		var a = arguments[2];
	} else {
		m = false;
		var e = arguments[0];
		var a = arguments[1];
	}

	var reason = "Expected '"+e+"', received '"+a+"'";
	if (m) { reason += " ("+m+")"; }
	if (e !== a) { fail(reason, "assertEquals"); }
}

var assertThrows = function(message, callback) {
	if (arguments.length > 1) {
		var m = arguments[0];
		var c = arguments[1];
	} else {
		var m = false;
		var c = arguments[0];
	}

	var ok = false;
	try {
		c();
	} catch(e) {
		ok = true;
	} finally {
		var reason = "Exception not thrown";
		if (m) { reason += " ("+m+")"; }
		if (!ok) { fail(reason, "assertThrows"); }
	}
}

exports.fail = fail;
exports.assert = assert;
exports.assertEquals = assertEquals;
exports.assertThrows = assertThrows;<commit_msg>assert fix<commit_after>var fail = function(message, origin) {
	var o = origin || "fail";
	throw new Error("["+o+"] " + message);
}

var assert = function(message, expression) {
	var m = "Assertion failed";
	if (message) { m += " ("+message+")"; }
	if (!expression) { fail(m, "assert"); }
}

var assertEquals = function(message, expected, actual) {
	if (arguments.length > 2) {
		var m = arguments[0];
		var e = arguments[1];
		var a = arguments[2];
	} else {
		m = false;
		var e = arguments[0];
		var a = arguments[1];
	}

	var reason = "Expected '"+e+"', received '"+a+"'";
	if (m) { reason += " ("+m+")"; }
	if (e !== a) { fail(reason, "assertEquals"); }
}

var assertThrows = function(message, callback) {
	if (arguments.length > 1) {
		var m = arguments[0];
		var c = arguments[1];
	} else {
		var m = false;
		var c = arguments[0];
	}

	var ok = false;
	try {
		c();
	} catch(e) {
		ok = true;
	} finally {
		var reason = "Exception not thrown";
		if (m) { reason += " ("+m+")"; }
		if (!ok) { fail(reason, "assertThrows"); }
	}
}

exports.fail = fail;
exports.assert = assert;
exports.assertEquals = assertEquals;
exports.assertThrows = assertThrows;<|endoftext|>


<commit_before>'use strict';
const {
    Webhook,
    ExpressJS,
    Lambda
} = require('jovo-framework');

const {app} = require('./app.js.js');

if (process.argv.indexOf('--webhook') > -1) {
    const port = process.env.JOVO_PORT || 3000;
    Webhook.jovoApp = app;
    Webhook.listen(port, () => {
        console.info(`Local server listening on port ${port}!`);
    });

    Webhook.post('/webhook', async (req, res) => {
        await app.handle(new ExpressJS(req, res));
    });
}

// AWS Lambda
exports.handler = async (event, context, callback) => {
    await app.handle(new Lambda(event, context, callback));
};
<commit_msg>:bug: examples: fixes file path<commit_after>'use strict';
const {
    Webhook,
    ExpressJS,
    Lambda
} = require('jovo-framework');

const {app} = require('./app.js');

if (process.argv.indexOf('--webhook') > -1) {
    const port = process.env.JOVO_PORT || 3000;
    Webhook.jovoApp = app;
    Webhook.listen(port, () => {
        console.info(`Local server listening on port ${port}!`);
    });

    Webhook.post('/webhook', async (req, res) => {
        await app.handle(new ExpressJS(req, res));
    });
}

// AWS Lambda
exports.handler = async (event, context, callback) => {
    await app.handle(new Lambda(event, context, callback));
};
<|endoftext|>


<commit_before>/**
 * Set up Selenium WebDriver for UI tests.
 */
'use strict';

var webdriver;

var pageLoadTimeoutms = 60 * 1000;
var implicitlyWaitTimeoutms = 1 * 1000;

/*
 * Convert a string to camel case.
 * @param  String string The string to modify.
 * @return String        The modified string.
 */
function toCamelCase(string) {
  return string.replace(/^([A-Z])|\s(\w)/g, function(match, p1, p2) {
    if (p2) {
      return p2.toUpperCase();
    }
    return p1.toLowerCase();
  });
}

function getCustomCapabilitiesFromEnvironment(webdriver) {
  var saucelabsProperties = {};

  var browserKey = webdriver.Capability.BROWSER_NAME;
  var platformKey = webdriver.Capability.PLATFORM_NAME;

  var browserValue = process.env.SELENIUM_BROWSER || webdriver.Browser.FIREFOX;

  // Object.values() will be in ES7, but until then...
  let validBrowser = false;
  for (let key in webdriver.Browser) {
    validBrowser = webdriver.Browser[key] === browserValue;
    if (validBrowser) {
      break;
    }
  }

  if (validBrowser) {
    saucelabsProperties[browserKey] = browserValue;
  } else {
    /* eslint-disable no-console */
    console.warn('Unsupported browser requested, ignoring: ', browserValue);
    /* eslint-enable no-console */
  }

  if(process.env.SELENIUM_PLATFORM) {
    saucelabsProperties[platformKey] = process.env.SELENIUM_PLATFORM;
  }

  // Parse any prefixed general environment variables,
  // e.g. SAUCELABS_BUILD on teamcity.
  Object.keys(process.env).forEach(function(key) {
    if(/^SAUCELABS_.*/.test(key)) {
      var sanitisedKey = key
        .replace(/^SAUCELABS_/, '')
        .split(/_/)
        .map(function (item) { return item.toLowerCase(); })
        .join(' ');
      sanitisedKey = toCamelCase(sanitisedKey);
      saucelabsProperties[sanitisedKey] = process.env[key];
    }
  });

  return saucelabsProperties;
}

module.exports = function seleniumHooks() {
  this.Before('@ui-automation', function(scenario, callback) {
    var world = this;
    var timeoutManager;

    // Lazy require WebDriver so it isn't pulled in for non-selenium tests.
    webdriver = require('selenium-webdriver');

    var capabilities = getCustomCapabilitiesFromEnvironment(webdriver);

    // Set the meta data for possbile use by SauceLabs.
    capabilities.name = scenario.getName() || undefined;
    capabilities.tags = scenario.getTags().map((t) => t.getName()) || undefined;

    // this is defaults, can be overriden through environment variables
    // http://selenium.googlecode.com/git/docs/api/javascript/module_selenium-webdriver_builder_class_Builder.html
    try {
      var driver = world.browser = new webdriver.Builder()
        .withCapabilities(capabilities)
        .build();

      // Manage timeouts.
      timeoutManager = driver.manage().timeouts();
      timeoutManager.pageLoadTimeout(pageLoadTimeoutms);
      timeoutManager.implicitlyWait(implicitlyWaitTimeoutms);

    } catch (error) {
      callback(error);
    }
    callback();
  });

  // Tidy up.
  this.After('@ui-automation', function(scenario, callback) {
    var browser = this.browser;
    browser.quit();
    callback();
  });
};
<commit_msg>Attempt to tell the TeamCity Sauce plugin which Sauce results to link to.<commit_after>/**
 * Set up Selenium WebDriver for UI tests.
 */
'use strict';

var webdriver;

var pageLoadTimeoutms = 60 * 1000;
var implicitlyWaitTimeoutms = 1 * 1000;

/*
 * Convert a string to camel case.
 * @param  String string The string to modify.
 * @return String        The modified string.
 */
function toCamelCase(string) {
  return string.replace(/^([A-Z])|\s(\w)/g, function(match, p1, p2) {
    if (p2) {
      return p2.toUpperCase();
    }
    return p1.toLowerCase();
  });
}

function getCustomCapabilitiesFromEnvironment(webdriver) {
  var saucelabsProperties = {};

  var browserKey = webdriver.Capability.BROWSER_NAME;
  var platformKey = webdriver.Capability.PLATFORM_NAME;

  var browserValue = process.env.SELENIUM_BROWSER || webdriver.Browser.FIREFOX;

  // Object.values() will be in ES7, but until then...
  let validBrowser = false;
  for (let key in webdriver.Browser) {
    validBrowser = webdriver.Browser[key] === browserValue;
    if (validBrowser) {
      break;
    }
  }

  if (validBrowser) {
    saucelabsProperties[browserKey] = browserValue;
  } else {
    /* eslint-disable no-console */
    console.warn('Unsupported browser requested, ignoring: ', browserValue);
    /* eslint-enable no-console */
  }

  if(process.env.SELENIUM_PLATFORM) {
    saucelabsProperties[platformKey] = process.env.SELENIUM_PLATFORM;
  }

  // Parse any prefixed general environment variables,
  // e.g. SAUCELABS_BUILD on teamcity.
  Object.keys(process.env).forEach(function(key) {
    if(/^SAUCELABS_.*/.test(key)) {
      var sanitisedKey = key
        .replace(/^SAUCELABS_/, '')
        .split(/_/)
        .map(function (item) { return item.toLowerCase(); })
        .join(' ');
      sanitisedKey = toCamelCase(sanitisedKey);
      saucelabsProperties[sanitisedKey] = process.env[key];
    }
  });

  return saucelabsProperties;
}

module.exports = function seleniumHooks() {
  this.Before('@ui-automation', function(scenario, callback) {
    var world = this;
    var timeoutManager;

    // Lazy require WebDriver so it isn't pulled in for non-selenium tests.
    webdriver = require('selenium-webdriver');

    var capabilities = getCustomCapabilitiesFromEnvironment(webdriver);

    // Set the meta data for possbile use by SauceLabs.
    capabilities.name = scenario.getName() || undefined;
    capabilities.tags = scenario.getTags().map((t) => t.getName()) || undefined;

    // this is defaults, can be overriden through environment variables
    // http://selenium.googlecode.com/git/docs/api/javascript/module_selenium-webdriver_builder_class_Builder.html
    try {
      var driver = world.browser = new webdriver.Builder()
        .withCapabilities(capabilities)
        .build();

      // Manage timeouts.
      timeoutManager = driver.manage().timeouts();
      timeoutManager.pageLoadTimeout(pageLoadTimeoutms);
      timeoutManager.implicitlyWait(implicitlyWaitTimeoutms);

    } catch (error) {
      callback(error);
    }
    callback();
  });

  // Tidy up.
  this.After('@ui-automation', function(scenario, callback) {
    var browser = this.browser;

    browser.getSession().then(function(session) {

      // Communicate the Sauce Id to TeamCity Sauce plugin.
      /* eslint-disable no-console */
      console.log('SauceOnDemandSessionID=%s job-name=%s', session.getId(), scenario.getName());
      /* eslint-enable no-console */

      browser.quit();
      callback();
    });
  });
};
<|endoftext|>