diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index fcc2288356..e6c14d06da 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -213,8 +213,6 @@ void Agent::run() { loop.exec(); - - // let the AvatarData and ResourceCache classes use our QNetworkAccessManager AvatarData::setNetworkAccessManager(networkManager); ResourceCache::setNetworkAccessManager(networkManager); diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 8e4ce04f0b..407a4413d5 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include #include #include @@ -478,40 +481,81 @@ void AudioMixer::run() { nodeList->linkedDataCreateCallback = attachNewBufferToNode; - // check the payload to see if we have any unattenuated zones - const QString UNATTENUATED_ZONE_REGEX_STRING = "--unattenuated-zone ([\\d.,-]+)"; - QRegExp unattenuatedZoneMatch(UNATTENUATED_ZONE_REGEX_STRING); + // setup a QNetworkAccessManager to ask the domain-server for our settings + QNetworkAccessManager *networkManager = new QNetworkAccessManager(this); - if (unattenuatedZoneMatch.indexIn(_payload) != -1) { - QString unattenuatedZoneString = unattenuatedZoneMatch.cap(1); - QStringList zoneStringList = unattenuatedZoneString.split(','); + QUrl settingsJSONURL; + settingsJSONURL.setScheme("http"); + settingsJSONURL.setHost(nodeList->getDomainHandler().getHostname()); + settingsJSONURL.setPort(DOMAIN_SERVER_HTTP_PORT); + settingsJSONURL.setPath("/settings.json"); + settingsJSONURL.setQuery(QString("type=%1").arg(_type)); + + QNetworkReply *reply = NULL; + + int failedAttempts = 0; + const int MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS = 5; + + qDebug() << "Requesting settings for assignment from domain-server at" << settingsJSONURL.toString(); + + while (!reply || reply->error() != QNetworkReply::NoError) { + reply = networkManager->get(QNetworkRequest(settingsJSONURL)); - glm::vec3 sourceCorner(zoneStringList[0].toFloat(), zoneStringList[1].toFloat(), zoneStringList[2].toFloat()); - glm::vec3 sourceDimensions(zoneStringList[3].toFloat(), zoneStringList[4].toFloat(), zoneStringList[5].toFloat()); - - glm::vec3 listenerCorner(zoneStringList[6].toFloat(), zoneStringList[7].toFloat(), zoneStringList[8].toFloat()); - glm::vec3 listenerDimensions(zoneStringList[9].toFloat(), zoneStringList[10].toFloat(), zoneStringList[11].toFloat()); + QEventLoop loop; + QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); - _sourceUnattenuatedZone = new AABox(sourceCorner, sourceDimensions); - _listenerUnattenuatedZone = new AABox(listenerCorner, listenerDimensions); + loop.exec(); - glm::vec3 sourceCenter = _sourceUnattenuatedZone->calcCenter(); - glm::vec3 destinationCenter = _listenerUnattenuatedZone->calcCenter(); + ++failedAttempts; - qDebug() << "There is an unattenuated zone with source center at" - << QString("%1, %2, %3").arg(sourceCenter.x).arg(sourceCenter.y).arg(sourceCenter.z); - qDebug() << "Buffers inside this zone will not be attenuated inside a box with center at" - << QString("%1, %2, %3").arg(destinationCenter.x).arg(destinationCenter.y).arg(destinationCenter.z); + if (failedAttempts == MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS) { + qDebug() << "Failed to get settings from domain-server. Bailing on assignment."; + setFinished(true); + return; + } } - - // check the payload to see if we have asked for dynamicJitterBuffer support - const QString DYNAMIC_JITTER_BUFFER_REGEX_STRING = "--dynamicJitterBuffer"; - QRegExp dynamicJitterBufferMatch(DYNAMIC_JITTER_BUFFER_REGEX_STRING); - if (dynamicJitterBufferMatch.indexIn(_payload) != -1) { - qDebug() << "Enable dynamic jitter buffers."; - _useDynamicJitterBuffers = true; - } else { - qDebug() << "Dynamic jitter buffers disabled, using old behavior."; + + QJsonObject settingsObject = QJsonDocument::fromJson(reply->readAll()).object(); + + // check the settings object to see if we have anything we can parse out + const QString AUDIO_GROUP_KEY = "audio"; + + if (settingsObject.contains(AUDIO_GROUP_KEY)) { + QJsonObject audioGroupObject = settingsObject[AUDIO_GROUP_KEY].toObject(); + + const QString UNATTENUATED_ZONE_KEY = "unattenuated-zone"; + + QString unattenuatedZoneString = audioGroupObject[UNATTENUATED_ZONE_KEY].toString(); + if (!unattenuatedZoneString.isEmpty()) { + QStringList zoneStringList = unattenuatedZoneString.split(','); + + glm::vec3 sourceCorner(zoneStringList[0].toFloat(), zoneStringList[1].toFloat(), zoneStringList[2].toFloat()); + glm::vec3 sourceDimensions(zoneStringList[3].toFloat(), zoneStringList[4].toFloat(), zoneStringList[5].toFloat()); + + glm::vec3 listenerCorner(zoneStringList[6].toFloat(), zoneStringList[7].toFloat(), zoneStringList[8].toFloat()); + glm::vec3 listenerDimensions(zoneStringList[9].toFloat(), zoneStringList[10].toFloat(), zoneStringList[11].toFloat()); + + _sourceUnattenuatedZone = new AABox(sourceCorner, sourceDimensions); + _listenerUnattenuatedZone = new AABox(listenerCorner, listenerDimensions); + + glm::vec3 sourceCenter = _sourceUnattenuatedZone->calcCenter(); + glm::vec3 destinationCenter = _listenerUnattenuatedZone->calcCenter(); + + qDebug() << "There is an unattenuated zone with source center at" + << QString("%1, %2, %3").arg(sourceCenter.x).arg(sourceCenter.y).arg(sourceCenter.z); + qDebug() << "Buffers inside this zone will not be attenuated inside a box with center at" + << QString("%1, %2, %3").arg(destinationCenter.x).arg(destinationCenter.y).arg(destinationCenter.z); + } + + // check the payload to see if we have asked for dynamicJitterBuffer support + const QString DYNAMIC_JITTER_BUFFER_JSON_KEY = "dynamic-jitter-buffer"; + bool shouldUseDynamicJitterBuffers = audioGroupObject[DYNAMIC_JITTER_BUFFER_JSON_KEY].toBool(); + if (shouldUseDynamicJitterBuffers) { + qDebug() << "Enable dynamic jitter buffers."; + _useDynamicJitterBuffers = true; + } else { + qDebug() << "Dynamic jitter buffers disabled, using old behavior."; + } } int nextFrame = 0; diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index ff33cc206b..3b60ada78b 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -1,8 +1,8 @@ -#nodes-lead { +#nodes-lead, #settings-lead { color: #66CCCC; } -#nodes-lead .lead-line { +#nodes-lead .lead-line, #settings-lead .lead-line { background-color: #66CCCC; } diff --git a/domain-server/resources/web/index.shtml b/domain-server/resources/web/index.shtml index b6ba8f67db..f0315a113f 100644 --- a/domain-server/resources/web/index.shtml +++ b/domain-server/resources/web/index.shtml @@ -18,7 +18,20 @@ - +

Queued Assignments

@@ -31,8 +44,18 @@ + + \ No newline at end of file diff --git a/domain-server/resources/web/js/form2js.min.js b/domain-server/resources/web/js/form2js.min.js new file mode 100755 index 0000000000..f1e610f7c3 --- /dev/null +++ b/domain-server/resources/web/js/form2js.min.js @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010 Maxim Vasiliev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author Maxim Vasiliev + * Date: 09.09.2010 + * Time: 19:02:33 + */ +(function(e,t){if(typeof define==="function"&&define.amd){define(t)}else{e.form2js=t()}})(this,function(){"use strict";function e(e,r,i,s,o,u){u=u?true:false;if(typeof i=="undefined"||i==null)i=true;if(typeof r=="undefined"||r==null)r=".";if(arguments.length<5)o=false;e=typeof e=="string"?document.getElementById(e):e;var a=[],f,l=0;if(e.constructor==Array||typeof NodeList!="undefined"&&e.constructor==NodeList){while(f=e[l++]){a=a.concat(n(f,s,o,u))}}else{a=n(e,s,o,u)}return t(a,i,r)}function t(e,t,n){var r={},i={},s,o,u,a,f,l,c,h,p,d,v,m,g;for(s=0;s1){for(u=0;u-1&&o==l.length-1){p=v.substr(0,v.indexOf("["));h+=p;if(!c[p])c[p]=[];c[p].push(f)}else if(v.indexOf("[")>-1){p=v.substr(0,v.indexOf("["));d=v.replace(/(^([a-z_]+)?\[)|(\]$)/gi,"");h+="_"+p+"_"+d;if(!i[h])i[h]={};if(p!=""&&!c[p])c[p]=[];if(o==l.length-1){if(p==""){c.push(f);i[h][d]=c[c.length-1]}else{c[p].push(f);i[h][d]=c[p][c[p].length-1]}}else{if(!i[h][d]){if(/^[0-9a-z_]+\[?/i.test(l[o+1]))c[p].push({});else c[p].push([]);i[h][d]=c[p][c[p].length-1]}}c=i[h][d]}else{h+=v;if(o0?o:r(e,t,n,s)}function r(e,t,n,r){var s=[],o=e.firstChild;while(o){s=s.concat(i(o,t,n,r));o=o.nextSibling}return s}function i(e,t,n,i){if(e.disabled&&!i)return[];var u,a,f,l=s(e,n);u=t&&t(e);if(u&&u.name){f=[u]}else if(l!=""&&e.nodeName.match(/INPUT|TEXTAREA/i)){a=o(e,i);if(null===a){f=[]}else{f=[{name:l,value:a}]}}else if(l!=""&&e.nodeName.match(/SELECT/i)){a=o(e,i);f=[{name:l.replace(/\[\]$/,""),value:a}]}else{f=r(e,t,n,i)}return f}function s(e,t){if(e.name&&e.name!="")return e.name;else if(t&&e.id&&e.id!="")return e.id;else return""}function o(e,t){if(e.disabled&&!t)return null;switch(e.nodeName){case"INPUT":case"TEXTAREA":switch(e.type.toLowerCase()){case"radio":if(e.checked&&e.value==="false")return false;case"checkbox":if(e.checked&&e.value==="true")return true;if(!e.checked&&e.value==="true")return false;if(e.checked)return e.value;break;case"button":case"reset":case"submit":case"image":return"";break;default:return e.value;break}break;case"SELECT":return u(e);break;default:break}return null}function u(e){var t=e.multiple,n=[],r,i,s;if(!t)return e.value;for(r=e.getElementsByTagName("option"),i=0,s=r.length;i"; - nodesTableBody += "" + data.uuid + ""; - nodesTableBody += "" + (data.pool ? data.pool : "") + ""; - nodesTableBody += "" + data.public.ip + ":" + data.public.port + ""; - nodesTableBody += "" + data.local.ip + ":" + data.local.port + ""; - - var uptimeSeconds = (Date.now() - data.wake_timestamp) / 1000; - nodesTableBody += "" + uptimeSeconds.toLocaleString() + ""; - - nodesTableBody += "" + (typeof data.pending_credits == 'number' ? data.pending_credits.toLocaleString() : 'N/A') + ""; - - nodesTableBody += ""; - nodesTableBody += ""; - }); - - $('#nodes-table tbody').html(nodesTableBody); + $('#nodes-table tbody').html(nodeTemplate(json)); }); $.getJSON("assignments.json", function(json){ - queuedTableBody = ""; - - $.each(json.queued, function (uuid, data) { - queuedTableBody += ""; - queuedTableBody += "" + data.type + ""; - queuedTableBody += "" + uuid + ""; - queuedTableBody += "" + (data.pool ? data.pool : "") + ""; - queuedTableBody += ""; - }); - - $('#assignments-table tbody').html(queuedTableBody); + $('#assignments-table tbody').html(queuedTemplate(json)); }); } diff --git a/domain-server/resources/web/js/underscore-1.5.0.min.js b/domain-server/resources/web/js/underscore-1.5.0.min.js new file mode 100644 index 0000000000..4db9729997 --- /dev/null +++ b/domain-server/resources/web/js/underscore-1.5.0.min.js @@ -0,0 +1,7 @@ +// Underscore.js 1.5.0 +// http://underscorejs.org +// (c) 2009-2011 Jeremy Ashkenas, DocumentCloud Inc. +// (c) 2011-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Underscore may be freely distributed under the MIT license. +!function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,v=e.reduce,h=e.reduceRight,d=e.filter,g=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,w=i.bind,j=function(n){return n instanceof j?n:this instanceof j?(this._wrapped=n,void 0):new j(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=j),exports._=j):n._=j,j.VERSION="1.5.0";var A=j.each=j.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(j.has(n,a)&&t.call(e,n[a],a,n)===r)return};j.map=j.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e.push(t.call(r,n,u,i))}),e)};var E="Reduce of empty array with no initial value";j.reduce=j.foldl=j.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduce===v)return e&&(t=j.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(E);return r},j.reduceRight=j.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduceRight===h)return e&&(t=j.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=j.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(E);return r},j.find=j.detect=function(n,t,r){var e;return O(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},j.filter=j.select=function(n,t,r){var e=[];return null==n?e:d&&n.filter===d?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&e.push(n)}),e)},j.reject=function(n,t,r){return j.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},j.every=j.all=function(n,t,e){t||(t=j.identity);var u=!0;return null==n?u:g&&n.every===g?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var O=j.some=j.any=function(n,t,e){t||(t=j.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};j.contains=j.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:O(n,function(n){return n===t})},j.invoke=function(n,t){var r=o.call(arguments,2),e=j.isFunction(t);return j.map(n,function(n){return(e?t:n[t]).apply(n,r)})},j.pluck=function(n,t){return j.map(n,function(n){return n[t]})},j.where=function(n,t,r){return j.isEmpty(t)?r?void 0:[]:j[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},j.findWhere=function(n,t){return j.where(n,t,!0)},j.max=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.max.apply(Math,n);if(!t&&j.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>e.computed&&(e={value:n,computed:a})}),e.value},j.min=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.min.apply(Math,n);if(!t&&j.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;ae||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.indexi;){var o=i+a>>>1;r.call(e,n[o])=0})})},j.difference=function(n){var t=c.apply(e,o.call(arguments,1));return j.filter(n,function(n){return!j.contains(t,n)})},j.zip=function(){return j.unzip.apply(j,o.call(arguments))},j.unzip=function(){for(var n=j.max(j.pluck(arguments,"length").concat(0)),t=new Array(n),r=0;n>r;r++)t[r]=j.pluck(arguments,""+r);return t},j.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},j.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=j.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},j.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},j.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=new Array(e);e>u;)i[u++]=n,n+=r;return i};var M=function(){};j.bind=function(n,t){var r,e;if(w&&n.bind===w)return w.apply(n,o.call(arguments,1));if(!j.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));M.prototype=n.prototype;var u=new M;M.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},j.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},j.bindAll=function(n){var t=o.call(arguments,1);if(0===t.length)throw new Error("bindAll must be passed function names");return A(t,function(t){n[t]=j.bind(n[t],n)}),n},j.memoize=function(n,t){var r={};return t||(t=j.identity),function(){var e=t.apply(this,arguments);return j.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},j.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},j.defer=function(n){return j.delay.apply(j,[n,1].concat(o.call(arguments,1)))},j.throttle=function(n,t,r){var e,u,i,a=null,o=0;r||(r={});var c=function(){o=new Date,a=null,i=n.apply(e,u)};return function(){var l=new Date;o||r.leading!==!1||(o=l);var f=t-(l-o);return e=this,u=arguments,0>=f?(clearTimeout(a),a=null,o=l,i=n.apply(e,u)):a||r.trailing===!1||(a=setTimeout(c,f)),i}},j.debounce=function(n,t,r){var e,u=null;return function(){var i=this,a=arguments,o=function(){u=null,r||(e=n.apply(i,a))},c=r&&!u;return clearTimeout(u),u=setTimeout(o,t),c&&(e=n.apply(i,a)),e}},j.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},j.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},j.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},j.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},j.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)j.has(n,r)&&t.push(r);return t},j.values=function(n){var t=[];for(var r in n)j.has(n,r)&&t.push(n[r]);return t},j.pairs=function(n){var t=[];for(var r in n)j.has(n,r)&&t.push([r,n[r]]);return t},j.invert=function(n){var t={};for(var r in n)j.has(n,r)&&(t[n[r]]=r);return t},j.functions=j.methods=function(n){var t=[];for(var r in n)j.isFunction(n[r])&&t.push(r);return t.sort()},j.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},j.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},j.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)j.contains(r,u)||(t[u]=n[u]);return t},j.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]===void 0&&(n[r]=t[r])}),n},j.clone=function(n){return j.isObject(n)?j.isArray(n)?n.slice():j.extend({},n):n},j.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof j&&(n=n._wrapped),t instanceof j&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==String(t);case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;var a=n.constructor,o=t.constructor;if(a!==o&&!(j.isFunction(a)&&a instanceof a&&j.isFunction(o)&&o instanceof o))return!1;r.push(n),e.push(t);var c=0,f=!0;if("[object Array]"==u){if(c=n.length,f=c==t.length)for(;c--&&(f=S(n[c],t[c],r,e)););}else{for(var s in n)if(j.has(n,s)&&(c++,!(f=j.has(t,s)&&S(n[s],t[s],r,e))))break;if(f){for(s in t)if(j.has(t,s)&&!c--)break;f=!c}}return r.pop(),e.pop(),f};j.isEqual=function(n,t){return S(n,t,[],[])},j.isEmpty=function(n){if(null==n)return!0;if(j.isArray(n)||j.isString(n))return 0===n.length;for(var t in n)if(j.has(n,t))return!1;return!0},j.isElement=function(n){return!(!n||1!==n.nodeType)},j.isArray=x||function(n){return"[object Array]"==l.call(n)},j.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){j["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),j.isArguments(arguments)||(j.isArguments=function(n){return!(!n||!j.has(n,"callee"))}),"function"!=typeof/./&&(j.isFunction=function(n){return"function"==typeof n}),j.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},j.isNaN=function(n){return j.isNumber(n)&&n!=+n},j.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},j.isNull=function(n){return null===n},j.isUndefined=function(n){return n===void 0},j.has=function(n,t){return f.call(n,t)},j.noConflict=function(){return n._=t,this},j.identity=function(n){return n},j.times=function(n,t,r){for(var e=Array(Math.max(0,n)),u=0;n>u;u++)e[u]=t.call(r,u);return e},j.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var I={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};I.unescape=j.invert(I.escape);var T={escape:new RegExp("["+j.keys(I.escape).join("")+"]","g"),unescape:new RegExp("("+j.keys(I.unescape).join("|")+")","g")};j.each(["escape","unescape"],function(n){j[n]=function(t){return null==t?"":(""+t).replace(T[n],function(t){return I[n][t]})}}),j.result=function(n,t){if(null==n)return void 0;var r=n[t];return j.isFunction(r)?r.call(n):r},j.mixin=function(n){A(j.functions(n),function(t){var r=j[t]=n[t];j.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),D.call(this,r.apply(j,n))}})};var N=0;j.uniqueId=function(n){var t=++N+"";return n?n+t:t},j.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},z=/\\|'|\r|\n|\t|\u2028|\u2029/g;j.template=function(n,t,r){var e;r=j.defaults({},r,j.templateSettings);var u=new RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(z,function(n){return"\\"+B[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=new Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,j);var c=function(n){return e.call(this,n,j)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},j.chain=function(n){return j(n).chain()};var D=function(n){return this._chain?j(n).chain():n};j.mixin(j),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];j.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],D.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];j.prototype[n]=function(){return D.call(this,t.apply(this._wrapped,arguments))}}),j.extend(j.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}.call(this); +//# sourceMappingURL=underscore-min.map \ No newline at end of file diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json new file mode 100644 index 0000000000..227b6bf0cd --- /dev/null +++ b/domain-server/resources/web/settings/describe.json @@ -0,0 +1,20 @@ +{ + "audio": { + "label": "Audio", + "assignment-types": [0], + "settings": { + "unattenuated-zone": { + "label": "Unattenuated Zone", + "help": "Boxes for source and listener (corner x, corner y, corner z, size x, size y, size z, corner x, corner y, corner z, size x, size y, size z)", + "placeholder": "no zone", + "default": "" + }, + "dynamic-jitter-buffer": { + "type": "checkbox", + "label": "Dynamic Jitter Buffers", + "help": "Dynamically buffer client audio based on perceived jitter in packet receipt timing", + "default": false + } + } + } +} \ No newline at end of file diff --git a/domain-server/resources/web/settings/index.shtml b/domain-server/resources/web/settings/index.shtml new file mode 100644 index 0000000000..3bb669b32e --- /dev/null +++ b/domain-server/resources/web/settings/index.shtml @@ -0,0 +1,46 @@ + +

Settings

+
+ +
+ + + +
+
+ + + + + + \ No newline at end of file diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d55a9b52ca..fcfe1ff582 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -41,7 +41,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : _oauthClientID(), _hostname(), _networkReplyUUIDMap(), - _sessionAuthenticationHash() + _sessionAuthenticationHash(), + _settingsManager() { setOrganizationName("High Fidelity"); setOrganizationDomain("highfidelity.io"); @@ -362,7 +363,7 @@ void DomainServer::createStaticAssignmentsForType(Assignment::Type type, const Q QString dashes = payloadKey.size() == 1 ? "-" : "--"; payloadStringList << QString("%1%2 %3").arg(dashes).arg(payloadKey).arg(jsonObject[payloadKey].toString()); } - + configAssignment->setPayload(payloadStringList.join(' ').toUtf8()); addStaticAssignmentToAssignmentHash(configAssignment); @@ -1162,8 +1163,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url } } - // didn't process the request, let the HTTPManager try and handle - return false; + // didn't process the request, let our DomainServerSettingsManager or HTTPManager handle + return _settingsManager.handleHTTPRequest(connection, url); } bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url) { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index b038850b3d..01f44b698e 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -24,6 +24,7 @@ #include #include +#include "DomainServerSettingsManager.h" #include "WalletTransaction.h" #include "PendingAssignedNodeData.h" @@ -110,6 +111,8 @@ private: QString _hostname; QMap _networkReplyUUIDMap; QHash _sessionAuthenticationHash; + + DomainServerSettingsManager _settingsManager; }; #endif // hifi_DomainServer_h diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp new file mode 100644 index 0000000000..d7e2e05ca8 --- /dev/null +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -0,0 +1,179 @@ +// +// DomainServerSettingsManager.cpp +// domain-server/src +// +// Created by Stephen Birarda on 2014-06-24. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "DomainServerSettingsManager.h" + +const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/web/settings/describe.json"; +const QString SETTINGS_CONFIG_FILE_RELATIVE_PATH = "/resources/config.json"; + +DomainServerSettingsManager::DomainServerSettingsManager() : + _descriptionObject(), + _settingsMap() +{ + // load the description object from the settings description + QFile descriptionFile(QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH); + descriptionFile.open(QIODevice::ReadOnly); + + _descriptionObject = QJsonDocument::fromJson(descriptionFile.readAll()).object(); + + // load the existing config file to get the current values + QFile configFile(QCoreApplication::applicationDirPath() + SETTINGS_CONFIG_FILE_RELATIVE_PATH); + configFile.open(QIODevice::ReadOnly); + + _settingsMap = QJsonDocument::fromJson(configFile.readAll()).toVariant().toMap(); +} + +const QString DESCRIPTION_SETTINGS_KEY = "settings"; +const QString SETTING_DEFAULT_KEY = "default"; + +bool DomainServerSettingsManager::handleHTTPRequest(HTTPConnection* connection, const QUrl &url) { + if (connection->requestOperation() == QNetworkAccessManager::PostOperation && url.path() == "/settings.json") { + // this is a POST operation to change one or more settings + QJsonDocument postedDocument = QJsonDocument::fromJson(connection->requestContent()); + QJsonObject postedObject = postedDocument.object(); + + // we recurse one level deep below each group for the appropriate setting + recurseJSONObjectAndOverwriteSettings(postedObject, _settingsMap, _descriptionObject); + + // store whatever the current _settingsMap is to file + persistToFile(); + + // return success to the caller + QString jsonSuccess = "{\"status\": \"success\"}"; + connection->respond(HTTPConnection::StatusCode200, jsonSuccess.toUtf8(), "application/json"); + + return true; + } else if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == "/settings.json") { + // this is a GET operation for our settings + + // check if there is a query parameter for settings affecting a particular type of assignment + const QString SETTINGS_TYPE_QUERY_KEY = "type"; + QUrlQuery settingsQuery(url); + QString typeValue = settingsQuery.queryItemValue(SETTINGS_TYPE_QUERY_KEY); + + QJsonObject responseObject; + + if (typeValue.isEmpty()) { + // combine the description object and our current settings map + responseObject["descriptions"] = _descriptionObject; + responseObject["values"] = QJsonDocument::fromVariant(_settingsMap).object(); + } else { + // convert the string type value to a QJsonValue + QJsonValue queryType = QJsonValue(typeValue.toInt()); + + const QString AFFECTED_TYPES_JSON_KEY = "assignment-types"; + + // enumerate the groups in the description object to find which settings to pass + foreach(const QString& group, _descriptionObject.keys()) { + QJsonObject groupObject = _descriptionObject[group].toObject(); + QJsonObject groupSettingsObject = groupObject[DESCRIPTION_SETTINGS_KEY].toObject(); + + QJsonObject groupResponseObject; + + + foreach(const QString& settingKey, groupSettingsObject.keys()) { + QJsonObject settingObject = groupSettingsObject[settingKey].toObject(); + + QJsonArray affectedTypesArray = settingObject[AFFECTED_TYPES_JSON_KEY].toArray(); + if (affectedTypesArray.isEmpty()) { + affectedTypesArray = groupObject[AFFECTED_TYPES_JSON_KEY].toArray(); + } + + if (affectedTypesArray.contains(queryType)) { + // this is a setting we should include in the responseObject + + // we need to check if the settings map has a value for this setting + QVariant variantValue; + QVariant settingsMapGroupValue = _settingsMap.value(group); + + if (!settingsMapGroupValue.isNull()) { + variantValue = settingsMapGroupValue.toMap().value(settingKey); + } + + if (variantValue.isNull()) { + // no value for this setting, pass the default + groupResponseObject[settingKey] = settingObject[SETTING_DEFAULT_KEY]; + } else { + groupResponseObject[settingKey] = QJsonValue::fromVariant(variantValue); + } + } + } + + if (!groupResponseObject.isEmpty()) { + // set this group's object to the constructed object + responseObject[group] = groupResponseObject; + } + } + + } + + connection->respond(HTTPConnection::StatusCode200, QJsonDocument(responseObject).toJson(), "application/json"); + return true; + } + + return false; +} + +void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, + QVariantMap& settingsVariant, + QJsonObject descriptionObject) { + foreach(const QString& key, postedObject.keys()) { + + QJsonValue rootValue = postedObject[key]; + + // we don't continue if this key is not present in our descriptionObject + if (descriptionObject.contains(key)) { + if (rootValue.isString()) { + settingsVariant[key] = rootValue.toString(); + } else if (rootValue.isBool()) { + settingsVariant[key] = rootValue.toBool(); + } else if (rootValue.isObject()) { + // there's a JSON Object to explore, so attempt to recurse into it + QJsonObject nextDescriptionObject = descriptionObject[key].toObject(); + + if (nextDescriptionObject.contains(DESCRIPTION_SETTINGS_KEY)) { + if (!settingsVariant.contains(key)) { + // we don't have a map below this key yet, so set it up now + settingsVariant[key] = QVariantMap(); + } + + recurseJSONObjectAndOverwriteSettings(rootValue.toObject(), + *reinterpret_cast(settingsVariant[key].data()), + nextDescriptionObject[DESCRIPTION_SETTINGS_KEY].toObject()); + } + } + } + } +} + +QByteArray DomainServerSettingsManager::getJSONSettingsMap() const { + return QJsonDocument::fromVariant(_settingsMap).toJson(); +} + +void DomainServerSettingsManager::persistToFile() { + QFile settingsFile(QCoreApplication::applicationDirPath() + SETTINGS_CONFIG_FILE_RELATIVE_PATH); + + if (settingsFile.open(QIODevice::WriteOnly)) { + settingsFile.write(getJSONSettingsMap()); + } else { + qCritical("Could not write to JSON settings file. Unable to persist settings."); + } +} \ No newline at end of file diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h new file mode 100644 index 0000000000..8b80cad280 --- /dev/null +++ b/domain-server/src/DomainServerSettingsManager.h @@ -0,0 +1,35 @@ +// +// DomainServerSettingsManager.h +// domain-server/src +// +// Created by Stephen Birarda on 2014-06-24. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_DomainServerSettingsManager_h +#define hifi_DomainServerSettingsManager_h + +#include + +#include + +class DomainServerSettingsManager : public QObject, HTTPRequestHandler { + Q_OBJECT +public: + DomainServerSettingsManager(); + bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url); + + QByteArray getJSONSettingsMap() const; +private: + void recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant, + QJsonObject descriptionObject); + void persistToFile(); + + QJsonObject _descriptionObject; + QVariantMap _settingsMap; +}; + +#endif // hifi_DomainServerSettingsManager_h \ No newline at end of file diff --git a/interface/src/location/LocationManager.cpp b/interface/src/location/LocationManager.cpp index 1d783cc8e7..32172d6e38 100644 --- a/interface/src/location/LocationManager.cpp +++ b/interface/src/location/LocationManager.cpp @@ -15,7 +15,7 @@ #include "LocationManager.h" const QString GET_USER_ADDRESS = "/api/v1/users/%1/address"; -const QString GET_PLACE_ADDRESS = "/api/v1/places/%1/address"; +const QString GET_PLACE_ADDRESS = "/api/v1/places/%1"; const QString GET_ADDRESSES = "/api/v1/addresses/%1"; const QString POST_PLACE_CREATE = "/api/v1/places/"; diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp index 4296a096a0..203c54d97a 100644 --- a/interface/src/ui/ModelsBrowser.cpp +++ b/interface/src/ui/ModelsBrowser.cpp @@ -89,6 +89,12 @@ ModelsBrowser::ModelsBrowser(ModelType modelsType, QWidget* parent) : _view.setEditTriggers(QAbstractItemView::NoEditTriggers); _view.setRootIsDecorated(false); _view.setModel(_handler->getModel()); + _view.blockSignals(true); + + // Initialize the search bar + _searchBar = new QLineEdit; + _searchBar->setDisabled(true); + connect(_handler, SIGNAL(doneDownloading()), SLOT(enableSearchBar())); } void ModelsBrowser::applyFilter(const QString &filter) { @@ -130,6 +136,11 @@ void ModelsBrowser::resizeView() { } } +void ModelsBrowser::enableSearchBar() { + _view.blockSignals(false); + _searchBar->setEnabled(true); +} + void ModelsBrowser::browse() { QDialog dialog; dialog.setWindowTitle("Browse models"); @@ -138,12 +149,10 @@ void ModelsBrowser::browse() { QGridLayout* layout = new QGridLayout(&dialog); dialog.setLayout(layout); - QLineEdit* searchBar = new QLineEdit(&dialog); - layout->addWidget(searchBar, 0, 0); - + layout->addWidget(_searchBar, 0, 0); layout->addWidget(&_view, 1, 0); dialog.connect(&_view, SIGNAL(doubleClicked(const QModelIndex&)), SLOT(accept())); - connect(searchBar, SIGNAL(textChanged(const QString&)), SLOT(applyFilter(const QString&))); + connect(_searchBar, SIGNAL(textChanged(const QString&)), SLOT(applyFilter(const QString&))); QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); layout->addWidget(buttons, 2, 0); diff --git a/interface/src/ui/ModelsBrowser.h b/interface/src/ui/ModelsBrowser.h index ff273a45bc..3e832c9dbe 100644 --- a/interface/src/ui/ModelsBrowser.h +++ b/interface/src/ui/ModelsBrowser.h @@ -74,9 +74,11 @@ public slots: private slots: void applyFilter(const QString& filter); void resizeView(); + void enableSearchBar(); private: ModelHandler* _handler; + QLineEdit* _searchBar; QTreeView _view; };