From 93e32f1bd58bc1a0a3e26e9d7b8b6771eaac7cee Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@alumni.brown.edu>
Date: Wed, 30 Jan 2019 12:25:48 -0800
Subject: [PATCH 01/14] fix skybox depth testing

---
 libraries/procedural/src/procedural/ProceduralSkybox.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp
index 211f6ca0a2..6eb6d531e1 100644
--- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp
+++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp
@@ -26,6 +26,7 @@ ProceduralSkybox::ProceduralSkybox() : graphics::Skybox() {
     const int8_t STENCIL_BACKGROUND = 0;
     _procedural._opaqueState->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_BACKGROUND, 0xFF, gpu::EQUAL,
         gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
+    _procedural._opaqueState->setDepthTest(gpu::State::DepthTest(false));
 }
 
 bool ProceduralSkybox::empty() {

From 08b21109c1daac786a67490bfef658a359bf47bb Mon Sep 17 00:00:00 2001
From: Simon Walton <simon@highfidelity.io>
Date: Wed, 30 Jan 2019 13:55:12 -0800
Subject: [PATCH 02/14] Include the new scale float in min remaining size
 calculation

---
 libraries/avatars/src/AvatarData.cpp | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index 4e95774efb..c733cfa291 100755
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -632,9 +632,11 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
 
     // include jointData if there is room for the most minimal section. i.e. no translations or rotations.
     IF_AVATAR_SPACE(PACKET_HAS_JOINT_DATA, AvatarDataPacket::minJointDataSize(numJoints)) {
-        // Allow for faux joints + translation bit-vector:
-        const ptrdiff_t minSizeForJoint = sizeof(AvatarDataPacket::SixByteQuat)
-            + jointBitVectorSize + AvatarDataPacket::FAUX_JOINTS_SIZE;
+        // Minimum space required for another rotation joint -
+        // size of joint + following translation bit-vector + translation scale + faux joints:
+        const ptrdiff_t minSizeForJoint = sizeof(AvatarDataPacket::SixByteQuat) + jointBitVectorSize +
+            sizeof(float) + AvatarDataPacket::FAUX_JOINTS_SIZE;
+
         auto startSection = destinationBuffer;
 
         // compute maxTranslationDimension before we send any joint data.
@@ -724,6 +726,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
             const JointData& data = joints[i];
             const JointData& last = lastSentJointData[i];
 
+            // Note minSizeForJoint is conservative since there isn't a following bit-vector + scale.
             if (packetEnd - destinationBuffer >= minSizeForJoint) {
                 if (!data.translationIsDefaultPose) {
                     if (sendAll || last.translationIsDefaultPose || (!cullSmallChanges && last.translation != data.translation)

From 93a91cdba2802a0b0f9fd6da063ac8bd0749e4eb Mon Sep 17 00:00:00 2001
From: Dante Ruiz <danteruiz102@gmail.com>
Date: Tue, 29 Jan 2019 15:40:45 -0800
Subject: [PATCH 03/14] webengine fileselector

---
 .../resources/qml/+webengine/Browser.qml      | 275 ++++++++++++++++++
 .../resources/qml/+webengine/InfoView.qml     |  50 ++++
 .../qml/+webengine/TabletBrowser.qml          | 125 ++++++++
 interface/resources/qml/Browser.qml           |  69 +----
 interface/resources/qml/InfoView.qml          |   3 +-
 interface/resources/qml/TabletBrowser.qml     |  97 +-----
 .../+webengine/FlickableWebViewCore.qml       | 189 ++++++++++++
 .../qml/controls/FlickableWebViewCore.qml     | 141 +--------
 .../resources/qml/controls/TabletWebView.qml  |   1 -
 .../controlsUit/+webengine/BaseWebView.qml    |  38 +++
 .../resources/qml/controlsUit/BaseWebView.qml |  27 +-
 .../qml/controlsUit/ProxyWebView.qml          |  30 ++
 interface/resources/qml/controlsUit/qmldir    |   1 +
 libraries/shared/src/shared/FileUtils.cpp     |   4 +
 libraries/ui/src/ui/OffscreenQmlSurface.cpp   |   5 +-
 15 files changed, 730 insertions(+), 325 deletions(-)
 create mode 100644 interface/resources/qml/+webengine/Browser.qml
 create mode 100644 interface/resources/qml/+webengine/InfoView.qml
 create mode 100644 interface/resources/qml/+webengine/TabletBrowser.qml
 create mode 100644 interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml
 create mode 100644 interface/resources/qml/controlsUit/+webengine/BaseWebView.qml
 create mode 100644 interface/resources/qml/controlsUit/ProxyWebView.qml

diff --git a/interface/resources/qml/+webengine/Browser.qml b/interface/resources/qml/+webengine/Browser.qml
new file mode 100644
index 0000000000..52157bdf42
--- /dev/null
+++ b/interface/resources/qml/+webengine/Browser.qml
@@ -0,0 +1,275 @@
+import QtQuick 2.5
+import QtWebChannel 1.0
+import QtWebEngine 1.5
+
+import controlsUit 1.0
+import stylesUit 1.0
+import "qrc:////qml//windows"
+
+ScrollingWindow {
+    id: root
+    HifiConstants { id: hifi }
+    //HifiStyles.HifiConstants { id: hifistyles }
+    title: "Browser"
+    resizable: true
+    destroyOnHidden: true
+    width: 800
+    height: 600
+    property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
+    property alias url: webview.url
+    property alias webView: webview
+
+    signal loadingChanged(int status)
+
+    x: 100
+    y: 100
+
+    Component.onCompleted: {
+        focus = true
+        shown = true
+        addressBar.text = webview.url
+    }
+
+    function setProfile(profile) {
+        webview.profile = profile;
+    }
+
+    function showPermissionsBar(){
+        permissionsContainer.visible=true;
+    }
+
+    function hidePermissionsBar(){
+      permissionsContainer.visible=false;
+    }
+
+    function allowPermissions(){
+        webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true);
+        hidePermissionsBar();
+    }
+
+    function setAutoAdd(auto) {
+        desktop.setAutoAdd(auto);
+    }
+
+    Item {
+        id:item
+        width: pane.contentWidth
+        implicitHeight: pane.scrollHeight
+
+        Row {
+            id: buttons
+            spacing: 4
+            anchors.top: parent.top
+            anchors.topMargin: 8
+            anchors.left: parent.left
+            anchors.leftMargin: 8
+            HiFiGlyphs {
+                id: back;
+                enabled: webview.canGoBack;
+                text: hifi.glyphs.backward
+                color: enabled ? hifi.colors.text : hifi.colors.disabledText
+                size: 48
+                MouseArea { anchors.fill: parent;  onClicked: webview.goBack() }
+            }
+
+            HiFiGlyphs {
+                id: forward;
+                enabled: webview.canGoForward;
+                text: hifi.glyphs.forward
+                color: enabled ? hifi.colors.text : hifi.colors.disabledText
+                size: 48
+                MouseArea { anchors.fill: parent;  onClicked: webview.goForward() }
+            }
+
+            HiFiGlyphs {
+                id: reload;
+                enabled: webview.canGoForward;
+                text: webview.loading ? hifi.glyphs.close : hifi.glyphs.reload
+                color: enabled ? hifi.colors.text : hifi.colors.disabledText
+                size: 48
+                MouseArea { anchors.fill: parent;  onClicked: webview.goForward() }
+            }
+
+        }
+
+        Item {
+            id: border
+            height: 48
+            anchors.top: parent.top
+            anchors.topMargin: 8
+            anchors.right: parent.right
+            anchors.rightMargin: 8
+            anchors.left: buttons.right
+            anchors.leftMargin: 8
+    
+            Item {
+                id: barIcon
+                width: parent.height
+                height: parent.height
+                Image {
+                    source: webview.icon;
+                    x: (parent.height - height) / 2
+                    y: (parent.width - width) / 2
+                    sourceSize: Qt.size(width, height);
+                    verticalAlignment: Image.AlignVCenter;
+                    horizontalAlignment: Image.AlignHCenter
+                }
+            }
+
+            TextField {
+                id: addressBar
+                anchors.right: parent.right
+                anchors.rightMargin: 8
+                anchors.left: barIcon.right
+                anchors.leftMargin: 0
+                anchors.verticalCenter: parent.verticalCenter
+                focus: true
+                colorScheme: hifi.colorSchemes.dark
+                placeholderText: "Enter URL"
+                Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i")
+                Keys.onPressed: {
+                    switch(event.key) {
+                        case Qt.Key_Enter:
+                        case Qt.Key_Return:
+                            event.accepted = true
+                            if (text.indexOf("http") != 0) {
+                                text = "http://" + text;
+                            }
+                            root.hidePermissionsBar();
+                            root.keyboardRaised = false;
+                            webview.url = text;
+                            break;
+                    }
+                }
+            }
+        }
+
+        Rectangle {
+            id:permissionsContainer
+            visible:false
+            color: "#000000"
+            width:  parent.width
+            anchors.top: buttons.bottom
+            height:40
+            z:100
+            gradient: Gradient {
+                GradientStop { position: 0.0; color: "black" }
+                GradientStop { position: 1.0; color: "grey" }
+            }
+
+            RalewayLight {
+                    id: permissionsInfo
+                    anchors.right:permissionsRow.left
+                    anchors.rightMargin: 32
+                    anchors.topMargin:8
+                    anchors.top:parent.top
+                    text: "This site wants to use your microphone/camera"
+                    size: 18
+                    color: hifi.colors.white
+            }
+         
+            Row {
+                id: permissionsRow
+                spacing: 4
+                anchors.top:parent.top
+                anchors.topMargin: 8
+                anchors.right: parent.right
+                visible: true
+                z:101
+                
+                Button {
+                    id:allow
+                    text: "Allow"
+                    color: hifi.buttons.blue
+                    colorScheme: root.colorScheme
+                    width: 120
+                    enabled: true
+                    onClicked: root.allowPermissions(); 
+                    z:101
+                }
+
+                Button {
+                    id:block
+                    text: "Block"
+                    color: hifi.buttons.red
+                    colorScheme: root.colorScheme
+                    width: 120
+                    enabled: true
+                    onClicked: root.hidePermissionsBar();
+                    z:101
+                }
+            }
+        }
+
+        WebView {
+            id: webview
+            url: "https://highfidelity.com/"
+            profile: FileTypeProfile;
+
+            // Create a global EventBridge object for raiseAndLowerKeyboard.
+            WebEngineScript {
+                id: createGlobalEventBridge
+                sourceCode: eventBridgeJavaScriptToInject
+                injectionPoint: WebEngineScript.Deferred
+                worldId: WebEngineScript.MainWorld
+            }
+
+            // Detect when may want to raise and lower keyboard.
+            WebEngineScript {
+                id: raiseAndLowerKeyboard
+                injectionPoint: WebEngineScript.Deferred
+                sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
+                worldId: WebEngineScript.MainWorld
+            }
+
+            userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
+
+            anchors.top: buttons.bottom
+            anchors.topMargin: 8
+            anchors.bottom: parent.bottom
+            anchors.left: parent.left
+            anchors.right: parent.right
+
+            onFeaturePermissionRequested: {
+                if (feature == 2) { // QWebEnginePage::MediaAudioCapture
+                    grantFeaturePermission(securityOrigin, feature, true);
+                } else {
+                    permissionsBar.securityOrigin = securityOrigin;
+                    permissionsBar.feature = feature;
+                    root.showPermissionsBar();
+                }
+            }
+
+            onLoadingChanged: {
+                if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
+                    addressBar.text = loadRequest.url
+                }
+                root.loadingChanged(loadRequest.status);
+            }
+
+            onWindowCloseRequested: {
+                root.destroy();
+            }
+
+            Component.onCompleted: {
+                webChannel.registerObject("eventBridge", eventBridge);
+                webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
+                desktop.initWebviewProfileHandlers(webview.profile);
+            }
+        }
+
+    } // item
+
+
+    Keys.onPressed: {
+        switch(event.key) {
+            case Qt.Key_L:
+                if (event.modifiers == Qt.ControlModifier) {
+                    event.accepted = true
+                    addressBar.selectAll()
+                    addressBar.forceActiveFocus()
+                }
+                break;
+        }
+    }
+} // dialog
diff --git a/interface/resources/qml/+webengine/InfoView.qml b/interface/resources/qml/+webengine/InfoView.qml
new file mode 100644
index 0000000000..eb190c3c45
--- /dev/null
+++ b/interface/resources/qml/+webengine/InfoView.qml
@@ -0,0 +1,50 @@
+//
+//  InfoView.qml
+//
+//  Created by Bradley Austin Davis on 27 Apr 2015
+//  Copyright 2015 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 QtQuick 2.5
+import Hifi 1.0 as Hifi
+
+import controlsUit 1.0
+import "qrc:////qml//windows" as Windows
+
+Windows.ScrollingWindow {
+    id: root
+    width: 800
+    height: 800
+    resizable: true
+    
+    Hifi.InfoView {
+        id: infoView
+        width: pane.contentWidth
+        implicitHeight: pane.scrollHeight
+
+        WebView {
+            id: webview
+            objectName: "WebView"
+            anchors.fill: parent
+            url: infoView.url
+        }
+    }
+
+    Component.onCompleted: {
+        centerWindow(root);
+    }
+
+    onVisibleChanged: {
+        if (visible) {
+            centerWindow(root);
+        }
+    }
+
+    function centerWindow() {
+        desktop.centerOnVisible(root);
+    }
+
+}
diff --git a/interface/resources/qml/+webengine/TabletBrowser.qml b/interface/resources/qml/+webengine/TabletBrowser.qml
new file mode 100644
index 0000000000..720a904231
--- /dev/null
+++ b/interface/resources/qml/+webengine/TabletBrowser.qml
@@ -0,0 +1,125 @@
+import QtQuick 2.5
+import QtWebChannel 1.0
+import QtWebEngine 1.5
+
+import "controls"
+import controlsUit 1.0 as HifiControls
+import "styles" as HifiStyles
+import stylesUit 1.0
+import "windows"
+
+Item {
+    id: root
+    HifiConstants { id: hifi }
+    HifiStyles.HifiConstants { id: hifistyles }
+
+    height: 600
+    property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
+    property alias url: webview.url
+
+    property bool canGoBack: webview.canGoBack
+    property bool canGoForward: webview.canGoForward
+
+
+    signal loadingChanged(int status)
+
+    x: 0
+    y: 0
+
+    function setProfile(profile) {
+        webview.profile = profile;
+    }
+
+    WebEngineView {
+        id: webview
+        objectName: "webEngineView"
+        x: 0
+        y: 0
+        width: parent.width
+        height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
+
+        profile: HFWebEngineProfile;
+
+        property string userScriptUrl: ""
+
+        // creates a global EventBridge object.
+        WebEngineScript {
+            id: createGlobalEventBridge
+            sourceCode: eventBridgeJavaScriptToInject
+            injectionPoint: WebEngineScript.DocumentCreation
+            worldId: WebEngineScript.MainWorld
+        }
+
+        // detects when to raise and lower virtual keyboard
+        WebEngineScript {
+            id: raiseAndLowerKeyboard
+            injectionPoint: WebEngineScript.Deferred
+            sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
+            worldId: WebEngineScript.MainWorld
+        }
+
+        // User script.
+        WebEngineScript {
+            id: userScript
+            sourceUrl: webview.userScriptUrl
+            injectionPoint: WebEngineScript.DocumentReady  // DOM ready but page load may not be finished.
+            worldId: WebEngineScript.MainWorld
+        }
+
+        userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
+
+        property string newUrl: ""
+
+        Component.onCompleted: {
+            webChannel.registerObject("eventBridge", eventBridge);
+            webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
+
+            // Ensure the JS from the web-engine makes it to our logging
+            webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
+                console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " +  message);
+            });
+
+            webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
+            web.address = url;
+        }
+
+        onFeaturePermissionRequested: {
+            grantFeaturePermission(securityOrigin, feature, true);
+        }
+
+        onLoadingChanged: {
+            keyboardRaised = false;
+            punctuationMode = false;
+            keyboard.resetShiftMode(false);
+
+            // Required to support clicking on "hifi://" links
+            if (WebEngineView.LoadStartedStatus == loadRequest.status) {
+                urlAppend(loadRequest.url.toString())
+                var url = loadRequest.url.toString();
+                if (urlHandler.canHandleUrl(url)) {
+                    if (urlHandler.handleUrl(url)) {
+                        root.stop();
+                    }
+                }
+            }
+        }
+
+        onNewViewRequested: {
+            request.openIn(webView);
+        }
+
+        HifiControls.WebSpinner { }
+    }
+
+    Keys.onPressed: {
+        switch(event.key) {
+        case Qt.Key_L:
+            if (event.modifiers == Qt.ControlModifier) {
+                event.accepted = true
+                addressBar.selectAll()
+                addressBar.forceActiveFocus()
+            }
+            break;
+        }
+    }
+}
diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml
index 4ddee15a10..78ffb51e67 100644
--- a/interface/resources/qml/Browser.qml
+++ b/interface/resources/qml/Browser.qml
@@ -1,16 +1,14 @@
 import QtQuick 2.5
-import QtWebChannel 1.0
-import QtWebEngine 1.5
 
 import controlsUit 1.0
-import "styles" as HifiStyles
 import stylesUit 1.0
+
 import "windows"
 
 ScrollingWindow {
     id: root
     HifiConstants { id: hifi }
-    HifiStyles.HifiConstants { id: hifistyles }
+    //HifiStyles.HifiConstants { id: hifistyles }
     title: "Browser"
     resizable: true
     destroyOnHidden: true
@@ -32,7 +30,6 @@ ScrollingWindow {
     }
 
     function setProfile(profile) {
-        webview.profile = profile;
     }
 
     function showPermissionsBar(){
@@ -44,7 +41,6 @@ ScrollingWindow {
     }
 
     function allowPermissions(){
-        webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true);
         hidePermissionsBar();
     }
 
@@ -68,7 +64,7 @@ ScrollingWindow {
                 id: back;
                 enabled: webview.canGoBack;
                 text: hifi.glyphs.backward
-                color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText
+                color: enabled ? hifi.colors.text : hifi.colors.disabledText
                 size: 48
                 MouseArea { anchors.fill: parent;  onClicked: webview.goBack() }
             }
@@ -77,7 +73,7 @@ ScrollingWindow {
                 id: forward;
                 enabled: webview.canGoForward;
                 text: hifi.glyphs.forward
-                color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText
+                color: enabled ? hifi.colors.text : hifi.colors.disabledText
                 size: 48
                 MouseArea { anchors.fill: parent;  onClicked: webview.goForward() }
             }
@@ -86,7 +82,7 @@ ScrollingWindow {
                 id: reload;
                 enabled: webview.canGoForward;
                 text: webview.loading ? hifi.glyphs.close : hifi.glyphs.reload
-                color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText
+                color: enabled ? hifi.colors.text : hifi.colors.disabledText
                 size: 48
                 MouseArea { anchors.fill: parent;  onClicked: webview.goForward() }
             }
@@ -202,61 +198,10 @@ ScrollingWindow {
             }
         }
 
-        WebView {
+        ProxyWebView {
             id: webview
+            anchors.centerIn: parent
             url: "https://highfidelity.com/"
-            profile: FileTypeProfile;
-
-            // Create a global EventBridge object for raiseAndLowerKeyboard.
-            WebEngineScript {
-                id: createGlobalEventBridge
-                sourceCode: eventBridgeJavaScriptToInject
-                injectionPoint: WebEngineScript.Deferred
-                worldId: WebEngineScript.MainWorld
-            }
-
-            // Detect when may want to raise and lower keyboard.
-            WebEngineScript {
-                id: raiseAndLowerKeyboard
-                injectionPoint: WebEngineScript.Deferred
-                sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
-                worldId: WebEngineScript.MainWorld
-            }
-
-            userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
-
-            anchors.top: buttons.bottom
-            anchors.topMargin: 8
-            anchors.bottom: parent.bottom
-            anchors.left: parent.left
-            anchors.right: parent.right
-
-            onFeaturePermissionRequested: {
-                if (feature == 2) { // QWebEnginePage::MediaAudioCapture
-                    grantFeaturePermission(securityOrigin, feature, true);
-                } else {
-                    permissionsBar.securityOrigin = securityOrigin;
-                    permissionsBar.feature = feature;
-                    root.showPermissionsBar();
-                }
-            }
-
-            onLoadingChanged: {
-                if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
-                    addressBar.text = loadRequest.url
-                }
-                root.loadingChanged(loadRequest.status);
-            }
-
-            onWindowCloseRequested: {
-                root.destroy();
-            }
-
-            Component.onCompleted: {
-                webChannel.registerObject("eventBridge", eventBridge);
-                webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
-                desktop.initWebviewProfileHandlers(webview.profile);
-            }
         }
 
     } // item
diff --git a/interface/resources/qml/InfoView.qml b/interface/resources/qml/InfoView.qml
index 8c5900b4c3..5c2c7fcff9 100644
--- a/interface/resources/qml/InfoView.qml
+++ b/interface/resources/qml/InfoView.qml
@@ -19,13 +19,12 @@ Windows.ScrollingWindow {
     width: 800
     height: 800
     resizable: true
-    
     Hifi.InfoView {
         id: infoView
         width: pane.contentWidth
         implicitHeight: pane.scrollHeight
 
-        WebView {
+        ProxyWebView {
             id: webview
             objectName: "WebView"
             anchors.fill: parent
diff --git a/interface/resources/qml/TabletBrowser.qml b/interface/resources/qml/TabletBrowser.qml
index 720a904231..f83a9c81a5 100644
--- a/interface/resources/qml/TabletBrowser.qml
+++ b/interface/resources/qml/TabletBrowser.qml
@@ -1,17 +1,11 @@
 import QtQuick 2.5
-import QtWebChannel 1.0
-import QtWebEngine 1.5
 
-import "controls"
 import controlsUit 1.0 as HifiControls
-import "styles" as HifiStyles
 import stylesUit 1.0
-import "windows"
 
 Item {
     id: root
     HifiConstants { id: hifi }
-    HifiStyles.HifiConstants { id: hifistyles }
 
     height: 600
     property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
@@ -30,96 +24,9 @@ Item {
         webview.profile = profile;
     }
 
-    WebEngineView {
+    HifiControls.ProxyWebView {
         id: webview
-        objectName: "webEngineView"
-        x: 0
-        y: 0
         width: parent.width
-        height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
-
-        profile: HFWebEngineProfile;
-
-        property string userScriptUrl: ""
-
-        // creates a global EventBridge object.
-        WebEngineScript {
-            id: createGlobalEventBridge
-            sourceCode: eventBridgeJavaScriptToInject
-            injectionPoint: WebEngineScript.DocumentCreation
-            worldId: WebEngineScript.MainWorld
-        }
-
-        // detects when to raise and lower virtual keyboard
-        WebEngineScript {
-            id: raiseAndLowerKeyboard
-            injectionPoint: WebEngineScript.Deferred
-            sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
-            worldId: WebEngineScript.MainWorld
-        }
-
-        // User script.
-        WebEngineScript {
-            id: userScript
-            sourceUrl: webview.userScriptUrl
-            injectionPoint: WebEngineScript.DocumentReady  // DOM ready but page load may not be finished.
-            worldId: WebEngineScript.MainWorld
-        }
-
-        userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
-
-        property string newUrl: ""
-
-        Component.onCompleted: {
-            webChannel.registerObject("eventBridge", eventBridge);
-            webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
-
-            // Ensure the JS from the web-engine makes it to our logging
-            webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
-                console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " +  message);
-            });
-
-            webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
-            web.address = url;
-        }
-
-        onFeaturePermissionRequested: {
-            grantFeaturePermission(securityOrigin, feature, true);
-        }
-
-        onLoadingChanged: {
-            keyboardRaised = false;
-            punctuationMode = false;
-            keyboard.resetShiftMode(false);
-
-            // Required to support clicking on "hifi://" links
-            if (WebEngineView.LoadStartedStatus == loadRequest.status) {
-                urlAppend(loadRequest.url.toString())
-                var url = loadRequest.url.toString();
-                if (urlHandler.canHandleUrl(url)) {
-                    if (urlHandler.handleUrl(url)) {
-                        root.stop();
-                    }
-                }
-            }
-        }
-
-        onNewViewRequested: {
-            request.openIn(webView);
-        }
-
-        HifiControls.WebSpinner { }
-    }
-
-    Keys.onPressed: {
-        switch(event.key) {
-        case Qt.Key_L:
-            if (event.modifiers == Qt.ControlModifier) {
-                event.accepted = true
-                addressBar.selectAll()
-                addressBar.forceActiveFocus()
-            }
-            break;
-        }
+        height: parent.height
     }
 }
diff --git a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml
new file mode 100644
index 0000000000..823d0107a2
--- /dev/null
+++ b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml
@@ -0,0 +1,189 @@
+import QtQuick 2.7
+import QtWebEngine 1.5
+import QtWebChannel 1.0
+
+import QtQuick.Controls 2.2
+
+import stylesUit 1.0 as StylesUIt
+
+Item {
+    id: flick
+
+    property alias url: webViewCore.url
+    property alias canGoBack: webViewCore.canGoBack
+    property alias webViewCore: webViewCore
+    property alias webViewCoreProfile: webViewCore.profile
+    property string webViewCoreUserAgent
+
+    property string userScriptUrl: ""
+    property string urlTag: "noDownload=false";
+
+    signal newViewRequestedCallback(var request)
+    signal loadingChangedCallback(var loadRequest)
+
+
+    width: parent.width
+
+    property bool interactive: false
+
+    property bool blurOnCtrlShift: true
+
+    StylesUIt.HifiConstants {
+        id: hifi
+    }
+
+    function stop() {
+        webViewCore.stop();
+    }
+
+    Timer {
+        id: delayedUnfocuser
+        repeat: false
+        interval: 200
+        onTriggered: {
+
+            // The idea behind this is to delay unfocusing, so that fast lower/raise will not result actual unfocusing.
+            // Fast lower/raise happens every time keyboard is being re-raised (see the code below in OffscreenQmlSurface::setKeyboardRaised)
+            //
+            // if (raised) {
+            //    item->setProperty("keyboardRaised", QVariant(!raised));
+            // }
+            //
+            // item->setProperty("keyboardRaised", QVariant(raised));
+            //
+
+            webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
+                console.log('unfocus completed: ', result);
+            });
+        }
+    }
+
+    function unfocus() {
+        delayedUnfocuser.start();
+    }
+
+    function stopUnfocus() {
+        delayedUnfocuser.stop();
+    }
+
+    function onLoadingChanged(loadRequest) {
+        if (WebEngineView.LoadStartedStatus === loadRequest.status) {
+
+            // Required to support clicking on "hifi://" links
+            var url = loadRequest.url.toString();
+            url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag;
+            if (urlHandler.canHandleUrl(url)) {
+                if (urlHandler.handleUrl(url)) {
+                    webViewCore.stop();
+                }
+            }
+        }
+
+        if (WebEngineView.LoadFailedStatus === loadRequest.status) {
+            console.log("Tablet WebEngineView failed to load url: " + loadRequest.url.toString());
+        }
+
+        if (WebEngineView.LoadSucceededStatus === loadRequest.status) {
+            //disable Chromium's scroll bars
+        }
+    }
+
+    WebEngineView {
+        id: webViewCore
+
+        width: parent.width
+        height: parent.height
+
+        profile: HFWebEngineProfile;
+        settings.pluginsEnabled: true
+        settings.touchIconsEnabled: true
+        settings.allowRunningInsecureContent: true
+
+        // creates a global EventBridge object.
+        WebEngineScript {
+            id: createGlobalEventBridge
+            sourceCode: eventBridgeJavaScriptToInject
+            injectionPoint: WebEngineScript.DocumentCreation
+            worldId: WebEngineScript.MainWorld
+        }
+
+        // detects when to raise and lower virtual keyboard
+        WebEngineScript {
+            id: raiseAndLowerKeyboard
+            injectionPoint: WebEngineScript.Deferred
+            sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
+            worldId: WebEngineScript.MainWorld
+        }
+
+        // User script.
+        WebEngineScript {
+            id: userScript
+            sourceUrl: flick.userScriptUrl
+            injectionPoint: WebEngineScript.DocumentReady  // DOM ready but page load may not be finished.
+            worldId: WebEngineScript.MainWorld
+        }
+
+        userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
+
+        Component.onCompleted: {
+            webChannel.registerObject("eventBridge", eventBridge);
+            webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
+
+            if (webViewCoreUserAgent !== undefined) {
+                webViewCore.profile.httpUserAgent = webViewCoreUserAgent
+            } else {
+                webViewCore.profile.httpUserAgent += " (HighFidelityInterface)";
+            }
+            // Ensure the JS from the web-engine makes it to our logging
+            webViewCore.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
+                console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " +  message);
+            });
+        }
+
+        onFeaturePermissionRequested: {
+            grantFeaturePermission(securityOrigin, feature, true);
+        }
+
+        //disable popup
+        onContextMenuRequested: {
+            request.accepted = true;
+        }
+
+        onNewViewRequested: {
+            newViewRequestedCallback(request)
+        }
+
+        // Prior to 5.10, the WebEngineView loading property is true during initial page loading and then stays false
+        // as in-page javascript adds more html content. However, in 5.10 there is a bug such that adding html turns
+        // loading true, and never turns it false again. safeLoading provides a workaround, but it should be removed
+        // when QT fixes this.
+        property bool safeLoading: false
+        property bool loadingLatched: false
+        property var loadingRequest: null
+        onLoadingChanged: {
+            webViewCore.loadingRequest = loadRequest;
+            webViewCore.safeLoading = webViewCore.loading && !loadingLatched;
+            webViewCore.loadingLatched |= webViewCore.loading;
+         }
+        onSafeLoadingChanged: {
+            flick.onLoadingChanged(webViewCore.loadingRequest)
+            loadingChangedCallback(webViewCore.loadingRequest)
+        }
+    }
+
+    AnimatedImage {
+        //anchoring doesnt works here when changing content size
+        x: flick.width/2 - width/2
+        y: flick.height/2 - height/2
+        source: "../../icons/loader-snake-64-w.gif"
+        visible: webViewCore.safeLoading && /^(http.*|)$/i.test(webViewCore.url.toString())
+        playing: visible
+        z: 10000
+    }
+
+    Keys.onPressed: {
+        if (blurOnCtrlShift && (event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) {
+            webViewCore.focus = false; 
+        }
+    }
+}
diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml
index 823d0107a2..a844c8b624 100644
--- a/interface/resources/qml/controls/FlickableWebViewCore.qml
+++ b/interface/resources/qml/controls/FlickableWebViewCore.qml
@@ -1,10 +1,9 @@
 import QtQuick 2.7
-import QtWebEngine 1.5
-import QtWebChannel 1.0
 
 import QtQuick.Controls 2.2
 
 import stylesUit 1.0 as StylesUIt
+import controlsUit 1.0 as ControlsUit
 
 Item {
     id: flick
@@ -33,157 +32,21 @@ Item {
     }
 
     function stop() {
-        webViewCore.stop();
-    }
 
-    Timer {
-        id: delayedUnfocuser
-        repeat: false
-        interval: 200
-        onTriggered: {
-
-            // The idea behind this is to delay unfocusing, so that fast lower/raise will not result actual unfocusing.
-            // Fast lower/raise happens every time keyboard is being re-raised (see the code below in OffscreenQmlSurface::setKeyboardRaised)
-            //
-            // if (raised) {
-            //    item->setProperty("keyboardRaised", QVariant(!raised));
-            // }
-            //
-            // item->setProperty("keyboardRaised", QVariant(raised));
-            //
-
-            webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
-                console.log('unfocus completed: ', result);
-            });
-        }
     }
 
     function unfocus() {
-        delayedUnfocuser.start();
     }
 
     function stopUnfocus() {
-        delayedUnfocuser.stop();
     }
 
     function onLoadingChanged(loadRequest) {
-        if (WebEngineView.LoadStartedStatus === loadRequest.status) {
-
-            // Required to support clicking on "hifi://" links
-            var url = loadRequest.url.toString();
-            url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag;
-            if (urlHandler.canHandleUrl(url)) {
-                if (urlHandler.handleUrl(url)) {
-                    webViewCore.stop();
-                }
-            }
-        }
-
-        if (WebEngineView.LoadFailedStatus === loadRequest.status) {
-            console.log("Tablet WebEngineView failed to load url: " + loadRequest.url.toString());
-        }
-
-        if (WebEngineView.LoadSucceededStatus === loadRequest.status) {
-            //disable Chromium's scroll bars
-        }
     }
 
-    WebEngineView {
+    ControlsUit.ProxyWebView {
         id: webViewCore
-
         width: parent.width
         height: parent.height
-
-        profile: HFWebEngineProfile;
-        settings.pluginsEnabled: true
-        settings.touchIconsEnabled: true
-        settings.allowRunningInsecureContent: true
-
-        // creates a global EventBridge object.
-        WebEngineScript {
-            id: createGlobalEventBridge
-            sourceCode: eventBridgeJavaScriptToInject
-            injectionPoint: WebEngineScript.DocumentCreation
-            worldId: WebEngineScript.MainWorld
-        }
-
-        // detects when to raise and lower virtual keyboard
-        WebEngineScript {
-            id: raiseAndLowerKeyboard
-            injectionPoint: WebEngineScript.Deferred
-            sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
-            worldId: WebEngineScript.MainWorld
-        }
-
-        // User script.
-        WebEngineScript {
-            id: userScript
-            sourceUrl: flick.userScriptUrl
-            injectionPoint: WebEngineScript.DocumentReady  // DOM ready but page load may not be finished.
-            worldId: WebEngineScript.MainWorld
-        }
-
-        userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
-
-        Component.onCompleted: {
-            webChannel.registerObject("eventBridge", eventBridge);
-            webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
-
-            if (webViewCoreUserAgent !== undefined) {
-                webViewCore.profile.httpUserAgent = webViewCoreUserAgent
-            } else {
-                webViewCore.profile.httpUserAgent += " (HighFidelityInterface)";
-            }
-            // Ensure the JS from the web-engine makes it to our logging
-            webViewCore.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
-                console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " +  message);
-            });
-        }
-
-        onFeaturePermissionRequested: {
-            grantFeaturePermission(securityOrigin, feature, true);
-        }
-
-        //disable popup
-        onContextMenuRequested: {
-            request.accepted = true;
-        }
-
-        onNewViewRequested: {
-            newViewRequestedCallback(request)
-        }
-
-        // Prior to 5.10, the WebEngineView loading property is true during initial page loading and then stays false
-        // as in-page javascript adds more html content. However, in 5.10 there is a bug such that adding html turns
-        // loading true, and never turns it false again. safeLoading provides a workaround, but it should be removed
-        // when QT fixes this.
-        property bool safeLoading: false
-        property bool loadingLatched: false
-        property var loadingRequest: null
-        onLoadingChanged: {
-            webViewCore.loadingRequest = loadRequest;
-            webViewCore.safeLoading = webViewCore.loading && !loadingLatched;
-            webViewCore.loadingLatched |= webViewCore.loading;
-         }
-        onSafeLoadingChanged: {
-            flick.onLoadingChanged(webViewCore.loadingRequest)
-            loadingChangedCallback(webViewCore.loadingRequest)
-        }
-    }
-
-    AnimatedImage {
-        //anchoring doesnt works here when changing content size
-        x: flick.width/2 - width/2
-        y: flick.height/2 - height/2
-        source: "../../icons/loader-snake-64-w.gif"
-        visible: webViewCore.safeLoading && /^(http.*|)$/i.test(webViewCore.url.toString())
-        playing: visible
-        z: 10000
-    }
-
-    Keys.onPressed: {
-        if (blurOnCtrlShift && (event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) {
-            webViewCore.focus = false; 
-        }
     }
 }
diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml
index 3959dbf01b..9cbbd48a22 100644
--- a/interface/resources/qml/controls/TabletWebView.qml
+++ b/interface/resources/qml/controls/TabletWebView.qml
@@ -1,5 +1,4 @@
 import QtQuick 2.7
-import QtWebEngine 1.5
 import controlsUit 1.0 as HiFiControls
 import "../styles" as HifiStyles
 import stylesUit 1.0
diff --git a/interface/resources/qml/controlsUit/+webengine/BaseWebView.qml b/interface/resources/qml/controlsUit/+webengine/BaseWebView.qml
new file mode 100644
index 0000000000..fdd9c12220
--- /dev/null
+++ b/interface/resources/qml/controlsUit/+webengine/BaseWebView.qml
@@ -0,0 +1,38 @@
+//
+//  WebView.qml
+//
+//  Created by Bradley Austin Davis on 12 Jan 2016
+//  Copyright 2016 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 QtQuick 2.7
+import QtWebEngine 1.5
+
+WebEngineView {
+    id: root
+
+    Component.onCompleted: {
+        console.log("Connecting JS messaging to Hifi Logging")
+        // Ensure the JS from the web-engine makes it to our logging
+        root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
+            console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " +  message);
+        });
+    }
+
+    onLoadingChanged: {
+        // Required to support clicking on "hifi://" links
+        if (WebEngineView.LoadStartedStatus == loadRequest.status) {
+            var url = loadRequest.url.toString();
+            if (urlHandler.canHandleUrl(url)) {
+                if (urlHandler.handleUrl(url)) {
+                    root.stop();
+                }
+            }
+        }
+    }
+
+    WebSpinner { }
+}
diff --git a/interface/resources/qml/controlsUit/BaseWebView.qml b/interface/resources/qml/controlsUit/BaseWebView.qml
index fdd9c12220..52b2d1f3db 100644
--- a/interface/resources/qml/controlsUit/BaseWebView.qml
+++ b/interface/resources/qml/controlsUit/BaseWebView.qml
@@ -8,31 +8,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-import QtQuick 2.7
-import QtWebEngine 1.5
+import "."
 
-WebEngineView {
+ProxyWebView {
     id: root
-
-    Component.onCompleted: {
-        console.log("Connecting JS messaging to Hifi Logging")
-        // Ensure the JS from the web-engine makes it to our logging
-        root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
-            console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " +  message);
-        });
-    }
-
-    onLoadingChanged: {
-        // Required to support clicking on "hifi://" links
-        if (WebEngineView.LoadStartedStatus == loadRequest.status) {
-            var url = loadRequest.url.toString();
-            if (urlHandler.canHandleUrl(url)) {
-                if (urlHandler.handleUrl(url)) {
-                    root.stop();
-                }
-            }
-        }
-    }
-
-    WebSpinner { }
 }
diff --git a/interface/resources/qml/controlsUit/ProxyWebView.qml b/interface/resources/qml/controlsUit/ProxyWebView.qml
new file mode 100644
index 0000000000..adcc472831
--- /dev/null
+++ b/interface/resources/qml/controlsUit/ProxyWebView.qml
@@ -0,0 +1,30 @@
+import QtQuick 2.7
+import stylesUit 1.0
+
+Rectangle {
+    HifiConstants {
+        id: hifi
+    }
+
+    color: hifi.colors.darkGray
+
+    signal onNewViewRequested();
+
+    property string url: "";
+    property bool canGoBack: false
+    property bool canGoForward: false
+    property string icon: ""
+    property var profile: {}
+
+    property bool safeLoading: false
+    property bool loadingLatched: false
+    property var loadingRequest: null
+
+
+    Text {
+        anchors.centerIn: parent
+        text: "This feature is not supported"
+        font.pixelSize: 32
+        color: hifi.colors.white
+    }
+}
diff --git a/interface/resources/qml/controlsUit/qmldir b/interface/resources/qml/controlsUit/qmldir
index d0577f5575..e1665df40e 100644
--- a/interface/resources/qml/controlsUit/qmldir
+++ b/interface/resources/qml/controlsUit/qmldir
@@ -29,6 +29,7 @@ TextEdit 1.0 TextEdit.qml
 TextField 1.0 TextField.qml
 ToolTip 1.0 ToolTip.qml
 Tree 1.0 Tree.qml
+ProxyWebView 1.0 ProxyWebView.qml
 VerticalSpacer 1.0 VerticalSpacer.qml
 WebGlyphButton 1.0 WebGlyphButton.qml
 WebSpinner 1.0 WebSpinner.qml
diff --git a/libraries/shared/src/shared/FileUtils.cpp b/libraries/shared/src/shared/FileUtils.cpp
index 0709a53602..f65acccfa1 100644
--- a/libraries/shared/src/shared/FileUtils.cpp
+++ b/libraries/shared/src/shared/FileUtils.cpp
@@ -33,6 +33,10 @@ const QStringList& FileUtils::getFileSelectors() {
 #if defined(USE_GLES)
         extraSelectors << "gles";
 #endif
+
+#ifndef Q_OS_ANDROID
+        extraSelectors << "webengine";
+#endif
     });
     return extraSelectors;
 
diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp
index f67a356078..71bb65509f 100644
--- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp
+++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp
@@ -49,6 +49,7 @@
 #include <shared/ReadWriteLockable.h>
 
 #include "SecurityImageProvider.h"
+#include "shared/FileUtils.h"
 #include "types/FileTypeProfile.h"
 #include "types/HFWebEngineProfile.h"
 #include "types/SoundEffect.h"
@@ -237,7 +238,9 @@ void OffscreenQmlSurface::clearFocusItem() {
 
 void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
     Parent::initializeEngine(engine);
-    new QQmlFileSelector(engine);
+    QQmlFileSelector* fileSelector = new QQmlFileSelector(engine);
+    fileSelector->setExtraSelectors(FileUtils::getFileSelectors());
+
     static std::once_flag once;
     std::call_once(once, [] { 
         qRegisterMetaType<TabletProxy*>();

From 0846eb8ec68730af06629a6cea7ab7cffb39d86c Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Wed, 30 Jan 2019 13:59:37 -0800
Subject: [PATCH 04/14] attempt to allow position edits in releaseGrab
 entity-method

---
 interface/src/avatar/MyAvatar.cpp             | 10 ++++
 .../src/avatars-renderer/Avatar.cpp           |  3 ++
 libraries/physics/src/EntityMotionState.cpp   | 49 ++++++++++++++-----
 libraries/shared/src/Grab.h                   | 14 ++++--
 libraries/shared/src/SpatiallyNestable.cpp    |  7 ++-
 libraries/shared/src/SpatiallyNestable.h      |  2 +-
 6 files changed, 68 insertions(+), 17 deletions(-)

diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 92d9270d20..a0dd817742 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -5300,6 +5300,16 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
     bool tellHandler { false };
 
     _avatarGrabsLock.withWriteLock([&] {
+
+        std::map<QUuid, GrabPointer>::iterator itr;
+        itr = _avatarGrabs.find(grabID);
+        if (itr != _avatarGrabs.end()) {
+            GrabPointer grab = itr->second;
+            if (grab) {
+                grab->setDeleted(true);
+            }
+        }
+
         if (_avatarGrabData.remove(grabID)) {
             _grabsToDelete.push_back(grabID);
             tellHandler = true;
diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
index 07c1ca9a32..4bfaea0617 100644
--- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
+++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
@@ -412,6 +412,9 @@ void Avatar::accumulateGrabPositions(std::map<QUuid, GrabLocationAccumulator>& g
             if (!grab || !grab->getActionID().isNull()) {
                 continue; // the accumulated value isn't used, in this case.
             }
+            if (grab->getDeleted()) {
+                continue;
+            }
 
             glm::vec3 jointTranslation = getAbsoluteJointTranslationInObjectFrame(grab->getParentJointIndex());
             glm::quat jointRotation = getAbsoluteJointRotationInObjectFrame(grab->getParentJointIndex());
diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp
index a82931064a..5a658e1f01 100644
--- a/libraries/physics/src/EntityMotionState.cpp
+++ b/libraries/physics/src/EntityMotionState.cpp
@@ -114,18 +114,45 @@ void EntityMotionState::updateServerPhysicsVariables() {
     }
 }
 
+// void EntityMotionState::handleDeactivation() {
+//     // copy _server data to entity
+//     Transform localTransform = _entity->getLocalTransform();
+//     // localTransform.setTranslation(_serverPosition);
+//     // localTransform.setRotation(_serverRotation);
+//     _entity->setLocalTransformAndVelocities(localTransform, ENTITY_ITEM_ZERO_VEC3, ENTITY_ITEM_ZERO_VEC3);
+//     // and also to RigidBody
+//     btTransform worldTrans;
+//     worldTrans.setOrigin(glmToBullet(_entity->getWorldPosition()));
+//     worldTrans.setRotation(glmToBullet(_entity->getWorldOrientation()));
+//     _body->setWorldTransform(worldTrans);
+//     // no need to update velocities... should already be zero
+// }
+
 void EntityMotionState::handleDeactivation() {
-    // copy _server data to entity
-    Transform localTransform = _entity->getLocalTransform();
-    localTransform.setTranslation(_serverPosition);
-    localTransform.setRotation(_serverRotation);
-    _entity->setLocalTransformAndVelocities(localTransform, ENTITY_ITEM_ZERO_VEC3, ENTITY_ITEM_ZERO_VEC3);
-    // and also to RigidBody
-    btTransform worldTrans;
-    worldTrans.setOrigin(glmToBullet(_entity->getWorldPosition()));
-    worldTrans.setRotation(glmToBullet(_entity->getWorldOrientation()));
-    _body->setWorldTransform(worldTrans);
-    // no need to update velocities... should already be zero
+   if (_entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) {
+       // Some non-physical event (script-call or network-packet) has modified the entity's transform and/or velocities
+       // at the last minute before deactivation --> the values stored in _server* and _body are stale.
+       // We assume the EntityMotionState is the last to know, so we copy from EntityItem and let things sort themselves out.
+       Transform localTransform;
+       _entity->getLocalTransformAndVelocities(localTransform, _serverVelocity, _serverAngularVelocity);
+       _serverPosition = localTransform.getTranslation();
+       _serverRotation = localTransform.getRotation();
+       _serverAcceleration = _entity->getAcceleration();
+       _serverActionData = _entity->getDynamicData();
+       _lastStep = ObjectMotionState::getWorldSimulationStep();
+   } else {
+       // copy _server data to entity
+       Transform localTransform = _entity->getLocalTransform();
+       localTransform.setTranslation(_serverPosition);
+       localTransform.setRotation(_serverRotation);
+       _entity->setLocalTransformAndVelocities(localTransform, ENTITY_ITEM_ZERO_VEC3, ENTITY_ITEM_ZERO_VEC3);
+       // and also to RigidBody
+       btTransform worldTrans;
+       worldTrans.setOrigin(glmToBullet(_entity->getWorldPosition()));
+       worldTrans.setRotation(glmToBullet(_entity->getWorldOrientation()));
+       _body->setWorldTransform(worldTrans);
+       // no need to update velocities... should already be zero
+   }
 }
 
 // virtual
diff --git a/libraries/shared/src/Grab.h b/libraries/shared/src/Grab.h
index 5765d6fd0e..59602439f7 100644
--- a/libraries/shared/src/Grab.h
+++ b/libraries/shared/src/Grab.h
@@ -26,16 +26,16 @@ public:
     void accumulate(glm::vec3 position, glm::quat orientation) {
         _position += position;
         _orientation = orientation; // XXX
-        count++;
+        _count++;
     }
 
-    glm::vec3 finalizePosition() { return count > 0 ? _position * (1.0f / count) : glm::vec3(0.0f); }
+    glm::vec3 finalizePosition() { return _count > 0 ? _position * (1.0f / _count) : glm::vec3(0.0f); }
     glm::quat finalizeOrientation() { return _orientation; } // XXX
 
 protected:
     glm::vec3 _position;
     glm::quat _orientation;
-    int count { 0 };
+    int _count { 0 };
 };
 
 class Grab {
@@ -48,7 +48,8 @@ public:
         _parentJointIndex(newParentJointIndex),
         _hand(newHand),
         _positionalOffset(newPositionalOffset),
-        _rotationalOffset(newRotationalOffset) {}
+        _rotationalOffset(newRotationalOffset),
+        _deleted(false) {}
 
     QByteArray toByteArray();
     bool fromByteArray(const QByteArray& grabData);
@@ -61,6 +62,7 @@ public:
         _positionalOffset = other->_positionalOffset;
         _rotationalOffset = other->_rotationalOffset;
         _actionID = other->_actionID;
+        _deleted = other->_deleted;
         return *this;
     }
 
@@ -85,6 +87,9 @@ public:
     glm::quat getRotationalOffset() const { return _rotationalOffset; }
     void setRotationalOffset(glm::quat rotationalOffset) { _rotationalOffset = rotationalOffset; }
 
+    bool getDeleted() const { return _deleted; }
+    void setDeleted(bool value) { _deleted = value; }
+
 protected:
     QUuid _actionID; // if an action is created in bullet for this grab, this is the ID
     QUuid _ownerID; // avatar ID of grabber
@@ -93,6 +98,7 @@ protected:
     QString _hand; // "left" or "right"
     glm::vec3 _positionalOffset; // relative to joint
     glm::quat _rotationalOffset; // relative to joint
+    bool _deleted { false }; // scheduled for deletion
 };
 
 
diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp
index c524e3183b..dd0d749919 100644
--- a/libraries/shared/src/SpatiallyNestable.cpp
+++ b/libraries/shared/src/SpatiallyNestable.cpp
@@ -1390,7 +1390,12 @@ void SpatiallyNestable::removeGrab(GrabPointer grab) {
 bool SpatiallyNestable::hasGrabs() {
     bool result { false };
     _grabsLock.withReadLock([&] {
-        result = !_grabs.isEmpty();
+        foreach (const GrabPointer &grab, _grabs) {
+            if (grab && !grab->getDeleted()) {
+                result = true;
+                break;
+            }
+        }
     });
     return result;
 }
diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h
index 319f07236b..ed432647fd 100644
--- a/libraries/shared/src/SpatiallyNestable.h
+++ b/libraries/shared/src/SpatiallyNestable.h
@@ -241,7 +241,7 @@ protected:
     quint64 _rotationChanged { 0 };
 
     mutable ReadWriteLockable _grabsLock;
-    QSet<GrabPointer> _grabs;
+    QSet<GrabPointer> _grabs; // upon this thing
 
 private:
     SpatiallyNestable() = delete;

From 3ab2db96b6ecd0d43d15dad43143f81a52c93218 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Wed, 30 Jan 2019 14:42:47 -0800
Subject: [PATCH 05/14] deactivate grab action when grab is released

---
 interface/src/avatar/MyAvatar.cpp                      |  5 ++++-
 .../avatars-renderer/src/avatars-renderer/Avatar.cpp   |  2 +-
 libraries/entities/src/EntityDynamicInterface.h        |  1 +
 libraries/entities/src/EntityItem.cpp                  | 10 ++++++++++
 libraries/entities/src/EntityItem.h                    |  1 +
 libraries/shared/src/Grab.h                            | 10 +++++-----
 libraries/shared/src/SpatiallyNestable.cpp             |  2 +-
 libraries/shared/src/SpatiallyNestable.h               |  1 +
 8 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index a0dd817742..0530ba8eb2 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -5306,7 +5306,10 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
         if (itr != _avatarGrabs.end()) {
             GrabPointer grab = itr->second;
             if (grab) {
-                grab->setDeleted(true);
+                grab->setReleased(true);
+                bool success;
+                SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success);
+                target->disableGrab(grab);
             }
         }
 
diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
index 4bfaea0617..b8626c813e 100644
--- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
+++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
@@ -412,7 +412,7 @@ void Avatar::accumulateGrabPositions(std::map<QUuid, GrabLocationAccumulator>& g
             if (!grab || !grab->getActionID().isNull()) {
                 continue; // the accumulated value isn't used, in this case.
             }
-            if (grab->getDeleted()) {
+            if (grab->getReleased()) {
                 continue;
             }
 
diff --git a/libraries/entities/src/EntityDynamicInterface.h b/libraries/entities/src/EntityDynamicInterface.h
index 836dae2057..c911eda471 100644
--- a/libraries/entities/src/EntityDynamicInterface.h
+++ b/libraries/entities/src/EntityDynamicInterface.h
@@ -59,6 +59,7 @@ public:
     virtual bool isReadyForAdd() const { return true; }
 
     bool isActive() { return _active; }
+    void deactivate() { _active = false; }
 
     virtual void removeFromSimulation(EntitySimulationPointer simulation) const = 0;
     virtual EntityItemWeakPointer getOwnerEntity() const = 0;
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 41e4f43a5d..1640e97ff4 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -3506,3 +3506,13 @@ void EntityItem::removeGrab(GrabPointer grab) {
     }
     disableNoBootstrap();
 }
+
+void EntityItem::disableGrab(GrabPointer grab) {
+    QUuid actionID = grab->getActionID();
+    if (!actionID.isNull()) {
+        EntityDynamicPointer action = _grabActions.value(actionID);
+        if (action) {
+            action->deactivate();
+        }
+    }
+}
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index ec7ad78313..27b207b6f3 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -561,6 +561,7 @@ public:
 
     virtual void addGrab(GrabPointer grab) override;
     virtual void removeGrab(GrabPointer grab) override;
+    virtual void disableGrab(GrabPointer grab) override;
 
 signals:
     void requestRenderUpdate();
diff --git a/libraries/shared/src/Grab.h b/libraries/shared/src/Grab.h
index 59602439f7..f16a80befa 100644
--- a/libraries/shared/src/Grab.h
+++ b/libraries/shared/src/Grab.h
@@ -49,7 +49,7 @@ public:
         _hand(newHand),
         _positionalOffset(newPositionalOffset),
         _rotationalOffset(newRotationalOffset),
-        _deleted(false) {}
+        _released(false) {}
 
     QByteArray toByteArray();
     bool fromByteArray(const QByteArray& grabData);
@@ -62,7 +62,7 @@ public:
         _positionalOffset = other->_positionalOffset;
         _rotationalOffset = other->_rotationalOffset;
         _actionID = other->_actionID;
-        _deleted = other->_deleted;
+        _released = other->_released;
         return *this;
     }
 
@@ -87,8 +87,8 @@ public:
     glm::quat getRotationalOffset() const { return _rotationalOffset; }
     void setRotationalOffset(glm::quat rotationalOffset) { _rotationalOffset = rotationalOffset; }
 
-    bool getDeleted() const { return _deleted; }
-    void setDeleted(bool value) { _deleted = value; }
+    bool getReleased() const { return _released; }
+    void setReleased(bool value) { _released = value; }
 
 protected:
     QUuid _actionID; // if an action is created in bullet for this grab, this is the ID
@@ -98,7 +98,7 @@ protected:
     QString _hand; // "left" or "right"
     glm::vec3 _positionalOffset; // relative to joint
     glm::quat _rotationalOffset; // relative to joint
-    bool _deleted { false }; // scheduled for deletion
+    bool _released { false }; // released and scheduled for deletion
 };
 
 
diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp
index dd0d749919..d3ed79faf4 100644
--- a/libraries/shared/src/SpatiallyNestable.cpp
+++ b/libraries/shared/src/SpatiallyNestable.cpp
@@ -1391,7 +1391,7 @@ bool SpatiallyNestable::hasGrabs() {
     bool result { false };
     _grabsLock.withReadLock([&] {
         foreach (const GrabPointer &grab, _grabs) {
-            if (grab && !grab->getDeleted()) {
+            if (grab && !grab->getReleased()) {
                 result = true;
                 break;
             }
diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h
index ed432647fd..e7a449f73f 100644
--- a/libraries/shared/src/SpatiallyNestable.h
+++ b/libraries/shared/src/SpatiallyNestable.h
@@ -218,6 +218,7 @@ public:
 
     virtual void addGrab(GrabPointer grab);
     virtual void removeGrab(GrabPointer grab);
+    virtual void disableGrab(GrabPointer grab) {};
     bool hasGrabs();
     virtual QUuid getEditSenderID();
 

From f33e5ba5ae5d0df55018128489b4c115ef6b8111 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Wed, 30 Jan 2019 14:47:38 -0800
Subject: [PATCH 06/14] cleanup

---
 libraries/physics/src/EntityMotionState.cpp | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp
index 5a658e1f01..ce9cb20c21 100644
--- a/libraries/physics/src/EntityMotionState.cpp
+++ b/libraries/physics/src/EntityMotionState.cpp
@@ -114,20 +114,6 @@ void EntityMotionState::updateServerPhysicsVariables() {
     }
 }
 
-// void EntityMotionState::handleDeactivation() {
-//     // copy _server data to entity
-//     Transform localTransform = _entity->getLocalTransform();
-//     // localTransform.setTranslation(_serverPosition);
-//     // localTransform.setRotation(_serverRotation);
-//     _entity->setLocalTransformAndVelocities(localTransform, ENTITY_ITEM_ZERO_VEC3, ENTITY_ITEM_ZERO_VEC3);
-//     // and also to RigidBody
-//     btTransform worldTrans;
-//     worldTrans.setOrigin(glmToBullet(_entity->getWorldPosition()));
-//     worldTrans.setRotation(glmToBullet(_entity->getWorldOrientation()));
-//     _body->setWorldTransform(worldTrans);
-//     // no need to update velocities... should already be zero
-// }
-
 void EntityMotionState::handleDeactivation() {
    if (_entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) {
        // Some non-physical event (script-call or network-packet) has modified the entity's transform and/or velocities

From c6f44234f8afdc1f30f5c4ac2b1b317c8027ef75 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Wed, 30 Jan 2019 14:56:02 -0800
Subject: [PATCH 07/14] avoid possible crash

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

diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 0530ba8eb2..2eae7aa181 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -5309,7 +5309,9 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
                 grab->setReleased(true);
                 bool success;
                 SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success);
-                target->disableGrab(grab);
+                if (target) {
+                    target->disableGrab(grab);
+                }
             }
         }
 

From 30d9fe705ebee66d6d30a3233959a699c3ad0010 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Wed, 30 Jan 2019 14:56:50 -0800
Subject: [PATCH 08/14] avoid possible crash

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

diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 2eae7aa181..3f32f96795 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -5309,7 +5309,7 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
                 grab->setReleased(true);
                 bool success;
                 SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success);
-                if (target) {
+                if (target && success) {
                     target->disableGrab(grab);
                 }
             }

From 03789e01da412cc50cb8ecb86d1a5f03833b0509 Mon Sep 17 00:00:00 2001
From: Wayne Chen <wayne@highfidelity.io>
Date: Wed, 30 Jan 2019 15:53:21 -0800
Subject: [PATCH 09/14] adding fix for oculus store argument check

---
 interface/src/Application.cpp | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index daf2dd6363..3b9fe15f25 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -761,6 +761,11 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
     static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
     bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET);
 
+    // set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store
+    static const auto OCULUS_STORE_ARG = "--oculus-store";
+    bool isStore = cmdOptionExists(argc, const_cast<const char**>(argv), OCULUS_STORE_ARG);
+    qApp->setProperty(hifi::properties::OCULUS_STORE, isStore);
+
     // Ignore any previous crashes if running from command line with a test script.
     bool inTestMode { false };
     for (int i = 0; i < argc; ++i) {
@@ -1138,10 +1143,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
     qCDebug(interfaceapp) << "[VERSION] We will use DEVELOPMENT global services.";
 #endif
 
-    // set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store
-    static const QString OCULUS_STORE_ARG = "--oculus-store";
-    bool isStore = arguments().indexOf(OCULUS_STORE_ARG) != -1;
-    setProperty(hifi::properties::OCULUS_STORE, isStore);
+    bool isStore = property(hifi::properties::OCULUS_STORE).toBool();
+
     DependencyManager::get<WalletScriptingInterface>()->setLimitedCommerce(isStore);  // Or we could make it a separate arg, or if either arg is set, etc. And should this instead by a hifi::properties?
 
     updateHeartbeat();

From 2c40b120ca98dff84a9c660af97b665643fbc43a Mon Sep 17 00:00:00 2001
From: Anthony Thibault <tony@highfidelity.io>
Date: Wed, 30 Jan 2019 17:26:52 -0800
Subject: [PATCH 10/14] Make Animation Tests Pass Again

---
 .../animation/src/AnimBlendLinearMove.cpp     |  4 +-
 libraries/animation/src/AnimVariant.h         |  7 +--
 libraries/animation/src/Rig.cpp               |  2 -
 tests/animation/CMakeLists.txt                |  2 +-
 .../src/AnimInverseKinematicsTests.cpp        |  4 +-
 tests/animation/src/AnimTests.cpp             | 62 ++++++++++---------
 6 files changed, 40 insertions(+), 41 deletions(-)

diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp
index 07e1c17f77..28b8bb4a9f 100644
--- a/libraries/animation/src/AnimBlendLinearMove.cpp
+++ b/libraries/animation/src/AnimBlendLinearMove.cpp
@@ -151,10 +151,10 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn
     if (_phase < 0.0f) {
         _phase = 0.0f;
     }
-    
+
     // detect loop trigger events
     if (_phase >= 1.0f) {
-        triggersOut.setTrigger(_id + "Loop");
+        triggersOut.setTrigger(_id + "OnLoop");
         _phase = glm::fract(_phase);
     }
 
diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h
index 0f921984b1..eb9ebd33dd 100644
--- a/libraries/animation/src/AnimVariant.h
+++ b/libraries/animation/src/AnimVariant.h
@@ -209,15 +209,14 @@ public:
     void set(const QString& key, const QString& value) { _map[key] = AnimVariant(value); }
     void unset(const QString& key) { _map.erase(key); }
 
-    void setTrigger(const QString& key) { _triggers.insert(key); }
-    void clearTriggers() { _triggers.clear(); }
+    void setTrigger(const QString& key) { _map[key] = AnimVariant(true); }
 
     void setRigToGeometryTransform(const glm::mat4& rigToGeometry) {
         _rigToGeometryMat = rigToGeometry;
         _rigToGeometryRot = glmExtractRotation(rigToGeometry);
     }
 
-    void clearMap() { _map.clear(); }
+    void clearMap() { _map.clear(); _triggers.clear(); }
     bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); }
 
     const AnimVariant& get(const QString& key) const {
@@ -238,7 +237,7 @@ public:
     // For stat debugging.
     std::map<QString, QString> toDebugMap() const;
 
-#ifdef NDEBUG
+#ifndef NDEBUG
     void dump() const {
         qCDebug(animation) << "AnimVariantMap =";
         for (auto& pair : _map) {
diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp
index bc4dca54f2..7842ec0804 100644
--- a/libraries/animation/src/Rig.cpp
+++ b/libraries/animation/src/Rig.cpp
@@ -1207,9 +1207,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
             _networkPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
         }
         _lastAnimVars = _animVars;
-        _animVars.clearTriggers();
         _animVars = triggersOut;
-        _networkVars.clearTriggers();
         _networkVars = networkTriggersOut;
         _lastContext = context;
     }
diff --git a/tests/animation/CMakeLists.txt b/tests/animation/CMakeLists.txt
index 17999c4d8e..2af4d5f2cd 100644
--- a/tests/animation/CMakeLists.txt
+++ b/tests/animation/CMakeLists.txt
@@ -1,7 +1,7 @@
 # Declare dependencies
 macro (setup_testcase_dependencies)
   # link in the shared libraries
-  link_hifi_libraries(shared animation gpu fbx graphics networking test-utils)
+  link_hifi_libraries(shared animation gpu fbx hfm graphics networking test-utils)
 
   package_libraries_for_deployment()
 endmacro ()
diff --git a/tests/animation/src/AnimInverseKinematicsTests.cpp b/tests/animation/src/AnimInverseKinematicsTests.cpp
index 910770bb0d..708b1b2d2a 100644
--- a/tests/animation/src/AnimInverseKinematicsTests.cpp
+++ b/tests/animation/src/AnimInverseKinematicsTests.cpp
@@ -143,7 +143,7 @@ void AnimInverseKinematicsTests::testSingleChain() {
         ikDoll.setTargetVars(QString("D"), QString("positionD"), QString("rotationD"), QString("targetTypeD"),
                              QString("weightD"), 1.0f, flexCoefficients, QString("poleVectorEnabledD"),
                              QString("poleReferenceVectorD"), QString("poleVectorD"));
-        AnimNode::Triggers triggers;
+        AnimVariantMap triggers;
 
         // the IK solution should be:
         //
@@ -236,7 +236,7 @@ void AnimInverseKinematicsTests::testSingleChain() {
         ikDoll.setTargetVars(QString("D"), QString("positionD"), QString("rotationD"), QString("targetTypeD"),
                              QString("weightD"), 1.0f, flexCoefficients, QString("poleVectorEnabledD"),
                              QString("poleReferenceVectorD"), QString("poleVectorD"));
-        AnimNode::Triggers triggers;
+        AnimVariantMap triggers;
 
         // the IK solution should be:
         //
diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp
index 01c8d1c1b6..0cd9571e22 100644
--- a/tests/animation/src/AnimTests.cpp
+++ b/tests/animation/src/AnimTests.cpp
@@ -19,6 +19,7 @@
 #include <AddressManager.h>
 #include <AccountManager.h>
 #include <ResourceManager.h>
+#include <ResourceRequestObserver.h>
 #include <StatTracker.h>
 #include <test-utils/QTestExtensions.h>
 
@@ -33,6 +34,7 @@ void AnimTests::initTestCase() {
     DependencyManager::set<NodeList>(NodeType::Agent);
     DependencyManager::set<ResourceManager>();
     DependencyManager::set<AnimationCache>();
+    DependencyManager::set<ResourceRequestObserver>();
     DependencyManager::set<ResourceCacheSharedItems>();
     DependencyManager::set<StatTracker>();
 }
@@ -84,26 +86,26 @@ void AnimTests::testClipEvaulate() {
 
     AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag, mirrorFlag);
 
-    AnimNode::Triggers triggers;
+    AnimVariantMap triggers;
     clip.evaluate(vars, context, framesToSec(10.0f), triggers);
     QCOMPARE_WITH_ABS_ERROR(clip._frame, 12.0f, TEST_EPSILON);
 
     // does it loop?
-    triggers.clear();
+    triggers.clearMap();
     clip.evaluate(vars, context, framesToSec(12.0f), triggers);
     QCOMPARE_WITH_ABS_ERROR(clip._frame, 3.0f, TEST_EPSILON);  // Note: frame 3 and not 4, because extra frame between start and end.
 
     // did we receive a loop trigger?
-    QVERIFY(std::find(triggers.begin(), triggers.end(), "myClipNodeOnLoop") != triggers.end());
+    QVERIFY(triggers.hasKey("myClipNodeOnLoop"));
 
     // does it pause at end?
-    triggers.clear();
+    triggers.clearMap();
     clip.setLoopFlagVar("FalseVar");
     clip.evaluate(vars, context, framesToSec(20.0f), triggers);
     QCOMPARE_WITH_ABS_ERROR(clip._frame, 22.0f, TEST_EPSILON);
 
     // did we receive a done trigger?
-    QVERIFY(std::find(triggers.begin(), triggers.end(), "myClipNodeOnDone") != triggers.end());
+    QVERIFY(triggers.hasKey("myClipNodeOnDone"));
 }
 
 void AnimTests::testClipEvaulateWithVars() {
@@ -133,7 +135,7 @@ void AnimTests::testClipEvaulateWithVars() {
     clip.setTimeScaleVar("timeScale2");
     clip.setLoopFlagVar("loopFlag2");
 
-    AnimNode::Triggers triggers;
+    AnimVariantMap triggers;
     clip.evaluate(vars, context, framesToSec(0.1f), triggers);
 
     // verify that the values from the AnimVariantMap made it into the clipNode's
@@ -284,11 +286,11 @@ void AnimTests::testAccumulateTime() {
     timeScale = 1.0f;
     float dt = 1.0f;
     QString id = "testNode";
-    AnimNode::Triggers triggers;
+    AnimVariantMap triggers;
     float loopFlag = true;
     float resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers);
     // a one frame looping animation should NOT trigger onLoop events
-    QVERIFY(triggers.empty());
+    QVERIFY(!triggers.hasKey("testNodeOnLoop"));
 
     const uint32_t MAX_TRIGGER_COUNT = 3;
 
@@ -296,45 +298,45 @@ void AnimTests::testAccumulateTime() {
     endFrame = 1.1f;
     timeScale = 10.0f;
     dt = 10.0f;
-    triggers.clear();
+    triggers.clearMap();
     loopFlag = true;
     resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers);
-    // a short animation with a large dt & a large timescale, should only create a MAXIMUM of 3 loop events.
-    QVERIFY(triggers.size() <= MAX_TRIGGER_COUNT);
+    // a short animation with a large dt & a large timescale, should generate a onLoop event.
+    QVERIFY(triggers.hasKey("testNodeOnLoop"));
 }
 
 void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFrame, float timeScale) const {
 
     float dt = (1.0f / 30.0f) / timeScale;  // sec
     QString id = "testNode";
-    AnimNode::Triggers triggers;
+    AnimVariantMap triggers;
     bool loopFlag = false;
 
     float resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers);
     QVERIFY(resultFrame == startFrame + 1.0f);
-    QVERIFY(triggers.empty());
-    triggers.clear();
+    QVERIFY(!triggers.hasKey("testNodeOnLoop"));
+    triggers.clearMap();
 
     resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers);
     QVERIFY(resultFrame == startFrame + 2.0f);
-    QVERIFY(triggers.empty());
-    triggers.clear();
+    QVERIFY(!triggers.hasKey("testNodeOnLoop"));
+    triggers.clearMap();
 
     resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers);
     QVERIFY(resultFrame == startFrame + 3.0f);
-    QVERIFY(triggers.empty());
-    triggers.clear();
+    QVERIFY(!triggers.hasKey("testNodeOnLoop"));
+    triggers.clearMap();
 
     // test onDone trigger and frame clamping.
     resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 1.0f, dt, loopFlag, id, triggers);
     QVERIFY(resultFrame == endFrame);
-    QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnDone");
-    triggers.clear();
+    QVERIFY(triggers.hasKey("testNodeOnDone"));
+    triggers.clearMap();
 
     resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 0.5f, dt, loopFlag, id, triggers);
     QVERIFY(resultFrame == endFrame);
-    QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnDone");
-    triggers.clear();
+    QVERIFY(triggers.hasKey("testNodeOnDone"));
+    triggers.clearMap();
 
     // test onLoop trigger and looping frame logic
     loopFlag = true;
@@ -342,26 +344,26 @@ void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFram
     // should NOT trigger loop even though we stop at last frame, because there is an extra frame between end and start frames.
     resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 1.0f, dt, loopFlag, id, triggers);
     QVERIFY(resultFrame == endFrame);
-    QVERIFY(triggers.empty());
-    triggers.clear();
+    QVERIFY(!triggers.hasKey("testNodeOnLoop"));
+    triggers.clearMap();
 
     // now we should hit loop trigger
     resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers);
     QVERIFY(resultFrame == startFrame);
-    QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop");
-    triggers.clear();
+    QVERIFY(triggers.hasKey("testNodeOnLoop"));
+    triggers.clearMap();
 
     // should NOT trigger loop, even though we move past the end frame, because of extra frame between end and start.
     resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 0.5f, dt, loopFlag, id, triggers);
     QVERIFY(resultFrame == endFrame + 0.5f);
-    QVERIFY(triggers.empty());
-    triggers.clear();
+    QVERIFY(!triggers.hasKey("testNodeOnLoop"));
+    triggers.clearMap();
 
     // now we should hit loop trigger
     resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers);
     QVERIFY(resultFrame == startFrame + 0.5f);
-    QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop");
-    triggers.clear();
+    QVERIFY(triggers.hasKey("testNodeOnLoop"));
+    triggers.clearMap();
 }
 
 void AnimTests::testAnimPose() {

From 283dabc62296ce314257427e0f2cd181033e61a0 Mon Sep 17 00:00:00 2001
From: Clement <clement.brisset@gmail.com>
Date: Wed, 30 Jan 2019 17:03:16 -0800
Subject: [PATCH 11/14] Fix reload mechanic for entity server scripts

---
 .../src/scripts/EntityScriptServer.cpp           | 16 ++++++++--------
 .../src/scripts/EntityScriptServer.h             |  2 +-
 .../entities-renderer/src/EntityTreeRenderer.cpp |  2 +-
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp
index ef0c807bc4..f1a6c97831 100644
--- a/assignment-client/src/scripts/EntityScriptServer.cpp
+++ b/assignment-client/src/scripts/EntityScriptServer.cpp
@@ -112,7 +112,6 @@ void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer<Rec
 
         if (_entityViewer.getTree() && !_shuttingDown) {
             qCDebug(entity_script_server) << "Reloading: " << entityID;
-            _entitiesScriptEngine->unloadEntityScript(entityID);
             checkAndCallPreload(entityID, true);
         }
     }
@@ -184,7 +183,6 @@ void EntityScriptServer::updateEntityPPS() {
         pps = std::min(_maxEntityPPS, pps);
     }
     _entityEditSender.setPacketsPerSecond(pps);
-    qDebug() << QString("Updating entity PPS to: %1 @ %2 PPS per script = %3 PPS").arg(numRunningScripts).arg(_entityPPSPerScript).arg(pps);
 }
 
 void EntityScriptServer::handleEntityServerScriptLogPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
@@ -525,23 +523,25 @@ void EntityScriptServer::deletingEntity(const EntityItemID& entityID) {
 
 void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID, bool reload) {
     if (_entityViewer.getTree() && !_shuttingDown) {
-        _entitiesScriptEngine->unloadEntityScript(entityID, true);
         checkAndCallPreload(entityID, reload);
     }
 }
 
-void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool reload) {
+void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool forceRedownload) {
     if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) {
 
         EntityItemPointer entity = _entityViewer.getTree()->findEntityByEntityItemID(entityID);
         EntityScriptDetails details;
-        bool notRunning = !_entitiesScriptEngine->getEntityScriptDetails(entityID, details);
-        if (entity && (reload || notRunning || details.scriptText != entity->getServerScripts())) {
+        bool isRunning = _entitiesScriptEngine->getEntityScriptDetails(entityID, details);
+        if (entity && (forceRedownload || !isRunning || details.scriptText != entity->getServerScripts())) {
+            if (isRunning) {
+                _entitiesScriptEngine->unloadEntityScript(entityID, true);
+            }
+
             QString scriptUrl = entity->getServerScripts();
             if (!scriptUrl.isEmpty()) {
                 scriptUrl = DependencyManager::get<ResourceManager>()->normalizeURL(scriptUrl);
-                qCDebug(entity_script_server) << "Loading entity server script" << scriptUrl << "for" << entityID;
-                _entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, reload);
+                _entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, forceRedownload);
             }
         }
     }
diff --git a/assignment-client/src/scripts/EntityScriptServer.h b/assignment-client/src/scripts/EntityScriptServer.h
index 9c6c4c752e..944fee36a3 100644
--- a/assignment-client/src/scripts/EntityScriptServer.h
+++ b/assignment-client/src/scripts/EntityScriptServer.h
@@ -67,7 +67,7 @@ private:
     void addingEntity(const EntityItemID& entityID);
     void deletingEntity(const EntityItemID& entityID);
     void entityServerScriptChanging(const EntityItemID& entityID, bool reload);
-    void checkAndCallPreload(const EntityItemID& entityID, bool reload = false);
+    void checkAndCallPreload(const EntityItemID& entityID, bool forceRedownload = false);
 
     void cleanupOldKilledListeners();
 
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
index c71b296a74..44025fc8f4 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
@@ -1048,7 +1048,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool
         QString scriptUrl = entity->getScript();
         if ((shouldLoad && unloadFirst) || scriptUrl.isEmpty()) {
             if (_entitiesScriptEngine) {
-            _entitiesScriptEngine->unloadEntityScript(entityID);
+                _entitiesScriptEngine->unloadEntityScript(entityID);
             }
             entity->scriptHasUnloaded();
         }

From 778ddad9ac3ef4c2585e2f6a6c3a87616507da9a Mon Sep 17 00:00:00 2001
From: Dante Ruiz <danteruiz102@gmail.com>
Date: Thu, 31 Jan 2019 10:52:48 -0800
Subject: [PATCH 12/14] to the spot on first launch

---
 interface/src/ui/AddressBarDialog.cpp       | 2 +-
 libraries/networking/src/AddressManager.cpp | 3 ++-
 libraries/networking/src/AddressManager.h   | 1 +
 3 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp
index 789a2a2bdf..799d7ea182 100644
--- a/interface/src/ui/AddressBarDialog.cpp
+++ b/interface/src/ui/AddressBarDialog.cpp
@@ -59,7 +59,7 @@ void AddressBarDialog::loadHome() {
     auto locationBookmarks = DependencyManager::get<LocationBookmarks>();
     QString homeLocation = locationBookmarks->addressForBookmark(LocationBookmarks::HOME_BOOKMARK);
     if (homeLocation == "") {
-        homeLocation = DEFAULT_HIFI_ADDRESS;
+        homeLocation = DEFAULT_HOME_ADDRESS;
     }
     DependencyManager::get<AddressManager>()->handleLookupString(homeLocation);
 }
diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp
index e6957728e8..9145b4a79e 100644
--- a/libraries/networking/src/AddressManager.cpp
+++ b/libraries/networking/src/AddressManager.cpp
@@ -30,7 +30,8 @@
 #include "UserActivityLogger.h"
 #include "udt/PacketHeaders.h"
 
-const QString DEFAULT_HIFI_ADDRESS = "file:///~/serverless/tutorial.json";
+const QString DEFAULT_HIFI_ADDRESS = "hifi://welcome";
+const QString DEFAULT_HOME_ADDRESS = "file:///~/serverless/tutorial.json";
 const QString REDIRECT_HIFI_ADDRESS = "file:///~/serverless/redirect.json";
 const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager";
 const QString SETTINGS_CURRENT_ADDRESS_KEY = "address";
diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h
index 5318822cdc..450b71023c 100644
--- a/libraries/networking/src/AddressManager.h
+++ b/libraries/networking/src/AddressManager.h
@@ -24,6 +24,7 @@
 
 extern const QString DEFAULT_HIFI_ADDRESS;
 extern const QString REDIRECT_HIFI_ADDRESS;
+extern const QString DEFAULT_HOME_ADDRESS;
 
 const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost";
 const QString INDEX_PATH = "/";

From f4118213b15fa12082a73990ed70e9af0678d0bf Mon Sep 17 00:00:00 2001
From: Dante Ruiz <danteruiz102@gmail.com>
Date: Fri, 1 Feb 2019 10:12:04 -0800
Subject: [PATCH 13/14] give tablet root color

---
 .../qml/hifi/commerce/common/sendAsset/SendAsset.qml         | 3 +--
 interface/resources/qml/hifi/tablet/TabletRoot.qml           | 5 ++++-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml
index bc8816e0ea..68d437a346 100644
--- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml
+++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml
@@ -21,11 +21,10 @@ import "../../../../controls" as HifiControls
 import "../" as HifiCommerceCommon
 import "qrc:////qml//hifi//models" as HifiModels  // Absolute path so the same code works everywhere.
 
-Rectangle {
+Item {
     HifiConstants { id: hifi; }
 
     id: root;
-    color: hifi.colors.baseGray
     property int parentAppTitleBarHeight;
     property int parentAppNavBarHeight;
     property string currentActiveView: "sendAssetHome";
diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml
index b19dcbb919..93a23f1b9d 100644
--- a/interface/resources/qml/hifi/tablet/TabletRoot.qml
+++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml
@@ -3,10 +3,13 @@ import Hifi 1.0
 
 import "../../dialogs"
 import "../../controls"
+import stylesUit 1.0
 
-Item {
+Rectangle {
+    HifiConstants { id: hifi; }
     id: tabletRoot
     objectName: "tabletRoot"
+    color: hifi.colors.baseGray
     property string username: "Unknown user"
     property string usernameShort: "Unknown user"
     property var rootMenu;

From 30bd9774b2d9127db8ddc1b857f689bab83d169a Mon Sep 17 00:00:00 2001
From: danteruiz <danteruiz102@gmail.com>
Date: Tue, 5 Feb 2019 13:11:54 -0800
Subject: [PATCH 14/14] script engine to load platform specific files

---
 libraries/script-engine/src/ScriptEngines.cpp |   3 +
 libraries/shared/src/shared/FileUtils.cpp     |   5 +
 .../+android_questInterface/defaultScripts.js | 118 ++++++++++++++++++
 3 files changed, 126 insertions(+)
 create mode 100644 scripts/+android_questInterface/defaultScripts.js

diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp
index 8ecfb84633..3963ad5593 100644
--- a/libraries/script-engine/src/ScriptEngines.cpp
+++ b/libraries/script-engine/src/ScriptEngines.cpp
@@ -16,6 +16,7 @@
 #include <SettingHandle.h>
 #include <UserActivityLogger.h>
 #include <PathUtils.h>
+#include <shared/FileUtils.h>
 
 #include "ScriptEngine.h"
 #include "ScriptEngineLogging.h"
@@ -476,6 +477,8 @@ ScriptEnginePointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool i
         scriptUrl = normalizeScriptURL(scriptFilename);
     }
 
+    scriptUrl = QUrl(FileUtils::selectFile(scriptUrl.toString()));
+
     auto scriptEngine = getScriptEngine(scriptUrl);
     if (scriptEngine && !scriptEngine->isStopping()) {
         return scriptEngine;
diff --git a/libraries/shared/src/shared/FileUtils.cpp b/libraries/shared/src/shared/FileUtils.cpp
index 0709a53602..041fca5459 100644
--- a/libraries/shared/src/shared/FileUtils.cpp
+++ b/libraries/shared/src/shared/FileUtils.cpp
@@ -30,6 +30,11 @@ const QStringList& FileUtils::getFileSelectors() {
     static std::once_flag once;
     static QStringList extraSelectors;
     std::call_once(once, [] {
+
+#if defined(Q_OS_ANDROID)
+        //extraSelectors << "android_" HIFI_ANDROID_APP;
+#endif
+
 #if defined(USE_GLES)
         extraSelectors << "gles";
 #endif
diff --git a/scripts/+android_questInterface/defaultScripts.js b/scripts/+android_questInterface/defaultScripts.js
new file mode 100644
index 0000000000..da50e4a182
--- /dev/null
+++ b/scripts/+android_questInterface/defaultScripts.js
@@ -0,0 +1,118 @@
+"use strict";
+/* jslint vars: true, plusplus: true */
+
+//
+//  defaultScripts.js
+//  examples
+//
+//  Copyright 2014 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+var DEFAULT_SCRIPTS_COMBINED = [
+    "system/request-service.js",
+    "system/progress.js",
+    //"system/away.js",
+    "system/hmd.js",
+    "system/menu.js",
+    "system/bubble.js",
+    "system/pal.js", // "system/mod.js", // older UX, if you prefer
+    "system/avatarapp.js",
+    "system/makeUserConnection.js",
+    "system/tablet-goto.js",
+    "system/notifications.js",
+    "system/commerce/wallet.js",
+    "system/dialTone.js",
+    "system/firstPersonHMD.js",
+    "system/tablet-ui/tabletUI.js",
+    "system/miniTablet.js"
+];
+var DEFAULT_SCRIPTS_SEPARATE = [
+    "system/controllers/controllerScripts.js",
+    //"system/chat.js"
+];
+
+if (Window.interstitialModeEnabled) {
+    // Insert interstitial scripts at front so that they're started first.
+    DEFAULT_SCRIPTS_COMBINED.splice(0, 0, "system/interstitialPage.js", "system/redirectOverlays.js");
+}
+
+// add a menu item for debugging
+var MENU_CATEGORY = "Developer > Scripting";
+var MENU_ITEM = "Debug defaultScripts.js";
+
+var SETTINGS_KEY = '_debugDefaultScriptsIsChecked';
+var previousSetting = Settings.getValue(SETTINGS_KEY);
+
+if (previousSetting === '' || previousSetting === false || previousSetting === 'false') {
+    previousSetting = false;
+}
+
+if (previousSetting === true || previousSetting === 'true') {
+    previousSetting = true;
+}
+
+if (Menu.menuExists(MENU_CATEGORY) && !Menu.menuItemExists(MENU_CATEGORY, MENU_ITEM)) {
+    Menu.addMenuItem({
+        menuName: MENU_CATEGORY,
+        menuItemName: MENU_ITEM,
+        isCheckable: true,
+        isChecked: previousSetting,
+    });
+}
+
+function loadSeparateDefaults() {
+    for (var i in DEFAULT_SCRIPTS_SEPARATE) {
+        Script.load(DEFAULT_SCRIPTS_SEPARATE[i]);
+    }
+}
+
+function runDefaultsTogether() {
+    for (var i in DEFAULT_SCRIPTS_COMBINED) {
+        Script.include(DEFAULT_SCRIPTS_COMBINED[i]);
+    }
+    loadSeparateDefaults();
+}
+
+function runDefaultsSeparately() {
+    for (var i in DEFAULT_SCRIPTS_COMBINED) {
+        Script.load(DEFAULT_SCRIPTS_COMBINED[i]);
+    }
+    loadSeparateDefaults();
+}
+
+// start all scripts
+if (Menu.isOptionChecked(MENU_ITEM)) {
+    // we're debugging individual default scripts
+    // so we load each into its own ScriptEngine instance
+    runDefaultsSeparately();
+} else {
+    // include all default scripts into this ScriptEngine
+    runDefaultsTogether();
+}
+
+function menuItemEvent(menuItem) {
+    if (menuItem === MENU_ITEM) {
+        var isChecked = Menu.isOptionChecked(MENU_ITEM);
+        if (isChecked === true) {
+            Settings.setValue(SETTINGS_KEY, true);
+        } else if (isChecked === false) {
+            Settings.setValue(SETTINGS_KEY, false);
+        }
+        Menu.triggerOption("Reload All Scripts");
+    }
+}
+
+function removeMenuItem() {
+    if (!Menu.isOptionChecked(MENU_ITEM)) {
+        Menu.removeMenuItem(MENU_CATEGORY, MENU_ITEM);
+    }
+}
+
+Script.scriptEnding.connect(function() {
+    removeMenuItem();
+});
+
+Menu.menuItemEvent.connect(menuItemEvent);