src/mopidy-out.html
<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>