<!doctype html> <html lang='en'> <head> <meta charset='utf-8' /> <title>Server Manager</title> <script src='jquery.js'></script> <script>var MD5=function(j){function RotateLeft(a,b){return(a<<b)|(a>>>(32-b))}function AddUnsigned(a,b){var c,lY4,lX8,lY8,lResult;lX8=(a&0x80000000);lY8=(b&0x80000000);c=(a&0x40000000);lY4=(b&0x40000000);lResult=(a&0x3FFFFFFF)+(b&0x3FFFFFFF);if(c&lY4){return(lResult^0x80000000^lX8^lY8)}if(c|lY4){if(lResult&0x40000000){return(lResult^0xC0000000^lX8^lY8)}else{return(lResult^0x40000000^lX8^lY8)}}else{return(lResult^lX8^lY8)}}function F(x,y,z){return(x&y)|((~x)&z)}function G(x,y,z){return(x&z)|(y&(~z))}function H(x,y,z){return(x^y^z)}function I(x,y,z){return(y^(x|(~z)))}function FF(a,b,c,d,x,s,e){a=AddUnsigned(a,AddUnsigned(AddUnsigned(F(b,c,d),x),e));return AddUnsigned(RotateLeft(a,s),b)};function GG(a,b,c,d,x,s,e){a=AddUnsigned(a,AddUnsigned(AddUnsigned(G(b,c,d),x),e));return AddUnsigned(RotateLeft(a,s),b)};function HH(a,b,c,d,x,s,e){a=AddUnsigned(a,AddUnsigned(AddUnsigned(H(b,c,d),x),e));return AddUnsigned(RotateLeft(a,s),b)};function II(a,b,c,d,x,s,e){a=AddUnsigned(a,AddUnsigned(AddUnsigned(I(b,c,d),x),e));return AddUnsigned(RotateLeft(a,s),b)};function ConvertToWordArray(a){var b;var c=a.length;var d=c+8;var e=(d-(d%64))/64;var f=(e+1)*16;var g=Array(f-1);var h=0;var i=0;while(i<c){b=(i-(i%4))/4;h=(i%4)*8;g[b]=(g[b]|(a.charCodeAt(i)<<h));i++}b=(i-(i%4))/4;h=(i%4)*8;g[b]=g[b]|(0x80<<h);g[f-2]=c<<3;g[f-1]=c>>>29;return g};function WordToHex(a){var b="",WordToHexValue_temp="",lByte,lCount;for(lCount=0;lCount<=3;lCount++){lByte=(a>>>(lCount*8))&255;WordToHexValue_temp="0"+lByte.toString(16);b=b+WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2)}return b};function Utf8Encode(a){a=a.replace(/\r\n/g,"\n");var b="";for(var n=0;n<a.length;n++){var c=a.charCodeAt(n);if(c<128){b+=String.fromCharCode(c)}else if((c>127)&&(c<2048)){b+=String.fromCharCode((c>>6)|192);b+=String.fromCharCode((c&63)|128)}else{b+=String.fromCharCode((c>>12)|224);b+=String.fromCharCode(((c>>6)&63)|128);b+=String.fromCharCode((c&63)|128)}}return b};var x=Array();var k,AA,BB,CC,DD,a,b,c,d;var l=7,S12=12,S13=17,S14=22;var m=5,S22=9,S23=14,S24=20;var o=4,S32=11,S33=16,S34=23;var p=6,S42=10,S43=15,S44=21;j=Utf8Encode(j);x=ConvertToWordArray(j);a=0x67452301;b=0xEFCDAB89;c=0x98BADCFE;d=0x10325476;for(k=0;k<x.length;k+=16){AA=a;BB=b;CC=c;DD=d;a=FF(a,b,c,d,x[k+0],l,0xD76AA478);d=FF(d,a,b,c,x[k+1],S12,0xE8C7B756);c=FF(c,d,a,b,x[k+2],S13,0x242070DB);b=FF(b,c,d,a,x[k+3],S14,0xC1BDCEEE);a=FF(a,b,c,d,x[k+4],l,0xF57C0FAF);d=FF(d,a,b,c,x[k+5],S12,0x4787C62A);c=FF(c,d,a,b,x[k+6],S13,0xA8304613);b=FF(b,c,d,a,x[k+7],S14,0xFD469501);a=FF(a,b,c,d,x[k+8],l,0x698098D8);d=FF(d,a,b,c,x[k+9],S12,0x8B44F7AF);c=FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1);b=FF(b,c,d,a,x[k+11],S14,0x895CD7BE);a=FF(a,b,c,d,x[k+12],l,0x6B901122);d=FF(d,a,b,c,x[k+13],S12,0xFD987193);c=FF(c,d,a,b,x[k+14],S13,0xA679438E);b=FF(b,c,d,a,x[k+15],S14,0x49B40821);a=GG(a,b,c,d,x[k+1],m,0xF61E2562);d=GG(d,a,b,c,x[k+6],S22,0xC040B340);c=GG(c,d,a,b,x[k+11],S23,0x265E5A51);b=GG(b,c,d,a,x[k+0],S24,0xE9B6C7AA);a=GG(a,b,c,d,x[k+5],m,0xD62F105D);d=GG(d,a,b,c,x[k+10],S22,0x2441453);c=GG(c,d,a,b,x[k+15],S23,0xD8A1E681);b=GG(b,c,d,a,x[k+4],S24,0xE7D3FBC8);a=GG(a,b,c,d,x[k+9],m,0x21E1CDE6);d=GG(d,a,b,c,x[k+14],S22,0xC33707D6);c=GG(c,d,a,b,x[k+3],S23,0xF4D50D87);b=GG(b,c,d,a,x[k+8],S24,0x455A14ED);a=GG(a,b,c,d,x[k+13],m,0xA9E3E905);d=GG(d,a,b,c,x[k+2],S22,0xFCEFA3F8);c=GG(c,d,a,b,x[k+7],S23,0x676F02D9);b=GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);a=HH(a,b,c,d,x[k+5],o,0xFFFA3942);d=HH(d,a,b,c,x[k+8],S32,0x8771F681);c=HH(c,d,a,b,x[k+11],S33,0x6D9D6122);b=HH(b,c,d,a,x[k+14],S34,0xFDE5380C);a=HH(a,b,c,d,x[k+1],o,0xA4BEEA44);d=HH(d,a,b,c,x[k+4],S32,0x4BDECFA9);c=HH(c,d,a,b,x[k+7],S33,0xF6BB4B60);b=HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);a=HH(a,b,c,d,x[k+13],o,0x289B7EC6);d=HH(d,a,b,c,x[k+0],S32,0xEAA127FA);c=HH(c,d,a,b,x[k+3],S33,0xD4EF3085);b=HH(b,c,d,a,x[k+6],S34,0x4881D05);a=HH(a,b,c,d,x[k+9],o,0xD9D4D039);d=HH(d,a,b,c,x[k+12],S32,0xE6DB99E5);c=HH(c,d,a,b,x[k+15],S33,0x1FA27CF8);b=HH(b,c,d,a,x[k+2],S34,0xC4AC5665);a=II(a,b,c,d,x[k+0],p,0xF4292244);d=II(d,a,b,c,x[k+7],S42,0x432AFF97);c=II(c,d,a,b,x[k+14],S43,0xAB9423A7);b=II(b,c,d,a,x[k+5],S44,0xFC93A039);a=II(a,b,c,d,x[k+12],p,0x655B59C3);d=II(d,a,b,c,x[k+3],S42,0x8F0CCC92);c=II(c,d,a,b,x[k+10],S43,0xFFEFF47D);b=II(b,c,d,a,x[k+1],S44,0x85845DD1);a=II(a,b,c,d,x[k+8],p,0x6FA87E4F);d=II(d,a,b,c,x[k+15],S42,0xFE2CE6E0);c=II(c,d,a,b,x[k+6],S43,0xA3014314);b=II(b,c,d,a,x[k+13],S44,0x4E0811A1);a=II(a,b,c,d,x[k+4],p,0xF7537E82);d=II(d,a,b,c,x[k+11],S42,0xBD3AF235);c=II(c,d,a,b,x[k+2],S43,0x2AD7D2BB);b=II(b,c,d,a,x[k+9],S44,0xEB86D391);a=AddUnsigned(a,AA);b=AddUnsigned(b,BB);c=AddUnsigned(c,CC);d=AddUnsigned(d,DD)}var q=WordToHex(a)+WordToHex(b)+WordToHex(c)+WordToHex(d);return q.toLowerCase()};</script> <style> body > div { border: 1px solid #000; margin: 25px 50px 25px 50px; border-radius: 3px; padding: 5px; } h2 { margin: 0 0 10px 0; background-color: #333; color: #fff; font-size: 1.2em; padding: 5px; border-radius: 5px 5px 0 0; } #config label, #config-version, #config-time { display: block; padding: 3px; margin: 5px 0 5px 10px; } #config-status { margin: 0 0 0 10px; } #limits-list td { width: 25%; } #stream-limit-table, #logs table { width: 100%; margin: 10px 0 20px 15px; } #stream-limit-table th, #logs th, #limits-table th { text-align: left; } #limits-table { margin: 20px 0 5px 15px; width: 100%; } #limits p, #protocols p { margin: 20px 0 5px 15px; } #protocol-list { list-style: none; margin: 15px 0 0 10px; padding: 0; } #protocol-list li { margin: 0 0 10px 5px; } #protocol-list li span { margin: 0 0 0 15px; } #streams-list { list-style: none; padding: 0 0 25px 20px; } #streams-list li { margin: 0 0 50px 0; } #streams-list h3 { font-size: 1.1em; margin: 0 0 10px -10px; } #streams-list h3 span { font-size: 1em; font-weight: normal; } #streams-list .blockish { display: block; } #streams-list li > div { margin: 0 0 20px 10px; border: 1px solid #aaa; border-radius: 5px; padding: 10px; } #streams-list li div .blockish { margin: 5px 0 0 10px; width: 250px; overflow: hidden; } #streams-list li div .blockish input { float: right; } .mleft25 { margin-left: 10px; } #streams-list .stream-limit div { margin: 10px 0 5px 10px; } #streams-list .stream-limit div span { margin: 5px 0 5px 10px; } #streams-list .stream-preset div { margin: 10px 0 5px 15px; } .errorlogin { color: #c33; } .correctlogin { color: #393; } </style> </head> <body> <h1>Server Manager</h1> <div id='current-status'> Status: <span id='status'>disconnected</span> </div> <div id='connect'> <label for='username'> Username: <input type='text' id='username' value='testaccount' /> </label> <label for='password'> Password: <input type='password' id='password' value='pisvlek' /> </label> <label for='server'> Server: <input type='text' id='server' value='http://localhost:4242' /> </label> <button id='login'>login</button> </div> <div id='config'> <h2>Config</h2> <label for='config-host'> Host: <input type='text' id='config-host' value='' /> <button id='save-config-host'>save</button> </label> <label for='config-name'> Name: <input type='text' id='config-name' value='' /> <button id='save-config-name'>save</button> </label> <span id='config-status'>Status: </span> <button id='config-status-disable'>DISABLE</button> <span id='config-version'>Version: </span> <span id='config-time'>Time: </span> </div> <div id='limits'> <h2>Limits</h2> <table id='limits-table'> <thead> <th>Type</th> <th>Hard/soft limit</th> <th>Value</th> <th></th> </head> <tbody id='limits-list'> </tbody> </table> <p> new limit: <select id='new-limit-name'> <option value='kb_total'>Total bandwidth</option> <option value='kbps_max'>Current bandwidth</option> <option value='users'>Concurrent users</option> <option value='streams'>Concurrent streams</option> <option value='geo'>Geolimited</option> <option value='host'>Hostlimited</option> <option value='time'>Time limited</option> <option value='duration'>Duration</option> <option value='str_kbps_min'>Minimum bitrate</option> <option value='str_kbps_max'>Maximum bitrate</option> </select> <select id='new-limit-type'> <option value='hard'>Hard limit - shuts down streams when passed</option> <option value='soft'>Soft limit - warning only</option> </select> <input type='text' id='new-limit-val' /> <button id='new-limit-build'>add</button> </p> </div> <div id='protocols'> <h2>Protocols</h2> <ul id='protocol-list'> </ul> <p> new protocol: <select id='new-protocol-name'> <option value='HTTP'>HTTP</option> <option value='RTMP'>RTMP</option> </select> port: <input type='text' id='new-protocol-port' /> <button id='new-protocol-build'>add</button> </p> </div> <div id='streams'> <h2>Streams</h2> <ul id='streams-list'> </ul> </div> <div id='logs'> <h2>Logs</h2> <table> <thead> <th>Date</th> <th>Type</th> <th>Message</th> </thead> <tbody id='log-list'> </tbody> </table> </div> <script> var settings = { server: '', credentials: { username: "", password: "", authstring: "" }, settings: {} }; function limitShortToLong(name) { var i, rep = [ ['kb_total', 'Total bandwidth'], ['kbps_max', 'Current bandwidth'], ['users', 'Concurrent users'], ['streams', 'Cocurrent streams'], ['geo', 'Geolimited'], ['host', 'Hostlimited'], ['time', 'Timelimited'], ['duration', 'Duration'], ['str_kbps_min', 'Minimum bitrate'], ['str_kbps_max', 'Maximum bitrate'] ]; for(i = 0; i < rep.length; i++) { if(name == rep[i][0]) { return rep[i][1]; } } return '[error] name "' + name + '" has no entry in rep (@350)!'; } function loadSettings(callback) { var errorstr = '', data = $.extend(settings.settings, { 'authorize': { 'username': settings.credentials.username, 'password': MD5(MD5(settings.credentials.password) + settings.credentials.authstring) } }); console.log('SEND', data); $.ajax( { 'url': settings.server, 'data': { "command": JSON.stringify(data) }, 'dataType': 'jsonp', 'success': function(d) { console.log('RECV', d); if(d && d['authorize'] && d['authorize']['challenge']) { if (settings.credentials.authstring != d['authorize']['challenge']) { settings.credentials.authstring = d['authorize']['challenge']; //console.log('need to reload settings with new auth string'); loadSettings(callback); return; }else{ errorstr = 'wrong credentials'; //console.log('current authstring == chalenge string, but still received a chalenge! = ERROR'); } }else{ //console.log('no challenge - we\'re logged in! = OK'); settings.settings = $.extend(true, { "config": { "host": "", "limits": [], "name": "", "protocols": {}, "status": "", "version": "" }, "streams": {}, "log": {}, "statistics": {} }, d); console.log('new shinyness object:', settings.settings); } if(callback) { callback(errorstr); } } }); } $(document).ready(function() { $('div').hide(); $('#current-status').show(); $('#connect').show(); $('#login').click(function() { settings.credentials.username = $('#username').val(), settings.credentials.password = $('#password').val(); settings.server = $('#server').val(); $('#status').text('logging in...'); $('#status').attr('class', ''); loadSettings(function(errorstr) { if(errorstr == '') { $('#status').text('logged in'); $('#status').attr('class', 'correctlogin'); $('div').show(); fillHTML(); /* setInterval(function() { loadSettings(fillLogs); }, 5000); */ console.log('logged in!'); }else{ $('#status').text('disconnected - ' + errorstr); $('#status').attr('class', 'errorlogin'); $('div:not(#current-status):not(#connect)').hide(); //$('div').hide(); //console.log('error logging in: ' + errorstr); } }); }); $('#save-config-host').click(function() { settings.settings.config.host = $('#config-host').val(); loadSettings(fillConfig); }); $('#save-config-name').click(function() { settings.settings.config.name = $('#config-name').val(); loadSettings(fillConfig); }); $('#config-status-disable').click(function() { settings.settings.config.status = 'DISABLED'; loadSettings(fillConfig); }); $('#new-limit-build').click(function() { settings.settings.config.limits.push( { name: $('#new-limit-name option:selected').val(), type: $('#new-limit-type option:selected').val(), val: $('#new-limit-val').val() }); $('#new-limit-val').val(''); loadSettings(fillLimits); }); $('#new-protocol-build').click(function() { var prot = $('#new-protocol-name option:selected').val(), port = $('#new-protocol-port').val(); settings.settings.config.protocols[prot] = {"port": port}; $('#new-protocol-port').val(''); loadSettings(fillProtocols); }); }); function fillHTML() { //fill config fillConfig(); // limits fillLimits(); // protocols fillProtocols(); // streams fillStreams(); // log fillLogs(); } function fillConfig() { $('#config-host').val(settings.settings.config.host); $('#config-name').val(settings.settings.config.name); $('#config-version').text('version: ' + (settings.settings.config.version || '')); $('#config-time').text('time: ' + new Date(settings.settings.config.time*1000).toUTCString() || ''); $('#config-status').text('status: ' + (settings.settings.config.status || '')); } function fillLimits() { var i, cur, lim, del, len = settings.settings.config.limits.length; $('#limits-list').html(''); for(i = 0; i < len; i++) { cur = $('<tr>').attr('id', 'limits-' + i); lim = settings.settings.config.limits[i]; del = $('<td>').click(function() { var sfrom = $(this).parent().attr('id').replace('limits-', ''); settings.settings.config.limits.splice(sfrom, 1); $(this).parent().remove(); loadSettings(fillLimits); }).html('<button>delete</button>'); cur.append($('<td>').text(limitShortToLong(lim.name))); cur.append($('<td>').text(lim.type)); cur.append($('<td>').text(lim.val)); cur.append(del); $('#limits-list').append(cur); } } function fillProtocols() { var protocol, li; $('#protocol-list').html(''); for(protocol in settings.settings.config.protocols) { li = $('<li>').attr('id', 'protocol-' + protocol); li.text(protocol + ' on port ' + settings.settings.config.protocols[protocol].port); li.append($('<button>').click(function() { var prot = $(this).parent().attr('id').replace('protocol-', ''); console.log(prot); delete settings.settings.config.protocols[prot]; $(this).parent().remove(); loadSettings(fillProtocols); }).text('remove')); $('#protocol-list').append(li); } } function fillStreams() { var stream, cur, li, channel, limit; $('#streams-list').html(''); for(stream in settings.settings.streams) { var d = $('<button>').text('delete').attr('id', 'stream-delete-' + stream).click(function() { var id = $(this).attr('id').replace('stream-delete-', ''); console.log('delete this stream', id); delete settings.settings.streams[id]; loadSettings(fillStreams); }); li = $('<li>').append($('<h3>').text(stream).append(d)); cur = settings.settings.streams[stream]; li.append( $('<div>').text('Name: ').append( $('<input>').attr('type', 'text').attr('id', 'stream-name-' + stream).attr('value', cur.name) ).append( $('<button>').text('save').click(function(x) { return function() { var name = $('#stream-name-' + x).val(); settings.settings.streams[x].name = name; loadSettings(fillStreams); } }(stream)) ) ); //li.append($('<div>').text('Group: ' + cur.group)); li.append( $('<div>').text('Group: ').append( $('<input>').attr('type', 'text').attr('id', 'stream-group-' + stream).attr('value', cur.group) ).append( $('<button>').text('save').click(function(x) { return function() { var group = $('#stream-group-' + x).val(); settings.settings.streams[x].group = group; loadSettings(fillStreams); } }(stream)) ) ); // add channel info channel = $('<div>').attr('id', 'stream-channel-' + stream).text('Channel: '); channel.append( $('<span>').attr('class', 'blockish').text('URL:').append( $('<input>').attr('type', 'text').attr('id', 'stream-channel-url-' + stream).attr('value', cur.channel.URL) ) ); channel.append( $('<span>').attr('class', 'blockish').text('Account:').append( $('<input>').attr('type', 'text').attr('id', 'stream-channel-account-' + stream).attr('value', cur.channel.account) ) ); channel.append($('<button>').attr('class', 'mleft25').text('save').click(function() { var cname = $(this).parent().attr('id').replace('stream-channel-', ''), url = $('#stream-channel-url-' + cname).val(), acc = $('#stream-channel-account-' + cname).val(); settings.settings.streams[cname].channel = {URL: url, account: acc}; loadSettings(fillStreams); })); li.append(channel); // add limits limit = $('<div>').attr('class', 'stream-limit').attr('id', 'stream-limit-' + stream).text('Limits: '); var table = $('<table>').attr('id', 'stream-limit-table'); var tbody = $('<tbody>'); table.append( $('<thead>').append( $('<th>').text('Type') ).append( $('<th>').text('Hard/soft limit') ).append( $('<th>').text('Value') ).append( $('<th>') ) ).append(tbody); var cll = cur.limits ? cur.limits.length : 0; for(var i = 0; i < cll; i++) { var climit = $('<tr>').attr('id', 'stream-limit-' + stream + '-' + i); var del = $('<td>').click(function() { var stuff = $(this).parent().attr('id').replace('stream-limit-', ''), lastd = stuff.lastIndexOf('-'), stream = stuff.substr(0, lastd), id = parseInt(stuff.substr(lastd + 1), 10); settings.settings.streams[stream].limits.splice(id, 1); loadSettings(fillStreams); }).html('<button>delete</button>'); climit.append($('<td>').text(limitShortToLong(cur.limits[i].name))); climit.append($('<td>').text(cur.limits[i].type)); climit.append($('<td>').text(cur.limits[i].val)); climit.append(del); tbody.append(climit); } limit.append(table); var nlimit = $('<div>').attr('id', 'new-limit-stream-' + stream).text('New limit: '); var nlimitsel = $('<select>').attr('id', 'new-limit-stream-name-' + stream); var lnames = [ ['kb_total', 'Total bandwidth'], ['kbps_max', 'Current bandwidth'], ['users', 'Concurrent users'], ['streams', 'Cocurrent streams'], ['geo', 'Geolimited'], ['host', 'Hostlimited'], ['time', 'Timelimited'], ['duration', 'Duration'], ['str_kbps_min', 'Minimum bitrate'], ['str_kbps_max', 'Maximum bitrate'] ] for(var j = 0; j < lnames.length; j++) { nlimitsel.append($('<option>').attr('value', lnames[j][0]).text(lnames[j][1])); } nlimit.append(nlimitsel); nlimit.append( $('<select>').attr('id', 'new-limit-stream-type-' + stream).append( $('<option>').attr('value', 'hard').text('Hard limit - shuts down streams when passed') ).append( $('<option>').attr('value', 'soft').text('Soft limit - warning only') ) ); nlimit.append( $('<input>').attr('type', 'text').attr('id', 'new-limit-stream-val-' + stream) ); nlimit.append( $('<button>').click(function() { var stream = $(this).parent().attr('id').replace('new-limit-stream-', ''), limit = { name: $('#new-limit-stream-name-' + stream + ' :selected').val(), type: $('#new-limit-stream-type-' + stream + ' :selected').val(), val: $('#new-limit-stream-val-' + stream).val() }; //console.log(stream, limit); settings.settings.streams[stream].limits.push(limit); loadSettings(fillStreams); }).text('add') ); limit.append(nlimit); li.append(limit); // do preset x1 // notes: this reads the 'preset' prop, NOT the 'presets' prop. var spreset = $('<div>').attr('class', 'stream-preset').attr('id', 'stream-preset-' + stream).text('preset: '); li.append(spreset); spreset.append( $('<div>').text('Command: ').append( $('<input>').attr('id', 'stream-preset-cmd-' + stream).attr('type', 'text').val(settings.settings.streams[stream].preset.cmd) ).append( $('<button>').text('save').click(function() { var stream = $(this).parent().parent().attr('id').replace('stream-preset-', ''); var cmd = $('#stream-preset-cmd-' + stream).val(); settings.settings.streams[stream].preset.cmd = cmd; loadSettings(fillStreams); }) ) ); spreset.append( $('<div>').text('Description: ').append( $('<input>').attr('id', 'stream-preset-desc-' + stream).attr('type', 'text').val(settings.settings.streams[stream].preset.desc) ).append( $('<button>').text('save').click(function() { var stream = $(this).parent().parent().attr('id').replace('stream-preset-', ''); var desc = $('#stream-preset-desc-' + stream).val(); settings.settings.streams[stream].preset.desc = desc; loadSettings(fillStreams); }) ) ); spreset.append( $('<div>').text('Name: ').append( $('<input>').attr('id', 'stream-preset-name-' + stream).attr('type', 'text').val(settings.settings.streams[stream].preset.name) ).append( $('<button>').text('save').click(function() { var stream = $(this).parent().parent().attr('id').replace('stream-preset-', ''); var name = $('#stream-preset-name-' + stream).val(); settings.settings.streams[stream].preset.name = name; loadSettings(fillStreams); }) ) ); $('#streams-list').append(li); } // new stream var nspreset = $('<div>').attr('id', 'new-stream').text('New stream').append( $('<div>').text('Name: ').append( $('<input>').attr('id', 'new-stream-name').attr('type', 'text') ).append( $('<button>').text('create').click(function() { var nstream = {}; nstream[$('#new-stream-name').val()] = { "account": "", "channel": {"URL": "", "account": ""}, "group": "", "limits": [], "name": $('#new-stream-name').val(), "preset": {"cmd": "", "name": "", "desc": ""}, "status": "" } $.extend(settings.settings.streams, nstream); loadSettings(fillStreams); }) ) ); $('#streams-list').append(nspreset); } function fillLogs() { var i, cur, tr, tbody = $('#log-list'), logs = settings.settings.log.reverse(), len = logs.length; tbody.html(''); for(i = 0; i < len; i++) { cur = settings.settings.log[i]; tr = $('<tr>').append( $('<td>').text(new Date(cur[0] * 1000).toUTCString()) ).append( $('<td>').text(cur[1]) ).append( $('<td>').text(cur[2]) ); tbody.append(tr); } } </script> </body> </html>