From 8010d86210bef1c2d95c235bba9b112d0e3a2e1a Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxanne@highfidelity.io>
Date: Mon, 21 Jan 2019 13:39:16 -0800
Subject: [PATCH 01/18] QML Marketplace support Support QML UI for the
 Marketplace as some devices do not handle web on 3d surfaces. Checkpoint code

---
 .../hifi/commerce/marketplace/Marketplace.qml | 336 ++++++++++++++++++
 interface/src/Application.cpp                 |   9 +
 interface/src/commerce/QmlMarketplace.cpp     | 131 +++++++
 interface/src/commerce/QmlMarketplace.h       |  68 ++++
 scripts/system/marketplaces/marketplace.js    | 117 +-----
 scripts/system/marketplaces/marketplaces.js   |   8 +-
 6 files changed, 565 insertions(+), 104 deletions(-)
 create mode 100644 interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
 create mode 100644 interface/src/commerce/QmlMarketplace.cpp
 create mode 100644 interface/src/commerce/QmlMarketplace.h

diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
new file mode 100644
index 0000000000..fb8b410e3f
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
@@ -0,0 +1,336 @@
+//
+//  Marketplace.qml
+//  qml/hifi/commerce/marketplace
+//
+//  Marketplace
+//
+//  Created by Roxanne Skelly on 2019-01-18
+//  Copyright 2019 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import Hifi 1.0 as Hifi
+import QtQuick 2.9
+import QtQuick.Controls 2.2
+import QtGraphicalEffects 1.0
+import stylesUit 1.0
+import controlsUit 1.0 as HifiControlsUit
+import "../../../controls" as HifiControls
+import "../common" as HifiCommerceCommon
+import "qrc:////qml//hifi//models" as HifiModels  // Absolute path so the same code works everywhere.
+import "../common/sendAsset"
+import "../.." as HifiCommon
+
+Rectangle {
+    HifiConstants { id: hifi; }
+
+    id: root;
+
+    property string activeView: "initialize";
+    property bool keyboardRaised: false;
+    property int category_index: -1;
+    property alias categoryChoices: categoriesModel;
+ 
+    anchors.fill: (typeof parent === undefined) ? undefined : parent;
+
+    Component.onDestruction: {
+        KeyboardScriptingInterface.raised = false;
+    }
+    
+    Connections {
+        target: Marketplace;
+
+        onGetMarketplaceCategoriesResult: {
+            if (result.status !== 'success') {
+                console.log("Failed to get Marketplace Categories", result.data.message);
+            } else {
+
+            }       
+        }
+    }
+
+    HifiCommerceCommon.CommerceLightbox {
+        id: lightboxPopup;
+        visible: false;
+        anchors.fill: parent;
+    }
+
+    //
+    // HEADER BAR START
+    //
+    Item {
+        id: header;
+        visible: true;
+        width: parent.width;
+        anchors.left: parent.left;
+        anchors.top: parent.top;
+        anchors.right: parent.right;
+        
+        Item {
+            id: titleBarContainer;
+            visible: true;
+            // Size
+            width: parent.width;
+            height: 50;
+            // Anchors
+            anchors.left: parent.left;
+            anchors.top: parent.top;
+
+            // Wallet icon
+            Image {
+                id: walletIcon;
+                source: "../../../../images/hifi-logo-blackish.svg";
+                height: 20
+                width: walletIcon.height;
+                anchors.left: parent.left;
+                anchors.leftMargin: 8;
+                anchors.verticalCenter: parent.verticalCenter;
+                visible: true;
+            }
+
+            // Title Bar text
+            RalewaySemiBold {
+                id: titleBarText;
+                text: "Marketplace";
+                // Text size
+                size: hifi.fontSizes.overlayTitle;
+                // Anchors
+                anchors.top: parent.top;
+                anchors.left: walletIcon.right;
+                anchors.leftMargin: 6;
+                anchors.bottom: parent.bottom;
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.black;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+            }
+        }
+
+        Item {
+            id: searchBarContainer;
+            visible: true;
+            // Size
+            width: parent.width;
+            anchors.top: titleBarContainer.bottom;
+            height: 50;
+
+
+           Rectangle {
+                id: categoriesButton;
+                anchors.left: parent.left;
+                anchors.leftMargin: 10;
+                anchors.verticalCenter: parent.verticalCenter;
+                height: 34;
+                width: categoriesText.width + 25;
+                color: "white";
+                radius: 4;
+                border.width: 1;
+                border.color: hifi.colors.lightGray;
+                
+
+                // Categories Text
+                RalewayRegular {
+                    id: categoriesText;
+                    text: "Categories";
+                    // Text size
+                    size: 18;
+                    // Style
+                    color: hifi.colors.baseGray;
+                    elide: Text.ElideRight;
+                    horizontalAlignment: Text.AlignHCenter;
+                    verticalAlignment: Text.AlignVCenter;
+                    width: Math.min(textMetrics.width + 25, 110);
+                    // Anchors
+                    anchors.centerIn: parent;
+                    rightPadding: 10;
+                }
+                HiFiGlyphs {
+                    id: categoriesDropdownIcon;
+                    text: hifi.glyphs.caratDn;
+                    // Size
+                    size: 34;
+                    // Anchors
+                    anchors.right: parent.right;
+                    anchors.rightMargin: -8;
+                    anchors.verticalCenter: parent.verticalCenter;
+                    horizontalAlignment: Text.AlignHCenter;
+                    // Style
+                    color: hifi.colors.baseGray;
+                }
+
+                MouseArea {
+                    anchors.fill: parent;
+                    hoverEnabled: enabled;
+                    onClicked: {
+                        categoriesDropdown.visible = !categoriesDropdown.visible;
+                    }
+                    onEntered: categoriesText.color = hifi.colors.baseGrayShadow;
+                    onExited: categoriesText.color = hifi.colors.baseGray;
+                }
+
+                Component.onCompleted: {
+                    console.log("Getting Marketplace Categories");
+                    console.log(JSON.stringify(Marketplace));
+                    Marketplace.getMarketplaceItems();
+                }                
+            }
+            
+            Rectangle {
+                id: categoriesContainer;
+                visible: true;
+                height: 50 * categoriesModel.count;
+                width: parent.width;
+                anchors.top: categoriesButton.bottom;
+                anchors.left: categoriesButton.left;
+                color: hifi.colors.white;
+
+                ListModel {
+                    id: categoriesModel;
+                }
+
+                ListView {
+                    id: dropdownListView;
+                    interactive: false;
+                    anchors.fill: parent;
+                    model: categoriesModel;
+                    delegate: Item {
+                        width: parent.width;
+                        height: 50;
+                        Rectangle {
+                            id: dropDownButton;
+                            color: hifi.colors.white;
+                            width: parent.width;
+                            height: 50;
+                            visible: true;
+
+                            RalewaySemiBold {
+                                id: dropDownButtonText;
+                                text: model.displayName;
+                                anchors.fill: parent;
+                                anchors.topMargin: 2;
+                                anchors.leftMargin: 12;
+                                color: hifi.colors.baseGray;
+                                horizontalAlignment: Text.AlignLeft;
+                                verticalAlignment: Text.AlignVCenter;
+                                size: 18;
+                            }
+
+                            MouseArea {
+                                anchors.fill: parent;
+                                hoverEnabled: true;
+                                propagateComposedEvents: false;
+                                onEntered: {
+                                    dropDownButton.color = hifi.colors.blueHighlight;
+                                }
+                                onExited: {
+                                    dropDownButton.color = hifi.colors.white;
+                                }
+                                onClicked: {
+                                    root.category_index = index;
+                                    dropdownContainer.visible = false;
+                                }
+                            }
+                        }
+                        Rectangle {
+                            height: 2;
+                            width: parent.width;
+                            color: hifi.colors.lightGray;
+                            visible: model.separator
+                        }
+                    }
+                }
+            }            
+            
+            // or
+            RalewayRegular {
+                id: orText;
+                text: "or";
+                // Text size
+                size: 18;
+                // Style
+                color: hifi.colors.baseGray;
+                elide: Text.ElideRight;
+                horizontalAlignment: Text.AlignHCenter;
+                verticalAlignment: Text.AlignVCenter;
+                width: Math.min(textMetrics.width + 25, 110);
+                // Anchors
+                anchors.left: categoriesButton.right;
+                rightPadding: 10;
+                leftPadding: 10;
+                anchors.verticalCenter: parent.verticalCenter;            
+            }
+            HifiControlsUit.TextField {
+                id: searchField;
+                anchors.verticalCenter: parent.verticalCenter;
+                anchors.right: parent.right;
+                anchors.left: orText.right;
+                anchors.rightMargin: 10;
+                height: 34;
+                isSearchField: true;
+                colorScheme: hifi.colorSchemes.faintGray;
+                
+
+                font.family: "Fira Sans"
+                font.pixelSize: hifi.fontSizes.textFieldInput;
+
+                placeholderText: "Search Marketplace";
+                            
+                TextMetrics {
+                    id: primaryFilterTextMetrics;
+                    font.family: "FiraSans Regular";
+                    font.pixelSize: hifi.fontSizes.textFieldInput;
+                    font.capitalization: Font.AllUppercase;
+                    text: root.primaryFilter_displayName;
+                }
+
+                // workaround for https://bugreports.qt.io/browse/QTBUG-49297
+                Keys.onPressed: {
+                    switch (event.key) {
+                        case Qt.Key_Return: 
+                        case Qt.Key_Enter: 
+                            event.accepted = true;
+
+                            // emit accepted signal manually
+                            if (acceptableInput) {
+                                root.accepted();
+                                root.forceActiveFocus();
+                            }
+                        break;
+                        case Qt.Key_Backspace:
+                            if (textField.text === "") {
+                                primaryFilter_index = -1;
+                            }
+                        break;
+                    }
+                }
+
+                onAccepted: {
+                    root.forceActiveFocus();
+                }
+
+                onActiveFocusChanged: {
+                    if (!activeFocus) {
+                        dropdownContainer.visible = false;
+                    }
+                }
+ 
+            }
+        }
+    }
+    //
+    // HEADER BAR END
+    //
+    DropShadow {
+        anchors.fill: header;
+        horizontalOffset: 0;
+        verticalOffset: 4;
+        radius: 4.0;
+        samples: 9
+        color: Qt.rgba(0, 0, 0, 0.25);
+        source: header;
+        visible: header.visible;
+    }
+}
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 7ed05611ee..1ec9c93e12 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -232,6 +232,7 @@
 #include "commerce/Ledger.h"
 #include "commerce/Wallet.h"
 #include "commerce/QmlCommerce.h"
+#include "commerce/QmlMarketplace.h"
 #include "ResourceRequestObserver.h"
 
 #include "webbrowser/WebBrowserSuggestionsEngine.h"
@@ -2913,6 +2914,14 @@ void Application::initializeUi() {
         QUrl{ "hifi/dialogs/security/SecurityImageModel.qml" },
         QUrl{ "hifi/dialogs/security/SecurityImageSelection.qml" },
     }, commerceCallback);
+
+    QmlContextCallback marketplaceCallback = [](QQmlContext* context) {
+        context->setContextProperty("Marketplace", new QmlMarketplace());
+    };
+    OffscreenQmlSurface::addWhitelistContextHandler({
+        QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
+        }, marketplaceCallback);
+
     QmlContextCallback ttsCallback = [](QQmlContext* context) {
         context->setContextProperty("TextToSpeech", DependencyManager::get<TTSScriptingInterface>().data());
     };
diff --git a/interface/src/commerce/QmlMarketplace.cpp b/interface/src/commerce/QmlMarketplace.cpp
new file mode 100644
index 0000000000..99d3bb1ae6
--- /dev/null
+++ b/interface/src/commerce/QmlMarketplace.cpp
@@ -0,0 +1,131 @@
+//
+//  QmlMarketplace.cpp
+//  interface/src/commerce
+//
+//  Guard for safe use of Marketplace by authorized QML.
+//
+//  Created by Roxanne Skelly on 1/18/19.
+//  Copyright 2019 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+
+#include "QmlMarketplace.h"
+#include "CommerceLogging.h"
+#include "Application.h"
+#include "DependencyManager.h"
+#include <Application.h>
+#include <UserActivityLogger.h>
+#include <ScriptEngines.h>
+#include <ui/TabletScriptingInterface.h>
+#include "scripting/HMDScriptingInterface.h"
+
+#define ApiHandler(NAME) void QmlMarketplace::NAME##Success(QNetworkReply* reply) { emit NAME##Result(apiResponse(#NAME, reply)); }
+#define FailHandler(NAME) void QmlMarketplace::NAME##Failure(QNetworkReply* reply) { emit NAME##Result(failResponse(#NAME, reply)); }
+#define Handler(NAME) ApiHandler(NAME) FailHandler(NAME)
+Handler(getMarketplaceItems)
+Handler(getMarketplaceItem)
+Handler(marketplaceItemLike)
+Handler(getMarketplaceCategories)
+
+QmlMarketplace::QmlMarketplace() {
+}
+
+void QmlMarketplace::openMarketplace(const QString& marketplaceItemId) {
+    auto tablet = dynamic_cast<TabletProxy*>(
+        DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system"));
+    tablet->loadQMLSource("hifi/commerce/marketplace/Marketplace.qml");
+    DependencyManager::get<HMDScriptingInterface>()->openTablet();
+    if (!marketplaceItemId.isEmpty()) {
+        tablet->sendToQml(QVariantMap({ { "method", "marketplace_openItem" }, { "itemId", marketplaceItemId } }));
+    }
+}
+
+void QmlMarketplace::getMarketplaceItems(
+    const QString& q,
+    const QString& view,
+    const QString& category,
+    const QString& adminFilter,
+    const QString& adminFilterCost,
+    const QString& sort,
+    const bool isFree,
+    const int& page,
+    const int& perPage) {
+
+    QString endpoint = "items";
+    QJsonObject request;
+    request["q"] = q;
+    request["view"] = view;
+    request["category"] = category;
+    request["adminFilter"] = adminFilter;
+    request["adminFilterCost"] = adminFilterCost;
+    request["sort"] = sort;
+    request["isFree"] = isFree;
+    request["page"] = page;
+    request["perPage"] = perPage;
+    send(endpoint, "getMarketplaceItemsSuccess", "getMarketplaceItemsFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::Optional, request);
+}
+
+void QmlMarketplace::getMarketplaceItem(const QString& marketplaceItemId) {
+    QString endpoint = QString("items/") + marketplaceItemId;
+    QJsonObject request;
+    send(endpoint, "getMarketplaceItemSuccess", "getMarketplaceItemFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::Optional, request);
+}
+
+void QmlMarketplace::marketplaceItemLike(const QString& marketplaceItemId, const bool like) {
+    QString endpoint = QString("items/") + marketplaceItemId + "/like";
+    QJsonObject request;
+    send(endpoint, "marketplaceItemLikeSuccess", "marketplaceItemLikeFailure", like ? QNetworkAccessManager::PutOperation : QNetworkAccessManager::DeleteOperation, AccountManagerAuth::Required, request);
+}
+
+void QmlMarketplace::getMarketplaceCategories() {
+    QString endpoint = "categories";
+    QJsonObject request;
+    send(endpoint, "getMarketplaceCategoriesSuccess", "getMarketplaceCategoriesFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::None, request);
+}
+
+
+void QmlMarketplace::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request) {
+    auto accountManager = DependencyManager::get<AccountManager>();
+    const QString URL = "/api/v1/marketplace/";
+    JSONCallbackParameters callbackParams(this, success, fail);
+#if defined(DEV_BUILD)  // Don't expose user's personal data in the wild. But during development this can be handy.
+    qCInfo(commerce) << "Sending" << QJsonDocument(request).toJson(QJsonDocument::Compact);
+#endif
+    accountManager->sendRequest(URL + endpoint,
+        authType,
+        method,
+        callbackParams,
+        QJsonDocument(request).toJson());
+}
+
+QJsonObject QmlMarketplace::apiResponse(const QString& label, QNetworkReply* reply) {
+    QByteArray response = reply->readAll();
+    QJsonObject data = QJsonDocument::fromJson(response).object();
+#if defined(DEV_BUILD)  // Don't expose user's personal data in the wild. But during development this can be handy.
+    qInfo(commerce) << label << "response" << QJsonDocument(data).toJson(QJsonDocument::Compact);
+#endif
+    return data;
+}
+
+// Non-200 responses are not json:
+QJsonObject QmlMarketplace::failResponse(const QString& label, QNetworkReply* reply) {
+    QString response = reply->readAll();
+    qWarning(commerce) << "FAILED" << label << response;
+
+    // tempResult will be NULL if the response isn't valid JSON.
+    QJsonDocument tempResult = QJsonDocument::fromJson(response.toLocal8Bit());
+    if (tempResult.isNull()) {
+        QJsonObject result
+        {
+            { "status", "fail" },
+            { "message", response }
+        };
+        return result;
+    }
+    else {
+        return tempResult.object();
+    }
+}
\ No newline at end of file
diff --git a/interface/src/commerce/QmlMarketplace.h b/interface/src/commerce/QmlMarketplace.h
new file mode 100644
index 0000000000..95a1aa3911
--- /dev/null
+++ b/interface/src/commerce/QmlMarketplace.h
@@ -0,0 +1,68 @@
+//
+//  QmlMarketplace.h
+//  interface/src/commerce
+//
+//  Guard for safe use of Marketplace by authorized QML.
+//
+//  Created by Roxanne Skelly on 1/18/19.
+//  Copyright 2019 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#pragma once
+#ifndef hifi_QmlMarketplace_h
+#define hifi_QmlMarketplace_h
+
+#include <QJsonObject>
+
+#include <QPixmap>
+#include <QtNetwork/QNetworkReply>
+#include "AccountManager.h"
+
+class QmlMarketplace : public QObject {
+    Q_OBJECT
+
+public:
+    QmlMarketplace();
+
+public slots:
+    void getMarketplaceItemsSuccess(QNetworkReply* reply);
+    void getMarketplaceItemsFailure(QNetworkReply* reply);
+    void getMarketplaceItemSuccess(QNetworkReply* reply);
+    void getMarketplaceItemFailure(QNetworkReply* reply);
+    void getMarketplaceCategoriesSuccess(QNetworkReply* reply);
+    void getMarketplaceCategoriesFailure(QNetworkReply* reply);
+    void marketplaceItemLikeSuccess(QNetworkReply* reply);
+    void marketplaceItemLikeFailure(QNetworkReply* reply);
+
+protected:
+    Q_INVOKABLE void openMarketplace(const QString& marketplaceItemId = QString());
+    Q_INVOKABLE void getMarketplaceItems(
+        const QString& q = QString(),
+        const QString& view = QString(),
+        const QString& category = QString(),
+        const QString& adminFilter = QString("published"),
+        const QString& adminFilterCost = QString(),
+        const QString& sort = QString(),
+        const bool isFree = false,
+        const int& page = 1,
+        const int& perPage = 20);
+    Q_INVOKABLE void getMarketplaceItem(const QString& marketplaceItemId);
+    Q_INVOKABLE void marketplaceItemLike(const QString& marketplaceItemId, const bool like = true);
+    Q_INVOKABLE void getMarketplaceCategories();
+
+signals:
+    void getMarketplaceItemsResult(QJsonObject result);
+    void getMarketplaceItemResult(QJsonObject result);
+    void getMarketplaceCategoriesResult(QJsonObject result);
+    void marketplaceItemLikeResult(QJsonObject result);
+
+private:
+    void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request);
+    QJsonObject apiResponse(const QString& label, QNetworkReply* reply);
+    QJsonObject failResponse(const QString& label, QNetworkReply* reply);
+};
+
+#endif // hifi_QmlMarketplace_h
diff --git a/scripts/system/marketplaces/marketplace.js b/scripts/system/marketplaces/marketplace.js
index d3e5c96739..70680acd1d 100644
--- a/scripts/system/marketplaces/marketplace.js
+++ b/scripts/system/marketplaces/marketplace.js
@@ -1,8 +1,8 @@
 //
 //  marketplace.js
 //
-//  Created by Eric Levin on 8 Jan 2016
-//  Copyright 2016 High Fidelity, Inc.
+//  Created by Roxanne Skelly on 1/18/2019
+//  Copyright 2019 High Fidelity, Inc.
 //
 //  Distributed under the Apache License, Version 2.0.
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@@ -10,108 +10,27 @@
 
 (function() { // BEGIN LOCAL_SCOPE
 
-/* global WebTablet */
-Script.include("../libraries/WebTablet.js");
+var AppUi = Script.require('appUi');
 
-var toolIconUrl = Script.resolvePath("../assets/images/tools/");
+var BUTTON_NAME = "MARKET";
+var MARKETPLACE_QML_SOURCE = "hifi/commerce/marketplace/Marketplace.qml";
+var ui;
+function startup() {
 
-var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace";
-var marketplaceWindow = new OverlayWebWindow({
-    title: "Marketplace",
-    source: "about:blank",
-    width: 900,
-    height: 700,
-    visible: false
-});
-
-var toolHeight = 50;
-var toolWidth = 50;
-var TOOLBAR_MARGIN_Y = 0;
-var marketplaceVisible = false;
-var marketplaceWebTablet;
-
-// We persist avatarEntity data in the .ini file, and reconsistitute it on restart.
-// To keep things consistent, we pickle the tablet data in Settings, and kill any existing such on restart and domain change.
-var persistenceKey = "io.highfidelity.lastDomainTablet";
-
-function shouldShowWebTablet() {
-    var rightPose = Controller.getPoseValue(Controller.Standard.RightHand);
-    var leftPose = Controller.getPoseValue(Controller.Standard.LeftHand);
-    var hasHydra = !!Controller.Hardware.Hydra;
-    return HMD.active && (leftPose.valid || rightPose.valid || hasHydra);
+    ui = new AppUi({
+        buttonName: BUTTON_NAME,
+        sortOrder: 10,
+        home: MARKETPLACE_QML_SOURCE
+    });
 }
 
-function showMarketplace(marketplaceID) {
-    var url = MARKETPLACE_URL;
-    if (marketplaceID) {
-        url = url + "/items/" + marketplaceID;
-    }
-    tablet.gotoWebScreen(url);
-    marketplaceVisible = true;
-    UserActivityLogger.openedMarketplace();
+function shutdown() {
 }
 
-function hideTablet(tablet) {
-    if (!tablet) {
-        return;
-    }
-    updateButtonState(false);
-    tablet.unregister();
-    tablet.destroy();
-    marketplaceWebTablet = null;
-    Settings.setValue(persistenceKey, "");
-}
-function clearOldTablet() { // If there was a tablet from previous domain or session, kill it and let it be recreated
-
-}
-function hideMarketplace() {
-    if (marketplaceWindow.visible) {
-        marketplaceWindow.setVisible(false);
-        marketplaceWindow.setURL("about:blank");
-    } else if (marketplaceWebTablet) {
-
-    }
-    marketplaceVisible = false;
-}
-
-function toggleMarketplace() {
-    if (marketplaceVisible) {
-        hideMarketplace();
-    } else {
-        showMarketplace();
-    }
-}
-
-var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
-
-var browseExamplesButton = tablet.addButton({
-    icon: "icons/tablet-icons/market-i.svg",
-    text: "MARKET"
-});
-
-function updateButtonState(visible) {
-
-}
-function onMarketplaceWindowVisibilityChanged() {
-    updateButtonState(marketplaceWindow.visible);
-    marketplaceVisible = marketplaceWindow.visible;
-}
-
-function onClick() {
-    toggleMarketplace();
-}
-
-browseExamplesButton.clicked.connect(onClick);
-marketplaceWindow.visibleChanged.connect(onMarketplaceWindowVisibilityChanged);
-
-clearOldTablet(); // Run once at startup, in case there's anything laying around from a crash.
-// We could also optionally do something like Window.domainChanged.connect(function () {Script.setTimeout(clearOldTablet, 2000)}),
-// but the HUD version stays around, so lets do the same.
-
-Script.scriptEnding.connect(function () {
-    browseExamplesButton.clicked.disconnect(onClick);
-    tablet.removeButton(browseExamplesButton);
-    marketplaceWindow.visibleChanged.disconnect(onMarketplaceWindowVisibilityChanged);
-});
+//
+// Run the functions.
+//
+startup();
+Script.scriptEnding.connect(shutdown);
 
 }()); // END LOCAL_SCOPE
diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js
index e4891a9bae..db3b2e2107 100644
--- a/scripts/system/marketplaces/marketplaces.js
+++ b/scripts/system/marketplaces/marketplaces.js
@@ -769,16 +769,14 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) {
 
 
 var BUTTON_NAME = "MARKET";
-var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace";
-// Append "?" if necessary to signal injected script that it's the initial page.
-var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + (MARKETPLACE_URL.indexOf("?") > -1 ? "" : "?");
+var MARKETPLACE_QML_SOURCE = "hifi/commerce/marketplace/Marketplace.qml";
+
 var ui;
 function startup() {
     ui = new AppUi({
         buttonName: BUTTON_NAME,
         sortOrder: 9,
-        inject: MARKETPLACES_INJECT_SCRIPT_URL,
-        home: MARKETPLACE_URL_INITIAL,
+        home: MARKETPLACE_QML_SOURCE,
         onScreenChanged: onTabletScreenChanged,
         onMessage: onQmlMessageReceived
     });

From c1ff51a02d5816d1094e98dfdb4692e474b53556 Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxanne@highfidelity.io>
Date: Thu, 24 Jan 2019 17:20:42 -0800
Subject: [PATCH 02/18] QML Marketplace Checkpoint full functionality

---
 .../hifi/commerce/marketplace/Marketplace.qml | 775 +++++++++++++++---
 .../commerce/marketplace/MarketplaceItem.qml  | 473 +++++++++++
 .../marketplace/MarketplaceListItem.qml       | 278 +++++++
 .../hifi/commerce/marketplace/SortButton.qml  |  87 ++
 interface/src/Application.cpp                 |   2 +-
 interface/src/commerce/QmlMarketplace.cpp     |  42 +-
 interface/src/commerce/QmlMarketplace.h       |   2 +-
 libraries/networking/src/AccountManager.cpp   |   8 +-
 libraries/networking/src/AccountManager.h     |   2 +-
 scripts/system/commerce/wallet.js             |  23 +-
 scripts/system/html/js/marketplacesInject.js  |  13 +-
 scripts/system/marketplaces/marketplaces.js   |  47 +-
 12 files changed, 1592 insertions(+), 160 deletions(-)
 create mode 100644 interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
 create mode 100644 interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
 create mode 100644 interface/resources/qml/hifi/commerce/marketplace/SortButton.qml

diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
index fb8b410e3f..e5ce431de8 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
@@ -30,24 +30,72 @@ Rectangle {
 
     property string activeView: "initialize";
     property bool keyboardRaised: false;
-    property int category_index: -1;
-    property alias categoryChoices: categoriesModel;
+    property int currentSortIndex: 0;
+    property string sortString: "";
+    property string categoryString: "";
+    property string searchString: "";
  
     anchors.fill: (typeof parent === undefined) ? undefined : parent;
 
+    function getMarketplaceItems() {
+        marketplaceItemView.visible = false;
+        itemsList.visible = true;          
+        marketBrowseModel.getFirstPage();
+    }
+    
     Component.onDestruction: {
         KeyboardScriptingInterface.raised = false;
     }
     
     Connections {
-        target: Marketplace;
+        target: MarketplaceScriptingInterface;
 
         onGetMarketplaceCategoriesResult: {
             if (result.status !== 'success') {
                 console.log("Failed to get Marketplace Categories", result.data.message);
             } else {
-
-            }       
+                categoriesModel.clear();
+                categoriesModel.append({
+                    id: -1,
+                    name: "Everything"
+                });                
+                result.data.items.forEach(function(category) {
+                    categoriesModel.append({
+                        id: category.id,
+                        name: category.name
+                    });
+                });
+            }
+            getMarketplaceItems();            
+        }
+        onGetMarketplaceItemsResult: {
+            marketBrowseModel.handlePage(result.status !== "success" && result.message, result);
+        }
+        
+        onGetMarketplaceItemResult: {
+            if (result.status !== 'success') {
+                console.log("Failed to get Marketplace Item", result.data.message);
+            } else {
+            
+                console.log(JSON.stringify(result.data));
+                marketplaceItem.item_id = result.data.id;
+                marketplaceItem.image_url = result.data.thumbnail_url;
+                marketplaceItem.name = result.data.title;
+                marketplaceItem.likes = result.data.likes;
+                marketplaceItem.liked = result.data.has_liked;
+                marketplaceItem.creator = result.data.creator;
+                marketplaceItem.categories = result.data.categories;
+                marketplaceItem.price = result.data.cost;
+                marketplaceItem.description = result.data.description;
+                marketplaceItem.attributions = result.data.attributions;
+                marketplaceItem.license = result.data.license;
+                marketplaceItem.available = result.data.availability == "available";
+                marketplaceItem.created_at = result.data.created_at;
+                console.log("HEIGHT: " + marketplaceItemContent.height);
+                marketplaceItemScrollView.contentHeight = marketplaceItemContent.height;
+                itemsList.visible = false;
+                marketplaceItemView.visible = true;
+            }
         }
     }
 
@@ -60,15 +108,36 @@ Rectangle {
     //
     // HEADER BAR START
     //
-    Item {
+    
+    
+    Rectangle {
+        id: headerShadowBase;
+        anchors.fill: header;
+        color: "white";
+    }
+    DropShadow {
+        anchors.fill: headerShadowBase;
+        source: headerShadowBase;
+        verticalOffset: 4;
+        horizontalOffset: 4;
+        radius: 6;
+        samples: 9;
+        color: hifi.colors.baseGrayShadow;
+        z:5;
+    }    
+    Rectangle {
         id: header;
         visible: true;
-        width: parent.width;
         anchors.left: parent.left;
         anchors.top: parent.top;
         anchors.right: parent.right;
-        
-        Item {
+        anchors.topMargin: -1;
+        anchors.leftMargin: -1;
+        anchors.rightMargin: -1;
+        height: childrenRect.height+5;
+        z: 5;
+    
+        Rectangle {
             id: titleBarContainer;
             visible: true;
             // Size
@@ -77,13 +146,14 @@ Rectangle {
             // Anchors
             anchors.left: parent.left;
             anchors.top: parent.top;
-
-            // Wallet icon
+              
+            
+            // Marketplace icon
             Image {
-                id: walletIcon;
+                id: marketplaceIcon;
                 source: "../../../../images/hifi-logo-blackish.svg";
                 height: 20
-                width: walletIcon.height;
+                width: marketplaceIcon.height;
                 anchors.left: parent.left;
                 anchors.leftMargin: 8;
                 anchors.verticalCenter: parent.verticalCenter;
@@ -98,7 +168,7 @@ Rectangle {
                 size: hifi.fontSizes.overlayTitle;
                 // Anchors
                 anchors.top: parent.top;
-                anchors.left: walletIcon.right;
+                anchors.left: marketplaceIcon.right;
                 anchors.leftMargin: 6;
                 anchors.bottom: parent.bottom;
                 width: paintedWidth;
@@ -109,9 +179,10 @@ Rectangle {
             }
         }
 
-        Item {
+        Rectangle {
             id: searchBarContainer;
             visible: true;
+            clip: false;
             // Size
             width: parent.width;
             anchors.top: titleBarContainer.bottom;
@@ -125,10 +196,10 @@ Rectangle {
                 anchors.verticalCenter: parent.verticalCenter;
                 height: 34;
                 width: categoriesText.width + 25;
-                color: "white";
+                color: hifi.colors.white;
                 radius: 4;
                 border.width: 1;
-                border.color: hifi.colors.lightGray;
+                border.color: hifi.colors.lightGrayText;
                 
 
                 // Categories Text
@@ -136,9 +207,9 @@ Rectangle {
                     id: categoriesText;
                     text: "Categories";
                     // Text size
-                    size: 18;
+                    size: 14;
                     // Style
-                    color: hifi.colors.baseGray;
+                    color: hifi.colors.lightGrayText;
                     elide: Text.ElideRight;
                     horizontalAlignment: Text.AlignHCenter;
                     verticalAlignment: Text.AlignVCenter;
@@ -166,6 +237,8 @@ Rectangle {
                     hoverEnabled: enabled;
                     onClicked: {
                         categoriesDropdown.visible = !categoriesDropdown.visible;
+                        categoriesButton.color = categoriesDropdown.visible ? hifi.colors.lightGray : hifi.colors.white;
+                        categoriesDropdown.forceActiveFocus();
                     }
                     onEntered: categoriesText.color = hifi.colors.baseGrayShadow;
                     onExited: categoriesText.color = hifi.colors.baseGray;
@@ -173,77 +246,10 @@ Rectangle {
 
                 Component.onCompleted: {
                     console.log("Getting Marketplace Categories");
-                    console.log(JSON.stringify(Marketplace));
-                    Marketplace.getMarketplaceItems();
+                    MarketplaceScriptingInterface.getMarketplaceCategories();
                 }                
             }
-            
-            Rectangle {
-                id: categoriesContainer;
-                visible: true;
-                height: 50 * categoriesModel.count;
-                width: parent.width;
-                anchors.top: categoriesButton.bottom;
-                anchors.left: categoriesButton.left;
-                color: hifi.colors.white;
-
-                ListModel {
-                    id: categoriesModel;
-                }
-
-                ListView {
-                    id: dropdownListView;
-                    interactive: false;
-                    anchors.fill: parent;
-                    model: categoriesModel;
-                    delegate: Item {
-                        width: parent.width;
-                        height: 50;
-                        Rectangle {
-                            id: dropDownButton;
-                            color: hifi.colors.white;
-                            width: parent.width;
-                            height: 50;
-                            visible: true;
-
-                            RalewaySemiBold {
-                                id: dropDownButtonText;
-                                text: model.displayName;
-                                anchors.fill: parent;
-                                anchors.topMargin: 2;
-                                anchors.leftMargin: 12;
-                                color: hifi.colors.baseGray;
-                                horizontalAlignment: Text.AlignLeft;
-                                verticalAlignment: Text.AlignVCenter;
-                                size: 18;
-                            }
-
-                            MouseArea {
-                                anchors.fill: parent;
-                                hoverEnabled: true;
-                                propagateComposedEvents: false;
-                                onEntered: {
-                                    dropDownButton.color = hifi.colors.blueHighlight;
-                                }
-                                onExited: {
-                                    dropDownButton.color = hifi.colors.white;
-                                }
-                                onClicked: {
-                                    root.category_index = index;
-                                    dropdownContainer.visible = false;
-                                }
-                            }
-                        }
-                        Rectangle {
-                            height: 2;
-                            width: parent.width;
-                            color: hifi.colors.lightGray;
-                            visible: model.separator
-                        }
-                    }
-                }
-            }            
-            
+                
             // or
             RalewayRegular {
                 id: orText;
@@ -283,7 +289,6 @@ Rectangle {
                     font.family: "FiraSans Regular";
                     font.pixelSize: hifi.fontSizes.textFieldInput;
                     font.capitalization: Font.AllUppercase;
-                    text: root.primaryFilter_displayName;
                 }
 
                 // workaround for https://bugreports.qt.io/browse/QTBUG-49297
@@ -295,20 +300,22 @@ Rectangle {
 
                             // emit accepted signal manually
                             if (acceptableInput) {
-                                root.accepted();
-                                root.forceActiveFocus();
+                                searchField.accepted();
+                                searchField.forceActiveFocus();
                             }
                         break;
                         case Qt.Key_Backspace:
-                            if (textField.text === "") {
+                            if (searchField.text === "") {
                                 primaryFilter_index = -1;
                             }
                         break;
                     }
                 }
-
+                onTextChanged: root.searchString = text;
                 onAccepted: {
-                    root.forceActiveFocus();
+                    root.searchString = searchField.text;
+                    getMarketplaceItems();
+                    searchField.forceActiveFocus();
                 }
 
                 onActiveFocusChanged: {
@@ -320,17 +327,579 @@ Rectangle {
             }
         }
     }
+    
     //
     // HEADER BAR END
     //
-    DropShadow {
-        anchors.fill: header;
-        horizontalOffset: 0;
-        verticalOffset: 4;
-        radius: 4.0;
-        samples: 9
-        color: Qt.rgba(0, 0, 0, 0.25);
-        source: header;
-        visible: header.visible;
+    
+    //
+    // CATEGORIES LIST START
+    //    
+    Item {
+        id: categoriesDropdown;    
+        anchors.fill: parent;
+        visible: false;
+        z: 10;
+        
+        MouseArea {
+            anchors.fill: parent;
+            propagateComposedEvents: true;
+            onClicked: {
+                categoriesDropdown.visible = false;
+                categoriesButton.color = hifi.colors.white;
+            }            
+        }
+        
+        Rectangle {
+            anchors.left: parent.left;
+            anchors.bottom: parent.bottom;
+            anchors.top: parent.top;
+            anchors.topMargin: 100;
+            width: parent.width/3;
+            color: hifi.colors.white;
+            
+            ListModel {
+                id: categoriesModel;
+            }
+
+            ListView {
+                id: categoriesListView;
+                anchors.fill: parent;
+                anchors.rightMargin: 10;
+                width: parent.width;
+                clip: true;
+                
+                model: categoriesModel;
+                delegate: ItemDelegate {
+                    height: 34;
+                    width: parent.width;
+                    clip: true;
+                    contentItem: Rectangle {
+                        id: categoriesItem;
+                        anchors.fill: parent;
+                        color: hifi.colors.white;
+                        visible: true;
+
+                        RalewayRegular {
+                            id: categoriesItemText;
+                            text: model.name;
+                            anchors.leftMargin: 15;
+                            anchors.fill:parent;
+                            color: ListView.isCurrentItem ? hifi.colors.lightBlueHighlight : hifi.colors.baseGray;
+                            horizontalAlignment: Text.AlignLeft;
+                            verticalAlignment: Text.AlignVCenter;
+                            size: 14;
+                        }
+                    }
+                    MouseArea {
+                        anchors.fill: parent;
+                        hoverEnabled: true;
+                        propagateComposedEvents: false;
+                        z: 10;
+                        onEntered: {
+                            categoriesItem.color = ListView.isCurrentItem ? hifi.colors.white : hifi.colors.lightBlueHighlight;
+                        }
+                        onExited: {
+                            categoriesItem.color = ListView.isCurrentItem ? hifi.colors.lightBlueHighlight : hifi.colors.white;
+                        }
+                        onClicked: {
+                            categoriesListView.currentIndex = index;
+                            categoriesText.text = categoriesItemText.text;
+                            categoriesDropdown.visible = false;
+                            categoriesButton.color = hifi.colors.white;
+                            root.categoryString = categoriesItemText.text;
+                            getMarketplaceItems();
+                        }
+                    }
+                    
+                }
+                ScrollBar.vertical: ScrollBar {
+                    parent: categoriesListView.parent;
+                    anchors.top: categoriesListView.top;
+                    anchors.bottom: categoriesListView.bottom;
+                    anchors.left: categoriesListView.right;
+                    contentItem.opacity: 1;
+                }
+            }
+        }
+    }
+    //
+    // CATEGORIES LIST END
+    //
+    
+    //
+    // ITEMS LIST START
+    //
+    Item {
+        id: itemsList;
+        anchors.fill: parent;
+        anchors.topMargin: 100;
+        anchors.bottomMargin: 50;
+        visible: true;
+        
+        HifiModels.PSFListModel {
+            id: marketBrowseModel;
+            itemsPerPage: 7;
+            listModelName: 'marketBrowse';
+            listView: marketBrowseList;
+            getPage: function () {
+
+                MarketplaceScriptingInterface.getMarketplaceItems(
+                    root.searchString == "" ? undefined : root.searchString,
+                    "",
+                    root.categoryString.toLowerCase(),
+                    "",
+                    "",
+                    root.sortString,
+                    false,
+                    marketBrowseModel.currentPageToRetrieve,
+                    marketBrowseModel.itemsPerPage
+                );
+            }
+            processPage: function(data) {
+                console.log(JSON.stringify(data));
+                data.items.forEach(function (item) {
+                    console.log(JSON.stringify(item));
+                });
+                return data.items;
+            }
+        }
+        ListView {
+            id: marketBrowseList;
+            model: marketBrowseModel;
+            // Anchors
+            anchors.fill: parent;
+            anchors.rightMargin: 10;
+            orientation: ListView.Vertical;
+            focus: true;
+            clip: true;            
+            
+            delegate: MarketplaceListItem {
+                item_id: model.id;
+                image_url:model.thumbnail_url;
+                name: model.title;
+                likes: model.likes;
+                liked: model.has_liked;
+                creator: model.creator;
+                category: model.primary_category;
+                price: model.cost;
+                available: model.availability == "available";
+                anchors.topMargin: 10;
+                
+                
+                Component.onCompleted: {
+                    console.log("Rendering marketplace list item " + model.id);
+                    console.log(marketBrowseModel.count);
+                }
+                
+                onShowItem: {
+                    MarketplaceScriptingInterface.getMarketplaceItem(item_id);
+                }
+                
+                onBuy: {
+                    sendToScript({method: 'marketplace_checkout', itemId: item_id});
+                }
+                
+                onCategoryClicked: {
+                    root.categoryString = category;
+                    categoriesText.text = category;                    
+                    getMarketplaceItems();
+                }
+                
+                onRequestReload: getMarketplaceItems();
+            }
+            ScrollBar.vertical: ScrollBar {
+                parent: marketBrowseList.parent;
+                anchors.top: marketBrowseList.top;
+                anchors.bottom: marketBrowseList.bottom;
+                anchors.left: marketBrowseList.right;
+                contentItem.opacity: 1;
+            }            
+            headerPositioning: ListView.InlineHeader;
+            header: Item {
+                id: itemsHeading;
+                
+                height: childrenRect.height;
+                width: parent.width;
+                
+                Item {
+                    id: breadcrumbs;
+                    anchors.left: parent.left;
+                    anchors.right: parent.right;
+                    height: 34;
+                    visible: categoriesListView.currentIndex >= 0;
+                    RalewayRegular {
+                        id: categoriesItemText;
+                        text: "Home /";
+                        anchors.leftMargin: 15;
+                        anchors.fill:parent;
+                        color: hifi.colors.blueHighlight;
+                        horizontalAlignment: Text.AlignLeft;
+                        verticalAlignment: Text.AlignVCenter;
+                        size: 18;
+                    }
+                    MouseArea {
+                        anchors.fill: parent;
+                        onClicked: {
+                            categoriesListView.currentIndex = -1;
+                            categoriesText.text = "Categories";
+                            root.categoryString = "";
+                            getMarketplaceItems();
+                        }
+                    }
+                }
+                
+                Item {
+                    id: searchScope;
+                    anchors.top: breadcrumbs.bottom;
+                    anchors.left: parent.left;
+                    anchors.right: parent.right;
+                    height: 50;
+                    
+                    RalewaySemiBold {
+                        id: searchScopeText;
+                        text: "Featured";
+                        anchors.leftMargin: 15;
+                        anchors.fill:parent;
+                        anchors.topMargin: 10;
+                        color: hifi.colors.baseGray;
+                        horizontalAlignment: Text.AlignLeft;
+                        verticalAlignment: Text.AlignVCenter;
+                        size: 22;
+                    }
+                }
+                Item {
+                    id: sort;
+                    anchors.top: searchScope.bottom;
+                    anchors.left: parent.left;
+                    anchors.right: parent.right;
+                    anchors.topMargin: 10;
+                    anchors.leftMargin: 15;
+                    height: childrenRect.height;
+                    RalewayRegular {
+                        id: sortText;
+                        text: "Sort:";
+                        anchors.leftMargin: 15;
+                        anchors.rightMargin: 20;
+                        height: 34;
+                        color: hifi.colors.lightGrayText;
+                        horizontalAlignment: Text.AlignLeft;
+                        verticalAlignment: Text.AlignVCenter;
+                        size: 14;
+                    }
+                    
+                    Rectangle {
+                        radius: 4;
+                        border.width: 1;
+                        border.color: hifi.colors.faintGray;
+                        anchors.left: sortText.right;
+                        anchors.top: parent.top;
+                        width: 322;
+                        height: 36;
+                        anchors.leftMargin: 20;
+                        
+                        
+                        ListModel {
+                            id: sortModel;
+                            ListElement {
+                                name: "Name";
+                                glyph: ";";
+                                sortString: "alpha";
+                            }
+                            ListElement {
+                                name: "Date";
+                                glyph: ";";
+                                sortString: "recent";
+                            }
+                            ListElement {
+                                name: "Popular";
+                                glyph: ";";
+                                sortString: "likes";
+                            }
+                            ListElement {
+                                name: "My Likes";
+                                glyph: ";";
+                                sortString: "my_likes";
+                            }
+                        }
+                    
+                        ListView {
+                            id: sortListView;
+                            anchors.top: parent.top;
+                            anchors.bottom: parent.bottom;
+                            anchors.left: parent.left;
+                            anchors.topMargin:1;
+                            anchors.bottomMargin:1;
+                            anchors.leftMargin:1;
+                            anchors.rightMargin:1;
+                            width: childrenRect.width;
+                            height: 34;
+                            orientation: ListView.Horizontal;
+                            focus: true;
+                            clip: true;
+                            highlightFollowsCurrentItem: false;
+                            
+                            delegate: SortButton {
+                                width: 80;
+                                height: parent.height;
+                                glyph: model.glyph;
+                                text: model.name;
+                                
+                                checked: {
+                                    ListView.isCurrentItem;
+                                }
+                                onClicked: {
+                                    root.currentSortIndex = index;
+                                    sortListView.positionViewAtIndex(index, ListView.Beginning);
+                                    sortListView.currentIndex = index;
+                                    root.sortString = model.sortString;
+                                    getMarketplaceItems();
+                                }                            
+                            }
+                            highlight: Rectangle {
+                                width: 80;
+                                height: parent.height;
+                                color: hifi.colors.faintGray;
+                                x: sortListView.currentItem.x;
+                            }
+                            model: sortModel;
+                        } 
+                    }
+                }
+            }
+        }
+    }
+    
+    //
+    // ITEMS LIST END
+    // 
+
+    //
+    // ITEM START
+    //     
+    Item {
+        id: marketplaceItemView;
+        anchors.fill: parent;
+        width: parent.width;
+        anchors.topMargin: 120;
+        visible: false;
+        
+        ScrollView {
+            id: marketplaceItemScrollView;
+            anchors.fill: parent;
+
+            clip: true;
+            ScrollBar.vertical.policy: ScrollBar.AlwaysOn;
+            contentWidth: parent.width;
+
+            Rectangle {
+                id: marketplaceItemContent;
+                width: parent.width;
+                height: childrenRect.height + 100;
+                
+                // Title Bar text
+                RalewaySemiBold {
+                    id: backText;
+                    text: "Back";
+                    // Text size
+                    size: hifi.fontSizes.overlayTitle;
+                    // Anchors
+                    anchors.top: parent.top;
+                    anchors.left: parent.left
+                    anchors.leftMargin: 15;
+                    anchors.bottomMargin: 10;
+                    width: paintedWidth;
+                    height: paintedHeight;
+                    // Style
+                    color: hifi.colors.blueHighlight;
+                    // Alignment
+                    verticalAlignment: Text.AlignVCenter;
+                }
+                MouseArea {
+                    anchors.fill: backText;
+                    
+                    onClicked: {
+                        getMarketplaceItems();
+                    }
+                }
+                
+                MarketplaceItem {
+                    id: marketplaceItem;
+                    anchors.top: backText.bottom;
+                    width: parent.width;
+                    height: childrenRect.height;
+                    anchors.topMargin: 15;
+                    
+                    onBuy: {
+                        sendToScript({method: 'marketplace_checkout', itemId: item_id});
+                    }
+                    
+                    onShowLicense: {
+                        licenseInfoWebView.url = url;
+                        licenseInfo.visible = true;
+                    }
+                    onCategoryClicked: {
+                        root.categoryString = category;
+                        categoriesText.text = category;                      
+                        getMarketplaceItems();
+                    }
+                }
+            }
+        }
+    }
+    //
+    // ITEM END
+    //
+    
+    //
+    // FOOTER START
+    //
+    
+    Rectangle {
+        id: footer;
+        anchors.bottom: parent.bottom;
+        anchors.left: parent.left;
+        anchors.right: parent.right;
+        height: 50;
+        
+        color: hifi.colors.blueHighlight;
+
+        Item {
+            anchors.fill: parent;
+            anchors.rightMargin: 15;
+            anchors.leftMargin: 15;
+        
+            HiFiGlyphs {
+                id: footerGlyph;
+                text: hifi.glyphs.info;
+                // Size
+                size: 34;
+                // Anchors
+                anchors.left: parent.left;
+                anchors.top: parent.top;
+                anchors.bottom: parent.bottom;
+                
+                anchors.rightMargin: 10;
+                // Style
+                color: hifi.colors.white;
+                // Alignment
+                horizontalAlignment: Text.AlignHCenter;
+                verticalAlignment: Text.AlignVCenter;
+            }
+
+            RalewaySemiBold {
+                id: footerInfo;
+                text: "Get items from Clara.io!";
+                anchors.left: footerGlyph.right;
+                anchors.top: parent.top;
+                anchors.bottom: parent.bottom;
+                color: hifi.colors.white;
+                horizontalAlignment: Text.AlignLeft;
+                verticalAlignment: Text.AlignVCenter;
+                size: 18;
+            }
+            
+            HifiControlsUit.Button {
+                text: "SEE ALL MARKETS";
+                anchors.right: parent.right;
+                anchors.top: parent.top;
+                anchors.bottom: parent.bottom;
+                anchors.topMargin: 10;
+                anchors.bottomMargin: 10;
+                anchors.leftMargin: 10;
+                anchors.rightMargin: 10;
+                width: 180;
+                
+                onClicked: {
+                    sendToScript({method: 'marketplace_marketplaces'});
+                }
+            }   
+        }  
+    }
+    
+    
+    //
+    // FOOTER END
+    //
+    
+    
+    //
+    // LICENSE START
+    //    
+    Rectangle {
+        id: licenseInfo;
+        anchors.fill: root;
+        anchors.topMargin: 100;
+        anchors.bottomMargin: 0;
+        visible: false;
+        
+        
+        HifiControlsUit.WebView {
+            id: licenseInfoWebView;
+            anchors.bottomMargin: 1;
+            anchors.topMargin: 50;
+            anchors.leftMargin: 1;
+            anchors.rightMargin: 1;
+            anchors.fill: parent;
+        }
+        Item {
+            id: licenseClose;
+            anchors.top: parent.top;
+            anchors.right: parent.right;
+            anchors.topMargin: 10;
+            anchors.rightMargin: 10;
+            
+            width: 30;
+            height: 30;
+            HiFiGlyphs {
+                anchors.fill: parent;
+                height: 30;
+                text: hifi.glyphs.close;
+                // Size
+                size: 34;
+                // Anchors
+                anchors.rightMargin: 0;
+                anchors.verticalCenter: parent.verticalCenter;
+                horizontalAlignment: Text.AlignHCenter;
+                // Style
+                color: hifi.colors.baseGray;
+            }
+            
+            MouseArea {
+                anchors.fill: licenseClose;
+                onClicked: licenseInfo.visible = false;
+            }
+        }
+    }
+    //
+    // LICENSE END
+    //        
+
+    //
+    // Function Name: fromScript()
+    //
+    // Relevant Variables:
+    // None
+    //
+    // Arguments:
+    // message: The message sent from the JavaScript, in this case the Marketplaces JavaScript.
+    //     Messages are in format "{method, params}", like json-rpc.
+    //
+    // Description:
+    // Called when a message is received from a script.
+    //
+
+    function fromScript(message) {
+        console.log("FROM SCRIPT " + JSON.stringify(message));
+        switch (message.method) {
+            case 'updateMarketplaceQMLItem':
+                if (!message.params.itemId) {
+                    console.log("A message with method 'updateMarketplaceQMLItem' was sent without an itemId!");
+                    return;
+                }
+
+                MarketplaceScriptingInterface.getMarketplaceItem(message.params.itemId);
+                break;
+        }
     }
 }
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
new file mode 100644
index 0000000000..a7f2991920
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
@@ -0,0 +1,473 @@
+//
+//  MarketplaceListItem.qml
+//  qml/hifi/commerce/marketplace
+//
+//  MarketplaceListItem
+//
+//  Created by Roxanne Skelly on 2019-01-22
+//  Copyright 2019 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import Hifi 1.0 as Hifi
+import QtQuick 2.9
+import QtQuick.Controls 2.2
+import QtGraphicalEffects 1.0
+import stylesUit 1.0
+import controlsUit 1.0 as HifiControlsUit
+import "../../../controls" as HifiControls
+import "../common" as HifiCommerceCommon
+import "qrc:////qml//hifi//models" as HifiModels  // Absolute path so the same code works everywhere.
+import "../common/sendAsset"
+import "../.." as HifiCommon
+
+Rectangle {
+    id: root;
+    property string item_id: "";
+    property string image_url: "";
+    property string name: "";
+    property int likes: 0;
+    property bool liked: false;
+    property string creator: "";
+    property var categories: [];
+    property int price: 0;
+    property var attributions: [];
+    property string description: "";
+    property string license: "";
+    property string posted: "";
+    property bool available: false;
+    property string created_at: "";
+    
+    onCategoriesChanged: {
+        categoriesListModel.clear();
+        categories.forEach(function(category) {
+        console.log("category is " + category);
+            categoriesListModel.append({"category":category});
+        });
+    }
+
+    signal buy();
+    signal categoryClicked(string category);
+    signal showLicense(string url);
+    
+    HifiConstants { id: hifi; }
+    
+    Connections {
+        target: MarketplaceScriptingInterface;
+
+        onMarketplaceItemLikeResult: {
+            if (result.status !== 'success') {
+                console.log("Failed to get Marketplace Categories", result.data.message);
+            } else {
+                root.liked = !root.liked;
+                root.likes = root.liked ? root.likes + 1 : root.likes - 1;
+            }
+        }
+    }
+    function getFormattedDate(timestamp) {
+        function addLeadingZero(n) {
+            return n < 10 ? '0' + n : '' + n;
+        }
+
+        var a = new Date(timestamp);
+
+        var year = a.getFullYear();
+        var month = addLeadingZero(a.getMonth() + 1);
+        var day = addLeadingZero(a.getDate());
+        var hour = a.getHours();
+        var drawnHour = hour;
+        if (hour === 0) {
+            drawnHour = 12;
+        } else if (hour > 12) {
+            drawnHour -= 12;
+        }
+        drawnHour = addLeadingZero(drawnHour);
+        
+        var amOrPm = "AM";
+        if (hour >= 12) {
+            amOrPm = "PM";
+        }
+
+        var min = addLeadingZero(a.getMinutes());
+        var sec = addLeadingZero(a.getSeconds());
+        return a.toDateString() + " " + drawnHour + ':' + min + amOrPm;
+    }
+    
+    anchors.left: parent.left;
+    anchors.right: parent.right;
+    anchors.leftMargin: 15;
+    anchors.rightMargin: 15;
+    height: childrenRect.height;
+    
+    
+    Rectangle {
+        id: header;
+        anchors.left: parent.left;
+        anchors.right: parent.right;
+        anchors.top: parent.top;
+        
+        height: 50;
+        
+        RalewaySemiBold {
+            id: nameText;
+            text: root.name;
+            // Text size
+            size: 24;
+            // Anchors
+            anchors.top: parent.top;
+            anchors.left: parent.left;
+            anchors.bottom: parent.bottom;
+            width: paintedWidth;
+            // Style
+            color: hifi.colors.baseGray;
+            // Alignment
+            verticalAlignment: Text.AlignVCenter;
+        }
+        Item {
+            id: likes;
+            anchors.top: parent.top;
+            anchors.right: parent.right;
+            anchors.bottom: parent.bottom;
+            anchors.rightMargin: 5;
+            
+            RalewaySemiBold {
+                id: heart;
+                text: "\u2665";
+                // Size
+                size: 20;
+                // Anchors
+                anchors.top: parent.top;
+                anchors.right: parent.right;
+                anchors.rightMargin: 0;
+                anchors.verticalCenter: parent.verticalCenter;
+                horizontalAlignment: Text.AlignHCenter;
+                // Style
+                color: root.liked ? hifi.colors.redAccent  : hifi.colors.lightGrayText;
+            }
+            
+            RalewaySemiBold {
+                id: likesText;
+                text: root.likes;
+                // Text size
+                size: hifi.fontSizes.overlayTitle;
+                // Anchors
+                anchors.top: parent.top;
+                anchors.right: heart.left;
+                anchors.rightMargin: 5;
+                anchors.leftMargin: 15;
+                anchors.bottom: parent.bottom;
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.baseGray;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+            }
+            MouseArea {
+                anchors.left: likesText.left;
+                anchors.right: heart.right;
+                anchors.top: likesText.top;
+                anchors.bottom: likesText.bottom;
+                
+                onClicked: {
+                    MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, !root.liked);
+                }
+            }                
+        }
+    }
+    Image {
+        id: itemImage;
+        source: root.image_url;
+        anchors.top: header.bottom;
+        anchors.left: parent.left;
+        anchors.right: parent.right;
+        height: width*0.5625
+        fillMode: Image.PreserveAspectCrop;
+    }
+    Item {
+        id: footer;
+        anchors.left: parent.left;
+        anchors.right: parent.right;
+        anchors.top: itemImage.bottom;
+        height: childrenRect.height;
+        
+        HifiControlsUit.Button {
+            id: buyButton;
+            text: root.available ? (root.price ? root.price : "FREE") : "UNAVAILABLE (not for sale)";
+            enabled: root.available;
+            buttonGlyph: root.available ? (root.price ? hifi.glyphs.hfc : "") : "";
+            anchors.right: parent.right;
+            anchors.top: parent.top;
+            anchors.left: parent.left;
+            anchors.topMargin: 15;
+            height: 50;
+            color: hifi.buttons.blue;
+            
+            onClicked: root.buy();
+        }        
+        
+        Item {
+            id: creatorItem;
+            anchors.top: buyButton.bottom;
+            anchors.leftMargin: 15;
+            anchors.topMargin: 15;
+            width: parent.width;
+            height: childrenRect.height;
+            
+            RalewaySemiBold {
+                id: creatorLabel;
+                text: "CREATOR:";
+                // Text size
+                size: 14;
+                // Anchors
+                anchors.top: parent.top;
+                anchors.left: parent.left;
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.lightGrayText;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+            }
+            
+            RalewaySemiBold {
+                id: creatorText;
+                text: root.creator;
+                // Text size
+                size: 18;
+                // Anchors
+                anchors.top: creatorLabel.bottom;
+                anchors.left: parent.left;
+                anchors.topMargin: 10;
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.blueHighlight;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+            }
+        }
+        
+        Item {
+            id: posted;
+            anchors.top: creatorItem.bottom;
+            anchors.leftMargin: 15;
+            anchors.topMargin: 15;
+            width: parent.width;
+            height: childrenRect.height;
+            RalewaySemiBold {
+                id: postedLabel;
+                text: "POSTED:";
+                // Text size
+                size: 14;
+                // Anchors
+                anchors.top: parent.top;
+                anchors.left: parent.left;
+
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.lightGrayText;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+            }
+            
+            RalewaySemiBold {
+                id: created_at;
+                text: { getFormattedDate(root.created_at); }
+                // Text size
+                size: 14;
+                // Anchors
+                anchors.top: postedLabel.bottom;
+                anchors.left: parent.left;
+                anchors.right: parent.right;
+                anchors.topMargin: 10;
+                // Style
+                color: hifi.colors.lightGray;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+            }           
+        }
+        
+        Rectangle {
+            anchors.top: posted.bottom;
+            anchors.leftMargin: 15;
+            anchors.topMargin: 15;
+            width: parent.width;
+            height: 1;
+            color: hifi.colors.lightGray;
+        }
+
+        Item {
+            id: licenseItem;
+            anchors.top: posted.bottom;
+            anchors.left: parent.left;
+            anchors.topMargin: 30;
+            width: parent.width;  
+            height: childrenRect.height;
+            RalewaySemiBold {
+                id: licenseLabel;
+                text: "SHARED UNDER:";
+                // Text size
+                size: 14;
+                // Anchors
+                anchors.top: parent.top;
+                anchors.left: parent.left;
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.lightGrayText;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+            }
+            
+            RalewaySemiBold {
+                id: licenseText;
+                text: root.license;
+                // Text size
+                size: 14;
+                // Anchors
+                anchors.top: licenseLabel.bottom;
+                anchors.left: parent.left;
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.lightGray;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+            }
+            RalewaySemiBold {
+                id: licenseHelp;
+                text: "More about this license";
+                // Text size
+                size: 14;
+                // Anchors
+                anchors.top: licenseText.bottom;
+                anchors.left: parent.left;
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.blueHighlight;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+                
+                MouseArea {
+                    anchors.fill: parent;
+                    
+                    onClicked: { 
+                        licenseInfo.visible = true;
+                        var url;
+                        if (root.license == "No Rights Reserved (CC0)") {
+                          url = "https://creativecommons.org/publicdomain/zero/1.0/"
+                        } else if (root.license == "Attribution (CC BY)") {
+                          url = "https://creativecommons.org/licenses/by/4.0/"
+                        } else if (root.license == "Attribution-ShareAlike (CC BY-SA)") {
+                          url = "https://creativecommons.org/licenses/by-sa/4.0/"
+                        } else if (root.license == "Attribution-NoDerivs (CC BY-ND)") {
+                          url = "https://creativecommons.org/licenses/by-nd/4.0/"
+                        } else if (root.license == "Attribution-NonCommercial (CC BY-NC)") {
+                          url = "https://creativecommons.org/licenses/by-nc/4.0/"
+                        } else if (root.license == "Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)") {
+                          url = "https://creativecommons.org/licenses/by-nc-sa/4.0/"
+                        } else if (root.license == "Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)") {
+                          url = "https://creativecommons.org/licenses/by-nc-nd/4.0/"
+                        } else if (root.license == "Proof of Provenance License (PoP License)") {
+                          url = "https://digitalassetregistry.com/PoP-License/v1/"
+                        }
+                        if(url) {
+                            licenseInfoWebView.url = url;
+                            
+                        }
+                    }
+                }
+            }
+        }
+
+        Item {
+            id: descriptionItem;
+            anchors.top: licenseItem.bottom;
+            anchors.topMargin: 15;
+            anchors.left: parent.left;
+            anchors.right: parent.right;
+            height: childrenRect.height;            
+            RalewaySemiBold {
+                id: descriptionLabel;
+                text: "DESCRIPTION:";
+                // Text size
+                size: 14;
+                // Anchors
+                anchors.top: parent.top;
+                anchors.left: parent.left;
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.lightGrayText;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+            }
+            RalewaySemiBold {
+                id: descriptionText;
+                text: root.description;
+                // Text size
+                size: 14;
+                // Anchors
+                anchors.top: descriptionLabel.bottom;
+                anchors.left: parent.left;
+                width: parent.width;
+                // Style
+                color: hifi.colors.lightGray;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+                wrapMode: Text.Wrap;
+            }
+        }
+        
+        Item {
+            id: categoriesList;
+            anchors.top: descriptionItem.bottom;
+            anchors.topMargin: 15;
+            anchors.left: parent.left;
+            anchors.right: parent.right;            
+            width: parent.width;
+            height: childrenRect.height;
+            RalewaySemiBold {
+                id: categoryLabel;
+                text: "IN:";
+                // Text size
+                size: 14;
+                // Anchors
+                anchors.top: parent.top;
+                anchors.left: parent.left;
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.lightGrayText;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+            }
+            ListModel {
+                id: categoriesListModel;
+            }
+            
+            ListView {
+                anchors.left: parent.left;
+                anchors.right: parent.right;
+                anchors.top: categoryLabel.bottom;
+                model: categoriesListModel;
+                height: 20*model.count;
+                delegate: RalewaySemiBold {
+                    id: categoryText;
+                    text: model.category;
+                    // Text size
+                    size: 14;
+                    // Anchors
+                    anchors.leftMargin: 15;
+                    width: paintedWidth;
+                    height: 20;
+                    // Style
+                    color: hifi.colors.blueHighlight;
+                    // Alignment
+                    verticalAlignment: Text.AlignVCenter;
+                    
+                    MouseArea {
+                        anchors.fill: categoryText;
+                        onClicked: root.categoryClicked(model.category);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
new file mode 100644
index 0000000000..4b8471e8b9
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
@@ -0,0 +1,278 @@
+//
+//  MarketplaceListItem.qml
+//  qml/hifi/commerce/marketplace
+//
+//  MarketplaceListItem
+//
+//  Created by Roxanne Skelly on 2019-01-22
+//  Copyright 2019 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import Hifi 1.0 as Hifi
+import QtQuick 2.9
+import QtQuick.Controls 2.2
+import QtGraphicalEffects 1.0
+import stylesUit 1.0
+import controlsUit 1.0 as HifiControlsUit
+import "../../../controls" as HifiControls
+import "../common" as HifiCommerceCommon
+import "qrc:////qml//hifi//models" as HifiModels  // Absolute path so the same code works everywhere.
+import "../common/sendAsset"
+import "../.." as HifiCommon
+
+Rectangle {
+    id: root;
+    property string item_id: "";
+    property string image_url: "";
+    property string name: "";
+    property int likes: 0;
+    property bool liked: false;
+    property string creator: "";
+    property string category: "";
+    property int price: 0;
+    property bool available: false;
+
+    signal buy();
+    signal showItem();
+    signal categoryClicked(string category);
+    signal requestReload();
+    
+    HifiConstants { id: hifi; }
+
+    width: parent.width;
+    height: childrenRect.height+50;
+    
+    DropShadow {
+        anchors.fill: shadowBase;
+        source: shadowBase;
+        verticalOffset: 4;
+        horizontalOffset: 4;
+        radius: 6;
+        samples: 9;
+        color: hifi.colors.baseGrayShadow;
+    }
+    
+    Rectangle {
+        id: shadowBase;
+        anchors.fill: itemRect;
+        color: "white";
+    }
+    
+    Rectangle {
+        id: itemRect;
+        height: childrenRect.height;
+        anchors.left: parent.left;
+        anchors.right: parent.right;
+        anchors.top: parent.top;
+        anchors.topMargin: 20;
+        anchors.bottomMargin: 10;
+        anchors.leftMargin: 15;
+        anchors.rightMargin: 15;        
+        
+        MouseArea {
+            anchors.fill: parent;
+            onClicked: root.showItem();
+            onEntered: { hoverRect.visible = true; console.log("entered"); }
+            onExited: { hoverRect.visible = false; console.log("exited"); }
+            hoverEnabled: true           
+        }
+        
+        Rectangle {
+            id: header;
+            anchors.left: parent.left;
+            anchors.right: parent.right;
+            anchors.top: parent.top;
+            
+            height: 50;
+            
+            RalewaySemiBold {
+                id: nameText;
+                text: root.name;
+                // Text size
+                size: hifi.fontSizes.overlayTitle;
+                // Anchors
+                anchors.top: parent.top;
+                anchors.left: parent.left;
+                anchors.right: parent.right;
+                anchors.rightMargin: 50;
+                anchors.leftMargin: 15;
+                anchors.bottom: parent.bottom;
+                elide: Text.ElideRight;
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.blueHighlight;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+            }
+            Item {
+                id: likes;
+                anchors.top: parent.top;
+                anchors.right: parent.right;
+                anchors.rightMargin: 15;
+                anchors.bottom: parent.bottom;
+                width: childrenRect.width;
+                Connections {
+                    target: MarketplaceScriptingInterface;
+
+                    onMarketplaceItemLikeResult: {
+                        if (result.status !== 'success') {
+                            console.log("Failed to get Marketplace Categories", result.data.message);
+                            root.requestReload();
+                        }
+                    }
+                }                
+                
+                RalewaySemiBold {
+                    id: heart;
+                    text: "\u2665";
+                    // Size
+                    size: 20;
+                    // Anchors
+                    anchors.top: parent.top;
+                    anchors.right: parent.right;
+                    anchors.rightMargin: 0;
+                    anchors.verticalCenter: parent.verticalCenter;
+                    horizontalAlignment: Text.AlignHCenter;
+                    // Style
+                    color: root.liked ? hifi.colors.redAccent  : hifi.colors.lightGrayText;
+                }
+                
+                RalewaySemiBold {
+                    id: likesText;
+                    text: root.likes;
+                    // Text size
+                    size: hifi.fontSizes.overlayTitle;
+                    // Anchors
+                    anchors.top: parent.top;
+                    anchors.right: heart.left;
+                    anchors.rightMargin: 5;
+                    anchors.leftMargin: 15;
+                    anchors.bottom: parent.bottom;
+                    width: paintedWidth;
+                    // Style
+                    color: hifi.colors.baseGray;
+                    // Alignment
+                    verticalAlignment: Text.AlignVCenter;
+                }
+                MouseArea {
+                    anchors.fill: parent;
+                    onClicked: {
+                        console.log("like " + root.item_id);
+                        root.liked = !root.liked;
+                        root.likes = root.liked ? root.likes + 1 : root.likes - 1;
+                        MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, root.liked);
+                    }
+                }                
+            }
+        }
+        Image {
+            id: itemImage;
+            source: root.image_url;
+            anchors.top: header.bottom;
+            anchors.left: parent.left;
+            anchors.right: parent.right;
+            height: width*0.5625
+            fillMode: Image.PreserveAspectCrop;
+        }
+        Item {
+            id: footer;
+            anchors.left: parent.left;
+            anchors.right: parent.right;
+            anchors.top: itemImage.bottom;
+            height: 60;
+            
+            RalewaySemiBold {
+                id: creatorLabel;
+                text: "CREATOR:";
+                // Text size
+                size: 14;
+                // Anchors
+                anchors.top: parent.top;
+                anchors.left: parent.left;
+                anchors.leftMargin: 15;
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.lightGrayText;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+            }
+            
+            RalewaySemiBold {
+                id: creatorText;
+                text: root.creator;
+                // Text size
+                size: 14;
+                // Anchors
+                anchors.top: creatorLabel.top;
+                anchors.left: creatorLabel.right;
+                anchors.leftMargin: 15;
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.lightGray;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+            }
+            RalewaySemiBold {
+                id: categoryLabel;
+                text: "IN:";
+                // Text size
+                size: 14;
+                // Anchors
+                anchors.top: creatorLabel.bottom;
+                anchors.left: parent.left;
+                anchors.leftMargin: 15;
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.lightGrayText;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+            }
+            
+            RalewaySemiBold {
+                id: categoryText;
+                text: root.category;
+                // Text size
+                size: 14;
+                // Anchors
+                anchors.top: categoryLabel.top;
+                anchors.left: categoryLabel.right;
+                anchors.leftMargin: 15;
+                width: paintedWidth;
+                // Style
+                color: hifi.colors.blueHighlight;
+                // Alignment
+                verticalAlignment: Text.AlignVCenter;
+                
+                MouseArea {
+                    anchors.fill: parent;
+                    onClicked: root.categoryClicked(root.category);
+                }
+            }
+
+            HifiControlsUit.Button {
+                text: root.price ? root.price : "FREE";
+                buttonGlyph: root.price ? hifi.glyphs.hfc : "";
+                anchors.right: parent.right;
+                anchors.top: parent.top;
+                anchors.bottom: parent.bottom;
+                anchors.rightMargin: 15;
+                anchors.topMargin:10;
+                anchors.bottomMargin: 10;
+                color: hifi.buttons.blue;
+                
+                onClicked: root.buy();
+            }
+        }
+        Rectangle {
+            id: hoverRect;
+            anchors.fill: parent;
+            border.color: hifi.colors.blueHighlight;
+            border.width: 2;
+            color: "#00000000";
+            visible: false;
+        }        
+    }
+}
diff --git a/interface/resources/qml/hifi/commerce/marketplace/SortButton.qml b/interface/resources/qml/hifi/commerce/marketplace/SortButton.qml
new file mode 100644
index 0000000000..37ad2735ce
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/marketplace/SortButton.qml
@@ -0,0 +1,87 @@
+//
+//  SortButton.qml
+//  qml/hifi/commerce/marketplace
+//
+//  SortButton
+//
+//  Created by Roxanne Skelly on 2019-01-18
+//  Copyright 2019 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import Hifi 1.0 as Hifi
+import QtQuick 2.9
+import QtQuick.Controls 2.2
+import QtGraphicalEffects 1.0
+import stylesUit 1.0
+import controlsUit 1.0 as HifiControlsUit
+import "../../../controls" as HifiControls
+import "../common" as HifiCommerceCommon
+import "qrc:////qml//hifi//models" as HifiModels  // Absolute path so the same code works everywhere.
+import "../common/sendAsset"
+import "../.." as HifiCommon
+
+Item {
+    HifiConstants { id: hifi; }
+    
+    id: root;
+    
+    
+    property string glyph: "";
+    property string text: "";
+    property bool checked: false;
+    signal clicked();
+    
+    width: childrenRect.width;
+    height: parent.height;
+
+    Rectangle {
+        anchors.top: parent.top;
+        anchors.left: parent.left;
+        height: parent.height;
+        width: 2;
+        color: hifi.colors.faintGray;
+        visible: index > 0;
+    }
+    
+    HiFiGlyphs {
+        id: buttonGlyph;
+        text: root.glyph;
+        // Size
+        size: 14;
+        // Anchors
+        anchors.left: parent.left;
+        anchors.leftMargin: 0;
+        anchors.top: parent.top;
+        anchors.verticalCenter: parent.verticalCenter;
+        height: parent.height;
+        horizontalAlignment: Text.AlignHCenter;
+        // Style
+        color: hifi.colors.lightGray;
+    }
+    RalewayRegular {
+        id: buttonText;
+        text: root.text;
+        // Text size
+        size: 14;
+        // Style
+        color: hifi.colors.lightGray;
+        elide: Text.ElideRight;
+        horizontalAlignment: Text.AlignHCenter;
+        verticalAlignment: Text.AlignVCenter;
+        // Anchors
+        anchors.left: parent.left;
+        anchors.leftMargin: 20;
+        anchors.top: parent.top;
+        height: parent.height;
+    }
+    MouseArea {
+        anchors.fill: parent;
+        hoverEnabled: enabled;
+        onClicked: {
+            root.clicked();
+        }
+    }
+}
\ No newline at end of file
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 1ec9c93e12..88fda37623 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -2916,7 +2916,7 @@ void Application::initializeUi() {
     }, commerceCallback);
 
     QmlContextCallback marketplaceCallback = [](QQmlContext* context) {
-        context->setContextProperty("Marketplace", new QmlMarketplace());
+        context->setContextProperty("MarketplaceScriptingInterface", new QmlMarketplace());
     };
     OffscreenQmlSurface::addWhitelistContextHandler({
         QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
diff --git a/interface/src/commerce/QmlMarketplace.cpp b/interface/src/commerce/QmlMarketplace.cpp
index 99d3bb1ae6..07a9e570bd 100644
--- a/interface/src/commerce/QmlMarketplace.cpp
+++ b/interface/src/commerce/QmlMarketplace.cpp
@@ -55,50 +55,54 @@ void QmlMarketplace::getMarketplaceItems(
     const int& perPage) {
 
     QString endpoint = "items";
-    QJsonObject request;
-    request["q"] = q;
-    request["view"] = view;
-    request["category"] = category;
-    request["adminFilter"] = adminFilter;
-    request["adminFilterCost"] = adminFilterCost;
-    request["sort"] = sort;
-    request["isFree"] = isFree;
-    request["page"] = page;
-    request["perPage"] = perPage;
+    QUrlQuery request;
+    request.addQueryItem("q", q);
+    request.addQueryItem("view", view);
+    request.addQueryItem("category", category);
+    request.addQueryItem("adminFilter", adminFilter);
+    request.addQueryItem("adminFilterCost", adminFilterCost);
+    request.addQueryItem("sort", sort);
+    if (isFree) {
+        request.addQueryItem("isFree", "true");
+    }
+    request.addQueryItem("page", QString::number(page));
+    request.addQueryItem("perPage", QString::number(perPage));
     send(endpoint, "getMarketplaceItemsSuccess", "getMarketplaceItemsFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::Optional, request);
 }
 
 void QmlMarketplace::getMarketplaceItem(const QString& marketplaceItemId) {
     QString endpoint = QString("items/") + marketplaceItemId;
-    QJsonObject request;
+    QUrlQuery request;
     send(endpoint, "getMarketplaceItemSuccess", "getMarketplaceItemFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::Optional, request);
 }
 
 void QmlMarketplace::marketplaceItemLike(const QString& marketplaceItemId, const bool like) {
     QString endpoint = QString("items/") + marketplaceItemId + "/like";
-    QJsonObject request;
-    send(endpoint, "marketplaceItemLikeSuccess", "marketplaceItemLikeFailure", like ? QNetworkAccessManager::PutOperation : QNetworkAccessManager::DeleteOperation, AccountManagerAuth::Required, request);
+    QUrlQuery request;
+    send(endpoint, "marketplaceItemLikeSuccess", "marketplaceItemLikeFailure", like ? QNetworkAccessManager::PostOperation : QNetworkAccessManager::DeleteOperation, AccountManagerAuth::Required, request);
 }
 
 void QmlMarketplace::getMarketplaceCategories() {
     QString endpoint = "categories";
-    QJsonObject request;
+    QUrlQuery request;
     send(endpoint, "getMarketplaceCategoriesSuccess", "getMarketplaceCategoriesFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::None, request);
 }
 
 
-void QmlMarketplace::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request) {
+void QmlMarketplace::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, const QUrlQuery & request) {
     auto accountManager = DependencyManager::get<AccountManager>();
     const QString URL = "/api/v1/marketplace/";
     JSONCallbackParameters callbackParams(this, success, fail);
-#if defined(DEV_BUILD)  // Don't expose user's personal data in the wild. But during development this can be handy.
-    qCInfo(commerce) << "Sending" << QJsonDocument(request).toJson(QJsonDocument::Compact);
-#endif
+
     accountManager->sendRequest(URL + endpoint,
         authType,
         method,
         callbackParams,
-        QJsonDocument(request).toJson());
+        QByteArray(),
+        NULL,
+        QVariantMap(),
+        request);
+
 }
 
 QJsonObject QmlMarketplace::apiResponse(const QString& label, QNetworkReply* reply) {
diff --git a/interface/src/commerce/QmlMarketplace.h b/interface/src/commerce/QmlMarketplace.h
index 95a1aa3911..f954198371 100644
--- a/interface/src/commerce/QmlMarketplace.h
+++ b/interface/src/commerce/QmlMarketplace.h
@@ -60,7 +60,7 @@ signals:
     void marketplaceItemLikeResult(QJsonObject result);
 
 private:
-    void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request);
+    void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, const QUrlQuery & request);
     QJsonObject apiResponse(const QString& label, QNetworkReply* reply);
     QJsonObject failResponse(const QString& label, QNetworkReply* reply);
 };
diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp
index 989661cb81..5be2c6d02e 100644
--- a/libraries/networking/src/AccountManager.cpp
+++ b/libraries/networking/src/AccountManager.cpp
@@ -208,7 +208,7 @@ void AccountManager::setSessionID(const QUuid& sessionID) {
     }
 }
 
-QNetworkRequest AccountManager::createRequest(QString path, AccountManagerAuth::Type authType) {
+QNetworkRequest AccountManager::createRequest(QString path, AccountManagerAuth::Type authType, const QUrlQuery & query) {
     QNetworkRequest networkRequest;
     networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
     networkRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
@@ -227,6 +227,7 @@ QNetworkRequest AccountManager::createRequest(QString path, AccountManagerAuth::
     } else {
         requestURL.setPath("/" + path);
     }
+    requestURL.setQuery(query);
 
     if (authType != AccountManagerAuth::None ) {
         if (hasValidAccessToken()) {
@@ -263,13 +264,14 @@ void AccountManager::sendRequest(const QString& path,
                                   Q_ARG(const JSONCallbackParameters&, callbackParams),
                                   Q_ARG(const QByteArray&, dataByteArray),
                                   Q_ARG(QHttpMultiPart*, dataMultiPart),
-                                  Q_ARG(QVariantMap, propertyMap));
+                                  Q_ARG(QVariantMap, propertyMap),
+                                  Q_ARG(QUrlQuery, query));
         return;
     }
 
     QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
 
-    QNetworkRequest networkRequest = createRequest(path, authType);
+    QNetworkRequest networkRequest = createRequest(path, authType, query);
 
     if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
         qCDebug(networking) << "Making a request to" << qPrintable(networkRequest.url().toString());
diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h
index ca2b826c98..36563a6ae0 100644
--- a/libraries/networking/src/AccountManager.h
+++ b/libraries/networking/src/AccountManager.h
@@ -61,7 +61,7 @@ class AccountManager : public QObject, public Dependency {
 public:
     AccountManager(UserAgentGetter userAgentGetter = DEFAULT_USER_AGENT_GETTER);
 
-    QNetworkRequest createRequest(QString path, AccountManagerAuth::Type authType);
+    QNetworkRequest createRequest(QString path, AccountManagerAuth::Type authType, const QUrlQuery & query = QUrlQuery());
     Q_INVOKABLE void sendRequest(const QString& path,
                                  AccountManagerAuth::Type authType,
                                  QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js
index 19efdc042c..7cacfd7935 100644
--- a/scripts/system/commerce/wallet.js
+++ b/scripts/system/commerce/wallet.js
@@ -379,21 +379,16 @@ function deleteSendMoneyParticleEffect() {
 function onUsernameChanged() {
 }    
 
-var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js");
-var METAVERSE_SERVER_URL = Account.metaverseServerURL;
-var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page.
-function openMarketplace(optionalItemOrUrl) {
-    // This is a bit of a kluge, but so is the whole file.
-    // If given a whole path, use it with no cta.
-    // If given an id, build the appropriate url and use the id as the cta.
-    // Otherwise, use home and 'marketplace cta'.
-    // AND... if call onMarketplaceOpen to setupWallet if we need to.
-    var url = optionalItemOrUrl || MARKETPLACE_URL_INITIAL;
-    // If optionalItemOrUrl contains the metaverse base, then it's a url, not an item id.
-    if (optionalItemOrUrl && optionalItemOrUrl.indexOf(METAVERSE_SERVER_URL) === -1) {
-        url = MARKETPLACE_URL + '/items/' + optionalItemOrUrl;
+var MARKETPLACE_QML_PATH = "hifi/commerce/marketplace/Marketplace.qml";
+function openMarketplace(optionalItem) {
+    ui.open(MARKETPLACE_QML_PATH);
+    
+    if (optionalItem) {
+        ui.tablet.sendToQml({
+            method: 'updateMarketplaceQMLItem',
+            params: { itemId: optionalItem }
+        });
     }
-    ui.open(url, MARKETPLACES_INJECT_SCRIPT_URL);
 }
 
 function setCertificateInfo(itemCertificateId) {
diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js
index f1931192e4..74bf8d3fec 100644
--- a/scripts/system/html/js/marketplacesInject.js
+++ b/scripts/system/html/js/marketplacesInject.js
@@ -19,6 +19,7 @@
     var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD";
     var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD";
     var GOTO_DIRECTORY = "GOTO_DIRECTORY";
+    var GOTO_MARKETPLACE = "GOTO_MARKETPLACE";
     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";
@@ -72,7 +73,13 @@
 
         // Footer actions.
         $("#back-button").on("click", function () {
-            (document.referrer !== "") ? window.history.back() : window.location = (marketplaceBaseURL + "/marketplace?");
+            if (document.referrer !== "") {
+                window.history.back();
+            } else {
+                EventBridge.emitWebEvent(JSON.stringify({
+                    type: GOTO_MARKETPLACE
+                }));
+            }
         });
         $("#all-markets").on("click", function () {
             EventBridge.emitWebEvent(JSON.stringify({
@@ -93,7 +100,9 @@
             window.location = "https://clara.io/library?gameCheck=true&public=true";
         });
         $('#exploreHifiMarketplace').on('click', function () {
-            window.location = marketplaceBaseURL + "/marketplace?";
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: GOTO_MARKETPLACE
+            }));
         });
     }
 
diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js
index db3b2e2107..2ca79b49f0 100644
--- a/scripts/system/marketplaces/marketplaces.js
+++ b/scripts/system/marketplaces/marketplaces.js
@@ -25,6 +25,7 @@ var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertif
 var MARKETPLACE_ITEM_TESTER_QML_PATH = "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml";
 var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; // HRS FIXME "hifi/commerce/purchases/Purchases.qml";
 var MARKETPLACE_WALLET_QML_PATH = "hifi/commerce/wallet/Wallet.qml";
+var MARKETPLACE_QML_PATH = "hifi/commerce/marketplace/Marketplace.qml";
 var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js");
 var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html");
 var METAVERSE_SERVER_URL = Account.metaverseServerURL;
@@ -36,6 +37,7 @@ 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 GOTO_MARKETPLACE = "GOTO_MARKETPLACE";
 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";
@@ -154,18 +156,15 @@ function onMarketplaceOpen(referrer) {
     }
 }
 
-function openMarketplace(optionalItemOrUrl) {
-    // This is a bit of a kluge, but so is the whole file.
-    // If given a whole path, use it with no cta.
-    // If given an id, build the appropriate url and use the id as the cta.
-    // Otherwise, use home and 'marketplace cta'.
-    // AND... if call onMarketplaceOpen to setupWallet if we need to.
-    var url = optionalItemOrUrl || MARKETPLACE_URL_INITIAL;
-    // If optionalItemOrUrl contains the metaverse base, then it's a url, not an item id.
-    if (optionalItemOrUrl && optionalItemOrUrl.indexOf(METAVERSE_SERVER_URL) === -1) {
-        url = MARKETPLACE_URL + '/items/' + optionalItemOrUrl;
+function openMarketplace(optionalItem) {
+    ui.open(MARKETPLACE_QML_PATH);
+    
+    if (optionalItem) {
+        ui.tablet.sendToQml({
+            method: 'updateMarketplaceQMLItem',
+            params: { itemId: optionalItem }
+        });
     }
-    ui.open(url, MARKETPLACES_INJECT_SCRIPT_URL);
 }
 
 // Function Name: wireQmlEventBridge()
@@ -439,7 +438,9 @@ var referrerURL; // Used for updating Purchases QML
 var filterText; // Used for updating Purchases QML
 function onWebEventReceived(message) {
     message = JSON.parse(message);
-    if (message.type === GOTO_DIRECTORY) {
+    if (message.type === GOTO_MARKETPLACE) {
+        openMarketplace();
+    } else if (message.type === GOTO_DIRECTORY) {
         // This is the chooser between marketplaces. Only OUR markteplace
         // requires/makes-use-of wallet, so doesn't go through openMarketplace bottleneck.
         ui.open(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL);
@@ -518,6 +519,7 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
     if (message.messageSrc === "HTML") {
         return;
     }
+    console.log(JSON.stringify(message));
     switch (message.method) {
     case 'gotoBank':
         ui.close();
@@ -560,6 +562,18 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
     case 'checkout_continue':
         openMarketplace();
         break;
+    case 'marketplace_checkout':
+        ui.open(MARKETPLACE_CHECKOUT_QML_PATH);
+        ui.tablet.sendToQml({
+            method: 'updateCheckoutQMLItemID',
+            params: message
+        });
+        break;
+    case 'marketplace_marketplaces':
+        // This is the chooser between marketplaces. Only OUR markteplace
+        // requires/makes-use-of wallet, so doesn't go through openMarketplace bottleneck.
+        ui.open(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL);
+        break;
     case 'checkout_rezClicked':
     case 'purchases_rezClicked':
     case 'tester_rezClicked':
@@ -699,7 +713,9 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) {
     var onMarketplaceItemTesterScreenNow = (
         url.indexOf(MARKETPLACE_ITEM_TESTER_QML_PATH) !== -1 ||
         url === MARKETPLACE_ITEM_TESTER_QML_PATH);
-
+    var onMarketplaceScreenNow = (
+        url.indexOf(MARKETPLACE_QML_PATH) !== -1 ||
+        url === MARKETPLACE_QML_PATH);
     if ((!onWalletScreenNow && onWalletScreen) ||
         (!onCommerceScreenNow && onCommerceScreen) ||
         (!onMarketplaceItemTesterScreenNow && onMarketplaceScreen)
@@ -711,7 +727,7 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) {
     onCommerceScreen = onCommerceScreenNow;
     onWalletScreen = onWalletScreenNow;
     onMarketplaceItemTesterScreen = onMarketplaceItemTesterScreenNow;
-    wireQmlEventBridge(onCommerceScreen || onWalletScreen || onMarketplaceItemTesterScreen);
+    wireQmlEventBridge(onCommerceScreen || onWalletScreen || onMarketplaceItemTesterScreen || onMarketplaceScreenNow);
 
     if (url === MARKETPLACE_PURCHASES_QML_PATH) {
         // FIXME? Is there a race condition here in which the event
@@ -769,14 +785,13 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) {
 
 
 var BUTTON_NAME = "MARKET";
-var MARKETPLACE_QML_SOURCE = "hifi/commerce/marketplace/Marketplace.qml";
 
 var ui;
 function startup() {
     ui = new AppUi({
         buttonName: BUTTON_NAME,
         sortOrder: 9,
-        home: MARKETPLACE_QML_SOURCE,
+        home: MARKETPLACE_QML_PATH,
         onScreenChanged: onTabletScreenChanged,
         onMessage: onQmlMessageReceived
     });

From ff2d51701e6589a7ac2c99b668cdeaf429ab5935 Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxanne@highfidelity.io>
Date: Fri, 25 Jan 2019 13:41:16 -0800
Subject: [PATCH 03/18] QML Marketplace Add keyboard while in search General
 cleanup

---
 .../hifi/commerce/marketplace/Marketplace.qml | 996 ++++++++++--------
 .../commerce/marketplace/MarketplaceItem.qml  | 577 +++++-----
 .../marketplace/MarketplaceListItem.qml       | 376 ++++---
 3 files changed, 1058 insertions(+), 891 deletions(-)

diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
index e5ce431de8..77820ccaca 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
@@ -24,18 +24,21 @@ import "../common/sendAsset"
 import "../.." as HifiCommon
 
 Rectangle {
-    HifiConstants { id: hifi; }
+    HifiConstants {
+        id: hifi
+    }
 
-    id: root;
+    id: root
 
-    property string activeView: "initialize";
-    property bool keyboardRaised: false;
-    property int currentSortIndex: 0;
-    property string sortString: "";
-    property string categoryString: "";
-    property string searchString: "";
- 
-    anchors.fill: (typeof parent === undefined) ? undefined : parent;
+    property string activeView: "initialize"
+    property int currentSortIndex: 0
+    property string sortString: ""
+    property string categoryString: ""
+    property string searchString: ""
+    property bool keyboardEnabled: HMD.active
+    property bool keyboardRaised: false
+    
+    anchors.fill: (typeof parent === undefined) ? undefined : parent
 
     function getMarketplaceItems() {
         marketplaceItemView.visible = false;
@@ -44,11 +47,11 @@ Rectangle {
     }
     
     Component.onDestruction: {
-        KeyboardScriptingInterface.raised = false;
+        keyboard.raised = false;
     }
     
     Connections {
-        target: MarketplaceScriptingInterface;
+        target: MarketplaceScriptingInterface
 
         onGetMarketplaceCategoriesResult: {
             if (result.status !== 'success') {
@@ -66,7 +69,7 @@ Rectangle {
                     });
                 });
             }
-            getMarketplaceItems();            
+            getMarketplaceItems();
         }
         onGetMarketplaceItemsResult: {
             marketBrowseModel.handlePage(result.status !== "success" && result.message, result);
@@ -77,7 +80,6 @@ Rectangle {
                 console.log("Failed to get Marketplace Item", result.data.message);
             } else {
             
-                console.log(JSON.stringify(result.data));
                 marketplaceItem.item_id = result.data.id;
                 marketplaceItem.image_url = result.data.thumbnail_url;
                 marketplaceItem.name = result.data.title;
@@ -91,7 +93,6 @@ Rectangle {
                 marketplaceItem.license = result.data.license;
                 marketplaceItem.available = result.data.availability == "available";
                 marketplaceItem.created_at = result.data.created_at;
-                console.log("HEIGHT: " + marketplaceItemContent.height);
                 marketplaceItemScrollView.contentHeight = marketplaceItemContent.height;
                 itemsList.visible = false;
                 marketplaceItemView.visible = true;
@@ -100,196 +101,194 @@ Rectangle {
     }
 
     HifiCommerceCommon.CommerceLightbox {
-        id: lightboxPopup;
-        visible: false;
-        anchors.fill: parent;
+        id: lightboxPopup
+        visible: false
+        anchors.fill: parent
     }
 
     //
     // HEADER BAR START
     //
     
-    
     Rectangle {
-        id: headerShadowBase;
-        anchors.fill: header;
-        color: "white";
+        id: headerShadowBase
+        anchors.fill: header
+        color: "white"
     }
     DropShadow {
-        anchors.fill: headerShadowBase;
-        source: headerShadowBase;
-        verticalOffset: 4;
-        horizontalOffset: 4;
-        radius: 6;
-        samples: 9;
-        color: hifi.colors.baseGrayShadow;
-        z:5;
-    }    
+        anchors.fill: headerShadowBase
+        source: headerShadowBase
+        verticalOffset: 4
+        horizontalOffset: 4
+        radius: 6
+        samples: 9
+        color: hifi.colors.baseGrayShadow
+        z:5
+    }
     Rectangle {
         id: header;
-        visible: true;
-        anchors.left: parent.left;
-        anchors.top: parent.top;
-        anchors.right: parent.right;
-        anchors.topMargin: -1;
-        anchors.leftMargin: -1;
-        anchors.rightMargin: -1;
-        height: childrenRect.height+5;
-        z: 5;
+
+        visible: true
+        anchors {
+            left: parent.left
+            top: parent.top
+            right: parent.right
+        }
+
+        height: childrenRect.height+5
+        z: 5
     
         Rectangle {
-            id: titleBarContainer;
-            visible: true;
-            // Size
-            width: parent.width;
-            height: 50;
-            // Anchors
-            anchors.left: parent.left;
-            anchors.top: parent.top;
-              
-            
-            // Marketplace icon
+            id: titleBarContainer
+
+            anchors.left: parent.left
+            anchors.top: parent.top
+            width: parent.width
+            height: 50
+            visible: true
+
             Image {
-                id: marketplaceIcon;
-                source: "../../../../images/hifi-logo-blackish.svg";
+                id: marketplaceIcon
+
+                anchors {
+                    left: parent.left
+                    leftMargin: 8
+                    verticalCenter: parent.verticalCenter
+                }
                 height: 20
-                width: marketplaceIcon.height;
-                anchors.left: parent.left;
-                anchors.leftMargin: 8;
-                anchors.verticalCenter: parent.verticalCenter;
-                visible: true;
+                width: marketplaceIcon.height
+                source: "../../../../images/hifi-logo-blackish.svg"
+                visible: true
             }
 
-            // Title Bar text
             RalewaySemiBold {
-                id: titleBarText;
-                text: "Marketplace";
-                // Text size
-                size: hifi.fontSizes.overlayTitle;
-                // Anchors
-                anchors.top: parent.top;
-                anchors.left: marketplaceIcon.right;
-                anchors.leftMargin: 6;
-                anchors.bottom: parent.bottom;
-                width: paintedWidth;
-                // Style
-                color: hifi.colors.black;
-                // Alignment
-                verticalAlignment: Text.AlignVCenter;
+                id: titleBarText
+
+                anchors {
+                    top: parent.top
+                    left: marketplaceIcon.right
+                    bottom: parent.bottom
+                    leftMargin: 6
+                }
+                width: paintedWidth
+
+                text: "Marketplace"
+                size: hifi.fontSizes.overlayTitle
+                color: hifi.colors.black
+                verticalAlignment: Text.AlignVCenter
             }
         }
 
         Rectangle {
-            id: searchBarContainer;
-            visible: true;
-            clip: false;
-            // Size
-            width: parent.width;
-            anchors.top: titleBarContainer.bottom;
-            height: 50;
+            id: searchBarContainer
 
+            anchors.top: titleBarContainer.bottom
+            width: parent.width
+            height: 50
+            
+            visible: true
+            clip: false
 
-           Rectangle {
-                id: categoriesButton;
-                anchors.left: parent.left;
-                anchors.leftMargin: 10;
-                anchors.verticalCenter: parent.verticalCenter;
-                height: 34;
-                width: categoriesText.width + 25;
-                color: hifi.colors.white;
-                radius: 4;
-                border.width: 1;
-                border.color: hifi.colors.lightGrayText;
-                
+            //
+            // TODO: Possibly change this to be a combo box
+            //
+            Rectangle {
+                id: categoriesButton
 
-                // Categories Text
-                RalewayRegular {
-                    id: categoriesText;
-                    text: "Categories";
-                    // Text size
-                    size: 14;
-                    // Style
-                    color: hifi.colors.lightGrayText;
-                    elide: Text.ElideRight;
-                    horizontalAlignment: Text.AlignHCenter;
-                    verticalAlignment: Text.AlignVCenter;
-                    width: Math.min(textMetrics.width + 25, 110);
-                    // Anchors
-                    anchors.centerIn: parent;
-                    rightPadding: 10;
+                anchors {
+                    left: parent.left
+                    leftMargin: 10
+                    verticalCenter: parent.verticalCenter
                 }
+                height: 34
+                width: categoriesText.width + 25
+
+                color: hifi.colors.white
+                radius: 4
+                border.width: 1
+                border.color: hifi.colors.lightGrayText
+                
+                RalewayRegular {
+                    id: categoriesText
+
+                    anchors.centerIn: parent
+
+                    text: "Categories"
+                    size: 14
+                    color: hifi.colors.lightGrayText
+                    elide: Text.ElideRight
+                    horizontalAlignment: Text.AlignHCenter
+                    verticalAlignment: Text.AlignVCenter
+                    rightPadding: 10
+                }
+
                 HiFiGlyphs {
-                    id: categoriesDropdownIcon;
-                    text: hifi.glyphs.caratDn;
-                    // Size
-                    size: 34;
-                    // Anchors
-                    anchors.right: parent.right;
-                    anchors.rightMargin: -8;
-                    anchors.verticalCenter: parent.verticalCenter;
-                    horizontalAlignment: Text.AlignHCenter;
-                    // Style
-                    color: hifi.colors.baseGray;
+                    id: categoriesDropdownIcon
+
+                    anchors {
+                        right: parent.right
+                        rightMargin: -8
+                        verticalCenter: parent.verticalCenter
+                    }
+
+                    text: hifi.glyphs.caratDn
+                    size: 34
+                    horizontalAlignment: Text.AlignHCenter
+                    color: hifi.colors.baseGray
                 }
 
                 MouseArea {
-                    anchors.fill: parent;
-                    hoverEnabled: enabled;
+                    anchors.fill: parent
+
+                    hoverEnabled: enabled
                     onClicked: {
                         categoriesDropdown.visible = !categoriesDropdown.visible;
                         categoriesButton.color = categoriesDropdown.visible ? hifi.colors.lightGray : hifi.colors.white;
                         categoriesDropdown.forceActiveFocus();
                     }
-                    onEntered: categoriesText.color = hifi.colors.baseGrayShadow;
-                    onExited: categoriesText.color = hifi.colors.baseGray;
+                    onEntered: categoriesText.color = hifi.colors.baseGrayShadow
+                    onExited: categoriesText.color = hifi.colors.baseGray
                 }
 
                 Component.onCompleted: {
-                    console.log("Getting Marketplace Categories");
                     MarketplaceScriptingInterface.getMarketplaceCategories();
                 }                
             }
                 
             // or
             RalewayRegular {
-                id: orText;
-                text: "or";
-                // Text size
+                id: orText
+
+                anchors.left: categoriesButton.right
+                anchors.verticalCenter: parent.verticalCenter
+                width: 25
+
+                text: "or"
                 size: 18;
-                // Style
-                color: hifi.colors.baseGray;
-                elide: Text.ElideRight;
-                horizontalAlignment: Text.AlignHCenter;
-                verticalAlignment: Text.AlignVCenter;
-                width: Math.min(textMetrics.width + 25, 110);
-                // Anchors
-                anchors.left: categoriesButton.right;
-                rightPadding: 10;
-                leftPadding: 10;
-                anchors.verticalCenter: parent.verticalCenter;            
+                color: hifi.colors.baseGray
+                elide: Text.ElideRight
+                horizontalAlignment: Text.AlignHCenter
+                verticalAlignment: Text.AlignVCenter
+                rightPadding: 10
+                leftPadding: 10
             }
+
             HifiControlsUit.TextField {
-                id: searchField;
-                anchors.verticalCenter: parent.verticalCenter;
-                anchors.right: parent.right;
-                anchors.left: orText.right;
-                anchors.rightMargin: 10;
-                height: 34;
-                isSearchField: true;
-                colorScheme: hifi.colorSchemes.faintGray;
-                
+                id: searchField
 
-                font.family: "Fira Sans"
-                font.pixelSize: hifi.fontSizes.textFieldInput;
-
-                placeholderText: "Search Marketplace";
-                            
-                TextMetrics {
-                    id: primaryFilterTextMetrics;
-                    font.family: "FiraSans Regular";
-                    font.pixelSize: hifi.fontSizes.textFieldInput;
-                    font.capitalization: Font.AllUppercase;
+                anchors {
+                    verticalCenter: parent.verticalCenter
+                    right: parent.right
+                    left: orText.right
+                    rightMargin: 10
                 }
+                height: 34
+
+                isSearchField: true
+                colorScheme: hifi.colorSchemes.faintGray
+                font.family: "Fira Sans"
+                font.pixelSize: hifi.fontSizes.textFieldInput
+                placeholderText: "Search Marketplace"
 
                 // workaround for https://bugreports.qt.io/browse/QTBUG-49297
                 Keys.onPressed: {
@@ -311,7 +310,7 @@ Rectangle {
                         break;
                     }
                 }
-                onTextChanged: root.searchString = text;
+                onTextChanged: root.searchString = text
                 onAccepted: {
                     root.searchString = searchField.text;
                     getMarketplaceItems();
@@ -319,15 +318,12 @@ Rectangle {
                 }
 
                 onActiveFocusChanged: {
-                    if (!activeFocus) {
-                        dropdownContainer.visible = false;
-                    }
+                    root.keyboardRaised = activeFocus;
                 }
  
             }
         }
     }
-    
     //
     // HEADER BAR END
     //
@@ -336,14 +332,15 @@ Rectangle {
     // CATEGORIES LIST START
     //    
     Item {
-        id: categoriesDropdown;    
+        id: categoriesDropdown
+
         anchors.fill: parent;
-        visible: false;
-        z: 10;
-        
+
+        visible: false
+        z: 10
         MouseArea {
-            anchors.fill: parent;
-            propagateComposedEvents: true;
+            anchors.fill: parent
+            propagateComposedEvents: true
             onClicked: {
                 categoriesDropdown.visible = false;
                 categoriesButton.color = hifi.colors.white;
@@ -351,57 +348,72 @@ Rectangle {
         }
         
         Rectangle {
-            anchors.left: parent.left;
-            anchors.bottom: parent.bottom;
-            anchors.top: parent.top;
-            anchors.topMargin: 100;
-            width: parent.width/3;
-            color: hifi.colors.white;
+            anchors {
+                left: parent.left;
+                bottom: parent.bottom;
+                top: parent.top;
+                topMargin: 100;
+            }
+            width: parent.width/3
+
+            color: hifi.colors.white
             
             ListModel {
-                id: categoriesModel;
+                id: categoriesModel
             }
 
             ListView {
                 id: categoriesListView;
-                anchors.fill: parent;
-                anchors.rightMargin: 10;
-                width: parent.width;
-                clip: true;
+
+                anchors.fill: parent
+                anchors.rightMargin: 10
+                width: parent.width
+
+                clip: true
                 
-                model: categoriesModel;
+                model: categoriesModel
                 delegate: ItemDelegate {
-                    height: 34;
-                    width: parent.width;
-                    clip: true;
+                    height: 34
+                    width: parent.width
+
+                    clip: true
                     contentItem: Rectangle {
-                        id: categoriesItem;
-                        anchors.fill: parent;
-                        color: hifi.colors.white;
-                        visible: true;
+                        id: categoriesItem
+
+                        anchors.fill: parent
+
+                        color: hifi.colors.white
+                        visible: true
 
                         RalewayRegular {
-                            id: categoriesItemText;
-                            text: model.name;
-                            anchors.leftMargin: 15;
-                            anchors.fill:parent;
-                            color: ListView.isCurrentItem ? hifi.colors.lightBlueHighlight : hifi.colors.baseGray;
-                            horizontalAlignment: Text.AlignLeft;
-                            verticalAlignment: Text.AlignVCenter;
-                            size: 14;
+                            id: categoriesItemText
+
+                            anchors.leftMargin: 15
+                            anchors.fill:parent
+ 
+                            text: model.name
+                            color: ListView.isCurrentItem ? hifi.colors.lightBlueHighlight : hifi.colors.baseGray
+                            horizontalAlignment: Text.AlignLeft
+                            verticalAlignment: Text.AlignVCenter
+                            size: 14
                         }
                     }
+
                     MouseArea {
-                        anchors.fill: parent;
-                        hoverEnabled: true;
-                        propagateComposedEvents: false;
-                        z: 10;
+                        anchors.fill: parent
+                        z: 10
+
+                        hoverEnabled: true
+                        propagateComposedEvents: false
+
                         onEntered: {
                             categoriesItem.color = ListView.isCurrentItem ? hifi.colors.white : hifi.colors.lightBlueHighlight;
                         }
+
                         onExited: {
                             categoriesItem.color = ListView.isCurrentItem ? hifi.colors.lightBlueHighlight : hifi.colors.white;
                         }
+
                         onClicked: {
                             categoriesListView.currentIndex = index;
                             categoriesText.text = categoriesItemText.text;
@@ -413,12 +425,17 @@ Rectangle {
                     }
                     
                 }
+
                 ScrollBar.vertical: ScrollBar {
-                    parent: categoriesListView.parent;
-                    anchors.top: categoriesListView.top;
-                    anchors.bottom: categoriesListView.bottom;
-                    anchors.left: categoriesListView.right;
-                    contentItem.opacity: 1;
+                    parent: categoriesListView.parent
+
+                    anchors {
+                        top: categoriesListView.top;
+                        bottom: categoriesListView.bottom;
+                        left: categoriesListView.right;
+                    }
+
+                    contentItem.opacity: 1
                 }
             }
         }
@@ -431,19 +448,24 @@ Rectangle {
     // ITEMS LIST START
     //
     Item {
-        id: itemsList;
-        anchors.fill: parent;
-        anchors.topMargin: 100;
-        anchors.bottomMargin: 50;
+        id: itemsList
+
+        anchors {
+            fill: parent
+            topMargin: 100
+            bottomMargin: 50
+        }
+
         visible: true;
         
         HifiModels.PSFListModel {
-            id: marketBrowseModel;
-            itemsPerPage: 7;
-            listModelName: 'marketBrowse';
-            listView: marketBrowseList;
-            getPage: function () {
+            id: marketBrowseModel
 
+            itemsPerPage: 7
+            listModelName: 'marketBrowse'
+            listView: marketBrowseList
+
+            getPage: function () {
                 MarketplaceScriptingInterface.getMarketplaceItems(
                     root.searchString == "" ? undefined : root.searchString,
                     "",
@@ -456,90 +478,98 @@ Rectangle {
                     marketBrowseModel.itemsPerPage
                 );
             }
+
             processPage: function(data) {
-                console.log(JSON.stringify(data));
-                data.items.forEach(function (item) {
-                    console.log(JSON.stringify(item));
-                });
                 return data.items;
             }
         }
         ListView {
-            id: marketBrowseList;
-            model: marketBrowseModel;
-            // Anchors
-            anchors.fill: parent;
-            anchors.rightMargin: 10;
-            orientation: ListView.Vertical;
-            focus: true;
-            clip: true;            
+            id: marketBrowseList
+
+            anchors.fill: parent
+            anchors.rightMargin: 10
+
+            model: marketBrowseModel
+
+            orientation: ListView.Vertical
+            focus: true
+            clip: true
             
             delegate: MarketplaceListItem {
-                item_id: model.id;
-                image_url:model.thumbnail_url;
-                name: model.title;
-                likes: model.likes;
-                liked: model.has_liked;
-                creator: model.creator;
-                category: model.primary_category;
-                price: model.cost;
-                available: model.availability == "available";
-                anchors.topMargin: 10;
-                
-                
-                Component.onCompleted: {
-                    console.log("Rendering marketplace list item " + model.id);
-                    console.log(marketBrowseModel.count);
-                }
-                
+                item_id: model.id
+
+                anchors.topMargin: 10
+
+                image_url:model.thumbnail_url
+                name: model.title
+                likes: model.likes
+                liked: model.has_liked
+                creator: model.creator
+                category: model.primary_category
+                price: model.cost
+                available: model.availability == "available"
+
                 onShowItem: {
                     MarketplaceScriptingInterface.getMarketplaceItem(item_id);
                 }
-                
+
                 onBuy: {
                     sendToScript({method: 'marketplace_checkout', itemId: item_id});
                 }
-                
+
                 onCategoryClicked: {
                     root.categoryString = category;
                     categoriesText.text = category;                    
                     getMarketplaceItems();
                 }
-                
+
                 onRequestReload: getMarketplaceItems();
             }
+
             ScrollBar.vertical: ScrollBar {
-                parent: marketBrowseList.parent;
-                anchors.top: marketBrowseList.top;
-                anchors.bottom: marketBrowseList.bottom;
-                anchors.left: marketBrowseList.right;
-                contentItem.opacity: 1;
-            }            
-            headerPositioning: ListView.InlineHeader;
+                parent: marketBrowseList.parent
+
+                anchors {
+                    top: marketBrowseList.top
+                    bottom: marketBrowseList.bottom
+                    left: marketBrowseList.right
+                }
+
+                contentItem.opacity: 1
+            }
+            
+            headerPositioning: ListView.InlineHeader
+
             header: Item {
-                id: itemsHeading;
+                id: itemsHeading
                 
-                height: childrenRect.height;
-                width: parent.width;
+                height: childrenRect.height
+                width: parent.width
                 
                 Item {
-                    id: breadcrumbs;
-                    anchors.left: parent.left;
-                    anchors.right: parent.right;
-                    height: 34;
-                    visible: categoriesListView.currentIndex >= 0;
+                    id: breadcrumbs
+
+                    anchors.left: parent.left
+                    anchors.right: parent.right
+                    height: 34
+                    visible: categoriesListView.currentIndex >= 0
+
                     RalewayRegular {
-                        id: categoriesItemText;
-                        text: "Home /";
-                        anchors.leftMargin: 15;
-                        anchors.fill:parent;
-                        color: hifi.colors.blueHighlight;
-                        horizontalAlignment: Text.AlignLeft;
-                        verticalAlignment: Text.AlignVCenter;
-                        size: 18;
+                        id: categoriesItemText
+
+                        anchors.leftMargin: 15
+                        anchors.fill:parent
+
+                        text: "Home /"
+                        color: hifi.colors.blueHighlight
+                        horizontalAlignment: Text.AlignLeft
+                        verticalAlignment: Text.AlignVCenter
+                        size: 18
                     }
+
                     MouseArea {
-                        anchors.fill: parent;
+                        anchors.fill: parent
+                        
                         onClicked: {
                             categoriesListView.currentIndex = -1;
                             categoriesText.text = "Categories";
@@ -550,72 +580,91 @@ Rectangle {
                 }
                 
                 Item {
-                    id: searchScope;
-                    anchors.top: breadcrumbs.bottom;
-                    anchors.left: parent.left;
-                    anchors.right: parent.right;
-                    height: 50;
+                    id: searchScope
+                    
+                    anchors {
+                        top: breadcrumbs.bottom
+                        left: parent.left
+                        right: parent.right
+                    }
+                    height: 50
                     
                     RalewaySemiBold {
                         id: searchScopeText;
+
+                        anchors {
+                            leftMargin: 15
+                            fill:parent
+                            topMargin: 10
+                        }
+
                         text: "Featured";
-                        anchors.leftMargin: 15;
-                        anchors.fill:parent;
-                        anchors.topMargin: 10;
-                        color: hifi.colors.baseGray;
-                        horizontalAlignment: Text.AlignLeft;
-                        verticalAlignment: Text.AlignVCenter;
-                        size: 22;
+                        color: hifi.colors.baseGray
+                        horizontalAlignment: Text.AlignLeft
+                        verticalAlignment: Text.AlignVCenter
+                        size: 22
                     }
                 }
                 Item {
-                    id: sort;
-                    anchors.top: searchScope.bottom;
-                    anchors.left: parent.left;
-                    anchors.right: parent.right;
-                    anchors.topMargin: 10;
-                    anchors.leftMargin: 15;
-                    height: childrenRect.height;
+                    id: sort
+
+                    anchors {
+                        top: searchScope.bottom;
+                        left: parent.left;
+                        right: parent.right;
+                        topMargin: 10;
+                        leftMargin: 15;
+                    }
+                    height: childrenRect.height
+
                     RalewayRegular {
-                        id: sortText;
-                        text: "Sort:";
-                        anchors.leftMargin: 15;
-                        anchors.rightMargin: 20;
-                        height: 34;
-                        color: hifi.colors.lightGrayText;
-                        horizontalAlignment: Text.AlignLeft;
-                        verticalAlignment: Text.AlignVCenter;
-                        size: 14;
+                        id: sortText
+
+                        anchors.leftMargin: 15
+                        anchors.rightMargin: 20
+                        height: 34
+
+                        text: "Sort:"
+                        color: hifi.colors.lightGrayText
+                        horizontalAlignment: Text.AlignLeft
+                        verticalAlignment: Text.AlignVCenter
+                        size: 14
                     }
                     
                     Rectangle {
-                        radius: 4;
-                        border.width: 1;
-                        border.color: hifi.colors.faintGray;
-                        anchors.left: sortText.right;
-                        anchors.top: parent.top;
-                        width: 322;
-                        height: 36;
-                        anchors.leftMargin: 20;
-                        
-                        
+                        anchors {
+                            left: sortText.right
+                            top: parent.top
+                            leftMargin: 20
+                        }
+                        width: 322
+                        height: 36
+
+                        radius: 4
+                        border.width: 1
+                        border.color: hifi.colors.faintGray
+
                         ListModel {
-                            id: sortModel;
+                            id: sortModel
+
                             ListElement {
                                 name: "Name";
-                                glyph: ";";
-                                sortString: "alpha";
+                                glyph: ";"
+                                sortString: "alpha"
                             }
+
                             ListElement {
                                 name: "Date";
                                 glyph: ";";
                                 sortString: "recent";
                             }
+
                             ListElement {
                                 name: "Popular";
                                 glyph: ";";
                                 sortString: "likes";
                             }
+
                             ListElement {
                                 name: "My Likes";
                                 glyph: ";";
@@ -624,30 +673,34 @@ Rectangle {
                         }
                     
                         ListView {
-                            id: sortListView;
-                            anchors.top: parent.top;
-                            anchors.bottom: parent.bottom;
-                            anchors.left: parent.left;
-                            anchors.topMargin:1;
-                            anchors.bottomMargin:1;
-                            anchors.leftMargin:1;
-                            anchors.rightMargin:1;
-                            width: childrenRect.width;
-                            height: 34;
-                            orientation: ListView.Horizontal;
-                            focus: true;
-                            clip: true;
-                            highlightFollowsCurrentItem: false;
+                            id: sortListView
+
+                            anchors {
+                                top: parent.top
+                                bottom: parent.bottom
+                                left: parent.left
+                                topMargin:1
+                                bottomMargin:1
+                                leftMargin:1
+                                rightMargin:1
+                            }
+                            width: childrenRect.width
+                            height: 34
+
+                            orientation: ListView.Horizontal
+                            focus: true
+                            clip: true
+                            highlightFollowsCurrentItem: false
                             
                             delegate: SortButton {
-                                width: 80;
-                                height: parent.height;
-                                glyph: model.glyph;
-                                text: model.name;
+                                width: 80
+                                height: parent.height
+
+                                glyph: model.glyph
+                                text: model.name
                                 
-                                checked: {
-                                    ListView.isCurrentItem;
-                                }
+                                checked: ListView.isCurrentItem
+
                                 onClicked: {
                                     root.currentSortIndex = index;
                                     sortListView.positionViewAtIndex(index, ListView.Beginning);
@@ -657,12 +710,14 @@ Rectangle {
                                 }                            
                             }
                             highlight: Rectangle {
-                                width: 80;
-                                height: parent.height;
-                                color: hifi.colors.faintGray;
-                                x: sortListView.currentItem.x;
+                                width: 80
+                                height: parent.height
+
+                                color: hifi.colors.faintGray
+                                x: sortListView.currentItem.x
                             }
-                            model: sortModel;
+
+                            model: sortModel
                         } 
                     }
                 }
@@ -678,57 +733,62 @@ Rectangle {
     // ITEM START
     //     
     Item {
-        id: marketplaceItemView;
-        anchors.fill: parent;
-        width: parent.width;
-        anchors.topMargin: 120;
-        visible: false;
+        id: marketplaceItemView
+
+        anchors.fill: parent
+        anchors.topMargin: 120
+        width: parent.width
+
+        visible: false
         
         ScrollView {
-            id: marketplaceItemScrollView;
+            id: marketplaceItemScrollView
+
             anchors.fill: parent;
 
-            clip: true;
-            ScrollBar.vertical.policy: ScrollBar.AlwaysOn;
-            contentWidth: parent.width;
+            clip: true
+            ScrollBar.vertical.policy: ScrollBar.AlwaysOn
+            contentWidth: parent.width
 
             Rectangle {
-                id: marketplaceItemContent;
-                width: parent.width;
-                height: childrenRect.height + 100;
+                id: marketplaceItemContent
+
+                width: parent.width
+                height: childrenRect.height + 100
                 
-                // Title Bar text
                 RalewaySemiBold {
-                    id: backText;
-                    text: "Back";
-                    // Text size
-                    size: hifi.fontSizes.overlayTitle;
-                    // Anchors
-                    anchors.top: parent.top;
-                    anchors.left: parent.left
-                    anchors.leftMargin: 15;
-                    anchors.bottomMargin: 10;
-                    width: paintedWidth;
-                    height: paintedHeight;
-                    // Style
-                    color: hifi.colors.blueHighlight;
-                    // Alignment
-                    verticalAlignment: Text.AlignVCenter;
+                    id: backText
+
+                    anchors {
+                        top: parent.top
+                        left: parent.left
+                        leftMargin: 15
+                        bottomMargin: 10
+                    }
+                    width: paintedWidth
+                    height: paintedHeight
+
+                    text: "Back"
+                    size: hifi.fontSizes.overlayTitle
+                    color: hifi.colors.blueHighlight
+                    verticalAlignment: Text.AlignVCenter
                 }
+
                 MouseArea {
-                    anchors.fill: backText;
+                    anchors.fill: backText
                     
                     onClicked: {
                         getMarketplaceItems();
                     }
                 }
-                
+
                 MarketplaceItem {
-                    id: marketplaceItem;
-                    anchors.top: backText.bottom;
-                    width: parent.width;
-                    height: childrenRect.height;
-                    anchors.topMargin: 15;
+                    id: marketplaceItem
+
+                    anchors.topMargin: 15
+                    anchors.top: backText.bottom
+                    width: parent.width
+                    height: childrenRect.height
                     
                     onBuy: {
                         sendToScript({method: 'marketplace_checkout', itemId: item_id});
@@ -756,68 +816,77 @@ Rectangle {
     //
     
     Rectangle {
-        id: footer;
-        anchors.bottom: parent.bottom;
-        anchors.left: parent.left;
-        anchors.right: parent.right;
-        height: 50;
+        id: footer
+
+        anchors {
+            bottom: parent.bottom
+            left: parent.left
+            right: parent.right
+        }
+        height: 50
         
-        color: hifi.colors.blueHighlight;
+        color: hifi.colors.blueHighlight
 
         Item {
-            anchors.fill: parent;
-            anchors.rightMargin: 15;
-            anchors.leftMargin: 15;
-        
+            anchors {
+                fill: parent
+                rightMargin: 15
+                leftMargin: 15
+            }
+
             HiFiGlyphs {
-                id: footerGlyph;
-                text: hifi.glyphs.info;
-                // Size
-                size: 34;
-                // Anchors
-                anchors.left: parent.left;
-                anchors.top: parent.top;
-                anchors.bottom: parent.bottom;
-                
-                anchors.rightMargin: 10;
-                // Style
-                color: hifi.colors.white;
-                // Alignment
-                horizontalAlignment: Text.AlignHCenter;
-                verticalAlignment: Text.AlignVCenter;
+                id: footerGlyph
+
+                anchors {
+                    left: parent.left
+                    top: parent.top
+                    bottom: parent.bottom
+                    rightMargin: 10
+                }
+
+                text: hifi.glyphs.info
+                size: 34
+                color: hifi.colors.white
+                horizontalAlignment: Text.AlignHCenter
+                verticalAlignment: Text.AlignVCenter
             }
 
             RalewaySemiBold {
-                id: footerInfo;
-                text: "Get items from Clara.io!";
-                anchors.left: footerGlyph.right;
-                anchors.top: parent.top;
-                anchors.bottom: parent.bottom;
-                color: hifi.colors.white;
-                horizontalAlignment: Text.AlignLeft;
-                verticalAlignment: Text.AlignVCenter;
-                size: 18;
+                id: footerInfo
+
+                anchors {
+                    left: footerGlyph.right
+                    top: parent.top
+                    bottom: parent.bottom
+                }
+
+                text: "Get items from Clara.io!"
+                color: hifi.colors.white
+                horizontalAlignment: Text.AlignLeft
+                verticalAlignment: Text.AlignVCenter
+                size: 18
             }
             
             HifiControlsUit.Button {
-                text: "SEE ALL MARKETS";
-                anchors.right: parent.right;
-                anchors.top: parent.top;
-                anchors.bottom: parent.bottom;
-                anchors.topMargin: 10;
-                anchors.bottomMargin: 10;
-                anchors.leftMargin: 10;
-                anchors.rightMargin: 10;
-                width: 180;
-                
+                anchors {
+                    right: parent.right
+                    top: parent.top
+                    bottom: parent.bottom
+                    topMargin: 10
+                    bottomMargin: 10
+                    leftMargin: 10
+                    rightMargin: 10
+                }
+
+                text: "SEE ALL MARKETS"
+                width: 180
+
                 onClicked: {
                     sendToScript({method: 'marketplace_marketplaces'});
                 }
-            }   
+            }
         }  
     }
-    
-    
     //
     // FOOTER END
     //
@@ -827,46 +896,57 @@ Rectangle {
     // LICENSE START
     //    
     Rectangle {
-        id: licenseInfo;
-        anchors.fill: root;
-        anchors.topMargin: 100;
-        anchors.bottomMargin: 0;
-        visible: false;
-        
-        
-        HifiControlsUit.WebView {
-            id: licenseInfoWebView;
-            anchors.bottomMargin: 1;
-            anchors.topMargin: 50;
-            anchors.leftMargin: 1;
-            anchors.rightMargin: 1;
-            anchors.fill: parent;
+        id: licenseInfo
+
+        anchors {
+            fill: root
+            topMargin: 100
+            bottomMargin: 0
         }
-        Item {
-            id: licenseClose;
-            anchors.top: parent.top;
-            anchors.right: parent.right;
-            anchors.topMargin: 10;
-            anchors.rightMargin: 10;
+
+        visible: false;
+
+        HifiControlsUit.WebView {
+            id: licenseInfoWebView
             
-            width: 30;
-            height: 30;
+            anchors {
+                bottomMargin: 1
+                topMargin: 50
+                leftMargin: 1
+                rightMargin: 1
+                fill: parent
+            }
+        }
+
+        Item {
+            id: licenseClose
+
+            anchors {
+                top: parent.top
+                right: parent.right
+                topMargin: 10
+                rightMargin: 10
+            }
+            width: 30
+            height: 30
+
             HiFiGlyphs {
-                anchors.fill: parent;
-                height: 30;
-                text: hifi.glyphs.close;
-                // Size
-                size: 34;
-                // Anchors
-                anchors.rightMargin: 0;
-                anchors.verticalCenter: parent.verticalCenter;
-                horizontalAlignment: Text.AlignHCenter;
-                // Style
-                color: hifi.colors.baseGray;
+                anchors {
+                    fill: parent
+                    rightMargin: 0
+                    verticalCenter: parent.verticalCenter
+                }
+                height: 30
+
+                text: hifi.glyphs.close
+                size: 34
+                horizontalAlignment: Text.AlignHCenter
+                color: hifi.colors.baseGray
             }
             
             MouseArea {
-                anchors.fill: licenseClose;
+                anchors.fill: licenseClose
+
                 onClicked: licenseInfo.visible = false;
             }
         }
@@ -875,6 +955,19 @@ Rectangle {
     // LICENSE END
     //        
 
+    HifiControlsUit.Keyboard {
+        id: keyboard
+
+        anchors {
+            bottom: parent.bottom
+            left: parent.left
+            right: parent.right
+        }
+
+        raised: parent.keyboardEnabled && parent.keyboardRaised
+        numeric: false
+    }
+    
     //
     // Function Name: fromScript()
     //
@@ -890,7 +983,6 @@ Rectangle {
     //
 
     function fromScript(message) {
-        console.log("FROM SCRIPT " + JSON.stringify(message));
         switch (message.method) {
             case 'updateMarketplaceQMLItem':
                 if (!message.params.itemId) {
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
index a7f2991920..9b95b36918 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
@@ -25,37 +25,39 @@ import "../.." as HifiCommon
 
 Rectangle {
     id: root;
-    property string item_id: "";
-    property string image_url: "";
-    property string name: "";
-    property int likes: 0;
-    property bool liked: false;
-    property string creator: "";
-    property var categories: [];
-    property int price: 0;
-    property var attributions: [];
-    property string description: "";
-    property string license: "";
-    property string posted: "";
-    property bool available: false;
-    property string created_at: "";
+
+    property string item_id: ""
+    property string image_url: ""
+    property string name: ""
+    property int likes: 0
+    property bool liked: false
+    property string creator: ""
+    property var categories: []
+    property int price: 0
+    property var attributions: []
+    property string description: ""
+    property string license: ""
+    property string posted: ""
+    property bool available: false
+    property string created_at: ""
     
     onCategoriesChanged: {
         categoriesListModel.clear();
         categories.forEach(function(category) {
-        console.log("category is " + category);
             categoriesListModel.append({"category":category});
         });
     }
 
-    signal buy();
-    signal categoryClicked(string category);
-    signal showLicense(string url);
+    signal buy()
+    signal categoryClicked(string category)
+    signal showLicense(string url)
     
-    HifiConstants { id: hifi; }
+    HifiConstants {
+        id: hifi 
+    }
     
     Connections {
-        target: MarketplaceScriptingInterface;
+        target: MarketplaceScriptingInterface
 
         onMarketplaceItemLikeResult: {
             if (result.status !== 'success') {
@@ -95,80 +97,91 @@ Rectangle {
         return a.toDateString() + " " + drawnHour + ':' + min + amOrPm;
     }
     
-    anchors.left: parent.left;
-    anchors.right: parent.right;
-    anchors.leftMargin: 15;
-    anchors.rightMargin: 15;
+    anchors {
+        left: parent.left;
+        right: parent.right;
+        leftMargin: 15;
+        rightMargin: 15;
+    }
     height: childrenRect.height;
-    
-    
+
     Rectangle {
-        id: header;
-        anchors.left: parent.left;
-        anchors.right: parent.right;
-        anchors.top: parent.top;
+        id: header
         
+        anchors {
+            left: parent.left;
+            right: parent.right;
+            top: parent.top;
+        }
         height: 50;
         
         RalewaySemiBold {
-            id: nameText;
-            text: root.name;
-            // Text size
-            size: 24;
-            // Anchors
-            anchors.top: parent.top;
-            anchors.left: parent.left;
-            anchors.bottom: parent.bottom;
-            width: paintedWidth;
-            // Style
-            color: hifi.colors.baseGray;
-            // Alignment
-            verticalAlignment: Text.AlignVCenter;
+            id: nameText
+
+            anchors {
+                top: parent.top
+                left: parent.left
+                bottom: parent.bottom
+            }
+            width: paintedWidth
+            
+            text: root.name
+            size: 24
+            color: hifi.colors.baseGray
+            verticalAlignment: Text.AlignVCenter
         }
+
         Item {
-            id: likes;
-            anchors.top: parent.top;
-            anchors.right: parent.right;
-            anchors.bottom: parent.bottom;
-            anchors.rightMargin: 5;
-            
+            id: likes
+
+            anchors {
+                top: parent.top;
+                right: parent.right;
+                bottom: parent.bottom;
+                rightMargin: 5;
+            }
+
             RalewaySemiBold {
-                id: heart;
-                text: "\u2665";
-                // Size
-                size: 20;
-                // Anchors
-                anchors.top: parent.top;
-                anchors.right: parent.right;
-                anchors.rightMargin: 0;
-                anchors.verticalCenter: parent.verticalCenter;
-                horizontalAlignment: Text.AlignHCenter;
-                // Style
-                color: root.liked ? hifi.colors.redAccent  : hifi.colors.lightGrayText;
+                id: heart
+
+                anchors {
+                    top: parent.top
+                    right: parent.right
+                    rightMargin: 0
+                    verticalCenter: parent.verticalCenter
+                }
+                
+                text: "\u2665"
+                size: 20
+                horizontalAlignment: Text.AlignHCenter
+                color: root.liked ? hifi.colors.redAccent  : hifi.colors.lightGrayText
             }
             
             RalewaySemiBold {
-                id: likesText;
-                text: root.likes;
-                // Text size
-                size: hifi.fontSizes.overlayTitle;
-                // Anchors
-                anchors.top: parent.top;
-                anchors.right: heart.left;
-                anchors.rightMargin: 5;
-                anchors.leftMargin: 15;
-                anchors.bottom: parent.bottom;
-                width: paintedWidth;
-                // Style
-                color: hifi.colors.baseGray;
-                // Alignment
-                verticalAlignment: Text.AlignVCenter;
+                id: likesText
+
+                anchors {
+                    top: parent.top
+                    right: heart.left
+                    rightMargin: 5
+                    leftMargin: 15
+                    bottom: parent.bottom
+                }
+                width: paintedWidth
+
+                text: root.likes
+                size: hifi.fontSizes.overlayTitle
+                color: hifi.colors.baseGray
+                verticalAlignment: Text.AlignVCenter
             }
+
             MouseArea {
-                anchors.left: likesText.left;
-                anchors.right: heart.right;
-                anchors.top: likesText.top;
-                anchors.bottom: likesText.bottom;
+                anchors {
+                    left: likesText.left
+                    right: heart.right
+                    top: likesText.top
+                    bottom: likesText.bottom
+                }
                 
                 onClicked: {
                     MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, !root.liked);
@@ -176,178 +189,195 @@ Rectangle {
             }                
         }
     }
+
     Image {
-        id: itemImage;
-        source: root.image_url;
-        anchors.top: header.bottom;
-        anchors.left: parent.left;
-        anchors.right: parent.right;
+        id: itemImage
+
+        anchors {
+            top: header.bottom
+            left: parent.left
+            right: parent.right
+        }
         height: width*0.5625
+
+        source: root.image_url
         fillMode: Image.PreserveAspectCrop;
     }
+
     Item {
-        id: footer;
-        anchors.left: parent.left;
-        anchors.right: parent.right;
-        anchors.top: itemImage.bottom;
-        height: childrenRect.height;
+        id: footer
+
+        anchors {
+            left: parent.left;
+            right: parent.right;
+            top: itemImage.bottom;
+        }
+        height: childrenRect.height
         
         HifiControlsUit.Button {
-            id: buyButton;
-            text: root.available ? (root.price ? root.price : "FREE") : "UNAVAILABLE (not for sale)";
-            enabled: root.available;
-            buttonGlyph: root.available ? (root.price ? hifi.glyphs.hfc : "") : "";
-            anchors.right: parent.right;
-            anchors.top: parent.top;
-            anchors.left: parent.left;
-            anchors.topMargin: 15;
-            height: 50;
-            color: hifi.buttons.blue;
+            id: buyButton
+
+            anchors {
+                right: parent.right
+                top: parent.top
+                left: parent.left
+                topMargin: 15
+            }
+            height: 50 
+
+            text: root.available ? (root.price ? root.price : "FREE") : "UNAVAILABLE (not for sale)"
+            enabled: root.available
+            buttonGlyph: root.available ? (root.price ? hifi.glyphs.hfc : "") : ""
+            color: hifi.buttons.blue
             
             onClicked: root.buy();
         }        
         
         Item {
-            id: creatorItem;
-            anchors.top: buyButton.bottom;
-            anchors.leftMargin: 15;
-            anchors.topMargin: 15;
-            width: parent.width;
-            height: childrenRect.height;
+            id: creatorItem
+
+            anchors {
+                top: buyButton.bottom
+                leftMargin: 15
+                topMargin: 15
+            }
+            width: parent.width
+            height: childrenRect.height
             
             RalewaySemiBold {
-                id: creatorLabel;
-                text: "CREATOR:";
-                // Text size
-                size: 14;
-                // Anchors
-                anchors.top: parent.top;
-                anchors.left: parent.left;
-                width: paintedWidth;
-                // Style
-                color: hifi.colors.lightGrayText;
-                // Alignment
-                verticalAlignment: Text.AlignVCenter;
+                id: creatorLabel
+                
+                anchors.top: parent.top
+                anchors.left: parent.left
+                width: paintedWidth
+                
+                text: "CREATOR:"
+                size: 14
+                color: hifi.colors.lightGrayText
+                verticalAlignment: Text.AlignVCenter
             }
             
             RalewaySemiBold {
-                id: creatorText;
-                text: root.creator;
-                // Text size
-                size: 18;
-                // Anchors
-                anchors.top: creatorLabel.bottom;
-                anchors.left: parent.left;
-                anchors.topMargin: 10;
-                width: paintedWidth;
-                // Style
-                color: hifi.colors.blueHighlight;
-                // Alignment
-                verticalAlignment: Text.AlignVCenter;
+                id: creatorText
+                
+                anchors {
+                    top: creatorLabel.bottom
+                    left: parent.left
+                    topMargin: 10
+                }
+                width: paintedWidth              
+                text: root.creator
+
+                size: 18
+                color: hifi.colors.blueHighlight
+                verticalAlignment: Text.AlignVCenter
             }
         }
         
         Item {
-            id: posted;
-            anchors.top: creatorItem.bottom;
-            anchors.leftMargin: 15;
-            anchors.topMargin: 15;
-            width: parent.width;
-            height: childrenRect.height;
-            RalewaySemiBold {
-                id: postedLabel;
-                text: "POSTED:";
-                // Text size
-                size: 14;
-                // Anchors
-                anchors.top: parent.top;
-                anchors.left: parent.left;
+            id: posted
+            
+            anchors {
+                top: creatorItem.bottom
+                leftMargin: 15
+                topMargin: 15
+            }
+            width: parent.width
+            height: childrenRect.height
 
-                width: paintedWidth;
-                // Style
-                color: hifi.colors.lightGrayText;
-                // Alignment
-                verticalAlignment: Text.AlignVCenter;
+            RalewaySemiBold {
+                id: postedLabel
+
+                anchors.top: parent.top
+                anchors.left: parent.left
+                width: paintedWidth
+                
+                text: "POSTED:"
+                size: 14
+                color: hifi.colors.lightGrayText
+                verticalAlignment: Text.AlignVCenter
             }
             
             RalewaySemiBold {
-                id: created_at;
+                id: created_at
+
+                anchors {
+                    top: postedLabel.bottom
+                    left: parent.left
+                    right: parent.right
+                    topMargin: 10
+                }
+
                 text: { getFormattedDate(root.created_at); }
-                // Text size
-                size: 14;
-                // Anchors
-                anchors.top: postedLabel.bottom;
-                anchors.left: parent.left;
-                anchors.right: parent.right;
-                anchors.topMargin: 10;
-                // Style
-                color: hifi.colors.lightGray;
-                // Alignment
-                verticalAlignment: Text.AlignVCenter;
+                size: 14
+                color: hifi.colors.lightGray
+                verticalAlignment: Text.AlignVCenter
             }           
         }
         
         Rectangle {
-            anchors.top: posted.bottom;
-            anchors.leftMargin: 15;
-            anchors.topMargin: 15;
+            anchors {
+                top: posted.bottom;
+                leftMargin: 15;
+                topMargin: 15;
+            }
             width: parent.width;
             height: 1;
+
             color: hifi.colors.lightGray;
         }
 
         Item {
             id: licenseItem;
-            anchors.top: posted.bottom;
-            anchors.left: parent.left;
-            anchors.topMargin: 30;
-            width: parent.width;  
-            height: childrenRect.height;
+            
+            anchors {
+                top: posted.bottom
+                left: parent.left
+                topMargin: 30
+            }
+            width: parent.width
+            height: childrenRect.height
+
             RalewaySemiBold {
-                id: licenseLabel;
-                text: "SHARED UNDER:";
-                // Text size
-                size: 14;
-                // Anchors
+                id: licenseLabel
+
                 anchors.top: parent.top;
                 anchors.left: parent.left;
                 width: paintedWidth;
-                // Style
+
+                text: "SHARED UNDER:";
+                size: 14;
                 color: hifi.colors.lightGrayText;
-                // Alignment
                 verticalAlignment: Text.AlignVCenter;
             }
-            
+
             RalewaySemiBold {
-                id: licenseText;
-                text: root.license;
-                // Text size
-                size: 14;
-                // Anchors
-                anchors.top: licenseLabel.bottom;
-                anchors.left: parent.left;
-                width: paintedWidth;
-                // Style
-                color: hifi.colors.lightGray;
-                // Alignment
-                verticalAlignment: Text.AlignVCenter;
+                id: licenseText
+                
+                anchors.top: licenseLabel.bottom
+                anchors.left: parent.left
+                width: paintedWidth
+
+                text: root.license
+                size: 14
+                color: hifi.colors.lightGray
+                verticalAlignment: Text.AlignVCenter
             }
+
             RalewaySemiBold {
-                id: licenseHelp;
-                text: "More about this license";
-                // Text size
-                size: 14;
-                // Anchors
+                id: licenseHelp
+
                 anchors.top: licenseText.bottom;
                 anchors.left: parent.left;
                 width: paintedWidth;
-                // Style
-                color: hifi.colors.blueHighlight;
-                // Alignment
-                verticalAlignment: Text.AlignVCenter;
+
+                text: "More about this license"
+                size: 14
+                color: hifi.colors.blueHighlight
+                verticalAlignment: Text.AlignVCenter
                 
                 MouseArea {
-                    anchors.fill: parent;
+                    anchors.fill: parent
                     
                     onClicked: { 
                         licenseInfo.visible = true;
@@ -371,7 +401,6 @@ Rectangle {
                         }
                         if(url) {
                             licenseInfoWebView.url = url;
-                            
                         }
                     }
                 }
@@ -379,91 +408,97 @@ Rectangle {
         }
 
         Item {
-            id: descriptionItem;
-            anchors.top: licenseItem.bottom;
-            anchors.topMargin: 15;
-            anchors.left: parent.left;
-            anchors.right: parent.right;
-            height: childrenRect.height;            
-            RalewaySemiBold {
-                id: descriptionLabel;
-                text: "DESCRIPTION:";
-                // Text size
-                size: 14;
-                // Anchors
-                anchors.top: parent.top;
-                anchors.left: parent.left;
-                width: paintedWidth;
-                // Style
-                color: hifi.colors.lightGrayText;
-                // Alignment
-                verticalAlignment: Text.AlignVCenter;
+            id: descriptionItem
+
+            anchors {
+                top: licenseItem.bottom
+                topMargin: 15
+                left: parent.left
+                right: parent.right
             }
+            height: childrenRect.height
+
             RalewaySemiBold {
-                id: descriptionText;
-                text: root.description;
-                // Text size
-                size: 14;
-                // Anchors
-                anchors.top: descriptionLabel.bottom;
-                anchors.left: parent.left;
-                width: parent.width;
-                // Style
-                color: hifi.colors.lightGray;
-                // Alignment
-                verticalAlignment: Text.AlignVCenter;
-                wrapMode: Text.Wrap;
+                id: descriptionLabel
+                
+                anchors.top: parent.top
+                anchors.left: parent.left
+                width: paintedWidth
+
+                text: "DESCRIPTION:"
+                size: 14
+                color: hifi.colors.lightGrayText
+                verticalAlignment: Text.AlignVCenter
+            }
+
+            RalewaySemiBold {
+                id: descriptionText
+                
+                anchors.top: descriptionLabel.bottom
+                anchors.left: parent.left
+                width: parent.width
+
+                text: root.description
+                size: 14
+                color: hifi.colors.lightGray
+                verticalAlignment: Text.AlignVCenter
+                wrapMode: Text.Wrap
             }
         }
         
         Item {
-            id: categoriesList;
-            anchors.top: descriptionItem.bottom;
-            anchors.topMargin: 15;
-            anchors.left: parent.left;
-            anchors.right: parent.right;            
-            width: parent.width;
-            height: childrenRect.height;
+            id: categoriesList
+
+            anchors {
+                top: descriptionItem.bottom
+                topMargin: 15
+                left: parent.left
+                right: parent.right
+            }
+            width: parent.width
+            height: childrenRect.height
+
             RalewaySemiBold {
-                id: categoryLabel;
-                text: "IN:";
-                // Text size
-                size: 14;
-                // Anchors
-                anchors.top: parent.top;
-                anchors.left: parent.left;
-                width: paintedWidth;
-                // Style
-                color: hifi.colors.lightGrayText;
-                // Alignment
-                verticalAlignment: Text.AlignVCenter;
+                id: categoryLabel
+                
+                anchors.top: parent.top
+                anchors.left: parent.left
+                width: paintedWidth
+
+                text: "IN:"
+                size: 14
+                color: hifi.colors.lightGrayText
+                verticalAlignment: Text.AlignVCenter
             }
             ListModel {
-                id: categoriesListModel;
+                id: categoriesListModel
             }
             
             ListView {
-                anchors.left: parent.left;
-                anchors.right: parent.right;
-                anchors.top: categoryLabel.bottom;
-                model: categoriesListModel;
-                height: 20*model.count;
+                anchors {
+                    left: parent.left;
+                    right: parent.right;
+                    top: categoryLabel.bottom;
+                }
+
+                height: 20*model.count
+
+                model: categoriesListModel
                 delegate: RalewaySemiBold {
-                    id: categoryText;
-                    text: model.category;
-                    // Text size
-                    size: 14;
-                    // Anchors
-                    anchors.leftMargin: 15;
-                    width: paintedWidth;
-                    height: 20;
-                    // Style
-                    color: hifi.colors.blueHighlight;
-                    // Alignment
-                    verticalAlignment: Text.AlignVCenter;
+                    id: categoryText
+
+                    anchors.leftMargin: 15
+                    width: paintedWidth
+
+                    text: model.category
+                    size: 14
+                    height: 20
+                    color: hifi.colors.blueHighlight
+                    verticalAlignment: Text.AlignVCenter
                     
                     MouseArea {
-                        anchors.fill: categoryText;
+                        anchors.fill: categoryText
+
                         onClicked: root.categoryClicked(model.category);
                     }
                 }
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
index 4b8471e8b9..46e6a9d920 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
@@ -24,98 +24,115 @@ import "../common/sendAsset"
 import "../.." as HifiCommon
 
 Rectangle {
-    id: root;
-    property string item_id: "";
-    property string image_url: "";
-    property string name: "";
-    property int likes: 0;
-    property bool liked: false;
-    property string creator: "";
-    property string category: "";
-    property int price: 0;
-    property bool available: false;
+    id: root
 
-    signal buy();
-    signal showItem();
-    signal categoryClicked(string category);
-    signal requestReload();
+    property string item_id: ""
+    property string image_url: ""
+    property string name: ""
+    property int likes: 0
+    property bool liked: false
+    property string creator: ""
+    property string category: ""
+    property int price: 0
+    property bool available: false
+
+    signal buy()
+    signal showItem()
+    signal categoryClicked(string category)
+    signal requestReload()
     
-    HifiConstants { id: hifi; }
+    HifiConstants { id: hifi }
 
-    width: parent.width;
-    height: childrenRect.height+50;
+    width: parent.width
+    height: childrenRect.height+50
     
     DropShadow {
-        anchors.fill: shadowBase;
-        source: shadowBase;
-        verticalOffset: 4;
-        horizontalOffset: 4;
-        radius: 6;
-        samples: 9;
-        color: hifi.colors.baseGrayShadow;
+        anchors.fill: shadowBase
+
+        source: shadowBase
+        verticalOffset: 4
+        horizontalOffset: 4
+        radius: 6
+        samples: 9
+        color: hifi.colors.baseGrayShadow
     }
     
     Rectangle {
-        id: shadowBase;
-        anchors.fill: itemRect;
-        color: "white";
+        id: shadowBase
+
+        anchors.fill: itemRect
+
+        color: "white"
     }
     
     Rectangle {
-        id: itemRect;
-        height: childrenRect.height;
-        anchors.left: parent.left;
-        anchors.right: parent.right;
-        anchors.top: parent.top;
-        anchors.topMargin: 20;
-        anchors.bottomMargin: 10;
-        anchors.leftMargin: 15;
-        anchors.rightMargin: 15;        
+        id: itemRect
         
+        anchors {
+            left: parent.left
+            right: parent.right
+            top: parent.top
+            topMargin: 20
+            bottomMargin: 10
+            leftMargin: 15
+            rightMargin: 15
+        }
+        height: childrenRect.height
+
         MouseArea {
-            anchors.fill: parent;
+            anchors.fill: parent
+
+            hoverEnabled: true
+
             onClicked: root.showItem();
-            onEntered: { hoverRect.visible = true; console.log("entered"); }
-            onExited: { hoverRect.visible = false; console.log("exited"); }
-            hoverEnabled: true           
+            onEntered: hoverRect.visible = true;
+            onExited: hoverRect.visible = false;
+       
         }
         
         Rectangle {
-            id: header;
-            anchors.left: parent.left;
-            anchors.right: parent.right;
-            anchors.top: parent.top;
-            
-            height: 50;
+            id: header
+
+            anchors {
+                left: parent.left
+                right: parent.right
+                top: parent.top
+            }
+            height: 50
             
             RalewaySemiBold {
-                id: nameText;
-                text: root.name;
-                // Text size
-                size: hifi.fontSizes.overlayTitle;
-                // Anchors
-                anchors.top: parent.top;
-                anchors.left: parent.left;
-                anchors.right: parent.right;
-                anchors.rightMargin: 50;
-                anchors.leftMargin: 15;
-                anchors.bottom: parent.bottom;
-                elide: Text.ElideRight;
-                width: paintedWidth;
-                // Style
-                color: hifi.colors.blueHighlight;
-                // Alignment
-                verticalAlignment: Text.AlignVCenter;
+                id: nameText
+
+                anchors {
+                    top: parent.top
+                    left: parent.left
+                    right: parent.right
+                    rightMargin: 50
+                    leftMargin: 15
+                    bottom: parent.bottom
+                }
+                width: paintedWidth
+
+                text: root.name
+                size: hifi.fontSizes.overlayTitle
+                elide: Text.ElideRight
+                color: hifi.colors.blueHighlight
+                verticalAlignment: Text.AlignVCenter
             }
+
             Item {
-                id: likes;
-                anchors.top: parent.top;
-                anchors.right: parent.right;
-                anchors.rightMargin: 15;
-                anchors.bottom: parent.bottom;
-                width: childrenRect.width;
+                id: likes
+
+                anchors {
+                    top: parent.top
+                    right: parent.right
+                    rightMargin: 15
+                    bottom: parent.bottom
+                }
+                width: heart.width + likesText.width
+
                 Connections {
-                    target: MarketplaceScriptingInterface;
+                    target: MarketplaceScriptingInterface
 
                     onMarketplaceItemLikeResult: {
                         if (result.status !== 'success') {
@@ -126,41 +143,47 @@ Rectangle {
                 }                
                 
                 RalewaySemiBold {
-                    id: heart;
-                    text: "\u2665";
-                    // Size
-                    size: 20;
-                    // Anchors
-                    anchors.top: parent.top;
-                    anchors.right: parent.right;
-                    anchors.rightMargin: 0;
-                    anchors.verticalCenter: parent.verticalCenter;
+                    id: heart
+                    
+                    anchors {
+                        top: parent.top
+                        right: parent.right
+                        rightMargin: 0
+                        verticalCenter: parent.verticalCenter
+                    }
+                    
+                    text: "\u2665"
+                    size: 20
                     horizontalAlignment: Text.AlignHCenter;
-                    // Style
-                    color: root.liked ? hifi.colors.redAccent  : hifi.colors.lightGrayText;
+                    color: root.liked ? hifi.colors.redAccent  : hifi.colors.lightGrayText
                 }
                 
                 RalewaySemiBold {
-                    id: likesText;
-                    text: root.likes;
-                    // Text size
-                    size: hifi.fontSizes.overlayTitle;
-                    // Anchors
-                    anchors.top: parent.top;
-                    anchors.right: heart.left;
-                    anchors.rightMargin: 5;
-                    anchors.leftMargin: 15;
-                    anchors.bottom: parent.bottom;
-                    width: paintedWidth;
-                    // Style
-                    color: hifi.colors.baseGray;
-                    // Alignment
-                    verticalAlignment: Text.AlignVCenter;
+                    id: likesText
+                    
+                    anchors {
+                        top: parent.top
+                        right: heart.left
+                        rightMargin: 5
+                        leftMargin: 15
+                        bottom: parent.bottom
+                    }
+                    width: paintedWidth
+
+                    text: root.likes
+                    size: hifi.fontSizes.overlayTitle
+                    color: hifi.colors.baseGray
+                    verticalAlignment: Text.AlignVCenter
                 }
+
                 MouseArea {
-                    anchors.fill: parent;
+                    anchors {
+                        left: likesText.left
+                        right: heart.right
+                        top: parent.top
+                        bottom: parent.bottom
+                    }
                     onClicked: {
-                        console.log("like " + root.item_id);
                         root.liked = !root.liked;
                         root.likes = root.liked ? root.likes + 1 : root.likes - 1;
                         MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, root.liked);
@@ -168,111 +191,128 @@ Rectangle {
                 }                
             }
         }
+
         Image {
-            id: itemImage;
-            source: root.image_url;
-            anchors.top: header.bottom;
-            anchors.left: parent.left;
-            anchors.right: parent.right;
+            id: itemImage
+            
+            anchors {
+                top: header.bottom
+                left: parent.left
+                right: parent.right
+            }
             height: width*0.5625
-            fillMode: Image.PreserveAspectCrop;
+
+            source: root.image_url
+            fillMode: Image.PreserveAspectCrop
         }
+
         Item {
-            id: footer;
-            anchors.left: parent.left;
-            anchors.right: parent.right;
-            anchors.top: itemImage.bottom;
-            height: 60;
+            id: footer
+
+            anchors {
+                left: parent.left
+                right: parent.right
+                top: itemImage.bottom
+            }
+            height: 60
             
             RalewaySemiBold {
-                id: creatorLabel;
-                text: "CREATOR:";
-                // Text size
-                size: 14;
-                // Anchors
-                anchors.top: parent.top;
-                anchors.left: parent.left;
-                anchors.leftMargin: 15;
-                width: paintedWidth;
-                // Style
-                color: hifi.colors.lightGrayText;
-                // Alignment
-                verticalAlignment: Text.AlignVCenter;
+                id: creatorLabel
+                
+                anchors {
+                    top: parent.top
+                    left: parent.left
+                    leftMargin: 15
+                }
+                width: paintedWidth
+                
+                text: "CREATOR:"
+                size: 14
+                color: hifi.colors.lightGrayText
+                verticalAlignment: Text.AlignVCenter
             }
             
             RalewaySemiBold {
-                id: creatorText;
+                id: creatorText
+
+                anchors {
+                    top: creatorLabel.top;
+                    left: creatorLabel.right;
+                    leftMargin: 15;
+                }
+                width: paintedWidth;
+
                 text: root.creator;
-                // Text size
                 size: 14;
-                // Anchors
-                anchors.top: creatorLabel.top;
-                anchors.left: creatorLabel.right;
-                anchors.leftMargin: 15;
-                width: paintedWidth;
-                // Style
                 color: hifi.colors.lightGray;
-                // Alignment
                 verticalAlignment: Text.AlignVCenter;
             }
+
             RalewaySemiBold {
-                id: categoryLabel;
+                id: categoryLabel
+                
+                anchors {
+                    top: creatorLabel.bottom
+                    left: parent.left
+                    leftMargin: 15
+                }
+                width: paintedWidth;      
+
                 text: "IN:";
-                // Text size
                 size: 14;
-                // Anchors
-                anchors.top: creatorLabel.bottom;
-                anchors.left: parent.left;
-                anchors.leftMargin: 15;
-                width: paintedWidth;
-                // Style
                 color: hifi.colors.lightGrayText;
-                // Alignment
                 verticalAlignment: Text.AlignVCenter;
             }
-            
+
             RalewaySemiBold {
-                id: categoryText;
-                text: root.category;
-                // Text size
-                size: 14;
-                // Anchors
-                anchors.top: categoryLabel.top;
-                anchors.left: categoryLabel.right;
-                anchors.leftMargin: 15;
-                width: paintedWidth;
-                // Style
+                id: categoryText
+
+                anchors {
+                    top: categoryLabel.top
+                    left: categoryLabel.right
+                    leftMargin: 15
+                }
+                width: paintedWidth
+
+                text: root.category
+                size: 14
                 color: hifi.colors.blueHighlight;
-                // Alignment
                 verticalAlignment: Text.AlignVCenter;
                 
                 MouseArea {
-                    anchors.fill: parent;
+                    anchors.fill: parent
+
                     onClicked: root.categoryClicked(root.category);
                 }
             }
 
             HifiControlsUit.Button {
-                text: root.price ? root.price : "FREE";
-                buttonGlyph: root.price ? hifi.glyphs.hfc : "";
-                anchors.right: parent.right;
-                anchors.top: parent.top;
-                anchors.bottom: parent.bottom;
-                anchors.rightMargin: 15;
-                anchors.topMargin:10;
-                anchors.bottomMargin: 10;
+                anchors {
+                    right: parent.right
+                    top: parent.top
+                    bottom: parent.bottom
+                    rightMargin: 15
+                    topMargin:10
+                    bottomMargin: 10
+                }
+
+                text: root.price ? root.price : "FREE"
+                buttonGlyph: root.price ? hifi.glyphs.hfc : ""
                 color: hifi.buttons.blue;
-                
+
                 onClicked: root.buy();
             }
         }
+
         Rectangle {
-            id: hoverRect;
-            anchors.fill: parent;
-            border.color: hifi.colors.blueHighlight;
-            border.width: 2;
-            color: "#00000000";
-            visible: false;
+            id: hoverRect
+
+            anchors.fill: parent
+
+            border.color: hifi.colors.blueHighlight
+            border.width: 2
+            color: "#00000000"
+            visible: false
         }        
     }
 }

From dfac0d88a25abd56cde17679dc3f08df379790a6 Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxanne@highfidelity.io>
Date: Fri, 25 Jan 2019 16:41:13 -0800
Subject: [PATCH 04/18] QmlMarketplace - Add 'logged out' behavior, fix issue
 with search scope display

---
 .../hifi/commerce/marketplace/Marketplace.qml | 178 ++++++++++++++++--
 .../commerce/marketplace/MarketplaceItem.qml  |  21 ++-
 .../marketplace/MarketplaceListItem.qml       |   9 +-
 interface/src/Application.cpp                 |   1 +
 scripts/system/marketplaces/marketplaces.js   |   2 +-
 5 files changed, 184 insertions(+), 27 deletions(-)

diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
index 77820ccaca..10b59ac83b 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
@@ -37,18 +37,42 @@ Rectangle {
     property string searchString: ""
     property bool keyboardEnabled: HMD.active
     property bool keyboardRaised: false
+    property string searchScopeString: "Featured"
+    property bool isLoggedIn: false;
     
     anchors.fill: (typeof parent === undefined) ? undefined : parent
 
     function getMarketplaceItems() {
         marketplaceItemView.visible = false;
-        itemsList.visible = true;          
+        itemsList.visible = true;
         marketBrowseModel.getFirstPage();
+        {
+        if(root.searchString !== undefined && root.searchString !== "") {
+                root.searchScopeString = "Search Results: \"" + root.searchString + "\"";
+            } else if (root.categoryString !== "") {
+                root.searchScopeString = root.categoryString;
+            } else {
+                root.searchScopeString = "Featured";
+            }
+        }
     }
-    
+
+    Component.onCompleted: {
+        Commerce.getLoginStatus();
+    }
+
     Component.onDestruction: {
         keyboard.raised = false;
     }
+
+    Connections {
+        target: GlobalServices
+
+        onMyUsernameChanged: {
+            console.log("LOGIN STATUS CHANGING");
+            Commerce.getLoginStatus();
+        }
+    }
     
     Connections {
         target: MarketplaceScriptingInterface
@@ -61,7 +85,7 @@ Rectangle {
                 categoriesModel.append({
                     id: -1,
                     name: "Everything"
-                });                
+                });
                 result.data.items.forEach(function(category) {
                     categoriesModel.append({
                         id: category.id,
@@ -84,14 +108,16 @@ Rectangle {
                 marketplaceItem.image_url = result.data.thumbnail_url;
                 marketplaceItem.name = result.data.title;
                 marketplaceItem.likes = result.data.likes;
-                marketplaceItem.liked = result.data.has_liked;
+                if(result.data.has_liked !== undefined) {
+                    marketplaceItem.liked = result.data.has_liked;
+                }
                 marketplaceItem.creator = result.data.creator;
                 marketplaceItem.categories = result.data.categories;
                 marketplaceItem.price = result.data.cost;
                 marketplaceItem.description = result.data.description;
                 marketplaceItem.attributions = result.data.attributions;
                 marketplaceItem.license = result.data.license;
-                marketplaceItem.available = result.data.availability == "available";
+                marketplaceItem.available = result.data.availability === "available";
                 marketplaceItem.created_at = result.data.created_at;
                 marketplaceItemScrollView.contentHeight = marketplaceItemContent.height;
                 itemsList.visible = false;
@@ -100,6 +126,15 @@ Rectangle {
         }
     }
 
+    Connections {
+        target: Commerce
+
+        onLoginStatusResult: {
+            root.isLoggedIn = isLoggedIn;
+            itemsLoginStatus.visible = !isLoggedIn;
+        }
+    }
+
     HifiCommerceCommon.CommerceLightbox {
         id: lightboxPopup
         visible: false
@@ -423,7 +458,6 @@ Rectangle {
                             getMarketplaceItems();
                         }
                     }
-                    
                 }
 
                 ScrollBar.vertical: ScrollBar {
@@ -452,12 +486,12 @@ Rectangle {
 
         anchors {
             fill: parent
-            topMargin: 100
+            topMargin: 120
             bottomMargin: 50
         }
-
+ 
         visible: true;
-        
+
         HifiModels.PSFListModel {
             id: marketBrowseModel
 
@@ -467,7 +501,7 @@ Rectangle {
 
             getPage: function () {
                 MarketplaceScriptingInterface.getMarketplaceItems(
-                    root.searchString == "" ? undefined : root.searchString,
+                    root.searchString === "" ? undefined : root.searchString,
                     "",
                     root.categoryString.toLowerCase(),
                     "",
@@ -503,11 +537,12 @@ Rectangle {
                 image_url:model.thumbnail_url
                 name: model.title
                 likes: model.likes
-                liked: model.has_liked
+                liked: model.has_liked ? model.has_liked : false
                 creator: model.creator
                 category: model.primary_category
                 price: model.cost
-                available: model.availability == "available"
+                available: model.availability === "available"
+                isLoggedIn: root.isLoggedIn;
 
                 onShowItem: {
                     MarketplaceScriptingInterface.getMarketplaceItem(item_id);
@@ -546,9 +581,65 @@ Rectangle {
                 height: childrenRect.height
                 width: parent.width
                 
+                Rectangle {
+                    id: itemsLoginStatus;
+                    anchors {
+                        left: parent.left
+                        right: parent.right
+                        leftMargin: 15
+                        rightMargin: 15
+                    }
+                    height: root.isLoggedIn ? 0 : 80
+
+                    visible: !root.isLoggedIn
+                    color: hifi.colors.greenHighlight
+                    border.color: hifi.colors.greenShadow
+                    border.width: 1
+                    radius: 4
+                    z: 10000
+
+                    HifiControlsUit.Button {
+                        id: loginButton;
+                        anchors {
+                            left: parent.left
+                            top: parent.top
+                            bottom: parent.bottom
+                            leftMargin: 15
+                            topMargin:10
+                            bottomMargin: 10
+                        }
+                        width: 80;
+
+                        text: root.price ? root.price : "LOG IN"
+
+                        onClicked: {
+                            sendToScript({method: 'needsLogIn_loginClicked'});
+                        }
+                    }
+
+                    RalewayRegular {
+                        id: itemsLoginText
+
+                        anchors {
+                            leftMargin: 15
+                            top: parent.top;
+                            bottom: parent.bottom;
+                            right: parent.right;
+                            left: loginButton.right
+                        }
+
+                        text: "to get items from the Marketplace."
+                        color: hifi.colors.baseGray
+                        horizontalAlignment: Text.AlignLeft
+                        verticalAlignment: Text.AlignVCenter
+                        size: 18
+                    }
+                }
+
                 Item {
                     id: breadcrumbs
 
+                    anchors.top: itemsLoginStatus.bottom;
                     anchors.left: parent.left
                     anchors.right: parent.right
                     height: 34
@@ -598,7 +689,7 @@ Rectangle {
                             topMargin: 10
                         }
 
-                        text: "Featured";
+                        text: searchScopeString
                         color: hifi.colors.baseGray
                         horizontalAlignment: Text.AlignLeft
                         verticalAlignment: Text.AlignVCenter
@@ -751,8 +842,64 @@ Rectangle {
             contentWidth: parent.width
 
             Rectangle {
-                id: marketplaceItemContent
+                id: itemLoginStatus;
+                anchors {
+                    left: parent.left
+                    right: parent.right
+                    leftMargin: 15
+                    rightMargin: 15
+                }
+                height: root.isLoggedIn ? 0 : 80
 
+                visible: !root.isLoggedIn
+                color: hifi.colors.greenHighlight
+                border.color: hifi.colors.greenShadow
+                border.width: 1
+                radius: 4
+                z: 10000
+
+                HifiControlsUit.Button {
+                    id: loginButton;
+                    anchors {
+                        left: parent.left
+                        top: parent.top
+                        bottom: parent.bottom
+                        leftMargin: 15
+                        topMargin:10
+                        bottomMargin: 10
+                    }
+                    width: 80;
+
+                    text: root.price ? root.price : "LOG IN"
+
+                    onClicked: {
+                        sendToScript({method: 'needsLogIn_loginClicked'});
+                    }
+                }
+
+                RalewayRegular {
+                    id: itemsLoginText
+
+                    anchors {
+                        leftMargin: 15
+                        top: parent.top;
+                        bottom: parent.bottom;
+                        right: parent.right;
+                        left: loginButton.right
+                    }
+
+                    text: "to get items from the Marketplace."
+                    color: hifi.colors.baseGray
+                    horizontalAlignment: Text.AlignLeft
+                    verticalAlignment: Text.AlignVCenter
+                    size: 18
+                }
+            }
+
+
+            Rectangle {
+                id: marketplaceItemContent
+                anchors.top: itemLoginStatus.bottom;
                 width: parent.width
                 height: childrenRect.height + 100
                 
@@ -785,11 +932,14 @@ Rectangle {
                 MarketplaceItem {
                     id: marketplaceItem
 
+
                     anchors.topMargin: 15
                     anchors.top: backText.bottom
                     width: parent.width
                     height: childrenRect.height
                     
+                    isLoggedIn: root.isLoggedIn;
+
                     onBuy: {
                         sendToScript({method: 'marketplace_checkout', itemId: item_id});
                     }
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
index 9b95b36918..5795d0d67d 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
@@ -40,6 +40,7 @@ Rectangle {
     property string posted: ""
     property bool available: false
     property string created_at: ""
+    property bool isLoggedIn: false;
     
     onCategoriesChanged: {
         categoriesListModel.clear();
@@ -184,7 +185,9 @@ Rectangle {
                 }
                 
                 onClicked: {
-                    MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, !root.liked);
+                    if (isLoggedIn) {
+                        MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, !root.liked);
+                    }
                 }
             }                
         }
@@ -382,21 +385,21 @@ Rectangle {
                     onClicked: { 
                         licenseInfo.visible = true;
                         var url;
-                        if (root.license == "No Rights Reserved (CC0)") {
+                        if (root.license === "No Rights Reserved (CC0)") {
                           url = "https://creativecommons.org/publicdomain/zero/1.0/"
-                        } else if (root.license == "Attribution (CC BY)") {
+                        } else if (root.license === "Attribution (CC BY)") {
                           url = "https://creativecommons.org/licenses/by/4.0/"
-                        } else if (root.license == "Attribution-ShareAlike (CC BY-SA)") {
+                        } else if (root.license === "Attribution-ShareAlike (CC BY-SA)") {
                           url = "https://creativecommons.org/licenses/by-sa/4.0/"
-                        } else if (root.license == "Attribution-NoDerivs (CC BY-ND)") {
+                        } else if (root.license === "Attribution-NoDerivs (CC BY-ND)") {
                           url = "https://creativecommons.org/licenses/by-nd/4.0/"
-                        } else if (root.license == "Attribution-NonCommercial (CC BY-NC)") {
+                        } else if (root.license === "Attribution-NonCommercial (CC BY-NC)") {
                           url = "https://creativecommons.org/licenses/by-nc/4.0/"
-                        } else if (root.license == "Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)") {
+                        } else if (root.license === "Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)") {
                           url = "https://creativecommons.org/licenses/by-nc-sa/4.0/"
-                        } else if (root.license == "Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)") {
+                        } else if (root.license === "Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)") {
                           url = "https://creativecommons.org/licenses/by-nc-nd/4.0/"
-                        } else if (root.license == "Proof of Provenance License (PoP License)") {
+                        } else if (root.license === "Proof of Provenance License (PoP License)") {
                           url = "https://digitalassetregistry.com/PoP-License/v1/"
                         }
                         if(url) {
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
index 46e6a9d920..eb99106cf4 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
@@ -35,6 +35,7 @@ Rectangle {
     property string category: ""
     property int price: 0
     property bool available: false
+    property bool isLoggedIn: false;
 
     signal buy()
     signal showItem()
@@ -184,9 +185,11 @@ Rectangle {
                         bottom: parent.bottom
                     }
                     onClicked: {
-                        root.liked = !root.liked;
-                        root.likes = root.liked ? root.likes + 1 : root.likes - 1;
-                        MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, root.liked);
+                        if(isLoggedIn) {
+                            root.liked = !root.liked;
+                            root.likes = root.liked ? root.likes + 1 : root.likes - 1;
+                            MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, root.liked);
+                        }
                     }
                 }                
             }
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 2f0ca7ea2e..7ae7ebf65c 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -2990,6 +2990,7 @@ void Application::initializeUi() {
         QUrl{ "hifi/dialogs/security/SecurityImageModel.qml" },
         QUrl{ "hifi/dialogs/security/SecurityImageSelection.qml" },
         QUrl{ "hifi/tablet/TabletMenu.qml" },
+        QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
     }, commerceCallback);
 
     QmlContextCallback marketplaceCallback = [](QQmlContext* context) {
diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js
index 2ca79b49f0..655d286049 100644
--- a/scripts/system/marketplaces/marketplaces.js
+++ b/scripts/system/marketplaces/marketplaces.js
@@ -613,7 +613,7 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
     case 'passphrasePopup_cancelClicked':
     case 'needsLogIn_cancelClicked':
         // Should/must NOT check for wallet setup.
-        ui.open(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL);
+        openMarketplace();
         break;
     case 'needsLogIn_loginClicked':
         openLoginWindow();

From 8357f1f9b5cbc792ff0ad45cbb4de752452e0f4b Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxanne@highfidelity.io>
Date: Mon, 28 Jan 2019 11:10:35 -0800
Subject: [PATCH 05/18] CR fixes

---
 .../resources/qml/hifi/commerce/marketplace/Marketplace.qml   | 4 +++-
 .../qml/hifi/commerce/marketplace/MarketplaceListItem.qml     | 4 ++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
index 10b59ac83b..cbeedfbb79 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
@@ -728,7 +728,7 @@ Rectangle {
                             top: parent.top
                             leftMargin: 20
                         }
-                        width: 322
+                        width: root.isLoggedIn ? 322 : 242
                         height: 36
 
                         radius: 4
@@ -790,6 +790,8 @@ Rectangle {
                                 glyph: model.glyph
                                 text: model.name
                                 
+                                visible: root.isLoggedIn || model.sortString != "my_likes"
+
                                 checked: ListView.isCurrentItem
 
                                 onClicked: {
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
index eb99106cf4..2f37637e40 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
@@ -185,7 +185,7 @@ Rectangle {
                         bottom: parent.bottom
                     }
                     onClicked: {
-                        if(isLoggedIn) {
+                        if (isLoggedIn) {
                             root.liked = !root.liked;
                             root.likes = root.liked ? root.likes + 1 : root.likes - 1;
                             MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, root.liked);
@@ -203,7 +203,7 @@ Rectangle {
                 left: parent.left
                 right: parent.right
             }
-            height: width*0.5625
+            height: width * 0.5625
 
             source: root.image_url
             fillMode: Image.PreserveAspectCrop

From 2fe78383edc9c2f268b0a1ebaf9c4f1a97034257 Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxanne@highfidelity.io>
Date: Mon, 28 Jan 2019 15:14:49 -0800
Subject: [PATCH 06/18] QmlMarketplace - Fix issues found during test * command
 line --url hifiapp:MARKET was not bringing up correct marketplace * Search
 field was not being cleared when 'home' button was clicked after a search *
 tablet button was not lit when marketplace was launched

---
 .../resources/qml/hifi/commerce/marketplace/Marketplace.qml    | 1 +
 interface/src/commerce/QmlCommerce.cpp                         | 3 +--
 scripts/system/marketplaces/marketplaces.js                    | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
index cbeedfbb79..8ba5d0bac0 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
@@ -665,6 +665,7 @@ Rectangle {
                             categoriesListView.currentIndex = -1;
                             categoriesText.text = "Categories";
                             root.categoryString = "";
+                            searchField.text = "";
                             getMarketplaceItems();
                         }
                     }
diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp
index 5236c5a7fb..046e697b6d 100644
--- a/interface/src/commerce/QmlCommerce.cpp
+++ b/interface/src/commerce/QmlCommerce.cpp
@@ -54,11 +54,10 @@ void QmlCommerce::openSystemApp(const QString& appName) {
         {"GOTO",        "hifi/tablet/TabletAddressDialog.qml"},
         {"PEOPLE",      "hifi/Pal.qml"},
         {"WALLET",      "hifi/commerce/wallet/Wallet.qml"},
-        {"MARKET",      "/marketplace.html"}
+        {"MARKET",      "hifi/commerce/marketplace/Marketplace.qml"}
     };
 
     static const QMap<QString, QString> systemInject{
-        {"MARKET",      "/scripts/system/html/js/marketplacesInject.js"}
     };
 
 
diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js
index 655d286049..9d571d9284 100644
--- a/scripts/system/marketplaces/marketplaces.js
+++ b/scripts/system/marketplaces/marketplaces.js
@@ -703,7 +703,7 @@ var onMarketplaceScreen = false;
 var onWalletScreen = false;
 var onTabletScreenChanged = function onTabletScreenChanged(type, url) {
     ui.setCurrentVisibleScreenMetadata(type, url);
-    onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1;
+    onMarketplaceScreen = type === "QML" && url.indexOf(MARKETPLACE_QML_PATH) !== -1;
     onInspectionCertificateScreen = type === "QML" && url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1;
     var onWalletScreenNow = url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1;
     var onCommerceScreenNow = type === "QML" && (

From 0fdbca8ade2d24f931b9e6537241cb36cab97cc0 Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxanne@highfidelity.io>
Date: Tue, 29 Jan 2019 16:07:27 -0800
Subject: [PATCH 07/18] QmlMarketplace Bugfixes * Fix upgrades * Certificate
 'View in Marketplace' wasn't working * command-line hifiapp:MARKET wasn't
 launching * Home link wasn't disappearing where it should * Log In button on
 marketplace wasn't working * Other minor UI bugfixes

---
 .../qml/hifi/commerce/checkout/Checkout.qml   |   2 +-
 .../common/EmulatedMarketplaceHeader.qml      | 163 +-----------------
 .../InspectionCertificate.qml                 |  12 +-
 .../hifi/commerce/marketplace/Marketplace.qml |  52 +++---
 .../commerce/marketplace/MarketplaceItem.qml  |   5 +-
 .../hifi/commerce/purchases/PurchasedItem.qml |  24 +--
 .../qml/hifi/commerce/purchases/Purchases.qml |  59 +------
 .../qml/hifi/commerce/wallet/WalletHome.qml   |   2 +-
 scripts/system/commerce/wallet.js             |  12 +-
 scripts/system/marketplaces/marketplaces.js   |  26 ++-
 10 files changed, 66 insertions(+), 291 deletions(-)

diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
index c76f5a428a..2d5f77f006 100644
--- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
+++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
@@ -662,7 +662,7 @@ Rectangle {
                 anchors.right: parent.right;
                 text: "Cancel"
                 onClicked: {
-                    sendToScript({method: 'checkout_cancelClicked', params: itemId});
+                    sendToScript({method: 'checkout_cancelClicked', itemId: itemId});
                 }
             }
         }
diff --git a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml
index 0d0af875d1..759d61b924 100644
--- a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml
+++ b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml
@@ -24,11 +24,8 @@ Item {
     HifiConstants { id: hifi; }
 
     id: root;
-    property string referrerURL: (Account.metaverseServerURL + "/marketplace?");
-    readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin;
-    property alias usernameDropdownVisible: usernameDropdown.visible;
 
-    height: mainContainer.height + additionalDropdownHeight;
+    height: mainContainer.height;
 
     Connections {
         target: Commerce;
@@ -93,77 +90,7 @@ Item {
             MouseArea {
                 anchors.fill: parent;
                 onClicked: {
-                    sendToParent({method: "header_marketplaceImageClicked", referrerURL: root.referrerURL});
-                }
-            }
-        }
-
-        Item {
-            id: buttonAndUsernameContainer;
-            anchors.left: marketplaceHeaderImage.right;
-            anchors.leftMargin: 8;
-            anchors.top: parent.top;
-            anchors.bottom: parent.bottom;
-            anchors.bottomMargin: 10;
-            anchors.right: securityImage.left;
-            anchors.rightMargin: 6;
-
-            TextMetrics {
-                id: textMetrics;
-                font.family: "Raleway"
-                text: usernameText.text;
-            }
-
-            Rectangle {
-                id: myUsernameButton;
-                anchors.right: parent.right;
-                anchors.verticalCenter: parent.verticalCenter;
-                height: 40;
-                width: usernameText.width + 25;
-                color: "white";
-                radius: 4;
-                border.width: 1;
-                border.color: hifi.colors.lightGray;
-
-                // Username Text
-                RalewayRegular {
-                    id: usernameText;
-                    text: Account.username;
-                    // Text size
-                    size: 18;
-                    // Style
-                    color: hifi.colors.baseGray;
-                    elide: Text.ElideRight;
-                    horizontalAlignment: Text.AlignHCenter;
-                    verticalAlignment: Text.AlignVCenter;
-                    width: Math.min(textMetrics.width + 25, 110);
-                    // Anchors
-                    anchors.centerIn: parent;
-                    rightPadding: 10;
-                }
-
-                HiFiGlyphs {
-                    id: dropdownIcon;
-                    text: hifi.glyphs.caratDn;
-                    // Size
-                    size: 50;
-                    // Anchors
-                    anchors.right: parent.right;
-                    anchors.rightMargin: -14;
-                    anchors.verticalCenter: parent.verticalCenter;
-                    horizontalAlignment: Text.AlignHCenter;
-                    // Style
-                    color: hifi.colors.baseGray;
-                }
-
-                MouseArea {
-                    anchors.fill: parent;
-                    hoverEnabled: enabled;
-                    onClicked: {
-                        usernameDropdown.visible = !usernameDropdown.visible;
-                    }
-                    onEntered: usernameText.color = hifi.colors.baseGrayShadow;
-                    onExited: usernameText.color = hifi.colors.baseGray;
+                    sendToParent({method: "header_marketplaceImageClicked"});
                 }
             }
         }
@@ -205,92 +132,6 @@ Item {
             }
         }
 
-        Item {
-            id: usernameDropdown;
-            z: 998;
-            visible: false;
-            anchors.top: buttonAndUsernameContainer.bottom;
-            anchors.topMargin: -buttonAndUsernameContainer.anchors.bottomMargin;
-            anchors.right: buttonAndUsernameContainer.right;
-            height: childrenRect.height;
-            width: 150;
-
-            Rectangle {
-                id: myItemsButton;
-                color: hifi.colors.white;
-                anchors.top: parent.top;
-                anchors.left: parent.left;
-                anchors.right: parent.right;
-                height: 50;
-
-                RalewaySemiBold {
-                    anchors.fill: parent;
-                    text: "My Submissions"
-                    color: hifi.colors.baseGray;
-                    horizontalAlignment: Text.AlignHCenter;
-                    verticalAlignment: Text.AlignVCenter;
-                    size: 18;
-                }
-
-                MouseArea {
-                    anchors.fill: parent;
-                    hoverEnabled: true;
-                    onEntered: {
-                        myItemsButton.color = hifi.colors.blueHighlight;
-                    }
-                    onExited: {
-                        myItemsButton.color = hifi.colors.white;
-                    }
-                    onClicked: {
-                        sendToParent({method: "header_myItemsClicked"});
-                    }
-                }
-            }
-
-            Rectangle {
-                id: logOutButton;
-                color: hifi.colors.white;
-                anchors.top: myItemsButton.bottom;
-                anchors.left: parent.left;
-                anchors.right: parent.right;
-                height: 50;
-
-                RalewaySemiBold {
-                    anchors.fill: parent;
-                    text: "Log Out"
-                    color: hifi.colors.baseGray;
-                    horizontalAlignment: Text.AlignHCenter;
-                    verticalAlignment: Text.AlignVCenter;
-                    size: 18;
-                }
-
-                MouseArea {
-                    anchors.fill: parent;
-                    hoverEnabled: true;
-                    onEntered: {
-                        logOutButton.color = hifi.colors.blueHighlight;
-                    }
-                    onExited: {
-                        logOutButton.color = hifi.colors.white;
-                    }
-                    onClicked: {
-                        Account.logOut();
-                    }
-                }
-            }
-        }
-
-        DropShadow {
-            z: 997;
-            visible: usernameDropdown.visible;
-            anchors.fill: usernameDropdown;
-            horizontalOffset: 3;
-            verticalOffset: 3;
-            radius: 8.0;
-            samples: 17;
-            color: "#80000000";
-            source: usernameDropdown;
-        }
     }
 
 
diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml
index 232e17d851..8ca34af28a 100644
--- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml
+++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml
@@ -22,7 +22,6 @@ Rectangle {
     HifiConstants { id: hifi; }
 
     id: root;
-    property string marketplaceUrl: "";
     property string entityId: "";
     property string certificateId: "";
     property string itemName: "--";
@@ -30,6 +29,7 @@ Rectangle {
     property string itemEdition: "--";
     property string dateAcquired: "--";
     property string itemCost: "--";
+    property string marketplace_item_id: "";
     property string certTitleTextColor: hifi.colors.darkGray;
     property string certTextColor: hifi.colors.white;
     property string infoTextColor: hifi.colors.blueAccent;
@@ -69,7 +69,7 @@ Rectangle {
                     errorText.text = "Information about this certificate is currently unavailable. Please try again later.";
                 }
             } else {
-                root.marketplaceUrl = result.data.marketplace_item_url;
+                root.marketplace_item_id = result.data.marketplace_item_id;
                 root.isMyCert = result.isMyCert ? result.isMyCert : false;
 
                 if (root.certInfoReplaceMode > 3) {
@@ -352,7 +352,7 @@ Rectangle {
                 anchors.fill: parent;
                 hoverEnabled: enabled;
                 onClicked: {
-                    sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
+                    sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', itemId: root.marketplace_item_id});
                 }
                 onEntered: itemName.color = hifi.colors.blueHighlight;
                 onExited: itemName.color = root.certTextColor;
@@ -391,7 +391,7 @@ Rectangle {
         // "Show In Marketplace" button
         HifiControlsUit.Button {
             id: showInMarketplaceButton;
-            enabled: root.marketplaceUrl;
+            enabled: root.marketplace_item_id && marketplace_item_id !== "";
             color: hifi.buttons.blue;
             colorScheme: hifi.colorSchemes.light;
             anchors.bottom: parent.bottom;
@@ -401,7 +401,7 @@ Rectangle {
             height: 40;
             text: "View In Market"
             onClicked: {
-                sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
+                sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', itemId: root.marketplace_item_id});
             }
         }
     }
@@ -620,7 +620,7 @@ Rectangle {
         root.itemOwner = "--";
         root.itemEdition = "--";
         root.dateAcquired = "--";
-        root.marketplaceUrl = "";
+        root.marketplace_item_id = "";
         root.itemCost = "--";
         root.isMyCert = false;
         errorText.text = "";
diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
index 8ba5d0bac0..d32d298acd 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
@@ -131,7 +131,6 @@ Rectangle {
 
         onLoginStatusResult: {
             root.isLoggedIn = isLoggedIn;
-            itemsLoginStatus.visible = !isLoggedIn;
         }
     }
 
@@ -179,38 +178,27 @@ Rectangle {
             anchors.left: parent.left
             anchors.top: parent.top
             width: parent.width
-            height: 50
+            height: 60
             visible: true
 
             Image {
-                id: marketplaceIcon
+                id: marketplaceHeaderImage;
+                source: "../common/images/marketplaceHeaderImage.png";
+                anchors.top: parent.top;
+                anchors.topMargin: 2;
+                anchors.bottom: parent.bottom;
+                anchors.bottomMargin: 0;
+                anchors.left: parent.left;
+                anchors.leftMargin: 8;
+                width: 140;
+                fillMode: Image.PreserveAspectFit;
 
-                anchors {
-                    left: parent.left
-                    leftMargin: 8
-                    verticalCenter: parent.verticalCenter
+                MouseArea {
+                    anchors.fill: parent;
+                    onClicked: {
+                        sendToParent({method: "header_marketplaceImageClicked"});
+                    }
                 }
-                height: 20
-                width: marketplaceIcon.height
-                source: "../../../../images/hifi-logo-blackish.svg"
-                visible: true
-            }
-
-            RalewaySemiBold {
-                id: titleBarText
-
-                anchors {
-                    top: parent.top
-                    left: marketplaceIcon.right
-                    bottom: parent.bottom
-                    leftMargin: 6
-                }
-                width: paintedWidth
-
-                text: "Marketplace"
-                size: hifi.fontSizes.overlayTitle
-                color: hifi.colors.black
-                verticalAlignment: Text.AlignVCenter
             }
         }
 
@@ -403,7 +391,7 @@ Rectangle {
                 anchors.fill: parent
                 anchors.rightMargin: 10
                 width: parent.width
-
+                currentIndex: -1;
                 clip: true
                 
                 model: categoriesModel
@@ -587,7 +575,7 @@ Rectangle {
                         left: parent.left
                         right: parent.right
                         leftMargin: 15
-                        rightMargin: 15
+                        top: parent.top+15
                     }
                     height: root.isLoggedIn ? 0 : 80
 
@@ -944,7 +932,7 @@ Rectangle {
                     isLoggedIn: root.isLoggedIn;
 
                     onBuy: {
-                        sendToScript({method: 'marketplace_checkout', itemId: item_id});
+                        sendToScript({method: 'marketplace_checkout', itemId: item_id, itemEdition: edition});
                     }
                     
                     onShowLicense: {
@@ -1142,7 +1130,7 @@ Rectangle {
                     console.log("A message with method 'updateMarketplaceQMLItem' was sent without an itemId!");
                     return;
                 }
-
+                marketplaceItem.edition = message.params.edition ? message.params.edition : -1;
                 MarketplaceScriptingInterface.getMarketplaceItem(message.params.itemId);
                 break;
         }
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
index 5795d0d67d..0478f38764 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
@@ -41,6 +41,7 @@ Rectangle {
     property bool available: false
     property string created_at: ""
     property bool isLoggedIn: false;
+    property int edition: -1;
     
     onCategoriesChanged: {
         categoriesListModel.clear();
@@ -228,8 +229,8 @@ Rectangle {
             }
             height: 50 
 
-            text: root.available ? (root.price ? root.price : "FREE") : "UNAVAILABLE (not for sale)"
-            enabled: root.available
+            text: root.edition >= 0 ? "UPGRADE FOR FREE" : (root.available ?  (root.price ? root.price : "FREE") : "UNAVAILABLE (not for sale)")
+            enabled: root.edition >= 0 || root.available
             buttonGlyph: root.available ? (root.price ? hifi.glyphs.hfc : "") : ""
             color: hifi.buttons.blue
             
diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
index f7dc26df5f..df6e216b32 100644
--- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
@@ -28,6 +28,7 @@ Item {
     property string purchaseStatus;
     property string itemName;
     property string itemId;
+    property string updateItemId;
     property string itemPreviewImageUrl;
     property string itemHref;
     property string certificateId;
@@ -45,9 +46,9 @@ Item {
     property bool cardBackVisible;
     property bool isInstalled;
     property string wornEntityID;
-    property string upgradeUrl;
+    property string updatedItemId;
     property string upgradeTitle;
-    property bool updateAvailable: root.upgradeUrl !== "";
+    property bool updateAvailable: root.updateItemId && root.updateItemId !== "";
     property bool valid;
 
     property string originalStatusText;
@@ -175,7 +176,7 @@ Item {
 
                     Item {
                         property alias buttonGlyphText: buttonGlyph.text;
-                        property alias buttonText: buttonText.text;
+                        property alias itemButtonText: buttonText.text;
                         property alias glyphSize: buttonGlyph.size;
                         property string buttonColor: hifi.colors.black;
                         property string buttonColor_hover: hifi.colors.blueHighlight;
@@ -243,7 +244,7 @@ Item {
                     onLoaded: {
                         item.enabled = root.valid;
                         item.buttonGlyphText = hifi.glyphs.gift;
-                        item.buttonText = "Gift";
+                        item.itemButtonText = "Gift";
                         item.buttonClicked = function() {
                             sendToPurchases({ method: 'flipCard', closeAll: true });
                             sendToPurchases({
@@ -270,7 +271,7 @@ Item {
 
                     onLoaded: {
                         item.buttonGlyphText = hifi.glyphs.market;
-                        item.buttonText = "View in Marketplace";
+                        item.itemButtonText = "View in Marketplace";
                         item.buttonClicked = function() {
                             sendToPurchases({ method: 'flipCard', closeAll: true });
                             sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
@@ -288,7 +289,7 @@ Item {
 
                     onLoaded: {
                         item.buttonGlyphText = hifi.glyphs.certificate;
-                        item.buttonText = "View Certificate";
+                        item.itemButtonText = "View Certificate";
                         item.buttonClicked = function() {
                             sendToPurchases({ method: 'flipCard', closeAll: true });
                             sendToPurchases({method: 'purchases_itemCertificateClicked', itemCertificateId: root.certificateId});
@@ -307,7 +308,7 @@ Item {
 
                     onLoaded: {
                         item.buttonGlyphText = hifi.glyphs.uninstall;
-                        item.buttonText = "Uninstall";
+                        item.itemButtonText = "Uninstall";
                         item.buttonClicked = function() {
                             sendToPurchases({ method: 'flipCard', closeAll: true });
                             Commerce.uninstallApp(root.itemHref);
@@ -330,15 +331,14 @@ Item {
 
                     onLoaded: {
                         item.buttonGlyphText = hifi.glyphs.update;
-                        item.buttonText = "Update";
+                        item.itemButtonText = "Update";
                         item.buttonColor = "#E2334D";
                         item.buttonClicked = function() {
                             sendToPurchases({ method: 'flipCard', closeAll: true });
                             sendToPurchases({
                                 method: 'updateItemClicked',
-                                itemId: root.itemId,
+                                itemId: root.updateAvailable ? root.updateItemId : root.itemId,
                                 itemEdition: root.itemEdition,
-                                upgradeUrl: root.upgradeUrl,
                                 itemHref: root.itemHref,
                                 itemType: root.itemType,
                                 isInstalled: root.isInstalled,
@@ -378,10 +378,10 @@ Item {
 
                     function updateProperties() {
                         if (updateButton.visible && uninstallButton.visible) {
-                            item.buttonText = "";
+                            item.itemButtonText = "";
                             item.glyphSize = 20;
                         } else {
-                            item.buttonText = "Send to Trash";
+                            item.itemButtonText = "Send to Trash";
                             item.glyphSize = 30;
                         }
                     }
diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
index 9433618b6b..bcc2a2821c 100644
--- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
@@ -29,7 +29,6 @@ Rectangle {
 
     id: root;
     property string activeView: "initialize";
-    property string referrerURL: "";
     property bool securityImageResultReceived: false;
     property bool purchasesReceived: false;
     property bool punctuationMode: false;
@@ -154,55 +153,10 @@ Rectangle {
         }
     }
 
-    //
-    // TITLE BAR START
-    //
-    HifiCommerceCommon.EmulatedMarketplaceHeader {
-        id: titleBarContainer;
-        z: 997;
-        visible: false;
-        height: 100;
-        // Size
-        width: parent.width;
-        // Anchors
-        anchors.left: parent.left;
-        anchors.top: parent.top;
-
-        Connections {
-            onSendToParent: {
-                if (msg.method === 'needsLogIn' && root.activeView !== "needsLogIn") {
-                    root.activeView = "needsLogIn";
-                } else if (msg.method === 'showSecurityPicLightbox') {
-                    lightboxPopup.titleText = "Your Security Pic";
-                    lightboxPopup.bodyImageSource = msg.securityImageSource;
-                    lightboxPopup.bodyText = lightboxPopup.securityPicBodyText;
-                    lightboxPopup.button1text = "CLOSE";
-                    lightboxPopup.button1method = function() {
-                        lightboxPopup.visible = false;
-                    }
-                    lightboxPopup.visible = true;
-                } else {
-                    sendToScript(msg);
-                }
-            }
-        }
-    }
-    MouseArea {
-        enabled: titleBarContainer.usernameDropdownVisible;
-        anchors.fill: parent;
-        onClicked: {
-            titleBarContainer.usernameDropdownVisible = false;
-        }
-    }
-    //
-    // TITLE BAR END
-    //
-
     Rectangle {
         id: initialize;
         visible: root.activeView === "initialize";
-        anchors.top: titleBarContainer.bottom;
-        anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
+        anchors.top: parent.top;
         anchors.bottom: parent.bottom;
         anchors.left: parent.left;
         anchors.right: parent.right;
@@ -219,8 +173,7 @@ Rectangle {
         id: installedAppsContainer;
         z: 998;
         visible: false;
-        anchors.top: titleBarContainer.bottom;
-        anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
+        anchors.top: parent.top;
         anchors.left: parent.left;
         anchors.bottom: parent.bottom;
         width: parent.width;
@@ -422,8 +375,8 @@ Rectangle {
         // Anchors
         anchors.left: parent.left;
         anchors.right: parent.right;
-        anchors.top: titleBarContainer.bottom;
-        anchors.topMargin: 8 - titleBarContainer.additionalDropdownHeight;
+        anchors.top: parent.top;
+        anchors.topMargin: 8;
         anchors.bottom: parent.bottom;
 
         //
@@ -585,6 +538,7 @@ Rectangle {
             delegate: PurchasedItem {
                 itemName: title;
                 itemId: id;
+                updateItemId: model.upgrade_id ? model.upgrade_id : "";
                 itemPreviewImageUrl: preview;
                 itemHref: download_url;
                 certificateId: certificate_id;
@@ -596,7 +550,6 @@ Rectangle {
                 cardBackVisible: model.cardBackVisible || false;
                 isInstalled: model.isInstalled || false;
                 wornEntityID: model.wornEntityID;
-                upgradeUrl: model.upgrade_url;
                 upgradeTitle: model.upgrade_title;
                 itemType: model.item_type;
                 valid: model.valid;
@@ -1083,8 +1036,6 @@ Rectangle {
     function fromScript(message) {
         switch (message.method) {
             case 'updatePurchases':
-                referrerURL = message.referrerURL || "";
-                titleBarContainer.referrerURL = message.referrerURL || "";
                 filterBar.text = message.filterText ? message.filterText : "";
             break;
             case 'purchases_showMyItems':
diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
index cf293a06df..eb8aa0f809 100644
--- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
@@ -335,7 +335,7 @@ Item {
                                 if (link.indexOf("users/") !== -1) {
                                     sendSignalToWallet({method: 'transactionHistory_usernameLinkClicked', usernameLink: link});
                                 } else {
-                                    sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
+                                    sendSignalToWallet({method: 'transactionHistory_linkClicked', itemId: model.marketplace_item});
                                 }
                             }
                         }
diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js
index 7cacfd7935..17ff918243 100644
--- a/scripts/system/commerce/wallet.js
+++ b/scripts/system/commerce/wallet.js
@@ -420,10 +420,10 @@ function fromQml(message) {
         case 'purchases':
         case 'marketplace cta':
         case 'mainPage':
-            ui.open(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL);
+            openMarketplace();
             break;
-        default: // User needs to return to an individual marketplace item URL
-            ui.open(MARKETPLACE_URL + '/items/' + message.referrer, MARKETPLACES_INJECT_SCRIPT_URL);
+        default:
+            openMarketplace();
             break;
         }
         break;
@@ -435,13 +435,13 @@ function fromQml(message) {
     case 'maybeEnableHmdPreview':
         break; // do nothing here, handled in marketplaces.js
     case 'transactionHistory_linkClicked':
-        ui.open(message.marketplaceLink, MARKETPLACES_INJECT_SCRIPT_URL);
+        openMarketplace(message.itemId);
         break;
     case 'goToMarketplaceMainPage':
-        ui.open(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL);
+        openMarketplace();
         break;
     case 'goToMarketplaceItemPage':
-        ui.open(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL);
+        openMarketplace(message.itemId);
         break;
     case 'refreshConnections':
         print('Refreshing Connections...');
diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js
index 9d571d9284..b7a6b951a9 100644
--- a/scripts/system/marketplaces/marketplaces.js
+++ b/scripts/system/marketplaces/marketplaces.js
@@ -19,6 +19,7 @@ var selectionDisplay = null; // for gridTool.js to ignore
 var AppUi = Script.require('appUi');
 Script.include("/~/system/libraries/gridTool.js");
 Script.include("/~/system/libraries/connectionUtils.js");
+Script.include("/~/system/libraries/accountUtils.js");
 
 var MARKETPLACE_CHECKOUT_QML_PATH = "hifi/commerce/checkout/Checkout.qml";
 var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml";
@@ -156,13 +157,12 @@ function onMarketplaceOpen(referrer) {
     }
 }
 
-function openMarketplace(optionalItem) {
+function openMarketplace(optionalItem, edition) {
     ui.open(MARKETPLACE_QML_PATH);
-    
     if (optionalItem) {
         ui.tablet.sendToQml({
             method: 'updateMarketplaceQMLItem',
-            params: { itemId: optionalItem }
+            params: { itemId: optionalItem, edition: edition }
         });
     }
 }
@@ -486,7 +486,6 @@ function onWebEventReceived(message) {
     } else if (message.type === "WALLET_SETUP") {
         setupWallet('marketplace cta');
     } else if (message.type === "MY_ITEMS") {
-        referrerURL = MARKETPLACE_URL_INITIAL;
         filterText = "";
         ui.open(MARKETPLACE_PURCHASES_QML_PATH);
         wireQmlEventBridge(true);
@@ -519,7 +518,6 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
     if (message.messageSrc === "HTML") {
         return;
     }
-    console.log(JSON.stringify(message));
     switch (message.method) {
     case 'gotoBank':
         ui.close();
@@ -548,11 +546,10 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
         openWallet();
         break;
     case 'checkout_cancelClicked':
-        openMarketplace(message.params);
+        openMarketplace(message.itemId);
         break;
     case 'header_goToPurchases':
     case 'checkout_goToPurchases':
-        referrerURL = MARKETPLACE_URL_INITIAL;
         filterText = message.filterText;
         ui.open(MARKETPLACE_PURCHASES_QML_PATH);
         break;
@@ -602,13 +599,13 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
         }
         break;
     case 'header_marketplaceImageClicked':
-        openMarketplace(message.referrerURL);
+        openMarketplace();
         break;
     case 'purchases_goToMarketplaceClicked':
         openMarketplace();
         break;
     case 'updateItemClicked':
-        openMarketplace(message.upgradeUrl + "?edition=" + message.itemEdition);
+        openMarketplace(message.itemId, message.itemEdition);
         break;
     case 'passphrasePopup_cancelClicked':
     case 'needsLogIn_cancelClicked':
@@ -638,10 +635,10 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
         ContextOverlay.requestOwnershipVerification(message.entity);
         break;
     case 'inspectionCertificate_showInMarketplaceClicked':
-        openMarketplace(message.marketplaceUrl);
+        console.log("INSPECTION CERTIFICATE SHOW IN MARKETPLACE CLICKED: " + message.itemId);
+        openMarketplace(message.itemId);
         break;
     case 'header_myItemsClicked':
-        referrerURL = MARKETPLACE_URL_INITIAL;
         filterText = "";
         ui.open(MARKETPLACE_PURCHASES_QML_PATH);
         wireQmlEventBridge(true);
@@ -750,11 +747,8 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) {
         Keyboard.raised = false;
     }
 
-    if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) {
-        ContextOverlay.isInMarketplaceInspectionMode = true;
-    } else {
-        ContextOverlay.isInMarketplaceInspectionMode = false;
-    }
+    ContextOverlay.isInMarketplaceInspectionMode = false;
+
 
     if (onInspectionCertificateScreen) {
         setCertificateInfo(contextOverlayEntity);

From 39ad36a4d03c2fdaad0216ddaeb2209c66cc3cf0 Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxanne@highfidelity.io>
Date: Fri, 1 Feb 2019 15:27:17 -0800
Subject: [PATCH 08/18] QmlMarketplace - bugfixes and Markup rendering of
 descriptions * Render markup in descriptions (bold, italic, quote, etc.) *
 Back button from marketplaces list now works properly * Layout fixes

---
 .../hifi/commerce/marketplace/Marketplace.qml | 133 +++++++++++------
 .../commerce/marketplace/MarketplaceItem.qml  | 136 +++++++++++++-----
 scripts/system/html/js/marketplacesInject.js  |   9 +-
 scripts/system/marketplaces/marketplaces.js   |  13 +-
 4 files changed, 210 insertions(+), 81 deletions(-)

diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
index d32d298acd..a4cf260173 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
@@ -32,7 +32,7 @@ Rectangle {
 
     property string activeView: "initialize"
     property int currentSortIndex: 0
-    property string sortString: ""
+    property string sortString: "recent"
     property string categoryString: ""
     property string searchString: ""
     property bool keyboardEnabled: HMD.active
@@ -45,6 +45,7 @@ Rectangle {
     function getMarketplaceItems() {
         marketplaceItemView.visible = false;
         itemsList.visible = true;
+        licenseInfo.visible = false;
         marketBrowseModel.getFirstPage();
         {
         if(root.searchString !== undefined && root.searchString !== "") {
@@ -69,7 +70,6 @@ Rectangle {
         target: GlobalServices
 
         onMyUsernameChanged: {
-            console.log("LOGIN STATUS CHANGING");
             Commerce.getLoginStatus();
         }
     }
@@ -333,7 +333,7 @@ Rectangle {
                         break;
                     }
                 }
-                onTextChanged: root.searchString = text
+
                 onAccepted: {
                     root.searchString = searchField.text;
                     getMarketplaceItems();
@@ -474,11 +474,11 @@ Rectangle {
 
         anchors {
             fill: parent
-            topMargin: 120
+            topMargin: 115
             bottomMargin: 50
         }
  
-        visible: true;
+        visible: true
 
         HifiModels.PSFListModel {
             id: marketBrowseModel
@@ -565,17 +565,22 @@ Rectangle {
 
             header: Item {
                 id: itemsHeading
-                
+
                 height: childrenRect.height
                 width: parent.width
-                
+
+                Rectangle {
+                    id: itemsSpacer;
+                    height: 20
+                }
+
                 Rectangle {
                     id: itemsLoginStatus;
                     anchors {
+                        top: itemsSpacer.bottom
                         left: parent.left
                         right: parent.right
                         leftMargin: 15
-                        top: parent.top+15
                     }
                     height: root.isLoggedIn ? 0 : 80
 
@@ -598,7 +603,7 @@ Rectangle {
                         }
                         width: 80;
 
-                        text: root.price ? root.price : "LOG IN"
+                        text: "LOG IN"
 
                         onClicked: {
                             sendToScript({method: 'needsLogIn_loginClicked'});
@@ -687,6 +692,7 @@ Rectangle {
                 }
                 Item {
                     id: sort
+                    visible: searchString === undefined || searchString === ""
 
                     anchors {
                         top: searchScope.bottom;
@@ -695,7 +701,7 @@ Rectangle {
                         topMargin: 10;
                         leftMargin: 15;
                     }
-                    height: childrenRect.height
+                    height: visible ? childrenRect.height : 0
 
                     RalewayRegular {
                         id: sortText
@@ -771,6 +777,7 @@ Rectangle {
                             focus: true
                             clip: true
                             highlightFollowsCurrentItem: false
+                            currentIndex: 1;
                             
                             delegate: SortButton {
                                 width: 80
@@ -818,25 +825,39 @@ Rectangle {
         id: marketplaceItemView
 
         anchors.fill: parent
-        anchors.topMargin: 120
+        anchors.topMargin: 115
+        anchors.bottomMargin: 50
         width: parent.width
 
         visible: false
-        
+
         ScrollView {
             id: marketplaceItemScrollView
 
-            anchors.fill: parent;
+            anchors.fill: parent
 
             clip: true
             ScrollBar.vertical.policy: ScrollBar.AlwaysOn
             contentWidth: parent.width
+            contentHeight: childrenRect.height
+
+            function resize() {
+                contentHeight = (marketplaceItemContent.y - itemSpacer.y + marketplaceItemContent.height);
+            }
+
+            Item {
+                id: itemSpacer
+                anchors.top: parent.top
+                height: 15
+            }
 
             Rectangle {
                 id: itemLoginStatus;
                 anchors {
                     left: parent.left
                     right: parent.right
+                    top: itemSpacer.bottom
+                    topMargin: 10
                     leftMargin: 15
                     rightMargin: 15
                 }
@@ -861,7 +882,7 @@ Rectangle {
                     }
                     width: 80;
 
-                    text: root.price ? root.price : "LOG IN"
+                    text: "LOG IN"
 
                     onClicked: {
                         sendToScript({method: 'needsLogIn_loginClicked'});
@@ -890,9 +911,9 @@ Rectangle {
 
             Rectangle {
                 id: marketplaceItemContent
-                anchors.top: itemLoginStatus.bottom;
+                anchors.top: itemLoginStatus.bottom
                 width: parent.width
-                height: childrenRect.height + 100
+                height: childrenRect.height;
                 
                 RalewaySemiBold {
                     id: backText
@@ -900,6 +921,7 @@ Rectangle {
                     anchors {
                         top: parent.top
                         left: parent.left
+                        topMargin: 10
                         leftMargin: 15
                         bottomMargin: 10
                     }
@@ -944,6 +966,10 @@ Rectangle {
                         categoriesText.text = category;                      
                         getMarketplaceItems();
                     }
+
+                    onResized: {
+                        marketplaceItemScrollView.resize();
+                    }
                 }
             }
         }
@@ -975,37 +1001,64 @@ Rectangle {
                 leftMargin: 15
             }
 
-            HiFiGlyphs {
-                id: footerGlyph
+            Item {
+                id: footerText
 
+                anchors.fill: parent
+                visible: itemsList.visible
+
+                HiFiGlyphs {
+                    id: footerGlyph
+
+                    anchors {
+                        left: parent.left
+                        top: parent.top
+                        bottom: parent.bottom
+                        rightMargin: 10
+                    }
+
+                    text: hifi.glyphs.info
+                    size: 34
+                    color: hifi.colors.white
+                    horizontalAlignment: Text.AlignHCenter
+                    verticalAlignment: Text.AlignVCenter
+                }
+
+                RalewaySemiBold {
+                    id: footerInfo
+
+                    anchors {
+                        left: footerGlyph.right
+                        top: parent.top
+                        bottom: parent.bottom
+                    }
+
+                    text: "Get items from Clara.io!"
+                    color: hifi.colors.white
+                    horizontalAlignment: Text.AlignLeft
+                    verticalAlignment: Text.AlignVCenter
+                    size: 18
+                }
+            }
+
+            HifiControlsUit.Button {
                 anchors {
                     left: parent.left
                     top: parent.top
                     bottom: parent.bottom
+                    topMargin: 10
+                    bottomMargin: 10
+                    leftMargin: 10
                     rightMargin: 10
                 }
 
-                text: hifi.glyphs.info
-                size: 34
-                color: hifi.colors.white
-                horizontalAlignment: Text.AlignHCenter
-                verticalAlignment: Text.AlignVCenter
-            }
+                visible: marketplaceItemView.visible
+                text: "< BACK"
+                width: 100
 
-            RalewaySemiBold {
-                id: footerInfo
-
-                anchors {
-                    left: footerGlyph.right
-                    top: parent.top
-                    bottom: parent.bottom
+                onClicked: {
+                    getMarketplaceItems();
                 }
-
-                text: "Get items from Clara.io!"
-                color: hifi.colors.white
-                horizontalAlignment: Text.AlignLeft
-                verticalAlignment: Text.AlignVCenter
-                size: 18
             }
             
             HifiControlsUit.Button {
@@ -1023,7 +1076,7 @@ Rectangle {
                 width: 180
 
                 onClicked: {
-                    sendToScript({method: 'marketplace_marketplaces'});
+                    sendToScript({method: 'marketplace_marketplaces', itemId: marketplaceItemView.visible ? marketplaceItem.item_id : undefined});
                 }
             }
         }  
@@ -1041,7 +1094,7 @@ Rectangle {
 
         anchors {
             fill: root
-            topMargin: 100
+            topMargin: 120
             bottomMargin: 0
         }
 
@@ -1052,7 +1105,7 @@ Rectangle {
             
             anchors {
                 bottomMargin: 1
-                topMargin: 50
+                topMargin: 60
                 leftMargin: 1
                 rightMargin: 1
                 fill: parent
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
index 0478f38764..b7e9a711d2 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
@@ -15,6 +15,7 @@ import Hifi 1.0 as Hifi
 import QtQuick 2.9
 import QtQuick.Controls 2.2
 import QtGraphicalEffects 1.0
+import QtWebEngine 1.5
 import stylesUit 1.0
 import controlsUit 1.0 as HifiControlsUit
 import "../../../controls" as HifiControls
@@ -49,6 +50,11 @@ Rectangle {
             categoriesListModel.append({"category":category});
         });
     }
+    
+    onDescriptionChanged: {
+        descriptionTextModel.clear();
+        descriptionTextModel.append({text: description})
+    }
 
     signal buy()
     signal categoryClicked(string category)
@@ -63,7 +69,7 @@ Rectangle {
 
         onMarketplaceItemLikeResult: {
             if (result.status !== 'success') {
-                console.log("Failed to get Marketplace Categories", result.data.message);
+                console.log("Like/Unlike item", result.data.message);
             } else {
                 root.liked = !root.liked;
                 root.likes = root.liked ? root.likes + 1 : root.likes - 1;
@@ -98,24 +104,33 @@ Rectangle {
         var sec = addLeadingZero(a.getSeconds());
         return a.toDateString() + " " + drawnHour + ':' + min + amOrPm;
     }
+    function evalHeight() {
+        height = footer.y - header.y + footer.height;
+    }
+    
+    signal resized()
+    
+    onHeightChanged: {
+        resized();
+    }
     
     anchors {
-        left: parent.left;
-        right: parent.right;
-        leftMargin: 15;
-        rightMargin: 15;
+        left: parent.left
+        right: parent.right
+        leftMargin: 15
+        rightMargin: 15
     }
-    height: childrenRect.height;
+    height: footer.y - header.y + footer.height
 
     Rectangle {
         id: header
         
         anchors {
-            left: parent.left;
-            right: parent.right;
-            top: parent.top;
+            left: parent.left
+            right: parent.right
+            top: parent.top
         }
-        height: 50;
+        height: 50
         
         RalewaySemiBold {
             id: nameText
@@ -137,10 +152,10 @@ Rectangle {
             id: likes
 
             anchors {
-                top: parent.top;
-                right: parent.right;
-                bottom: parent.bottom;
-                rightMargin: 5;
+                top: parent.top
+                right: parent.right
+                bottom: parent.bottom
+                rightMargin: 5
             }
 
             RalewaySemiBold {
@@ -216,7 +231,11 @@ Rectangle {
             right: parent.right;
             top: itemImage.bottom;
         }
-        height: childrenRect.height
+        height: categoriesList.y - buyButton.y + categoriesList.height
+        
+        function evalHeight() {
+            height = categoriesList.y - buyButton.y + categoriesList.height;
+        }
         
         HifiControlsUit.Button {
             id: buyButton
@@ -309,7 +328,7 @@ Rectangle {
                     top: postedLabel.bottom
                     left: parent.left
                     right: parent.right
-                    topMargin: 10
+                    topMargin: 5
                 }
 
                 text: { getFormattedDate(root.created_at); }
@@ -360,6 +379,7 @@ Rectangle {
                 
                 anchors.top: licenseLabel.bottom
                 anchors.left: parent.left
+                anchors.topMargin: 5
                 width: paintedWidth
 
                 text: root.license
@@ -371,9 +391,10 @@ Rectangle {
             RalewaySemiBold {
                 id: licenseHelp
 
-                anchors.top: licenseText.bottom;
-                anchors.left: parent.left;
-                width: paintedWidth;
+                anchors.top: licenseText.bottom
+                anchors.left: parent.left
+                anchors.topMargin: 5
+                width: paintedWidth
 
                 text: "More about this license"
                 size: 14
@@ -413,6 +434,7 @@ Rectangle {
 
         Item {
             id: descriptionItem
+            property string text: ""
 
             anchors {
                 top: licenseItem.bottom
@@ -421,13 +443,16 @@ Rectangle {
                 right: parent.right
             }
             height: childrenRect.height
-
+            onHeightChanged: {
+                footer.evalHeight();
+            }
             RalewaySemiBold {
                 id: descriptionLabel
                 
                 anchors.top: parent.top
                 anchors.left: parent.left
                 width: paintedWidth
+                height: 20
 
                 text: "DESCRIPTION:"
                 size: 14
@@ -435,18 +460,60 @@ Rectangle {
                 verticalAlignment: Text.AlignVCenter
             }
 
-            RalewaySemiBold {
-                id: descriptionText
+            //RalewaySemiBold {
+            //    id: descriptionText
+            //    
+            //    anchors.top: descriptionLabel.bottom
+            //    anchors.left: parent.left
+            //    anchors.topMargin: 5
+            //    width: parent.width
+            //
+            //    text: root.description
+            //    size: 14
+            //   color: hifi.colors.lightGray
+            //    verticalAlignment: Text.AlignVCenter
+            //    wrapMode: Text.Wrap
+            //}
+            
+            
+            ListModel {
+                id: descriptionTextModel
+            }
+            
+            ListView {
+                id: descriptionTextView;
                 
                 anchors.top: descriptionLabel.bottom
                 anchors.left: parent.left
-                width: parent.width
+                anchors.right: parent.right                
 
-                text: root.description
-                size: 14
-                color: hifi.colors.lightGray
-                verticalAlignment: Text.AlignVCenter
-                wrapMode: Text.Wrap
+                model: descriptionTextModel
+                interactive: false
+            
+                delegate: Component {
+                    Rectangle {
+                        id: descriptionWebRect
+                        width: parent.width
+                        height: 5
+                        WebEngineView {
+                            id: descriptionWebView
+                            anchors.fill: parent
+
+                            Component.onCompleted: {
+                                loadHtml("<html><head><style>body { color: #393939; font-family: Arial !important;}</style></head><body>"+model.text+"</body></html>");
+                            }
+
+                            onContentsSizeChanged: {
+                                descriptionWebRect.height = contentsSize.height;
+                                descriptionTextView.height = contentsSize.height;
+                            }
+
+                            onNewViewRequested: function(request) {
+                                sendToScript({method: 'marketplace_open_link', link: request.requestedUrl});
+                            }
+                        }
+                    }
+                }
             }
         }
         
@@ -460,7 +527,7 @@ Rectangle {
                 right: parent.right
             }
             width: parent.width
-            height: childrenRect.height
+            height: childrenRect.height + 50
 
             RalewaySemiBold {
                 id: categoryLabel
@@ -480,12 +547,13 @@ Rectangle {
             
             ListView {
                 anchors {
-                    left: parent.left;
-                    right: parent.right;
-                    top: categoryLabel.bottom;
+                    left: parent.left
+                    right: parent.right
+                    top: categoryLabel.bottom
+                    bottomMargin: 15
                 }
 
-                height: 20*model.count
+                height: 24*model.count+10
 
                 model: categoriesListModel
                 delegate: RalewaySemiBold {
@@ -496,7 +564,7 @@ Rectangle {
 
                     text: model.category
                     size: 14
-                    height: 20
+                    height: 24
                     color: hifi.colors.blueHighlight
                     verticalAlignment: Text.AlignVCenter
                     
diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js
index 74bf8d3fec..8d408169ba 100644
--- a/scripts/system/html/js/marketplacesInject.js
+++ b/scripts/system/html/js/marketplacesInject.js
@@ -76,9 +76,12 @@
             if (document.referrer !== "") {
                 window.history.back();
             } else {
-                EventBridge.emitWebEvent(JSON.stringify({
-                    type: GOTO_MARKETPLACE
-                }));
+                var params = { type: GOTO_MARKETPLACE };
+                var itemIdMatch = location.search.match(/itemId=([^&]*)/);
+                if (itemIdMatch && itemIdMatch.length === 2) {
+                    params.itemId = itemIdMatch[1];
+                }
+                EventBridge.emitWebEvent(JSON.stringify(params));
             }
         });
         $("#all-markets").on("click", function () {
diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js
index b7a6b951a9..c085763fad 100644
--- a/scripts/system/marketplaces/marketplaces.js
+++ b/scripts/system/marketplaces/marketplaces.js
@@ -41,7 +41,6 @@ var GOTO_DIRECTORY = "GOTO_DIRECTORY";
 var GOTO_MARKETPLACE = "GOTO_MARKETPLACE";
 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 CLARA_DOWNLOAD_TITLE = "Preparing Download";
 var messageBox = null;
@@ -437,9 +436,8 @@ function rezEntity(itemHref, itemType, marketplaceItemTesterId) {
 var referrerURL; // Used for updating Purchases QML
 var filterText; // Used for updating Purchases QML
 function onWebEventReceived(message) {
-    message = JSON.parse(message);
     if (message.type === GOTO_MARKETPLACE) {
-        openMarketplace();
+        openMarketplace(message.itemId);
     } else if (message.type === GOTO_DIRECTORY) {
         // This is the chooser between marketplaces. Only OUR markteplace
         // requires/makes-use-of wallet, so doesn't go through openMarketplace bottleneck.
@@ -569,7 +567,14 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
     case 'marketplace_marketplaces':
         // This is the chooser between marketplaces. Only OUR markteplace
         // requires/makes-use-of wallet, so doesn't go through openMarketplace bottleneck.
-        ui.open(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL);
+        var url = MARKETPLACES_URL;
+        if(message.itemId) {
+            url = url + "?itemId=" + message.itemId
+        }
+        ui.open(url, MARKETPLACES_INJECT_SCRIPT_URL);
+        break;
+    case 'marketplace_open_link':
+        ui.open(message.link);
         break;
     case 'checkout_rezClicked':
     case 'purchases_rezClicked':

From 1d7265f668f0974daf21d07e671b9ed125485119 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Mon, 4 Feb 2019 09:45:06 -0800
Subject: [PATCH 09/18] trust the data in the packet, Luke

---
 libraries/octree/src/OctreePacketData.cpp | 27 -----------------------
 1 file changed, 27 deletions(-)

diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp
index a79f0a0c2b..8ab502e951 100755
--- a/libraries/octree/src/OctreePacketData.cpp
+++ b/libraries/octree/src/OctreePacketData.cpp
@@ -729,12 +729,6 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char *dataBytes, QVecto
     uint16_t length;
     memcpy(&length, dataBytes, sizeof(uint16_t));
     dataBytes += sizeof(length);
-
-    // FIXME - this size check is wrong if we allow larger packets
-    if (length * sizeof(glm::vec3) > MAX_OCTREE_UNCOMRESSED_PACKET_SIZE) {
-        result.resize(0);
-        return sizeof(uint16_t);
-    }
     result.resize(length);
     memcpy(result.data(), dataBytes, length * sizeof(glm::vec3));
     return sizeof(uint16_t) + length * sizeof(glm::vec3);
@@ -744,14 +738,7 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char *dataBytes, QVecto
     uint16_t length;
     memcpy(&length, dataBytes, sizeof(uint16_t));
     dataBytes += sizeof(length);
-
-    // FIXME - this size check is wrong if we allow larger packets
-    if (length * sizeof(glm::quat) > MAX_OCTREE_UNCOMRESSED_PACKET_SIZE) {
-        result.resize(0);
-        return sizeof(uint16_t);
-    }
     result.resize(length);
-
     const unsigned char *start = dataBytes;
     for (int i = 0; i < length; i++) {
         dataBytes += unpackOrientationQuatFromBytes(dataBytes, result[i]);
@@ -764,12 +751,6 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QVecto
     uint16_t length;
     memcpy(&length, dataBytes, sizeof(uint16_t));
     dataBytes += sizeof(length);
-
-    // FIXME - this size check is wrong if we allow larger packets
-    if (length * sizeof(float) > MAX_OCTREE_UNCOMRESSED_PACKET_SIZE) {
-        result.resize(0);
-        return sizeof(uint16_t);
-    }
     result.resize(length);
     memcpy(result.data(), dataBytes, length * sizeof(float));
     return sizeof(uint16_t) + length * sizeof(float);
@@ -779,14 +760,7 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QVecto
     uint16_t length;
     memcpy(&length, dataBytes, sizeof(uint16_t));
     dataBytes += sizeof(length);
-
-    // FIXME - this size check is wrong if we allow larger packets
-    if (length / 8 > MAX_OCTREE_UNCOMRESSED_PACKET_SIZE) {
-        result.resize(0);
-        return sizeof(uint16_t);
-    }
     result.resize(length);
-
     int bit = 0;
     unsigned char current = 0;
     const unsigned char *start = dataBytes;
@@ -797,7 +771,6 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QVecto
         result[i] = (bool)(current & (1 << bit));
         bit = (bit + 1) % BITS_IN_BYTE;
     }
-
     return (dataBytes - start) + (int)sizeof(uint16_t);
 }
 

From 1840f874ab4e29e3c78a3ce9c649cac0e2724404 Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxanne@roxiware.com>
Date: Mon, 4 Feb 2019 13:29:06 -0800
Subject: [PATCH 10/18] Add Attributions to Qml Marketplace

---
 .../hifi/commerce/marketplace/Marketplace.qml |   6 +-
 .../commerce/marketplace/MarketplaceItem.qml  | 118 ++++++++++++++++--
 2 files changed, 111 insertions(+), 13 deletions(-)

diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
index a4cf260173..8458e28ba8 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
@@ -369,7 +369,7 @@ Rectangle {
                 categoriesButton.color = hifi.colors.white;
             }            
         }
-        
+
         Rectangle {
             anchors {
                 left: parent.left;
@@ -1057,7 +1057,9 @@ Rectangle {
                 width: 100
 
                 onClicked: {
-                    getMarketplaceItems();
+                    marketplaceItemView.visible = false;
+                    itemsList.visible = true;
+                    licenseInfo.visible = false;
                 }
             }
             
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
index b7e9a711d2..cb158a2b69 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
@@ -50,7 +50,15 @@ Rectangle {
             categoriesListModel.append({"category":category});
         });
     }
-    
+
+    onAttributionsChanged: {
+        attributionsModel.clear();
+        root.attributions.forEach(function(attribution) {
+            console.log("ATTRIBUITION:" + JSON.stringify(attribution));
+            attributionsModel.append(attribution);
+        });
+    }
+
     onDescriptionChanged: {
         descriptionTextModel.clear();
         descriptionTextModel.append({text: description})
@@ -339,24 +347,112 @@ Rectangle {
         }
         
         Rectangle {
-            anchors {
-                top: posted.bottom;
-                leftMargin: 15;
-                topMargin: 15;
-            }
-            width: parent.width;
-            height: 1;
 
-            color: hifi.colors.lightGray;
+            anchors {
+                top: posted.bottom
+                leftMargin: 15
+                topMargin: 15
+            }
+            width: parent.width
+            height: 1
+
+            color: hifi.colors.lightGrayText
         }
 
+        Item {
+            id: attributions
+
+            anchors {
+                top: posted.bottom
+                topMargin: 30
+                left: parent.left
+                right: parent.right
+            }
+            width: parent.width
+            height: attributionsModel.count > 0 ? childrenRect.height : 0
+            visible: attributionsModel.count > 0
+
+            RalewaySemiBold {
+                id: attributionsLabel
+
+                anchors.top: parent.top
+                anchors.left: parent.left
+                width: paintedWidth
+
+                text: "ATTRIBUTIONS:"
+                size: 14
+                color: hifi.colors.lightGrayText
+                verticalAlignment: Text.AlignVCenter
+            }
+            ListModel {
+                id: attributionsModel
+            }
+
+            ListView {
+                anchors {
+                    left: parent.left
+                    right: parent.right
+                    top: attributionsLabel.bottom
+                    bottomMargin: 15
+                }
+
+                height: 24*model.count+10
+
+                model: attributionsModel
+                delegate: Item {
+				    RalewaySemiBold {
+						id: attributionName
+
+						anchors.leftMargin: 15
+						width: paintedWidth
+
+						text: model.name
+						size: 14
+						height: 24
+						color: hifi.colors.baseGray
+						verticalAlignment: Text.AlignVCenter
+					}
+
+					RalewaySemiBold {
+						id: attributionLink
+
+						anchors.leftMargin: 15
+                        anchors.left: attributionName.right
+						width: paintedWidth
+
+						text: "Link"
+						size: 14
+						height: 24
+						color: hifi.colors.blueHighlight
+						verticalAlignment: Text.AlignVCenter
+
+						MouseArea {
+							anchors.fill: attributionLink
+
+							onClicked: sendToScript({method: 'marketplace_open_link', link: model.link});
+						}
+					}
+                }
+            }
+            Rectangle {
+
+                anchors {
+                    bottom: attributions.bottom
+                    leftMargin: 15
+                }
+                width: parent.width
+                height: 1
+
+                color: hifi.colors.lightGrayText
+            }
+        }
         Item {
             id: licenseItem;
             
             anchors {
-                top: posted.bottom
+                top: attributions.bottom
                 left: parent.left
-                topMargin: 30
+                topMargin: 15
             }
             width: parent.width
             height: childrenRect.height

From e23c589de7f7db344256cc7471d8f29d8ee719ca Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxanne@roxiware.com>
Date: Mon, 4 Feb 2019 15:04:18 -0800
Subject: [PATCH 11/18] QmlMarketplace - fix issue with footer not appearing in
 marketplaces list

---
 .../qml/hifi/commerce/marketplace/MarketplaceItem.qml       | 6 ++++--
 scripts/system/marketplaces/marketplaces.js                 | 2 ++
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
index cb158a2b69..3d5b1c3bc8 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
@@ -262,7 +262,7 @@ Rectangle {
             color: hifi.buttons.blue
             
             onClicked: root.buy();
-        }        
+        }
         
         Item {
             id: creatorItem
@@ -596,7 +596,9 @@ Rectangle {
                             anchors.fill: parent
 
                             Component.onCompleted: {
-                                loadHtml("<html><head><style>body { color: #393939; font-family: Arial !important;}</style></head><body>"+model.text+"</body></html>");
+                                descriptionWebView.enabled = false;
+                                loadHtml("<html><head><style>body { overflow: hidden; color: #393939; font-family: Arial !important;}</style></head><body>"+model.text+"</body></html>");
+                                descriptionWebView.enabled = true;
                             }
 
                             onContentsSizeChanged: {
diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js
index c085763fad..e059081741 100644
--- a/scripts/system/marketplaces/marketplaces.js
+++ b/scripts/system/marketplaces/marketplaces.js
@@ -41,6 +41,7 @@ var GOTO_DIRECTORY = "GOTO_DIRECTORY";
 var GOTO_MARKETPLACE = "GOTO_MARKETPLACE";
 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 CLARA_DOWNLOAD_TITLE = "Preparing Download";
 var messageBox = null;
@@ -436,6 +437,7 @@ function rezEntity(itemHref, itemType, marketplaceItemTesterId) {
 var referrerURL; // Used for updating Purchases QML
 var filterText; // Used for updating Purchases QML
 function onWebEventReceived(message) {
+    message = JSON.parse(message);
     if (message.type === GOTO_MARKETPLACE) {
         openMarketplace(message.itemId);
     } else if (message.type === GOTO_DIRECTORY) {

From db9b44a71af5c1c9689af23d4b556a5ff53ce3dc Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxanne@roxiware.com>
Date: Mon, 4 Feb 2019 15:30:53 -0800
Subject: [PATCH 12/18] QmlMarketplace - The QML Text object has sufficient
 markup  to handle all of our needs, so use that instead of webengineview

---
 .../commerce/marketplace/MarketplaceItem.qml  | 62 ++++---------------
 1 file changed, 11 insertions(+), 51 deletions(-)

diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
index 3d5b1c3bc8..303a193d92 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
@@ -556,61 +556,21 @@ Rectangle {
                 verticalAlignment: Text.AlignVCenter
             }
 
-            //RalewaySemiBold {
-            //    id: descriptionText
-            //    
-            //    anchors.top: descriptionLabel.bottom
-            //    anchors.left: parent.left
-            //    anchors.topMargin: 5
-            //    width: parent.width
-            //
-            //    text: root.description
-            //    size: 14
-            //   color: hifi.colors.lightGray
-            //    verticalAlignment: Text.AlignVCenter
-            //    wrapMode: Text.Wrap
-            //}
-            
-            
-            ListModel {
-                id: descriptionTextModel
-            }
-            
-            ListView {
-                id: descriptionTextView;
+            RalewaySemiBold {
+                id: descriptionText
                 
                 anchors.top: descriptionLabel.bottom
                 anchors.left: parent.left
-                anchors.right: parent.right                
-
-                model: descriptionTextModel
-                interactive: false
+                anchors.topMargin: 5
+                width: parent.width
             
-                delegate: Component {
-                    Rectangle {
-                        id: descriptionWebRect
-                        width: parent.width
-                        height: 5
-                        WebEngineView {
-                            id: descriptionWebView
-                            anchors.fill: parent
-
-                            Component.onCompleted: {
-                                descriptionWebView.enabled = false;
-                                loadHtml("<html><head><style>body { overflow: hidden; color: #393939; font-family: Arial !important;}</style></head><body>"+model.text+"</body></html>");
-                                descriptionWebView.enabled = true;
-                            }
-
-                            onContentsSizeChanged: {
-                                descriptionWebRect.height = contentsSize.height;
-                                descriptionTextView.height = contentsSize.height;
-                            }
-
-                            onNewViewRequested: function(request) {
-                                sendToScript({method: 'marketplace_open_link', link: request.requestedUrl});
-                            }
-                        }
-                    }
+                text: root.description
+                size: 14
+                color: hifi.colors.lightGray
+                verticalAlignment: Text.AlignVCenter
+                wrapMode: Text.Wrap
+                onLinkActivated: {
+                    sendToScript({method: 'marketplace_open_link', link: link});
                 }
             }
         }

From 364a1698feadc52f5509fa605118b874dc3474d7 Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxanne@roxiware.com>
Date: Mon, 4 Feb 2019 17:24:30 -0800
Subject: [PATCH 13/18] QmlMarketplace - Height of Marketplace Item was
 improperly calcualted with attributions

---
 .../commerce/marketplace/MarketplaceItem.qml  | 24 +++++++++++--------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
index 303a193d92..bb757bedca 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
@@ -53,15 +53,13 @@ Rectangle {
 
     onAttributionsChanged: {
         attributionsModel.clear();
-        root.attributions.forEach(function(attribution) {
-            console.log("ATTRIBUITION:" + JSON.stringify(attribution));
-            attributionsModel.append(attribution);
-        });
-    }
-
-    onDescriptionChanged: {
-        descriptionTextModel.clear();
-        descriptionTextModel.append({text: description})
+        if(root.attributions) {
+            root.attributions.forEach(function(attribution) {
+                console.log("ATTRIBUITION:" + JSON.stringify(attribution));
+                attributionsModel.append(attribution);
+            });
+        }
+        footer.evalHeight();
     }
 
     signal buy()
@@ -243,6 +241,7 @@ Rectangle {
         
         function evalHeight() {
             height = categoriesList.y - buyButton.y + categoriesList.height;
+            console.log("HEIGHT: " + height);
         }
         
         HifiControlsUit.Button {
@@ -378,6 +377,7 @@ Rectangle {
                 anchors.top: parent.top
                 anchors.left: parent.left
                 width: paintedWidth
+                height: paintedHeight
 
                 text: "ATTRIBUTIONS:"
                 size: 14
@@ -572,6 +572,8 @@ Rectangle {
                 onLinkActivated: {
                     sendToScript({method: 'marketplace_open_link', link: link});
                 }
+                
+                onHeightChanged: { footer.evalHeight(); }
             }
         }
         
@@ -585,7 +587,9 @@ Rectangle {
                 right: parent.right
             }
             width: parent.width
-            height: childrenRect.height + 50
+            height: categoriesListModel.count*24 + categoryLabel.height + (isLoggedIn ? 50 : 150)
+            
+            onHeightChanged: { footer.evalHeight(); }
 
             RalewaySemiBold {
                 id: categoryLabel

From 5a4960b3001cdb32d88af6b0b4d5ca36a466ccf7 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 5 Feb 2019 08:57:18 -0800
Subject: [PATCH 14/18] add crash::doAssert() for debug purposes

---
 libraries/shared/src/CrashHelpers.cpp | 15 +++++++++++++++
 libraries/shared/src/CrashHelpers.h   |  1 +
 2 files changed, 16 insertions(+)

diff --git a/libraries/shared/src/CrashHelpers.cpp b/libraries/shared/src/CrashHelpers.cpp
index f8ca90bc4c..1676318f3e 100644
--- a/libraries/shared/src/CrashHelpers.cpp
+++ b/libraries/shared/src/CrashHelpers.cpp
@@ -11,6 +11,16 @@
 
 #include "CrashHelpers.h"
 
+#ifdef NDEBUG
+// undefine NDEBUG so doAssert() works for all builds
+#undef NDEBUG
+#include <assert.h>
+#define NDEBUG
+#else
+#include <assert.h>
+#endif
+
+
 namespace crash {
 
 class B;
@@ -34,6 +44,11 @@ A::~A() {
     _b->virtualFunction();
 }
 
+// only use doAssert() for debug purposes
+void doAssert(bool value) {
+    assert(value);
+}
+
 void pureVirtualCall() {
     qCDebug(shared) << "About to make a pure virtual call";
     B b;
diff --git a/libraries/shared/src/CrashHelpers.h b/libraries/shared/src/CrashHelpers.h
index ad988c8906..247aea5cde 100644
--- a/libraries/shared/src/CrashHelpers.h
+++ b/libraries/shared/src/CrashHelpers.h
@@ -18,6 +18,7 @@
 
 namespace crash {
 
+void doAssert(bool value); // works for Release
 void pureVirtualCall();
 void doubleFree();
 void nullDeref();

From e50892b3d2b3b67292fabeda51443fe589a0b740 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 5 Feb 2019 08:57:55 -0800
Subject: [PATCH 15/18] MyAvatar is unmovable until physics is enabled

---
 interface/src/avatar/MyAvatar.cpp | 60 +++++++++++++------------------
 interface/src/avatar/MyAvatar.h   |  1 +
 2 files changed, 26 insertions(+), 35 deletions(-)

diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 92d9270d20..a0deb721e2 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -847,6 +847,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
 
         updateOrientation(deltaTime);
         updatePosition(deltaTime);
+        updateViewBoom();
     }
 
     // update sensorToWorldMatrix for camera and hand controllers
@@ -3323,21 +3324,22 @@ void MyAvatar::updateActionMotor(float deltaTime) {
         direction = Vectors::ZERO;
     }
 
+    float sensorToWorldScale = getSensorToWorldScale();
     if (state == CharacterController::State::Hover) {
         // we're flying --> complex acceleration curve that builds on top of current motor speed and caps at some max speed
 
         float motorSpeed = glm::length(_actionMotorVelocity);
-        float finalMaxMotorSpeed = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_FLYING_SPEED * _walkSpeedScalar;
+        float finalMaxMotorSpeed = sensorToWorldScale * DEFAULT_AVATAR_MAX_FLYING_SPEED * _walkSpeedScalar;
         float speedGrowthTimescale  = 2.0f;
         float speedIncreaseFactor = 1.8f * _walkSpeedScalar;
         motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale, 0.0f, 1.0f) * speedIncreaseFactor;
-        const float maxBoostSpeed = getSensorToWorldScale() * MAX_BOOST_SPEED;
+        const float maxBoostSpeed = sensorToWorldScale * MAX_BOOST_SPEED;
 
         if (_isPushing) {
             if (motorSpeed < maxBoostSpeed) {
                 // an active action motor should never be slower than this
                 float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
-                motorSpeed += getSensorToWorldScale() * MIN_AVATAR_SPEED * boostCoefficient;
+                motorSpeed += sensorToWorldScale * MIN_AVATAR_SPEED * boostCoefficient;
             } else if (motorSpeed > finalMaxMotorSpeed) {
                 motorSpeed = finalMaxMotorSpeed;
             }
@@ -3348,45 +3350,21 @@ void MyAvatar::updateActionMotor(float deltaTime) {
         const glm::vec2 currentVel = { direction.x, direction.z };
         float scaledSpeed = scaleSpeedByDirection(currentVel, _walkSpeed.get(), _walkBackwardSpeed.get());
         // _walkSpeedScalar is a multiplier if we are in sprint mode, otherwise 1.0
-        _actionMotorVelocity = getSensorToWorldScale() * (scaledSpeed * _walkSpeedScalar)  * direction;
-    }
-
-    float previousBoomLength = _boomLength;
-    float boomChange = getDriveKey(ZOOM);
-    _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
-    _boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
-
-    // May need to change view if boom length has changed
-    if (previousBoomLength != _boomLength) {
-        qApp->changeViewAsNeeded(_boomLength);
+        _actionMotorVelocity = sensorToWorldScale * (scaledSpeed * _walkSpeedScalar)  * direction;
     }
 }
 
 void MyAvatar::updatePosition(float deltaTime) {
-    if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
-        updateActionMotor(deltaTime);
-    }
-
-    vec3 velocity = getWorldVelocity();
-    float sensorToWorldScale = getSensorToWorldScale();
-    float sensorToWorldScale2 = sensorToWorldScale * sensorToWorldScale;
-    const float MOVING_SPEED_THRESHOLD_SQUARED = 0.0001f; // 0.01 m/s
-    if (!_characterController.isEnabledAndReady()) {
-        // _characterController is not in physics simulation but it can still compute its target velocity
-        updateMotors();
-        _characterController.computeNewVelocity(deltaTime, velocity);
-
-        float speed2 = glm::length(velocity);
-        if (speed2 > sensorToWorldScale2 * MIN_AVATAR_SPEED_SQUARED) {
-            // update position ourselves
-            applyPositionDelta(deltaTime * velocity);
+    if (_characterController.isEnabledAndReady()) {
+        if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
+            updateActionMotor(deltaTime);
         }
-        measureMotionDerivatives(deltaTime);
-        _moving = speed2 > sensorToWorldScale2 * MOVING_SPEED_THRESHOLD_SQUARED;
-    } else {
+        float sensorToWorldScale = getSensorToWorldScale();
+        float sensorToWorldScale2 = sensorToWorldScale * sensorToWorldScale;
+        vec3 velocity = getWorldVelocity();
         float speed2 = glm::length2(velocity);
+        const float MOVING_SPEED_THRESHOLD_SQUARED = 0.0001f; // 0.01 m/s
         _moving = speed2 > sensorToWorldScale2 * MOVING_SPEED_THRESHOLD_SQUARED;
-
         if (_moving) {
             // scan for walkability
             glm::vec3 position = getWorldPosition();
@@ -3398,6 +3376,18 @@ void MyAvatar::updatePosition(float deltaTime) {
     }
 }
 
+void MyAvatar::updateViewBoom() {
+    float previousBoomLength = _boomLength;
+    float boomChange = getDriveKey(ZOOM);
+    _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
+    _boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
+
+    // May need to change view if boom length has changed
+    if (previousBoomLength != _boomLength) {
+        qApp->changeViewAsNeeded(_boomLength);
+    }
+}
+
 void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) {
     // COLLISION SOUND API in Audio has been removed
 }
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index 0d27988543..c53eae65d4 100755
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -1732,6 +1732,7 @@ private:
     void updateOrientation(float deltaTime);
     void updateActionMotor(float deltaTime);
     void updatePosition(float deltaTime);
+    void updateViewBoom();
     void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
     void initHeadBones();
     void initAnimGraph();

From 2b402609535e3b758df1ecd707ae9f162abd54d6 Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxanne@highfidelity.io>
Date: Tue, 5 Feb 2019 09:27:12 -0800
Subject: [PATCH 16/18] QmlMarketplace - disable HTML for quest

---
 .../hifi/commerce/marketplace/Marketplace.qml | 43 +++++++++++----
 .../commerce/marketplace/MarketplaceItem.qml  | 54 +++++++++++--------
 interface/src/Application.cpp                 |  7 +++
 .../PlatformInfoScriptingInterface.cpp        |  8 +++
 .../PlatformInfoScriptingInterface.h          |  6 +++
 5 files changed, 85 insertions(+), 33 deletions(-)

diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
index a4cf260173..1db133c249 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
@@ -39,7 +39,8 @@ Rectangle {
     property bool keyboardRaised: false
     property string searchScopeString: "Featured"
     property bool isLoggedIn: false;
-    
+    property bool supports3DHTML: true;
+
     anchors.fill: (typeof parent === undefined) ? undefined : parent
 
     function getMarketplaceItems() {
@@ -60,6 +61,8 @@ Rectangle {
 
     Component.onCompleted: {
         Commerce.getLoginStatus();
+        
+        supports3DHTML = PlatformInfo.has3DHTML();
     }
 
     Component.onDestruction: {
@@ -103,7 +106,7 @@ Rectangle {
             if (result.status !== 'success') {
                 console.log("Failed to get Marketplace Item", result.data.message);
             } else {
-            
+                marketplaceItem.supports3DHTML = root.supports3DHTML;
                 marketplaceItem.item_id = result.data.id;
                 marketplaceItem.image_url = result.data.thumbnail_url;
                 marketplaceItem.name = result.data.title;
@@ -958,8 +961,16 @@ Rectangle {
                     }
                     
                     onShowLicense: {
-                        licenseInfoWebView.url = url;
-                        licenseInfo.visible = true;
+                        var xhr = new XMLHttpRequest;
+                        xhr.open("GET", url);
+                        xhr.onreadystatechange = function() {
+                            if (xhr.readyState == XMLHttpRequest.DONE) {
+                                console.log(xhr.responseText);
+                                licenseText.text = xhr.responseText;
+                                licenseInfo.visible = true;
+                            }
+                        };
+                        xhr.send();
                     }
                     onCategoryClicked: {
                         root.categoryString = category;
@@ -1001,11 +1012,13 @@ Rectangle {
                 leftMargin: 15
             }
 
+
+
             Item {
                 id: footerText
 
                 anchors.fill: parent
-                visible: itemsList.visible
+                visible: root.supports3DHTML && itemsList.visible
 
                 HiFiGlyphs {
                     id: footerGlyph
@@ -1072,6 +1085,8 @@ Rectangle {
                     rightMargin: 10
                 }
 
+                visible: root.supports3DHTML
+
                 text: "SEE ALL MARKETS"
                 width: 180
 
@@ -1100,16 +1115,24 @@ Rectangle {
 
         visible: false;
 
-        HifiControlsUit.WebView {
-            id: licenseInfoWebView
-            
+        ScrollView {
             anchors {
                 bottomMargin: 1
                 topMargin: 60
-                leftMargin: 1
-                rightMargin: 1
+                leftMargin: 15
                 fill: parent
             }
+
+            RalewayRegular {
+                id: licenseText
+
+                width:440
+                wrapMode: Text.Wrap
+
+                text: ""
+                size: 18;
+                color: hifi.colors.baseGray
+            }
         }
 
         Item {
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
index b7e9a711d2..0c6d661609 100644
--- a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
@@ -43,6 +43,8 @@ Rectangle {
     property string created_at: ""
     property bool isLoggedIn: false;
     property int edition: -1;
+    property bool supports3DHTML: false;
+
     
     onCategoriesChanged: {
         categoriesListModel.clear();
@@ -52,8 +54,13 @@ Rectangle {
     }
     
     onDescriptionChanged: {
-        descriptionTextModel.clear();
-        descriptionTextModel.append({text: description})
+    
+        if(root.supports3DHTML) {
+            descriptionTextModel.clear();
+            descriptionTextModel.append({text: description});
+        } else {
+            descriptionText.text = description;
+        }
     }
 
     signal buy()
@@ -410,22 +417,22 @@ Rectangle {
                         if (root.license === "No Rights Reserved (CC0)") {
                           url = "https://creativecommons.org/publicdomain/zero/1.0/"
                         } else if (root.license === "Attribution (CC BY)") {
-                          url = "https://creativecommons.org/licenses/by/4.0/"
+                          url = "https://creativecommons.org/licenses/by/4.0/legalcode.txt"
                         } else if (root.license === "Attribution-ShareAlike (CC BY-SA)") {
-                          url = "https://creativecommons.org/licenses/by-sa/4.0/"
+                          url = "https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt"
                         } else if (root.license === "Attribution-NoDerivs (CC BY-ND)") {
-                          url = "https://creativecommons.org/licenses/by-nd/4.0/"
+                          url = "https://creativecommons.org/licenses/by-nd/4.0/legalcode.txt"
                         } else if (root.license === "Attribution-NonCommercial (CC BY-NC)") {
-                          url = "https://creativecommons.org/licenses/by-nc/4.0/"
+                          url = "https://creativecommons.org/licenses/by-nc/4.0/legalcode.txt"
                         } else if (root.license === "Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)") {
-                          url = "https://creativecommons.org/licenses/by-nc-sa/4.0/"
+                          url = "https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt"
                         } else if (root.license === "Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)") {
-                          url = "https://creativecommons.org/licenses/by-nc-nd/4.0/"
+                          url = "https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode.txt"
                         } else if (root.license === "Proof of Provenance License (PoP License)") {
                           url = "https://digitalassetregistry.com/PoP-License/v1/"
                         }
                         if(url) {
-                            licenseInfoWebView.url = url;
+                            showLicense(url)
                         }
                     }
                 }
@@ -460,20 +467,21 @@ Rectangle {
                 verticalAlignment: Text.AlignVCenter
             }
 
-            //RalewaySemiBold {
-            //    id: descriptionText
-            //    
-            //    anchors.top: descriptionLabel.bottom
-            //    anchors.left: parent.left
-            //    anchors.topMargin: 5
-            //    width: parent.width
-            //
-            //    text: root.description
-            //    size: 14
-            //   color: hifi.colors.lightGray
-            //    verticalAlignment: Text.AlignVCenter
-            //    wrapMode: Text.Wrap
-            //}
+            RalewaySemiBold {
+                id: descriptionText
+
+                anchors.top: descriptionLabel.bottom
+                anchors.left: parent.left
+                anchors.topMargin: 5
+                width: parent.width
+
+                visible: !root.supports3DHTML
+
+                text: root.description
+                size: 14
+                color: hifi.colors.lightGray
+                wrapMode: Text.Wrap
+            }
             
             
             ListModel {
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 39ab3a8b1c..48c8f35ede 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -3000,6 +3000,13 @@ void Application::initializeUi() {
         QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
         }, marketplaceCallback);
 
+    QmlContextCallback platformInfoCallback = [](QQmlContext* context) {
+        context->setContextProperty("PlatformInfo", new PlatformInfoScriptingInterface());
+    };
+    OffscreenQmlSurface::addWhitelistContextHandler({
+        QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
+        }, platformInfoCallback);
+
     QmlContextCallback ttsCallback = [](QQmlContext* context) {
         context->setContextProperty("TextToSpeech", DependencyManager::get<TTSScriptingInterface>().data());
     };
diff --git a/interface/src/scripting/PlatformInfoScriptingInterface.cpp b/interface/src/scripting/PlatformInfoScriptingInterface.cpp
index b6e4df0d40..b390ab7119 100644
--- a/interface/src/scripting/PlatformInfoScriptingInterface.cpp
+++ b/interface/src/scripting/PlatformInfoScriptingInterface.cpp
@@ -133,3 +133,11 @@ bool PlatformInfoScriptingInterface::hasRiftControllers() {
 bool PlatformInfoScriptingInterface::hasViveControllers() {
     return qApp->hasViveControllers();
 }
+
+bool PlatformInfoScriptingInterface::has3DHTML() {
+#if defined(Q_OS_ANDROID)
+    return false;
+#else
+    return true;
+#endif
+}
diff --git a/interface/src/scripting/PlatformInfoScriptingInterface.h b/interface/src/scripting/PlatformInfoScriptingInterface.h
index 3ed57965c9..aece09b008 100644
--- a/interface/src/scripting/PlatformInfoScriptingInterface.h
+++ b/interface/src/scripting/PlatformInfoScriptingInterface.h
@@ -65,6 +65,12 @@ public slots:
     * @function Window.hasRift
     * @returns {boolean} <code>true</code> if running on Windows, otherwise <code>false</code>.*/
     bool hasViveControllers();
+
+    /**jsdoc
+    * Returns true if device supports 3d HTML
+    * @function Window.hasRift
+    * @returns {boolean} <code>true</code> if device supports 3d HTML, otherwise <code>false</code>.*/
+    bool has3DHTML();
 };
 
 #endif  // hifi_PlatformInfoScriptingInterface_h

From cbd83f972c312107bca27bb1d7ee6d7f07c5a60b Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 5 Feb 2019 10:17:10 -0800
Subject: [PATCH 17/18] remove unsued variable

---
 interface/src/avatar/MyAvatar.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index a0deb721e2..098d7943c7 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -75,7 +75,6 @@ const float PITCH_SPEED_DEFAULT = 75.0f; // degrees/sec
 
 const float MAX_BOOST_SPEED = 0.5f * DEFAULT_AVATAR_MAX_WALKING_SPEED; // action motor gets additive boost below this speed
 const float MIN_AVATAR_SPEED = 0.05f;
-const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this
 
 float MIN_SCRIPTED_MOTOR_TIMESCALE = 0.005f;
 float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f;

From 16ef30ced0fb7bc9ec0619b1e6649e2d4d701dc8 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 5 Feb 2019 17:14:25 -0800
Subject: [PATCH 18/18] don't flushRepeatedMessages() in LogHandler dtor

---
 libraries/shared/src/LogHandler.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libraries/shared/src/LogHandler.cpp b/libraries/shared/src/LogHandler.cpp
index 65651373be..c51d9bf611 100644
--- a/libraries/shared/src/LogHandler.cpp
+++ b/libraries/shared/src/LogHandler.cpp
@@ -38,7 +38,6 @@ LogHandler::LogHandler() {
 }
 
 LogHandler::~LogHandler() {
-    flushRepeatedMessages();
 }
 
 const char* stringForLogType(LogMsgType msgType) {