diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 82b228008f..6167a45f15 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -7,6 +7,7 @@ // // Copyright 2014 High Fidelity, Inc. // Copyright 2020 Vircadia contributors. +// Copyright 2022 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -38,14 +39,14 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/checkForUpdates.js", "system/onEscape.js", "system/onFirstRun.js", - "system/appreciate/appreciate_app.js" + "system/appreciate/appreciate_app.js", + "system/places/places.js" ]; var DEFAULT_SCRIPTS_SEPARATE = [ "system/controllers/controllerScripts.js", "communityScripts/notificationCore/notificationCore.js", "simplifiedUI/ui/simplifiedNametag/simplifiedNametag.js", {"stable": "system/more/app-more.js", "beta": "https://cdn.vircadia.com/community-apps/more/app-more.js"}, - {"stable": "communityScripts/explore/explore.js", "beta": "https://metaverse.vircadia.com/interim/d-goto/app/explore.js"}, {"stable": "communityScripts/chat/FloofChat.js", "beta": "https://content.fluffy.ws/scripts/chat/FloofChat.js"} //"system/chat.js" ]; diff --git a/scripts/system/places/federation.json b/scripts/system/places/federation.json new file mode 100644 index 0000000000..be6d596613 --- /dev/null +++ b/scripts/system/places/federation.json @@ -0,0 +1,4 @@ +[ + {"node": "https://metaverse.vircadia.com/live"}, + {"node": "http://buyanvr.org:9400"} +] \ No newline at end of file diff --git a/scripts/system/places/fonts.css b/scripts/system/places/fonts.css new file mode 100644 index 0000000000..4332475a6d --- /dev/null +++ b/scripts/system/places/fonts.css @@ -0,0 +1,48 @@ +@font-face { + font-family: 'Roboto' + src: url('fonts/Roboto-Regular.ttf') format('truetype') + font-weight: normal + font-style: normal +} + +@font-face { + font-family: 'Roboto' + src: url('fonts/Roboto-Bold.ttf') format('truetype') + font-weight: bold + font-style: normal +} + +@font-face { + font-family: 'Roboto'; + src: url('fonts/Roboto-Italic.ttf'); + font-weight: normal; + font-style: italic; +} + +@font-face { + font-family: 'Roboto'; + src: url('fonts/Roboto-BoldItalic.ttf'); + font-weight: bold; + font-style: italic; +} + +@font-face { + font-family: 'Roboto'; + src: url('fonts/Roboto-Medium.ttf'); + font-weight: 500; + font-style: normal; +} + +@font-face { + font-family: 'Roboto'; + src: url('fonts/Roboto-MediumItalic.ttf'); + font-weight: 500; + font-style: italic; +} + +/* Roboto-Regular.ttf 400 */ +/* Roboto-Bold.ttf 700 */ +/* Roboto-Italic.ttf 400 */ +/* Roboto-BoldItalic.ttf 700 */ +/* Roboto-Medium.ttf 500 */ +/* Roboto-MediumItalic.ttf 500 */ diff --git a/scripts/system/places/fonts/LICENSE.txt b/scripts/system/places/fonts/LICENSE.txt new file mode 100644 index 0000000000..75b52484ea --- /dev/null +++ b/scripts/system/places/fonts/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/scripts/system/places/fonts/Roboto-Black.ttf b/scripts/system/places/fonts/Roboto-Black.ttf new file mode 100644 index 0000000000..43a00e0df0 Binary files /dev/null and b/scripts/system/places/fonts/Roboto-Black.ttf differ diff --git a/scripts/system/places/fonts/Roboto-BlackItalic.ttf b/scripts/system/places/fonts/Roboto-BlackItalic.ttf new file mode 100644 index 0000000000..5082cdc4e8 Binary files /dev/null and b/scripts/system/places/fonts/Roboto-BlackItalic.ttf differ diff --git a/scripts/system/places/fonts/Roboto-Bold.ttf b/scripts/system/places/fonts/Roboto-Bold.ttf new file mode 100644 index 0000000000..3742457900 Binary files /dev/null and b/scripts/system/places/fonts/Roboto-Bold.ttf differ diff --git a/scripts/system/places/fonts/Roboto-BoldItalic.ttf b/scripts/system/places/fonts/Roboto-BoldItalic.ttf new file mode 100644 index 0000000000..e85e7fb9e3 Binary files /dev/null and b/scripts/system/places/fonts/Roboto-BoldItalic.ttf differ diff --git a/scripts/system/places/fonts/Roboto-Italic.ttf b/scripts/system/places/fonts/Roboto-Italic.ttf new file mode 100644 index 0000000000..c9df607a4d Binary files /dev/null and b/scripts/system/places/fonts/Roboto-Italic.ttf differ diff --git a/scripts/system/places/fonts/Roboto-Light.ttf b/scripts/system/places/fonts/Roboto-Light.ttf new file mode 100644 index 0000000000..0e977514ff Binary files /dev/null and b/scripts/system/places/fonts/Roboto-Light.ttf differ diff --git a/scripts/system/places/fonts/Roboto-LightItalic.ttf b/scripts/system/places/fonts/Roboto-LightItalic.ttf new file mode 100644 index 0000000000..3ad14fa7c4 Binary files /dev/null and b/scripts/system/places/fonts/Roboto-LightItalic.ttf differ diff --git a/scripts/system/places/fonts/Roboto-Medium.ttf b/scripts/system/places/fonts/Roboto-Medium.ttf new file mode 100644 index 0000000000..e89b0b79a2 Binary files /dev/null and b/scripts/system/places/fonts/Roboto-Medium.ttf differ diff --git a/scripts/system/places/fonts/Roboto-MediumItalic.ttf b/scripts/system/places/fonts/Roboto-MediumItalic.ttf new file mode 100644 index 0000000000..a5a41d3d00 Binary files /dev/null and b/scripts/system/places/fonts/Roboto-MediumItalic.ttf differ diff --git a/scripts/system/places/fonts/Roboto-Regular.ttf b/scripts/system/places/fonts/Roboto-Regular.ttf new file mode 100644 index 0000000000..3d6861b423 Binary files /dev/null and b/scripts/system/places/fonts/Roboto-Regular.ttf differ diff --git a/scripts/system/places/icons/appicon_a.png b/scripts/system/places/icons/appicon_a.png new file mode 100644 index 0000000000..4fde8df1c8 Binary files /dev/null and b/scripts/system/places/icons/appicon_a.png differ diff --git a/scripts/system/places/icons/appicon_i.png b/scripts/system/places/icons/appicon_i.png new file mode 100644 index 0000000000..e5b01fe00c Binary files /dev/null and b/scripts/system/places/icons/appicon_i.png differ diff --git a/scripts/system/places/icons/back.png b/scripts/system/places/icons/back.png new file mode 100644 index 0000000000..313e456cde Binary files /dev/null and b/scripts/system/places/icons/back.png differ diff --git a/scripts/system/places/icons/fetched.png b/scripts/system/places/icons/fetched.png new file mode 100644 index 0000000000..fe70343345 Binary files /dev/null and b/scripts/system/places/icons/fetched.png differ diff --git a/scripts/system/places/icons/forward.png b/scripts/system/places/icons/forward.png new file mode 100644 index 0000000000..635ec54ba8 Binary files /dev/null and b/scripts/system/places/icons/forward.png differ diff --git a/scripts/system/places/icons/home.png b/scripts/system/places/icons/home.png new file mode 100644 index 0000000000..7b2d7e74ce Binary files /dev/null and b/scripts/system/places/icons/home.png differ diff --git a/scripts/system/places/icons/info.png b/scripts/system/places/icons/info.png new file mode 100644 index 0000000000..97f5dfc035 Binary files /dev/null and b/scripts/system/places/icons/info.png differ diff --git a/scripts/system/places/icons/notfetched.png b/scripts/system/places/icons/notfetched.png new file mode 100644 index 0000000000..3385ec6f30 Binary files /dev/null and b/scripts/system/places/icons/notfetched.png differ diff --git a/scripts/system/places/icons/notpinned.png b/scripts/system/places/icons/notpinned.png new file mode 100644 index 0000000000..9741e48fb6 Binary files /dev/null and b/scripts/system/places/icons/notpinned.png differ diff --git a/scripts/system/places/icons/pinned.png b/scripts/system/places/icons/pinned.png new file mode 100644 index 0000000000..bc626e6fc7 Binary files /dev/null and b/scripts/system/places/icons/pinned.png differ diff --git a/scripts/system/places/icons/placeholder_external.jpg b/scripts/system/places/icons/placeholder_external.jpg new file mode 100644 index 0000000000..ef3043e78c Binary files /dev/null and b/scripts/system/places/icons/placeholder_external.jpg differ diff --git a/scripts/system/places/icons/placeholder_federation.jpg b/scripts/system/places/icons/placeholder_federation.jpg new file mode 100644 index 0000000000..82dc5ee200 Binary files /dev/null and b/scripts/system/places/icons/placeholder_federation.jpg differ diff --git a/scripts/system/places/icons/placeholder_local.jpg b/scripts/system/places/icons/placeholder_local.jpg new file mode 100644 index 0000000000..fd5c168b19 Binary files /dev/null and b/scripts/system/places/icons/placeholder_local.jpg differ diff --git a/scripts/system/places/icons/sidewalk.png b/scripts/system/places/icons/sidewalk.png new file mode 100644 index 0000000000..16b41a6571 Binary files /dev/null and b/scripts/system/places/icons/sidewalk.png differ diff --git a/scripts/system/places/icons/teleport.png b/scripts/system/places/icons/teleport.png new file mode 100644 index 0000000000..8711838f43 Binary files /dev/null and b/scripts/system/places/icons/teleport.png differ diff --git a/scripts/system/places/places.css b/scripts/system/places/places.css new file mode 100644 index 0000000000..f02b949d75 --- /dev/null +++ b/scripts/system/places/places.css @@ -0,0 +1,915 @@ +/* +// +// places.css +// +// Created by Alezia Kurdis, January 1st, 2022. +// Copyright 2022 Overte e.V. +// +// css for the ui of the Places application. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +*/ + +body { + font-family: Roboto; + margin: 0px; + padding: 0px; + width: 100%; + background-color: #000000; +} + +.navBtn { + opacity: 0.5; + width: 24px; + height: 24px; +} + +.searchBtn { + opacity: 0.5; + width: 24px; + height: 24px; +} + +*:focus { + outline: none; +} + +#toolbar { + background-color: rgba(0,0,0,1); + width: 100%; + text-align: center; + justify-content: center; +} + +#navigationBar { + text-align: right; + padding: 3px; +} + +#currentLocation { + color: #CCCCCC; + background-color: rgba(255,255,255,0); + font-size: 11px; + border: 0px; + border-top: solid 1px #555555; + padding: 3px; + width: 96%; + margin: 0px; +} + +#search { + color: #ffffff; + background-color: rgba(255,255,255,0); + font-size: 16px; + border: 0px; + width: 96%; +} + +#searchFrame { + background-color: rgba(255,255,255,0.5); + padding: 2px; + border: 0px; + border-radius: 10px; + width: 96%; + margin: 0 auto; +} + +#searchTable { + width: 100%; +} + +#searchTableExternal { + width: 100%; +} + +#navigationBarContainer { + width: 100%; +} + +#clearBtn:link { + font-family: Roboto, sans-serif; + text-decoration: none; + font-size: 18px; + color: #555555; +} +#clearBtn:visited { + font-family: Roboto, sans-serif; + text-decoration: none; + font-size: 18px; + color: #555555; +} +#clearBtn:hover { + font-family: Roboto, sans-serif; + text-decoration: none; + font-size: 18px; + color: #000000; +} +#clearBtn:active { + font-family: Roboto, sans-serif; + text-decoration: none; + font-size: 18px; + color: #555555; +} + +::placeholder { + color: #555555; + opacity: 1; +} + +#list_content { + color: #ffffff; + width: 100%; + overflow-y: scroll; + height: 550px; + background-color: #404040; + background-image: url("icons/sidewalk.png"); + background-repeat: repeat-y; + background-attachment: local; + background-position: 0px 0px; +} + +#filters { + width: 95%; + padding: 2px; + text-align: right; +} + +#tabs { + width: 95%; + padding: 0px; + text-align: left; + +} + +.maturityFilter { + width: 16px; + height: 16px; + border: 0px; +} + +.presenceOfLifeFilter { + width: 64px; + height: 16px; + border: 0px; +} + +.overlay { + height: 100%; + width: 0; + position: fixed; + z-index: 1; + top: 0; + left: 0; + background-color: rgba(0,0,0, 1); + overflow: hidden; + transition: 0.5s; +} + +.overlay-content { + position: relative; + top: 0%; + width: 100%; + text-align: left; + margin-top: 0px; + color: #ffffff; +} + +.overlay a { + padding: 8px; + text-decoration: none; + font-size: 36px; + color: #ffffff; + display: block; + transition: 0.3s; +} + +.overlay a:hover, .overlay a:focus { + color: #f1f1f1; +} + +.overlay .closebtn { + position: absolute; + top: 5px; + right: 5px; + font-size: 50px; + z-index: 10; + text-shadow: 1px 1px 3px #000000; +} + +@media screen and (max-height: 450px) { + .overlay a {font-size: 20px} + .overlay .closebtn { + font-size: 40px; + top: 15px; + right: 35px; + } +} + +.inviteForm { + background-color: #3e3e57; + color: #ffffff; +} + +td.invite { + padding-top: 60px; + padding-right: 60px; + padding-bottom: 10px; + padding-left: 60px; + text-align: left; + font-weight: 700; + font-size: 18px; +} + +td.rightInvite { + padding-top: 0px; + padding-right: 60px; + padding-bottom: 0px; + padding-left: 60px; + text-align: right; +} +#eventDescTextBox { + width: 100%; + font-weight: 600; + font-size: 14px; +} + +#eventErrorMessage { + width: 100%; + color: #ffff00; + font-weight: 500; + font-size: 14px; +} + +.makeInviteBtn { + width: 100%; + border: 3px solid rgba(0,0,0,0); + border-radius: 10px; + font-weight: 700; + color: #ffffff; + font-size: 16px; + padding: 3px 22px 3px 22px; + text-decoration: none; + margin: 3px; +} + +.makeInviteBtn:hover { + background-color: #ffffff; + color: #000000; + border: 3px solid #000000; + text-decoration: none; +} + +#cancelInvite { + background: #405270; + background-image: linear-gradient(to bottom, #405270, #0d1117); + border: 0px; + border-radius: 10px; + font-weight: 700; + color: #ffffff; + font-size: 16px; + padding: 3px 22px 3px 22px; + text-decoration: none; +} + +#cancelInvite:hover { + background: #5d779e; + background-image: linear-gradient(to bottom, #5d779e, #0d1117); + text-decoration: none; +} + +.removeInviteBtn { + background: #b00000; + background-image: linear-gradient(to bottom, #b00000, #110000); + border-radius: 8px; + font-weight: 600; + color: #ffffff; + font-size: 12px; + padding: 3px 6px 3px 6px; + border: solid #1f628d 0px; + text-decoration: none; + margin-top: 3px; +} + +.removeInviteBtn:hover { + background: #c24646; + background-image: linear-gradient(to bottom, #c24646, #110000); + text-decoration: none; + margin-top: 3px; +} + +#presenceOfLifeLabel { + color: #00ff00; + font-size: 10px; + padding: 0px; +} + +#matuirityAdultLabel { + color: #00ff00; + font-size: 10px; + padding: 0px; +} + +#matuirityMatureLabel { + color: #00ff00; + font-size: 10px; + padding: 0px; +} + +#matuirityTeenLabel { + color: #00ff00; + font-size: 10px; + padding: 0px; +} + +#matuirityEveryoneLabel { + color: #00ff00; + font-size: 10px; + padding: 0px; +} + +#matuirityUnratedLabel{ + color: #00ff00; + font-size: 10px; + padding: 0px; +} + +.tabBtn { + border-radius: 9px 9px 0px 0px; + border: 0px; + font-weight: 700; + color: #808080; + font-size: 14px; + background: #404040; + padding: 5px 15px 5px 15px; + text-decoration: none; + background-image: linear-gradient(to bottom, #404040, #1c1c1c); +} + +.tabBtn:disabled { + background: #404040; + color: #b0b0b0; + text-decoration: none; +} + + +.adultFilterOn { + background: #de1107; + color: #ffffff; + font-size: 10px; + padding: 1px 2px 1px 2px; + border: solid #000000 1px; + text-decoration: none; + width: 14px; + text-align: center; +} + +button.adultFilterOn:hover { + border: solid #ffffff 1px; +} + +.lifeFilterOn { + background: #1aa616; + color: #00FF00; + font-size: 10px; + padding: 1px 2px 1px 2px; + border: solid #000000 1px; + text-decoration: none; +} + +button.lifeFilterOn:hover { + border: solid #00FF00 1px; +} + +.matureFilterOn { + background: #e08c0d; + color: #ffffff; + font-size: 10px; + padding: 1px 2px 1px 2px; + border: solid #000000 1px; + text-decoration: none; + width: 14px; + text-align: center; +} + +button.matureFilterOn:hover { + border: solid #ffffff 1px; +} + +.teenFilterOn { + background: #d6c313; + color: #ffffff; + font-size: 10px; + padding: 1px 2px 1px 2px; + border: solid #000000 1px; + text-decoration: none; + width: 14px; + text-align: center; +} + +button.teenFilterOn:hover { + border: solid #ffffff 1px; +} + +.everyoneFilterOn { + background: #7acf1f; + color: #ffffff; + font-size: 10px; + padding: 1px 2px 1px 2px; + border: solid #000000 1px; + text-decoration: none; + width: 14px; + text-align: center; +} + +button.everyoneFilterOn:hover { + border: solid #ffffff 1px; +} + +.unratedFilterOn { + background: #b5b5b5; + color: #ffffff; + font-size: 10px; + padding: 1px 2px 1px 2px; + border: solid #000000 1px; + text-decoration: none; + width: 14px; + text-align: center; +} + +button.unratedFilterOn:hover { + border: solid #ffffff 1px; +} + +.filterOff { + background: #333333; + color: #888888; + font-size: 10px; + padding: 1px 2px 1px 2px; + border: solid #000000 1px; + text-decoration: none; + width: 14px; + text-align: center; +} + +button.filterOff:hover { + border: solid #888888 1px; +} + +.filterLifeOff { + background: #333333; + color: #888888; + font-size: 10px; + padding: 1px 2px 1px 2px; + border: solid #000000 1px; + text-decoration: none; +} + +button.filterLifeOff:hover { + border: solid #888888 1px; +} + +.placeMaturity { + width: 100%; + text-align: left; +} + + +div.placeEntry { + width: 85%; + height: 60px; + background: #292929; + border: 0px; + border-radius: 6px; + box-shadow: 3px 3px 5px rgba(0,0,0,0.8); + margin: 6px 0px 9px 6px; + text-align: center; +} + +div.placeEntryHighlighter { + width: 85%; + height: 80px; + font-size: 14px; + font-weight: 700; + border: 0px; + border-radius: 6px; + box-shadow: 3px 3px 5px rgba(0,0,0,0.8); + margin: 6px 0px 9px 6px; + padding-top: 6px; + text-align: center; +} + +.OPEN { + background: #00b7ff; + color: #ffffff; +} + +.MEET { + background: #ffaa00; + color: #000000; +} + +.GAME { + background: #00e32d; + color: #000000; +} + +.SHOW { + background: #8000ff; + color: #ffffff; +} + +.CLUB { + background: #e32d00; + color: #ffffff; +} + +.LEAR { + background: #eeff00; + color: #000000; +} + +div.placeEntryHighlighted { + width: 100%; + height: 60px; + background: #292929; + border: 0px; + border-radius: 6px; + text-align: center; +} + +div.placeDetail-event { + width: 100%; + text-align: left; + font-size: 10px; + font-weight: 700px; + padding: 0px; +} + +#listInfo { + width: 85%; + text-align: right; + color: #7a7a7a; + font-size: 13px; + font-weight: 700; + padding: 4px; +} + +#loadMore { + width: 85%; + text-align: center; + padding: 8px; +} + + +#loadMoreBtn{ + background: #a6a6a6; + background-image: linear-gradient(to bottom, #a6a6a6, #525252); + border-radius: 10px; + border: 0px; + color: #ffffff; + font-size: 14px; + font-weight: 500; + padding: 4px 30px 4px 30px; + text-decoration: none; +} + +#loadMoreBtn:hover { + background: #bfbfbf; + background-image: linear-gradient(to bottom, #bfbfbf, #6e6e6e); + text-decoration: none; +} + +.blur { + width: 100%; + height: 100%; + backdrop-filter: blur(6px); + padding: 0px; + border-radius: 6px; + border: 0px; +} + +.placeTitle { + color: #ffffff; + font-size: 21px; + font-weight: 700; + text-decoration: none; + text-shadow: 1px 1px 3px #000000; +} + +.placeTable { + width: 100%; +} + +td.presenceOfLife-LIFE { + font-size: 10px; + font-weight: 700; + color: #00FF00; + text-align: right; +} + +td.presenceOfLife-FULL { + font-size: 10px; + font-weight: 700; + color: #ff0000; + text-align: right; +} + +td.presenceOfLife-NOBODY { + font-size: 10px; + font-weight: 700; + color: #555555; + text-align: right; +} + +td.place-entry-description { + font-size: 12px; + font-weight: 600; + font-style: italic; + color: #ffffff; + text-align: left; + text-shadow: 1px 1px 2px #000000; +} + +div.domainEntry { + width: 100%; + height: 40px; + background: rgba(32,0,255,0.15); + border: 0px; + text-align: left; + padding-bottom: 4px; +} + +.domainTable { + width: 79%; + vertical-align: top; +} + +td.domainTitle { + font-size: 20px; + font-weight: 700; + font-style: none; + color: #7366c4; + text-align: left; +} + +font.domain-capacity { + font-size: 10px; + font-weight: 600; + color: #999999; +} + +font.domain-nbrUser { + font-size: 18px; + font-weight: 500; +} + +font.NOBODY { + color: #555555; +} + +font.LIFE { + color: #00FF00; +} + +font.FULL { + color: #FF0000; +} + +font.domain-nbrUser_small { + font-size: 10px; + color: #999999; +} + +#placeDetail-image { + width: 100%; + height: 270px; + object-fit: cover; +} + +#placeDetail-placeName { + width: 95%; + font-size: 30px; + font-weight: 700; + color: #ffffff; + padding: 10px; +} + +#placeDetail-description { + width: 95%; + font-size: 12px; + font-weight: 500; + color: #cccccc; + padding: 10px; + text-align: justify; + text-justify: inter-word; +} + +#placeDetail-visitBtn { + background: #0000ff; + background-image: linear-gradient(to bottom, #0000ff, #000020); + border: 0px; + border-radius: 10px; + font-weight: 800; + color: #ffffff; + font-size: 20px; + padding: 3px 22px 3px 22px; + text-decoration: none; +} + +#placeDetail-visitBtn:hover { + background: #057eff; + background-image: linear-gradient(to bottom, #057eff, #00090f); + text-decoration: none; +} + +#placeDetail-visitBtn-container { + width: 100%; + text-align: left; + margin-bottom: 40px; +} + +#placeDetail-placedata { + width: 100%; + font-size: 11px; + font-weight: 500; + color: #888888; + padding: 5px; + text-align: left; +} + +#placeDetail-managers { + width: 100%; + font-size: 12px; + font-weight: 700; + color: #ffffff; + padding-left: 60px; + text-align: left; +} + +#placeDetail-domain { + font-size: 13px; + font-weight: 700; + color: #cccccc; + width: 100%; +} + +#placeDetail-users { + font-size: 30px; + font-weight: 600; +} + +#placeDetail-capacity { + width: 100%; +} + +#placeDetail-metaverse { + font-size: 10px; + font-weight: 500; + color: #cccccc; + width: 100%; +} + +.fieldsSearchFilterOn { + background: #0085b5; + color: #00bbff; + font-size: 10px; + padding: 1px 2px 1px 2px; + border: solid #000000 1px; + text-decoration: none; + text-align: center; +} + +button.fieldsSearchFilterOn:hover { + border: solid #00bbff 1px; +} + +.fieldsSearchFilterOff { + background: #333333; + color: #888888; + font-size: 10px; + padding: 1px 2px 1px 2px; + border: solid #000000 1px; + text-decoration: none; + text-align: center; +} + +button.fieldsSearchFilterOff:hover { + border: solid #888888 1px; +} + +::-webkit-scrollbar { + width: 16px; + height: 10px; +} +::-webkit-scrollbar-track { + background-color: #2e2e2e; +} + +::-webkit-scrollbar-thumb { + background-color: #696969; + border: 2px solid #2e2e2e; + border-radius: 8px; +} + +div.warningEntry { + width: 100%; + height: 30px; + background: rgba(255,128,0,0.2); + border: 0px; + text-align: left; + padding-bottom: 4px; +} + +.warningTable { + width: 79%; + vertical-align: top; +} + +td.warningTitle { + font-size: 12px; + font-weight: 500; + font-style: none; + color: #ffa200; + text-align: left; +} + +.metaverseTitle { + color: #ffffff; + font-size: 16px; + font-weight: 700; + text-decoration: none; + text-shadow: 1px 1px 3px #000000; +} + +.metaverseRegion { + font-size: 10px; + font-style: none; + color: #ffffff; + text-align: left; +} + +.region-LOCAL { + color: #ffbc00; +} + +.region-FEDERATION { + color: #00ff58; +} + +.region-EXTERNAL { + color: #00e2ff; +} + +.filterRegionOff { + background: #333333; + color: #888888; + font-size: 10px; + padding: 1px 2px 1px 2px; + border: solid #000000 1px; + text-decoration: none; +} + +button.filterRegionOff:hover { + border: solid #888888 1px; +} + +.localFilterOn { + background: #ad8000; + color: #ffbc00; + font-size: 10px; + padding: 1px 2px 1px 2px; + border: solid #000000 1px; + text-decoration: none; +} + +button.localFilterOn:hover { + border: solid #ffbc00 1px; +} + +.federationFilterOn { + background: #00ad3c; + color: #00ff58; + font-size: 10px; + padding: 1px 2px 1px 2px; + border: solid #000000 1px; + text-decoration: none; +} + +button.federationFilterOn:hover { + border: solid #00ff58 1px; +} + +.externalFilterOn { + background: #009aad; + color: #00e2ff; + font-size: 10px; + padding: 1px 2px 1px 2px; + border: solid #000000 1px; + text-decoration: none; +} + +button.externalFilterOn:hover { + border: solid #00e2ff 1px; +} diff --git a/scripts/system/places/places.html b/scripts/system/places/places.html new file mode 100644 index 0000000000..549aad556e --- /dev/null +++ b/scripts/system/places/places.html @@ -0,0 +1,758 @@ + + + + + + Places + + + + + + +
+ + + + + + +
+
+ + + + +     +
+ + + +     + +     + + + + + +
+
+ + + + +
+
+

  Loading...
+
+ +
+ × +
+ +
+ + + + + + +
+
+
  + +
+
+
+ DOMAIN: +
+

USERS:
+
+

+
+

+ REGION: +
+
+
+
+
+
+ + + + diff --git a/scripts/system/places/places.js b/scripts/system/places/places.js new file mode 100644 index 0000000000..056dc2d174 --- /dev/null +++ b/scripts/system/places/places.js @@ -0,0 +1,588 @@ +"use strict"; +// +// places.js +// +// Created by Alezia Kurdis, January 1st, 2022. +// Copyright 2022 Overte e.V. +// +// Generate an explore app based on the differents source of placename data. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +(function() { + var jsMainFileName = "places.js"; + var ROOT = Script.resolvePath('').split(jsMainFileName)[0]; + + var metaverseServers = []; + var SETTING_METAVERSE_TO_FETCH = "placesAppMetaverseToFetch"; + var SETTING_PINNED_METAVERSE = "placesAppPinnedMetaverse"; + + var httpRequest = null; + var placesData; + var portalList = []; + + var nbrPlacesNoProtocolMatch = 0; + var nbrPlaceProtocolKnown = 0; + + var APP_NAME = "PLACES"; + var APP_URL = ROOT + "places.html"; + var APP_ICON_INACTIVE = ROOT + "icons/appicon_i.png"; + var APP_ICON_ACTIVE = ROOT + "icons/appicon_a.png"; + var appStatus = false; + var channel = "com.overte.places"; + + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + + tablet.screenChanged.connect(onScreenChanged); + + var button = tablet.addButton({ + text: APP_NAME, + icon: APP_ICON_INACTIVE, + activeIcon: APP_ICON_ACTIVE, + sortOrder: 8 + }); + + var timestamp = 0; + var INTERCALL_DELAY = 200; //0.3 sec + var PERSISTENCE_ORDERING_CYCLE = 5 * 24 * 3600 * 1000; //5 days + + function clicked(){ + if (appStatus === true) { + tablet.webEventReceived.disconnect(onAppWebEventReceived); + tablet.gotoHomeScreen(); + appStatus = false; + } else { + tablet.gotoWebScreen(APP_URL); + tablet.webEventReceived.connect(onAppWebEventReceived); + appStatus = true; + } + + button.editProperties({ + isActive: appStatus + }); + } + + button.clicked.connect(clicked); + + + function onAppWebEventReceived(message) { + var d = new Date(); + var n = d.getTime(); + + var messageObj = JSON.parse(message); + if (messageObj.channel === channel) { + if (messageObj.action === "READY_FOR_CONTENT" && (n - timestamp) > INTERCALL_DELAY) { + d = new Date(); + timestamp = d.getTime(); + transmitPortalList(); + + sendCurrentLocationToUI(); + + } else if (messageObj.action === "TELEPORT" && (n - timestamp) > INTERCALL_DELAY) { + d = new Date(); + timestamp = d.getTime(); + + if (messageObj.address.length > 0) { + Window.location = messageObj.address; + } + + } else if (messageObj.action === "GO_HOME" && (n - timestamp) > INTERCALL_DELAY) { + if (LocationBookmarks.getHomeLocationAddress()) { + location.handleLookupString(LocationBookmarks.getHomeLocationAddress()); + } else { + location.goToLocalSandbox(); + } + } else if (messageObj.action === "GO_BACK" && (n - timestamp) > INTERCALL_DELAY) { + location.goBack(); + } else if (messageObj.action === "GO_FORWARD" && (n - timestamp) > INTERCALL_DELAY) { + location.goForward(); + } else if (messageObj.action === "PIN_META" && (n - timestamp) > INTERCALL_DELAY) { + d = new Date(); + timestamp = d.getTime(); + metaverseServers[messageObj.metaverseIndex].pinned = messageObj.value; + savePinnedMetaverseSetting(); + } else if (messageObj.action === "FETCH_META" && (n - timestamp) > INTERCALL_DELAY) { + d = new Date(); + timestamp = d.getTime(); + metaverseServers[messageObj.metaverseIndex].fetch = messageObj.value; + saveMetaverseToFetchSetting(); + + } + } + } + + function savePinnedMetaverseSetting() { + var pinnedServers = []; + for (var q = 0; q < metaverseServers.length; q++) { + if (metaverseServers[q].pinned) { + pinnedServers.push(metaverseServers[q].url); + } + } + Settings.setValue(SETTING_PINNED_METAVERSE, pinnedServers); + } + + function saveMetaverseToFetchSetting() { + var fetchedServers = []; + for (var q = 0; q < metaverseServers.length; q++) { + if (metaverseServers[q].fetch) { + fetchedServers.push(metaverseServers[q].url); + } + } + Settings.setValue(SETTING_METAVERSE_TO_FETCH, fetchedServers); + } + + function onHostChanged(host) { + sendCurrentLocationToUI(); + } + + location.hostChanged.connect(onHostChanged); + + function sendCurrentLocationToUI() { + var currentLocationMessage = { + "channel": channel, + "action": "CURRENT_LOCATION", + "data": location.href + }; + + tablet.emitScriptEvent(currentLocationMessage); + } + + function onScreenChanged(type, url) { + if (type == "Web" && url.indexOf(APP_URL) != -1) { + appStatus = true; + } else { + appStatus = false; + } + + button.editProperties({ + isActive: appStatus + }); + } + + function transmitPortalList() { + metaverseServers = []; + buildMetaverseServerList(); + + portalList = []; + nbrPlacesNoProtocolMatch = 0; + nbrPlaceProtocolKnown = 0; + var extractedData; + + for (var i = 0; i < metaverseServers.length; i++ ) { + if (metaverseServers[i].fetch === true) { + extractedData = getContent(metaverseServers[i].url + "/api/v1/places?status=online" + "&acash=" + Math.floor(Math.random() * 999999)); + try { + placesData = JSON.parse(extractedData); + processData(metaverseServers[i]); + } catch(e) { + placesData = {}; + } + httpRequest = null; + } + } + + //################### TO REMOVED ONCE NO MORE USED ##################### + getDeprecatedBeaconsData(); + //################### END: TO REMOVED ONCE NO MORE USED ##################### + + addUtilityPortals(); + + portalList.sort(sortOrder); + + var percentProtocolRejected = Math.floor((nbrPlacesNoProtocolMatch/nbrPlaceProtocolKnown) * 100); + + var warning = ""; + if (percentProtocolRejected > 50) { + warning = "WARNING: " + percentProtocolRejected + "% of the places are not listed because they are running under a different protocol. Maybe consider to upgrade."; + } + + var message = { + "channel": channel, + "action": "PLACE_DATA", + "data": portalList, + "warning": warning, + "metaverseServers": metaverseServers + }; + + tablet.emitScriptEvent(message); + + }; + + function getFederationData() { + /* + //If federation.json is got from the Metaverse Server (not implemented yet) + var fedDirectoryUrl = AccountServices.metaverseServerURL + "/federation.json"; + var extractedFedData = getContent(fedDirectoryUrl); + */ + + /* + //If federation.json is got from a web storage + var fedDirectoryUrl = ROOT + "federation.json"; + "?version=" + Math.floor(Math.random() * 999999); + var extractedFedData = getContent(fedDirectoryUrl); + */ + + //if federation.json is local, on the user installation + var extractedFedData = JSON.stringify(Script.require("./federation.json")); + + return extractedFedData; + + } + + function buildMetaverseServerList () { + var extractedFedData = getFederationData(); + + var pinnedMetaverses = Settings.getValue(SETTING_PINNED_METAVERSE, []); + var metaversesToFetch = Settings.getValue(SETTING_METAVERSE_TO_FETCH, []); + + var federation = []; + try { + federation = JSON.parse(extractedFedData); + } catch(e) { + federation = []; + } + var currentFound = false; + var region, pinned, fetch, order, metaverse; + for (var i=0; i < federation.length; i++) { + if (federation[i].node === AccountServices.metaverseServerURL) { + region = "local"; + order = "A"; + fetch = true; + pinned = false; + currentFound = true; + } else { + region = "federation"; + order = "F"; + fetch = false; + pinned = false; + } + + metaverse = { + "url": federation[i].node, + "region": region, + "fetch": fetch, + "pinned": pinned, + "order": order + }; + metaverseServers.push(metaverse); + } + if (!currentFound) { + metaverse = { + "url": AccountServices.metaverseServerURL, + "region": "local", + "fetch": true, + "pinned": false, + "order": "A" + }; + metaverseServers.push(metaverse); + } + + for (i = 0; i < pinnedMetaverses.length; i++) { + var target = pinnedMetaverses[i]; + var found = false; + for (var k = 0; k < metaverseServers.length; k++) { + if (metaverseServers[k].url === target) { + metaverseServers[k].pinned = true; + found = true; + break; + } + } + if (!found) { + metaverse = { + "url": target, + "region": "external", + "fetch": false, + "pinned": true, + "order": "Z" + }; + metaverseServers.push(metaverse); + } + } + + for (i = 0; i < metaversesToFetch.length; i++) { + var target = metaversesToFetch[i]; + for (var k = 0; k < metaverseServers.length; k++) { + if (metaverseServers[k].url === target) { + metaverseServers[k].fetch = true; + break; + } + } + } + + metaverseServers.sort(sortOrder); + } + + function getContent(url) { + httpRequest = new XMLHttpRequest(); + httpRequest.open("GET", url, false); // false for synchronous request + httpRequest.send( null ); + return httpRequest.responseText; + } + + function processData(metaverseInfo){ + var supportedProtocole = Window.protocolSignature(); + + var places = placesData.data.places; + for (var i = 0;i < places.length; i++) { + + var region, category, accessStatus; + + var description = (places[i].description ? places[i].description : ""); + var thumbnail = (places[i].thumbnail ? places[i].thumbnail : ""); + + if ( places[i].domain.protocol_version === supportedProtocole ) { + + region = metaverseInfo.order; + + if ( thumbnail.substr(0, 4).toLocaleLowerCase() !== "http") { + category = "O"; //Other + } else { + category = "A"; //Attraction + } + + if (places[i].domain.num_users > 0) { + if (places[i].domain.num_users >= places[i].domain.capacity && places[i].domain.capacity !== 0) { + accessStatus = "FULL"; + } else { + accessStatus = "LIFE"; + } + } else { + accessStatus = "NOBODY"; + } + + var portal = { + "order": category + "_" + region + "_" + getSeededRandomForString(places[i].id), + "category": category, + "accessStatus": accessStatus, + "name": places[i].name, + "description": description, + "thumbnail": thumbnail, + "maturity": places[i].maturity, + "address": places[i].address, + "current_attendance": places[i].domain.num_users, + "id": places[i].id, + "visibility": places[i].visibility, + "capacity": places[i].domain.capacity, + "tags": getListFromArray(places[i].tags), + "managers": getListFromArray(places[i].managers), + "domain": places[i].domain.name, + "domainOrder": aplphabetize(zeroPad(places[i].domain.num_users, 6)) + "_" + places[i].domain.name + "_" + places[i].name, + "metaverseServer": metaverseInfo.url, + "metaverseRegion": metaverseInfo.region + }; + portalList.push(portal); + + } else { + nbrPlacesNoProtocolMatch++; + } + } + + nbrPlaceProtocolKnown = nbrPlaceProtocolKnown + places.length; + + } + + //################### CODE TO REMOVED ONCE NO MORE USED ##################### + function getDeprecatedBeaconsData() { + var url = "https://metaverse.vircadia.com/interim/d-goto/app/goto.json"; + httpRequest = new XMLHttpRequest(); + httpRequest.open("GET", url, false); // false for synchronous request + httpRequest.send( null ); + var extractedData = httpRequest.responseText; + + httpRequest = null; + + var places; + try { + places = JSON.parse(extractedData); + } catch(e) { + places = {}; + } + + for (var i = 0;i < places.length; i++) { + + var category, accessStatus; + + var description = "..."; + var thumbnail = ""; + category = "U"; //uncertain + + if (places[i].People > 0) { + accessStatus = "LIFE"; + } else { + accessStatus = "NOBODY"; + } + + var shortenName = places[i]["Domain Name"].substr(0, 24); + + + var portal = { + "order": category + "_Z_" + getSeededRandomForString(places[i]["Domain Name"]), + "category": category, + "accessStatus": accessStatus, + "name": shortenName, + "description": description, + "thumbnail": thumbnail, + "maturity": "unrated", + "address": places[i].Visit, + "current_attendance": places[i].People, + "id": "BEACON" + i, + "visibility": "open", + "capacity": 0, + "tags": "", + "managers": places[i].Owner, + "domain": "UNKNOWN (Beacon)", + "domainOrder": "ZZZZZZZZZZZZZUA", + "metaverseServer": "", + "metaverseRegion": "external" + }; + portalList.push(portal); + } + } + //################### END::: CODE TO REMOVED ONCE NO MORE USED ##################### + + function addUtilityPortals() { + var localHostPortal = { + "order": "Z_Z_AAAAAA", + "category": "Z", + "accessStatus": "NOBODY", + "name": "localhost", + "description": "", + "thumbnail": "", + "maturity": "unrated", + "address": "localhost", + "current_attendance": 0, + "id": "", + "visibility": "open", + "capacity": 0, + "tags": "", + "managers": "", + "domain": "", + "domainOrder": "ZZZZZZZZZZZZZZA", + "metaverseServer": "", + "metaverseRegion": "local" + }; + portalList.push(localHostPortal); + + var tutorialPortal = { + "order": "Z_Z_AAAAAZ", + "category": "Z", + "accessStatus": "NOBODY", + "name": "tutorial", + "description": "", + "thumbnail": "", + "maturity": "unrated", + "address": "file:///~/serverless/tutorial.json", + "current_attendance": 0, + "id": "", + "visibility": "open", + "capacity": 0, + "tags": "", + "managers": "", + "domain": "", + "domainOrder": "ZZZZZZZZZZZZZZZ", + "metaverseServer": "", + "metaverseRegion": "local" + }; + portalList.push(tutorialPortal); + + } + + function aplphabetize(num) { + var numbstring = num.toString(); + var newChar = "JIHGFEDCBA"; + var refChar = "0123456789"; + var processed = ""; + for (var j=0; j < numbstring.length; j++) { + processed = processed + newChar.substr(refChar.indexOf(numbstring.charAt(j)),1); + } + return processed; + } + + function getListFromArray(dataArray) { + var dataList = ""; + if (dataArray !== undefined && dataArray.length > 0) { + for (var k = 0; k < dataArray.length; k++) { + if (k !== 0) { + dataList += ", "; + } + dataList += dataArray[k]; + } + if (dataArray.length > 1){ + dataList += "."; + } + } + + return dataList; + } + + function sortOrder(a, b) { + var orderA = a.order.toUpperCase(); + var orderB = b.order.toUpperCase(); + if (orderA > orderB) { + return 1; + } else if (orderA < orderB) { + return -1; + } + if (a.order > b.order) { + return 1; + } else if (a.order < b.order) { + return -1; + } + return 0; + } + + function zeroPad(num, places) { + var zero = places - num.toString().length + 1; + return Array(+(zero > 0 && zero)).join("0") + num; + } + + function getFrequentPlaces(list) { + var count = {}; + list.forEach(function(list) { + count[list] = (count[list] || 0) + 1; + }); + return count; + } + + //####### seed random library ################ + Math.seed = 75; + + Math.seededRandom = function(max, min) { + max = max || 1; + min = min || 0; + Math.seed = (Math.seed * 9301 + 49297) % 233280; + var rnd = Math.seed / 233280; + return min + rnd * (max - min); + } + + function getStringScore(str) { + var score = 0; + for (var j = 0; j < str.length; j++){ + score += str.charAt(j).charCodeAt(0) + 1; + } + return score; + } + + function getSeededRandomForString(str) { + var score = getStringScore(str); + var d = new Date(); + var n = d.getTime(); + var currentSeed = Math.floor(n / PERSISTENCE_ORDERING_CYCLE); + Math.seed = score * currentSeed; + return zeroPad(Math.floor(Math.seededRandom() * 100000),5); + } + //####### END of seed random library ################ + + function cleanup() { + + if (appStatus) { + tablet.gotoHomeScreen(); + tablet.webEventReceived.disconnect(onAppWebEventReceived); + } + + tablet.screenChanged.disconnect(onScreenChanged); + tablet.removeButton(button); + } + + Script.scriptEnding.connect(cleanup); +}());