Merge branch 'master' of github.com:highfidelity/hifi into airhockey-hull

This commit is contained in:
Seth Alves 2015-05-18 12:09:10 -07:00
commit 6d9db21d88
19 changed files with 1242 additions and 517 deletions

View file

@ -6,7 +6,8 @@
{ {
"name": "access_token", "name": "access_token",
"label": "Access Token", "label": "Access Token",
"help": "This is an access token generated on the <a href='https://metaverse.highfidelity.com/user/security' target='_blank'>My Security</a> page of your High Fidelity account.<br/>Generate a token with the 'domains' scope and paste it here.<br/>This is required to associate this domain-server with a domain in your account." "help": "This is your OAuth access token to connect this domain-server with your High Fidelity account. <br/>It can be generated by clicking the 'Connect Account' button above.<br/>You can also go to the <a href='https://metaverse.highfidelity.com/user/security' target='_blank'>My Security</a> page of your account and generate a token with the 'domains' scope and paste it here.",
"advanced": true
}, },
{ {
"name": "id", "name": "id",
@ -45,11 +46,12 @@
] ]
}, },
{ {
"label": "Paths", "label": "Places / Paths",
"html_id": "places_paths",
"settings": [ "settings": [
{ {
"name": "paths", "name": "paths",
"label": "", "label": "Paths",
"help": "Clients can enter a path to reach an exact viewpoint in your domain.<br/>Add rows to the table below to map a path to a viewpoint.<br/>The index path ( / ) is where clients will enter if they do not enter an explicit path.", "help": "Clients can enter a path to reach an exact viewpoint in your domain.<br/>Add rows to the table below to map a path to a viewpoint.<br/>The index path ( / ) is where clients will enter if they do not enter an explicit path.",
"type": "table", "type": "table",
"key": { "key": {

View file

@ -62,7 +62,7 @@ span.port {
} }
td.buttons { td.buttons {
width: 14px; width: 30px;
} }
td .glyphicon { td .glyphicon {
@ -78,6 +78,11 @@ td.reorder-buttons .glyphicon {
display: inherit; display: inherit;
} }
td a.glyphicon {
color: black;
text-decoration: none;
}
tr.new-row { tr.new-row {
color: #3c763d; color: #3c763d;
background-color: #dff0d8; background-color: #dff0d8;
@ -86,3 +91,61 @@ tr.new-row {
.highchart-modal .modal-dialog { .highchart-modal .modal-dialog {
width: 650px; width: 650px;
} }
table {
table-layout: fixed;
word-wrap: break-word;
}
#xs-advanced-container {
margin-bottom: 20px;
}
#advanced-toggle-button-xs {
width: 100%;
}
.spinner {
margin: 30px auto 0;
width: 70px;
text-align: center;
}
.spinner > div {
width: 18px;
height: 18px;
background-color: #333;
border-radius: 100%;
display: inline-block;
-webkit-animation: bouncedelay 1.4s infinite ease-in-out;
animation: bouncedelay 1.4s infinite ease-in-out;
/* Prevent first frame from flickering when animation starts */
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.spinner .bounce1 {
-webkit-animation-delay: -0.32s;
animation-delay: -0.32s;
}
.spinner .bounce2 {
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
}
@-webkit-keyframes bouncedelay {
0%, 80%, 100% { -webkit-transform: scale(0.0) }
40% { -webkit-transform: scale(1.0) }
}
@keyframes bouncedelay {
0%, 80%, 100% {
transform: scale(0.0);
-webkit-transform: scale(0.0);
} 40% {
transform: scale(1.0);
-webkit-transform: scale(1.0);
}
}

View file

@ -1,4 +1,12 @@
body.stop-scrolling {
height: 100%;
overflow: hidden; }
.sweet-overlay { .sweet-overlay {
background-color: black;
/* IE8 */
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)";
/* IE8 */
background-color: rgba(0, 0, 0, 0.4); background-color: rgba(0, 0, 0, 0.4);
position: fixed; position: fixed;
left: 0; left: 0;
@ -6,11 +14,11 @@
top: 0; top: 0;
bottom: 0; bottom: 0;
display: none; display: none;
z-index: 1000; } z-index: 10000; }
.sweet-alert { .sweet-alert {
background-color: white; background-color: white;
font-family: sans-serif; font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
width: 478px; width: 478px;
padding: 17px; padding: 17px;
border-radius: 5px; border-radius: 5px;
@ -22,7 +30,7 @@
margin-top: -200px; margin-top: -200px;
overflow: hidden; overflow: hidden;
display: none; display: none;
z-index: 2000; } z-index: 99999; }
@media all and (max-width: 540px) { @media all and (max-width: 540px) {
.sweet-alert { .sweet-alert {
width: auto; width: auto;
@ -36,15 +44,120 @@
text-align: center; text-align: center;
font-weight: 600; font-weight: 600;
text-transform: none; text-transform: none;
position: relative; } position: relative;
margin: 25px 0;
padding: 0;
line-height: 40px;
display: block; }
.sweet-alert p { .sweet-alert p {
color: #797979; color: #797979;
font-size: 16px; font-size: 16px;
text-align: center; text-align: center;
font-weight: 300; font-weight: 300;
position: relative; position: relative;
text-align: inherit;
float: none;
margin: 0; margin: 0;
padding: 0;
line-height: normal; } line-height: normal; }
.sweet-alert fieldset {
border: none;
position: relative; }
.sweet-alert .sa-error-container {
background-color: #f1f1f1;
margin-left: -17px;
margin-right: -17px;
overflow: hidden;
padding: 0 10px;
max-height: 0;
webkit-transition: padding 0.15s, max-height 0.15s;
transition: padding 0.15s, max-height 0.15s; }
.sweet-alert .sa-error-container.show {
padding: 10px 0;
max-height: 100px;
webkit-transition: padding 0.2s, max-height 0.2s;
transition: padding 0.25s, max-height 0.25s; }
.sweet-alert .sa-error-container .icon {
display: inline-block;
width: 24px;
height: 24px;
border-radius: 50%;
background-color: #ea7d7d;
color: white;
line-height: 24px;
text-align: center;
margin-right: 3px; }
.sweet-alert .sa-error-container p {
display: inline-block; }
.sweet-alert .sa-input-error {
position: absolute;
top: 29px;
right: 26px;
width: 20px;
height: 20px;
opacity: 0;
-webkit-transform: scale(0.5);
transform: scale(0.5);
-webkit-transform-origin: 50% 50%;
transform-origin: 50% 50%;
-webkit-transition: all 0.1s;
transition: all 0.1s; }
.sweet-alert .sa-input-error::before, .sweet-alert .sa-input-error::after {
content: "";
width: 20px;
height: 6px;
background-color: #f06e57;
border-radius: 3px;
position: absolute;
top: 50%;
margin-top: -4px;
left: 50%;
margin-left: -9px; }
.sweet-alert .sa-input-error::before {
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg); }
.sweet-alert .sa-input-error::after {
-webkit-transform: rotate(45deg);
transform: rotate(45deg); }
.sweet-alert .sa-input-error.show {
opacity: 1;
-webkit-transform: scale(1);
transform: scale(1); }
.sweet-alert input {
width: 100%;
box-sizing: border-box;
border-radius: 3px;
border: 1px solid #d7d7d7;
height: 43px;
margin-top: 10px;
margin-bottom: 17px;
font-size: 18px;
box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.06);
padding: 0 12px;
display: none;
-webkit-transition: all 0.3s;
transition: all 0.3s; }
.sweet-alert input:focus {
outline: none;
box-shadow: 0px 0px 3px #c4e6f5;
border: 1px solid #b4dbed; }
.sweet-alert input:focus::-moz-placeholder {
transition: opacity 0.3s 0.03s ease;
opacity: 0.5; }
.sweet-alert input:focus:-ms-input-placeholder {
transition: opacity 0.3s 0.03s ease;
opacity: 0.5; }
.sweet-alert input:focus::-webkit-input-placeholder {
transition: opacity 0.3s 0.03s ease;
opacity: 0.5; }
.sweet-alert input::-moz-placeholder {
color: #bdbdbd; }
.sweet-alert input:-ms-input-placeholder {
color: #bdbdbd; }
.sweet-alert input::-webkit-input-placeholder {
color: #bdbdbd; }
.sweet-alert.show-input input {
display: block; }
.sweet-alert button { .sweet-alert button {
background-color: #AEDEF4; background-color: #AEDEF4;
color: white; color: white;
@ -52,6 +165,7 @@
box-shadow: none; box-shadow: none;
font-size: 17px; font-size: 17px;
font-weight: 500; font-weight: 500;
-webkit-border-radius: 4px;
border-radius: 5px; border-radius: 5px;
padding: 10px 32px; padding: 10px 32px;
margin: 26px 5px 0 5px; margin: 26px 5px 0 5px;
@ -71,22 +185,29 @@
background-color: #b6b6b6; } background-color: #b6b6b6; }
.sweet-alert button.cancel:focus { .sweet-alert button.cancel:focus {
box-shadow: rgba(197, 205, 211, 0.8) 0px 0px 2px, rgba(0, 0, 0, 0.0470588) 0px 0px 0px 1px inset !important; } box-shadow: rgba(197, 205, 211, 0.8) 0px 0px 2px, rgba(0, 0, 0, 0.0470588) 0px 0px 0px 1px inset !important; }
.sweet-alert button::-moz-focus-inner {
border: 0; }
.sweet-alert[data-has-cancel-button=false] button { .sweet-alert[data-has-cancel-button=false] button {
box-shadow: none !important; } box-shadow: none !important; }
.sweet-alert .icon { .sweet-alert[data-has-confirm-button=false][data-has-cancel-button=false] {
padding-bottom: 40px; }
.sweet-alert .sa-icon {
width: 80px; width: 80px;
height: 80px; height: 80px;
border: 4px solid gray; border: 4px solid gray;
-webkit-border-radius: 40px;
border-radius: 40px;
border-radius: 50%; border-radius: 50%;
margin: 20px auto; margin: 20px auto;
padding: 0;
position: relative; position: relative;
box-sizing: content-box; } box-sizing: content-box; }
.sweet-alert .icon.error { .sweet-alert .sa-icon.sa-error {
border-color: #F27474; } border-color: #F27474; }
.sweet-alert .icon.error .x-mark { .sweet-alert .sa-icon.sa-error .sa-x-mark {
position: relative; position: relative;
display: block; } display: block; }
.sweet-alert .icon.error .line { .sweet-alert .sa-icon.sa-error .sa-line {
position: absolute; position: absolute;
height: 5px; height: 5px;
width: 47px; width: 47px;
@ -94,37 +215,39 @@
display: block; display: block;
top: 37px; top: 37px;
border-radius: 2px; } border-radius: 2px; }
.sweet-alert .icon.error .line.left { .sweet-alert .sa-icon.sa-error .sa-line.sa-left {
-webkit-transform: rotate(45deg); -webkit-transform: rotate(45deg);
transform: rotate(45deg); transform: rotate(45deg);
left: 17px; } left: 17px; }
.sweet-alert .icon.error .line.right { .sweet-alert .sa-icon.sa-error .sa-line.sa-right {
-webkit-transform: rotate(-45deg); -webkit-transform: rotate(-45deg);
transform: rotate(-45deg); transform: rotate(-45deg);
right: 16px; } right: 16px; }
.sweet-alert .icon.warning { .sweet-alert .sa-icon.sa-warning {
border-color: #F8BB86; } border-color: #F8BB86; }
.sweet-alert .icon.warning .body { .sweet-alert .sa-icon.sa-warning .sa-body {
position: absolute; position: absolute;
width: 5px; width: 5px;
height: 47px; height: 47px;
left: 50%; left: 50%;
top: 10px; top: 10px;
-webkit-border-radius: 2px;
border-radius: 2px; border-radius: 2px;
margin-left: -2px; margin-left: -2px;
background-color: #F8BB86; } background-color: #F8BB86; }
.sweet-alert .icon.warning .dot { .sweet-alert .sa-icon.sa-warning .sa-dot {
position: absolute; position: absolute;
width: 7px; width: 7px;
height: 7px; height: 7px;
-webkit-border-radius: 50%;
border-radius: 50%; border-radius: 50%;
margin-left: -3px; margin-left: -3px;
left: 50%; left: 50%;
bottom: 10px; bottom: 10px;
background-color: #F8BB86; } background-color: #F8BB86; }
.sweet-alert .icon.info { .sweet-alert .sa-icon.sa-info {
border-color: #C9DAE1; } border-color: #C9DAE1; }
.sweet-alert .icon.info::before { .sweet-alert .sa-icon.sa-info::before {
content: ""; content: "";
position: absolute; position: absolute;
width: 5px; width: 5px;
@ -134,7 +257,7 @@
border-radius: 2px; border-radius: 2px;
margin-left: -2px; margin-left: -2px;
background-color: #C9DAE1; } background-color: #C9DAE1; }
.sweet-alert .icon.info::after { .sweet-alert .sa-icon.sa-info::after {
content: ""; content: "";
position: absolute; position: absolute;
width: 7px; width: 7px;
@ -143,17 +266,21 @@
margin-left: -3px; margin-left: -3px;
top: 19px; top: 19px;
background-color: #C9DAE1; } background-color: #C9DAE1; }
.sweet-alert .icon.success { .sweet-alert .sa-icon.sa-success {
border-color: #A5DC86; } border-color: #A5DC86; }
.sweet-alert .icon.success::before, .sweet-alert .icon.success::after { .sweet-alert .sa-icon.sa-success::before, .sweet-alert .sa-icon.sa-success::after {
content: ''; content: '';
-webkit-border-radius: 40px;
border-radius: 40px;
border-radius: 50%; border-radius: 50%;
position: absolute; position: absolute;
width: 60px; width: 60px;
height: 120px; height: 120px;
background: white; background: white;
-webkit-transform: rotate(45deg);
transform: rotate(45deg); } transform: rotate(45deg); }
.sweet-alert .icon.success::before { .sweet-alert .sa-icon.sa-success::before {
-webkit-border-radius: 120px 0 0 120px;
border-radius: 120px 0 0 120px; border-radius: 120px 0 0 120px;
top: -7px; top: -7px;
left: -33px; left: -33px;
@ -161,7 +288,8 @@
transform: rotate(-45deg); transform: rotate(-45deg);
-webkit-transform-origin: 60px 60px; -webkit-transform-origin: 60px 60px;
transform-origin: 60px 60px; } transform-origin: 60px 60px; }
.sweet-alert .icon.success::after { .sweet-alert .sa-icon.sa-success::after {
-webkit-border-radius: 0 120px 120px 0;
border-radius: 0 120px 120px 0; border-radius: 0 120px 120px 0;
top: -11px; top: -11px;
left: 30px; left: 30px;
@ -169,17 +297,19 @@
transform: rotate(-45deg); transform: rotate(-45deg);
-webkit-transform-origin: 0px 60px; -webkit-transform-origin: 0px 60px;
transform-origin: 0px 60px; } transform-origin: 0px 60px; }
.sweet-alert .icon.success .placeholder { .sweet-alert .sa-icon.sa-success .sa-placeholder {
width: 80px; width: 80px;
height: 80px; height: 80px;
border: 4px solid rgba(165, 220, 134, 0.2); border: 4px solid rgba(165, 220, 134, 0.2);
-webkit-border-radius: 40px;
border-radius: 40px;
border-radius: 50%; border-radius: 50%;
box-sizing: content-box; box-sizing: content-box;
position: absolute; position: absolute;
left: -4px; left: -4px;
top: -4px; top: -4px;
z-index: 2; } z-index: 2; }
.sweet-alert .icon.success .fix { .sweet-alert .sa-icon.sa-success .sa-fix {
width: 5px; width: 5px;
height: 90px; height: 90px;
background-color: white; background-color: white;
@ -189,26 +319,26 @@
z-index: 1; z-index: 1;
-webkit-transform: rotate(-45deg); -webkit-transform: rotate(-45deg);
transform: rotate(-45deg); } transform: rotate(-45deg); }
.sweet-alert .icon.success .line { .sweet-alert .sa-icon.sa-success .sa-line {
height: 5px; height: 5px;
background-color: #A5DC86; background-color: #A5DC86;
display: block; display: block;
border-radius: 2px; border-radius: 2px;
position: absolute; position: absolute;
z-index: 2; } z-index: 2; }
.sweet-alert .icon.success .line.tip { .sweet-alert .sa-icon.sa-success .sa-line.sa-tip {
width: 25px; width: 25px;
left: 14px; left: 14px;
top: 46px; top: 46px;
-webkit-transform: rotate(45deg); -webkit-transform: rotate(45deg);
transform: rotate(45deg); } transform: rotate(45deg); }
.sweet-alert .icon.success .line.long { .sweet-alert .sa-icon.sa-success .sa-line.sa-long {
width: 47px; width: 47px;
right: 8px; right: 8px;
top: 38px; top: 38px;
-webkit-transform: rotate(-45deg); -webkit-transform: rotate(-45deg);
transform: rotate(-45deg); } transform: rotate(-45deg); }
.sweet-alert .icon.custom { .sweet-alert .sa-icon.sa-custom {
background-size: contain; background-size: contain;
border-radius: 0; border-radius: 0;
border: none; border: none;
@ -222,238 +352,274 @@
0% { 0% {
transform: scale(0.7); transform: scale(0.7);
-webkit-transform: scale(0.7); } -webkit-transform: scale(0.7); }
45% { 45% {
transform: scale(1.05); transform: scale(1.05);
-webkit-transform: scale(1.05); } -webkit-transform: scale(1.05); }
80% { 80% {
transform: scale(0.95); transform: scale(0.95);
-webkit-tranform: scale(0.95); } -webkit-transform: scale(0.95); }
100% {
transform: scale(1);
-webkit-transform: scale(1); } }
@-moz-keyframes showSweetAlert {
0% {
transform: scale(0.7);
-webkit-transform: scale(0.7); }
45% {
transform: scale(1.05);
-webkit-transform: scale(1.05); }
80% {
transform: scale(0.95);
-webkit-tranform: scale(0.95); }
100% { 100% {
transform: scale(1); transform: scale(1);
-webkit-transform: scale(1); } } -webkit-transform: scale(1); } }
@keyframes showSweetAlert { @keyframes showSweetAlert {
0% { 0% {
transform: scale(0.7); transform: scale(0.7);
-webkit-transform: scale(0.7); } -webkit-transform: scale(0.7); }
45% { 45% {
transform: scale(1.05); transform: scale(1.05);
-webkit-transform: scale(1.05); } -webkit-transform: scale(1.05); }
80% { 80% {
transform: scale(0.95); transform: scale(0.95);
-webkit-tranform: scale(0.95); } -webkit-transform: scale(0.95); }
100% { 100% {
transform: scale(1); transform: scale(1);
-webkit-transform: scale(1); } } -webkit-transform: scale(1); } }
@-webkit-keyframes hideSweetAlert { @-webkit-keyframes hideSweetAlert {
0% { 0% {
transform: scale(1); transform: scale(1);
-webkit-transform: scale(1); } -webkit-transform: scale(1); }
100% { 100% {
transform: scale(0.5); transform: scale(0.5);
-webkit-transform: scale(0.5); } } -webkit-transform: scale(0.5); } }
@-moz-keyframes hideSweetAlert {
0% {
transform: scale(1);
-webkit-transform: scale(1); }
100% {
transform: scale(0.5);
-webkit-transform: scale(0.5); } }
@keyframes hideSweetAlert { @keyframes hideSweetAlert {
0% { 0% {
transform: scale(1); transform: scale(1);
-webkit-transform: scale(1); } -webkit-transform: scale(1); }
100% { 100% {
transform: scale(0.5); transform: scale(0.5);
-webkit-transform: scale(0.5); } } -webkit-transform: scale(0.5); } }
.showSweetAlert {
-webkit-animation: showSweetAlert 0.3s;
-moz-animation: showSweetAlert 0.3s;
animation: showSweetAlert 0.3s; }
.hideSweetAlert { @-webkit-keyframes slideFromTop {
0% {
top: 0%; }
100% {
top: 50%; } }
@keyframes slideFromTop {
0% {
top: 0%; }
100% {
top: 50%; } }
@-webkit-keyframes slideToTop {
0% {
top: 50%; }
100% {
top: 0%; } }
@keyframes slideToTop {
0% {
top: 50%; }
100% {
top: 0%; } }
@-webkit-keyframes slideFromBottom {
0% {
top: 70%; }
100% {
top: 50%; } }
@keyframes slideFromBottom {
0% {
top: 70%; }
100% {
top: 50%; } }
@-webkit-keyframes slideToBottom {
0% {
top: 50%; }
100% {
top: 70%; } }
@keyframes slideToBottom {
0% {
top: 50%; }
100% {
top: 70%; } }
.showSweetAlert[data-animation=pop] {
-webkit-animation: showSweetAlert 0.3s;
animation: showSweetAlert 0.3s; }
.showSweetAlert[data-animation=none] {
-webkit-animation: none;
animation: none; }
.showSweetAlert[data-animation=slide-from-top] {
-webkit-animation: slideFromTop 0.3s;
animation: slideFromTop 0.3s; }
.showSweetAlert[data-animation=slide-from-bottom] {
-webkit-animation: slideFromBottom 0.3s;
animation: slideFromBottom 0.3s; }
.hideSweetAlert[data-animation=pop] {
-webkit-animation: hideSweetAlert 0.2s; -webkit-animation: hideSweetAlert 0.2s;
-moz-animation: hideSweetAlert 0.2s;
animation: hideSweetAlert 0.2s; } animation: hideSweetAlert 0.2s; }
.hideSweetAlert[data-animation=none] {
-webkit-animation: none;
animation: none; }
.hideSweetAlert[data-animation=slide-from-top] {
-webkit-animation: slideToTop 0.4s;
animation: slideToTop 0.4s; }
.hideSweetAlert[data-animation=slide-from-bottom] {
-webkit-animation: slideToBottom 0.3s;
animation: slideToBottom 0.3s; }
@-webkit-keyframes animateSuccessTip { @-webkit-keyframes animateSuccessTip {
0% { 0% {
width: 0; width: 0;
left: 1px; left: 1px;
top: 19px; } top: 19px; }
54% { 54% {
width: 0; width: 0;
left: 1px; left: 1px;
top: 19px; } top: 19px; }
70% { 70% {
width: 50px; width: 50px;
left: -8px; left: -8px;
top: 37px; } top: 37px; }
84% { 84% {
width: 17px; width: 17px;
left: 21px; left: 21px;
top: 48px; } top: 48px; }
100% { 100% {
width: 25px; width: 25px;
left: 14px; left: 14px;
top: 45px; } } top: 45px; } }
@-moz-keyframes animateSuccessTip {
0% {
width: 0;
left: 1px;
top: 19px; }
54% {
width: 0;
left: 1px;
top: 19px; }
70% {
width: 50px;
left: -8px;
top: 37px; }
84% {
width: 17px;
left: 21px;
top: 48px; }
100% {
width: 25px;
left: 14px;
top: 45px; } }
@keyframes animateSuccessTip { @keyframes animateSuccessTip {
0% { 0% {
width: 0; width: 0;
left: 1px; left: 1px;
top: 19px; } top: 19px; }
54% { 54% {
width: 0; width: 0;
left: 1px; left: 1px;
top: 19px; } top: 19px; }
70% { 70% {
width: 50px; width: 50px;
left: -8px; left: -8px;
top: 37px; } top: 37px; }
84% { 84% {
width: 17px; width: 17px;
left: 21px; left: 21px;
top: 48px; } top: 48px; }
100% { 100% {
width: 25px; width: 25px;
left: 14px; left: 14px;
top: 45px; } } top: 45px; } }
@-webkit-keyframes animateSuccessLong { @-webkit-keyframes animateSuccessLong {
0% { 0% {
width: 0; width: 0;
right: 46px; right: 46px;
top: 54px; } top: 54px; }
65% { 65% {
width: 0; width: 0;
right: 46px; right: 46px;
top: 54px; } top: 54px; }
84% { 84% {
width: 55px; width: 55px;
right: 0px; right: 0px;
top: 35px; } top: 35px; }
100% { 100% {
width: 47px; width: 47px;
right: 8px; right: 8px;
top: 38px; } } top: 38px; } }
@-moz-keyframes animateSuccessLong {
0% {
width: 0;
right: 46px;
top: 54px; }
65% {
width: 0;
right: 46px;
top: 54px; }
84% {
width: 55px;
right: 0px;
top: 35px; }
100% {
width: 47px;
right: 8px;
top: 38px; } }
@keyframes animateSuccessLong { @keyframes animateSuccessLong {
0% { 0% {
width: 0; width: 0;
right: 46px; right: 46px;
top: 54px; } top: 54px; }
65% { 65% {
width: 0; width: 0;
right: 46px; right: 46px;
top: 54px; } top: 54px; }
84% { 84% {
width: 55px; width: 55px;
right: 0px; right: 0px;
top: 35px; } top: 35px; }
100% { 100% {
width: 47px; width: 47px;
right: 8px; right: 8px;
top: 38px; } } top: 38px; } }
@-webkit-keyframes rotatePlaceholder { @-webkit-keyframes rotatePlaceholder {
0% { 0% {
transform: rotate(-45deg); transform: rotate(-45deg);
-webkit-transform: rotate(-45deg); } -webkit-transform: rotate(-45deg); }
5% { 5% {
transform: rotate(-45deg); transform: rotate(-45deg);
-webkit-transform: rotate(-45deg); } -webkit-transform: rotate(-45deg); }
12% { 12% {
transform: rotate(-405deg); transform: rotate(-405deg);
-webkit-transform: rotate(-405deg); } -webkit-transform: rotate(-405deg); }
100% { 100% {
transform: rotate(-405deg); transform: rotate(-405deg);
-webkit-transform: rotate(-405deg); } } -webkit-transform: rotate(-405deg); } }
@-moz-keyframes rotatePlaceholder {
0% {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg); }
5% {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg); }
12% {
transform: rotate(-405deg);
-webkit-transform: rotate(-405deg); }
100% {
transform: rotate(-405deg);
-webkit-transform: rotate(-405deg); } }
@keyframes rotatePlaceholder { @keyframes rotatePlaceholder {
0% { 0% {
transform: rotate(-45deg); transform: rotate(-45deg);
-webkit-transform: rotate(-45deg); } -webkit-transform: rotate(-45deg); }
5% { 5% {
transform: rotate(-45deg); transform: rotate(-45deg);
-webkit-transform: rotate(-45deg); } -webkit-transform: rotate(-45deg); }
12% { 12% {
transform: rotate(-405deg); transform: rotate(-405deg);
-webkit-transform: rotate(-405deg); } -webkit-transform: rotate(-405deg); }
100% { 100% {
transform: rotate(-405deg); transform: rotate(-405deg);
-webkit-transform: rotate(-405deg); } } -webkit-transform: rotate(-405deg); } }
.animateSuccessTip { .animateSuccessTip {
-webkit-animation: animateSuccessTip 0.75s; -webkit-animation: animateSuccessTip 0.75s;
-moz-animation: animateSuccessTip 0.75s;
animation: animateSuccessTip 0.75s; } animation: animateSuccessTip 0.75s; }
.animateSuccessLong { .animateSuccessLong {
-webkit-animation: animateSuccessLong 0.75s; -webkit-animation: animateSuccessLong 0.75s;
-moz-animation: animateSuccessLong 0.75s;
animation: animateSuccessLong 0.75s; } animation: animateSuccessLong 0.75s; }
.icon.success.animate::after { .sa-icon.sa-success.animate::after {
-webkit-animation: rotatePlaceholder 4.25s ease-in; -webkit-animation: rotatePlaceholder 4.25s ease-in;
-moz-animation: rotatePlaceholder 4.25s ease-in;
animation: rotatePlaceholder 4.25s ease-in; } animation: rotatePlaceholder 4.25s ease-in; }
@-webkit-keyframes animateErrorIcon { @-webkit-keyframes animateErrorIcon {
@ -461,31 +627,25 @@
transform: rotateX(100deg); transform: rotateX(100deg);
-webkit-transform: rotateX(100deg); -webkit-transform: rotateX(100deg);
opacity: 0; } opacity: 0; }
100% { 100% {
transform: rotateX(0deg); transform: rotateX(0deg);
-webkit-transform: rotateX(0deg); -webkit-transform: rotateX(0deg);
opacity: 1; } } opacity: 1; } }
@-moz-keyframes animateErrorIcon {
0% {
transform: rotateX(100deg);
-webkit-transform: rotateX(100deg);
opacity: 0; }
100% {
transform: rotateX(0deg);
-webkit-transform: rotateX(0deg);
opacity: 1; } }
@keyframes animateErrorIcon { @keyframes animateErrorIcon {
0% { 0% {
transform: rotateX(100deg); transform: rotateX(100deg);
-webkit-transform: rotateX(100deg); -webkit-transform: rotateX(100deg);
opacity: 0; } opacity: 0; }
100% { 100% {
transform: rotateX(0deg); transform: rotateX(0deg);
-webkit-transform: rotateX(0deg); -webkit-transform: rotateX(0deg);
opacity: 1; } } opacity: 1; } }
.animateErrorIcon { .animateErrorIcon {
-webkit-animation: animateErrorIcon 0.5s; -webkit-animation: animateErrorIcon 0.5s;
-moz-animation: animateErrorIcon 0.5s;
animation: animateErrorIcon 0.5s; } animation: animateErrorIcon 0.5s; }
@-webkit-keyframes animateXMark { @-webkit-keyframes animateXMark {
@ -494,108 +654,104 @@
-webkit-transform: scale(0.4); -webkit-transform: scale(0.4);
margin-top: 26px; margin-top: 26px;
opacity: 0; } opacity: 0; }
50% { 50% {
transform: scale(0.4); transform: scale(0.4);
-webkit-transform: scale(0.4); -webkit-transform: scale(0.4);
margin-top: 26px; margin-top: 26px;
opacity: 0; } opacity: 0; }
80% { 80% {
transform: scale(1.15); transform: scale(1.15);
-webkit-transform: scale(1.15); -webkit-transform: scale(1.15);
margin-top: -6px; } margin-top: -6px; }
100% { 100% {
transform: scale(1); transform: scale(1);
-webkit-transform: scale(1); -webkit-transform: scale(1);
margin-top: 0; margin-top: 0;
opacity: 1; } } opacity: 1; } }
@-moz-keyframes animateXMark {
0% {
transform: scale(0.4);
-webkit-transform: scale(0.4);
margin-top: 26px;
opacity: 0; }
50% {
transform: scale(0.4);
-webkit-transform: scale(0.4);
margin-top: 26px;
opacity: 0; }
80% {
transform: scale(1.15);
-webkit-transform: scale(1.15);
margin-top: -6px; }
100% {
transform: scale(1);
-webkit-transform: scale(1);
margin-top: 0;
opacity: 1; } }
@keyframes animateXMark { @keyframes animateXMark {
0% { 0% {
transform: scale(0.4); transform: scale(0.4);
-webkit-transform: scale(0.4); -webkit-transform: scale(0.4);
margin-top: 26px; margin-top: 26px;
opacity: 0; } opacity: 0; }
50% { 50% {
transform: scale(0.4); transform: scale(0.4);
-webkit-transform: scale(0.4); -webkit-transform: scale(0.4);
margin-top: 26px; margin-top: 26px;
opacity: 0; } opacity: 0; }
80% { 80% {
transform: scale(1.15); transform: scale(1.15);
-webkit-transform: scale(1.15); -webkit-transform: scale(1.15);
margin-top: -6px; } margin-top: -6px; }
100% { 100% {
transform: scale(1); transform: scale(1);
-webkit-transform: scale(1); -webkit-transform: scale(1);
margin-top: 0; margin-top: 0;
opacity: 1; } } opacity: 1; } }
.animateXMark { .animateXMark {
-webkit-animation: animateXMark 0.5s; -webkit-animation: animateXMark 0.5s;
-moz-animation: animateXMark 0.5s;
animation: animateXMark 0.5s; } animation: animateXMark 0.5s; }
/*@include keyframes(simpleRotate) {
0% { transform: rotateY(0deg); }
100% { transform: rotateY(-360deg); }
}
.simpleRotate {
@include animation('simpleRotate 0.75s');
}*/
@-webkit-keyframes pulseWarning { @-webkit-keyframes pulseWarning {
0% { 0% {
border-color: #F8D486; } border-color: #F8D486; }
100% { 100% {
border-color: #F8BB86; } } border-color: #F8BB86; } }
@-moz-keyframes pulseWarning {
0% {
border-color: #F8D486; }
100% {
border-color: #F8BB86; } }
@keyframes pulseWarning { @keyframes pulseWarning {
0% { 0% {
border-color: #F8D486; } border-color: #F8D486; }
100% { 100% {
border-color: #F8BB86; } } border-color: #F8BB86; } }
.pulseWarning { .pulseWarning {
-webkit-animation: pulseWarning 0.75s infinite alternate; -webkit-animation: pulseWarning 0.75s infinite alternate;
-moz-animation: pulseWarning 0.75s infinite alternate;
animation: pulseWarning 0.75s infinite alternate; } animation: pulseWarning 0.75s infinite alternate; }
@-webkit-keyframes pulseWarningIns { @-webkit-keyframes pulseWarningIns {
0% { 0% {
background-color: #F8D486; } background-color: #F8D486; }
100% { 100% {
background-color: #F8BB86; } } background-color: #F8BB86; } }
@-moz-keyframes pulseWarningIns {
0% {
background-color: #F8D486; }
100% {
background-color: #F8BB86; } }
@keyframes pulseWarningIns { @keyframes pulseWarningIns {
0% { 0% {
background-color: #F8D486; } background-color: #F8D486; }
100% { 100% {
background-color: #F8BB86; } } background-color: #F8BB86; } }
.pulseWarningIns { .pulseWarningIns {
-webkit-animation: pulseWarningIns 0.75s infinite alternate; -webkit-animation: pulseWarningIns 0.75s infinite alternate;
-moz-animation: pulseWarningIns 0.75s infinite alternate;
animation: pulseWarningIns 0.75s infinite alternate; } animation: pulseWarningIns 0.75s infinite alternate; }
/* Internet Explorer 9 has some special quirks that are fixed here */
/* The icons are not animated. */
/* This file is automatically merged into sweet-alert.min.js through Gulp */
/* Error icon */
.sweet-alert .sa-icon.sa-error .sa-line.sa-left {
-ms-transform: rotate(45deg) \9; }
.sweet-alert .sa-icon.sa-error .sa-line.sa-right {
-ms-transform: rotate(-45deg) \9; }
/* Success icon */
.sweet-alert .sa-icon.sa-success {
border-color: transparent\9; }
.sweet-alert .sa-icon.sa-success .sa-line.sa-tip {
-ms-transform: rotate(45deg) \9; }
.sweet-alert .sa-icon.sa-success .sa-line.sa-long {
-ms-transform: rotate(-45deg) \9; }

View file

@ -3,10 +3,10 @@
<head> <head>
<title>domain-server</title> <title>domain-server</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/css/bootstrap.min.css" rel="stylesheet" media="screen"> <link href="/css/bootstrap.min.css" rel="stylesheet" media="screen">
<link href="/css/style.css" rel="stylesheet" media="screen"> <link href="/css/style.css" rel="stylesheet" media="screen">
<link href="/css/sweet-alert.css" rel="stylesheet" media="screen"> <link href="/css/sweetalert.css" rel="stylesheet" media="screen">
<link href="/stats/css/json.human.css" rel="stylesheet" media="screen"> <link href="/stats/css/json.human.css" rel="stylesheet" media="screen">
</head> </head>
<body> <body>
@ -22,7 +22,7 @@
</button> </button>
<a class="navbar-brand" href="/">domain-server</a> <a class="navbar-brand" href="/">domain-server</a>
</div> </div>
<!-- Collect the nav links, forms, and other content for toggling --> <!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">

View file

@ -0,0 +1,5 @@
function qs(key) {
key = key.replace(/[*+?^$.\[\]{}()|\\\/]/g, "\\$&"); // escape RegEx meta chars
var match = location.search.match(new RegExp("[?&]"+key+"=([^&]+)(&|$)"));
return match && decodeURIComponent(match[1].replace(/\+/g, " "));
}

View file

@ -1,5 +1,6 @@
var Settings = { var Settings = {
showAdvanced: false, showAdvanced: false,
METAVERSE_URL: 'https://metaverse.highfidelity.com',
ADVANCED_CLASS: 'advanced-setting', ADVANCED_CLASS: 'advanced-setting',
TRIGGER_CHANGE_CLASS: 'trigger-change', TRIGGER_CHANGE_CLASS: 'trigger-change',
DATA_ROW_CLASS: 'value-row', DATA_ROW_CLASS: 'value-row',
@ -17,16 +18,24 @@ var Settings = {
ADD_DEL_BUTTONS_CLASSES: 'buttons add-del-buttons', ADD_DEL_BUTTONS_CLASSES: 'buttons add-del-buttons',
REORDER_BUTTONS_CLASS: 'reorder-buttons', REORDER_BUTTONS_CLASS: 'reorder-buttons',
REORDER_BUTTONS_CLASSES: 'buttons reorder-buttons', REORDER_BUTTONS_CLASSES: 'buttons reorder-buttons',
NEW_ROW_CLASS: 'new-row' NEW_ROW_CLASS: 'new-row',
CONNECT_ACCOUNT_BTN_ID: 'connect-account-btn',
DISCONNECT_ACCOUNT_BTN_ID: 'disconnect-account-btn',
CREATE_DOMAIN_ID_BTN_ID: 'create-domain-btn',
CHOOSE_DOMAIN_ID_BTN_ID: 'choose-domain-btn',
GET_TEMPORARY_NAME_BTN_ID: 'get-temp-name-btn',
DOMAIN_ID_SELECTOR: '[name="metaverse.id"]',
ACCESS_TOKEN_SELECTOR: '[name="metaverse.access_token"]',
PLACES_TABLE_ID: 'places-table',
FORM_ID: 'settings-form'
}; };
var viewHelpers = { var viewHelpers = {
getFormGroup: function(keypath, setting, values, isAdvanced, isLocked) { getFormGroup: function(keypath, setting, values, isAdvanced, isLocked) {
form_group = "<div class='form-group " + (isAdvanced ? Settings.ADVANCED_CLASS : "") + "'>"; form_group = "<div class='form-group " + (isAdvanced ? Settings.ADVANCED_CLASS : "") + "' data-keypath='" + keypath + "'>";
setting_value = _(values).valueForKeyPath(keypath); setting_value = _(values).valueForKeyPath(keypath);
if (setting_value === undefined || setting_value === null) { if (typeof setting_value == 'undefined' || setting_value === null) {
if (_.has(setting, 'default')) { if (_.has(setting, 'default')) {
setting_value = setting.default; setting_value = setting.default;
} else { } else {
@ -39,9 +48,13 @@ var viewHelpers = {
label_class += ' locked'; label_class += ' locked';
} }
common_attrs = " class='" + (setting.type !== 'checkbox' ? 'form-control' : '') function common_attrs(extra_classes) {
+ " " + Settings.TRIGGER_CHANGE_CLASS + "' data-short-name='" + setting.name + "' name='" + keypath + "' " extra_classes = (typeof extra_classes !== 'undefined' ? extra_classes : "");
+ "id='" + keypath + "'"; return " class='" + (setting.type !== 'checkbox' ? 'form-control' : '')
+ " " + Settings.TRIGGER_CHANGE_CLASS + " " + extra_classes + "' data-short-name='"
+ setting.name + "' name='" + keypath + "' "
+ "id='" + (typeof setting.html_id !== 'undefined' ? setting.html_id : keypath) + "'";
}
if (setting.type === 'checkbox') { if (setting.type === 'checkbox') {
if (setting.label) { if (setting.label) {
@ -49,7 +62,7 @@ var viewHelpers = {
} }
form_group += "<div class='checkbox" + (isLocked ? " disabled" : "") + "'>" form_group += "<div class='checkbox" + (isLocked ? " disabled" : "") + "'>"
form_group += "<label for='" + keypath + "'>" form_group += "<label for='" + keypath + "'>"
form_group += "<input type='checkbox'" + common_attrs + (setting_value ? "checked" : "") + (isLocked ? " disabled" : "") + "/>" form_group += "<input type='checkbox'" + common_attrs() + (setting_value ? "checked" : "") + (isLocked ? " disabled" : "") + "/>"
form_group += " " + setting.help + "</label>"; form_group += " " + setting.help + "</label>";
form_group += "</div>" form_group += "</div>"
} else { } else {
@ -72,14 +85,29 @@ var viewHelpers = {
form_group += "</select>" form_group += "</select>"
form_group += "<input type='hidden'" + common_attrs + "value='" + setting_value + "'>" form_group += "<input type='hidden'" + common_attrs() + "value='" + setting_value + "'>"
} else if (input_type === 'button') {
// Is this a button that should link to something directly?
// If so, we use an anchor tag instead of a button tag
if (setting.href) {
form_group += "<a href='" + setting.href + "'style='display: block;' role='button'"
+ (isLocked ? " disabled" : "")
+ common_attrs("btn " + setting.classes) + " target='_blank'>"
+ setting.button_label + "</a>";
} else {
form_group += "<button " + common_attrs("btn " + setting.classes)
+ (isLocked ? " disabled" : "") + ">"
+ setting.button_label + "</button>";
}
} else { } else {
if (input_type == 'integer') { if (input_type == 'integer') {
input_type = "text" input_type = "text"
} }
form_group += "<input type='" + input_type + "'" + common_attrs + form_group += "<input type='" + input_type + "'" + common_attrs() +
"placeholder='" + (_.has(setting, 'placeholder') ? setting.placeholder : "") + "placeholder='" + (_.has(setting, 'placeholder') ? setting.placeholder : "") +
"' value='" + setting_value + "'" + (isLocked ? " disabled" : "") + "/>" "' value='" + setting_value + "'" + (isLocked ? " disabled" : "") + "/>"
} }
@ -112,71 +140,107 @@ $(document).ready(function(){
resizeFn(); resizeFn();
$(window).resize(resizeFn); $(window).resize(resizeFn);
}) });
$('#settings-form').on('click', '.' + Settings.ADD_ROW_BUTTON_CLASS, function(){ $('#' + Settings.FORM_ID).on('click', '.' + Settings.ADD_ROW_BUTTON_CLASS, function(){
addTableRow(this); addTableRow(this);
}) });
$('#settings-form').on('click', '.' + Settings.DEL_ROW_BUTTON_CLASS, function(){ $('#' + Settings.FORM_ID).on('click', '.' + Settings.DEL_ROW_BUTTON_CLASS, function(){
deleteTableRow(this); deleteTableRow(this);
}) });
$('#settings-form').on('click', '.' + Settings.MOVE_UP_BUTTON_CLASS, function(){ $('#' + Settings.FORM_ID).on('click', '.' + Settings.MOVE_UP_BUTTON_CLASS, function(){
moveTableRow(this, true); moveTableRow(this, true);
}) });
$('#settings-form').on('click', '.' + Settings.MOVE_DOWN_BUTTON_CLASS, function(){ $('#' + Settings.FORM_ID).on('click', '.' + Settings.MOVE_DOWN_BUTTON_CLASS, function(){
moveTableRow(this, false); moveTableRow(this, false);
}) });
$('#settings-form').on('keypress', 'table input', function(e){ $('#' + Settings.FORM_ID).on('keyup', function(e){
var $target = $(e.target);
if (e.keyCode == 13) { if (e.keyCode == 13) {
// capture enter in table input if ($target.is('table input')) {
// if we have a sibling next to us that has an input, jump to it, otherwise check if we have a glyphicon for add to click // capture enter in table input
sibling = $(this).parent('td').next(); // if we have a sibling next to us that has an input, jump to it, otherwise check if we have a glyphicon for add to click
sibling = $target.parent('td').next();
if (sibling.hasClass(Settings.DATA_COL_CLASS)) { if (sibling.hasClass(Settings.DATA_COL_CLASS)) {
// set focus to next input // set focus to next input
sibling.find('input').focus() sibling.find('input').focus()
} else if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) { } else if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) {
sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click() sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click()
// set focus to the first input in the new row // set focus to the first input in the new row
$(this).closest('table').find('tr.inputs input:first').focus() $target.closest('table').find('tr.inputs input:first').focus()
}
} else if ($target.is('input')) {
$target.change().blur();
} }
e.preventDefault();
} }
}); });
$('#settings-form').on('change', '.' + Settings.TRIGGER_CHANGE_CLASS , function(){ $('#' + Settings.FORM_ID).on('keypress', function(e){
if (e.keyCode == 13) {
e.preventDefault();
}
});
$('#' + Settings.FORM_ID).on('change', '.' + Settings.TRIGGER_CHANGE_CLASS , function(){
// this input was changed, add the changed data attribute to it // this input was changed, add the changed data attribute to it
$(this).attr('data-changed', true) $(this).attr('data-changed', true)
badgeSidebarForDifferences($(this)) badgeSidebarForDifferences($(this))
}) })
$('#advanced-toggle-button').click(function(){ $('.advanced-toggle').click(function(){
Settings.showAdvanced = !Settings.showAdvanced Settings.showAdvanced = !Settings.showAdvanced
var advancedSelector = $('.' + Settings.ADVANCED_CLASS) var advancedSelector = $('.' + Settings.ADVANCED_CLASS)
if (Settings.showAdvanced) { if (Settings.showAdvanced) {
advancedSelector.show() advancedSelector.show();
$(this).html("Hide advanced") $(this).html("Hide advanced")
} else { } else {
advancedSelector.hide() advancedSelector.hide();
$(this).html("Show advanced") $(this).html("Show advanced")
} }
$(this).blur() $(this).blur();
}) })
$('#settings-form').on('click', '#choose-domain-btn', function(){ $('#' + Settings.FORM_ID).on('click', '#' + Settings.CREATE_DOMAIN_ID_BTN_ID, function(){
$(this).blur();
showDomainCreationAlert(false);
})
$('#' + Settings.FORM_ID).on('click', '#' + Settings.CHOOSE_DOMAIN_ID_BTN_ID, function(){
$(this).blur();
chooseFromHighFidelityDomains($(this)) chooseFromHighFidelityDomains($(this))
}) });
$('#settings-form').on('change', 'select', function(){ $('#' + Settings.FORM_ID).on('click', '#' + Settings.GET_TEMPORARY_NAME_BTN_ID, function(){
$(this).blur();
createTemporaryDomain();
});
$('#' + Settings.FORM_ID).on('change', 'select', function(){
$("input[name='" + $(this).attr('data-hidden-input') + "']").val($(this).val()).change() $("input[name='" + $(this).attr('data-hidden-input') + "']").val($(this).val()).change()
}) });
$('#' + Settings.FORM_ID).on('click', '#' + Settings.DISCONNECT_ACCOUNT_BTN_ID, function(e){
disonnectHighFidelityAccount();
});
$('#' + Settings.FORM_ID).on('click', '#' + Settings.CONNECT_ACCOUNT_BTN_ID, function(e){
$(this).blur();
prepareAccessTokenPrompt();
});
var panelsSource = $('#panels-template').html() var panelsSource = $('#panels-template').html()
Settings.panelsTemplate = _.template(panelsSource) Settings.panelsTemplate = _.template(panelsSource)
@ -187,7 +251,460 @@ $(document).ready(function(){
// $('body').scrollspy({ target: '#setup-sidebar'}) // $('body').scrollspy({ target: '#setup-sidebar'})
reloadSettings(); reloadSettings();
}) });
function dynamicButton(button_id, text) {
return $("<button type='button' id='" + button_id + "' class='btn btn-primary'>" + text + "</button>");
}
function postSettings(jsonSettings) {
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
$.ajax('/settings.json', {
data: JSON.stringify(jsonSettings),
contentType: 'application/json',
type: 'POST'
}).done(function(data){
if (data.status == "success") {
showRestartModal();
} else {
showErrorMessage("Error", SETTINGS_ERROR_MESSAGE)
reloadSettings();
}
}).fail(function(){
showErrorMessage("Error", SETTINGS_ERROR_MESSAGE)
reloadSettings();
});
}
function setupHFAccountButton() {
// figure out how we should handle the HF connect button
var accessToken = Settings.data.values.metaverse.access_token;
// setup an object for the settings we want our button to have
var buttonSetting = {
type: 'button',
name: 'connected_account',
label: 'Connected Account',
}
var hasAccessToken = accessToken.length > 0;
if (hasAccessToken) {
buttonSetting.help = "Click the button above to clear your OAuth token and disconnect your High Fidelity account.";
buttonSetting.classes = "btn-danger";
buttonSetting.button_label = "Disconnect High Fidelity Account";
buttonSetting.html_id = Settings.DISCONNECT_ACCOUNT_BTN_ID;
} else {
buttonSetting.help = "Click the button above to connect your High Fidelity account.";
buttonSetting.classes = "btn-primary";
buttonSetting.button_label = "Connect High Fidelity Account";
buttonSetting.html_id = Settings.CONNECT_ACCOUNT_BTN_ID;
buttonSetting.href = Settings.METAVERSE_URL + "/user/tokens/new?for_domain_server=true";
// since we do not have an access token we change hide domain ID and auto networking settings
// without an access token niether of them can do anything
$("[data-keypath='metaverse.id']").hide();
$("[data-keypath='metaverse.automatic_networking']").hide();
}
var tokenLocked = _(Settings.data).valueForKeyPath("locked.metaverse.access_token");
// use the existing getFormGroup helper to ask for a button
var buttonGroup = viewHelpers.getFormGroup('', buttonSetting, Settings.data.values, false, tokenLocked);
// add the button group to the top of the metaverse panel
$('#metaverse .panel-body').prepend(buttonGroup);
}
function disonnectHighFidelityAccount() {
// the user clicked on the disconnect account btn - give them a sweet alert to make sure this is what they want to do
swal({
title: "Are you sure?",
text: "This will remove your domain-server OAuth access token."
+ "</br></br>This could cause your domain to appear offline and no longer be reachable via any place names.",
type: "warning",
html: true,
showCancelButton: true,
}, function(){
// we need to post to settings to clear the access-token
$(Settings.ACCESS_TOKEN_SELECTOR).val('').change();
saveSettings();
});
}
function prepareAccessTokenPrompt() {
swal({
title: "Connect Account",
type: "input",
text: "Paste your created access token here." +
"</br></br>If you did not successfully create an access token click cancel below and attempt to connect your account again.</br></br>",
showCancelButton: true,
closeOnConfirm: false,
html: true
}, function(inputValue){
if (inputValue === false) return false;
if (inputValue === "") {
swal.showInputError("Please paste your access token in the input field.")
return false
}
// we have an input value - set the access token input with this and save settings
$(Settings.ACCESS_TOKEN_SELECTOR).val(inputValue).change();
// if the user doesn't have a domain ID set, give them the option to create one now
if (!Settings.data.values.metaverse.id) {
// show domain ID selection alert
showDomainIDChoiceAlert();
} else {
swal.close();
saveSettings();
}
});
}
function showDomainIDChoiceAlert() {
swal({
title: 'Domain ID',
type: 'info',
text: "You do not currently have a domain ID." +
"</br></br>This is required to point place names at your domain and to use automatic networking.</br></br>" +
"Would you like to create a domain ID via the Metaverse API?</br></br>",
showCancelButton: true,
confirmButtonText: "Create new domain ID",
cancelButtonText: "Skip",
closeOnConfirm: false,
html: true
}, function(isConfirm){
if (isConfirm) {
// show the swal to create a new domain via API
showDomainCreationAlert(true);
} else {
// user cancelled, close this swal and save the access token we got
swal.close();
saveSettings();
}
});
}
function showSpinnerAlert(title) {
swal({
title: title,
text: '<div class="spinner" style="color:black;"><div class="bounce1"></div><div class="bounce2"></div><div class="bounce3"></div></div>',
html: true,
showConfirmButton: false,
allowEscapeKey: false
});
}
function showDomainCreationAlert(justConnected) {
swal({
title: 'Create new domain ID',
type: 'input',
text: 'Enter a short description for this machine.</br></br>This will help you identify which domain ID belongs to which machine.</br></br>',
showCancelButton: true,
confirmButtonText: "Create",
closeOnConfirm: false,
html: true
}, function(inputValue){
if (inputValue === false) {
swal.close();
// user cancelled domain ID creation - if we're supposed to save after cancel then save here
if (justConnected) {
saveSettings();
}
} else {
// we're going to change the alert to a new one with a spinner while we create this domain
showSpinnerAlert('Creating domain ID');
createNewDomainID(inputValue, justConnected);
}
});
}
function createNewDomainID(description, justConnected) {
// get the JSON object ready that we'll use to create a new domain
var domainJSON = {
"domain": {
"description": description
},
"access_token": $(Settings.ACCESS_TOKEN_SELECTOR).val()
}
$.post(Settings.METAVERSE_URL + "/api/v1/domains", domainJSON, function(data){
if (data.status == "success") {
// we successfully created a domain ID, set it on that field
var domainID = data.domain.id;
$(Settings.DOMAIN_ID_SELECTOR).val(domainID).change();
if (justConnected) {
var successText = "We connnected your High Fidelity account and created a new domain ID for this machine."
} else {
var successText = "We created a new domain ID for this machine."
}
successText += "</br></br>Click the button below to save your new settings and restart your domain-server.";
// show a sweet alert to say we are all finished up and that we need to save
swal({
title: 'Success!',
type: 'success',
text: successText,
html: true,
confirmButtonText: 'Save'
}, function(){
saveSettings();
});
}
}).fail(function(){
var errorText = "There was a problem creating your new domain ID. Do you want to try again or";
if (justConnected) {
errorText += " just save your new access token?</br></br>You can always create a new domain ID later.";
} else {
errorText += " cancel?"
}
// we failed to create the new domain ID, show a sweet-alert that lets them try again or cancel
swal({
title: '',
type: 'error',
text: errorText,
html: true,
confirmButtonText: 'Try again',
showCancelButton: true,
closeOnConfirm: false
}, function(isConfirm){
if (isConfirm) {
// they want to try creating a domain ID again
showDomainCreationAlert(justConnected);
} else {
// they want to cancel
if (justConnected) {
// since they just connected we need to save the access token here
saveSettings();
}
}
});
});
}
function setupPlacesTable() {
// create a dummy table using our view helper
var placesTableSetting = {
type: 'table',
name: 'places',
label: 'Places',
html_id: Settings.PLACES_TABLE_ID,
help: "The following places currently point to this domain.</br>To point places to this domain, "
+ " go to the <a href='https://metaverse.highfidelity.com/user/places'>My Places</a> "
+ "page in your High Fidelity Metaverse account.",
read_only: true,
columns: [
{
"name": "name",
"label": "Name"
},
{
"name": "path",
"label": "Path"
},
{
"name": "edit",
"label": "",
"class": "buttons"
}
]
}
// get a table for the places
var placesTableGroup = viewHelpers.getFormGroup('', placesTableSetting, Settings.data.values, false, false);
// append the places table in the right place
$('#places_paths .panel-body').prepend(placesTableGroup);
// do we have a domain ID?
if (Settings.data.values.metaverse.id.length > 0) {
// now, ask the API for what places, if any, point to this domain
reloadPlacesOrTemporaryName();
} else {
// we don't have a domain ID - add a button to offer the user a chance to get a temporary one
var temporaryPlaceButton = dynamicButton(Settings.GET_TEMPORARY_NAME_BTN_ID, 'Get a temporary place name');
$('#' + Settings.PLACES_TABLE_ID).after(temporaryPlaceButton);
}
}
function placeTableRow(name, path, isTemporary) {
var name_link = "<a href='hifi://" + name + "'>" + (isTemporary ? name + " (temporary)" : name) + "</a>";
if (isTemporary) {
var editColumn = "<td class='buttons'></td>";
} else {
var editColumn = "<td class='buttons'><a class='glyphicon glyphicon-pencil'"
+ " href='" + Settings.METAVERSE_URL + "/user/places/" + name + "/edit" + "'</a></td>";
}
return "<tr><td>" + name_link + "</td><td>" + path + "</td>" + editColumn + "</tr>";
}
function placeTableRowForPlaceObject(place) {
var placePathOrIndex = (place.path ? place.path : "/");
return placeTableRow(place.name, placePathOrIndex, false);
}
function reloadPlacesOrTemporaryName() {
// we only need to do this if we have a current domain ID
var domainID = Settings.data.values.metaverse.id;
if (domainID.length > 0) {
var domainURL = Settings.METAVERSE_URL + "/api/v1/domains/" + domainID;
$.getJSON(domainURL, function(data){
// check if we have owner_places (for a real domain) or a name (for a temporary domain)
if (data.status == "success") {
if (data.domain.owner_places) {
// add a table row for each of these names
_.each(data.domain.owner_places, function(place){
$('#' + Settings.PLACES_TABLE_ID + " tbody").append(placeTableRowForPlaceObject(place));
});
} else if (data.domain.name) {
// add a table row for this temporary domain name
$('#' + Settings.PLACES_TABLE_ID + " tbody").append(placeTableRow(data.domain.name, '/', true));
}
}
});
}
}
function appendDomainIDButtons() {
var domainIDInput = $(Settings.DOMAIN_ID_SELECTOR);
var createButton = dynamicButton(Settings.CREATE_DOMAIN_ID_BTN_ID, "Create new domain ID");
createButton.css('margin-top', '10px');
var chooseButton = dynamicButton(Settings.CHOOSE_DOMAIN_ID_BTN_ID, "Choose from my domains");
chooseButton.css('margin', '10px 0px 0px 10px');
domainIDInput.after(chooseButton);
domainIDInput.after(createButton);
}
function chooseFromHighFidelityDomains(clickedButton) {
// setup the modal to help user pick their domain
if (Settings.initialValues.metaverse.access_token) {
// add a spinner to the choose button
clickedButton.html("Loading domains...")
clickedButton.attr('disabled', 'disabled')
// get a list of user domains from data-web
data_web_domains_url = Settings.METAVERSE_URL + "/api/v1/domains?access_token="
$.getJSON(data_web_domains_url + Settings.initialValues.metaverse.access_token, function(data){
modal_buttons = {
cancel: {
label: 'Cancel',
className: 'btn-default'
}
}
if (data.data.domains.length) {
// setup a select box for the returned domains
modal_body = "<p>Choose the High Fidelity domain you want this domain-server to represent.<br/>This will set your domain ID on the settings page.</p>"
domain_select = $("<select id='domain-name-select' class='form-control'></select>")
_.each(data.data.domains, function(domain){
var domainString = "";
if (domain.description) {
domainString += '"' + domain.description + '" - ';
}
domainString += domain.id;
domain_select.append("<option value='" + domain.id + "'>" + domainString + "</option>");
})
modal_body += "<label for='domain-name-select'>Domains</label>" + domain_select[0].outerHTML
modal_buttons["success"] = {
label: 'Choose domain',
callback: function() {
domainID = $('#domain-name-select').val()
// set the domain ID on the form
$(Settings.DOMAIN_ID_SELECTOR).val(domainID).change();
}
}
} else {
modal_buttons["success"] = {
label: 'Create new domain',
callback: function() {
window.open("https://metaverse.highfidelity.com/user/domains", '_blank');
}
}
modal_body = "<p>You do not have any domains in your High Fidelity account." +
"<br/><br/>Go to your domains page to create a new one. Once your domain is created re-open this dialog to select it.</p>"
}
bootbox.dialog({
title: "Choose matching domain",
message: modal_body,
buttons: modal_buttons
})
// remove the spinner from the choose button
clickedButton.html("Choose from my domains")
clickedButton.removeAttr('disabled')
})
} else {
bootbox.alert({
message: "You must have an access token to query your High Fidelity domains.<br><br>" +
"Please follow the instructions on the settings page to add an access token.",
title: "Access token required"
})
}
}
function createTemporaryDomain() {
swal({
title: 'Create temporary place name',
text: "This will create a temporary place name and domain ID (valid for 30 days)"
+ " so other users can easily connect to your domain.</br></br>"
+ "In order to make your domain reachable, this will also enable full automatic networking.",
showCancelButton: true,
confirmButtonText: 'Create',
closeOnConfirm: false,
html: true
}, function(isConfirm){
if (isConfirm) {
showSpinnerAlert('Creating temporary place name');
// make a get request to get a temporary domain
$.post(Settings.METAVERSE_URL + '/api/v1/domains/temporary', function(data){
if (data.status == "success") {
var domain = data.data.domain;
// we should have a new domain ID - set it on the domain ID value
$(Settings.DOMAIN_ID_SELECTOR).val(domain.id).change();
// we also need to make sure auto networking is set to full
$('[data-hidden-input="metaverse.automatic_networking"]').val("full").change();
swal({
type: 'success',
title: 'Success!',
text: "We have created a temporary name and domain ID for you.</br></br>"
+ "Your temporary place name is <strong>" + domain.name + "</strong>.</br></br>"
+ "Press the button below to save your new settings and restart your domain-server.",
confirmButtonText: 'Save',
html: true
}, function(){
saveSettings();
});
}
});
}
});
}
function reloadSettings() { function reloadSettings() {
$.getJSON('/settings.json', function(data){ $.getJSON('/settings.json', function(data){
@ -196,30 +713,32 @@ function reloadSettings() {
$('.nav-stacked').html(Settings.sidebarTemplate(data)) $('.nav-stacked').html(Settings.sidebarTemplate(data))
$('#panels').html(Settings.panelsTemplate(data)) $('#panels').html(Settings.panelsTemplate(data))
Settings.data = data;
Settings.initialValues = form2js('settings-form', ".", false, cleanupFormValues, true); Settings.initialValues = form2js('settings-form', ".", false, cleanupFormValues, true);
if (!_.has(data["locked"], "metaverse") && !_.has(data["locked"]["metaverse"], "id")) {
// append the domain selection modal, as long as it's not locked
appendDomainIDButtons();
}
// call our method to setup the HF account button
setupHFAccountButton();
// call our method to setup the place names table
setupPlacesTable();
// add tooltip to locked settings // add tooltip to locked settings
$('label.locked').tooltip({ $('label.locked').tooltip({
placement: 'right', placement: 'right',
title: 'This setting is in the master config file and cannot be changed' title: 'This setting is in the master config file and cannot be changed'
}) });
if (!_.has(data["locked"], "metaverse") && !_.has(data["locked"]["metaverse"], "id")) {
// append the domain selection modal, as long as it's not locked
appendDomainSelectionModal()
}
}); });
} }
function appendDomainSelectionModal() {
var metaverseInput = $("[name='metaverse.id']");
var chooseButton = $("<button type='button' id='choose-domain-btn' class='btn btn-primary' style='margin-top:10px'>Choose ID from my domains</button>");
metaverseInput.after(chooseButton);
}
var SETTINGS_ERROR_MESSAGE = "There was a problem saving domain settings. Please try again!"; var SETTINGS_ERROR_MESSAGE = "There was a problem saving domain settings. Please try again!";
$('body').on('click', '.save-button', function(e){ function saveSettings() {
// disable any inputs not changed // disable any inputs not changed
$("input:not([data-changed])").each(function(){ $("input:not([data-changed])").each(function(){
$(this).prop('disabled', true); $(this).prop('disabled', true);
@ -236,25 +755,14 @@ $('body').on('click', '.save-button', function(e){
}); });
// remove focus from the button // remove focus from the button
$(this).blur() $(this).blur();
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved // POST the form JSON to the domain-server settings.json endpoint so the settings are saved
$.ajax('/settings.json', { postSettings(formJSON);
data: JSON.stringify(formJSON), }
contentType: 'application/json',
type: 'POST'
}).done(function(data){
if (data.status == "success") {
showRestartModal();
} else {
showErrorMessage("Error", SETTINGS_ERROR_MESSAGE)
reloadSettings();
}
}).fail(function(){
showErrorMessage("Error", SETTINGS_ERROR_MESSAGE)
reloadSettings();
});
$('body').on('click', '.save-button', function(e){
saveSettings();
return false; return false;
}); });
@ -272,7 +780,7 @@ function makeTable(setting, keypath, setting_value, isLocked) {
} }
html += "<table class='table table-bordered " + (isLocked ? "locked-table" : "") + "' data-short-name='" + setting.name html += "<table class='table table-bordered " + (isLocked ? "locked-table" : "") + "' data-short-name='" + setting.name
+ "' name='" + keypath + "' data-setting-type='" + (isArray ? 'array' : 'hash') + "'>" + "' name='" + keypath + "' id='" + setting.html_id + "' data-setting-type='" + (isArray ? 'array' : 'hash') + "'>";
// Column names // Column names
html += "<tr class='headers'>" html += "<tr class='headers'>"
@ -286,66 +794,68 @@ function makeTable(setting, keypath, setting_value, isLocked) {
} }
_.each(setting.columns, function(col) { _.each(setting.columns, function(col) {
html += "<td class='data'><strong>" + col.label + "</strong></td>" // Data html += "<td class='data " + (col.class ? col.class : '') + "'><strong>" + col.label + "</strong></td>" // Data
}) })
if (!isLocked) { if (!isLocked && !setting.read_only) {
if (setting.can_order) { if (setting.can_order) {
html += "<td class=" + Settings.REORDER_BUTTONS_CLASSES + html += "<td class=" + Settings.REORDER_BUTTONS_CLASSES +
"><span class='glyphicon glyphicon-sort'></span></td>"; "><a href='javascript:void(0);' class='glyphicon glyphicon-sort'></a></td>";
} }
html += "<td class=" + Settings.ADD_DEL_BUTTONS_CLASSES + "></td></tr>" html += "<td class='" + Settings.ADD_DEL_BUTTONS_CLASSES + "'></td></tr>"
} }
// populate rows in the table from existing values // populate rows in the table from existing values
var row_num = 1 var row_num = 1;
_.each(setting_value, function(row, indexOrName) { if (setting_value.length > 0) {
html += "<tr class='" + Settings.DATA_ROW_CLASS + "'" + (isArray ? "" : "name='" + keypath + "." + indexOrName + "'") + ">" _.each(setting_value, function(row, indexOrName) {
html += "<tr class='" + Settings.DATA_ROW_CLASS + "'" + (isArray ? "" : "name='" + keypath + "." + indexOrName + "'") + ">"
if (setting.numbered === true) { if (setting.numbered === true) {
html += "<td class='numbered'>" + row_num + "</td>" html += "<td class='numbered'>" + row_num + "</td>"
}
if (setting.key) {
html += "<td class='key'>" + indexOrName + "</td>"
}
_.each(setting.columns, function(col) {
html += "<td class='" + Settings.DATA_COL_CLASS + "'>"
if (isArray) {
rowIsObject = setting.columns.length > 1
colValue = rowIsObject ? row[col.name] : row
html += colValue
// for arrays we add a hidden input to this td so that values can be posted appropriately
html += "<input type='hidden' name='" + keypath + "[" + indexOrName + "]"
+ (rowIsObject ? "." + col.name : "") + "' value='" + colValue + "'/>"
} else if (row.hasOwnProperty(col.name)) {
html += row[col.name]
} }
html += "</td>" if (setting.key) {
}) html += "<td class='key'>" + indexOrName + "</td>"
if (!isLocked) {
if (setting.can_order) {
html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES+
"'><span class='" + Settings.MOVE_UP_SPAN_CLASSES + "'></span><span class='" +
Settings.MOVE_DOWN_SPAN_CLASSES + "'></span></td>"
} }
html += "<td class='" + Settings.ADD_DEL_BUTTONS_CLASSES +
"'><span class='" + Settings.DEL_ROW_SPAN_CLASSES + "'></span></td>"
}
html += "</tr>" _.each(setting.columns, function(col) {
html += "<td class='" + Settings.DATA_COL_CLASS + "'>"
row_num++ if (isArray) {
}) rowIsObject = setting.columns.length > 1
colValue = rowIsObject ? row[col.name] : row
html += colValue
// for arrays we add a hidden input to this td so that values can be posted appropriately
html += "<input type='hidden' name='" + keypath + "[" + indexOrName + "]"
+ (rowIsObject ? "." + col.name : "") + "' value='" + colValue + "'/>"
} else if (row.hasOwnProperty(col.name)) {
html += row[col.name]
}
html += "</td>"
})
if (!isLocked && !setting.read_only) {
if (setting.can_order) {
html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES+
"'><a href='javascript:void(0);' class='" + Settings.MOVE_UP_SPAN_CLASSES + "'></a>"
+ "<a href='javascript:void(0);' class='" + Settings.MOVE_DOWN_SPAN_CLASSES + "'></a></td>"
}
html += "<td class='" + Settings.ADD_DEL_BUTTONS_CLASSES +
"'><a href='javascript:void(0);' class='" + Settings.DEL_ROW_SPAN_CLASSES + "'></a></td>"
}
html += "</tr>"
row_num++
});
}
// populate inputs in the table for new values // populate inputs in the table for new values
if (!isLocked) { if (!isLocked && !setting.read_only) {
html += makeTableInputs(setting) html += makeTableInputs(setting)
} }
html += "</table>" html += "</table>"
@ -377,7 +887,7 @@ function makeTableInputs(setting) {
html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES + "'></td>" html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES + "'></td>"
} }
html += "<td class='" + Settings.ADD_DEL_BUTTONS_CLASSES + html += "<td class='" + Settings.ADD_DEL_BUTTONS_CLASSES +
"'><span class='glyphicon glyphicon-plus " + Settings.ADD_ROW_BUTTON_CLASS + "'></span></td>" "'><a href='javascript:void(0);' class='glyphicon glyphicon-plus " + Settings.ADD_ROW_BUTTON_CLASS + "'></a></td>"
html += "</tr>" html += "</tr>"
return html return html
@ -385,10 +895,10 @@ function makeTableInputs(setting) {
function badgeSidebarForDifferences(changedElement) { function badgeSidebarForDifferences(changedElement) {
// figure out which group this input is in // figure out which group this input is in
var panelParentID = changedElement.closest('.panel').attr('id') var panelParentID = changedElement.closest('.panel').attr('id');
// if the panel contains non-grouped settings, the initial value is Settings.initialValues // if the panel contains non-grouped settings, the initial value is Settings.initialValues
var isGrouped = $(panelParentID).hasClass('grouped'); var isGrouped = $('#' + panelParentID).hasClass('grouped');
if (isGrouped) { if (isGrouped) {
var initialPanelJSON = Settings.initialValues[panelParentID]; var initialPanelJSON = Settings.initialValues[panelParentID];
@ -483,13 +993,14 @@ function addTableRow(add_glyphicon) {
$(element).html(1) $(element).html(1)
} }
} else if ($(element).hasClass(Settings.REORDER_BUTTONS_CLASS)) { } else if ($(element).hasClass(Settings.REORDER_BUTTONS_CLASS)) {
$(element).html("<td class='" + Settings.REORDER_BUTTONS_CLASSES + "'><span class='" + Settings.MOVE_UP_SPAN_CLASSES + $(element).html("<td class='" + Settings.REORDER_BUTTONS_CLASSES + "'><a href='javascript:void(0);'"
"'></span><span class='" + Settings.MOVE_DOWN_SPAN_CLASSES + "'></span></td>") + " class='" + Settings.MOVE_UP_SPAN_CLASSES + "'></a><a href='javascript:void(0);' class='"
+ Settings.MOVE_DOWN_SPAN_CLASSES + "'></span></td>")
} else if ($(element).hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) { } else if ($(element).hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) {
// Change buttons // Change buttons
var span = $(element).children("span") var anchor = $(element).children("a")
span.removeClass(Settings.ADD_ROW_SPAN_CLASSES) anchor.removeClass(Settings.ADD_ROW_SPAN_CLASSES)
span.addClass(Settings.DEL_ROW_SPAN_CLASSES) anchor.addClass(Settings.DEL_ROW_SPAN_CLASSES)
} else if ($(element).hasClass("key")) { } else if ($(element).hasClass("key")) {
var input = $(element).children("input") var input = $(element).children("input")
$(element).html(input.val()) $(element).html(input.val())
@ -658,70 +1169,3 @@ function cleanupFormValues(node) {
function showErrorMessage(title, message) { function showErrorMessage(title, message) {
swal(title, message) swal(title, message)
} }
function chooseFromHighFidelityDomains(clickedButton) {
// setup the modal to help user pick their domain
if (Settings.initialValues.metaverse.access_token) {
// add a spinner to the choose button
clickedButton.html("Loading domains...")
clickedButton.attr('disabled', 'disabled')
// get a list of user domains from data-web
data_web_domains_url = "https://metaverse.highfidelity.com/api/v1/domains?access_token="
$.getJSON(data_web_domains_url + Settings.initialValues.metaverse.access_token, function(data){
modal_buttons = {
cancel: {
label: 'Cancel',
className: 'btn-default'
}
}
if (data.data.domains.length) {
// setup a select box for the returned domains
modal_body = "<p>Choose the High Fidelity domain you want this domain-server to represent.<br/>This will set your domain ID on the settings page.</p>"
domain_select = $("<select id='domain-name-select' class='form-control'></select>")
_.each(data.data.domains, function(domain){
domain_select.append("<option value='" + domain.id + "'>(" + domain.id + ")" + (domain.names.length > 0 ? " [" + domain.names + "]" : "") + "</option>");
})
modal_body += "<label for='domain-name-select'>Domains</label>" + domain_select[0].outerHTML
modal_buttons["success"] = {
label: 'Choose domain',
callback: function() {
domainID = $('#domain-name-select').val()
// set the domain ID on the form
$("[name='metaverse.id']").val(domainID).change();
}
}
} else {
modal_buttons["success"] = {
label: 'Create new domain',
callback: function() {
window.open("https://metaverse.highfidelity.com/user/domains", '_blank');
}
}
modal_body = "<p>You do not have any domains in your High Fidelity account." +
"<br/><br/>Go to your domains page to create a new one. Once your domain is created re-open this dialog to select it.</p>"
}
bootbox.dialog({
title: "Choose matching domain",
message: modal_body,
buttons: modal_buttons
})
// remove the spinner from the choose button
clickedButton.html("Choose from my domains")
clickedButton.removeAttr('disabled')
})
} else {
bootbox.alert({
message: "You must have an access token to query your High Fidelity domains.<br><br>" +
"Please follow the instructions on the settings page to add an access token.",
title: "Access token required"
})
}
}

View file

@ -1,33 +0,0 @@
$(document).ready(function(){
/*
* Clamped-width.
* Usage:
* <div data-clampedwidth=".myParent">This long content will force clamped width</div>
*
* Author: LV
*/
$('[data-clampedwidth]').each(function () {
var elem = $(this);
var parentPanel = elem.data('clampedwidth');
var resizeFn = function () {
var sideBarNavWidth = $(parentPanel).width() - parseInt(elem.css('paddingLeft')) - parseInt(elem.css('paddingRight')) - parseInt(elem.css('marginLeft')) - parseInt(elem.css('marginRight')) - parseInt(elem.css('borderLeftWidth')) - parseInt(elem.css('borderRightWidth'));
elem.css('width', sideBarNavWidth);
};
resizeFn();
$(window).resize(resizeFn);
});
var listSource = $('#list-group-template').html();
var listTemplate = _.template(listSource);
reloadSettings();
function reloadSettings() {
$.getJSON('describe-setup.json', function(data){
$('.list-group').html(listTemplate(data));
});
}
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,75 +1,80 @@
<!--#include virtual="header.html"--> <!--#include virtual="header.html"-->
<div class="col-md-10 col-md-offset-1"> <div class="col-md-10 col-md-offset-1">
<div class="col-md-12"> <div class="row">
<div class="alert" style="display:none;"></div> <div class="col-md-12">
</div> <div class="alert" style="display:none;"></div>
<div class="col-md-3 col-sm-3" id="setup-sidebar-col">
<div id="setup-sidebar" data-clampedwidth="#setup-sidebar-col" class="hidden-xs" data-spy="affix" data-offset-top="55">
<script id="list-group-template" type="text/template">
<% _.each(descriptions, function(group){ %>
<% panelID = group.name ? group.name : group.label %>
<li>
<a href="#<%- panelID %>" class="list-group-item">
<span class="badge"></span>
<%- group.label %>
</a>
</li>
<% }); %>
</script>
<ul class="nav nav-pills nav-stacked">
</ul>
<button id="advanced-toggle-button" hidden=true class="btn btn-info">Show advanced</button>
<button class="btn btn-success save-button">Save and restart</button>
</div> </div>
</div> </div>
<div class="col-md-9 col-sm-9 col-xs-12"> <div class="row">
<form id="settings-form" role="form"> <div class="col-md-3 col-sm-3" id="setup-sidebar-col">
<div id="setup-sidebar" class="hidden-xs" data-spy="affix" data-offset-top="55" data-clampedwidth="#setup-sidebar-col">
<script id="list-group-template" type="text/template">
<% _.each(descriptions, function(group){ %>
<% panelID = group.name ? group.name : group.label %>
<li>
<a href="#<%- panelID %>" class="list-group-item">
<span class="badge"></span>
<%- group.label %>
</a>
</li>
<% }); %>
</script>
<script id="panels-template" type="text/template"> <ul class="nav nav-pills nav-stacked">
<% _.each(descriptions, function(group){ %> </ul>
<% split_settings = _.partition(group.settings, function(value, index) { return !value.advanced }) %>
<% isAdvanced = _.isEmpty(split_settings[0]) %>
<% if (isAdvanced) { %>
<% $("a[href=#" + group.name + "]").addClass('advanced-setting').hide() %>
<% } %>
<% isGrouped = !!group.name %> <button id="advanced-toggle-button" hidden=true class="btn btn-info advanced-toggle">Show advanced</button>
<% panelID = isGrouped ? group.name : group.label %> <button class="btn btn-success save-button">Save and restart</button>
</div>
</div>
<div class="panel panel-default<%- (isAdvanced) ? ' advanced-setting' : '' %><%- (isGrouped) ? ' grouped' : '' %>" <div class="col-md-9 col-sm-9 col-xs-12">
id="<%- panelID %>"> <div id="xs-advanced-container" class="col-xs-12 hidden-sm hidden-md hidden-lg">
<div class="panel-heading"> <button id="advanced-toggle-button-xs" class="btn btn-info advanced-toggle">Show advanced</button>
<h3 class="panel-title"><%- group.label %></h3> </div>
</div>
<div class="panel-body"> <div class="col-xs-12">
<% _.each(split_settings[0], function(setting) { %> <form id="settings-form" role="form">
<% keypath = isGrouped ? group.name + "." + setting.name : setting.name %>
<%= getFormGroup(keypath, setting, values, false, <script id="panels-template" type="text/template">
(_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %> <% _.each(descriptions, function(group){ %>
<% }); %> <% split_settings = _.partition(group.settings, function(value, index) { return !value.advanced }) %>
<% if (!_.isEmpty(split_settings[1])) { %> <% isAdvanced = _.isEmpty(split_settings[0]) %>
<% $("#advanced-toggle-button").show() %> <% if (isAdvanced) { %>
<% _.each(split_settings[1], function(setting) { %> <% $("a[href=#" + group.name + "]").addClass('advanced-setting').hide() %>
<% } %>
<% isGrouped = !!group.name %>
<% panelID = isGrouped ? group.name : group.html_id %>
<div class="panel panel-default<%- (isAdvanced) ? ' advanced-setting' : '' %><%- (isGrouped) ? ' grouped' : '' %>"
id="<%- panelID %>">
<div class="panel-heading">
<h3 class="panel-title"><%- group.label %></h3>
</div>
<div class="panel-body">
<% _.each(split_settings[0], function(setting) { %>
<% keypath = isGrouped ? group.name + "." + setting.name : setting.name %> <% keypath = isGrouped ? group.name + "." + setting.name : setting.name %>
<%= getFormGroup(keypath, setting, values, true, <%= getFormGroup(keypath, setting, values, false,
(_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %> (_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %>
<% }); %> <% }); %>
<% }%> <% if (!_.isEmpty(split_settings[1])) { %>
<% $("#advanced-toggle-button").show() %>
<% _.each(split_settings[1], function(setting) { %>
<% keypath = isGrouped ? group.name + "." + setting.name : setting.name %>
<%= getFormGroup(keypath, setting, values, true,
(_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %>
<% }); %>
<% }%>
</div>
</div> </div>
</div> <% }); %>
</script>
<% }); %> <div id="panels"></div>
</script> </form>
</div>
<div id="panels"></div>
</form>
</div> </div>
<div class="col-xs-12 hidden-sm hidden-md hidden-lg"> <div class="col-xs-12 hidden-sm hidden-md hidden-lg">
@ -94,7 +99,7 @@
<script src='/js/underscore-min.js'></script> <script src='/js/underscore-min.js'></script>
<script src='/js/underscore-keypath.min.js'></script> <script src='/js/underscore-keypath.min.js'></script>
<script src='/js/bootbox.min.js'></script> <script src='/js/bootbox.min.js'></script>
<script src='/js/sweet-alert.min.js'></script> <script src='/js/sweetalert.min.js'></script>
<script src='/js/settings.js'></script> <script src='/js/settings.js'></script>
<script src='/js/form2js.min.js'></script> <script src='/js/form2js.min.js'></script>
<!--#include virtual="page-end.html"--> <!--#include virtual="page-end.html"-->

View file

@ -5,6 +5,7 @@
</div> </div>
<div class="col-xs-12" id="stats-container"></div> <div class="col-xs-12" id="stats-container"></div>
<!--#include virtual="footer.html"--> <!--#include virtual="footer.html"-->
<script src='/js/query-string.js'></script>
<script src='js/stats.js'></script> <script src='js/stats.js'></script>
<script src='js/json.human.js'></script> <script src='js/json.human.js'></script>
<script src='js/highcharts-custom.js'></script> <script src='js/highcharts-custom.js'></script>

View file

@ -1,24 +1,18 @@
function qs(key) {
key = key.replace(/[*+?^$.\[\]{}()|\\\/]/g, "\\$&"); // escape RegEx meta chars
var match = location.search.match(new RegExp("[?&]"+key+"=([^&]+)(&|$)"));
return match && decodeURIComponent(match[1].replace(/\+/g, " "));
}
$(document).ready(function(){ $(document).ready(function(){
var currentHighchart; var currentHighchart;
// setup a function to grab the nodeStats // setup a function to grab the nodeStats
function getNodeStats() { function getNodeStats() {
var uuid = qs("uuid"); var uuid = qs("uuid");
$.getJSON("/nodes/" + uuid + ".json", function(json){ $.getJSON("/nodes/" + uuid + ".json", function(json){
// update the table header with the right node type // update the table header with the right node type
$('#stats-lead h3').html(json.node_type + " stats (" + uuid + ")"); $('#stats-lead h3').html(json.node_type + " stats (" + uuid + ")");
delete json.node_type; delete json.node_type;
var stats = JsonHuman.format(json); var stats = JsonHuman.format(json);
$('#stats-container').html(stats); $('#stats-container').html(stats);
@ -27,33 +21,33 @@ $(document).ready(function(){
var x = (new Date()).getTime(); var x = (new Date()).getTime();
// get the last value using underscore-keypath // get the last value using underscore-keypath
var y = _(json).valueForKeyPath(graphKeypath); var y = _(json).valueForKeyPath(graphKeypath);
// start shifting the chart once we hit 20 data points // start shifting the chart once we hit 20 data points
var shift = currentHighchart.series[0].data.length > 20; var shift = currentHighchart.series[0].data.length > 20;
currentHighchart.series[0].addPoint([x, y], true, shift); currentHighchart.series[0].addPoint([x, y], true, shift);
} }
}).fail(function(data) { }).fail(function(data) {
$('#stats-container th').each(function(){ $('#stats-container th').each(function(){
$(this).addClass('stale'); $(this).addClass('stale');
}); });
}); });
} }
// do the first GET on page load // do the first GET on page load
getNodeStats(); getNodeStats();
// grab the new assignments JSON every second // grab the new assignments JSON every second
var getNodeStatsInterval = setInterval(getNodeStats, 1000); var getNodeStatsInterval = setInterval(getNodeStats, 1000);
var graphKeypath = ""; var graphKeypath = "";
// set the global Highcharts option // set the global Highcharts option
Highcharts.setOptions({ Highcharts.setOptions({
global: { global: {
useUTC: false useUTC: false
} }
}); });
// add a function to help create the graph modal // add a function to help create the graph modal
function createGraphModal() { function createGraphModal() {
var chartModal = bootbox.dialog({ var chartModal = bootbox.dialog({
@ -66,7 +60,7 @@ $(document).ready(function(){
chartModal.on('hidden.bs.modal', function(e) { chartModal.on('hidden.bs.modal', function(e) {
currentHighchart.destroy(); currentHighchart.destroy();
currentHighchart = null; currentHighchart = null;
}); });
currentHighchart = new Highcharts.Chart({ currentHighchart = new Highcharts.Chart({
chart: { chart: {
@ -94,11 +88,11 @@ $(document).ready(function(){
}] }]
}); });
} }
// handle clicks on numerical values - this lets the user show a line graph in a modal // handle clicks on numerical values - this lets the user show a line graph in a modal
$('#stats-container').on('click', '.jh-type-number', function(){ $('#stats-container').on('click', '.jh-type-number', function(){
graphKeypath = $(this).data('keypath'); graphKeypath = $(this).data('keypath');
// setup the new graph modal // setup the new graph modal
createGraphModal(); createGraphModal();
}); });

View file

@ -279,16 +279,19 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
} }
bool DomainServer::didSetupAccountManagerWithAccessToken() { bool DomainServer::didSetupAccountManagerWithAccessToken() {
AccountManager& accountManager = AccountManager::getInstance(); if (AccountManager::getInstance().hasValidAccessToken()) {
if (accountManager.hasValidAccessToken()) {
// we already gave the account manager a valid access token // we already gave the account manager a valid access token
return true; return true;
} }
return resetAccountManagerAccessToken();
}
const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token";
bool DomainServer::resetAccountManagerAccessToken() {
if (!_oauthProviderURL.isEmpty()) { if (!_oauthProviderURL.isEmpty()) {
// check for an access-token in our settings, can optionally be overidden by env value // check for an access-token in our settings, can optionally be overidden by env value
const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token";
const QString ENV_ACCESS_TOKEN_KEY = "DOMAIN_SERVER_ACCESS_TOKEN"; const QString ENV_ACCESS_TOKEN_KEY = "DOMAIN_SERVER_ACCESS_TOKEN";
QString accessToken = QProcessEnvironment::systemEnvironment().value(ENV_ACCESS_TOKEN_KEY); QString accessToken = QProcessEnvironment::systemEnvironment().value(ENV_ACCESS_TOKEN_KEY);
@ -310,7 +313,7 @@ bool DomainServer::didSetupAccountManagerWithAccessToken() {
} }
// give this access token to the AccountManager // give this access token to the AccountManager
accountManager.setAccessTokenForCurrentAuthURL(accessToken); AccountManager::getInstance().setAccessTokenForCurrentAuthURL(accessToken);
return true; return true;
@ -1509,12 +1512,15 @@ QString pathForAssignmentScript(const QUuid& assignmentUUID) {
return newPath; return newPath;
} }
const QString URI_OAUTH = "/oauth";
bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) { bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) {
const QString JSON_MIME_TYPE = "application/json"; const QString JSON_MIME_TYPE = "application/json";
const QString URI_ASSIGNMENT = "/assignment"; const QString URI_ASSIGNMENT = "/assignment";
const QString URI_ASSIGNMENT_SCRIPTS = URI_ASSIGNMENT + "/scripts"; const QString URI_ASSIGNMENT_SCRIPTS = URI_ASSIGNMENT + "/scripts";
const QString URI_NODES = "/nodes"; const QString URI_NODES = "/nodes";
const QString URI_SETTINGS = "/settings";
const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
@ -1792,7 +1798,6 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
const QString HIFI_SESSION_COOKIE_KEY = "DS_WEB_SESSION_UUID"; const QString HIFI_SESSION_COOKIE_KEY = "DS_WEB_SESSION_UUID";
bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url, bool skipSubHandler) { bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url, bool skipSubHandler) {
const QString URI_OAUTH = "/oauth";
qDebug() << "HTTPS request received at" << url.toString(); qDebug() << "HTTPS request received at" << url.toString();
if (url.path() == URI_OAUTH) { if (url.path() == URI_OAUTH) {

View file

@ -72,9 +72,11 @@ private:
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid()); void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
bool optionallySetupOAuth(); bool optionallySetupOAuth();
bool optionallyReadX509KeyAndCertificate(); bool optionallyReadX509KeyAndCertificate();
bool didSetupAccountManagerWithAccessToken();
bool optionallySetupAssignmentPayment(); bool optionallySetupAssignmentPayment();
bool didSetupAccountManagerWithAccessToken();
bool resetAccountManagerAccessToken();
void setupAutomaticNetworking(); void setupAutomaticNetworking();
void sendHeartbeatToDataServer(const QString& networkAddress); void sendHeartbeatToDataServer(const QString& networkAddress);
void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr); void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr);

View file

@ -101,10 +101,8 @@ QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QStrin
return QVariant(); return QVariant();
} }
const QString SETTINGS_PATH = "/settings.json";
bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connection, const QUrl &url) { bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connection, const QUrl &url) {
if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == SETTINGS_PATH) { if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == SETTINGS_PATH_JSON) {
// this is a GET operation for our settings // this is a GET operation for our settings
// check if there is a query parameter for settings affecting a particular type of assignment // check if there is a query parameter for settings affecting a particular type of assignment
@ -127,7 +125,7 @@ bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connec
} }
bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection *connection, const QUrl &url) { bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection *connection, const QUrl &url) {
if (connection->requestOperation() == QNetworkAccessManager::PostOperation && url.path() == SETTINGS_PATH) { if (connection->requestOperation() == QNetworkAccessManager::PostOperation && url.path() == SETTINGS_PATH_JSON) {
// this is a POST operation to change one or more settings // this is a POST operation to change one or more settings
QJsonDocument postedDocument = QJsonDocument::fromJson(connection->requestContent()); QJsonDocument postedDocument = QJsonDocument::fromJson(connection->requestContent());
QJsonObject postedObject = postedDocument.object(); QJsonObject postedObject = postedDocument.object();
@ -149,7 +147,7 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection
QTimer::singleShot(DOMAIN_SERVER_RESTART_TIMER_MSECS, qApp, SLOT(restart())); QTimer::singleShot(DOMAIN_SERVER_RESTART_TIMER_MSECS, qApp, SLOT(restart()));
return true; return true;
} else if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == SETTINGS_PATH) { } else if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == SETTINGS_PATH_JSON) {
// setup a JSON Object with descriptions and non-omitted settings // setup a JSON Object with descriptions and non-omitted settings
const QString SETTINGS_RESPONSE_DESCRIPTION_KEY = "descriptions"; const QString SETTINGS_RESPONSE_DESCRIPTION_KEY = "descriptions";
const QString SETTINGS_RESPONSE_VALUE_KEY = "values"; const QString SETTINGS_RESPONSE_VALUE_KEY = "values";
@ -254,7 +252,6 @@ QJsonObject DomainServerSettingsManager::responseObjectForType(const QString& ty
void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap, void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap,
const QJsonObject& settingDescription) { const QJsonObject& settingDescription) {
if (newValue.isString()) { if (newValue.isString()) {
if (newValue.toString().isEmpty()) { if (newValue.toString().isEmpty()) {
// this is an empty value, clear it in settings variant so the default is sent // this is an empty value, clear it in settings variant so the default is sent

View file

@ -20,6 +20,9 @@
const QString SETTINGS_PATHS_KEY = "paths"; const QString SETTINGS_PATHS_KEY = "paths";
const QString SETTINGS_PATH = "/settings";
const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json";
class DomainServerSettingsManager : public QObject { class DomainServerSettingsManager : public QObject {
Q_OBJECT Q_OBJECT
public: public:
@ -30,6 +33,7 @@ public:
void setupConfigMap(const QStringList& argumentList); void setupConfigMap(const QStringList& argumentList);
QVariant valueOrDefaultValueForKeyPath(const QString& keyPath); QVariant valueOrDefaultValueForKeyPath(const QString& keyPath);
QVariantMap& getUserSettingsMap() { return _configMap.getUserConfig(); }
QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); } QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); }
private: private:
QJsonObject responseObjectForType(const QString& typeValue, bool isAuthenticated = false); QJsonObject responseObjectForType(const QString& typeValue, bool isAuthenticated = false);

View file

@ -15,7 +15,7 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
Script.include([ Script.include([
"libraries/stringHelpers.js", "libraries/stringHelpers.js",
"libraries/dataviewHelpers.js", "libraries/dataViewHelpers.js",
"libraries/toolBars.js", "libraries/toolBars.js",
"libraries/progressDialog.js", "libraries/progressDialog.js",

View file

@ -1,4 +1,3 @@
// grab.js // grab.js
// examples // examples
// //
@ -11,12 +10,38 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
// these are hand-measured bounds of the AirHockey table
var fieldMaxOffset = {
x: 0.475,
y: 0.315,
z: 0.830
};
var fieldMinOffset = {
x: -0.475,
y: 0.315,
z: -0.830
};
// parameters for storing the table playing field
var fieldMax = {
x: 0,
y: 0,
z: 0
};
var fieldMin = {
x: 0,
y: 0,
z: 0
};
var isGrabbing = false; var isGrabbing = false;
var grabbedEntity = null; var grabbedEntity = null;
var prevMouse = {}; var prevMouse = {};
var deltaMouse = { var deltaMouse = {
z: 0 z: 0
} }
var TABLE_SEARCH_RANGE = 10;
var entityProps; var entityProps;
var moveUpDown = false; var moveUpDown = false;
var CLOSE_ENOUGH = 0.001; var CLOSE_ENOUGH = 0.001;
@ -26,8 +51,13 @@ var DAMPING_RATE = 0.80;
var ANGULAR_DAMPING_RATE = 0.40; var ANGULAR_DAMPING_RATE = 0.40;
var SCREEN_TO_METERS = 0.001; var SCREEN_TO_METERS = 0.001;
var currentPosition, currentVelocity, cameraEntityDistance, currentRotation; var currentPosition, currentVelocity, cameraEntityDistance, currentRotation;
var grabHeight;
var velocityTowardTarget, desiredVelocity, addedVelocity, newVelocity, dPosition, camYaw, distanceToTarget, targetPosition; var velocityTowardTarget, desiredVelocity, addedVelocity, newVelocity, dPosition, camYaw, distanceToTarget, targetPosition;
var originalGravity = {x: 0, y: 0, z: 0}; var originalGravity = {
x: 0,
y: 0,
z: 0
};
var shouldRotate = false; var shouldRotate = false;
var dQ, theta, axisAngle, dT; var dQ, theta, axisAngle, dT;
var angularVelocity = { var angularVelocity = {
@ -58,11 +88,11 @@ var dropLine = Overlays.addOverlay("line3d", {
function vectorIsZero(v) { function vectorIsZero(v) {
return v.x == 0 && v.y == 0 && v.z == 0; return v.x == 0 && v.y == 0 && v.z == 0;
} }
function nearLinePoint(targetPosition) { function nearLinePoint(targetPosition) {
// var handPosition = Vec3.sum(MyAvatar.position, {x:0, y:0.2, z:0}); // var handPosition = Vec3.sum(MyAvatar.position, {x:0, y:0.2, z:0});
var handPosition = MyAvatar.getRightPalmPosition(); var handPosition = MyAvatar.getRightPalmPosition();
var along = Vec3.subtract(targetPosition, handPosition); var along = Vec3.subtract(targetPosition, handPosition);
along = Vec3.normalize(along); along = Vec3.normalize(along);
@ -75,9 +105,15 @@ function mousePressEvent(event) {
if (!event.isLeftButton) { if (!event.isLeftButton) {
return; return;
} }
prevMouse.x = event.x;
prevMouse.y = event.y;
var pickRay = Camera.computePickRay(event.x, event.y); var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
if (intersection.intersects && intersection.properties.collisionsWillMove) { if (!intersection.intersects) {
return;
}
if (intersection.properties.collisionsWillMove) {
grabbedEntity = intersection.entityID; grabbedEntity = intersection.entityID;
var props = Entities.getEntityProperties(grabbedEntity) var props = Entities.getEntityProperties(grabbedEntity)
isGrabbing = true; isGrabbing = true;
@ -87,15 +123,35 @@ function mousePressEvent(event) {
currentVelocity = props.velocity; currentVelocity = props.velocity;
updateDropLine(targetPosition); updateDropLine(targetPosition);
// remember the height of the object when first grabbed
// we'll try to maintain this height during the rest of this grab
grabHeight = currentPosition.y;
Entities.editEntity(grabbedEntity, { Entities.editEntity(grabbedEntity, {
gravity: {x: 0, y: 0, z: 0} gravity: {
x: 0,
y: 0,
z: 0
}
}); });
Audio.playSound(grabSound, { Audio.playSound(grabSound, {
position: props.position, position: props.position,
volume: VOLUME volume: VOLUME
}); });
}
//We want to detect table once user grabs something that may be on a table...
var potentialTables = Entities.findEntities(MyAvatar.position, TABLE_SEARCH_RANGE);
potentialTables.forEach(function(table) {
var props = Entities.getEntityProperties(table);
if (props.name === "table") {
var tablePosition = props.position;
// when we know the table's position we can compute the X-Z bounds of its field
fieldMax = Vec3.sum(tablePosition, fieldMaxOffset);
fieldMin = Vec3.sum(tablePosition, fieldMinOffset);
}
});
}
} }
function updateDropLine(position) { function updateDropLine(position) {
@ -127,7 +183,7 @@ function mouseReleaseEvent() {
// 5. interface B releases the entity and puts the original gravity back (to zero) // 5. interface B releases the entity and puts the original gravity back (to zero)
if (!vectorIsZero(originalGravity)) { if (!vectorIsZero(originalGravity)) {
Entities.editEntity(grabbedEntity, { Entities.editEntity(grabbedEntity, {
gravity: originalGravity gravity: originalGravity
}); });
} }
@ -144,6 +200,7 @@ function mouseReleaseEvent() {
} }
} }
// new mouseMoveEvent
function mouseMoveEvent(event) { function mouseMoveEvent(event) {
if (isGrabbing) { if (isGrabbing) {
// see if something added/restored gravity // see if something added/restored gravity
@ -152,24 +209,15 @@ function mouseMoveEvent(event) {
originalGravity = props.gravity; originalGravity = props.gravity;
} }
deltaMouse.x = event.x - prevMouse.x; if (shouldRotate) {
if (!moveUpDown) { deltaMouse.x = event.x - prevMouse.x;
deltaMouse.z = event.y - prevMouse.y; if (!moveUpDown) {
deltaMouse.y = 0; deltaMouse.z = event.y - prevMouse.y;
} else { deltaMouse.y = 0;
deltaMouse.y = (event.y - prevMouse.y) * -1; } else {
deltaMouse.z = 0; deltaMouse.y = (event.y - prevMouse.y) * -1;
} deltaMouse.z = 0;
// Update the target position by the amount the mouse moved }
camYaw = Quat.safeEulerAngles(Camera.getOrientation()).y;
dPosition = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, camYaw, 0), deltaMouse);
if (!shouldRotate) {
// Adjust target position for the object by the mouse move
cameraEntityDistance = Vec3.distance(Camera.getPosition(), currentPosition);
// Scale distance we want to move by the distance from the camera to the grabbed object
// TODO: Correct SCREEN_TO_METERS to be correct for the actual FOV, resolution
targetPosition = Vec3.sum(targetPosition, Vec3.multiply(dPosition, cameraEntityDistance * SCREEN_TO_METERS));
} else if (shouldRotate) {
var transformedDeltaMouse = { var transformedDeltaMouse = {
x: deltaMouse.z, x: deltaMouse.z,
y: deltaMouse.x, y: deltaMouse.x,
@ -180,15 +228,37 @@ function mouseMoveEvent(event) {
theta = 2 * Math.acos(dQ.w); theta = 2 * Math.acos(dQ.w);
axisAngle = Quat.axis(dQ); axisAngle = Quat.axis(dQ);
angularVelocity = Vec3.multiply((theta / dT), axisAngle); angularVelocity = Vec3.multiply((theta / dT), axisAngle);
} } else {
var relativePosition = Vec3.subtract(currentPosition, Camera.getPosition());
if (relativePosition.y < 0) {
// grabee is below camera, so movement is valid
// compute intersectionPoint where mouse ray hits grabee's current x-z plane
var pickRay = Camera.computePickRay(event.x, event.y);
var mousePosition = pickRay.direction;
var length = relativePosition.y / mousePosition.y;
mousePosition = Vec3.multiply(mousePosition, length);
mousePosition = Vec3.sum(Camera.getPosition(), mousePosition);
// clamp mousePosition to table field
if (mousePosition.x < fieldMin.x) {
mousePosition.x = fieldMin.x;
} else if (mousePosition.x > fieldMax.x) {
mousePosition.x = fieldMax.x;
}
if (mousePosition.z < fieldMin.z) {
mousePosition.z = fieldMin.z;
} else if (mousePosition.z > fieldMax.z) {
mousePosition.z = fieldMax.z;
}
mousePosition.y = grabHeight;
targetPosition = mousePosition;
}
}
} }
prevMouse.x = event.x; prevMouse.x = event.x;
prevMouse.y = event.y; prevMouse.y = event.y;
} }
function keyReleaseEvent(event) { function keyReleaseEvent(event) {
if (event.text === "SHIFT") { if (event.text === "SHIFT") {
moveUpDown = false; moveUpDown = false;
@ -237,14 +307,24 @@ function update(deltaTime) {
newVelocity = Vec3.subtract(newVelocity, Vec3.multiply(newVelocity, DAMPING_RATE)); newVelocity = Vec3.subtract(newVelocity, Vec3.multiply(newVelocity, DAMPING_RATE));
// Update entity // Update entity
} else { } else {
newVelocity = {x: 0, y: 0, z: 0}; newVelocity = {
x: 0,
y: 0,
z: 0
};
} }
if (shouldRotate) { if (shouldRotate) {
angularVelocity = Vec3.subtract(angularVelocity, Vec3.multiply(angularVelocity, ANGULAR_DAMPING_RATE)); angularVelocity = Vec3.subtract(angularVelocity, Vec3.multiply(angularVelocity, ANGULAR_DAMPING_RATE));
} else { } else {
angularVelocity = entityProps.angularVelocity; angularVelocity = entityProps.angularVelocity;
} }
// enforce that grabee's rotation is only about y-axis while being grabbed
currentRotation.x = 0;
currentRotation.z = 0;
currentRotation.y = Math.sqrt(1.0 - currentRotation.w * currentRotation.w);
// Hrm... slamming the currentRotation doesn't seem to work
Entities.editEntity(grabbedEntity, { Entities.editEntity(grabbedEntity, {
position: currentPosition, position: currentPosition,
rotation: currentRotation, rotation: currentRotation,

View file

@ -65,7 +65,7 @@ public:
const QUrl& getAuthURL() const { return _authURL; } const QUrl& getAuthURL() const { return _authURL; }
void setAuthURL(const QUrl& authURL); void setAuthURL(const QUrl& authURL);
bool hasAuthEndpoint() { return !_authURL.isEmpty(); } bool hasAuthEndpoint() { return !_authURL.isEmpty(); }
void disableSettingsFilePersistence() { _shouldPersistToSettingsFile = false; } void disableSettingsFilePersistence() { _shouldPersistToSettingsFile = false; }
bool isLoggedIn() { return !_authURL.isEmpty() && hasValidAccessToken(); } bool isLoggedIn() { return !_authURL.isEmpty() && hasValidAccessToken(); }
@ -79,7 +79,7 @@ public:
public slots: public slots:
void requestAccessToken(const QString& login, const QString& password); void requestAccessToken(const QString& login, const QString& password);
void requestAccessTokenFinished(); void requestAccessTokenFinished();
void requestProfileFinished(); void requestProfileFinished();
void requestAccessTokenError(QNetworkReply::NetworkError error); void requestAccessTokenError(QNetworkReply::NetworkError error);
@ -105,7 +105,7 @@ private:
AccountManager(); AccountManager();
AccountManager(AccountManager const& other); // not implemented AccountManager(AccountManager const& other); // not implemented
void operator=(AccountManager const& other); // not implemented void operator=(AccountManager const& other); // not implemented
void persistAccountToSettings(); void persistAccountToSettings();
void passSuccessToCallback(QNetworkReply* reply); void passSuccessToCallback(QNetworkReply* reply);