emiloberg/node-red-contrib-mopidy

View on GitHub
src/mopidy-out.html

Summary

Maintainability
Test Coverage
<script type="text/x-red" data-template-name="mopidy-out">
    <div class="form-row">
        <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
        <input type="text" id="node-input-name" placeholder="Name">
    </div>
    <div class="form-row">
        <label for="node-input-server"><i class="fa fa-globe"></i> <span data-i18n="mopidy-out.editor.server"></span></label>
        <input type="text" id="node-input-server">
    </div>
    <div class="form-row jq-mopidy-show-when-loaded" style="display: none;">
        <label for="mopidy-methods-categories"><i class="fa fa-list"></i> <span data-i18n="mopidy-out.editor.category"></span></label>
        <select id="mopidy-methods-categories"></select>
    </div>
    <div class="form-row jq-mopidy-show-when-loaded" style="display: none;">
        <label for="mopidy-methods-methods"><i class="fa fa-list"></i> <span data-i18n="mopidy-out.editor.method"></span></label>
        <select id="mopidy-methods-methods"></select>
    </div>

    <div class="js-mopidy-waiting" style="display: none; text-align: center;"><i class="fa fa-spinner fa-pulse fa-5x"></i><br><br><span data-i18n="mopidy-out.editor.msg.connecting-to-server"></span></div>
    
    <div class="js-mopidy-error-box error-box" style="display: none; text-align: center;"></div>

    <div id="mopidy-param-placeholder" class="form-row" style="display: none;">
        <label for="mopidy-param-placeholder-id"><i class="fa fa-tag"></i> Placeholder</label>
        <input type="text" id="mopidy-param-placeholder-id" placeholder="">
    </div>

    <input type="hidden" id="node-input-method" />
    <input type="hidden" id="node-input-params" />

    <style>
        .error-box {
            background-color: #D8EDF8;
            color: #307291;
            padding: 1em;
            border-color: #BBE8F2;
            border-radius: 4px;
        }
    </style>
</script>

<script type="text/javascript">
    RED.nodes.registerType('mopidy-out',{
        category: 'advanced-input',
        color:'#C6E8C0',
        defaults: {
            name: {value:''},
            server: {
                value: '',
                type: 'mopidy-config',
                required: false
            },
            params: { value: ''},
            method: { value: ''}
        },
        inputs:1,
        outputs:1,
        icon: 'mopidy-icon.png',
        label: function() {
            return this.name || this.method || 'mopidy';
        },
        labelStyle: function() {
            return this.name?'node_label_italic':'';
        },

        oneditprepare: function() {
            var allMethods = [];
            var jqMopidyMethodsCategories = $('#mopidy-methods-categories');
            var jqMopidyMethodsMethods = $('#mopidy-methods-methods');
            var jqNodeInputMethod = $('#node-input-method');
            var jqNodeInputParams = $('#node-input-params');
            var jqErrorBox = $('.js-mopidy-error-box');


            /**
             * Move the dialog up a bit. As we're dynamically adding stuff
             * to the dialog, Node-RED can't really figure out how to
             * center the dialog. Here we're saying that the average
             * dialog is 550px high.
             */
            var top = ($(window).height() - 550) / 2;
            top = top <= 10 ? 10 : top;
            setTimeout(function() {
                jqMopidyMethodsMethods.parents('.ui-dialog').first().css('top', top);
            }, 0);

            var defaultSelectedMethod = this.method || '';
            var defaultSelectedCategory = '';
            if (defaultSelectedMethod.indexOf('.') > 0) {
                defaultSelectedCategory = defaultSelectedMethod.split('.')[0];
            }
            var defaultParams = this.params || '{}';
            defaultParams = JSON.parse(defaultParams);

            function setHiddenParams() {
                var methodsObj = {};
                $('.js-mopidy-param').each(function () {
                    var jqThis = $(this);
                    methodsObj[jqThis.data('paramname')] = jqThis.val();
                });
                jqNodeInputParams.val(JSON.stringify(methodsObj));
            }
            $('#dialog-form').on('change', '.js-mopidy-param', function() {
                setHiddenParams();
            });

            function setHiddenMethod() {
                jqNodeInputMethod.val($(jqMopidyMethodsMethods).val());
            }

            function displayParams() {
                var curSelectedMethod = $(jqMopidyMethodsMethods).val();
                $('.js-mopidy-added').remove();
                allMethods.some(function(method) {
                    if (method.method !== curSelectedMethod) {
                        return false;
                    }
                    var jqDialogForm = $('#dialog-form');
                    method.params.forEach(function (param, index) {
                        var friendlyDefault = '';
                        if ('friendlyDefault' in param) {
                            friendlyDefault = param.friendlyDefault;
                        }
                        var prevValue = '';
                        if (param.name in defaultParams) {
                            prevValue = defaultParams[param.name];
                        }
                        jqParam = $('#mopidy-param-placeholder').clone();
                        jqParam
                            .attr('id', '')
                            .addClass('js-mopidy-added')
                            .attr('style', '');
                        jqParam.find('label')
                            .attr('for', 'mopidy-method-param-' + index)
                            .html('<i class="fa fa-bullseye"></i> ' + param.name);
                        jqParam.find('input')
                            .attr('id', 'mopidy-method-param-' + index)
                            .attr('placeholder', friendlyDefault)
                            .addClass('js-mopidy-param')
                            .val(prevValue)
                            .data('paramname', param.name);
                        jqDialogForm.append(jqParam);
                    });
                    jqDialogForm.append('<pre id="mopidy-method-description" class="js-mopidy-added" style="max-height: 100px; overflow: auto;">' + method.description + '</pre>');
                    defaultParams = {};
                });
                setHiddenParams();
            }
            jqMopidyMethodsMethods.change(displayParams.bind(this));
            jqMopidyMethodsMethods.change(setHiddenMethod.bind(this));

            function populateMethods() {
                var selectedCategory = jqMopidyMethodsCategories.val();
                var htmlMethods = '<option value=""></option>';
                allMethods.forEach(function(method) {
                   if (method.category === selectedCategory) {
                       var selected = '';
                       if (method.method === defaultSelectedMethod) {
                           selected = ' selected';
                           defaultSelectedMethod = '';
                       }
                       htmlMethods += '<option value="' + method.method + '"' + selected + '>' + method.method + '</option>';
                   }
                });
                jqMopidyMethodsMethods.html(htmlMethods);
                displayParams();
                setHiddenMethod();
            }
            jqMopidyMethodsCategories.change(populateMethods.bind(this));


            function populateCategories(methods) {
                var htmlCategories = '<option value=""></option>';
                getUniqueCatgories(methods).forEach(function(category) {
                    if (category === 'getVersion' || category === 'getUriSchemes') { return; } // Hide non useful categories
                    var selected = '';
                    if (category === defaultSelectedCategory) {
                        selected = ' selected';
                        defaultSelectedCategory = '';
                    }
                    htmlCategories += '<option value="' + category + '"' + selected + '>' + category + '</option>';
                });
                $('#mopidy-methods-categories').html(htmlCategories);
                populateMethods();
            }

            function getUniqueCatgories(methods) {
                return methods.map(function(method) {
                    return method.category
                })
                .reduce(function(prev, cur) {
                    if (prev.indexOf(cur) === -1) {
                        return prev.concat(cur);
                    } else {
                        return prev;
                    }
                }, [])
                .sort();
            }

            var fetchMethods = debounce(function() {
                allMethods = [];
                populateCategories([]);

                jqErrorBox.hide();

                var serverNodeId = $('#node-input-server').val();
                if (serverNodeId === '_ADD_') {
                    return;
                }

                $('.js-mopidy-waiting').show();
                $('.jq-mopidy-show-when-loaded').hide();

                var jqXHR = $.ajax('mopidy/' + serverNodeId + '/methods', {
                    type: 'GET'
                });

                jqXHR.always(function() {
                    $('.js-mopidy-waiting').hide();
                });

                jqXHR.done(function(data) {
                    allMethods = data;
                    populateCategories(data);
                    $('.jq-mopidy-show-when-loaded').show();
                });


                jqXHR.fail(function(err) {
                    if (err.status === 404) {
                        jqErrorBox.show().text(err.responseJSON.message);
                    } else {
                        if (err.responseJSON.hasOwnProperty('message')) {
                            jqErrorBox.show().text(err.responseJSON.message);
                        } else {
                            jqErrorBox.show().text('Could not connect to Mopidy. (Error: ' + JSON.stringify(err.responseJSON) + ')' );
                        }
                    }
                });
            }, 100);
            $('#node-input-server').change(fetchMethods.bind(this));

            function debounce(func, wait, immediate) {
                var timeout;
                return function() {
                    var context = this, args = arguments;
                    var later = function() {
                        timeout = null;
                        if (!immediate) func.apply(context, args);
                    };
                    var callNow = immediate && !timeout;
                    clearTimeout(timeout);
                    timeout = setTimeout(later, wait);
                    if (callNow) func.apply(context, args);
                };
            }

        }
    });
</script>