owntone-server/admin-root/config.js

442 lines
16 KiB
JavaScript

Event.observe(window,'load',init);
//###TODO
// * Fix cancel for multiple, create a span holding all multiple elements
// * Disable/enable save/cancel, Add key and onchange listeners keep a note on changed items
// * create the path/file browser
// * Inform user if server restart needed, ask Ron which options
// * Show required options in UI check with Ron if conf.c is correct
// * better errormessage for not writable config
// * make tabs?
// * add warning if leaving page without saving
// Config isn't defined until after the Event.observe above
// I could have put it below Config = ... but I want all window.load events
// at the start of the file
function init() {
Config.init();
}
var ConfigXML = {
config: {},
configIndex: {},
getOptionFromElementName: function (name) {
return this.configIndex[name];
},
getOption: function (section,id) {
return this.config[section][id];
},
getSections: function () {
return $H(this.config).keys();
},
getItems: function(section) {
return $H(this.config[section]).keys();
},
parseXML: function(xmlDoc) {
$A(xmlDoc.getElementsByTagName('section')).each(function (section) {
var items = {};
$A(section.getElementsByTagName('item')).each(function (item) {
var returnItem = {};
$A(item.attributes).each(function (attr) {
returnItem[attr.name] = attr.value;
});
$A(item.childNodes).each(function (node) {
if (Element.textContent(node) == '') {
return;
}
if ('options' == node.nodeName) {
var options = [];
$A(item.getElementsByTagName('option')).each(function (option) {
options.push({value: option.getAttribute('value'),
label: Element.textContent(option)});
});
returnItem['options'] = options;
} else {
returnItem[node.nodeName] = Element.textContent(node);
}
$A(node.attributes).each(function (attr) {
returnItem[attr.name] = attr.value;
});
});
// Double index everything one as [section][id]
// and one as [config_section:id]
items[item.getAttribute('id')] = returnItem;
ConfigXML.configIndex[returnItem.config_section+ ':' +returnItem.id] = returnItem;
});
ConfigXML.config[section.getAttribute('name')] = items;
});
}
};
var ConfigInitialValues = {
values: {},
getValues: function () {
return $H(this.values);
},
getValue: function (section,id) {
return this.values[section+':'+id];
},
parseXML: function (xmldoc) {
// IE and w3c treat xmldoc differently make shore firstChild is firstchild of <config>
if (xmldoc.childNodes[1] && xmldoc.childNodes[1].nodeName == 'config') {
sections = $A(xmldoc.childNodes[1].childNodes);
} else {
sections = $A(xmldoc.firstChild.childNodes);
}
sections.each(function (section) {
var sectionName = section.nodeName;
$A(section.childNodes).each(function (node) {
if (node.firstChild.hasChildNodes()) {
var values = [];
$A(node.childNodes).each(function (n) {
values.push(Element.textContent(n));
});
ConfigInitialValues.values[sectionName+':'+node.nodeName] = values;
} else {
ConfigInitialValues.values[sectionName+':'+node.nodeName] = Element.textContent(node);
}
});
});
}
};
var Config = {
configPath: '',
configOptionValues: '',
init: function () {
new Ajax.Request('/config.xml',{method: 'get',onComplete: Config.storeConfigLayout});
},
storeConfigLayout: function (request) {
ConfigXML.parseXML(request.responseXML);
new Ajax.Request('/xml-rpc?method=stats',{method: 'get',onComplete: Config.updateStatus});
},
updateStatus: function (request) {
Config.configPath = Element.textContent(request.responseXML.getElementsByTagName('config_path')[0]);
Config.isWritable = Element.textContent(request.responseXML.getElementsByTagName('writable_config')[0]) == '1';
// $('config_path').appendChild(document.createTextNode(
// );
new Ajax.Request('/xml-rpc?method=config',{method: 'get',onComplete: Config.showConfig});
},
showConfig: function (request) {
ConfigInitialValues.parseXML(request.responseXML);
Config.configOptionValues = request.responseXML;
var sections = ConfigXML.getSections();
sections.each(function (section) {
var head = document.createElement('div');
head.className= 'naviheader';
head.appendChild(document.createTextNode(section));
var body = document.createElement('div');
body.className = 'navibox';
if ('Server' == section) {
body.appendChild(Builder.node('span',{id:'config_path'},'Config File'));
body.appendChild(document.createTextNode(Config.configPath));
body.appendChild(Builder.node('br'));
body.appendChild(Builder.node('div',{style: 'clear: both;'}));
}
ConfigXML.getItems(section).each(function (itemId) {
body.appendChild(Config._buildItem(section,itemId));
});
$('theform').appendChild(head);
$('theform').appendChild(body);
});
if (!Config.isWritable) {
Effect.Appear('config_not_writable_warning');
} else {
// Create save and cancel buttons
// var save = Builder.node('button',{id: 'button_save', disabled: 'disabled'},'Save');
var save = Builder.node('button',{id: 'button_save'},'Save');
Event.observe(save,'click',saveForm);
var cancel = Builder.node('button',{id: 'button_cancel'},'Cancel');
Event.observe(cancel,'click',cancelForm);
var spacer = document.createTextNode('\u00a0\u00a0');
var buttons = $('buttons');
if (navigator.platform.toLowerCase().indexOf('mac') != -1) {
// We're on mac
buttons.appendChild(cancel);
buttons.appendChild(spacer);
buttons.appendChild(save);
} else {
//###TODO What about all them unix variants?
buttons.appendChild(save);
buttons.appendChild(spacer);
buttons.appendChild(cancel);
}
}
},
_buildItem: function(section,itemId) {
var frag = document.createDocumentFragment();
var href;
var span;
var item = ConfigXML.getOption(section,itemId);
var postId = item.config_section + ':' + itemId;
var noBrowse = false;
switch(item.type) {
case 'text':
if (item.multiple) {
var values = ConfigInitialValues.getValue(item.config_section,item.id);
if (!values || values.length == 0) {
values = [''];
}
values.each(function (val,i) {
var span = document.createElement('span');
span.appendChild(BuildElement.input(postId+i,postId,
item.name,
val || item.default_value || '',
item.size || 20,
item.short_description,
''));
if (item.browse) {
href = Builder.node('a',{href: 'javascript://'},'Browse');
Event.observe(href,'click',Config._browse);
span.appendChild(href);
}
span.appendChild(document.createTextNode('\u00a0\u00a0'));
href = Builder.node('a',{href: 'javascript://'},'Remove');
Event.observe(href,'click',Config._removeItemEvent);
span.appendChild(href);
span.appendChild(Builder.node('br'));
frag.appendChild(span);
});
href = Builder.node('a',{href:'javascript://',className:'addItemHref'},item.add_item_label);
frag.appendChild(href);
Event.observe(href,'click',Config._addItemEvent);
frag.appendChild(Builder.node('div',{style:'clear: both'}));
} else {
frag.appendChild(BuildElement.input(postId,postId,
item.name,
ConfigInitialValues.getValue(item.config_section,item.id) || item.default_value || '',
item.size || 20,
item.short_description,
''));
if (item.browse) {
href = Builder.node('a',{href: 'javascript://'},'Browse');
Event.observe(href,'click',Config._browse);
frag.appendChild(href);
}
frag.appendChild(Builder.node('br'));
}
break;
case 'select':
frag.appendChild(BuildElement.select(postId,
item.name,
item.options,
ConfigInitialValues.getValue(item.config_section,item.id) || item.default_value,
item.short_description));
frag.appendChild(Builder.node('br'));
break;
}
return frag;
},
_addItemEvent: function (e) {
var span = Event.element(e);
while (span.nodeName.toLowerCase() != 'span') {
span = span.previousSibling;
}
Config._addItem(span);
},
_addItem: function(span) {
var newSpan = span.cloneNode(true);
var id = newSpan.getElementsByTagName('input')[0].id;
var num = parseInt(id.match(/\d+$/));
num++;
var id = id.replace(/\d+$/,'') + num;
newSpan.getElementsByTagName('label')[0].setAttribute('for',id);
newSpan.getElementsByTagName('input')[0].id = id;
newSpan.getElementsByTagName('input')[0].value = '';
var hrefs = newSpan.getElementsByTagName('a');
if ('Netscape' == navigator.appName) {
// Firefox et al doesn't copy registered events on an element deep clone
// Don't know if that is w3c or if IE has it right
if (hrefs.length == 1) {
Event.observe(hrefs[0],'click',Config._removeItemEvent);
} else {
Event.observe(hrefs[0],'click',Config._browse);
Event.observe(hrefs[1],'click',Config._removeItemEvent);
}
}
span.parentNode.insertBefore(newSpan,span.nextSibling);
},
_removeItemEvent: function (e) {
var span = Event.element(e);
while (span.nodeName.toLowerCase() != 'span') {
span = span.parentNode;
}
Config._removeItem(span);
},
_removeItem: function(span) {
if ((span.previousSibling && span.previousSibling.nodeName.toLowerCase() == 'span')||
(span.nextSibling.nodeName.toLowerCase() == 'span')) {
Element.remove(span);
} else {
span.getElementsByTagName('input')[0].value='';
}
},
_browse: function(e) {
alert('Browse');
}
}
var BuildElement = {
input: function(id,name,displayName,value,size,short_description,long_description) {
var frag = document.createDocumentFragment();
var label = document.createElement('label');
label.setAttribute('for',id);
label.appendChild(document.createTextNode(displayName));
frag.appendChild(label);
if (Config.isWritable) {
frag.appendChild(Builder.node('input',{id: id,name: name,className: 'text',
value: value,size: size}));
} else {
frag.appendChild(Builder.node('input',{id: id,name: name,className: 'text',
value: value,size: size, disabled: 'disabled'}));
}
frag.appendChild(document.createTextNode('\u00a0'));
if (short_description) {
frag.appendChild(document.createTextNode(short_description));
}
return frag;
},
select: function(id,displayName,options,value,short_description,long_description) {
var frag = document.createDocumentFragment();
var label = document.createElement('label');
label.setAttribute('for',id);
label.appendChild(document.createTextNode(displayName));
frag.appendChild(label);
var select = Builder.node('select',{id: id,name: id,size: 1});
if (!Config.isWritable) {
select.disabled = 'disabled';
}
$A(options).each(function (option) {
select.appendChild(Builder.node('option',{value: option.value},
option.label));
});
select.value = value;
frag.appendChild(select);
frag.appendChild(document.createTextNode('\u00a0'));
if (short_description) {
frag.appendChild(document.createTextNode(short_description));
}
return frag;
}
}
function saved(req) {
if ('200' == Element.textContent(req.responseXML.getElementsByTagName('status')[0])) {
alert('Saved');
} else {
alert("Couldn't save and if this weren't a beta I'd tell you why");
}
}
function saveForm() {
var postVars = [];
var multiple = {};
$A($('theform').getElementsByTagName('select')).each(function (select) {
postVars.push(Form.Element.serialize(select.id));
});
$A($('theform').getElementsByTagName('input')).each(function (input) {
if (ConfigXML.getOptionFromElementName(input.name).multiple) {
if (multiple[input.name]) {
multiple[input.name].push(encodeURIComponent(input.value));
} else {
multiple[input.name] = [input.value];
}
} else {
postVars.push(Form.Element.serialize(input.id));
};
});
$H(multiple).each(function (item) {
postVars.push(item.key + '=' + item.value.join(','));
});
new Ajax.Request('/xml-rpc?method=updateconfig',
{method: 'post',
parameters: postVars.join('&'),
onComplete: saved
});
}
function cancelForm() {
alert("Cancel doesn't work, reload the page to revert your changes");
return;
ConfigXML.getSections().each(function (section){
ConfigXML.getItems(section).each(function (itemId) {
var item = ConfigXML.getOption(section,itemId);
var itemConfigId = item.config_section + ':' + item.id;
if (item.multiple) {
var values = ConfigInitialValues.getValue(itemConfigId);
if (!values || values.length == 0) {
values = [''];
}
//###TODO do the multiple thing
} else {
//###TODO potential error a select without a default value
$(itemConfigId).value = ConfigInitialValues.getValue(item.config_section,item.id) || item.default_value || '';
}
});
});
return;
ConfigInitialValues.getValues().each(function (option) {
if (typeof (option.value) == 'object') {
//###TODO what if user removed one of the multiplevalued options?
if (option.value != '') {
option.value.each(function (val,i) {
$(option.key + i).value = val;
});
}
}
var el = $(option.key);
if (el) {
el.value = option.value;
}
});
}
Object.extend(Element, {
removeChildren: function(element) {
while(element.hasChildNodes()) {
element.removeChild(element.firstChild);
}
},
textContent: function(node) {
if ((!node) || !node.hasChildNodes()) {
// Empty text node
return '';
} else {
if (node.textContent) {
// W3C ?
return node.textContent;
} else if (node.text) {
// IE
return node.text;
}
}
// We shouldn't end up here;
return '';
}
});
/* Detta script finns att hamta pa http://www.jojoxx.net och
far anvandas fritt sa lange som dessa rader star kvar. */
function DataDumper(obj,n,prefix){
var str=""; prefix=(prefix)?prefix:""; n=(n)?n+1:1; var ind=""; for(var i=0;i<n;i++){ ind+=" "; }
if(typeof(obj)=="string"){
str+=ind+prefix+"String:\""+obj+"\"\n";
} else if(typeof(obj)=="number"){
str+=ind+prefix+"Number:"+obj+"\n";
} else if(typeof(obj)=="function"){
str+=ind+prefix+"Function:"+obj+"\n";
} else if(typeof(obj) == 'boolean') {
str+=ind+prefix+"Boolean:" + obj + "\n";
} else {
var type="Array";
for(var i in obj){ type=(type=="Array"&&i==parseInt(i))?"Array":"Object"; }
str+=ind+prefix+type+"[\n";
if(type=="Array"){
for(var i in obj){ str+=DataDumper(obj[i],n,i+"=>"); }
} else {
for(var i in obj){ str+=DataDumper(obj[i],n,i+"=>"); }
}
str+=ind+"]\n";
}
return str;
}