From e35c6be45eacf19201f8e3a048cf5d4cd6795235 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 20 Jan 2015 11:13:08 -0800 Subject: [PATCH 01/22] Lint: White space --- examples/notifications.js | 126 +++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 62 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index 9a6fbbce29..f6ff675cfb 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -58,12 +58,11 @@ // createNotification(welcome); // } - var width = 340.0; //width of notification overlay var height = 40.0; // height of a single line notification overlay var windowDimensions = Controller.getViewportDimensions(); // get the size of the interface window var overlayLocationX = (windowDimensions.x - (width + 20.0));// positions window 20px from the right of the interface window -var buttonLocationX = overlayLocationX + (width - 28.0); +var buttonLocationX = overlayLocationX + (width - 28.0); var locationY = 20.0; // position down from top of interface window var topMargin = 13.0; var leftMargin = 10.0; @@ -74,16 +73,16 @@ var fontSize = 12.0; var persistTime = 10.0; // time in seconds before notification fades var clickedText = false; var frame = 0; -var ourWidth = Window.innerWidth; -var ourHeight = Window.innerHeight; +var ourWidth = Window.innerWidth; +var ourHeight = Window.innerHeight; var text = "placeholder"; var last_users = GlobalServices.onlineUsers; var users = []; var ctrlIsPressed = false; var ready = true; - + // When our script shuts down, we should clean up all of our overlays -function scriptEnding() { +function scriptEnding() { for (i = 0; i < notifications.length; i++) { Overlays.deleteOverlay(notifications[i]); Overlays.deleteOverlay(buttons[i]); @@ -92,7 +91,7 @@ function scriptEnding() { Script.scriptEnding.connect(scriptEnding); var notifications = []; -var buttons = []; +var buttons = []; var times = []; var heights = []; var myAlpha = []; @@ -117,7 +116,7 @@ function createNotification(text) { height = height + extraLine; var overlayProperties = { x: overlayLocationX, - y: level, + y: level, width: width, height: height, color: textColor, @@ -126,10 +125,10 @@ function createNotification(text) { topMargin: topMargin, leftMargin: leftMargin, font: {size: fontSize}, - text: text, - }; + text: text, + }; var bLevel = level + 12.0; - var buttonProperties = { + var buttonProperties = { x: buttonLocationX, y: bLevel, width: 10.0, @@ -139,19 +138,17 @@ function createNotification(text) { color: { red: 255, green: 255, blue: 255}, visible: true, alpha: backgroundAlpha, - }; - - Notify(overlayProperties, buttonProperties, height); - + }; + + Notify(overlayProperties, buttonProperties, height); } // Pushes data to each array and sets up data for 2nd dimension array // to handle auxiliary data not carried by the overlay class // specifically notification "heights", "times" of creation, and . -function Notify(notice, button, height){ - +function Notify(notice, button, height) { notifications.push((Overlays.addOverlay("text", notice))); - buttons.push((Overlays.addOverlay("image",button))); + buttons.push((Overlays.addOverlay("image", button))); times.push(new Date().getTime() / 1000); height = height + 1.0; heights.push(height); @@ -165,11 +162,11 @@ function fadeIn(noticeIn, buttonIn) { var myLength = arrays.length; var q = 0; var pauseTimer = null; - pauseTimer = Script.setInterval(function() { + pauseTimer = Script.setInterval(function () { q++; qFade = q / 10.0; - Overlays.editOverlay(noticeIn, {alpha: qFade}); - Overlays.editOverlay(buttonIn, {alpha: qFade}); + Overlays.editOverlay(noticeIn, {alpha: qFade}); + Overlays.editOverlay(buttonIn, {alpha: qFade}); if (q >= 9.0) { Script.clearInterval(pauseTimer); } @@ -178,24 +175,25 @@ function fadeIn(noticeIn, buttonIn) { // push data from above to the 2 dimensional array -function createArrays(notice, button, createTime, height, myAlpha) { - arrays.push([notice, button, createTime, height, myAlpha]); +function createArrays(notice, button, createTime, height, myAlpha) { + arrays.push([notice, button, createTime, height, myAlpha]); } + // handles mouse clicks on buttons -function mousePressEvent(event) { +function mousePressEvent(event) { var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); //identify which overlay was clicked for (i = 0; i < buttons.length; i++) { //if user clicked a button - if(clickedOverlay == buttons[i]) { + if (clickedOverlay == buttons[i]) { Overlays.deleteOverlay(notifications[i]); Overlays.deleteOverlay(buttons[i]); notifications.splice(i, 1); buttons.splice(i, 1); times.splice(i, 1); heights.splice(i, 1); - myAlpha.splice(i, 1); + myAlpha.splice(i, 1); arrays.splice(i, 1); } - } + } } // Control key remains active only while key is held down @@ -204,20 +202,21 @@ function keyReleaseEvent(key) { ctrlIsPressed = false; } } - + // Triggers notification on specific key driven events function keyPressEvent(key) { if (key.key == 16777249) { ctrlIsPressed = true; } - if (key.text == "q") { //queries number of users online - var numUsers = GlobalServices.onlineUsers.length; - var welcome = "There are " + numUsers + " users online now."; - createNotification(welcome); - } - if (key.text == "s") { - if (ctrlIsPressed == true){ + if (key.text == "q") { //queries number of users online + var numUsers = GlobalServices.onlineUsers.length; + var welcome = "There are " + numUsers + " users online now."; + createNotification(welcome); + } + + if (key.text == "s") { + if (ctrlIsPressed == true) { var noteString = "Snapshot taken."; createNotification(noteString); } @@ -229,6 +228,7 @@ function wordWrap(str) { var result = stringDivider(str, 43.0, "\n"); createNotification(result); } + // wraps whole word to newline function stringDivider(str, slotWidth, spaceReplacer) { if (str.length > slotWidth) { @@ -241,15 +241,15 @@ function stringDivider(str, slotWidth, spaceReplacer) { return left + spaceReplacer + stringDivider(right, slotWidth, spaceReplacer); } } - return str; + return str; } // This fires a notification on window resize -function checkSize(){ - if((Window.innerWidth != ourWidth)||(Window.innerHeight != ourHeight)) { +function checkSize() { + if ((Window.innerWidth != ourWidth) || (Window.innerHeight != ourHeight)) { var windowResize = "Window has been resized"; ourWidth = Window.innerWidth; - ourHeight = Window.innerHeight; + ourHeight = Window.innerHeight; windowDimensions = Controller.getViewportDimensions(); overlayLocationX = (windowDimensions.x - (width + 60.0)); buttonLocationX = overlayLocationX + (width - 35.0); @@ -262,27 +262,30 @@ function onOnlineUsersChanged(users) { if (!isStartingUp()) { // Skip user notifications at startup. for (user in users) { if (last_users.indexOf(users[user]) == -1.0) { - createNotification(users[user] + " has joined"); + createNotification(users[user] + " has joined"); } } + for (user in last_users) { if (users.indexOf(last_users[user]) == -1.0) { - createNotification(last_users[user] + " has left"); + createNotification(last_users[user] + " has left"); } } } + last_users = users; } // Triggers notification if @MyUserName is mentioned in chat and returns the message to the notification. function onIncomingMessage(user, message) { - var myMessage = message; - var alertMe = "@" + GlobalServices.myUsername; - var thisAlert = user + ": " + myMessage; - if (myMessage.indexOf(alertMe) > -1.0) { + var myMessage = message; + var alertMe = "@" + GlobalServices.myUsername; + var thisAlert = user + ": " + myMessage; + if (myMessage.indexOf(alertMe) > -1.0) { wordWrap(thisAlert); } } + // Triggers mic mute notification function onMuteStateChanged() { var muteState = AudioDevice.getMuted() ? "muted" : "unmuted"; @@ -290,26 +293,26 @@ function onMuteStateChanged() { createNotification(muteString); } -function update(){ +function update() { frame++; if ((frame % 60.0) == 0) { // only update once a second checkSize(); // checks for size change to trigger windowResize notification locationY = 20.0; for (var i = 0; i < arrays.length; i++) { //repositions overlays as others fade - var nextOverlay = Overlays.getOverlayAtPoint({x: overlayLocationX, y: locationY}); - Overlays.editOverlay(notifications[i], { x:overlayLocationX, y:locationY}); - Overlays.editOverlay(buttons[i], { x:buttonLocationX, y:locationY + 12.0}); + var nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); + Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY}); + Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0}); locationY = locationY + arrays[i][3]; } } -// This checks the age of the notification and prepares to fade it after 9.0 seconds (var persistTime - 1) - for (var i = 0; i < arrays.length; i++) { - if (ready){ - var j = arrays[i][2]; - var k = j + persistTime; + // This checks the age of the notification and prepares to fade it after 9.0 seconds (var persistTime - 1) + for (var i = 0; i < arrays.length; i++) { + if (ready) { + var j = arrays[i][2]; + var k = j + persistTime; if (k < (new Date().getTime() / 1000)) { - ready = false; + ready = false; noticeOut = arrays[i][0]; buttonOut = arrays[i][1]; var arraysOut = i; @@ -324,11 +327,11 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { var myLength = arrays.length; var r = 9.0; var pauseTimer = null; - pauseTimer = Script.setInterval(function() { + pauseTimer = Script.setInterval(function () { r--; - rFade = r / 10.0; - Overlays.editOverlay(noticeOut, {alpha: rFade}); - Overlays.editOverlay(buttonOut, {alpha: rFade}); + rFade = r / 10.0; + Overlays.editOverlay(noticeOut, { alpha: rFade }); + Overlays.editOverlay(buttonOut, { alpha: rFade }); if (r < 0) { dismiss(noticeOut, buttonOut, arraysOut); arrays.splice(arraysOut, 1); @@ -339,7 +342,7 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { } // This handles the final dismissal of a notification after fading -function dismiss(firstNoteOut, firstButOut, firstOut) { +function dismiss(firstNoteOut, firstButOut, firstOut) { var working = firstOut Overlays.deleteOverlay(firstNoteOut); Overlays.deleteOverlay(firstButOut); @@ -347,7 +350,7 @@ function dismiss(firstNoteOut, firstButOut, firstOut) { buttons.splice(firstOut, 1); times.splice(firstOut, 1); heights.splice(firstOut, 1); - myAlpha.splice(firstOut,1); + myAlpha.splice(firstOut, 1); } // This reports the number of users online at startup @@ -378,7 +381,6 @@ function isStartingUp() { return startingUp; } - AudioDevice.muteToggled.connect(onMuteStateChanged); Controller.keyPressEvent.connect(keyPressEvent); Controller.mousePressEvent.connect(mousePressEvent); From c4b024e51005c3dd064959489f7903a3aa3ed67e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 20 Jan 2015 11:59:43 -0800 Subject: [PATCH 02/22] Lint: Variable declarations --- examples/notifications.js | 147 ++++++++++++++++++++++++-------------- 1 file changed, 95 insertions(+), 52 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index f6ff675cfb..35dd09f7f7 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -52,16 +52,16 @@ // 2. Declare a text string. // 3. Call createNotifications(text) parsing the text. // example: +// var welcome; // if (key.text == "q") { //queries number of users online -// var numUsers = GlobalServices.onlineUsers.length; -// var welcome = "There are " + numUsers + " users online now."; -// createNotification(welcome); +// var welcome = "There are " + GlobalServices.onlineUsers.length + " users online now."; +// createNotification(welcome); // } var width = 340.0; //width of notification overlay var height = 40.0; // height of a single line notification overlay var windowDimensions = Controller.getViewportDimensions(); // get the size of the interface window -var overlayLocationX = (windowDimensions.x - (width + 20.0));// positions window 20px from the right of the interface window +var overlayLocationX = (windowDimensions.x - (width + 20.0)); // positions window 20px from the right of the interface window var buttonLocationX = overlayLocationX + (width - 28.0); var locationY = 20.0; // position down from top of interface window var topMargin = 13.0; @@ -80,16 +80,6 @@ var last_users = GlobalServices.onlineUsers; var users = []; var ctrlIsPressed = false; var ready = true; - -// When our script shuts down, we should clean up all of our overlays -function scriptEnding() { - for (i = 0; i < notifications.length; i++) { - Overlays.deleteOverlay(notifications[i]); - Overlays.deleteOverlay(buttons[i]); - } -} -Script.scriptEnding.connect(scriptEnding); - var notifications = []; var buttons = []; var times = []; @@ -97,24 +87,42 @@ var heights = []; var myAlpha = []; var arrays = []; +// When our script shuts down, we should clean up all of our overlays +function scriptEnding() { + var i; + + for (i = 0; i < notifications.length; i++) { + Overlays.deleteOverlay(notifications[i]); + Overlays.deleteOverlay(buttons[i]); + } +} +Script.scriptEnding.connect(scriptEnding); + // This function creates and sizes the overlays function createNotification(text) { - var count = (text.match(/\n/g) || []).length; - var breakPoint = 43.0; // length when new line is added - var extraLine = 0; - var breaks = 0; - var height = 40.0; - var stack = 0; + var count = (text.match(/\n/g) || []).length, + breakPoint = 43.0, // length when new line is added + extraLine = 0, + breaks = 0, + height = 40.0, + stack = 0, + level, + overlayProperties, + bLevel, + buttonProperties, + i; + if (text.length >= breakPoint) { breaks = count; } - var extraLine = breaks * 16.0; + extraLine = breaks * 16.0; for (i = 0; i < heights.length; i++) { stack = stack + heights[i]; } - var level = (stack + 20.0); + + level = (stack + 20.0); height = height + extraLine; - var overlayProperties = { + overlayProperties = { x: overlayLocationX, y: level, width: width, @@ -127,8 +135,9 @@ function createNotification(text) { font: {size: fontSize}, text: text, }; - var bLevel = level + 12.0; - var buttonProperties = { + + bLevel = level + 12.0; + buttonProperties = { x: buttonLocationX, y: bLevel, width: 10.0, @@ -159,9 +168,10 @@ function Notify(notice, button, height) { } function fadeIn(noticeIn, buttonIn) { - var myLength = arrays.length; - var q = 0; - var pauseTimer = null; + var q = 0, + qFade, + pauseTimer = null; + pauseTimer = Script.setInterval(function () { q++; qFade = q / 10.0; @@ -181,7 +191,11 @@ function createArrays(notice, button, createTime, height, myAlpha) { // handles mouse clicks on buttons function mousePressEvent(event) { - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); //identify which overlay was clicked + var clickedOverlay, + i; + + clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); //identify which overlay was clicked + for (i = 0; i < buttons.length; i++) { //if user clicked a button if (clickedOverlay == buttons[i]) { Overlays.deleteOverlay(notifications[i]); @@ -205,19 +219,23 @@ function keyReleaseEvent(key) { // Triggers notification on specific key driven events function keyPressEvent(key) { + var numUsers, + welcome, + noteString; + if (key.key == 16777249) { ctrlIsPressed = true; } if (key.text == "q") { //queries number of users online - var numUsers = GlobalServices.onlineUsers.length; - var welcome = "There are " + numUsers + " users online now."; + numUsers = GlobalServices.onlineUsers.length; + welcome = "There are " + numUsers + " users online now."; createNotification(welcome); } if (key.text == "s") { if (ctrlIsPressed == true) { - var noteString = "Snapshot taken."; + noteString = "Snapshot taken."; createNotification(noteString); } } @@ -231,13 +249,17 @@ function wordWrap(str) { // wraps whole word to newline function stringDivider(str, slotWidth, spaceReplacer) { + var p, + left, + right; + if (str.length > slotWidth) { - var p = slotWidth; + p = slotWidth; for (; p > 0 && str[p] != ' '; p--) { } if (p > 0) { - var left = str.substring(0, p); - var right = str.substring(p + 1); + left = str.substring(0, p); + right = str.substring(p + 1); return left + spaceReplacer + stringDivider(right, slotWidth, spaceReplacer); } } @@ -259,6 +281,8 @@ function checkSize() { // Triggers notification if a user logs on or off function onOnlineUsersChanged(users) { + var user; + if (!isStartingUp()) { // Skip user notifications at startup. for (user in users) { if (last_users.indexOf(users[user]) == -1.0) { @@ -278,9 +302,14 @@ function onOnlineUsersChanged(users) { // Triggers notification if @MyUserName is mentioned in chat and returns the message to the notification. function onIncomingMessage(user, message) { - var myMessage = message; - var alertMe = "@" + GlobalServices.myUsername; - var thisAlert = user + ": " + myMessage; + var myMessage, + alertMe, + thisAlert; + + myMessage = message; + alertMe = "@" + GlobalServices.myUsername; + thisAlert = user + ": " + myMessage; + if (myMessage.indexOf(alertMe) > -1.0) { wordWrap(thisAlert); } @@ -288,18 +317,29 @@ function onIncomingMessage(user, message) { // Triggers mic mute notification function onMuteStateChanged() { - var muteState = AudioDevice.getMuted() ? "muted" : "unmuted"; - var muteString = "Microphone is now " + muteState; + var muteState, + muteString; + + muteState = AudioDevice.getMuted() ? "muted" : "unmuted"; + muteString = "Microphone is now " + muteState; createNotification(muteString); } function update() { + var nextOverlay, + noticeOut, + buttonOut, + arraysOut, + i, + j, + k; + frame++; if ((frame % 60.0) == 0) { // only update once a second checkSize(); // checks for size change to trigger windowResize notification locationY = 20.0; - for (var i = 0; i < arrays.length; i++) { //repositions overlays as others fade - var nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); + for (i = 0; i < arrays.length; i++) { //repositions overlays as others fade + nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY}); Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0}); locationY = locationY + arrays[i][3]; @@ -307,15 +347,15 @@ function update() { } // This checks the age of the notification and prepares to fade it after 9.0 seconds (var persistTime - 1) - for (var i = 0; i < arrays.length; i++) { + for (i = 0; i < arrays.length; i++) { if (ready) { - var j = arrays[i][2]; - var k = j + persistTime; + j = arrays[i][2]; + k = j + persistTime; if (k < (new Date().getTime() / 1000)) { ready = false; noticeOut = arrays[i][0]; buttonOut = arrays[i][1]; - var arraysOut = i; + arraysOut = i; fadeOut(noticeOut, buttonOut, arraysOut); } } @@ -324,9 +364,10 @@ function update() { // this fades the notification ready for dismissal, and removes it from the arrays function fadeOut(noticeOut, buttonOut, arraysOut) { - var myLength = arrays.length; - var r = 9.0; - var pauseTimer = null; + var r = 9.0, + rFade, + pauseTimer = null; + pauseTimer = Script.setInterval(function () { r--; rFade = r / 10.0; @@ -343,7 +384,6 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { // This handles the final dismissal of a notification after fading function dismiss(firstNoteOut, firstButOut, firstOut) { - var working = firstOut Overlays.deleteOverlay(firstNoteOut); Overlays.deleteOverlay(firstButOut); notifications.splice(firstOut, 1); @@ -355,8 +395,11 @@ function dismiss(firstNoteOut, firstButOut, firstOut) { // This reports the number of users online at startup function reportUsers() { - var numUsers = GlobalServices.onlineUsers.length; - var welcome = "Welcome! There are " + numUsers + " users online now."; + var numUsers, + welcome; + + numUsers = GlobalServices.onlineUsers.length; + welcome = "Welcome! There are " + numUsers + " users online now."; createNotification(welcome); } From ce0c91628a5e788032d88f1c47965378a73599b0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 20 Jan 2015 12:15:27 -0800 Subject: [PATCH 03/22] Lint: Declaration order --- examples/notifications.js | 377 +++++++++++++++++++------------------- 1 file changed, 187 insertions(+), 190 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index 35dd09f7f7..6e73e67233 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -87,16 +87,72 @@ var heights = []; var myAlpha = []; var arrays = []; -// When our script shuts down, we should clean up all of our overlays -function scriptEnding() { - var i; - - for (i = 0; i < notifications.length; i++) { - Overlays.deleteOverlay(notifications[i]); - Overlays.deleteOverlay(buttons[i]); - } +// push data from above to the 2 dimensional array +function createArrays(notice, button, createTime, height, myAlpha) { + arrays.push([notice, button, createTime, height, myAlpha]); +} + +// This handles the final dismissal of a notification after fading +function dismiss(firstNoteOut, firstButOut, firstOut) { + Overlays.deleteOverlay(firstNoteOut); + Overlays.deleteOverlay(firstButOut); + notifications.splice(firstOut, 1); + buttons.splice(firstOut, 1); + times.splice(firstOut, 1); + heights.splice(firstOut, 1); + myAlpha.splice(firstOut, 1); +} + +function fadeIn(noticeIn, buttonIn) { + var q = 0, + qFade, + pauseTimer = null; + + pauseTimer = Script.setInterval(function () { + q++; + qFade = q / 10.0; + Overlays.editOverlay(noticeIn, { alpha: qFade }); + Overlays.editOverlay(buttonIn, { alpha: qFade }); + if (q >= 9.0) { + Script.clearInterval(pauseTimer); + } + }, 10); +} + +// this fades the notification ready for dismissal, and removes it from the arrays +function fadeOut(noticeOut, buttonOut, arraysOut) { + var r = 9.0, + rFade, + pauseTimer = null; + + pauseTimer = Script.setInterval(function () { + r--; + rFade = r / 10.0; + Overlays.editOverlay(noticeOut, { alpha: rFade }); + Overlays.editOverlay(buttonOut, { alpha: rFade }); + if (r < 0) { + dismiss(noticeOut, buttonOut, arraysOut); + arrays.splice(arraysOut, 1); + ready = true; + Script.clearInterval(pauseTimer); + } + }, 20); +} + +// Pushes data to each array and sets up data for 2nd dimension array +// to handle auxiliary data not carried by the overlay class +// specifically notification "heights", "times" of creation, and . +function Notify(notice, button, height) { + notifications.push((Overlays.addOverlay("text", notice))); + buttons.push((Overlays.addOverlay("image", button))); + times.push(new Date().getTime() / 1000); + height = height + 1.0; + heights.push(height); + myAlpha.push(0); + var last = notifications.length - 1; + createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]); + fadeIn(notifications[last], buttons[last]) } -Script.scriptEnding.connect(scriptEnding); // This function creates and sizes the overlays function createNotification(text) { @@ -152,101 +208,6 @@ function createNotification(text) { Notify(overlayProperties, buttonProperties, height); } -// Pushes data to each array and sets up data for 2nd dimension array -// to handle auxiliary data not carried by the overlay class -// specifically notification "heights", "times" of creation, and . -function Notify(notice, button, height) { - notifications.push((Overlays.addOverlay("text", notice))); - buttons.push((Overlays.addOverlay("image", button))); - times.push(new Date().getTime() / 1000); - height = height + 1.0; - heights.push(height); - myAlpha.push(0); - var last = notifications.length - 1; - createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]); - fadeIn(notifications[last], buttons[last]) -} - -function fadeIn(noticeIn, buttonIn) { - var q = 0, - qFade, - pauseTimer = null; - - pauseTimer = Script.setInterval(function () { - q++; - qFade = q / 10.0; - Overlays.editOverlay(noticeIn, {alpha: qFade}); - Overlays.editOverlay(buttonIn, {alpha: qFade}); - if (q >= 9.0) { - Script.clearInterval(pauseTimer); - } - }, 10); -} - - -// push data from above to the 2 dimensional array -function createArrays(notice, button, createTime, height, myAlpha) { - arrays.push([notice, button, createTime, height, myAlpha]); -} - -// handles mouse clicks on buttons -function mousePressEvent(event) { - var clickedOverlay, - i; - - clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); //identify which overlay was clicked - - for (i = 0; i < buttons.length; i++) { //if user clicked a button - if (clickedOverlay == buttons[i]) { - Overlays.deleteOverlay(notifications[i]); - Overlays.deleteOverlay(buttons[i]); - notifications.splice(i, 1); - buttons.splice(i, 1); - times.splice(i, 1); - heights.splice(i, 1); - myAlpha.splice(i, 1); - arrays.splice(i, 1); - } - } -} - -// Control key remains active only while key is held down -function keyReleaseEvent(key) { - if (key.key == 16777249) { - ctrlIsPressed = false; - } -} - -// Triggers notification on specific key driven events -function keyPressEvent(key) { - var numUsers, - welcome, - noteString; - - if (key.key == 16777249) { - ctrlIsPressed = true; - } - - if (key.text == "q") { //queries number of users online - numUsers = GlobalServices.onlineUsers.length; - welcome = "There are " + numUsers + " users online now."; - createNotification(welcome); - } - - if (key.text == "s") { - if (ctrlIsPressed == true) { - noteString = "Snapshot taken."; - createNotification(noteString); - } - } -} - -// formats string to add newline every 43 chars -function wordWrap(str) { - var result = stringDivider(str, 43.0, "\n"); - createNotification(result); -} - // wraps whole word to newline function stringDivider(str, slotWidth, spaceReplacer) { var p, @@ -266,6 +227,12 @@ function stringDivider(str, slotWidth, spaceReplacer) { return str; } +// formats string to add newline every 43 chars +function wordWrap(str) { + var result = stringDivider(str, 43.0, "\n"); + createNotification(result); +} + // This fires a notification on window resize function checkSize() { if ((Window.innerWidth != ourWidth) || (Window.innerHeight != ourHeight)) { @@ -279,6 +246,72 @@ function checkSize() { } } +function update() { + var nextOverlay, + noticeOut, + buttonOut, + arraysOut, + i, + j, + k; + + frame++; + if ((frame % 60.0) == 0) { // only update once a second + checkSize(); // checks for size change to trigger windowResize notification + locationY = 20.0; + for (i = 0; i < arrays.length; i++) { //repositions overlays as others fade + nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); + Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY }); + Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); + locationY = locationY + arrays[i][3]; + } + } + + // This checks the age of the notification and prepares to fade it after 9.0 seconds (var persistTime - 1) + for (i = 0; i < arrays.length; i++) { + if (ready) { + j = arrays[i][2]; + k = j + persistTime; + if (k < (new Date().getTime() / 1000)) { + ready = false; + noticeOut = arrays[i][0]; + buttonOut = arrays[i][1]; + arraysOut = i; + fadeOut(noticeOut, buttonOut, arraysOut); + } + } + } +} + +var STARTUP_TIMEOUT = 500, // ms + startingUp = true, + startupTimer = null; + +// This reports the number of users online at startup +function reportUsers() { + var welcome; + + welcome = "Welcome! There are " + GlobalServices.onlineUsers.length + " users online now."; + createNotification(welcome); +} + +function finishStartup() { + startingUp = false; + Script.clearTimeout(startupTimer); + reportUsers(); +} + +function isStartingUp() { + // Is starting up until get no checks that it is starting up for STARTUP_TIMEOUT + if (startingUp) { + if (startupTimer) { + Script.clearTimeout(startupTimer); + } + startupTimer = Script.setTimeout(finishStartup, STARTUP_TIMEOUT); + } + return startingUp; +} + // Triggers notification if a user logs on or off function onOnlineUsersChanged(users) { var user; @@ -325,103 +358,66 @@ function onMuteStateChanged() { createNotification(muteString); } -function update() { - var nextOverlay, - noticeOut, - buttonOut, - arraysOut, - i, - j, - k; +// handles mouse clicks on buttons +function mousePressEvent(event) { + var clickedOverlay, + i; - frame++; - if ((frame % 60.0) == 0) { // only update once a second - checkSize(); // checks for size change to trigger windowResize notification - locationY = 20.0; - for (i = 0; i < arrays.length; i++) { //repositions overlays as others fade - nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); - Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY}); - Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0}); - locationY = locationY + arrays[i][3]; - } - } + clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); //identify which overlay was clicked - // This checks the age of the notification and prepares to fade it after 9.0 seconds (var persistTime - 1) - for (i = 0; i < arrays.length; i++) { - if (ready) { - j = arrays[i][2]; - k = j + persistTime; - if (k < (new Date().getTime() / 1000)) { - ready = false; - noticeOut = arrays[i][0]; - buttonOut = arrays[i][1]; - arraysOut = i; - fadeOut(noticeOut, buttonOut, arraysOut); - } + for (i = 0; i < buttons.length; i++) { //if user clicked a button + if (clickedOverlay == buttons[i]) { + Overlays.deleteOverlay(notifications[i]); + Overlays.deleteOverlay(buttons[i]); + notifications.splice(i, 1); + buttons.splice(i, 1); + times.splice(i, 1); + heights.splice(i, 1); + myAlpha.splice(i, 1); + arrays.splice(i, 1); } } } -// this fades the notification ready for dismissal, and removes it from the arrays -function fadeOut(noticeOut, buttonOut, arraysOut) { - var r = 9.0, - rFade, - pauseTimer = null; - - pauseTimer = Script.setInterval(function () { - r--; - rFade = r / 10.0; - Overlays.editOverlay(noticeOut, { alpha: rFade }); - Overlays.editOverlay(buttonOut, { alpha: rFade }); - if (r < 0) { - dismiss(noticeOut, buttonOut, arraysOut); - arrays.splice(arraysOut, 1); - ready = true; - Script.clearInterval(pauseTimer); - } - }, 20); +// Control key remains active only while key is held down +function keyReleaseEvent(key) { + if (key.key == 16777249) { + ctrlIsPressed = false; + } } -// This handles the final dismissal of a notification after fading -function dismiss(firstNoteOut, firstButOut, firstOut) { - Overlays.deleteOverlay(firstNoteOut); - Overlays.deleteOverlay(firstButOut); - notifications.splice(firstOut, 1); - buttons.splice(firstOut, 1); - times.splice(firstOut, 1); - heights.splice(firstOut, 1); - myAlpha.splice(firstOut, 1); -} - -// This reports the number of users online at startup -function reportUsers() { +// Triggers notification on specific key driven events +function keyPressEvent(key) { var numUsers, - welcome; + welcome, + noteString; - numUsers = GlobalServices.onlineUsers.length; - welcome = "Welcome! There are " + numUsers + " users online now."; - createNotification(welcome); -} - -var STARTUP_TIMEOUT = 500, // ms - startingUp = true, - startupTimer = null; - -function finishStartup() { - startingUp = false; - Script.clearTimeout(startupTimer); - reportUsers(); -} - -function isStartingUp() { - // Is starting up until get no checks that it is starting up for STARTUP_TIMEOUT - if (startingUp) { - if (startupTimer) { - Script.clearTimeout(startupTimer); - } - startupTimer = Script.setTimeout(finishStartup, STARTUP_TIMEOUT); + if (key.key == 16777249) { + ctrlIsPressed = true; + } + + if (key.text == "q") { //queries number of users online + numUsers = GlobalServices.onlineUsers.length; + welcome = "There are " + numUsers + " users online now."; + createNotification(welcome); + } + + if (key.text == "s") { + if (ctrlIsPressed == true) { + noteString = "Snapshot taken."; + createNotification(noteString); + } + } +} + +// When our script shuts down, we should clean up all of our overlays +function scriptEnding() { + var i; + + for (i = 0; i < notifications.length; i++) { + Overlays.deleteOverlay(notifications[i]); + Overlays.deleteOverlay(buttons[i]); } - return startingUp; } AudioDevice.muteToggled.connect(onMuteStateChanged); @@ -431,3 +427,4 @@ GlobalServices.onlineUsersChanged.connect(onOnlineUsersChanged); GlobalServices.incomingMessage.connect(onIncomingMessage); Controller.keyReleaseEvent.connect(keyReleaseEvent); Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); From 63310fac503b8d5f8b8baa3f931c977c7ac68369 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 20 Jan 2015 12:18:58 -0800 Subject: [PATCH 04/22] Lint: Syntax --- examples/notifications.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index 6e73e67233..d1d69e448d 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -151,7 +151,7 @@ function Notify(notice, button, height) { myAlpha.push(0); var last = notifications.length - 1; createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]); - fadeIn(notifications[last], buttons[last]) + fadeIn(notifications[last], buttons[last]); } // This function creates and sizes the overlays @@ -189,7 +189,7 @@ function createNotification(text) { topMargin: topMargin, leftMargin: leftMargin, font: {size: fontSize}, - text: text, + text: text }; bLevel = level + 12.0; @@ -202,7 +202,7 @@ function createNotification(text) { imageURL: "http://hifi-public.s3.amazonaws.com/images/close-small-light.svg", color: { red: 255, green: 255, blue: 255}, visible: true, - alpha: backgroundAlpha, + alpha: backgroundAlpha }; Notify(overlayProperties, buttonProperties, height); @@ -242,7 +242,7 @@ function checkSize() { windowDimensions = Controller.getViewportDimensions(); overlayLocationX = (windowDimensions.x - (width + 60.0)); buttonLocationX = overlayLocationX + (width - 35.0); - createNotification(windowResize) + createNotification(windowResize); } } From adba46f90c7cc581bc72df94c691ca208211e531 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 20 Jan 2015 12:23:38 -0800 Subject: [PATCH 05/22] Lint: Preferred operators --- examples/notifications.js | 40 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index d1d69e448d..a08a8b473e 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -109,7 +109,7 @@ function fadeIn(noticeIn, buttonIn) { pauseTimer = null; pauseTimer = Script.setInterval(function () { - q++; + q += 1; qFade = q / 10.0; Overlays.editOverlay(noticeIn, { alpha: qFade }); Overlays.editOverlay(buttonIn, { alpha: qFade }); @@ -126,7 +126,7 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { pauseTimer = null; pauseTimer = Script.setInterval(function () { - r--; + r -= 1; rFade = r / 10.0; Overlays.editOverlay(noticeOut, { alpha: rFade }); Overlays.editOverlay(buttonOut, { alpha: rFade }); @@ -142,7 +142,7 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { // Pushes data to each array and sets up data for 2nd dimension array // to handle auxiliary data not carried by the overlay class // specifically notification "heights", "times" of creation, and . -function Notify(notice, button, height) { +function notify(notice, button, height) { notifications.push((Overlays.addOverlay("text", notice))); buttons.push((Overlays.addOverlay("image", button))); times.push(new Date().getTime() / 1000); @@ -172,7 +172,7 @@ function createNotification(text) { breaks = count; } extraLine = breaks * 16.0; - for (i = 0; i < heights.length; i++) { + for (i = 0; i < heights.length; i += 1) { stack = stack + heights[i]; } @@ -205,7 +205,7 @@ function createNotification(text) { alpha: backgroundAlpha }; - Notify(overlayProperties, buttonProperties, height); + notify(overlayProperties, buttonProperties, height); } // wraps whole word to newline @@ -235,7 +235,7 @@ function wordWrap(str) { // This fires a notification on window resize function checkSize() { - if ((Window.innerWidth != ourWidth) || (Window.innerHeight != ourHeight)) { + if ((Window.innerWidth !== ourWidth) || (Window.innerHeight !== ourHeight)) { var windowResize = "Window has been resized"; ourWidth = Window.innerWidth; ourHeight = Window.innerHeight; @@ -255,11 +255,11 @@ function update() { j, k; - frame++; - if ((frame % 60.0) == 0) { // only update once a second + frame += 1; + if ((frame % 60.0) === 0) { // only update once a second checkSize(); // checks for size change to trigger windowResize notification locationY = 20.0; - for (i = 0; i < arrays.length; i++) { //repositions overlays as others fade + for (i = 0; i < arrays.length; i += 1) { //repositions overlays as others fade nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY }); Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); @@ -268,7 +268,7 @@ function update() { } // This checks the age of the notification and prepares to fade it after 9.0 seconds (var persistTime - 1) - for (i = 0; i < arrays.length; i++) { + for (i = 0; i < arrays.length; i += 1) { if (ready) { j = arrays[i][2]; k = j + persistTime; @@ -318,13 +318,13 @@ function onOnlineUsersChanged(users) { if (!isStartingUp()) { // Skip user notifications at startup. for (user in users) { - if (last_users.indexOf(users[user]) == -1.0) { + if (last_users.indexOf(users[user]) === -1.0) { createNotification(users[user] + " has joined"); } } for (user in last_users) { - if (users.indexOf(last_users[user]) == -1.0) { + if (users.indexOf(last_users[user]) === -1.0) { createNotification(last_users[user] + " has left"); } } @@ -365,8 +365,8 @@ function mousePressEvent(event) { clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); //identify which overlay was clicked - for (i = 0; i < buttons.length; i++) { //if user clicked a button - if (clickedOverlay == buttons[i]) { + for (i = 0; i < buttons.length; i += 1) { //if user clicked a button + if (clickedOverlay === buttons[i]) { Overlays.deleteOverlay(notifications[i]); Overlays.deleteOverlay(buttons[i]); notifications.splice(i, 1); @@ -381,7 +381,7 @@ function mousePressEvent(event) { // Control key remains active only while key is held down function keyReleaseEvent(key) { - if (key.key == 16777249) { + if (key.key === 16777249) { ctrlIsPressed = false; } } @@ -392,18 +392,18 @@ function keyPressEvent(key) { welcome, noteString; - if (key.key == 16777249) { + if (key.key === 16777249) { ctrlIsPressed = true; } - if (key.text == "q") { //queries number of users online + if (key.text === "q") { //queries number of users online numUsers = GlobalServices.onlineUsers.length; welcome = "There are " + numUsers + " users online now."; createNotification(welcome); } - if (key.text == "s") { - if (ctrlIsPressed == true) { + if (key.text === "s") { + if (ctrlIsPressed === true) { noteString = "Snapshot taken."; createNotification(noteString); } @@ -414,7 +414,7 @@ function keyPressEvent(key) { function scriptEnding() { var i; - for (i = 0; i < notifications.length; i++) { + for (i = 0; i < notifications.length; i += 1) { Overlays.deleteOverlay(notifications[i]); Overlays.deleteOverlay(buttons[i]); } From 42b9ee8e77904b48f301cb74e9ca17674330d9ae Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 20 Jan 2015 12:51:34 -0800 Subject: [PATCH 06/22] Lint: Preferred control constructs --- examples/notifications.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index a08a8b473e..2929c61a64 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -216,8 +216,10 @@ function stringDivider(str, slotWidth, spaceReplacer) { if (str.length > slotWidth) { p = slotWidth; - for (; p > 0 && str[p] != ' '; p--) { + while (p > 0 && str[p] !== ' ') { + p -= 1; } + if (p > 0) { left = str.substring(0, p); right = str.substring(p + 1); @@ -314,18 +316,20 @@ function isStartingUp() { // Triggers notification if a user logs on or off function onOnlineUsersChanged(users) { - var user; + var user, + i; if (!isStartingUp()) { // Skip user notifications at startup. - for (user in users) { - if (last_users.indexOf(users[user]) === -1.0) { - createNotification(users[user] + " has joined"); + for (i = 0; i < users.length; i += 1) { + if (last_users.indexOf(users[i]) === -1.0) { + createNotification(users[i] + " has joined"); } + } - for (user in last_users) { - if (users.indexOf(last_users[user]) === -1.0) { - createNotification(last_users[user] + " has left"); + for (i = 0; i < last_users.length; i += 1) { + if (users.indexOf(last_users[i]) === -1.0) { + createNotification(last_users[i] + " has left"); } } } From 504bd5e1b1ac5b00940f1cde1b42a05d12655ec3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 20 Jan 2015 18:34:47 -0800 Subject: [PATCH 07/22] Expose default eye position to JavaScript --- interface/src/avatar/MyAvatar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index a4a01373a0..e549abbc96 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -51,7 +51,7 @@ public: // getters float getLeanScale() const { return _leanScale; } glm::vec3 getGravity() const { return _gravity; } - glm::vec3 getDefaultEyePosition() const; + Q_INVOKABLE glm::vec3 getDefaultEyePosition() const; bool getShouldRenderLocally() const { return _shouldRender; } const QList& getAnimationHandles() const { return _animationHandles; } From be6c6ed8ca3ee83b67f2e016d40c6d3f8826517f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Jan 2015 16:10:33 -0800 Subject: [PATCH 08/22] Add 3D notifications on a simple vertical plane --- examples/notifications.js | 110 ++++++++++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 22 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index 2929c61a64..0aad7c78d8 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -59,7 +59,6 @@ // } var width = 340.0; //width of notification overlay -var height = 40.0; // height of a single line notification overlay var windowDimensions = Controller.getViewportDimensions(); // get the size of the interface window var overlayLocationX = (windowDimensions.x - (width + 20.0)); // positions window 20px from the right of the interface window var buttonLocationX = overlayLocationX + (width - 28.0); @@ -86,6 +85,12 @@ var times = []; var heights = []; var myAlpha = []; var arrays = []; +var isOnHMD = false, + ENABLE_VR_MODE = "Enable VR Mode", + NOTIFICATIONS_3D_DISTANCE = 0.6, // Distance from avatar position. + NOTIFICATIONS_3D_ELEVATION = 0.0, // Height of top middle of top notification relative to avatar eyes. + NOTIFICATION_3D_SCALE = 0.002, // Multiplier that converts 2D overlay dimensions to 3D overlay dimensions. + NOTIFICATION_3D_BUTTON_WIDTH = 40 * NOTIFICATION_3D_SCALE; // Need a little more room for button in 3D. // push data from above to the 2 dimensional array function createArrays(notice, button, createTime, height, myAlpha) { @@ -143,13 +148,58 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { // to handle auxiliary data not carried by the overlay class // specifically notification "heights", "times" of creation, and . function notify(notice, button, height) { - notifications.push((Overlays.addOverlay("text", notice))); - buttons.push((Overlays.addOverlay("image", button))); - times.push(new Date().getTime() / 1000); + var noticeWidth, + noticeHeight, + noticeY, + position3D, + rotation3D, + last; + + if (isOnHMD) { + // Calculate 3D values using 2D overlay properties. + + rotation3D = MyAvatar.orientation; + + noticeWidth = notice.width * NOTIFICATION_3D_SCALE + NOTIFICATION_3D_BUTTON_WIDTH; + noticeHeight = notice.height * NOTIFICATION_3D_SCALE; + noticeY = NOTIFICATIONS_3D_ELEVATION - notice.y * NOTIFICATION_3D_SCALE - noticeHeight / 2; + + notice.size = { x: noticeWidth, y: noticeHeight }; + notice.topMargin = 0.75 * notice.topMargin * NOTIFICATION_3D_SCALE; + notice.leftMargin = 2 * notice.leftMargin * NOTIFICATION_3D_SCALE; + notice.bottomMargin = 0; + notice.rightMargin = 0; + notice.lineHeight = 10.0 * (fontSize / 12.0) * NOTIFICATION_3D_SCALE; + notice.isFacingAvatar = false; + + position3D = { x: 0, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE }; + position3D = Vec3.multiplyQbyV(rotation3D, position3D); + position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); + notice.position = position3D; + notice.rotation = rotation3D; + + button.url = button.imageURL; + button.scale = button.width * NOTIFICATION_3D_SCALE; + button.isFacingAvatar = false; + + position3D = { x: (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH) / 2, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE + 0.001 }; + position3D = Vec3.multiplyQbyV(rotation3D, position3D); + position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); + button.position = position3D; + button.rotation = rotation3D; + + notifications.push((Overlays.addOverlay("text3d", notice))); + buttons.push((Overlays.addOverlay("billboard", button))); + } else { + notifications.push((Overlays.addOverlay("text", notice))); + buttons.push((Overlays.addOverlay("image", button))); + } + height = height + 1.0; heights.push(height); + times.push(new Date().getTime() / 1000); myAlpha.push(0); - var last = notifications.length - 1; + last = notifications.length - 1; createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]); fadeIn(notifications[last], buttons[last]); } @@ -163,7 +213,7 @@ function createNotification(text) { height = 40.0, stack = 0, level, - overlayProperties, + noticeProperties, bLevel, buttonProperties, i; @@ -178,7 +228,7 @@ function createNotification(text) { level = (stack + 20.0); height = height + extraLine; - overlayProperties = { + noticeProperties = { x: overlayLocationX, y: level, width: width, @@ -205,7 +255,18 @@ function createNotification(text) { alpha: backgroundAlpha }; - notify(overlayProperties, buttonProperties, height); + notify(noticeProperties, buttonProperties, height); +} + +function deleteNotification(index) { + Overlays.deleteOverlay(notifications[index]); + Overlays.deleteOverlay(buttons[index]); + notifications.splice(index, 1); + buttons.splice(index, 1); + times.splice(index, 1); + heights.splice(index, 1); + myAlpha.splice(index, 1); + arrays.splice(index, 1); } // wraps whole word to newline @@ -257,15 +318,27 @@ function update() { j, k; + if (isOnHMD !== Menu.isOptionChecked(ENABLE_VR_MODE)) { + while (arrays.length > 0) { + deleteNotification(0); + } + isOnHMD = !isOnHMD; + return; + } + frame += 1; if ((frame % 60.0) === 0) { // only update once a second checkSize(); // checks for size change to trigger windowResize notification - locationY = 20.0; - for (i = 0; i < arrays.length; i += 1) { //repositions overlays as others fade - nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); - Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY }); - Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); - locationY = locationY + arrays[i][3]; + if (isOnHMD) { + // TODO + } else { + locationY = 20.0; + for (i = 0; i < arrays.length; i += 1) { //repositions overlays as others fade + nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); + Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY }); + Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); + locationY = locationY + arrays[i][3]; + } } } @@ -371,14 +444,7 @@ function mousePressEvent(event) { for (i = 0; i < buttons.length; i += 1) { //if user clicked a button if (clickedOverlay === buttons[i]) { - Overlays.deleteOverlay(notifications[i]); - Overlays.deleteOverlay(buttons[i]); - notifications.splice(i, 1); - buttons.splice(i, 1); - times.splice(i, 1); - heights.splice(i, 1); - myAlpha.splice(i, 1); - arrays.splice(i, 1); + deleteNotification(i); } } } From 567ac9eb2face9f817b4e24a08b1c15827a5273a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Jan 2015 17:08:31 -0800 Subject: [PATCH 09/22] Update 3D notification positions as avatar moves --- examples/notifications.js | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index 0aad7c78d8..fcafe3a8b2 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -90,7 +90,8 @@ var isOnHMD = false, NOTIFICATIONS_3D_DISTANCE = 0.6, // Distance from avatar position. NOTIFICATIONS_3D_ELEVATION = 0.0, // Height of top middle of top notification relative to avatar eyes. NOTIFICATION_3D_SCALE = 0.002, // Multiplier that converts 2D overlay dimensions to 3D overlay dimensions. - NOTIFICATION_3D_BUTTON_WIDTH = 40 * NOTIFICATION_3D_SCALE; // Need a little more room for button in 3D. + NOTIFICATION_3D_BUTTON_WIDTH = 40 * NOTIFICATION_3D_SCALE, // Need a little more room for button in 3D. + overlay3DPositions = []; // push data from above to the 2 dimensional array function createArrays(notice, button, createTime, height, myAlpha) { @@ -106,6 +107,7 @@ function dismiss(firstNoteOut, firstButOut, firstOut) { times.splice(firstOut, 1); heights.splice(firstOut, 1); myAlpha.splice(firstOut, 1); + overlay3DPositions.splice(firstOut, 1); } function fadeIn(noticeIn, buttonIn) { @@ -151,6 +153,8 @@ function notify(notice, button, height) { var noticeWidth, noticeHeight, noticeY, + notificationPosition, + buttonPosition, position3D, rotation3D, last; @@ -172,8 +176,8 @@ function notify(notice, button, height) { notice.lineHeight = 10.0 * (fontSize / 12.0) * NOTIFICATION_3D_SCALE; notice.isFacingAvatar = false; - position3D = { x: 0, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE }; - position3D = Vec3.multiplyQbyV(rotation3D, position3D); + notificationPosition = { x: 0, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE }; + position3D = Vec3.multiplyQbyV(rotation3D, notificationPosition); position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); notice.position = position3D; notice.rotation = rotation3D; @@ -182,14 +186,19 @@ function notify(notice, button, height) { button.scale = button.width * NOTIFICATION_3D_SCALE; button.isFacingAvatar = false; - position3D = { x: (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH) / 2, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE + 0.001 }; - position3D = Vec3.multiplyQbyV(rotation3D, position3D); + buttonPosition = { + x: (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH) / 2, + y: noticeY, + z: -NOTIFICATIONS_3D_DISTANCE + 0.001 + }; + position3D = Vec3.multiplyQbyV(rotation3D, buttonPosition); position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); button.position = position3D; button.rotation = rotation3D; notifications.push((Overlays.addOverlay("text3d", notice))); buttons.push((Overlays.addOverlay("billboard", button))); + overlay3DPositions.push({ notification: notificationPosition, button: buttonPosition }); } else { notifications.push((Overlays.addOverlay("text", notice))); buttons.push((Overlays.addOverlay("image", button))); @@ -266,6 +275,7 @@ function deleteNotification(index) { times.splice(index, 1); heights.splice(index, 1); myAlpha.splice(index, 1); + overlay3DPositions.splice(index, 1); arrays.splice(index, 1); } @@ -314,6 +324,10 @@ function update() { noticeOut, buttonOut, arraysOut, + defaultEyePosition, + avatarOrientation, + notificationPosition, + buttonPosition, i, j, k; @@ -356,6 +370,21 @@ function update() { } } } + + if (isOnHMD && notifications.length > 0) { + // Update 3D overlays to maintain positions relative to avatar + defaultEyePosition = MyAvatar.getDefaultEyePosition(); + avatarOrientation = MyAvatar.orientation; + + for (i = 0; i < notifications.length; i += 1) { + notificationPosition = Vec3.sum(defaultEyePosition, + Vec3.multiplyQbyV(avatarOrientation, overlay3DPositions[i].notification)); + buttonPosition = Vec3.sum(defaultEyePosition, + Vec3.multiplyQbyV(avatarOrientation, overlay3DPositions[i].button)); + Overlays.editOverlay(notifications[i], { position: notificationPosition, rotation: avatarOrientation }); + Overlays.editOverlay(buttons[i], { position: buttonPosition, rotation: avatarOrientation }); + } + } } var STARTUP_TIMEOUT = 500, // ms From f3147ab3e4b05772ecb809aede47e4aacc890730 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Jan 2015 17:58:50 -0800 Subject: [PATCH 10/22] Move 3D notifications up as old ones are deleted --- examples/notifications.js | 81 +++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index fcafe3a8b2..ecd95428aa 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -91,7 +91,7 @@ var isOnHMD = false, NOTIFICATIONS_3D_ELEVATION = 0.0, // Height of top middle of top notification relative to avatar eyes. NOTIFICATION_3D_SCALE = 0.002, // Multiplier that converts 2D overlay dimensions to 3D overlay dimensions. NOTIFICATION_3D_BUTTON_WIDTH = 40 * NOTIFICATION_3D_SCALE, // Need a little more room for button in 3D. - overlay3DPositions = []; + overlay3DDetails = []; // push data from above to the 2 dimensional array function createArrays(notice, button, createTime, height, myAlpha) { @@ -107,7 +107,7 @@ function dismiss(firstNoteOut, firstButOut, firstOut) { times.splice(firstOut, 1); heights.splice(firstOut, 1); myAlpha.splice(firstOut, 1); - overlay3DPositions.splice(firstOut, 1); + overlay3DDetails.splice(firstOut, 1); } function fadeIn(noticeIn, buttonIn) { @@ -146,15 +146,34 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { }, 20); } +function calculate3DOverlayPositions(noticeWidth, noticeHeight, y) { + var noticeY, + notificationPosition, + buttonPosition; + + noticeY = NOTIFICATIONS_3D_ELEVATION - y * NOTIFICATION_3D_SCALE - noticeHeight / 2; + notificationPosition = { x: 0, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE }; + buttonPosition = { + x: (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH) / 2, + y: noticeY, + z: -NOTIFICATIONS_3D_DISTANCE + 0.001 + }; + + return { + notificationPosition: notificationPosition, + buttonPosition: buttonPosition + }; +} + // Pushes data to each array and sets up data for 2nd dimension array // to handle auxiliary data not carried by the overlay class // specifically notification "heights", "times" of creation, and . function notify(notice, button, height) { var noticeWidth, noticeHeight, - noticeY, notificationPosition, buttonPosition, + positions, position3D, rotation3D, last; @@ -162,11 +181,8 @@ function notify(notice, button, height) { if (isOnHMD) { // Calculate 3D values using 2D overlay properties. - rotation3D = MyAvatar.orientation; - noticeWidth = notice.width * NOTIFICATION_3D_SCALE + NOTIFICATION_3D_BUTTON_WIDTH; noticeHeight = notice.height * NOTIFICATION_3D_SCALE; - noticeY = NOTIFICATIONS_3D_ELEVATION - notice.y * NOTIFICATION_3D_SCALE - noticeHeight / 2; notice.size = { x: noticeWidth, y: noticeHeight }; notice.topMargin = 0.75 * notice.topMargin * NOTIFICATION_3D_SCALE; @@ -176,21 +192,21 @@ function notify(notice, button, height) { notice.lineHeight = 10.0 * (fontSize / 12.0) * NOTIFICATION_3D_SCALE; notice.isFacingAvatar = false; - notificationPosition = { x: 0, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE }; + button.url = button.imageURL; + button.scale = button.width * NOTIFICATION_3D_SCALE; + button.isFacingAvatar = false; + + positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y); + notificationPosition = positions.notificationPosition; + buttonPosition = positions.buttonPosition; + + rotation3D = MyAvatar.orientation; + position3D = Vec3.multiplyQbyV(rotation3D, notificationPosition); position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); notice.position = position3D; notice.rotation = rotation3D; - button.url = button.imageURL; - button.scale = button.width * NOTIFICATION_3D_SCALE; - button.isFacingAvatar = false; - - buttonPosition = { - x: (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH) / 2, - y: noticeY, - z: -NOTIFICATIONS_3D_DISTANCE + 0.001 - }; position3D = Vec3.multiplyQbyV(rotation3D, buttonPosition); position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); button.position = position3D; @@ -198,7 +214,12 @@ function notify(notice, button, height) { notifications.push((Overlays.addOverlay("text3d", notice))); buttons.push((Overlays.addOverlay("billboard", button))); - overlay3DPositions.push({ notification: notificationPosition, button: buttonPosition }); + overlay3DDetails.push({ + notificationPosition: notificationPosition, + buttonPosition: buttonPosition, + width: noticeWidth, + height: noticeHeight + }); } else { notifications.push((Overlays.addOverlay("text", notice))); buttons.push((Overlays.addOverlay("image", button))); @@ -275,7 +296,7 @@ function deleteNotification(index) { times.splice(index, 1); heights.splice(index, 1); myAlpha.splice(index, 1); - overlay3DPositions.splice(index, 1); + overlay3DDetails.splice(index, 1); arrays.splice(index, 1); } @@ -328,6 +349,7 @@ function update() { avatarOrientation, notificationPosition, buttonPosition, + positions, i, j, k; @@ -343,16 +365,17 @@ function update() { frame += 1; if ((frame % 60.0) === 0) { // only update once a second checkSize(); // checks for size change to trigger windowResize notification - if (isOnHMD) { - // TODO - } else { - locationY = 20.0; - for (i = 0; i < arrays.length; i += 1) { //repositions overlays as others fade - nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); - Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY }); - Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); - locationY = locationY + arrays[i][3]; + locationY = 20.0; + for (i = 0; i < arrays.length; i += 1) { //repositions overlays as others fade + nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); + Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY }); + Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); + if (isOnHMD) { + positions = calculate3DOverlayPositions(overlay3DDetails[i].width, overlay3DDetails[i].height, locationY); + overlay3DDetails[i].notificationPosition = positions.notificationPosition; + overlay3DDetails[i].buttonPosition = positions.buttonPosition; } + locationY = locationY + arrays[i][3]; } } @@ -378,9 +401,9 @@ function update() { for (i = 0; i < notifications.length; i += 1) { notificationPosition = Vec3.sum(defaultEyePosition, - Vec3.multiplyQbyV(avatarOrientation, overlay3DPositions[i].notification)); + Vec3.multiplyQbyV(avatarOrientation, overlay3DDetails[i].notificationPosition)); buttonPosition = Vec3.sum(defaultEyePosition, - Vec3.multiplyQbyV(avatarOrientation, overlay3DPositions[i].button)); + Vec3.multiplyQbyV(avatarOrientation, overlay3DDetails[i].buttonPosition)); Overlays.editOverlay(notifications[i], { position: notificationPosition, rotation: avatarOrientation }); Overlays.editOverlay(buttons[i], { position: buttonPosition, rotation: avatarOrientation }); } From 00de7062cc4bad4e9ab9b5bdc6b2b1f504114872 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Jan 2015 18:03:00 -0800 Subject: [PATCH 11/22] Longer persist time for 3D notifications --- examples/notifications.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/notifications.js b/examples/notifications.js index ecd95428aa..d02e31cd0f 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -69,7 +69,9 @@ var textColor = { red: 228, green: 228, blue: 228}; // text color var backColor = { red: 2, green: 2, blue: 2}; // background color was 38,38,38 var backgroundAlpha = 0; var fontSize = 12.0; -var persistTime = 10.0; // time in seconds before notification fades +var PERSIST_TIME_2D = 10.0; // Time in seconds before notification fades +var PERSIST_TIME_3D = 15.0; +var persistTime = PERSIST_TIME_2D; var clickedText = false; var frame = 0; var ourWidth = Window.innerWidth; @@ -359,6 +361,7 @@ function update() { deleteNotification(0); } isOnHMD = !isOnHMD; + persistTime = isOnHMD ? PERSIST_TIME_3D : PERSIST_TIME_2D; return; } From ef7ea0eaa68ca6ffa323d8185eb7398f2bea67cf Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Jan 2015 21:34:34 -0800 Subject: [PATCH 12/22] Position and angle 3D notifications in lower part of view --- examples/notifications.js | 67 ++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index d02e31cd0f..682750f56f 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -89,8 +89,11 @@ var myAlpha = []; var arrays = []; var isOnHMD = false, ENABLE_VR_MODE = "Enable VR Mode", - NOTIFICATIONS_3D_DISTANCE = 0.6, // Distance from avatar position. - NOTIFICATIONS_3D_ELEVATION = 0.0, // Height of top middle of top notification relative to avatar eyes. + NOTIFICATIONS_3D_DIRECTION = 0.0, // Degrees from avatar orientation. + NOTIFICATIONS_3D_DISTANCE = 0.6, // Horizontal distance from avatar position. + NOTIFICATIONS_3D_ELEVATION = -0.8, // Height of top middle of top notification relative to avatar eyes. + NOTIFICATIONS_3D_YAW = 0.0, // Degrees relative to notifications direction. + NOTIFICATIONS_3D_PITCH = -60.0, // Degrees from vertical. NOTIFICATION_3D_SCALE = 0.002, // Multiplier that converts 2D overlay dimensions to 3D overlay dimensions. NOTIFICATION_3D_BUTTON_WIDTH = 40 * NOTIFICATION_3D_SCALE, // Need a little more room for button in 3D. overlay3DDetails = []; @@ -149,19 +152,33 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { } function calculate3DOverlayPositions(noticeWidth, noticeHeight, y) { + // Calculates overlay positions and orientations in avatar coordinates. var noticeY, + originOffset, + notificationOrientation, notificationPosition, buttonPosition; - noticeY = NOTIFICATIONS_3D_ELEVATION - y * NOTIFICATION_3D_SCALE - noticeHeight / 2; - notificationPosition = { x: 0, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE }; - buttonPosition = { - x: (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH) / 2, - y: noticeY, - z: -NOTIFICATIONS_3D_DISTANCE + 0.001 - }; + // Notification plane positions + noticeY = -y * NOTIFICATION_3D_SCALE - noticeHeight / 2; + notificationPosition = { x: 0, y: noticeY, z: 0 }; + buttonPosition = { x: (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH) / 2, y: noticeY, z: 0.001 }; + + // Rotate plane + notificationOrientation = Quat.fromPitchYawRollDegrees(NOTIFICATIONS_3D_PITCH, + NOTIFICATIONS_3D_DIRECTION + NOTIFICATIONS_3D_YAW, 0); + notificationPosition = Vec3.multiplyQbyV(notificationOrientation, notificationPosition); + buttonPosition = Vec3.multiplyQbyV(notificationOrientation, buttonPosition); + + // Translate plane + originOffset = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, NOTIFICATIONS_3D_DIRECTION, 0), + { x: 0, y: 0, z: -NOTIFICATIONS_3D_DISTANCE }); + originOffset.y += NOTIFICATIONS_3D_ELEVATION; + notificationPosition = Vec3.sum(originOffset, notificationPosition); + buttonPosition = Vec3.sum(originOffset, buttonPosition); return { + notificationOrientation: notificationOrientation, notificationPosition: notificationPosition, buttonPosition: buttonPosition }; @@ -173,15 +190,11 @@ function calculate3DOverlayPositions(noticeWidth, noticeHeight, y) { function notify(notice, button, height) { var noticeWidth, noticeHeight, - notificationPosition, - buttonPosition, positions, - position3D, - rotation3D, last; if (isOnHMD) { - // Calculate 3D values using 2D overlay properties. + // Calculate 3D values from 2D overlay properties. noticeWidth = notice.width * NOTIFICATION_3D_SCALE + NOTIFICATION_3D_BUTTON_WIDTH; noticeHeight = notice.height * NOTIFICATION_3D_SCALE; @@ -199,26 +212,13 @@ function notify(notice, button, height) { button.isFacingAvatar = false; positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y); - notificationPosition = positions.notificationPosition; - buttonPosition = positions.buttonPosition; - - rotation3D = MyAvatar.orientation; - - position3D = Vec3.multiplyQbyV(rotation3D, notificationPosition); - position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); - notice.position = position3D; - notice.rotation = rotation3D; - - position3D = Vec3.multiplyQbyV(rotation3D, buttonPosition); - position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); - button.position = position3D; - button.rotation = rotation3D; notifications.push((Overlays.addOverlay("text3d", notice))); buttons.push((Overlays.addOverlay("billboard", button))); overlay3DDetails.push({ - notificationPosition: notificationPosition, - buttonPosition: buttonPosition, + notificationOrientation: positions.notificationOrientation, + notificationPosition: positions.notificationPosition, + buttonPosition: positions.buttonPosition, width: noticeWidth, height: noticeHeight }); @@ -350,6 +350,7 @@ function update() { defaultEyePosition, avatarOrientation, notificationPosition, + notificationOrientation, buttonPosition, positions, i, @@ -375,6 +376,7 @@ function update() { Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); if (isOnHMD) { positions = calculate3DOverlayPositions(overlay3DDetails[i].width, overlay3DDetails[i].height, locationY); + overlay3DDetails[i].notificationOrientation = positions.notificationOrientation; overlay3DDetails[i].notificationPosition = positions.notificationPosition; overlay3DDetails[i].buttonPosition = positions.buttonPosition; } @@ -405,10 +407,11 @@ function update() { for (i = 0; i < notifications.length; i += 1) { notificationPosition = Vec3.sum(defaultEyePosition, Vec3.multiplyQbyV(avatarOrientation, overlay3DDetails[i].notificationPosition)); + notificationOrientation = Quat.multiply(avatarOrientation, overlay3DDetails[i].notificationOrientation); buttonPosition = Vec3.sum(defaultEyePosition, Vec3.multiplyQbyV(avatarOrientation, overlay3DDetails[i].buttonPosition)); - Overlays.editOverlay(notifications[i], { position: notificationPosition, rotation: avatarOrientation }); - Overlays.editOverlay(buttons[i], { position: buttonPosition, rotation: avatarOrientation }); + Overlays.editOverlay(notifications[i], { position: notificationPosition, rotation: notificationOrientation }); + Overlays.editOverlay(buttons[i], { position: buttonPosition, rotation: notificationOrientation }); } } } From 7d1cc1f20dc807a00392865fff529895c9d41060 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Jan 2015 21:44:55 -0800 Subject: [PATCH 13/22] Make 3D "x" button work --- examples/notifications.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index 682750f56f..525d438fe6 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -495,12 +495,18 @@ function onMuteStateChanged() { // handles mouse clicks on buttons function mousePressEvent(event) { - var clickedOverlay, + var pickRay, + clickedOverlay, i; - clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); //identify which overlay was clicked + if (isOnHMD) { + pickRay = Camera.computePickRay(event.x, event.y); + clickedOverlay = Overlays.findRayIntersection(pickRay).overlayID; + } else { + clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + } - for (i = 0; i < buttons.length; i += 1) { //if user clicked a button + for (i = 0; i < buttons.length; i += 1) { if (clickedOverlay === buttons[i]) { deleteNotification(i); } From 958d853b565237dc2c8aa649f62a1bfdf6cbd5aa Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:13:54 -0800 Subject: [PATCH 14/22] Add BatchLoader --- libraries/script-engine/src/BatchLoader.cpp | 82 +++++++++++++++++++++ libraries/script-engine/src/BatchLoader.h | 45 +++++++++++ 2 files changed, 127 insertions(+) create mode 100644 libraries/script-engine/src/BatchLoader.cpp create mode 100644 libraries/script-engine/src/BatchLoader.h diff --git a/libraries/script-engine/src/BatchLoader.cpp b/libraries/script-engine/src/BatchLoader.cpp new file mode 100644 index 0000000000..c44742c0b6 --- /dev/null +++ b/libraries/script-engine/src/BatchLoader.cpp @@ -0,0 +1,82 @@ +// +// BatchLoader.cpp +// libraries/script-engine/src +// +// Created by Ryan Huffman on 01/22/15 +// 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 +// + +#include +#include + +#include +#include "BatchLoader.h" +#include + +BatchLoader::BatchLoader(const QList& urls) + : QObject(), + _finished(false), + _urls(urls.toSet()), + _started(false), + _data() { +} + +void BatchLoader::start() { + if (_started) { + return; + } + + _started = true; + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + for (QUrl url : _urls) { + if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "ftp") { + QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); + + qDebug() << "Downloading file at" << url; + + connect(reply, &QNetworkReply::finished, [=]() { + if (reply->error()) { + emit errorLoadingFile(url); + _data.insert(url, QString()); + } else { + _data.insert(url, reply->readAll()); + } + reply->deleteLater(); + checkFinished(); + }); + + // If we end up being destroyed before the reply finishes, clean it up + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); + + } else { +#ifdef _WIN32 + QString fileName = url.toString(); +#else + QString fileName = url.toLocalFile(); +#endif + + qDebug() << "Reading file at " << fileName; + + QFile scriptFile(fileName); + if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { + QTextStream in(&scriptFile); + _data.insert(url, in.readAll()); + // includeContents = in.readAll(); + } else { + emit errorLoadingFile(url); + _data.insert(url, QString()); + } + } + } + checkFinished(); +} + +void BatchLoader::checkFinished() { + if (!_finished && _urls.size() == _data.size()) { + _finished = true; + emit finished(_data); + } +} diff --git a/libraries/script-engine/src/BatchLoader.h b/libraries/script-engine/src/BatchLoader.h new file mode 100644 index 0000000000..f165aafed4 --- /dev/null +++ b/libraries/script-engine/src/BatchLoader.h @@ -0,0 +1,45 @@ +// +// BatchLoader.h +// libraries/script-engine/src +// +// Created by Ryan Huffman on 01/22/15 +// 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 +// + +#ifndef hifi_BatchLoader_h +#define hifi_BatchLoader_h + +#include +#include +#include +#include +#include +#include + +#include + +class BatchLoader : public QObject { + Q_OBJECT +public: + BatchLoader(const QList& urls) ; + + void start(); + bool isFinished() const { return _finished; }; + +signals: + void finished(const QMap& data); + void errorLoadingFile(QUrl url); + +private: + void checkFinished(); + + bool _finished; + bool _started; + QSet _urls; + QMap _data; +}; + +#endif // hifi_BatchLoader_h From 38b38eb063422d625afa3137ae094fb0a3984ebe Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:14:21 -0800 Subject: [PATCH 15/22] Update ScriptEngine to use BatchLoader --- libraries/script-engine/src/ScriptEngine.cpp | 77 +++++++++++--------- libraries/script-engine/src/ScriptEngine.h | 1 + 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0f860208f4..a505b63b69 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -31,6 +31,7 @@ #include "AnimationObject.h" #include "ArrayBufferViewClass.h" +#include "BatchLoader.h" #include "DataViewClass.h" #include "EventTypes.h" #include "MenuItemProperties.h" @@ -304,7 +305,7 @@ QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileN QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber); if (hasUncaughtException()) { int line = uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ": " << result.toString(); + qDebug() << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString(); } emit evaluationFinished(result, hasUncaughtException()); clearExceptions(); @@ -595,46 +596,52 @@ void ScriptEngine::print(const QString& message) { emit printedMessage(message); } -void ScriptEngine::include(const QString& includeFile) { - QUrl url = resolvePath(includeFile); - QString includeContents; +void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) { + QList urls; + for (QString file : includeFiles) { + urls.append(resolvePath(file)); + } - if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "ftp") { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); - qDebug() << "Downloading included script at" << includeFile; - QEventLoop loop; - QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); - loop.exec(); - includeContents = reply->readAll(); - reply->deleteLater(); - } else { -#ifdef _WIN32 - QString fileName = url.toString(); -#else - QString fileName = url.toLocalFile(); -#endif - - QFile scriptFile(fileName); - if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { - qDebug() << "Including file:" << fileName; - QTextStream in(&scriptFile); - includeContents = in.readAll(); - } else { - qDebug() << "ERROR Including file:" << fileName; - emit errorMessage("ERROR Including file:" + fileName); + BatchLoader* loader = new BatchLoader(urls); + + auto evaluateScripts = [=](const QMap& data) { + for (QUrl url : urls) { + QString contents = data[url]; + qDebug() << "About to load: " << url; + if (contents.isNull()) { + qDebug() << "Error loading file: " << url; + } else { + QScriptValue result = evaluate(contents, url.toString()); + } } - } - QScriptValue result = evaluate(includeContents); - if (hasUncaughtException()) { - int line = uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at (" << includeFile << ") line" << line << ":" << result.toString(); - emit errorMessage("Uncaught exception at (" + includeFile + ") line" + QString::number(line) + ":" + result.toString()); - clearExceptions(); + if (callback.isFunction()) { + QScriptValue(callback).call(); + } + + loader->deleteLater(); + }; + + connect(loader, &BatchLoader::finished, this, evaluateScripts); + + // If we are destroyed before the loader completes, make sure to clean it up + connect(this, &QObject::destroyed, loader, &QObject::deleteLater); + + loader->start(); + + if (!callback.isFunction() && !loader->isFinished()) { + QEventLoop loop; + QObject::connect(loader, &BatchLoader::finished, &loop, &QEventLoop::quit); + loop.exec(); } } +void ScriptEngine::include(const QString& includeFile) { + QStringList urls; + urls.append(includeFile); + include(urls); +} + void ScriptEngine::load(const QString& loadFile) { QUrl url = resolvePath(loadFile); emit loadScript(url.toString(), false); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 10f419937a..126e943982 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -96,6 +96,7 @@ public slots: QObject* setTimeout(const QScriptValue& function, int timeoutMS); void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); } void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(timer)); } + void include(const QStringList& includeFiles, QScriptValue callback = QScriptValue()); void include(const QString& includeFile); void load(const QString& loadfile); void print(const QString& message); From 4fd5f743651479c33db04c8e29ffb7d1a16bfc10 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:14:49 -0800 Subject: [PATCH 16/22] Update editEntities.js to load includes in parallel --- examples/editEntities.js | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/examples/editEntities.js b/examples/editEntities.js index 9187b624fd..f9d881f0bd 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -12,34 +12,37 @@ // HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -Script.include("libraries/stringHelpers.js"); -Script.include("libraries/dataviewHelpers.js"); -Script.include("libraries/httpMultiPart.js"); -Script.include("libraries/modelUploader.js"); -Script.include("libraries/toolBars.js"); -Script.include("libraries/progressDialog.js"); -Script.include("libraries/entitySelectionTool.js"); +Script.include([ + "http://public.highfidelity.io/scripts/libraries/stringHelpers.js", + "http://public.highfidelity.io/scripts/libraries/dataviewHelpers.js", + "http://public.highfidelity.io/scripts/libraries/httpMultiPart.js", + "http://public.highfidelity.io/scripts/libraries/modelUploader.js", + "http://public.highfidelity.io/scripts/libraries/toolBars.js", + "http://public.highfidelity.io/scripts/libraries/progressDialog.js", + + "http://public.highfidelity.io/scripts/libraries/entitySelectionTool.js", + "http://public.highfidelity.io/scripts/libraries/ModelImporter.js", + + "http://public.highfidelity.io/scripts/libraries/ExportMenu.js", + "http://public.highfidelity.io/scripts/libraries/ToolTip.js", + + "http://public.highfidelity.io/scripts/libraries/entityPropertyDialogBox.js", + "http://public.highfidelity.io/scripts/libraries/entityCameraTool.js", + "http://public.highfidelity.io/scripts/libraries/gridTool.js", + "http://public.highfidelity.io/scripts/libraries/entityList.js", +]); + var selectionDisplay = SelectionDisplay; var selectionManager = SelectionManager; - -Script.include("libraries/ModelImporter.js"); var modelImporter = new ModelImporter(); - -Script.include("libraries/ExportMenu.js"); -Script.include("libraries/ToolTip.js"); - -Script.include("libraries/entityPropertyDialogBox.js"); var entityPropertyDialogBox = EntityPropertyDialogBox; -Script.include("libraries/entityCameraTool.js"); var cameraManager = new CameraManager(); -Script.include("libraries/gridTool.js"); var grid = Grid(); gridTool = GridTool({ horizontalGrid: grid }); -Script.include("libraries/entityList.js"); var entityListTool = EntityListTool(); var hasShownPropertiesTool = false; From b4132f1e7dd596b384d1e61c2677971a63bb3b7e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:16:36 -0800 Subject: [PATCH 17/22] Remove commented line --- libraries/script-engine/src/BatchLoader.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/script-engine/src/BatchLoader.cpp b/libraries/script-engine/src/BatchLoader.cpp index c44742c0b6..ed6591423f 100644 --- a/libraries/script-engine/src/BatchLoader.cpp +++ b/libraries/script-engine/src/BatchLoader.cpp @@ -64,7 +64,6 @@ void BatchLoader::start() { if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { QTextStream in(&scriptFile); _data.insert(url, in.readAll()); - // includeContents = in.readAll(); } else { emit errorLoadingFile(url); _data.insert(url, QString()); From e27db48e262c6297d3956bb36dcfa2de7a1c9c24 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:20:42 -0800 Subject: [PATCH 18/22] Remove qDebug and unnecessary include --- libraries/script-engine/src/BatchLoader.h | 2 -- libraries/script-engine/src/ScriptEngine.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/libraries/script-engine/src/BatchLoader.h b/libraries/script-engine/src/BatchLoader.h index f165aafed4..6d19b2322f 100644 --- a/libraries/script-engine/src/BatchLoader.h +++ b/libraries/script-engine/src/BatchLoader.h @@ -19,8 +19,6 @@ #include #include -#include - class BatchLoader : public QObject { Q_OBJECT public: diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a505b63b69..246f9d3be3 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -607,7 +607,6 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac auto evaluateScripts = [=](const QMap& data) { for (QUrl url : urls) { QString contents = data[url]; - qDebug() << "About to load: " << url; if (contents.isNull()) { qDebug() << "Error loading file: " << url; } else { From 22cdf1cb226928fd806ebb415e1b191f3dace368 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:24:20 -0800 Subject: [PATCH 19/22] Remove BatchLoader::errorLoadingFile and reorder properties --- libraries/script-engine/src/BatchLoader.cpp | 4 +--- libraries/script-engine/src/BatchLoader.h | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/libraries/script-engine/src/BatchLoader.cpp b/libraries/script-engine/src/BatchLoader.cpp index ed6591423f..e2c345ce16 100644 --- a/libraries/script-engine/src/BatchLoader.cpp +++ b/libraries/script-engine/src/BatchLoader.cpp @@ -18,9 +18,9 @@ BatchLoader::BatchLoader(const QList& urls) : QObject(), + _started(false), _finished(false), _urls(urls.toSet()), - _started(false), _data() { } @@ -39,7 +39,6 @@ void BatchLoader::start() { connect(reply, &QNetworkReply::finished, [=]() { if (reply->error()) { - emit errorLoadingFile(url); _data.insert(url, QString()); } else { _data.insert(url, reply->readAll()); @@ -65,7 +64,6 @@ void BatchLoader::start() { QTextStream in(&scriptFile); _data.insert(url, in.readAll()); } else { - emit errorLoadingFile(url); _data.insert(url, QString()); } } diff --git a/libraries/script-engine/src/BatchLoader.h b/libraries/script-engine/src/BatchLoader.h index 6d19b2322f..cda040d219 100644 --- a/libraries/script-engine/src/BatchLoader.h +++ b/libraries/script-engine/src/BatchLoader.h @@ -29,13 +29,12 @@ public: signals: void finished(const QMap& data); - void errorLoadingFile(QUrl url); private: void checkFinished(); - bool _finished; bool _started; + bool _finished; QSet _urls; QMap _data; }; From c8a755df1bb902f96dfa42ffa9dd746d3a130181 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 26 Jan 2015 08:32:23 -0800 Subject: [PATCH 20/22] Remove unused variable --- examples/notifications.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index 525d438fe6..1b512634d7 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -447,8 +447,7 @@ function isStartingUp() { // Triggers notification if a user logs on or off function onOnlineUsersChanged(users) { - var user, - i; + var i; if (!isStartingUp()) { // Skip user notifications at startup. for (i = 0; i < users.length; i += 1) { From ce9e8910e48c464c7e89f407370a9db52f2361d3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 29 Jan 2015 09:04:57 -0800 Subject: [PATCH 21/22] Add callback to QScriptEngine::include(QString) --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++-- libraries/script-engine/src/ScriptEngine.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 246f9d3be3..b400dd5cfb 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -635,10 +635,10 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac } } -void ScriptEngine::include(const QString& includeFile) { +void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { QStringList urls; urls.append(includeFile); - include(urls); + include(urls, callback); } void ScriptEngine::load(const QString& loadFile) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 126e943982..f78a14bffa 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -97,7 +97,7 @@ public slots: void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); } void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(timer)); } void include(const QStringList& includeFiles, QScriptValue callback = QScriptValue()); - void include(const QString& includeFile); + void include(const QString& includeFile, QScriptValue callback = QScriptValue()); void load(const QString& loadfile); void print(const QString& message); QUrl resolvePath(const QString& path) const; From e6caefa023f1e04095c23c76ae593557a07d64fc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 29 Jan 2015 09:08:55 -0800 Subject: [PATCH 22/22] Add comment to ScriptEngine::include() --- libraries/script-engine/src/ScriptEngine.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index b400dd5cfb..a002950d46 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -112,7 +112,7 @@ void ScriptEngine::setIsAvatar(bool isAvatar) { _avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); _avatarBillboardTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS); } - + if (!_isAvatar) { delete _avatarIdentityTimer; _avatarIdentityTimer = NULL; @@ -596,6 +596,12 @@ void ScriptEngine::print(const QString& message) { emit printedMessage(message); } +/** + * If a callback is specified, the included files will be loaded asynchronously and the callback will be called + * when all of the files have finished loading. + * If no callback is specified, the included files will be loaded synchronously and will block execution until + * all of the files have finished loading. + */ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) { QList urls; for (QString file : includeFiles) {