<commit_before>/**
 * @author: Dennis Hernández
 * @webSite: http://djhvscf.github.io/Blog
 * @version: v1.0.0
 */

!function ($) {

    'use strict';

    var sprintf = function (str) {
        var args = arguments,
            flag = true,
            i = 1;

        str = str.replace(/%s/g, function () {
            var arg = args[i++];

            if (typeof arg === 'undefined') {
                flag = false;
                return '';
            }
            return arg;
        });
        return flag ? str : '';
    };

    var getFieldIndex = function (columns, field) {
        var index = -1;

        $.each(columns, function (i, column) {
            if (column.field === field) {
                index = i;
                return false;
            }
            return true;
        });
        return index;
    };

    var calculateObjectValue = function (self, name, args, defaultValue) {
        if (typeof name === 'string') {
            // support obj.func1.func2
            var names = name.split('.');

            if (names.length > 1) {
                name = window;
                $.each(names, function (i, f) {
                    name = name[f];
                });
            } else {
                name = window[name];
            }
        }
        if (typeof name === 'object') {
            return name;
        }
        if (typeof name === 'function') {
            return name.apply(self, args);
        }
        return defaultValue;
    };

    var addOptionToSelectControl = function (selectControl, value, text) {
        //selectControl = $(selectControl.get(0));
        if (existsOptionInSelectControl(selectControl, value)) {
            selectControl.append($("<option></option>")
                .attr("value", value)
                .text($('
').html(text).text()));
            // Sort it. Not overly efficient to do this here
            var $opts = selectControl.find('option:gt(0)');
            $opts.sort(function(a,b){
                a = $(a).text().toLowerCase();
                b = $(b).text().toLowerCase();
                if ($.isNumeric(a) && $.isNumeric(b)) {
                    // Convert numerical values from string to float.
                    a = parseFloat(a);
                    b = parseFloat(b);
                }
                return a > b ? 1 : a < b ? -1 : 0;
            });
            selectControl.find('option:gt(0)').remove();
            selectControl.append($opts);
        }
    };

    var existsOptionInSelectControl = function (selectControl, value) {
        var options = selectControl.get(0).options,
            iOpt = 0;

        for (; iOpt < options.length; iOpt++) {
            if (options[iOpt].value === value) {
                //The value is nor valid to add
                return false;
            }
        }

        //If we get here, the value is valid to add
        return true;
    };

    var fixHeaderCSS = function (that) {
        that.$tableHeader.css('height', '77px');
    };

    var getCurrentHeader = function (that) {
        var header = that.$header;
        if (that.options.height) {
            header = that.$tableHeader;
        }

        return header;
    };

    var getCurrentSearchControls = function (that) {
        var searchControls = 'select, input';
        if (that.options.height) {
            searchControls = 'table select, table input';
        }

        return searchControls;
    };

    var copyValues = function (that) {
        var header = getCurrentHeader(that),
            searchControls = getCurrentSearchControls(that);

        that.options.values = [];

        header.find(searchControls).each(function () {
            that.options.values.push(
                {
                    field: $(this).parent().parent().parent().data('field'),
                    value: $(this).val()
                });
        });
    };

    var setValues = function(that) {
        var field = null,
            result = [],
            header = getCurrentHeader(that),
            searchControls = getCurrentSearchControls(that);

        if (that.options.values.length > 0) {
            header.find(searchControls).each(function (index, ele) {
                field = $(this).parent().parent().parent().data('field');
                result = $.grep(that.options.values, function (valueObj) {
                    return valueObj.field === field;
                });

                if (result.length > 0) {
                    $(this).val(result[0].value);
                }
            });
        }
    };

    var createControls = function (that, header) {
        var addedFilterControl = false,
            isVisible,
            html,
            timeoutId = 0;

        $.each(that.options.columns, function (i, column) {
            isVisible = 'hidden';
            html = [];

            if (!column.visible) {
                return;
            }

            if (!column.filterControl) {
                html.push('

');
            } else {
                html.push('
');

                if (column.filterControl && column.searchable) {
                    addedFilterControl = true;
                    isVisible = 'visible'
                }
                switch (column.filterControl.toLowerCase()) {
                    case 'input' :
                        html.push(sprintf('<input type="text" class="form-control" style="width: 100%; visibility: %s">', isVisible));
                        break;
                    case 'select':
                        html.push(sprintf('<select class="%s form-control" style="width: 100%; visibility: %s"></select>',
                            column.field, isVisible))
                        break;
                    case 'datepicker':
                        html.push(sprintf('<input type="text" class="date-filter-control %s form-control" style="width: 100%; visibility: %s">',
                            column.field, isVisible));
                        break;
                }
            }

            $.each(header.children().children(), function (i, tr) {
                tr = $(tr);
                if (tr.data('field') === column.field) {
                    tr.find('.fht-cell').append(html.join(''));
                    return false;
                }
            });
            if (column.filterData !== undefined && column.filterData.toLowerCase() !== 'column') {
                var filterDataType = column.filterData.substring(0, 3);
                var filterDataSource = column.filterData.substring(4, column.filterData.length);
                var selectControl = $('.' + column.field);
                addOptionToSelectControl(selectControl, '', '');

                switch (filterDataType) {
                    case 'url':
                        $.ajax({
                            url: filterDataSource,
                            dataType: 'json',
                            success: function (data) {
                                $.each(data, function (key, value) {
                                    addOptionToSelectControl(selectControl, key, value);
                                });
                            }
                        });
                        break;
                    case 'var':
                        var variableValues = window[filterDataSource];
                        for (var key in variableValues) {
                            addOptionToSelectControl(selectControl, key, variableValues[key]);
                        }
                        break;
                }
            }
        });

        if (addedFilterControl) {
            header.off('keyup', 'input').on('keyup', 'input', function (event) {
                clearTimeout(timeoutId);
                timeoutId = setTimeout(function () {
                    that.onColumnSearch(event);
                }, that.options.searchTimeOut);
            });

            header.off('change', 'select').on('change', 'select', function (event) {
                clearTimeout(timeoutId);
                timeoutId = setTimeout(function () {
                    that.onColumnSearch(event);
                }, that.options.searchTimeOut);
            });

            var datepickers = header.find('.date-filter-control');
            if (datepickers.length > 0) {
                $.each(that.options.columns, function (i, column) {
                    if (column.filterControl !== undefined && column.filterControl.toLowerCase() === 'datepicker') {
                        header.find('.date-filter-control.' + column.field).datepicker(column.filterDatepickerOptions)
                            .on('changeDate', function (e) {
                                //Fired the keyup event
                                $(e.currentTarget).keyup();
                            });
                    }
                });
            }
        } else {
            header.find('.filterControl').hide();
        }
    };

    $.extend($.fn.bootstrapTable.defaults, {
        filterControl: false,
        onColumnSearch: function (field, text) {
            return false;
        },
        //internal variables
        values: []
    });

    $.extend($.fn.bootstrapTable.COLUMN_DEFAULTS, {
        filterControl: undefined,
        filterData: undefined,
        filterDatepickerOptions: undefined
    });

    $.extend($.fn.bootstrapTable.Constructor.EVENTS, {
        'column-search.bs.table': 'onColumnSearch'
    });

    var BootstrapTable = $.fn.bootstrapTable.Constructor,
        _init = BootstrapTable.prototype.init,
        _initHeader = BootstrapTable.prototype.initHeader,
        _initBody = BootstrapTable.prototype.initBody,
        _initSearch = BootstrapTable.prototype.initSearch;

    BootstrapTable.prototype.init = function () {
        //Make sure that the filtercontrol option is set
        if (this.options.filterControl) {
            var that = this;
            //Make sure that the internal variables are set correctly
            this.options.values = [];

            this.$el.on('reset-view.bs.table', function () {
                //Create controls on $tableHeader if the height is set
                if (!that.options.height) {
                    return;
                }

                //Avoid recreate the controls
                if (that.$tableHeader.find('select').length > 0 || that.$tableHeader.find('input').length > 0) {
                    return;
                }

                createControls(that, that.$tableHeader);
            }).on('post-header.bs.table', function () {
                setValues(that);
            }).on('post-body.bs.table', function () {
                if (that.options.height) {
                    fixHeaderCSS(that);
                }
            }).on('column-switch.bs.table', function(field, checked) {
                setValues(that);
            });
        }
        _init.apply(this, Array.prototype.slice.apply(arguments));
    };

    BootstrapTable.prototype.initHeader = function () {
        _initHeader.apply(this, Array.prototype.slice.apply(arguments));

        if (!this.options.filterControl) {
            return;
        }
        createControls(this, this.$header);
    };

    BootstrapTable.prototype.initBody = function () {
        _initBody.apply(this, Array.prototype.slice.apply(arguments));

        var that = this,
            data = this.options.data,
            pageTo = this.pageTo < this.options.data.length ? this.options.data.length : this.pageTo;

        for (var i = this.pageFrom - 1; i < pageTo; i++) {
            var item = data[i];

            $.each(this.header.fields, function (j, field) {
                var value = item[field],
                    column = that.options.columns[getFieldIndex(that.options.columns, field)];

                value = calculateObjectValue(that.header, that.header.formatters[j], [value, item, i], value);

                if ((!column.checkbox) || (!column.radio)) {
                    if (column.filterControl !== undefined && column.filterControl.toLowerCase() === 'select' && column.searchable) {
                        if (column.filterData === undefined || column.filterData.toLowerCase() === 'column') {
                            var selectControl = $('.' + column.field);
                            if (selectControl !== undefined && selectControl.length > 0) {
                                if (selectControl.get(0).options.length === 0) {
                                    //Added the default option
                                    addOptionToSelectControl(selectControl, '', '');
                                }

                                //Added a new value
                                addOptionToSelectControl(selectControl, value, value);
                            }
                        }
                    }
                }
            });
        }
    };

    BootstrapTable.prototype.initSearch = function () {
        _initSearch.apply(this, Array.prototype.slice.apply(arguments));

        var that = this;
        var fp = $.isEmptyObject(this.filterColumnsPartial) ? null : this.filterColumnsPartial;

        //Check partial column filter
        this.data = fp ? $.grep(this.data, function (item, i) {
            for (var key in fp) {
                var fval = fp[key].toLowerCase();
                var value = item[key];
                value = calculateObjectValue(that.header,
                    that.header.formatters[$.inArray(key, that.header.fields)],
                    [value, item, i], value);

                if (!($.inArray(key, that.header.fields) !== -1 &&
                    (typeof value === 'string' || typeof value === 'number') &&
                    (value + '').toLowerCase().indexOf(fval) !== -1)) {
                    return false;
                }
            }
            return true;
        }) : this.data;
    };

    BootstrapTable.prototype.onColumnSearch = function (event) {
        copyValues(this);
        var text = $.trim($(event.currentTarget).val());
        var $field = $(event.currentTarget).parent().parent().parent().data('field')

        if ($.isEmptyObject(this.filterColumnsPartial)) {
            this.filterColumnsPartial = {};
        }
        if (text) {
            this.filterColumnsPartial[$field] = text;
        } else {
            delete this.filterColumnsPartial[$field];
        }

        this.options.pageNumber = 1;
        this.onSearch(event);
        this.updatePagination();
        this.trigger('column-search', $field, text);
    };
}(jQuery);
<commit_msg>Improve the filter-control extension<commit_after>/**
 * @author: Dennis Hernández
 * @webSite: http://djhvscf.github.io/Blog
 * @version: v1.0.0
 */

!function ($) {

    'use strict';

    var sprintf = function (str) {
        var args = arguments,
            flag = true,
            i = 1;

        str = str.replace(/%s/g, function () {
            var arg = args[i++];

            if (typeof arg === 'undefined') {
                flag = false;
                return '';
            }
            return arg;
        });
        return flag ? str : '';
    };

    var getFieldIndex = function (columns, field) {
        var index = -1;

        $.each(columns, function (i, column) {
            if (column.field === field) {
                index = i;
                return false;
            }
            return true;
        });
        return index;
    };

    var calculateObjectValue = function (self, name, args, defaultValue) {
        if (typeof name === 'string') {
            // support obj.func1.func2
            var names = name.split('.');

            if (names.length > 1) {
                name = window;
                $.each(names, function (i, f) {
                    name = name[f];
                });
            } else {
                name = window[name];
            }
        }
        if (typeof name === 'object') {
            return name;
        }
        if (typeof name === 'function') {
            return name.apply(self, args);
        }
        return defaultValue;
    };

    var addOptionToSelectControl = function (selectControl, value, text) {
        //selectControl = $(selectControl.get(0));
        if (existsOptionInSelectControl(selectControl, value)) {
            selectControl.append($("<option></option>")
                .attr("value", value)
                .text($('
').html(text).text()));
            // Sort it. Not overly efficient to do this here
            var $opts = selectControl.find('option:gt(0)');
            $opts.sort(function(a,b){
                a = $(a).text().toLowerCase();
                b = $(b).text().toLowerCase();
                if ($.isNumeric(a) && $.isNumeric(b)) {
                    // Convert numerical values from string to float.
                    a = parseFloat(a);
                    b = parseFloat(b);
                }
                return a > b ? 1 : a < b ? -1 : 0;
            });
            selectControl.find('option:gt(0)').remove();
            selectControl.append($opts);
        }
    };

    var existsOptionInSelectControl = function (selectControl, value) {
        var options = selectControl.get(0).options,
            iOpt = 0;

        for (; iOpt < options.length; iOpt++) {
            if (options[iOpt].value === value) {
                //The value is nor valid to add
                return false;
            }
        }

        //If we get here, the value is valid to add
        return true;
    };

    var fixHeaderCSS = function (that) {
        that.$tableHeader.css('height', '77px');
    };

    var getCurrentHeader = function (that) {
        var header = that.$header;
        if (that.options.height) {
            header = that.$tableHeader;
        }

        return header;
    };

    var getCurrentSearchControls = function (that) {
        var searchControls = 'select, input';
        if (that.options.height) {
            searchControls = 'table select, table input';
        }

        return searchControls;
    };

    var copyValues = function (that) {
        var header = getCurrentHeader(that),
            searchControls = getCurrentSearchControls(that);

        that.options.values = [];

        header.find(searchControls).each(function () {
            that.options.values.push(
                {
                    field: $(this).parent().parent().parent().data('field'),
                    value: $(this).val()
                });
        });
    };

    var setValues = function(that) {
        var field = null,
            result = [],
            header = getCurrentHeader(that),
            searchControls = getCurrentSearchControls(that);

        if (that.options.values.length > 0) {
            header.find(searchControls).each(function (index, ele) {
                field = $(this).parent().parent().parent().data('field');
                result = $.grep(that.options.values, function (valueObj) {
                    return valueObj.field === field;
                });

                if (result.length > 0) {
                    $(this).val(result[0].value);
                }
            });
        }
    };

    var createControls = function (that, header) {
        var addedFilterControl = false,
            isVisible,
            html,
            timeoutId = 0;

        $.each(that.options.columns, function (i, column) {
            isVisible = 'hidden';
            html = [];

            if (!column.visible) {
                return;
            }

            if (!column.filterControl) {
                html.push('

');
            } else {
                html.push('
');

                if (column.filterControl && column.searchable) {
                    addedFilterControl = true;
                    isVisible = 'visible'
                }
                switch (column.filterControl.toLowerCase()) {
                    case 'input' :
                        html.push(sprintf('<input type="text" class="form-control" style="width: 100%; visibility: %s">', isVisible));
                        break;
                    case 'select':
                        html.push(sprintf('<select class="%s form-control" style="width: 100%; visibility: %s"></select>',
                            column.field, isVisible))
                        break;
                    case 'datepicker':
                        html.push(sprintf('<input type="text" class="date-filter-control %s form-control" style="width: 100%; visibility: %s">',
                            column.field, isVisible));
                        break;
                }
            }

            $.each(header.children().children(), function (i, tr) {
                tr = $(tr);
                if (tr.data('field') === column.field) {
                    tr.find('.fht-cell').append(html.join(''));
                    return false;
                }
            });
            if (column.filterData !== undefined && column.filterData.toLowerCase() !== 'column') {
                var filterDataType = column.filterData.substring(0, 3);
                var filterDataSource = column.filterData.substring(4, column.filterData.length);
                var selectControl = $('.' + column.field);
                addOptionToSelectControl(selectControl, '', '');

                switch (filterDataType) {
                    case 'url':
                        $.ajax({
                            url: filterDataSource,
                            dataType: 'json',
                            success: function (data) {
                                $.each(data, function (key, value) {
                                    addOptionToSelectControl(selectControl, key, value);
                                });
                            }
                        });
                        break;
                    case 'var':
                        var variableValues = window[filterDataSource];
                        for (var key in variableValues) {
                            addOptionToSelectControl(selectControl, key, variableValues[key]);
                        }
                        break;
                }
            }
        });

        if (addedFilterControl) {
            header.off('keyup', 'input').on('keyup', 'input', function (event) {
                clearTimeout(timeoutId);
                timeoutId = setTimeout(function () {
                    that.onColumnSearch(event);
                }, that.options.searchTimeOut);
            });

            header.off('change', 'select').on('change', 'select', function (event) {
                clearTimeout(timeoutId);
                timeoutId = setTimeout(function () {
                    that.onColumnSearch(event);
                }, that.options.searchTimeOut);
            });

            if (header.find('.date-filter-control').length > 0) {
                $.each(that.options.columns, function (i, column) {
                    if (column.filterControl !== undefined && column.filterControl.toLowerCase() === 'datepicker') {
                        header.find('.date-filter-control.' + column.field).datepicker(column.filterDatepickerOptions)
                            .on('changeDate', function (e) {
                                //Fired the keyup event
                                $(e.currentTarget).keyup();
                            });
                    }
                });
            }
        } else {
            header.find('.filterControl').hide();
        }
    };

    $.extend($.fn.bootstrapTable.defaults, {
        filterControl: false,
        onColumnSearch: function (field, text) {
            return false;
        },
        //internal variables
        values: []
    });

    $.extend($.fn.bootstrapTable.COLUMN_DEFAULTS, {
        filterControl: undefined,
        filterData: undefined,
        filterDatepickerOptions: undefined
    });

    $.extend($.fn.bootstrapTable.Constructor.EVENTS, {
        'column-search.bs.table': 'onColumnSearch'
    });

    var BootstrapTable = $.fn.bootstrapTable.Constructor,
        _init = BootstrapTable.prototype.init,
        _initHeader = BootstrapTable.prototype.initHeader,
        _initBody = BootstrapTable.prototype.initBody,
        _initSearch = BootstrapTable.prototype.initSearch;

    BootstrapTable.prototype.init = function () {
        //Make sure that the filtercontrol option is set
        if (this.options.filterControl) {
            var that = this;
            //Make sure that the internal variables are set correctly
            this.options.values = [];

            this.$el.on('reset-view.bs.table', function () {
                //Create controls on $tableHeader if the height is set
                if (!that.options.height) {
                    return;
                }

                //Avoid recreate the controls
                if (that.$tableHeader.find('select').length > 0 || that.$tableHeader.find('input').length > 0) {
                    return;
                }

                createControls(that, that.$tableHeader);
            }).on('post-header.bs.table', function () {
                setValues(that);
            }).on('post-body.bs.table', function () {
                if (that.options.height) {
                    fixHeaderCSS(that);
                }
            }).on('column-switch.bs.table', function(field, checked) {
                setValues(that);
            });
        }
        _init.apply(this, Array.prototype.slice.apply(arguments));
    };

    BootstrapTable.prototype.initHeader = function () {
        _initHeader.apply(this, Array.prototype.slice.apply(arguments));

        if (!this.options.filterControl) {
            return;
        }
        createControls(this, this.$header);
    };

    BootstrapTable.prototype.initBody = function () {
        _initBody.apply(this, Array.prototype.slice.apply(arguments));

        var that = this,
            data = this.options.data,
            pageTo = this.pageTo < this.options.data.length ? this.options.data.length : this.pageTo;

        for (var i = this.pageFrom - 1; i < pageTo; i++) {
            var item = data[i];

            $.each(this.header.fields, function (j, field) {
                var value = item[field],
                    column = that.options.columns[getFieldIndex(that.options.columns, field)];

                value = calculateObjectValue(that.header, that.header.formatters[j], [value, item, i], value);

                if ((!column.checkbox) || (!column.radio)) {
                    if (column.filterControl !== undefined && column.filterControl.toLowerCase() === 'select' && column.searchable) {
                        if (column.filterData === undefined || column.filterData.toLowerCase() === 'column') {
                            var selectControl = $('.' + column.field);
                            if (selectControl !== undefined && selectControl.length > 0) {
                                if (selectControl.get(0).options.length === 0) {
                                    //Added the default option
                                    addOptionToSelectControl(selectControl, '', '');
                                }

                                //Added a new value
                                addOptionToSelectControl(selectControl, value, value);
                            }
                        }
                    }
                }
            });
        }
    };

    BootstrapTable.prototype.initSearch = function () {
        _initSearch.apply(this, Array.prototype.slice.apply(arguments));

        var that = this;
        var fp = $.isEmptyObject(this.filterColumnsPartial) ? null : this.filterColumnsPartial;

        //Check partial column filter
        this.data = fp ? $.grep(this.data, function (item, i) {
            for (var key in fp) {
                var fval = fp[key].toLowerCase();
                var value = item[key];
                value = calculateObjectValue(that.header,
                    that.header.formatters[$.inArray(key, that.header.fields)],
                    [value, item, i], value);

                if (!($.inArray(key, that.header.fields) !== -1 &&
                    (typeof value === 'string' || typeof value === 'number') &&
                    (value + '').toLowerCase().indexOf(fval) !== -1)) {
                    return false;
                }
            }
            return true;
        }) : this.data;
    };

    BootstrapTable.prototype.onColumnSearch = function (event) {
        copyValues(this);
        var text = $.trim($(event.currentTarget).val());
        var $field = $(event.currentTarget).parent().parent().parent().data('field')

        if ($.isEmptyObject(this.filterColumnsPartial)) {
            this.filterColumnsPartial = {};
        }
        if (text) {
            this.filterColumnsPartial[$field] = text;
        } else {
            delete this.filterColumnsPartial[$field];
        }

        this.options.pageNumber = 1;
        this.onSearch(event);
        this.updatePagination();
        this.trigger('column-search', $field, text);
    };
}(jQuery);
<|endoftext|>


<commit_before>import logger from 'logger';
import { isRunningPackaged } from 'constants';
import url from 'url';
// import mime from 'mime';
import path from 'path';

const authRoute = {
    method  : 'GET',
    path    : '/auth/{link*}',
    handler : async ( request, reply ) =>
    {
        try
        {
            const link = request.params.link;
            const linkUrl = url.parse( link );
            logger.verbose( 'safe-auth path: ', linkUrl.path );

            // account for asar packaging, as hapi cant unpack. We move the folders via package.json 'extraResources'
            const authDistLocale = isRunningPackaged ? `../extensions/safe/` : `../`;
            const authDist = path.resolve( __dirname, authDistLocale, 'auth-web-app/temp_dist/' );

            switch ( linkUrl.path )
            {
                case '/bundle.js':
                    reply.file( path.resolve( authDist, 'bundle.js' )  )
                    break;
                case '/styles.css':
                    reply.file( path.resolve( authDist, 'styles.css' )  )
                    break;
                case '/bundle.js.map':
                    reply.file( path.resolve( authDist, 'bundle.js.map' )  )

                    break;
                case '/favicon.png':
                    reply.file( path.resolve( authDist, 'favicon.png' )  )
                    break;
                default:
                    reply.file( path.resolve( authDist, 'app.html' )  ).type('text/html');
                    break;
            }
        }
        catch ( e )
        {
            logger.error( e );
            return reply( e.message || e );
        }
    }
};

export default authRoute;
<commit_msg>fix/auth: Fix hapi 403 issue for accessing outwith of current folder in linux.<commit_after>import logger from 'logger';
import { isRunningPackaged } from 'constants';
import url from 'url';
import path from 'path';

const authRoute = {
    method  : 'GET',
    path    : '/auth/{link*}',
    handler : async ( request, reply ) =>
    {
        try
        {
            const link = request.params.link;
            const linkUrl = url.parse( link );

            const authDistLocale = isRunningPackaged ? `../extensions/safe/` : `../`;
            const authDist = path.resolve( __dirname, authDistLocale, 'auth-web-app/temp_dist/' );

            switch ( linkUrl.path )
            {
                case '/bundle.js':
                    reply.file( path.resolve( authDist, 'bundle.js' ), { confine: false }  )
                    break;
                case '/styles.css':
                    reply.file( path.resolve( authDist, 'styles.css' ), { confine: false }  )
                    break;
                case '/bundle.js.map':
                    reply.file( path.resolve( authDist, 'bundle.js.map' ), { confine: false }  )

                    break;
                case '/favicon.png':
                    reply.file( path.resolve( authDist, 'favicon.png' ), { confine: false }  )
                    break;
                default:
                    reply.file( path.resolve( authDist, 'app.html' ), { confine: false }  ).type('text/html');
                    break;
            }
        }
        catch ( e )
        {
            logger.error( e );
            return reply( e.message || e );
        }
    }
};

export default authRoute;
<|endoftext|>


<commit_before>import log from "../../utils/log";

function BinxAdminBlogs() {
  return {
    init() {
      this.editDate()
      this.setIndexImage();
      this.setIndex();
      this.noPrimaryPhotoToggle();
      this.publicImageDelete();
    },

    editDate() {
      $("#change_published_date").on("click", e => {
        e.preventDefault();
        $("#blog-date").slideDown()
      })
    },

    publicImageDelete() {
      $('ul#public_images').on('click', ".image-delete-button", function(e) {
        e.preventDefault();
        const id = $(".image-delete-button").closest(".row").find("input").val()
        $.ajax({
          url: `/public_images/${id}`,
          type: 'delete'
        });
        this.closest('li').remove()
      })
    },

    noPrimaryPhotoToggle() {
      const noPrimaryPhotoBox = $(".index_image_0")
      noPrimaryPhotoBox.on("change", e => {
        if (noPrimaryPhotoBox.prop("checked")) {
          $(".index-image-select input").prop("checked", false)
        }
      })
    },

    setIndex() {
      const index_image = $('#blog_index_image_id').val()
      return $("li#" + index_image).find($("input")).prop("checked", true)
    },

    setIndexImage(e) {
      $("ul#public_images").on("change", '.index-image-select input', function(e) {
        e.preventDefault();
        if ($('#blog_index_image_id').val != 0) {
          $('#blog_index_image_id').val($(e.target).val());
          $(".index_image_0").prop("checked", false)
        }
      })
    },
  };
}

export default BinxAdminBlogs
<commit_msg>fix blog photo upload bug<commit_after>import log from "../../utils/log";

function BinxAdminBlogs() {
  return {
    init() {
      this.editDate();
      this.setIndexImage();
      this.setIndex();
      this.noPrimaryPhotoToggle();
      this.publicImageDelete();
    },

    editDate() {
      $("#change_published_date").on("click", e => {
        e.preventDefault();
        $("#blog-date").slideDown();
      });
    },

    publicImageDelete() {
      $("ul#public_images").on("click", ".image-delete-button", function(e) {
        e.preventDefault();
        const id = $(".image-delete-button")
          .closest(".row")
          .find("input")
          .val();
        $.ajax({
          url: `/public_images/${id}`,
          type: "delete"
        });
        this.closest("li").remove();
      });
    },

    noPrimaryPhotoToggle() {
      const noPrimaryPhotoBox = $(".index_image_0");
      noPrimaryPhotoBox.on("change", e => {
        if (noPrimaryPhotoBox.prop("checked")) {
          $(".index-image-select input").prop("checked", false);
        }
      });
    },

    setIndex() {
      const index_image = $("#blog_index_image_id").val();
      if (index_image.length) {
        return $("li#" + index_image)
          .find($("input"))
          .prop("checked", true);
      }
    },

    setIndexImage(e) {
      $("ul#public_images").on("change", ".index-image-select input", function(
        e
      ) {
        e.preventDefault();
        if ($("#blog_index_image_id").val != 0) {
          $("#blog_index_image_id").val($(e.target).val());
          $(".index_image_0").prop("checked", false);
        }
      });
    }
  };
}

export default BinxAdminBlogs;
<|endoftext|>


<commit_before>import log from "../utils/log";
import moment from "moment-timezone";

// Should be imported and initialized like this:
// if (!window.timeParser) { window.timeParser = new TimeParser() }

// And then you can add this to the react component:
// componentDidUpdate() { window.timeParser.localize() }

export default class TimeParser {
  constructor() {
    if (!window.localTimezone) {
      window.localTimezone = moment.tz.guess();
    }
    this.localTimezone = window.localTimezone;
    moment.tz.setDefault(this.localTimezone);
    this.yesterdayStart = moment().subtract(1, "day").startOf("day");
    this.todayStart = moment().startOf("day");
    this.todayEnd = moment().endOf("day");
    this.tomorrowEnd = moment().add(1, "day").endOf("day");
  }

  // If we're display time with the hour, we have different formats based on whether we include seconds
  // this manages that functionality
  hourFormat(time, baseTimeFormat, includeSeconds, includePreposition) {
    const prefix = includePreposition ? " at " : "";
    if (includeSeconds) {
      return `${prefix}${time.format(baseTimeFormat)}:${time.format(
        "ss"
      )} ${time.format("a")}`;
    } else {
      return prefix + time.format(`${baseTimeFormat}a`);
    }
  }

  localizedDateText(time, classList) {
    let prefix = "";
    // If we're dealing with yesterday or today (not the future)
    if (time < this.tomorrowEnd && time > this.yesterdayStart) {
      // If we're dealing with yesterday or tomorrow, we prepend that
      if (time < this.todayStart) {
        prefix = "Yesterday ";
      } else if (time > this.todayEnd) {
        prefix = "Tomorrow ";
      }
      return (
        prefix +
        this.hourFormat(
          time,
          "h:mm",
          classList.contains("preciseTimeSeconds"),
          classList.contains("withPreposition")
        )
      );
    }
    if (classList.contains("withPreposition")) {
      prefix = "on ";
    }

    // If it's preciseTime (or preciseTimeSeconds), always show the hours and mins
    if (
      classList.contains("preciseTime") ||
      classList.contains("preciseTimeSeconds")
    ) {
      let hourStr = this.hourFormat(
        time,
        "h:mm",
        classList.contains("preciseTimeSeconds"),
        classList.contains("withPreposition")
      );
      // Include the year if it isn't the current year
      if (time.year() === moment().year()) {
        return prefix + time.format("MMM Do[,] ") + hourStr;
      } else {
        return prefix + time.format("YYYY-MM-DD ") + hourStr;
      }
    }
    // Otherwise, format in basic format
    if (time.year() === moment().year()) {
      if (classList.contains("withPreposition")) {
        return prefix + time.format("MMM Do");
      } else {
        return prefix + time.format("MMM Do");
      }
    } else {
      return prefix + time.format("YYYY-MM-DD");
    }
  }

  preciseTimeSeconds(time) {
    return time.format("YYYY-MM-DD h:mm:ss a");
  }

  setHiddenTimezoneFields(el) {
    el.value = this.localTimezone;
  }

  writeUpdatedTimeText(el) {
    let text = el.textContent.trim();
    let time = null;
    // So running this again doesn't reapply to this element
    el.classList.remove("convertTime");
    // So we know which were updated (for styling, future updates, etc)
    el.classList.add("convertedTime");

    if (!(text.length > 0)) {
      return;
    }
    // If time is only a number, parse as a timestamp
    // Otherwise, parse as ISO_8601 which is the default time string
    if (/^\d+$/.test(text)) {
      time = moment.unix(text);
    } else {
      time = moment(text, moment.ISO_8601);
    }
    if (time === null || !time.isValid()) {
      return;
    }

    el.innerHTML = this.localizedDateText(time, el.classList);
    el.setAttribute("title", this.preciseTimeSeconds(time));
  }

  writeTimezone(el) {
    el.textContent = moment().format("z");
    el.classList.remove("convertTimezone");
  }

  localize() {
    // Write times
    Array.from(document.getElementsByClassName("convertTime")).forEach((el) =>
      this.writeUpdatedTimeText(el)
    );

    // Write timezones
    Array.from(
      document.getElementsByClassName("convertTimezone")
    ).forEach((el) => this.writeTimezone(el));

    // Write hidden timezone fields - so if we're submitting a form, it includes the current timezone
    Array.from(
      document.getElementsByClassName("hiddenFieldTimezone")
    ).forEach((el) => this.setHiddenTimezoneFields(el));
  }
}
<commit_msg>more functional time parser (#1810)<commit_after>import log from "../utils/log";
import moment from "moment-timezone";

// TimeParser updates all HTML elements with class '.convertTime', making them:
// - Human readable
// - Display time in current timezone
// - With context relevant data (e.g. today shows hour, last month just date)
// - Gives the element a title of the precise time with seconds (so hovering shows it)
// - If elements have classes '.preciseTime' or '.preciseTimeSeconds', includes extra specificity in output
// - If elements have class '.withPreposition' it includes preposition (to make time fit better in a sentence)
// - Requires elements have HTML content of a time string (e.g. a unix timestamp)
//
// Imported and initialized like this:
// if (!window.timeParser) { window.timeParser = new TimeParser() }
//
// To update an individual element with a time in it (doesn't require element to have class '.convertTime'):
// timeParser.writeTime(el)
//
// To get just the text that is output into the element:
// timeFromText("1604337131")
//
// You can add this to a react component:
// componentDidUpdate() { window.timeParser.localize() }

export default class TimeParser {
  constructor() {
    if (!window.localTimezone) {
      window.localTimezone = moment.tz.guess();
    }
    this.localTimezone = window.localTimezone;
    moment.tz.setDefault(this.localTimezone);
    this.yesterdayStart = moment().subtract(1, "day").startOf("day");
    this.todayStart = moment().startOf("day");
    this.todayEnd = moment().endOf("day");
    this.tomorrowEnd = moment().add(1, "day").endOf("day");
  }

  // If we're display time with the hour, we have different formats based on whether we include seconds
  // this manages that functionality
  hourFormat(time, baseTimeFormat, includeSeconds, includePreposition) {
    const prefix = includePreposition ? " at " : "";
    if (includeSeconds) {
      return `${prefix}${time.format(baseTimeFormat)}:${time.format(
        "ss"
      )} ${time.format("a")}`;
    } else {
      return prefix + time.format(`${baseTimeFormat}a`);
    }
  }

  localizedDateText(time, preciseTime, includeSeconds, includePreposition) {
    let prefix = "";
    // If we're dealing with yesterday or today (not the future)
    if (time < this.tomorrowEnd && time > this.yesterdayStart) {
      // If we're dealing with yesterday or tomorrow, we prepend that
      if (time < this.todayStart) {
        prefix = "Yesterday ";
      } else if (time > this.todayEnd) {
        prefix = "Tomorrow ";
      }
      return (
        prefix +
        this.hourFormat(time, "h:mm", includeSeconds, includePreposition)
      );
    }
    if (includePreposition) {
      prefix = "on ";
    }

    // If it's preciseTime (or preciseTimeSeconds), always show the hours and mins
    if (preciseTime || includeSeconds) {
      let hourStr = this.hourFormat(
        time,
        "h:mm",
        includeSeconds,
        includePreposition
      );
      // Include the year if it isn't the current year
      if (time.year() === moment().year()) {
        return prefix + time.format("MMM Do[,] ") + hourStr;
      } else {
        return prefix + time.format("YYYY-MM-DD ") + hourStr;
      }
    }
    // Otherwise, format in basic format
    if (time.year() === moment().year()) {
      if (includePreposition) {
        return prefix + time.format("MMM Do");
      } else {
        return prefix + time.format("MMM Do");
      }
    } else {
      return prefix + time.format("YYYY-MM-DD");
    }
  }

  preciseTimeSeconds(time) {
    return time.format("YYYY-MM-DD h:mm:ss a");
  }

  setHiddenTimezoneFields(el) {
    el.value = this.localTimezone;
  }

  parse(text) {
    // If time is only a number, parse as a timestamp
    // Otherwise, parse as ISO_8601 which is the default time string
    if (/^\d+$/.test(text)) {
      return moment.unix(text);
    } else if (text !== null) {
      return moment(text, moment.ISO_8601);
    }
    if (text === null || !time.isValid()) {
      return null;
    }
  }

  // timeFromText() is not used, included to make getting the text output separately easier
  timeFromText(
    text,
    preciseTime = false,
    includeSeconds = false,
    includePreposition = false
  ) {
    return this.localizedDateText(
      this.parse(text),
      preciseTime,
      includeSeconds,
      includePreposition
    );
  }

  writeTime(el) {
    const text = el.textContent.trim();
    const time = this.parse(text);
    // So running this again doesn't reapply to this element
    el.classList.remove("convertTime");
    // So we know which were updated (for styling, future updates, etc)
    el.classList.add("convertedTime");

    // If we couldn't parse the time, exit
    if (!(text.length > 0) || time === null) {
      return;
    }

    el.innerHTML = this.localizedDateText(
      time,
      el.classList.contains("preciseTime"),
      el.classList.contains("preciseTimeSeconds"),
      el.classList.contains("withPreposition")
    );
    el.setAttribute("title", this.preciseTimeSeconds(time));
  }

  writeTimezone(el) {
    el.textContent = moment().format("z");
    el.classList.remove("convertTimezone");
  }

  localize() {
    // Write times
    Array.from(document.getElementsByClassName("convertTime")).forEach((el) =>
      this.writeTime(el)
    );

    // Write timezones
    Array.from(
      document.getElementsByClassName("convertTimezone")
    ).forEach((el) => this.writeTimezone(el));

    // Write hidden timezone fields - so if we're submitting a form, it includes the current timezone
    Array.from(
      document.getElementsByClassName("hiddenFieldTimezone")
    ).forEach((el) => this.setHiddenTimezoneFields(el));
  }
}
<|endoftext|>


<commit_before>import React from 'react'
import { Switch, Route, withRouter } from 'react-router-dom'

import { connect } from 'react-redux'
import { isEmpty } from 'lodash'
import { setCurrentPage, setCurrentSection } from '../actions/navigation'
import {
  getConversations,
  updateConversationsData,
  clearConversations
} from '../actions/conversations'

import FilterMenu from '../components/FilterMenu'

import UserData from '../components/UserData'
import ConversationItemList from '../components/conversations/ItemList'
import AssignmentRules from '../components/conversations/AssignmentRules'
import Conversation from '../components/conversations/Conversation'
import Progress from '../components/Progress'
import EmptyView from '../components/EmptyView'
import Button from '../components/Button'
import emptyImage from '../images/empty-icon8.png'
// import {toCamelCase} from '../shared/caseConverter'

function Conversations ({
  dispatch,
  match,
  conversations,
  conversation,
  app,
  events,
  app_user
}) {
  let fetching = false

  React.useEffect(() => {
    dispatch(clearConversations([]))
    fetchConversations({ page: 1 })

    dispatch(setCurrentPage('Conversations'))

    dispatch(setCurrentSection('Conversations'))
  }, [])

  const fetchConversations = (options, cb) => {
    dispatch(
      getConversations(options, () => {
        cb && cb()
      })
    )
  }

  const setSort = (option) => {
    dispatch(updateConversationsData({ sort: option }))
    this.setState({ sort: option })
  }

  const handleScroll = (e) => {
    const element = e.target
    const scrollDiff = Math.round(element.scrollHeight - element.scrollTop)
    console.log(scrollDiff, element.clientHeight)

    if (scrollDiff === element.clientHeight) {
      if (conversations.meta.next_page && !fetching) {
        fetching = true
        fetchConversations({
          page: conversations.meta.next_page
        }, () => {
          fetching = false
        })
      }
    }
  }

  const setFilter = (option) => {
    dispatch(updateConversationsData({ filter: option }))
  }

  const filterButton = (handleClick) => {
    return (
      <Button
        aria-label="More"
        aria-controls="long-menu"
        aria-haspopup="true"
        variant={'outlined'}
        onClick={handleClick}
        size="small"
      >
        {/* <MoreVertIcon /> */}
        {conversations.filter}
      </Button>
    )
  }

  const sortButton = (handleClick) => {
    return (
      <Button
        aria-label="More"
        aria-controls="long-menu"
        aria-haspopup="true"
        variant={'outlined'}
        onClick={handleClick}
        size="small"
      >
        {/* <MoreVertIcon /> */}
        {conversations.sort}
      </Button>
    )
  }

  const filterConversations = (options, cb) => {
    dispatch(
      updateConversationsData(
        {
          filter: options.id,
          collection: []
        },
        () => {
          fetchConversations({ page: 1 }, cb)
          // getConversations({page: 1}, cb)
        }
      )
    )
  }

  const sortConversations = (options, cb) => {
    dispatch(
      updateConversationsData(
        {
          sort: options.id,
          collection: []
        },
        () => {
          fetchConversations({ page: 1 })
          // getConversations({page: 1}, cb)
        }
      )
    )
  }

  const renderConversations = () => {
    return (
      <React.Fragment>

          <FilterMenu
            options={[
              { id: 'opened', name: 'opened', count: 1, icon: null },
              { id: 'closed', name: 'closed', count: 2, icon: null }
            ]}
            value={conversations.filter}
            filterHandler={filterConversations}
            triggerButton={filterButton}
          />

          <FilterMenu
            options={[
              { id: 'newest', name: 'newest', count: 1, selected: true },
              { id: 'oldest', name: 'oldest', count: 1 },
              { id: 'waiting', name: 'waiting', count: 1 },
              { id: 'priority-first', name: 'priority first', count: 1 },
              { id: 'unfiltered', name: 'all', count: 1 }
            ]}
            position={'right'}
            value={conversations.sort}
            filterHandler={sortConversations}
            triggerButton={sortButton}
          />

          {conversations.collection.map((o) => {
            const user = o.mainParticipant
            return (
              <ConversationItemList
                key={o.key}
                user={user}
                app={app}
                conversation={o}
              />
            )
          })}

      </React.Fragment>
    )
  }

  return (

      <Switch>
        <Route exact path={`/apps/${app.key}/conversations`}>

            {renderConversations()}

        </Route>
      </Switch>

        {renderConversations()}

      <Switch>
        <Route exact path={`/apps/${app.key}/conversations`}>

            <EmptyView
              title={'No conversations'}
              shadowless
              image={

              }
              subtitle={'choose conversations on the side'}
            />

        </Route>

        <Route exact path={`/apps/${app.key}/conversations/assignment_rules`}>

            <AssignmentRules />

        </Route>

        <Route exact path={`/apps/${app.key}/conversations/:id`}>

            <Conversation events={events} />

        </Route>
      </Switch>

      {!isEmpty(conversation) && (

          {app_user && app_user.id ? (
            <UserData data={conversation.mainParticipant} />
          ) : (
            <Progress />
          )}

      )}

  )
}

function mapStateToProps (state) {
  const { auth, app, conversations, conversation, app_user } = state
  const { loading, isAuthenticated } = auth
  // const { sort, filter, collection , meta, loading} = conversations

  return {
    conversations,
    conversation,
    app_user,
    app,
    isAuthenticated
  }
}

export default withRouter(connect(mapStateToProps)(Conversations))
<commit_msg>Update Conversations.js<commit_after>import React from 'react'
import { Switch, Route, withRouter } from 'react-router-dom'

import { connect } from 'react-redux'
import { isEmpty } from 'lodash'
import { setCurrentPage, setCurrentSection } from '../actions/navigation'
import {
  getConversations,
  updateConversationsData,
  clearConversations
} from '../actions/conversations'

import FilterMenu from '../components/FilterMenu'

import UserData from '../components/UserData'
import ConversationItemList from '../components/conversations/ItemList'
import AssignmentRules from '../components/conversations/AssignmentRules'
import Conversation from '../components/conversations/Conversation'
import Progress from '../components/Progress'
import EmptyView from '../components/EmptyView'
import Button from '../components/Button'
import emptyImage from '../images/empty-icon8.png'
// import {toCamelCase} from '../shared/caseConverter'

function Conversations ({
  dispatch,
  match,
  conversations,
  conversation,
  app,
  events,
  app_user
}) {
  let fetching = false

  React.useEffect(() => {
    dispatch(clearConversations([]))
    fetchConversations({ page: 1 })

    dispatch(setCurrentPage('Conversations'))

    dispatch(setCurrentSection('Conversations'))
  }, [])

  const fetchConversations = (options, cb) => {
    dispatch(
      getConversations(options, () => {
        cb && cb()
      })
    )
  }

  const setSort = (option) => {
    dispatch(updateConversationsData({ sort: option }))
    this.setState({ sort: option })
  }

  const handleScroll = (e) => {
    const element = e.target
    const scrollDiff = Math.round(element.scrollHeight - element.scrollTop)
    if (scrollDiff === element.clientHeight) {
      if (conversations.meta.next_page && !fetching) {
        fetching = true
        fetchConversations({
          page: conversations.meta.next_page
        }, () => {
          fetching = false
        })
      }
    }
  }

  const setFilter = (option) => {
    dispatch(updateConversationsData({ filter: option }))
  }

  const filterButton = (handleClick) => {
    return (
      <Button
        aria-label="More"
        aria-controls="long-menu"
        aria-haspopup="true"
        variant={'outlined'}
        onClick={handleClick}
        size="small"
      >
        {/* <MoreVertIcon /> */}
        {conversations.filter}
      </Button>
    )
  }

  const sortButton = (handleClick) => {
    return (
      <Button
        aria-label="More"
        aria-controls="long-menu"
        aria-haspopup="true"
        variant={'outlined'}
        onClick={handleClick}
        size="small"
      >
        {/* <MoreVertIcon /> */}
        {conversations.sort}
      </Button>
    )
  }

  const filterConversations = (options, cb) => {
    dispatch(
      updateConversationsData(
        {
          filter: options.id,
          collection: []
        },
        () => {
          fetchConversations({ page: 1 }, cb)
          // getConversations({page: 1}, cb)
        }
      )
    )
  }

  const sortConversations = (options, cb) => {
    dispatch(
      updateConversationsData(
        {
          sort: options.id,
          collection: []
        },
        () => {
          fetchConversations({ page: 1 })
          // getConversations({page: 1}, cb)
        }
      )
    )
  }

  const renderConversations = () => {
    return (
      <React.Fragment>

          <FilterMenu
            options={[
              { id: 'opened', name: 'opened', count: 1, icon: null },
              { id: 'closed', name: 'closed', count: 2, icon: null }
            ]}
            value={conversations.filter}
            filterHandler={filterConversations}
            triggerButton={filterButton}
          />

          <FilterMenu
            options={[
              { id: 'newest', name: 'newest', count: 1, selected: true },
              { id: 'oldest', name: 'oldest', count: 1 },
              { id: 'waiting', name: 'waiting', count: 1 },
              { id: 'priority-first', name: 'priority first', count: 1 },
              { id: 'unfiltered', name: 'all', count: 1 }
            ]}
            position={'right'}
            value={conversations.sort}
            filterHandler={sortConversations}
            triggerButton={sortButton}
          />

          {conversations.collection.map((o) => {
            const user = o.mainParticipant
            return (
              <ConversationItemList
                key={o.key}
                user={user}
                app={app}
                conversation={o}
              />
            )
          })}

      </React.Fragment>
    )
  }

  return (

      <Switch>
        <Route exact path={`/apps/${app.key}/conversations`}>

            {renderConversations()}

        </Route>
      </Switch>

        {renderConversations()}

      <Switch>
        <Route exact path={`/apps/${app.key}/conversations`}>

            <EmptyView
              title={'No conversations'}
              shadowless
              image={

              }
              subtitle={'choose conversations on the side'}
            />

        </Route>

        <Route exact path={`/apps/${app.key}/conversations/assignment_rules`}>

            <AssignmentRules />

        </Route>

        <Route exact path={`/apps/${app.key}/conversations/:id`}>

            <Conversation events={events} />

        </Route>
      </Switch>

      {!isEmpty(conversation) && (

          {app_user && app_user.id ? (
            <UserData data={conversation.mainParticipant} />
          ) : (
            <Progress />
          )}

      )}

  )
}

function mapStateToProps (state) {
  const { auth, app, conversations, conversation, app_user } = state
  const { loading, isAuthenticated } = auth
  // const { sort, filter, collection , meta, loading} = conversations

  return {
    conversations,
    conversation,
    app_user,
    app,
    isAuthenticated
  }
}

export default withRouter(connect(mapStateToProps)(Conversations))
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas 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 Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import I18n from 'i18n!permissions_v2_add_tray'

import {connect} from 'react-redux'
import PropTypes from 'prop-types'
import React, {Component} from 'react'

import {COURSE} from '../propTypes'

import Button from '@instructure/ui-buttons/lib/components/Button'
import Container from '@instructure/ui-layout/lib/components/View'
import Flex, {FlexItem} from '@instructure/ui-layout/lib/components/Flex'
import Heading from '@instructure/ui-elements/lib/components/Heading'
import IconX from '@instructure/ui-icons/lib/Solid/IconX'
import Text from '@instructure/ui-elements/lib/components/Text'
import Select from '@instructure/ui-core/lib/components/Select'
import TextInput from '@instructure/ui-forms/lib/components/TextInput'
import Tray from '@instructure/ui-overlays/lib/components/Tray'
import Spinner from '@instructure/ui-elements/lib/components/Spinner'

import actions from '../actions'

import {roleIsCourseBaseRole} from '../helper/utils'

export default class AddTray extends Component {
  static propTypes = {
    allBaseRoles: PropTypes.arrayOf(PropTypes.object).isRequired,
    hideTray: PropTypes.func.isRequired,
    createNewRole: PropTypes.func.isRequired,
    open: PropTypes.bool.isRequired,
    loading: PropTypes.bool.isRequired,
    tab: PropTypes.string.isRequired
  }

  constructor(props) {
    super(props)
    this.state = {
      selectedRoleName: '',
      selectedBaseType: this.props.allBaseRoles[0] || {label: ''}
    }
  }

  componentWillReceiveProps(newProps) {
    if (!this.props.loading) {
      this.setState({
        selectedRoleName: '',
        selectedBaseType: newProps.allBaseRoles[0] || {label: ''}
      })
    }
  }

  onChangeRoleName = event => {
    this.setState({selectedRoleName: event.target.value})
  }

  onChangeBaseType = event => {
    const foundRole = this.props.allBaseRoles.find(element => element.label === event.target.value)
    this.setState({
      selectedBaseType: foundRole
    })
  }

  hideTray = () => {
    this.setState({
      selectedRoleName: '',
      selectedBaseType: this.props.allBaseRoles[0] || {label: ''}
    })
    this.props.hideTray()
  }

  handleSaveButton = () => {
    const newRole = this.state.selectedBaseType
    newRole.base_role_type =
      this.props.tab === COURSE ? newRole.base_role_type : 'AccountMembership'
    this.props.createNewRole(this.state.selectedRoleName, newRole)
  }

  isDoneSelecting = () => !!this.state.selectedRoleName

  renderTrayHeader = () => (
    <Flex alignItems="center" margin="small">
      <FlexItem>
        <Button
          variant="icon"
          size="small"
          onClick={this.hideTray}
          buttonRef={c => (this.closeButton = c)}
        >
          <IconX>Close</IconX>
        </Button>
      </FlexItem>
      <FlexItem>
        <Container as="div" margin="0 0 0 small">
          <Heading level="h3" as="h2">
            {I18n.t('New Course Role')}
          </Heading>
        </Container>
      </FlexItem>
    </Flex>
  )

  renderSelectRoleName = () => (
    <Container display="block" margin="medium 0">
      <TextInput
        onChange={this.onChangeRoleName}
        id="add_role_input"
        value={this.state.selectedRoleName}
        label={<Text weight="light">{`${I18n.t('Role Name')}:`}</Text>}
      />
    </Container>
  )

  renderSelectBaseRole = () => (
    <Container display="block" margin="medium 0">
      <Select
        onChange={this.onChangeBaseType}
        value={this.state.selectedBaseType.label}
        label={<Text weight="light">{`${I18n.t('Base Type')}:`}</Text>}
      >
        {this.props.allBaseRoles.map(item => (
          <option key={item.label} value={item.label}>
            {item.label}
          </option>
        ))}
      </Select>
    </Container>
  )

  renderTrayFooter() {
    return (

        <Container textAlign="end" display="block">

          <Button
            id="permissions-add-tray-cancel-button"
            onClick={this.props.hideTray}
            margin="0 x-small 0 0"
          >
            {I18n.t('Cancel')}
          </Button>
          <Button
            id="permissions-add-tray-submit-button"
            disabled={!this.isDoneSelecting()}
            type="submit"
            variant="primary"
            onClick={this.handleSaveButton}
            margin="0 x-small 0 0"
          >
            {I18n.t('Save')}
          </Button>
        </Container>

    )
  }

  renderLoadingIndicator() {
    return (
      <Container display="block" margin="auto">
        <Spinner size="large" title={I18n.t('Saving New Role')} />
      </Container>
    )
  }

  render() {
    return (
      <Tray
        label={I18n.t('New Course Role')}
        open={this.props.open}
        onDismiss={this.hideTray}
        size="small"
        placement="end"
      >
        {this.renderTrayHeader()}
        {this.props.loading ? (
          this.renderLoadingIndicator()
        ) : (
          <Container>
            <Container as="div" padding="small small x-large small">
              {this.renderSelectRoleName()}
              {this.props.tab === COURSE && this.renderSelectBaseRole()}
            </Container>
            {this.renderTrayFooter()}
          </Container>
        )}
      </Tray>
    )
  }
}

export function mapStateToProps(state, ownProps) {
  if (state.activeAddTray && !state.activeAddTray.show) {
    const stateProps = {
      allBaseRoles: [],
      open: false,
      loading: false,
      tab: COURSE
    }
    return {...stateProps, ...ownProps}
  }

  const allBaseRoles = state.roles.reduce((acc, r) => {
    if (roleIsCourseBaseRole(r)) {
      acc.push(r)
    }
    return acc
  }, [])

  const stateProps = {
    allBaseRoles,
    open: true,
    loading: state.activeAddTray && state.activeAddTray.loading,
    tab: state.roles.find(role => !!role.displayed).contextType
  }
  return {...ownProps, ...stateProps}
}

const mapDispatchToProps = {
  createNewRole: actions.createNewRole,
  hideTray: actions.hideAllTrays
}

export const ConnectedAddTray = connect(
  mapStateToProps,
  mapDispatchToProps
)(AddTray)
<commit_msg>Fix ally issues on create role tray<commit_after>/*
 * Copyright (C) 2018 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas 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 Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import I18n from 'i18n!permissions_v2_add_tray'

import {connect} from 'react-redux'
import PropTypes from 'prop-types'
import React, {Component} from 'react'

import {COURSE} from '../propTypes'

import Button from '@instructure/ui-buttons/lib/components/Button'
import Container from '@instructure/ui-layout/lib/components/View'
import Flex, {FlexItem} from '@instructure/ui-layout/lib/components/Flex'
import Heading from '@instructure/ui-elements/lib/components/Heading'
import IconX from '@instructure/ui-icons/lib/Solid/IconX'
import Text from '@instructure/ui-elements/lib/components/Text'
import Select from '@instructure/ui-core/lib/components/Select'
import TextInput from '@instructure/ui-forms/lib/components/TextInput'
import Tray from '@instructure/ui-overlays/lib/components/Tray'
import Spinner from '@instructure/ui-elements/lib/components/Spinner'

import actions from '../actions'

import {roleIsCourseBaseRole} from '../helper/utils'

export default class AddTray extends Component {
  static propTypes = {
    allBaseRoles: PropTypes.arrayOf(PropTypes.object).isRequired,
    hideTray: PropTypes.func.isRequired,
    createNewRole: PropTypes.func.isRequired,
    open: PropTypes.bool.isRequired,
    loading: PropTypes.bool.isRequired,
    tab: PropTypes.string.isRequired
  }

  constructor(props) {
    super(props)
    this.state = {
      selectedRoleName: '',
      selectedBaseType: this.props.allBaseRoles[0] || {label: ''}
    }
  }

  componentWillReceiveProps(newProps) {
    if (!this.props.loading) {
      this.setState({
        selectedRoleName: '',
        selectedBaseType: newProps.allBaseRoles[0] || {label: ''}
      })
    }
  }

  onChangeRoleName = event => {
    this.setState({selectedRoleName: event.target.value})
  }

  onChangeBaseType = event => {
    const foundRole = this.props.allBaseRoles.find(element => element.label === event.target.value)
    this.setState({
      selectedBaseType: foundRole
    })
  }

  hideTray = () => {
    this.setState({
      selectedRoleName: '',
      selectedBaseType: this.props.allBaseRoles[0] || {label: ''}
    })
    this.props.hideTray()
  }

  handleSaveButton = () => {
    const newRole = this.state.selectedBaseType
    newRole.base_role_type =
      this.props.tab === COURSE ? newRole.base_role_type : 'AccountMembership'
    this.props.createNewRole(this.state.selectedRoleName, newRole)
  }

  isDoneSelecting = () => !!this.state.selectedRoleName

  renderTrayHeader = () => (
    <Flex alignItems="center" margin="small">
      <FlexItem>
        <Button
          variant="icon"
          size="small"
          onClick={this.hideTray}
          buttonRef={c => (this.closeButton = c)}
        >
          <IconX title={I18n.t('Close')} />
        </Button>
      </FlexItem>
      <FlexItem>
        <Container as="div" margin="0 0 0 small">
          <Heading level="h3" as="h2">
            {this.props.tab === COURSE ? I18n.t('New Course Role') : I18n.t('New Account Role')}
          </Heading>
        </Container>
      </FlexItem>
    </Flex>
  )

  renderSelectRoleName = () => (
    <Container display="block" margin="medium 0">
      <TextInput
        onChange={this.onChangeRoleName}
        id="add_role_input"
        value={this.state.selectedRoleName}
        label={<Text weight="light">{`${I18n.t('Role Name')}:`}</Text>}
      />
    </Container>
  )

  renderSelectBaseRole = () => (
    <Container display="block" margin="medium 0">
      <Select
        onChange={this.onChangeBaseType}
        value={this.state.selectedBaseType.label}
        label={<Text weight="light">{`${I18n.t('Base Type')}:`}</Text>}
      >
        {this.props.allBaseRoles.map(item => (
          <option key={item.label} value={item.label}>
            {item.label}
          </option>
        ))}
      </Select>
    </Container>
  )

  renderTrayFooter() {
    return (

        <Container textAlign="end" display="block">

          <Button
            id="permissions-add-tray-cancel-button"
            onClick={this.props.hideTray}
            margin="0 x-small 0 0"
          >
            {I18n.t('Cancel')}
          </Button>
          <Button
            id="permissions-add-tray-submit-button"
            disabled={!this.isDoneSelecting()}
            type="submit"
            variant="primary"
            onClick={this.handleSaveButton}
            margin="0 x-small 0 0"
          >
            {I18n.t('Save')}
          </Button>
        </Container>

    )
  }

  renderLoadingIndicator() {
    return (
      <Container display="block" margin="auto">
        <Spinner size="large" title={I18n.t('Saving New Role')} />
      </Container>
    )
  }

  render() {
    return (
      <Tray
        label={I18n.t('New Course Role')}
        open={this.props.open}
        onDismiss={this.hideTray}
        size="small"
        placement="end"
      >
        {this.renderTrayHeader()}
        {this.props.loading ? (
          this.renderLoadingIndicator()
        ) : (
          <Container>
            <Container as="div" padding="small small x-large small">
              {this.renderSelectRoleName()}
              {this.props.tab === COURSE && this.renderSelectBaseRole()}
            </Container>
            {this.renderTrayFooter()}
          </Container>
        )}
      </Tray>
    )
  }
}

export function mapStateToProps(state, ownProps) {
  if (state.activeAddTray && !state.activeAddTray.show) {
    const stateProps = {
      allBaseRoles: [],
      open: false,
      loading: false,
      tab: COURSE
    }
    return {...stateProps, ...ownProps}
  }

  const allBaseRoles = state.roles.reduce((acc, r) => {
    if (roleIsCourseBaseRole(r)) {
      acc.push(r)
    }
    return acc
  }, [])

  const stateProps = {
    allBaseRoles,
    open: true,
    loading: state.activeAddTray && state.activeAddTray.loading,
    tab: state.roles.find(role => !!role.displayed).contextType
  }
  return {...ownProps, ...stateProps}
}

const mapDispatchToProps = {
  createNewRole: actions.createNewRole,
  hideTray: actions.hideAllTrays
}

export const ConnectedAddTray = connect(
  mapStateToProps,
  mapDispatchToProps
)(AddTray)
<|endoftext|>


<commit_before>sap.ui.define([
	'sap/ui/test/Opa5',
	'sap/ui/test/matchers/Properties',
	'sap/ui/test/matchers/PropertyStrictEquals',
	'sap/ui/test/matchers/AggregationLengthEquals',
	'sap/ui/test/matchers/AggregationFilled',
	'sap/ui/test/matchers/Ancestor',
	'sap/ui/test/matchers/Visible',
	'sap/ui/test/actions/Press'
], function (Opa5, Properties, PropertyStrictEquals, AggregationLengthEquals, AggregationFilled, Ancestor, Visible, Press) {
	"use strict";

	Opa5.createPageObjects({
		onTheApiDetailPage: {
			viewName: "ApiDetail",
			actions: {

				iSelectASection: function(sSectionName) {
					return this.waitFor({
						controlType: "sap.m.Button",
						matchers: [
							new Properties({text: sSectionName})
						],
						actions: new Press(),
						errorMessage: "Section button for " + sSectionName + " not found."
					});
				},

				iSelectALink: function(sLinkName) {
					return this.waitFor({
						controlType: "sap.m.Link",
						matchers: new Properties({text: sLinkName}),
						actions: new Press(),
						errorMessage: "Link" + sLinkName + " not found."
					});
				}

			},

			assertions: {

				iShouldSeeTheApiDetailPage: function() {
					return this.waitFor({
						success: function () {
							Opa5.assert.ok(true, "The Api Detail page was successfully displayed.");
						},
						errorMessage: "The Api Detail page was not displayed."
					});
				},

				iShouldSeeTheApiDetailObjectPage: function() {
					return this.waitFor({
						id: "apiDetailObjectPage",
						success: function() {
							Opa5.assert.ok(true, "The apiDetailObjectPage was successfully displayed.");
						},
						errorMessage: "The apiDetailObjectPage was not displayed."
					});
				},

				iShouldSeeTheCorrectTitleAndSubtitle: function (sTitle, sSubtitle) {
					return this.waitFor({
						id: "title",
						matchers: new Properties({objectTitle: sTitle, objectSubtitle: sSubtitle}),
						success: function() {
							Opa5.assert.ok(true, "Title: " + sTitle + " and Subtitle: " + sSubtitle + "  found.");
						},
						errorMessage: "Title: " + sTitle + " and Subtitle: " + sSubtitle + "  not found."
					});
				},

				iShouldSeeTheElementDetailsInHeaderContent: function () {
					return this.waitFor({
						controlType: "sap.uxap.ObjectPageHeaderContent",
						success: function(aContent) {
							var oHeaderContent =  aContent[0];
							if (oHeaderContent) {
								this.waitFor({
									controlType: "sap.ui.layout.VerticalLayout",
									matchers: new Ancestor(oHeaderContent),
									success: function(aLayouts) {

										this.waitFor({
											controlType: "sap.m.Label",
											matchers: new Ancestor(aLayouts[0]),
											success: function(aLabels) {
												Opa5.assert.strictEqual(aLabels.length, 3, "There are three labels in the first column");
											}
										});
										this.waitFor({
											controlType: "sap.m.ObjectAttribute",
											matchers: new Ancestor(aLayouts[1]),
											success: function(aObjectAttributes) {
												Opa5.assert.strictEqual(aObjectAttributes.length, 3, "There are three ObjectAttributes in the second column");
											}
										});
										this.waitFor({
											controlType: "sap.m.ObjectAttribute",
											matchers: new Ancestor(aLayouts[2]),
											success: function(aObjectAttributes) {
												Opa5.assert.strictEqual(aObjectAttributes.length, 1, "There is one ObjectAttribute in the third column");
											}
										});

									},
									errorMessage: "sap.ui.layout.VerticalLayout  not found."

								});
							}
							Opa5.assert.ok(aContent.length === 1, "There is only one Header content");
							Opa5.assert.ok(true, "sap.uxap.ObjectPageHeaderContent found");
						},
						errorMessage: "sap.uxap.ObjectPageHeaderContent  not found."
					});
				},

				iShouldSeeTheseSections: function () {
					var aExpectedSections = Array.from(arguments);
					return this.waitFor({
						id: "apiDetailObjectPage",
						success: function(oObjectPage) {
							var iVisibleSections = 0;
							oObjectPage.getSections().forEach(function(oSection) {
								if (oSection.getVisible()) {
									iVisibleSections++;
									Opa5.assert.ok(aExpectedSections.includes(oSection.getTitle()), oSection.getTitle() + " section found.");
								}
							});
							Opa5.assert.strictEqual(iVisibleSections, aExpectedSections.length, "There are " + iVisibleSections + " visible sections.");
						},
						errorMessage: "Cannot find the expected sections."
					});
				},

				iShouldSeeTheCorrectSectionSelected: function(sSectionName) {
					return this.waitFor({
						id: "apiDetailObjectPage",
						success: function(oObjectPage) {
							var sSelectedSectionId = oObjectPage.getSelectedSection();
							this.waitFor({
								id: sSelectedSectionId,
								success: function(oSection) {
									Opa5.assert.strictEqual(oSection.getTitle(), sSectionName, "Section " + sSectionName + " is selected.");
								},
								errorMessage: "Subsection " + sSelectedSectionId + " not found."
							});
						},
						errorMessage: "sap.uxap.ObjectPageHeaderContent  not found."
					});
				},

				iShouldSeeTheCorrectSubSectionOnTop: function(sSubSectionName) {
					return this.waitFor({
						id: "apiDetailObjectPage",
						success: function(oObjectPage) {
							this.waitFor({
								controlType: "sap.uxap.ObjectPageSubSection",
								matchers: new Properties({title: sSubSectionName}),
								success: function(oSubSections) {
									var oSubSection = oSubSections[0],
										oSectionInfo = oObjectPage._oSectionInfo[oSubSection.getId()],
										iScrollPosition = oObjectPage._$opWrapper[0].scrollTop;
									Opa5.assert.ok(oSubSections.length === 1, "There is only one " + sSubSectionName + " SubSection");
									Opa5.assert.ok(iScrollPosition === oSectionInfo.positionTop, sSubSectionName + " is on top");
								},
								errorMessage: "SubSection " + sSubSectionName + " not found."
							});
						},
						errorMessage: "apiDetailObjectPage  not found."
					});
				}

			}
		}
	});

});
<commit_msg>[INTERNAL] sap.ui.documentation: OPA - API Ref. Journey adjusted<commit_after>sap.ui.define([
	'sap/ui/test/Opa5',
	'sap/ui/test/matchers/Properties',
	'sap/ui/test/matchers/PropertyStrictEquals',
	'sap/ui/test/matchers/AggregationLengthEquals',
	'sap/ui/test/matchers/AggregationFilled',
	'sap/ui/test/matchers/Ancestor',
	'sap/ui/test/matchers/Visible',
	'sap/ui/test/actions/Press'
], function (Opa5, Properties, PropertyStrictEquals, AggregationLengthEquals, AggregationFilled, Ancestor, Visible, Press) {
	"use strict";

	Opa5.createPageObjects({
		onTheApiDetailPage: {
			viewName: "ApiDetail",
			actions: {

				iSelectASection: function(sSectionName) {
					return this.waitFor({
						controlType: "sap.m.Button",
						matchers: [
							new Properties({text: sSectionName})
						],
						actions: new Press(),
						errorMessage: "Section button for " + sSectionName + " not found."
					});
				},

				iSelectALink: function(sLinkName) {
					return this.waitFor({
						controlType: "sap.m.Link",
						matchers: new Properties({text: sLinkName}),
						//actions: new Press(),
						// Since Change 3002279, Links no longer navigate correctly when used with OPA Press action,
						// scrolling to wrong entity.
						// Using firePress as workaround
						success: function (aLink) {
							aLink[0].firePress();
						},
						errorMessage: "Link" + sLinkName + " not found."
					});
				}

			},

			assertions: {

				iShouldSeeTheApiDetailPage: function() {
					return this.waitFor({
						success: function () {
							Opa5.assert.ok(true, "The Api Detail page was successfully displayed.");
						},
						errorMessage: "The Api Detail page was not displayed."
					});
				},

				iShouldSeeTheApiDetailObjectPage: function() {
					return this.waitFor({
						id: "apiDetailObjectPage",
						success: function() {
							Opa5.assert.ok(true, "The apiDetailObjectPage was successfully displayed.");
						},
						errorMessage: "The apiDetailObjectPage was not displayed."
					});
				},

				iShouldSeeTheCorrectTitleAndSubtitle: function (sTitle, sSubtitle) {
					return this.waitFor({
						id: "title",
						matchers: new Properties({objectTitle: sTitle, objectSubtitle: sSubtitle}),
						success: function() {
							Opa5.assert.ok(true, "Title: " + sTitle + " and Subtitle: " + sSubtitle + "  found.");
						},
						errorMessage: "Title: " + sTitle + " and Subtitle: " + sSubtitle + "  not found."
					});
				},

				iShouldSeeTheElementDetailsInHeaderContent: function () {
					return this.waitFor({
						controlType: "sap.uxap.ObjectPageHeaderContent",
						success: function(aContent) {
							var oHeaderContent =  aContent[0];
							if (oHeaderContent) {
								this.waitFor({
									controlType: "sap.ui.layout.VerticalLayout",
									matchers: new Ancestor(oHeaderContent),
									success: function(aLayouts) {

										this.waitFor({
											controlType: "sap.m.Label",
											matchers: new Ancestor(aLayouts[0]),
											success: function(aLabels) {
												Opa5.assert.strictEqual(aLabels.length, 3, "There are three labels in the first column");
											}
										});
										this.waitFor({
											controlType: "sap.m.ObjectAttribute",
											matchers: new Ancestor(aLayouts[1]),
											success: function(aObjectAttributes) {
												Opa5.assert.strictEqual(aObjectAttributes.length, 3, "There are three ObjectAttributes in the second column");
											}
										});
										this.waitFor({
											controlType: "sap.m.ObjectAttribute",
											matchers: new Ancestor(aLayouts[2]),
											success: function(aObjectAttributes) {
												Opa5.assert.strictEqual(aObjectAttributes.length, 1, "There is one ObjectAttribute in the third column");
											}
										});

									},
									errorMessage: "sap.ui.layout.VerticalLayout  not found."

								});
							}
							Opa5.assert.ok(aContent.length === 1, "There is only one Header content");
							Opa5.assert.ok(true, "sap.uxap.ObjectPageHeaderContent found");
						},
						errorMessage: "sap.uxap.ObjectPageHeaderContent  not found."
					});
				},

				iShouldSeeTheseSections: function () {
					var aExpectedSections = Array.from(arguments);
					return this.waitFor({
						id: "apiDetailObjectPage",
						success: function(oObjectPage) {
							var iVisibleSections = 0;
							oObjectPage.getSections().forEach(function(oSection) {
								if (oSection.getVisible()) {
									iVisibleSections++;
									Opa5.assert.ok(aExpectedSections.includes(oSection.getTitle()), oSection.getTitle() + " section found.");
								}
							});
							Opa5.assert.strictEqual(iVisibleSections, aExpectedSections.length, "There are " + iVisibleSections + " visible sections.");
						},
						errorMessage: "Cannot find the expected sections."
					});
				},

				iShouldSeeTheCorrectSectionSelected: function(sSectionName) {
					return this.waitFor({
						id: "apiDetailObjectPage",
						success: function(oObjectPage) {
							var sSelectedSectionId = oObjectPage.getSelectedSection();
							this.waitFor({
								id: sSelectedSectionId,
								success: function(oSection) {
									Opa5.assert.strictEqual(oSection.getTitle(), sSectionName, "Section " + sSectionName + " is selected.");
								},
								errorMessage: "Subsection " + sSelectedSectionId + " not found."
							});
						},
						errorMessage: "sap.uxap.ObjectPageHeaderContent  not found."
					});
				},

				iShouldSeeTheCorrectSubSectionOnTop: function(sSubSectionName) {
					return this.waitFor({
						id: "apiDetailObjectPage",
						success: function(oObjectPage) {
							this.waitFor({
								controlType: "sap.uxap.ObjectPageSubSection",
								matchers: new Properties({title: sSubSectionName}),
								success: function(oSubSections) {
									var oSubSection = oSubSections[0],
										oSectionInfo = oObjectPage._oSectionInfo[oSubSection.getId()],
										iScrollPosition = oObjectPage._$opWrapper[0].scrollTop;
									Opa5.assert.ok(oSubSections.length === 1, "There is only one " + sSubSectionName + " SubSection");
									Opa5.assert.ok(iScrollPosition === oSectionInfo.positionTop, sSubSectionName + " is on top");
								},
								errorMessage: "SubSection " + sSubSectionName + " not found."
							});
						},
						errorMessage: "apiDetailObjectPage  not found."
					});
				}

			}
		}
	});

});
<|endoftext|>


<commit_before>'use strict';

var servers = {
	localHub: 'https://local.xena.ucsc.edu:7223',
	publicHub: 'https://ucscpublic.xenahubs.net',
	tcgaHub: 'https://tcga.xenahubs.net',
	icgcHub: 'https://icgc.xenahubs.net',
	toilHub: 'https://toil.xenahubs.net',
	pcawgHub: 'https://pcawg.xenahubs.net',
	singlecellHub: 'https://singlecell.xenahubs.net',
	pancanAtlasHub: 'https://pancanatlas.xenahubs.net',
	treehouseHub: 'https://xena.treehouse.gi.ucsc.edu',
	gdcHub: "https://gdc.xenahubs.net"
};

module.exports = {
	servers,
	serverNames: {
		[servers.localHub]: "My computer hub",
		[servers.publicHub]: 'UCSC public hub',
		[servers.tcgaHub]: 'TCGA hub',
		[servers.icgcHub]: 'ICGC hub',
		[servers.toilHub]: 'UCSC Toil RNAseq Recompute',
		[servers.pcawgHub]: 'PCAWG public hub',
		[servers.singlecellHub]: 'Single-cell RNAseq hub',
		[servers.pancanAtlasHub]: 'Pan-Cancer Atlas Hub',
		[servers.treehouseHub]: 'Treehouse Hub',
		[servers.gdcHub]: 'GDC Hub'
	},
	defaultServers: [
		servers.localHub,
		servers.publicHub,
		servers.tcgaHub,
		servers.pancanAtlasHub,
		servers.icgcHub,
		servers.toilHub,
//		servers.treehouseHub,
		servers.gdcHub
	],
	publicServers: [
		servers.publicHub,
		servers.tcgaHub,
		servers.icgcHub,
		servers.toilHub,
		servers.pancanAtlasHub,
		servers.pcawgHub,
//		servers.treehouseHub,
		servers.gdcHub
	]
};
<commit_msg>Revert "Remove treehouse hub while it's down."<commit_after>'use strict';

var servers = {
	localHub: 'https://local.xena.ucsc.edu:7223',
	publicHub: 'https://ucscpublic.xenahubs.net',
	tcgaHub: 'https://tcga.xenahubs.net',
	icgcHub: 'https://icgc.xenahubs.net',
	toilHub: 'https://toil.xenahubs.net',
	pcawgHub: 'https://pcawg.xenahubs.net',
	singlecellHub: 'https://singlecell.xenahubs.net',
	pancanAtlasHub: 'https://pancanatlas.xenahubs.net',
	treehouseHub: 'https://xena.treehouse.gi.ucsc.edu',
	gdcHub: "https://gdc.xenahubs.net"
};

module.exports = {
	servers,
	serverNames: {
		[servers.localHub]: "My computer hub",
		[servers.publicHub]: 'UCSC public hub',
		[servers.tcgaHub]: 'TCGA hub',
		[servers.icgcHub]: 'ICGC hub',
		[servers.toilHub]: 'UCSC Toil RNAseq Recompute',
		[servers.pcawgHub]: 'PCAWG public hub',
		[servers.singlecellHub]: 'Single-cell RNAseq hub',
		[servers.pancanAtlasHub]: 'Pan-Cancer Atlas Hub',
		[servers.treehouseHub]: 'Treehouse Hub',
		[servers.gdcHub]: 'GDC Hub'
	},
	defaultServers: [
		servers.localHub,
		servers.publicHub,
		servers.tcgaHub,
		servers.pancanAtlasHub,
		servers.icgcHub,
		servers.toilHub,
		servers.treehouseHub,
		servers.gdcHub
	],
	publicServers: [
		servers.publicHub,
		servers.tcgaHub,
		servers.icgcHub,
		servers.toilHub,
		servers.pancanAtlasHub,
		servers.pcawgHub,
		servers.treehouseHub,
		servers.gdcHub
	]
};
<|endoftext|>


<commit_before>import React from 'react';
import assert from 'assert';
import { shallow, mount } from 'enzyme';
import sinon from 'sinon';
import apiClient from 'panoptes-client/lib/api-client';
import SocialLinksEditor from './social-links-editor';

function mockPanoptesResource(type, options) {
  const resource = apiClient.type(type).create(options);
  apiClient._typesCache = {};
  sinon.stub(resource, 'save').callsFake(() => Promise.resolve(resource));
  sinon.stub(resource, 'get');
  sinon.stub(resource, 'delete');
  return resource;
}

const testProject = {
  urls: [
    {
      label: 'Example1',
      url: 'https://example.com/1'
    },
    {
      label: '',
      url: 'https://facebook.com/2',
      site: 'facebook.com/',
      path: 'test'
    }
  ]
};

describe('SocialLinksEditor', () => {
  let wrapper;

  it('should render without crashing', () => {
    shallow(<SocialLinksEditor project={mockPanoptesResource('projects', testProject)} />);
  });

  before(function () {
    wrapper = mount(<SocialLinksEditor project={mockPanoptesResource('projects', testProject)} />);
  });

  it('should contain the correct number of rows', () => {
    const rows = wrapper.find('tr');
    assert.equal(rows.length, 13);
  });

  it('should rearrange the default social links on load', () => {
    const rearrangedLinks = wrapper.state().socialOrder;
    assert.equal(rearrangedLinks[0], 'facebook.com/');
  });

  it('should correctly display paths', () => {
    const input = wrapper.find('input').first();
    assert.equal(input.props().value, 'test');
  });

  it('should call handleRemoveLink() when link is removed', () => {
    const removeStub = sinon.stub(wrapper.instance(), 'handleRemoveLink');
    const button = wrapper.find('button').first();
    wrapper.update();
    button.simulate('click');
    sinon.assert.calledOnce(removeStub);
  });
});
<commit_msg>Fix social links editor tests Stub event handlers on the object prototype, rather than on an individual instance.<commit_after>import React from 'react';
import assert from 'assert';
import { shallow, mount } from 'enzyme';
import sinon from 'sinon';
import apiClient from 'panoptes-client/lib/api-client';
import SocialLinksEditor from './social-links-editor';

function mockPanoptesResource(type, options) {
  const resource = apiClient.type(type).create(options);
  apiClient._typesCache = {};
  sinon.stub(resource, 'save').callsFake(() => Promise.resolve(resource));
  sinon.stub(resource, 'get');
  sinon.stub(resource, 'delete');
  return resource;
}

const testProject = {
  urls: [
    {
      label: 'Example1',
      url: 'https://example.com/1'
    },
    {
      label: '',
      url: 'https://facebook.com/2',
      site: 'facebook.com/',
      path: 'test'
    }
  ]
};

describe('SocialLinksEditor', () => {
  let wrapper;
  let removeStub;

  it('should render without crashing', () => {
    shallow(<SocialLinksEditor project={mockPanoptesResource('projects', testProject)} />);
  });

  before(function () {
    removeStub = sinon.stub(SocialLinksEditor.prototype, 'handleRemoveLink');
    wrapper = mount(<SocialLinksEditor project={mockPanoptesResource('projects', testProject)} />);
  });

  after(function () {
    removeStub.restore();
  });

  it('should contain the correct number of rows', () => {
    const rows = wrapper.find('tr');
    assert.equal(rows.length, 13);
  });

  it('should rearrange the default social links on load', () => {
    const rearrangedLinks = wrapper.state().socialOrder;
    assert.equal(rearrangedLinks[0], 'facebook.com/');
  });

  it('should correctly display paths', () => {
    const input = wrapper.find('input').first();
    assert.equal(input.props().value, 'test');
  });

  it('should call handleRemoveLink() when link is removed', () => {
    const button = wrapper.find('button').first();
    button.simulate('click');
    sinon.assert.calledOnce(removeStub);
  });
});
<|endoftext|>


<commit_before>(function($){

	$.fn.df_edit = function(options){
		var defs = $.extend({}, options);

		var models = {
			view: {
				keyval: function(key, value){
				return  '
' +
							'
"' + key + '" :
' +
							'
"' + value + '"
' +
						'
';
				},
				item: function(type, data){
					var item = '
' +
									'
' +
										'
{
' +
										'
<button class="close" title="Delete">✖</button>
' +
									'
';
									for(key of Object.keys(data)){
										item += this.keyval(key, data[key]);
									}
					item += 		'
},
' +
								'
';
					return item;

				}
			},
			edit: {
				item: function(type, data){
				// 	var item = '
';
				// 	for(key of Object.keys(data)){
				// 		item += this.keyval(key, data[key]);
				// 	}
				// 		<input type="text" autocomplete="off" name="name"  placeholder="Name">

				// 		<input type="text" autocomplete="off" name="qname"  placeholder="Queue name">

				// 		<input type="text" autocomplete="off" name="location"  placeholder="Location">

				// 		<input type="text" autocomplete="off" name="group"  placeholder="Group">

				// 		<input type="text" autocomplete="off" name="driver"  placeholder="Driver">

				// 		<input type="button" class="submit" autocomplete="off" name="submit" value="Submit">

				//

				}

			}

		};

		var handlers = {
			destroy: function(){
				$(this)
				.parents().eq(2)
				.animate({
					height: 0,
					width: 0,
					opacity: 0
				}, 250, function(){
					$(this).remove();
				});
			},
			submit: function(){
				var x = $(this).parents().eq(2);
				$('input, select', x).each(function(){
					if($(this).hasClass('df_btn_add')){

						$(this).parents().eq(1).animate({
							width: 0
						}, 250, function(){
							$(this).remove();
						});

					}else{
						$(this).animate({
							width: 0
						}, 250, function(){
							// $(this).remove();
							var value = $(this).val();
							var type = $(this);
							var parent = $(this).parent();
							$(parent).html('');
							$(parent)
								.append('"' + value + '"');
								// .animate({

								// }, 250);

						});
					}
				})
				// $(this)
				// .parents().eq(2).animate({
				// 	height: 0,
				// 	width: 0,
				// 	opacity: 0
				// }, 250);
			}
		};

		$('.df_btn_add').on('click', handlers.submit);
		// var structures = {
		// 	printer : {

		// 	}
		// }
		var converter = { name : 'Name', qname : 'Queue name', location : 'Location', group : 'Group', driver : 'Driver' };

		$.getJSON('data/printers.json').done(function(data){
			for(item of data){
				$('.printers').append(models.view.item('printer', item));
			}
			$('.close').on("click", handlers.destroy);
		});

		$.getJSON('data/drivers.json').done(function(data){
			for(item of data){
				$('.drivers').append(models.view.item('driver', item));
			}
			$('.close').on("click", handlers.destroy);
		});
	}

})(jQuery)<commit_msg>Added structures saving. Added error handling for getJSONs.<commit_after>(function($){

	$.fn.df_edit = function(options){
		var structures = {
			printers: {},
			drivers: {}
		};

		var defs = $.extend({}, options);

		var models = {
			view: {
				keyval: function(key, value){
				return  '
' +
							'
"' + key + '" :
' +
							'
"' + value + '"
' +
						'
';
				},
				item: function(type, data){
					var item = '
' +
									'
' +
										'
{
' +
										'
<button class="close" title="Delete">✖</button>
' +
									'
';
									for(key of Object.keys(data)){
										item += this.keyval(key, data[key]);
									}
					item += 		'
},
' +
								'
';
					return item;

				}
			},
			edit: {
				item: function(type, data){
				// 	var item = '
';
				// 	for(key of Object.keys(data)){
				// 		item += this.keyval(key, data[key]);
				// 	}
				// 		<input type="text" autocomplete="off" name="name"  placeholder="Name">

				// 		<input type="text" autocomplete="off" name="qname"  placeholder="Queue name">

				// 		<input type="text" autocomplete="off" name="location"  placeholder="Location">

				// 		<input type="text" autocomplete="off" name="group"  placeholder="Group">

				// 		<input type="text" autocomplete="off" name="driver"  placeholder="Driver">

				// 		<input type="button" class="submit" autocomplete="off" name="submit" value="Submit">

				//

				}

			}

		};

		var handlers = {
			destroy: function(){
				$(this)
				.parents().eq(2)
				.animate({
					height: 0,
					width: 0,
					opacity: 0
				}, 250, function(){
					$(this).remove();
				});
			},
			submit: function(){
				var x = $(this).parents().eq(2);
				$('input, select', x).each(function(){
					if($(this).hasClass('df_btn_add')){

						$(this).parents().eq(1).animate({
							width: 0
						}, 250, function(){
							$(this).remove();
						});

					}else{
						$(this).animate({
							width: 0
						}, 250, function(){
							// $(this).remove();
							var value = $(this).val();
							var type = $(this);
							var parent = $(this).parent();
							$(parent).html('');
							$(parent)
								.append('"' + value + '"');
						});
					}
				})
				// $(this)
				// .parents().eq(2).animate({
				// 	height: 0,
				// 	width: 0,
				// 	opacity: 0
				// }, 250);
			}
		};

		$('.df_btn_add').on('click', handlers.submit);

		var converter = { name : 'Name', qname : 'Queue name', location : 'Location', group : 'Group', driver : 'Driver' };

		$.getJSON('data/printers.json').done(function(data){
			for(item of data){
				$('.printers').append(models.view.item('printer', item));
			}
			$('.close').on("click", handlers.destroy);

			cache.printers[printer.qname] = printer;
		}).fail(function(){
			console.log('Error while loading ' + 'printers.json.
 Please fix the JSON and refresh the page.');
		});

		$.getJSON('data/drivers.json').done(function(data){
			for(item of data){
				$('.drivers').append(models.view.item('driver', item));
			}

			$('.close').on("click", handlers.destroy);

			cache.drivers[driver.name] = driver.location;

		}).fail(function(){
			console.log('Error while loading ' + 'drivers.json.
 Please fix the JSON and refresh the page.');
		});
	}

})(jQuery)<|endoftext|>