diff --git a/app/controllers/TaskController.scala b/app/controllers/TaskController.scala index 07f27af92e..5de72d40df 100644 --- a/app/controllers/TaskController.scala +++ b/app/controllers/TaskController.scala @@ -220,7 +220,7 @@ class TaskController @Inject() (implicit val env: Environment[User, SessionAuthe val userOption: Option[User] = identity val streetEdgeId: Int = data.auditTask.streetEdgeId val missionId: Int = data.missionProgress.missionId - val currTime: Timestamp = new Timestamp(Instant.now.toEpochMilli) + val currTime: Timestamp = new Timestamp(data.timestamp) if (data.auditTask.auditTaskId.isDefined) { val priorityBefore: StreetEdgePriority = streetPrioritiesFromIds(List(streetEdgeId)).head @@ -356,7 +356,7 @@ class TaskController @Inject() (implicit val env: Environment[User, SessionAuthe val env: EnvironmentSubmission = data.environment val taskEnv:AuditTaskEnvironment = AuditTaskEnvironment(0, auditTaskId, missionId, env.browser, env.browserVersion, env.browserWidth, env.browserHeight, env.availWidth, env.availHeight, env.screenWidth, - env.screenHeight, env.operatingSystem, Some(remoteAddress), env.language) + env.screenHeight, env.operatingSystem, Some(remoteAddress), env.language, env.cssZoom, Some(currTime)) AuditTaskEnvironmentTable.save(taskEnv) // Insert Street View metadata. @@ -364,7 +364,7 @@ class TaskController @Inject() (implicit val env: Environment[User, SessionAuthe // Insert new entry to gsv_data table, or update the last_viewed column if we've already recorded it. if (GSVDataTable.panoramaExists(pano.gsvPanoramaId)) { GSVDataTable.updateFromExplore(pano.gsvPanoramaId, pano.lat, pano.lng, pano.cameraHeading, - pano.cameraPitch, false, currTime) + pano.cameraPitch, expired = false, currTime) } else { val gsvData: GSVData = GSVData(pano.gsvPanoramaId, pano.width, pano.height, pano.tileWidth, pano.tileHeight, pano.captureDate, pano.copyright, pano.lat, pano.lng, pano.cameraHeading, pano.cameraPitch, expired = false, diff --git a/app/controllers/ValidationTaskController.scala b/app/controllers/ValidationTaskController.scala index a668a4f2f9..849fc7b26d 100644 --- a/app/controllers/ValidationTaskController.scala +++ b/app/controllers/ValidationTaskController.scala @@ -39,6 +39,7 @@ class ValidationTaskController @Inject() (implicit val env: Environment[User, Se */ def processValidationTaskSubmissions(data: ValidationTaskSubmission, remoteAddress: String, identity: Option[User]) = { val userOption = identity + val currTime = new Timestamp(data.timestamp) ValidationTaskInteractionTable.saveMultiple(data.interactions.map { interaction => ValidationTaskInteraction(0, interaction.missionId, interaction.action, interaction.gsvPanoramaId, interaction.lat, interaction.lng, interaction.heading, interaction.pitch, interaction.zoom, interaction.note, @@ -49,7 +50,7 @@ class ValidationTaskController @Inject() (implicit val env: Environment[User, Se val env: EnvironmentSubmission = data.environment val taskEnv: ValidationTaskEnvironment = ValidationTaskEnvironment(0, env.missionId, env.browser, env.browserVersion, env.browserWidth, env.browserHeight, env.availWidth, env.availHeight, env.screenWidth, - env.screenHeight, env.operatingSystem, Some(remoteAddress), env.language) + env.screenHeight, env.operatingSystem, Some(remoteAddress), env.language, env.cssZoom, Some(currTime)) ValidationTaskEnvironmentTable.save(taskEnv) // We aren't always submitting labels, so check if data.labels exists. diff --git a/app/formats/json/TaskSubmissionFormats.scala b/app/formats/json/TaskSubmissionFormats.scala index 6489bee5f1..d215d6101a 100644 --- a/app/formats/json/TaskSubmissionFormats.scala +++ b/app/formats/json/TaskSubmissionFormats.scala @@ -7,7 +7,7 @@ import scala.collection.immutable.Seq import play.api.libs.functional.syntax._ object TaskSubmissionFormats { - case class EnvironmentSubmission(browser: Option[String], browserVersion: Option[String], browserWidth: Option[Int], browserHeight: Option[Int], availWidth: Option[Int], availHeight: Option[Int], screenWidth: Option[Int], screenHeight: Option[Int], operatingSystem: Option[String], language: String) + case class EnvironmentSubmission(browser: Option[String], browserVersion: Option[String], browserWidth: Option[Int], browserHeight: Option[Int], availWidth: Option[Int], availHeight: Option[Int], screenWidth: Option[Int], screenHeight: Option[Int], operatingSystem: Option[String], language: String, cssZoom: Int) case class InteractionSubmission(action: String, gsvPanoramaId: Option[String], lat: Option[Float], lng: Option[Float], heading: Option[Float], pitch: Option[Float], zoom: Option[Int], note: Option[String], temporaryLabelId: Option[Int], timestamp: Long) case class LabelPointSubmission(panoX: Int, panoY: Int, canvasX: Int, canvasY: Int, heading: Float, pitch: Float, zoom: Int, lat: Option[Float], lng: Option[Float], computationMethod: Option[String]) case class LabelSubmission(gsvPanoramaId: String, auditTaskId: Int, labelType: String, deleted: Boolean, severity: Option[Int], temporary: Boolean, description: Option[String], tagIds: Seq[Int], point: LabelPointSubmission, temporaryLabelId:Int, timeCreated: Option[Long], tutorial: Boolean) @@ -16,7 +16,7 @@ object TaskSubmissionFormats { case class GSVLinkSubmission(targetGsvPanoramaId: String, yawDeg: Double, description: String) case class GSVPanoramaSubmission(gsvPanoramaId: String, captureDate: String, width: Option[Int], height: Option[Int], tileWidth: Option[Int], tileHeight: Option[Int], lat: Option[Float], lng: Option[Float], cameraHeading: Option[Float], cameraPitch: Option[Float], links: Seq[GSVLinkSubmission], copyright: String) case class AuditMissionProgress(missionId: Int, distanceProgress: Option[Float], completed: Boolean, auditTaskId: Option[Int], skipped: Boolean) - case class AuditTaskSubmission(missionProgress: AuditMissionProgress, auditTask: TaskSubmission, labels: Seq[LabelSubmission], interactions: Seq[InteractionSubmission], environment: EnvironmentSubmission, incomplete: Option[IncompleteTaskSubmission], gsvPanoramas: Seq[GSVPanoramaSubmission], amtAssignmentId: Option[Int], userRouteId: Option[Int]) + case class AuditTaskSubmission(missionProgress: AuditMissionProgress, auditTask: TaskSubmission, labels: Seq[LabelSubmission], interactions: Seq[InteractionSubmission], environment: EnvironmentSubmission, incomplete: Option[IncompleteTaskSubmission], gsvPanoramas: Seq[GSVPanoramaSubmission], amtAssignmentId: Option[Int], userRouteId: Option[Int], timestamp: Long) case class AMTAssignmentCompletionSubmission(assignmentId: Int, completed: Option[Boolean]) implicit val pointReads: Reads[Point] = ( @@ -40,7 +40,8 @@ object TaskSubmissionFormats { (JsPath \ "screen_width").readNullable[Int] and (JsPath \ "screen_height").readNullable[Int] and (JsPath \ "operating_system").readNullable[String] and - (JsPath \ "language").read[String] + (JsPath \ "language").read[String] and + (JsPath \ "css_zoom").read[Int] )(EnvironmentSubmission.apply _) implicit val interactionSubmissionReads: Reads[InteractionSubmission] = ( @@ -135,7 +136,8 @@ object TaskSubmissionFormats { (JsPath \ "incomplete").readNullable[IncompleteTaskSubmission] and (JsPath \ "gsv_panoramas").read[Seq[GSVPanoramaSubmission]] and (JsPath \ "amt_assignment_id").readNullable[Int] and - (JsPath \ "user_route_id").readNullable[Int] + (JsPath \ "user_route_id").readNullable[Int] and + (JsPath \ "timestamp").read[Long] )(AuditTaskSubmission.apply _) implicit val amtAssignmentCompletionReads: Reads[AMTAssignmentCompletionSubmission] = ( diff --git a/app/formats/json/ValidationTaskSubmissionFormats.scala b/app/formats/json/ValidationTaskSubmissionFormats.scala index 0a869c724c..82dbb2bcd7 100644 --- a/app/formats/json/ValidationTaskSubmissionFormats.scala +++ b/app/formats/json/ValidationTaskSubmissionFormats.scala @@ -7,12 +7,12 @@ import scala.collection.immutable.Seq import play.api.libs.functional.syntax._ object ValidationTaskSubmissionFormats { - case class EnvironmentSubmission(missionId: Option[Int], browser: Option[String], browserVersion: Option[String], browserWidth: Option[Int], browserHeight: Option[Int], availWidth: Option[Int], availHeight: Option[Int], screenWidth: Option[Int], screenHeight: Option[Int], operatingSystem: Option[String], language: String) + case class EnvironmentSubmission(missionId: Option[Int], browser: Option[String], browserVersion: Option[String], browserWidth: Option[Int], browserHeight: Option[Int], availWidth: Option[Int], availHeight: Option[Int], screenWidth: Option[Int], screenHeight: Option[Int], operatingSystem: Option[String], language: String, cssZoom: Int) case class InteractionSubmission(action: String, missionId: Option[Int], gsvPanoramaId: Option[String], lat: Option[Float], lng: Option[Float], heading: Option[Float], pitch: Option[Float], zoom: Option[Float], note: Option[String], timestamp: Long, isMobile: Boolean) case class LabelValidationSubmission(labelId: Int, missionId: Int, validationResult: Int, canvasX: Option[Int], canvasY: Option[Int], heading: Float, pitch: Float, zoom: Float, canvasHeight: Int, canvasWidth: Int, startTimestamp: Long, endTimestamp: Long, source: String) case class SkipLabelSubmission(labels: Seq[LabelValidationSubmission]) case class ValidationMissionProgress(missionId: Int, missionType: String, labelsProgress: Int, labelTypeId: Int, completed: Boolean, skipped: Boolean) - case class ValidationTaskSubmission(interactions: Seq[InteractionSubmission], environment: EnvironmentSubmission, labels: Seq[LabelValidationSubmission], missionProgress: Option[ValidationMissionProgress]) + case class ValidationTaskSubmission(interactions: Seq[InteractionSubmission], environment: EnvironmentSubmission, labels: Seq[LabelValidationSubmission], missionProgress: Option[ValidationMissionProgress], timestamp: Long) case class LabelMapValidationSubmission(labelId: Int, labelType: String, validationResult: Int, canvasX: Option[Int], canvasY: Option[Int], heading: Float, pitch: Float, zoom: Float, canvasHeight: Int, canvasWidth: Int, startTimestamp: Long, endTimestamp: Long, source: String) implicit val environmentSubmissionReads: Reads[EnvironmentSubmission] = ( @@ -26,7 +26,8 @@ object ValidationTaskSubmissionFormats { (JsPath \ "screen_width").readNullable[Int] and (JsPath \ "screen_height").readNullable[Int] and (JsPath \ "operating_system").readNullable[String] and - (JsPath \ "language").read[String] + (JsPath \ "language").read[String] and + (JsPath \ "css_zoom").read[Int] )(EnvironmentSubmission.apply _) implicit val interactionSubmissionReads: Reads[InteractionSubmission] = ( @@ -72,8 +73,9 @@ object ValidationTaskSubmissionFormats { (JsPath \ "interactions").read[Seq[InteractionSubmission]] and (JsPath \ "environment").read[EnvironmentSubmission] and (JsPath \ "labels").read[Seq[LabelValidationSubmission]] and - (JsPath \ "missionProgress").readNullable[ValidationMissionProgress] - )(ValidationTaskSubmission.apply _) // .map(ValidationTaskSubmission(_)) + (JsPath \ "missionProgress").readNullable[ValidationMissionProgress] and + (JsPath \ "timestamp").read[Long] + )(ValidationTaskSubmission.apply _) implicit val labelMapValidationSubmissionReads: Reads[LabelMapValidationSubmission] = ( (JsPath \ "label_id").read[Int] and diff --git a/app/models/audit/AuditTaskEnvironmentTable.scala b/app/models/audit/AuditTaskEnvironmentTable.scala index 078972626a..c9cbd91e4f 100644 --- a/app/models/audit/AuditTaskEnvironmentTable.scala +++ b/app/models/audit/AuditTaskEnvironmentTable.scala @@ -9,7 +9,7 @@ case class AuditTaskEnvironment(auditTaskEnvironmentId: Int, auditTaskId: Int, m browserVersion: Option[String], browserWidth: Option[Int], browserHeight: Option[Int], availWidth: Option[Int], availHeight: Option[Int], screenWidth: Option[Int], screenHeight: Option[Int], operatingSystem: Option[String], ipAddress: Option[String], - language: String) + language: String, cssZoom: Int, timestamp: Option[java.sql.Timestamp]) class AuditTaskEnvironmentTable(tag: Tag) extends Table[AuditTaskEnvironment](tag, "audit_task_environment") { def auditTaskEnvironmentId = column[Int]("audit_task_environment_id", O.PrimaryKey, O.AutoInc) @@ -26,9 +26,11 @@ class AuditTaskEnvironmentTable(tag: Tag) extends Table[AuditTaskEnvironment](ta def operatingSystem = column[Option[String]]("operating_system", O.Nullable) def ipAddress = column[Option[String]]("ip_address", O.Nullable) def language = column[String]("language", O.NotNull) + def cssZoom = column[Int]("css_zoom", O.NotNull) + def timestamp = column[Option[java.sql.Timestamp]]("timestamp", O.Nullable) def * = (auditTaskEnvironmentId, auditTaskId, missionId, browser, browserVersion, browserWidth, browserHeight, - availWidth, availHeight, screenWidth, screenHeight, operatingSystem, ipAddress, language) <> ((AuditTaskEnvironment.apply _).tupled, AuditTaskEnvironment.unapply) + availWidth, availHeight, screenWidth, screenHeight, operatingSystem, ipAddress, language, cssZoom, timestamp) <> ((AuditTaskEnvironment.apply _).tupled, AuditTaskEnvironment.unapply) def auditTask: ForeignKeyQuery[AuditTaskTable, AuditTask] = foreignKey("audit_task_environment_audit_task_id_fkey", auditTaskId, TableQuery[AuditTaskTable])(_.auditTaskId) diff --git a/app/models/validation/ValidationTaskEnvironmentTable.scala b/app/models/validation/ValidationTaskEnvironmentTable.scala index da5147d625..aa450931ed 100644 --- a/app/models/validation/ValidationTaskEnvironmentTable.scala +++ b/app/models/validation/ValidationTaskEnvironmentTable.scala @@ -9,7 +9,7 @@ case class ValidationTaskEnvironment(validationTaskEnvironmentId: Int, missionId browserVersion: Option[String], browserWidth: Option[Int], browserHeight: Option[Int], availWidth: Option[Int], availHeight: Option[Int], screenWidth: Option[Int], screenHeight: Option[Int], operatingSystem: Option[String], ipAddress: Option[String], - language: String) + language: String, cssZoom: Int, timestamp: Option[java.sql.Timestamp]) class ValidationTaskEnvironmentTable(tag: Tag) extends Table[ValidationTaskEnvironment](tag, "validation_task_environment") { def validationTaskEnvironmentId = column[Int]("validation_task_environment_id", O.PrimaryKey, O.AutoInc) @@ -25,9 +25,11 @@ class ValidationTaskEnvironmentTable(tag: Tag) extends Table[ValidationTaskEnvir def operatingSystem = column[Option[String]]("operating_system", O.Nullable) def ipAddress = column[Option[String]]("ip_address", O.Nullable) def language = column[String]("language", O.NotNull) + def cssZoom = column[Int]("css_zoom", O.NotNull) + def timestamp = column[Option[java.sql.Timestamp]]("timestamp", O.Nullable) def * = (validationTaskEnvironmentId, missionId, browser, browserVersion, browserWidth, browserHeight, availWidth, - availHeight, screenWidth, screenHeight, operatingSystem, ipAddress, language) <> ((ValidationTaskEnvironment.apply _).tupled, ValidationTaskEnvironment.unapply) + availHeight, screenWidth, screenHeight, operatingSystem, ipAddress, language, cssZoom, timestamp) <> ((ValidationTaskEnvironment.apply _).tupled, ValidationTaskEnvironment.unapply) def mission: ForeignKeyQuery[MissionTable, Mission] = foreignKey("validation_task_environment_mission_id_fkey", missionId, TableQuery[MissionTable])(_.missionId) diff --git a/app/views/main.scala.html b/app/views/main.scala.html index 720a117a67..5c5067cfbc 100644 --- a/app/views/main.scala.html +++ b/app/views/main.scala.html @@ -87,13 +87,17 @@ } @if(url.get == "/explore") { } - @if(url.get == "/explore" || url.get == "/signInMobile" || url.get == "/signUpMobile") { - - @footer() + + @if(url.get == "/explore" || url.get == "/validate" || url.get == "/signInMobile" || url.get == "/signUpMobile") { + @if(url.get == "/explore" || url.get == "/validate") { + } else { + + @footer() + } } else {
diff --git a/app/views/missionStartTutorial.scala.html b/app/views/missionStartTutorial.scala.html index 15738ac47c..d661cc80d0 100644 --- a/app/views/missionStartTutorial.scala.html +++ b/app/views/missionStartTutorial.scala.html @@ -115,7 +115,7 @@
-
+
diff --git a/conf/evolutions/default/210.sql b/conf/evolutions/default/210.sql new file mode 100644 index 0000000000..d12eebeaf6 --- /dev/null +++ b/conf/evolutions/default/210.sql @@ -0,0 +1,17 @@ +# --- !Ups +ALTER TABLE audit_task_environment + ADD COLUMN css_zoom INT NOT NULL DEFAULT 100, + ADD COLUMN timestamp TIMESTAMPTZ; + +ALTER TABLE validation_task_environment + ADD COLUMN css_zoom INT NOT NULL DEFAULT 100, + ADD COLUMN timestamp TIMESTAMPTZ; + +# --- !Downs +ALTER TABLE validation_task_environment + DROP COLUMN css_zoom, + DROP COLUMN timestamp; + +ALTER TABLE audit_task_environment + DROP COLUMN css_zoom, + DROP COLUMN timestamp; diff --git a/public/javascripts/SVLabel/css/svl.css b/public/javascripts/SVLabel/css/svl.css index d16916e806..a2d4595301 100644 --- a/public/javascripts/SVLabel/css/svl.css +++ b/public/javascripts/SVLabel/css/svl.css @@ -83,11 +83,15 @@ input[type="radio"] { text-decoration: underline; } +.tool-ui { + width: 1170px; /* Used to override bootstrap .container media queries. We want to keep a consistent width. */ +} + #svl-application-holder { background: rgba(255,255,255,1); margin: 0 auto; position: relative; - height: 640px; + height: 610px; width: 960px; } diff --git a/public/javascripts/SVLabel/src/SVLabel/Main.js b/public/javascripts/SVLabel/src/SVLabel/Main.js index 2dafc10223..a48c7b2f8e 100644 --- a/public/javascripts/SVLabel/src/SVLabel/Main.js +++ b/public/javascripts/SVLabel/src/SVLabel/Main.js @@ -288,7 +288,7 @@ function Main (params) { //hide any alerts svl.alert.hideAlert(); //hide footer - $("#mini-footer-audit").css("visibility", "hidden"); + svl.ui.footer.css("visibility", "hidden"); if (!onboardingHandAnimation) { onboardingHandAnimation = new HandAnimation(svl.rootDirectory, svl.ui.onboarding); @@ -362,11 +362,11 @@ function Main (params) { $(".visible").css({"visibility": "visible"}); if (mission.getProperty("missionType") === "auditOnboarding") { - $("#mini-footer-audit").css("visibility", "hidden"); + svl.ui.footer.css("visibility", "hidden"); startOnboarding(); } else { _calculateAndSetTasksMissionsOffset(); - $("#mini-footer-audit").css("visibility", "visible"); + svl.ui.footer.css("visibility", "visible"); // Initialize explore mission screens focused on a randomized label type, though users can switch between them. var currentNeighborhood = svl.neighborhoodContainer.getCurrentNeighborhood(); @@ -378,6 +378,13 @@ function Main (params) { startTheMission(mission, currentNeighborhood); } + + // Use CSS zoom to scale the UI for users with high resolution screens. + // Has only been tested on Chrome and Safari. Firefox doesn't support CSS zoom. + if (bowser.chrome || bowser.safari) { + svl.cssZoom = util.scaleUI(); + window.addEventListener('resize', (e) => { svl.cssZoom = util.scaleUI(); }); + } } } @@ -581,6 +588,8 @@ function Main (params) { svl.ui.areaComplete.overlay = $("#area-completion-overlay-wrapper"); svl.ui.areaComplete.title = $("#area-completion-title"); svl.ui.areaComplete.body = $("#area-completion-body"); + + svl.ui.footer = $("#mini-footer-audit"); } // Gets all the text on the explore page for the correct language. diff --git a/public/javascripts/SVLabel/src/SVLabel/data/Form.js b/public/javascripts/SVLabel/src/SVLabel/data/Form.js index 48b3d80ccc..b9ef474fcd 100644 --- a/public/javascripts/SVLabel/src/SVLabel/data/Form.js +++ b/public/javascripts/SVLabel/src/SVLabel/data/Form.js @@ -30,7 +30,7 @@ function Form (labelContainer, missionModel, missionContainer, navigationModel, * @returns {{}} */ this.compileSubmissionData = function (task) { - var data = {}; + var data = { timestamp: new Date().getTime() }; data.amt_assignment_id = svl.amtAssignmentId; data.user_route_id = svl.userRouteId; @@ -70,7 +70,8 @@ function Form (labelContainer, missionModel, missionContainer, navigationModel, avail_width: screen.availWidth, // total width - interface (taskbar) avail_height: screen.availHeight, // total height - interface }; operating_system: util.getOperatingSystem(), - language: i18next.language + language: i18next.language, + css_zoom: svl.cssZoom ? svl.cssZoom : 100 }; data.interactions = tracker.getActions(); diff --git a/public/javascripts/SVValidate/css/svv.css b/public/javascripts/SVValidate/css/svv.css index 9b0aec2439..0ad92db2fb 100644 --- a/public/javascripts/SVValidate/css/svv.css +++ b/public/javascripts/SVValidate/css/svv.css @@ -6,6 +6,10 @@ width: 960px; } +.tool-ui { + width: 1170px; /* Used to override bootstrap .container media queries. We want to keep a consistent width. */ +} + text { visibility: hidden; } diff --git a/public/javascripts/SVValidate/src/Main.js b/public/javascripts/SVValidate/src/Main.js index cf544f3180..2180c8e4e4 100644 --- a/public/javascripts/SVValidate/src/Main.js +++ b/public/javascripts/SVValidate/src/Main.js @@ -203,6 +203,13 @@ function Main (param) { const labelType = param.labelList[0].getAuditProperty('labelType'); const missionStartTutorial = new MissionStartTutorial('validate', labelType, { nLabels: param.mission.labels_validated }, svv, param.language); + + // Use CSS zoom to scale the UI for users with high resolution screens. + // Has only been tested on Chrome and Safari. Firefox doesn't support CSS zoom. + if (!isMobile() && (bowser.chrome || bowser.safari)) { + svv.cssZoom = util.scaleUI(); + window.addEventListener('resize', (e) => { svv.cssZoom = util.scaleUI(); }); + } } // Gets all the text on the validation page for the correct language. diff --git a/public/javascripts/SVValidate/src/data/Form.js b/public/javascripts/SVValidate/src/data/Form.js index 652ba8a83b..e8404b59cb 100644 --- a/public/javascripts/SVValidate/src/data/Form.js +++ b/public/javascripts/SVValidate/src/data/Form.js @@ -10,7 +10,7 @@ function Form(url, beaconUrl) { * @param {boolean} missionComplete Whether or not the mission is complete. To ensure we only send once per mission. */ function compileSubmissionData(missionComplete) { - let data = {}; + let data = { timestamp: new Date().getTime() }; let missionContainer = svv.missionContainer; let mission = missionContainer ? missionContainer.getCurrentMission() : null; @@ -49,7 +49,8 @@ function Form(url, beaconUrl) { avail_width: screen.availWidth, // total width - interface (taskbar) avail_height: screen.availHeight, // total height - interface }; operating_system: util.getOperatingSystem(), - language: i18next.language + language: i18next.language, + css_zoom: svv.cssZoom ? svv.cssZoom : 100 }; data.interactions = svv.tracker.getActions(); diff --git a/public/javascripts/SVValidate/src/panorama/GSVOverlay.js b/public/javascripts/SVValidate/src/panorama/GSVOverlay.js index bd16ec33a4..8c58bcbbbe 100644 --- a/public/javascripts/SVValidate/src/panorama/GSVOverlay.js +++ b/public/javascripts/SVValidate/src/panorama/GSVOverlay.js @@ -114,22 +114,6 @@ function GSVOverlay () { } } - // A cross-browser function to capture mouse positions. - function mouseposition (e, dom) { - let mx; - let my; - //if(e.offsetX) { - // Chrome - // mx = e.offsetX; - // my = e.offsetY; - //} else { - // Firefox, Safari - mx = e.pageX - $(dom).offset().left; - my = e.pageY - $(dom).offset().top; - //} - return {'x': parseInt(mx, 10) , 'y': parseInt(my, 10) }; - } - viewControlLayer.bind('mousemove', handlerViewControlLayerMouseMove); viewControlLayer.bind('mousedown', handlerViewControlLayerMouseDown); viewControlLayer.bind('mouseup', handlerViewControlLayerMouseUp); diff --git a/public/javascripts/common/GSVInfoPopover.js b/public/javascripts/common/GSVInfoPopover.js index 4bdf1babca..586ef28cb3 100644 --- a/public/javascripts/common/GSVInfoPopover.js +++ b/public/javascripts/common/GSVInfoPopover.js @@ -146,6 +146,10 @@ function GSVInfoPopover (container, panorama, coords, panoId, streetEdgeId, regi let xpos = infoRect.x + (infoRect.width / 2) - (infoPopover.width() / 2); infoPopover.css('left', `${xpos}px`); + // Set the popover zoom to the same zoom as the Explore/Validate page. + if (typeof svl !== 'undefined' && svl.cssZoom) infoPopover.css('zoom', `${svl.cssZoom}%`); + else if (typeof svv !== 'undefined' && svv.cssZoom) infoPopover.css('zoom', `${svv.cssZoom}%`); + // Copy to clipboard. $('#clipboard').on('click', function(e) { // Log the click on the copy to keyboard button. diff --git a/public/javascripts/common/Utilities.js b/public/javascripts/common/Utilities.js index e3c2673ae0..f23c3e0584 100644 --- a/public/javascripts/common/Utilities.js +++ b/public/javascripts/common/Utilities.js @@ -6,9 +6,15 @@ util.EXPLORE_CANVAS_HEIGHT = 480; // A cross-browser function to capture a mouse position. function mouseposition(e, dom) { - var mx, my; - mx = e.pageX - $(dom).offset().left; - my = e.pageY - $(dom).offset().top; + var mx, my, zoomFactor; + var toolUIElem = dom.closest('.tool-ui') + if (toolUIElem && toolUIElem.style.zoom) { + zoomFactor = parseFloat(toolUIElem.style.zoom) / 100.0 || 1; + } else { + zoomFactor = 1; + } + mx = (e.pageX / zoomFactor) - $(dom).offset().left; + my = (e.pageY / zoomFactor) - $(dom).offset().top; return {'x': parseInt(mx, 10) , 'y': parseInt(my, 10) }; } util.mouseposition = mouseposition; @@ -155,3 +161,66 @@ function camelToKebab(theString) { return theString.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); } util.camelToKebab = camelToKebab; + +/** + * Scales the UI on the Explore or Validate pages using CSS zoom. This is necessary because the UI is not responsive. + * + * This should only be called from the Explore or Validate pages at this time. We can always make this function more + * generic in the future. + * @returns {number} + */ +function scaleUI() { + var toolCSSZoom = 100; + if (!bowser.chrome && !bowser.safari) return toolCSSZoom; // Only tested for Chrome/Safari so far. + + var toolUI = document.querySelector('.tool-ui'); + var mst = document.querySelector('.mst-content'); + var zoomPercent = 50; + + // Start with the tool-ui at 50% zoom and find the maximum zoom level that is still visible. + if (!!toolUI.offsetParent) { + zoomPercent = _findMaxZoomLevel(toolUI, zoomPercent); + toolCSSZoom = zoomPercent; + } + + // If the Mission Start Tutorial is visible, scale it as well. + if (!!mst.offsetParent) { + document.querySelector('.mission-start-tutorial-overlay').style.height = 'calc(100% - 70px)'; + if (zoomPercent > 50) zoomPercent -= 20; // Should be similar as tool-ui, don't need to start at 50%. + zoomPercent = _findMaxZoomLevel(mst, zoomPercent); + } + + return toolCSSZoom; +} +util.scaleUI = scaleUI; + +// Returns true if the element is fully visible, false otherwise. Takes into account CSS zoom (tested on chrome/safari). +function _isVisible(elem) { + var zoomFactor = parseFloat(elem.style.zoom) / 100.0 || 1; + var scaledRect = elem.getBoundingClientRect(); + if (zoomFactor !== 1) { + scaledRect = { + left: scaledRect.left * zoomFactor, + bottom: scaledRect.bottom * zoomFactor, + right: scaledRect.right * zoomFactor + }; + } + return scaledRect.left >= 0 && + scaledRect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && + scaledRect.right <= (window.innerWidth || document.documentElement.clientWidth); +} + +// Finds the maximum CSS zoom level for an element (tested on chrome/safari). +function _findMaxZoomLevel(elem, startZoom) { + var zoomPercent = startZoom; + elem.style.zoom = zoomPercent + '%'; + while (_isVisible(elem) && zoomPercent < 500) { + zoomPercent += 10; + elem.style.zoom = zoomPercent + '%'; + } + while (!_isVisible(elem) && zoomPercent > 10) { + zoomPercent -= 1; + elem.style.zoom = zoomPercent + '%'; + } + return zoomPercent; +} diff --git a/public/stylesheets/common/missionStartTutorial.css b/public/stylesheets/common/missionStartTutorial.css index 60c8dbf988..12988379b4 100644 --- a/public/stylesheets/common/missionStartTutorial.css +++ b/public/stylesheets/common/missionStartTutorial.css @@ -22,12 +22,11 @@ .mission-start-tutorial-overlay { position: absolute; - height: 683px; + height: 683px; /* We override this when using CSS zoom. */ width: 100%; - min-width: 1170px; /* */ background: white; z-index: 10; - display: none; /* will be later set to flex by JS after rendering */ + display: none; /* Will be later set to flex by JS after rendering. */ flex-shrink: 0; justify-content: flex-start; overflow: auto; @@ -107,7 +106,7 @@ justify-content: space-between; } -.mts-text-area { +.mst-text-area { width: 290px; display: flex; flex-shrink: 0; @@ -350,21 +349,6 @@ background: #2C2A3D; } -/* This block handles layout on browser zoom. */ -@media (max-width: 1170px) { - .mission-start-tutorial-overlay { - min-width: 1069px; - } - .mst-content { - width: 1020px; - } - - /* We need to create space for the image at this zoom level. */ - .mts-text-area { - width: 230px; - } -} - /* Explore mission screen tab bar styling */ .explore-mission-start-tab-bar {