diff --git a/scripts/system/html/js/marketplacesInjectNoScrollbar.js b/scripts/system/html/js/marketplacesInjectNoScrollbar.js new file mode 100644 index 0000000000..86ec1b944a --- /dev/null +++ b/scripts/system/html/js/marketplacesInjectNoScrollbar.js @@ -0,0 +1,349 @@ +// +// marketplacesInject.js +// +// Created by David Rowe on 12 Nov 2016. +// Copyright 2016 High Fidelity, Inc. +// +// Injected into marketplace Web pages. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function () { + + // Event bridge messages. + var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; + var CLARA_IO_STATUS = "CLARA.IO STATUS"; + var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; + var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; + var GOTO_DIRECTORY = "GOTO_DIRECTORY"; + var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; + var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; + var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; + + var canWriteAssets = false; + var xmlHttpRequest = null; + var isPreparing = false; // Explicitly track download request status. + + function injectCommonCode(isDirectoryPage) { + + // Supporting styles from marketplaces.css. + // Glyph font family, size, and spacing adjusted because HiFi-Glyphs cannot be used cross-domain. + $("head").append( + '' + ); + + // Supporting styles from edit-style.css. + // Font family, size, and position adjusted because Raleway-Bold cannot be used cross-domain. + $("head").append( + '' + ); + + // Footer. + var isInitialHiFiPage = location.href === "https://metaverse.highfidelity.com/marketplace?"; + $("body").append( + '
' + + (!isInitialHiFiPage ? '' : '') + + (isInitialHiFiPage ? '🛈 See also other marketplaces.' : '') + + (!isDirectoryPage ? '' : '') + + (isDirectoryPage ? '🛈 Select a marketplace to explore.' : '') + + '
' + ); + + // Footer actions. + $("#back-button").on("click", function () { + window.history.back(); + }); + $("#all-markets").on("click", function () { + EventBridge.emitWebEvent(GOTO_DIRECTORY); + }); + } + + function injectDirectoryCode() { + + // Remove e-mail hyperlink. + var letUsKnow = $("#letUsKnow"); + letUsKnow.replaceWith(letUsKnow.html()); + + // Add button links. + $('#exploreClaraMarketplace').on('click', function () { + window.location = "https://clara.io/library?gameCheck=true&public=true"; + }); + $('#exploreHifiMarketplace').on('click', function () { + window.location = "http://www.highfidelity.com/marketplace"; + }); + } + + function injectHiFiCode() { + // Nothing to do. + } + + function updateClaraCode() { + // Have to repeatedly update Clara page because its content can change dynamically without location.href changing. + + // Clara library page. + if (location.href.indexOf("clara.io/library") !== -1) { + // Make entries navigate to "Image" view instead of default "Real Time" view. + var elements = $("a.thumbnail"); + for (var i = 0, length = elements.length; i < length; i++) { + var value = elements[i].getAttribute("href"); + if (value.slice(-6) !== "/image") { + elements[i].setAttribute("href", value + "/image"); + } + } + } + + // Clara item page. + if (location.href.indexOf("clara.io/view/") !== -1) { + // Make site navigation links retain gameCheck etc. parameters. + var element = $("a[href^=\'/library\']")[0]; + var parameters = "?gameCheck=true&public=true"; + var href = element.getAttribute("href"); + if (href.slice(-parameters.length) !== parameters) { + element.setAttribute("href", href + parameters); + } + + // Remove unwanted buttons and replace download options with a single "Download to High Fidelity" button. + var buttons = $("a.embed-button").parent("div"); + var downloadFBX; + if (buttons.find("div.btn-group").length > 0) { + buttons.children(".btn-primary, .btn-group , .embed-button").each(function () { this.remove(); }); + if ($("#hifi-download-container").length === 0) { // Button hasn't been moved already. + downloadFBX = $(' Download to High Fidelity'); + buttons.prepend(downloadFBX); + downloadFBX[0].addEventListener("click", startAutoDownload); + } + } + + // Move the "Download to High Fidelity" button to be more visible on tablet. + if ($("#hifi-download-container").length === 0 && window.innerWidth < 700) { + var downloadContainer = $('
'); + $(".top-title .col-sm-4").append(downloadContainer); + downloadContainer.append(downloadFBX); + } + + // Automatic download to High Fidelity. + function startAutoDownload() { + + // One file request at a time. + if (isPreparing) { + console.log("WARNIKNG: Clara.io FBX: Prepare only one download at a time"); + return; + } + + // User must be able to write to Asset Server. + if (!canWriteAssets) { + console.log("ERROR: Clara.io FBX: File download cancelled because no permissions to write to Asset Server"); + EventBridge.emitWebEvent(WARN_USER_NO_PERMISSIONS); + return; + } + + // User must be logged in. + var loginButton = $("#topnav a[href='/signup']"); + if (loginButton.length > 0) { + loginButton[0].click(); + return; + } + + // Obtain zip file to download for requested asset. + // Reference: https://clara.io/learn/sdk/api/export + + //var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true¢erScene=true&alignSceneGround=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL"; + // 13 Jan 2017: Specify FBX version 5 and remove some options in order to make Clara.io site more likely to + // be successful in generating zip files. + var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?fbxUnit=Meter&fbxVersion=5&fbxEmbedTextures=true&imageFormat=WebGL"; + + var uuid = location.href.match(/\/view\/([a-z0-9\-]*)/)[1]; + var url = XMLHTTPREQUEST_URL.replace("{uuid}", uuid); + + xmlHttpRequest = new XMLHttpRequest(); + var responseTextIndex = 0; + var zipFileURL = ""; + + xmlHttpRequest.onreadystatechange = function () { + // Messages are appended to responseText; process the new ones. + var message = this.responseText.slice(responseTextIndex); + var statusMessage = ""; + + if (isPreparing) { // Ignore messages in flight after finished/cancelled. + var lines = message.split(/[\n\r]+/); + + for (var i = 0, length = lines.length; i < length; i++) { + if (lines[i].slice(0, 5) === "data:") { + // Parse line. + var data; + try { + data = JSON.parse(lines[i].slice(5)); + } + catch (e) { + data = {}; + } + + // Extract status message. + if (data.hasOwnProperty("message") && data.message !== null) { + statusMessage = data.message; + console.log("Clara.io FBX: " + statusMessage); + } + + // Extract zip file URL. + if (data.hasOwnProperty("files") && data.files.length > 0) { + zipFileURL = data.files[0].url; + if (zipFileURL.slice(-4) !== ".zip") { + console.log(JSON.stringify(data)); // Data for debugging. + } + } + } + } + + if (statusMessage !== "") { + // Update the UI with the most recent status message. + EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); + } + } + + responseTextIndex = this.responseText.length; + }; + + // Note: onprogress doesn't have computable total length so can't use it to determine % complete. + + xmlHttpRequest.onload = function () { + var statusMessage = ""; + + if (!isPreparing) { + return; + } + + isPreparing = false; + + var HTTP_OK = 200; + if (this.status !== HTTP_OK) { + statusMessage = "Zip file request terminated with " + this.status + " " + this.statusText; + console.log("ERROR: Clara.io FBX: " + statusMessage); + EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); + } else if (zipFileURL.slice(-4) !== ".zip") { + statusMessage = "Error creating zip file for download."; + console.log("ERROR: Clara.io FBX: " + statusMessage + ": " + zipFileURL); + EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); + } else { + EventBridge.emitWebEvent(CLARA_IO_DOWNLOAD + " " + zipFileURL); + console.log("Clara.io FBX: File download initiated for " + zipFileURL); + } + + xmlHttpRequest = null; + } + + isPreparing = true; + + console.log("Clara.io FBX: Request zip file for " + uuid); + EventBridge.emitWebEvent(CLARA_IO_STATUS + " Initiating download"); + + xmlHttpRequest.open("POST", url, true); + xmlHttpRequest.setRequestHeader("Accept", "text/event-stream"); + xmlHttpRequest.send(); + } + } + } + + function injectClaraCode() { + + // Make space for marketplaces footer in Clara pages. + $("head").append( + '' + ); + + // Condense space. + $("head").append( + '' + ); + + // Move "Download to High Fidelity" button. + $("head").append( + '' + ); + + // Update code injected per page displayed. + var updateClaraCodeInterval = undefined; + updateClaraCode(); + updateClaraCodeInterval = setInterval(function () { + updateClaraCode(); + }, 1000); + + window.addEventListener("unload", function () { + clearInterval(updateClaraCodeInterval); + updateClaraCodeInterval = undefined; + }); + + EventBridge.emitWebEvent(QUERY_CAN_WRITE_ASSETS); + } + + function cancelClaraDownload() { + isPreparing = false; + + if (xmlHttpRequest) { + xmlHttpRequest.abort(); + xmlHttpRequest = null; + console.log("Clara.io FBX: File download cancelled"); + EventBridge.emitWebEvent(CLARA_IO_CANCELLED_DOWNLOAD); + } + } + + function onLoad() { + + EventBridge.scriptEventReceived.connect(function (message) { + if (message.slice(0, CAN_WRITE_ASSETS.length) === CAN_WRITE_ASSETS) { + canWriteAssets = message.slice(-4) === "true"; + } + + if (message.slice(0, CLARA_IO_CANCEL_DOWNLOAD.length) === CLARA_IO_CANCEL_DOWNLOAD) { + cancelClaraDownload(); + } + }); + + var DIRECTORY = 0; + var HIFI = 1; + var CLARA = 2; + var pageType = DIRECTORY; + + if (location.href.indexOf("highfidelity.com/") !== -1) { pageType = HIFI; } + if (location.href.indexOf("clara.io/") !== -1) { pageType = CLARA; } + + injectCommonCode(pageType === DIRECTORY); + switch (pageType) { + case DIRECTORY: + injectDirectoryCode(); + break; + case HIFI: + injectHiFiCode(); + break; + case CLARA: + injectClaraCode(); + break; + } + } + + // Load / unload. + window.addEventListener("load", onLoad); // More robust to Web site issues than using $(document).ready(). + +}()); diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index f130e3cedb..c24034b38e 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -19,6 +19,7 @@ var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); +var MARKETPLACES_INJECT_NO_SCROLLBAR_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInjectNoScrollbar.js"); var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; // var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; @@ -59,7 +60,13 @@ function showMarketplace() { UserActivityLogger.openedMarketplace(); shouldActivateButton = true; - tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); + + // by default the marketplace should NOT have a scrollbar, except when tablet is in toolbar mode. + var injectURL = MARKETPLACES_INJECT_NO_SCROLLBAR_SCRIPT_URL; + if (tablet.toolbarMode) { + injectURL = MARKETPLACES_INJECT_SCRIPT_URL; + } + tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, injectURL); onMarketplaceScreen = true; tablet.webEventReceived.connect(function (message) {