From 74b03ff47f779c0c603851c1bd2eb7559499253b Mon Sep 17 00:00:00 2001 From: hpinkos Date: Tue, 23 Jul 2013 17:30:46 -0400 Subject: [PATCH 01/25] balloon WIP --- Source/DynamicScene/DynamicObject.js | 7 ++ Source/Widgets/Balloon/Balloon.css | 51 ++++++++++++ Source/Widgets/Balloon/Balloon.js | 87 ++++++++++++++++++++ Source/Widgets/Balloon/BalloonViewModel.js | 95 ++++++++++++++++++++++ Source/Widgets/Viewer/Viewer.css | 1 + Source/Widgets/Viewer/Viewer.js | 18 ++++ 6 files changed, 259 insertions(+) create mode 100644 Source/Widgets/Balloon/Balloon.css create mode 100644 Source/Widgets/Balloon/Balloon.js create mode 100644 Source/Widgets/Balloon/BalloonViewModel.js diff --git a/Source/DynamicScene/DynamicObject.js b/Source/DynamicScene/DynamicObject.js index 7c7fca4a3c86..8d22e9370c1b 100644 --- a/Source/DynamicScene/DynamicObject.js +++ b/Source/DynamicScene/DynamicObject.js @@ -179,6 +179,13 @@ define([ * @default undefined */ this.viewFrom = undefined; + + /** + * Gets or sets the HTML content of a balloon that will be visible upon clicking the object. + * @type {String} + * @default undefined + */ + this.balloon = undefined; }; /** diff --git a/Source/Widgets/Balloon/Balloon.css b/Source/Widgets/Balloon/Balloon.css new file mode 100644 index 000000000000..95717be3a732 --- /dev/null +++ b/Source/Widgets/Balloon/Balloon.css @@ -0,0 +1,51 @@ +.cesium-balloon-wrapper { + position: absolute; +} + +.cesium-balloon { + z-index: 2; +} + +.cesium-balloon-content { + position: relative; + background-color: #fff; + border-radius: 3px; + padding: 5px; + z-index: 1; +} + +.cesium-balloon-point { + background-color: #fff; + height: 20px; + width: 20px; + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); + padding: 1px; + margin: -15px auto 0; + position: relative; + bottom: 0; +} + +.cesium-balloon-wrapper-visible { + visibility: visible; + opacity: 1; + transition: opacity 0.25s linear; + -webkit-transition: opacity 0.25s linear; + -moz-transition: opacity 0.25s linear; +} + +.cesium-balloon-wrapper-hidden { + visibility: hidden; + opacity: 0; + transition: visibility 0s 0.25s, opacity 0.25s linear; + -webkit-transition: visibility 0s 0.25s, opacity 0.25s linear; + -moz-transition: visibility 0s 0.25s, opacity 0.25s linear; +} + +.ceisum-balloon-close { + color: #f00; + text-align: right; +} diff --git a/Source/Widgets/Balloon/Balloon.js b/Source/Widgets/Balloon/Balloon.js new file mode 100644 index 000000000000..e72520a6908e --- /dev/null +++ b/Source/Widgets/Balloon/Balloon.js @@ -0,0 +1,87 @@ +/*global define*/ +define([ + '../../Core/defineProperties', + '../../Core/DeveloperError', + '../../Core/destroyObject', + '../getElement', + './BalloonViewModel', + '../../ThirdParty/knockout' + ], function( + defineProperties, + DeveloperError, + destroyObject, + getElement, + BalloonViewModel, + knockout) { + "use strict"; + + var Balloon = function(container, balloonElement) { + if (typeof container === 'undefined') { + throw new DeveloperError('container is required.'); + } + + container = getElement(container); + + this._container = container; + var el = document.createElement('div'); + this._element = el; + el.className = 'cesium-balloon-wrapper'; + container.appendChild(el); + el.setAttribute('data-bind', 'css: { "cesium-balloon-wrapper-visible" : balloonVisible, "cesium-balloon-wrapper-hidden" : !balloonVisible }, style: { "top" : positionY, "left" : positionX}'); + + var contentWrapper = document.createElement('div'); + contentWrapper.className = 'cesium-balloon-content'; + el.appendChild(contentWrapper); + var ex = document.createElement('div'); + var exA = document.createElement('a'); + exA.href = '#'; + exA.textContent = '[x]'; + ex.appendChild(exA); + ex.className = 'ceisum-balloon-close'; + ex.setAttribute('data-bind', 'click: showBalloon'); + contentWrapper.appendChild(ex); + el.appendChild(contentWrapper); + + this._content = document.createElement('div'); + var balloon = document.createElement('div'); + balloon.className = 'cesium-balloon'; + balloon.appendChild(this._content); + contentWrapper.appendChild(balloon); + var point = document.createElement('div'); + point.className = 'cesium-balloon-point'; + el.appendChild(point); + + + this._viewModel = new BalloonViewModel(balloonElement, this._content, this._element); + + knockout.applyBindings(this._viewModel, this._element); + }; + + defineProperties(Balloon.prototype, { + container : { + get : function() { + return this._container; + } + }, + + viewModel : { + get : function() { + return this._viewModel; + } + } + }); + + Balloon.prototype.isDestroyed = function() { + return false; + }; + + Balloon.prototype.destroy = function() { + var container = this._container; + knockout.cleanNode(container); + this._viewModel.destroy(); + container.removeChild(this._element); + return destroyObject(this); + }; + + return Balloon; +}); \ No newline at end of file diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js new file mode 100644 index 000000000000..e3423a08d54a --- /dev/null +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -0,0 +1,95 @@ +/*global define*/ +define([ + '../../Core/Cartesian2', + '../../Core/defaultValue', + '../../Core/defineProperties', + '../../Core/destroyObject', + '../../Core/DeveloperError', + '../createCommand', + '../../ThirdParty/knockout' + ], function( + Cartesian2, + defaultValue, + defineProperties, + destroyObject, + DeveloperError, + createCommand, + knockout) { + "use strict"; + + var BalloonViewModel = function(balloonElement, contentElement, balloonDiv) { + var that = this; + this._balloonElement = defaultValue(balloonElement, document.body); + this._balloonDiv = balloonDiv; + this._contentElement = contentElement; + this._callback = function() {}; + document.addEventListener(function(){}, this._callback); + + this.balloonVisible = false; + this.positionX = '0'; + this.positionY = '0'; + knockout.track(this, ['balloonVisible', 'positionX', 'positionY']); + + this._showBalloon = createCommand(function() { + that.balloonVisible = true; + }); + + this._hideBalloon = createCommand(function() { + that.balloonVisible = false; + }); + + }; + + defineProperties(BalloonViewModel.prototype, { + content : { + get : function() { + return this._contentElement.innerHTML; + }, + set : function(value) { + if (typeof value !== 'string') { + throw new DeveloperError('value must be a string'); + } + this._contentElement.innerHTML = value; + this.balloonVisible = true; + } + }, + + showBalloon: { + get: function() { + return this._showBalloon; + } + }, + + hideBalloon: { + get: function() { + return this._hideBalloon; + } + }, + + screenPosition: { + get : function() { + return this.position; + }, + set: function(value) { + var that = this; + setTimeout(function () { + var height = that._balloonDiv.offsetHeight; + var width = that._balloonDiv.offsetWidth / 2; + that.positionX = Math.max((value.x - width), 0) + 'px'; + that.positionY = Math.max((value.y - height - 20), 0) + 'px'; + }, 25); + } + } + }); + + BalloonViewModel.prototype.isDestroyed = function() { + return false; + }; + + BalloonViewModel.prototype.destroy = function() { + document.removeEventListener(function(){}, this._callback); + destroyObject(this); + }; + + return BalloonViewModel; +}); \ No newline at end of file diff --git a/Source/Widgets/Viewer/Viewer.css b/Source/Widgets/Viewer/Viewer.css index 1b8d2f9b812b..3fdd25eb4e5f 100644 --- a/Source/Widgets/Viewer/Viewer.css +++ b/Source/Widgets/Viewer/Viewer.css @@ -1,5 +1,6 @@ @import url(../Animation/Animation.css); @import url(../BaseLayerPicker/BaseLayerPicker.css); +@import url(../Balloon/Balloon.css); @import url(../CesiumWidget/CesiumWidget.css); @import url(../FullscreenButton/FullscreenButton.css); @import url(../HomeButton/HomeButton.css); diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js index 2b28c24abcb6..7e31ecc21942 100644 --- a/Source/Widgets/Viewer/Viewer.js +++ b/Source/Widgets/Viewer/Viewer.js @@ -12,6 +12,7 @@ define([ '../../DynamicScene/DataSourceDisplay', '../Animation/Animation', '../Animation/AnimationViewModel', + '../Balloon/Balloon', '../BaseLayerPicker/BaseLayerPicker', '../BaseLayerPicker/createDefaultBaseLayers', '../CesiumWidget/CesiumWidget', @@ -35,6 +36,7 @@ define([ DataSourceDisplay, Animation, AnimationViewModel, + Balloon, BaseLayerPicker, createDefaultBaseLayers, CesiumWidget, @@ -288,11 +290,21 @@ Either specify options.imageryProvider instead or set options.baseLayerPicker to timeline.container.style.right = 0; } + //Balloon + var balloon; + if (typeof options.balloon === 'undefined' || options.balloon !== false) { + var balloonContainer = document.createElement('div'); + balloonContainer.className = 'cesium-viewer-balloonContainer'; + viewerContainer.appendChild(balloonContainer); + balloon = new Balloon(balloonContainer, defaultValue(options.balloonElement, container)); + } + this._container = container; this._viewerContainer = viewerContainer; this._cesiumWidget = cesiumWidget; this._toolbar = toolbar; this._homeButton = homeButton; + this._balloon = balloon; this._sceneModePicker = sceneModePicker; this._baseLayerPicker = baseLayerPicker; this._animation = animation; @@ -375,6 +387,12 @@ Either specify options.imageryProvider instead or set options.baseLayerPicker to } }, + balloon : { + get : function() { + return this._balloon; + } + }, + /** * Gets the Timeline widget. * @memberof Viewer.prototype From c35a161fa656bfc511757326e6fef5f519697e6e Mon Sep 17 00:00:00 2001 From: hpinkos Date: Wed, 24 Jul 2013 17:56:55 -0400 Subject: [PATCH 02/25] balloon work continued --- Source/Scene/Scene.js | 21 +++++ Source/Widgets/Balloon/Balloon.js | 97 +++++++++++++++++++++- Source/Widgets/Balloon/BalloonViewModel.js | 85 ++++++++++--------- Source/Widgets/Viewer/Viewer.css | 5 ++ Source/Widgets/Viewer/Viewer.js | 5 +- 5 files changed, 171 insertions(+), 42 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 6345d584de58..29dd607069de 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -15,6 +15,7 @@ define([ '../Core/Interval', '../Core/Matrix4', '../Core/JulianDate', + '../Core/Transforms', '../Renderer/Context', '../Renderer/ClearCommand', '../Renderer/PassState', @@ -46,6 +47,7 @@ define([ Interval, Matrix4, JulianDate, + Transforms, Context, ClearCommand, PassState, @@ -690,6 +692,25 @@ define([ return this._pickFramebuffer.end(scratchRectangle); }; + /** + * Converts a cartesian position to a window position. + * + * @param {Cartesian3} position The cartesian position + * + * @returns {Cartesain2} The screen coordinates of the given position; + */ + Scene.prototype.computeWindowPosition = function(position) { + if (typeof position === 'undefined') { + return undefined; + } + + var uniformState = this._context.getUniformState(); + var modelViewProjectionMatrix = uniformState.getModelViewProjection(); + var viewportTransformation = uniformState.getViewportTransformation(); + var point = Transforms.pointToWindowCoordinates(modelViewProjectionMatrix, viewportTransformation, position); + return point; + }; + /** * DOC_TBA * @memberof Scene diff --git a/Source/Widgets/Balloon/Balloon.js b/Source/Widgets/Balloon/Balloon.js index e72520a6908e..f3a0aac3cf86 100644 --- a/Source/Widgets/Balloon/Balloon.js +++ b/Source/Widgets/Balloon/Balloon.js @@ -1,21 +1,87 @@ /*global define*/ define([ + '../../Core/Cartesian3', '../../Core/defineProperties', '../../Core/DeveloperError', '../../Core/destroyObject', + '../../Core/ScreenSpaceEventHandler', + '../../Core/ScreenSpaceEventType', '../getElement', './BalloonViewModel', '../../ThirdParty/knockout' ], function( + Cartesian3, defineProperties, DeveloperError, destroyObject, + ScreenSpaceEventHandler, + ScreenSpaceEventType, getElement, BalloonViewModel, knockout) { "use strict"; + var position; + var pickedObject; - var Balloon = function(container, balloonElement) { + function updatePosition(widget, screenPosition) { + var containerWidth = widget.container.clientWidth; + var containerHeight = widget.container.clientHeight; + + var width = widget._element.offsetWidth; + var height = widget._element.offsetHeight; + screenPosition.x = Math.min(Math.max((screenPosition.x - width/2), 0), containerWidth - width); + screenPosition.y = Math.min(Math.max((screenPosition.y + 20), 0), containerHeight - height); + + return screenPosition; + } + + function mouseOrTouch(widget, e) { + var scene = widget._scene; + var viewModel = widget.viewModel; + var dragging = viewModel.dragging; + + var clientX; + var clientY; + if (e.type === 'touchstart' || e.type === 'touchmove') { + clientX = e.touches[0].clientX; + clientY = e.touches[0].clientY; + } else { + clientX = e.clientX; + clientY = e.clientY; + } + + if (e.type === 'mouseup') { + var newPickedObject = scene.pick({x: clientX, y: clientY}); + if (typeof newPickedObject !== 'undefined' && typeof newPickedObject.balloon !== 'undefined') { + position = newPickedObject.getPosition(); + if (typeof position !== 'undefined') { + if (pickedObject === newPickedObject) { + viewModel.screenPosition = updatePosition(widget, scene.computeWindowPosition(position)); + } else { + viewModel.content = newPickedObject.balloon; + viewModel.screenPosition = updatePosition(widget, scene.computeWindowPosition(position)); + pickedObject = newPickedObject; + } + + } + } + viewModel.dragging = false; + } else if (e.type === 'mousedown' || (dragging && e.type === 'mousemove') || + (e.type === 'touchstart' && e.touches.length === 1) || + (dragging && e.type === 'touchmove' && e.touches.length === 1)) { + + if ((e.type === 'mousedown') || (e.type === 'touchstart')) { + viewModel.dragging = true; + } else if (dragging && (e.type === 'touchmove' || e.type === 'mousemove') && + viewModel.balloonVisible && typeof position !== 'undefined') { + viewModel.screenPosition = updatePosition(widget, scene.computeWindowPosition(position)); + } else { + viewModel.dragging = false; + } + } + } + + var Balloon = function(container, scene) { if (typeof container === 'undefined') { throw new DeveloperError('container is required.'); } @@ -23,11 +89,14 @@ define([ container = getElement(container); this._container = container; + this._scene = scene; var el = document.createElement('div'); this._element = el; el.className = 'cesium-balloon-wrapper'; container.appendChild(el); - el.setAttribute('data-bind', 'css: { "cesium-balloon-wrapper-visible" : balloonVisible, "cesium-balloon-wrapper-hidden" : !balloonVisible }, style: { "top" : positionY, "left" : positionX}'); + el.setAttribute('data-bind', + 'css: { "cesium-balloon-wrapper-visible" : _balloonVisible, "cesium-balloon-wrapper-hidden" : !_balloonVisible },\ + style: { "bottom" : _positionY, "left" : _positionX}'); var contentWrapper = document.createElement('div'); contentWrapper.className = 'cesium-balloon-content'; @@ -38,7 +107,7 @@ define([ exA.textContent = '[x]'; ex.appendChild(exA); ex.className = 'ceisum-balloon-close'; - ex.setAttribute('data-bind', 'click: showBalloon'); + ex.setAttribute('data-bind', 'click: function(){balloonVisible = false;}'); contentWrapper.appendChild(ex); el.appendChild(contentWrapper); @@ -51,8 +120,20 @@ define([ point.className = 'cesium-balloon-point'; el.appendChild(point); + var viewModel = new BalloonViewModel(scene, container, this._content, this._element); + this._viewModel = viewModel; - this._viewModel = new BalloonViewModel(balloonElement, this._content, this._element); + var that = this; + var mouseCallback = function(e) { + mouseOrTouch(that, e); + }; + + document.addEventListener('mousedown', mouseCallback, true); + document.addEventListener('touchstart', mouseCallback, true); + document.addEventListener('mousemove', mouseCallback, true); + document.addEventListener('touchmove', mouseCallback, true); + document.addEventListener('mouseup', mouseCallback, true); + document.addEventListener('touchend', mouseCallback, true); knockout.applyBindings(this._viewModel, this._element); }; @@ -83,5 +164,13 @@ define([ return destroyObject(this); }; + Balloon.prototype.render = function() { + var viewModel = this.viewModel; + if (typeof position !== 'undefined') { + viewModel.screenPosition = updatePosition(this, this._scene.computeWindowPosition(position)); + viewModel.render(); + } + }; + return Balloon; }); \ No newline at end of file diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index e3423a08d54a..b0169b876eeb 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -17,67 +17,78 @@ define([ knockout) { "use strict"; - var BalloonViewModel = function(balloonElement, contentElement, balloonDiv) { - var that = this; - this._balloonElement = defaultValue(balloonElement, document.body); - this._balloonDiv = balloonDiv; + var BalloonViewModel = function(scene, container, contentElement, balloonElement) { + this._scene = scene; + this._balloonElement = balloonElement; this._contentElement = contentElement; + this._con = contentElement.innerHTML; + this._position = new Cartesian2(); this._callback = function() {}; document.addEventListener(function(){}, this._callback); - this.balloonVisible = false; - this.positionX = '0'; - this.positionY = '0'; - knockout.track(this, ['balloonVisible', 'positionX', 'positionY']); + this.dragging = false; - this._showBalloon = createCommand(function() { - that.balloonVisible = true; - }); + this._balloonVisible = false; + this._positionX = '0'; + this._positionY = '0'; + this._updateContent = false; + this._updatePosition = false; + knockout.track(this, ['_balloonVisible', '_positionX', '_positionY']); - this._hideBalloon = createCommand(function() { - that.balloonVisible = false; - }); + var that = this; + this._render = createCommand(function() { //TODO: streamline so positions don't run over fade + if (that._updateContent) { + that._balloonVisible = false; + setTimeout(function () { + that._contentElement.innerHTML = that._content; + that._positionX = that._position.x + 'px'; + that._positionY = that._position.y + 'px'; + that._balloonVisible = true; + that.balloonVisible = true; + }, 250); + that._updatePosition = false; + that._updateContent = false; + } else if (that._updatePosition) { + that._balloonVisible = that.balloonVisible; + that._positionX = that._position.x + 'px'; + that._positionY = that._position.y + 'px'; + that._updatePosition = false; + } + }); }; defineProperties(BalloonViewModel.prototype, { + render: { + get: function() { + return this._render; + } + }, + content : { get : function() { - return this._contentElement.innerHTML; + return this._content; }, set : function(value) { if (typeof value !== 'string') { throw new DeveloperError('value must be a string'); } - this._contentElement.innerHTML = value; - this.balloonVisible = true; - } - }, - - showBalloon: { - get: function() { - return this._showBalloon; - } - }, - - hideBalloon: { - get: function() { - return this._hideBalloon; + if (value !== this._content) { + this._content = value; + this._updateContent = true; + } } }, screenPosition: { get : function() { - return this.position; + return this._position; }, set: function(value) { - var that = this; - setTimeout(function () { - var height = that._balloonDiv.offsetHeight; - var width = that._balloonDiv.offsetWidth / 2; - that.positionX = Math.max((value.x - width), 0) + 'px'; - that.positionY = Math.max((value.y - height - 20), 0) + 'px'; - }, 25); + if (!Cartesian2.equals(this._position, value)) { + this._position = Cartesian2.clone(value, this._position); + this._updatePosition = true; + } } } }); diff --git a/Source/Widgets/Viewer/Viewer.css b/Source/Widgets/Viewer/Viewer.css index 3fdd25eb4e5f..f5b682b0d776 100644 --- a/Source/Widgets/Viewer/Viewer.css +++ b/Source/Widgets/Viewer/Viewer.css @@ -21,6 +21,11 @@ -webkit-user-select: none; } +.cesium-viewer-balloonContainer { + width: 100%; + height: 100%; +} + .cesium-viewer-cesiumWidgetContainer { width: 100%; height: 100%; diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js index 7e31ecc21942..461cc8574e88 100644 --- a/Source/Widgets/Viewer/Viewer.js +++ b/Source/Widgets/Viewer/Viewer.js @@ -296,7 +296,7 @@ Either specify options.imageryProvider instead or set options.baseLayerPicker to var balloonContainer = document.createElement('div'); balloonContainer.className = 'cesium-viewer-balloonContainer'; viewerContainer.appendChild(balloonContainer); - balloon = new Balloon(balloonContainer, defaultValue(options.balloonElement, container)); + balloon = new Balloon(balloonContainer, cesiumWidget.scene); } this._container = container; @@ -661,6 +661,9 @@ Either specify options.imageryProvider instead or set options.baseLayerPicker to */ Viewer.prototype.render = function() { this._cesiumWidget.render(); + if (typeof this._balloon !== 'undefined') { + this._balloon.render(); + } }; /** From b3ef252a7b912dbae68c9e46317220e183948c15 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Thu, 25 Jul 2013 13:54:29 -0400 Subject: [PATCH 03/25] change position to function --- Source/Scene/Scene.js | 5 +- Source/Widgets/Balloon/Balloon.css | 12 +-- Source/Widgets/Balloon/Balloon.js | 79 ++++----------- Source/Widgets/Balloon/BalloonViewModel.js | 108 +++++++++++---------- 4 files changed, 85 insertions(+), 119 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 29dd607069de..6a557cc7b664 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -699,7 +699,7 @@ define([ * * @returns {Cartesain2} The screen coordinates of the given position; */ - Scene.prototype.computeWindowPosition = function(position) { + Scene.prototype.computeWindowPosition = function(position, result) { if (typeof position === 'undefined') { return undefined; } @@ -707,7 +707,8 @@ define([ var uniformState = this._context.getUniformState(); var modelViewProjectionMatrix = uniformState.getModelViewProjection(); var viewportTransformation = uniformState.getViewportTransformation(); - var point = Transforms.pointToWindowCoordinates(modelViewProjectionMatrix, viewportTransformation, position); + var point = Transforms.pointToWindowCoordinates(modelViewProjectionMatrix, viewportTransformation, position, result); + return point; }; diff --git a/Source/Widgets/Balloon/Balloon.css b/Source/Widgets/Balloon/Balloon.css index 95717be3a732..82ef6de0045f 100644 --- a/Source/Widgets/Balloon/Balloon.css +++ b/Source/Widgets/Balloon/Balloon.css @@ -32,17 +32,17 @@ .cesium-balloon-wrapper-visible { visibility: visible; opacity: 1; - transition: opacity 0.25s linear; - -webkit-transition: opacity 0.25s linear; - -moz-transition: opacity 0.25s linear; + transition: opacity 0.1s linear; + -webkit-transition: opacity 0.1s linear; + -moz-transition: opacity 0.1s linear; } .cesium-balloon-wrapper-hidden { visibility: hidden; opacity: 0; - transition: visibility 0s 0.25s, opacity 0.25s linear; - -webkit-transition: visibility 0s 0.25s, opacity 0.25s linear; - -moz-transition: visibility 0s 0.25s, opacity 0.25s linear; + transition: visibility 0s 0.1s, opacity 0.1s linear; + -webkit-transition: visibility 0s 0.1s, opacity 0.1s linear; + -moz-transition: visibility 0s 0.1s, opacity 0.1s linear; } .ceisum-balloon-close { diff --git a/Source/Widgets/Balloon/Balloon.js b/Source/Widgets/Balloon/Balloon.js index f3a0aac3cf86..285db5c809d4 100644 --- a/Source/Widgets/Balloon/Balloon.js +++ b/Source/Widgets/Balloon/Balloon.js @@ -1,5 +1,6 @@ /*global define*/ define([ + '../../Core/Cartesian2', '../../Core/Cartesian3', '../../Core/defineProperties', '../../Core/DeveloperError', @@ -10,6 +11,7 @@ define([ './BalloonViewModel', '../../ThirdParty/knockout' ], function( + Cartesian2, Cartesian3, defineProperties, DeveloperError, @@ -20,29 +22,15 @@ define([ BalloonViewModel, knockout) { "use strict"; - var position; - var pickedObject; + var screenPosition = new Cartesian2(); - function updatePosition(widget, screenPosition) { - var containerWidth = widget.container.clientWidth; - var containerHeight = widget.container.clientHeight; - - var width = widget._element.offsetWidth; - var height = widget._element.offsetHeight; - screenPosition.x = Math.min(Math.max((screenPosition.x - width/2), 0), containerWidth - width); - screenPosition.y = Math.min(Math.max((screenPosition.y + 20), 0), containerHeight - height); - - return screenPosition; - } - - function mouseOrTouch(widget, e) { + function clickOrTouch(widget, e) { var scene = widget._scene; var viewModel = widget.viewModel; - var dragging = viewModel.dragging; var clientX; var clientY; - if (e.type === 'touchstart' || e.type === 'touchmove') { + if (e.type === 'touchend') { clientX = e.touches[0].clientX; clientY = e.touches[0].clientY; } else { @@ -50,34 +38,15 @@ define([ clientY = e.clientY; } - if (e.type === 'mouseup') { - var newPickedObject = scene.pick({x: clientX, y: clientY}); - if (typeof newPickedObject !== 'undefined' && typeof newPickedObject.balloon !== 'undefined') { - position = newPickedObject.getPosition(); - if (typeof position !== 'undefined') { - if (pickedObject === newPickedObject) { - viewModel.screenPosition = updatePosition(widget, scene.computeWindowPosition(position)); - } else { - viewModel.content = newPickedObject.balloon; - viewModel.screenPosition = updatePosition(widget, scene.computeWindowPosition(position)); - pickedObject = newPickedObject; - } - - } - } - viewModel.dragging = false; - } else if (e.type === 'mousedown' || (dragging && e.type === 'mousemove') || - (e.type === 'touchstart' && e.touches.length === 1) || - (dragging && e.type === 'touchmove' && e.touches.length === 1)) { - - if ((e.type === 'mousedown') || (e.type === 'touchstart')) { - viewModel.dragging = true; - } else if (dragging && (e.type === 'touchmove' || e.type === 'mousemove') && - viewModel.balloonVisible && typeof position !== 'undefined') { - viewModel.screenPosition = updatePosition(widget, scene.computeWindowPosition(position)); - } else { - viewModel.dragging = false; + var pickedObject = scene.pick({x: clientX, y: clientY}); + if (typeof pickedObject !== 'undefined' && typeof pickedObject.balloon !== 'undefined') { + if (typeof pickedObject.computeScreenSpacePosition === 'function') { + viewModel.computeScreenPosition = function() { return pickedObject.computeScreenSpacePosition(scene.getContext(), scene.getFrameState()); }; + } else if (typeof pickedObject.getPosition === 'function') { + var position = pickedObject.getPosition(); + viewModel.computeScreenPosition = function() { return scene.computeWindowPosition( position, screenPosition); }; } + viewModel.content = pickedObject.balloon; } } @@ -89,13 +58,13 @@ define([ container = getElement(container); this._container = container; - this._scene = scene; var el = document.createElement('div'); this._element = el; + this._scene = scene; el.className = 'cesium-balloon-wrapper'; container.appendChild(el); el.setAttribute('data-bind', - 'css: { "cesium-balloon-wrapper-visible" : _balloonVisible, "cesium-balloon-wrapper-hidden" : !_balloonVisible },\ + 'css: { "cesium-balloon-wrapper-visible" : balloonVisible, "cesium-balloon-wrapper-hidden" : !balloonVisible },\ style: { "bottom" : _positionY, "left" : _positionX}'); var contentWrapper = document.createElement('div'); @@ -120,20 +89,16 @@ define([ point.className = 'cesium-balloon-point'; el.appendChild(point); - var viewModel = new BalloonViewModel(scene, container, this._content, this._element); + var viewModel = new BalloonViewModel(scene, this._content, this._element, this._container); this._viewModel = viewModel; var that = this; var mouseCallback = function(e) { - mouseOrTouch(that, e); + clickOrTouch(that, e); }; - document.addEventListener('mousedown', mouseCallback, true); - document.addEventListener('touchstart', mouseCallback, true); - document.addEventListener('mousemove', mouseCallback, true); - document.addEventListener('touchmove', mouseCallback, true); - document.addEventListener('mouseup', mouseCallback, true); - document.addEventListener('touchend', mouseCallback, true); + document.addEventListener('click', mouseCallback, false); + document.addEventListener('touchend', mouseCallback, false); knockout.applyBindings(this._viewModel, this._element); }; @@ -165,11 +130,7 @@ define([ }; Balloon.prototype.render = function() { - var viewModel = this.viewModel; - if (typeof position !== 'undefined') { - viewModel.screenPosition = updatePosition(this, this._scene.computeWindowPosition(position)); - viewModel.render(); - } + this.viewModel.render(); }; return Balloon; diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index b0169b876eeb..3b9771bbc7e7 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -1,61 +1,78 @@ /*global define*/ define([ '../../Core/Cartesian2', + '../../Core/Cartesian3', '../../Core/defaultValue', '../../Core/defineProperties', - '../../Core/destroyObject', '../../Core/DeveloperError', - '../createCommand', '../../ThirdParty/knockout' ], function( Cartesian2, + Cartesian3, defaultValue, defineProperties, - destroyObject, DeveloperError, - createCommand, knockout) { "use strict"; - var BalloonViewModel = function(scene, container, contentElement, balloonElement) { + function shiftPosition(viewModel, position){ + var containerWidth = viewModel._container.clientWidth; + var containerHeight = viewModel._container.clientHeight; + + var width = viewModel._balloonElement.offsetWidth; + var height = viewModel._balloonElement.offsetHeight; + position.x = Math.min(Math.max((position.x - width/2), 0), containerWidth - width); + position.y = Math.min(Math.max((position.y + 20), 0), containerHeight - height); + + return position; + } + + var BalloonViewModel = function(scene, contentElement, balloonElement, container) { this._scene = scene; + this._container = container; this._balloonElement = balloonElement; this._contentElement = contentElement; - this._con = contentElement.innerHTML; - this._position = new Cartesian2(); - this._callback = function() {}; - document.addEventListener(function(){}, this._callback); - - this.dragging = false; + this._content = contentElement.innerHTML; + this._computeScreenPosition = undefined; - this._balloonVisible = false; + this.balloonVisible = false; this._positionX = '0'; this._positionY = '0'; this._updateContent = false; this._updatePosition = false; - knockout.track(this, ['_balloonVisible', '_positionX', '_positionY']); + this._timerRunning = false; + + knockout.track(this, ['balloonVisible', '_positionX', '_positionY']); var that = this; - this._render = createCommand(function() { //TODO: streamline so positions don't run over fade - if (that._updateContent) { - that._balloonVisible = false; - setTimeout(function () { - that._contentElement.innerHTML = that._content; - that._positionX = that._position.x + 'px'; - that._positionY = that._position.y + 'px'; - that._balloonVisible = true; - that.balloonVisible = true; - }, 250); - that._updatePosition = false; - that._updateContent = false; - } else if (that._updatePosition) { - that._balloonVisible = that.balloonVisible; - that._positionX = that._position.x + 'px'; - that._positionY = that._position.y + 'px'; - that._updatePosition = false; + this._render = function() { + if (!that._timerRunning) { + if (that._updateContent) { + that.balloonVisible = false; + that._timerRunning = true; + setTimeout(function () { + that._contentElement.innerHTML = that._content; + if (typeof that._computeScreenPosition === 'function') { + var screenPos = that._computeScreenPosition(); + if (typeof screenPos !== 'undefined') { + screenPos = shiftPosition(that, screenPos); + that._positionX = screenPos.x + 'px'; + that._positionY = screenPos.y + 'px'; + } + } + that.balloonVisible = true; + that._timerRunning = false; + }, 100); + that._updateContent = false; + } else if (typeof that._computeScreenPosition === 'function') { + var screenPos = that._computeScreenPosition(); + screenPos = shiftPosition(that, screenPos); + that._positionX = screenPos.x + 'px'; + that._positionY = screenPos.y + 'px'; + } } - }); + }; }; defineProperties(BalloonViewModel.prototype, { @@ -71,36 +88,23 @@ define([ }, set : function(value) { if (typeof value !== 'string') { - throw new DeveloperError('value must be a string'); - } - if (value !== this._content) { - this._content = value; - this._updateContent = true; + throw new DeveloperError('content value must be a string'); } + this._content = value; + this._updateContent = true; + this.balloonVisible = true; } }, - screenPosition: { - get : function() { - return this._position; - }, + computeScreenPosition: { set: function(value) { - if (!Cartesian2.equals(this._position, value)) { - this._position = Cartesian2.clone(value, this._position); - this._updatePosition = true; + if (typeof value !== 'function') { + throw new DeveloperError('computeScreenPosition must be a function'); } + this._computeScreenPosition = value; } } }); - BalloonViewModel.prototype.isDestroyed = function() { - return false; - }; - - BalloonViewModel.prototype.destroy = function() { - document.removeEventListener(function(){}, this._callback); - destroyObject(this); - }; - return BalloonViewModel; }); \ No newline at end of file From 998ec90cf6f57e28f98df22fc5eecb575c49880c Mon Sep 17 00:00:00 2001 From: hpinkos Date: Thu, 25 Jul 2013 17:12:13 -0400 Subject: [PATCH 04/25] pointer edge and close button --- Source/Widgets/Balloon/Balloon.css | 58 ++++++++++++----- Source/Widgets/Balloon/Balloon.js | 21 +++--- Source/Widgets/Balloon/BalloonViewModel.js | 76 +++++++++++++++++++--- 3 files changed, 119 insertions(+), 36 deletions(-) diff --git a/Source/Widgets/Balloon/Balloon.css b/Source/Widgets/Balloon/Balloon.css index 82ef6de0045f..c772cc12ac65 100644 --- a/Source/Widgets/Balloon/Balloon.css +++ b/Source/Widgets/Balloon/Balloon.css @@ -1,5 +1,13 @@ .cesium-balloon-wrapper { position: absolute; + max-width: 25%; + max-height: 25%; + overflow: auto; + font-size: 10pt; + background-color: #fff; + border-radius: 3px; + z-index: 1; + } .cesium-balloon { @@ -7,11 +15,7 @@ } .cesium-balloon-content { - position: relative; - background-color: #fff; - border-radius: 3px; padding: 5px; - z-index: 1; } .cesium-balloon-point { @@ -24,28 +28,50 @@ -o-transform: rotate(45deg); transform: rotate(45deg); padding: 1px; - margin: -15px auto 0; - position: relative; - bottom: 0; + position: absolute; } .cesium-balloon-wrapper-visible { visibility: visible; opacity: 1; - transition: opacity 0.1s linear; - -webkit-transition: opacity 0.1s linear; - -moz-transition: opacity 0.1s linear; + transition: opacity 0.2s linear; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; } .cesium-balloon-wrapper-hidden { visibility: hidden; opacity: 0; - transition: visibility 0s 0.1s, opacity 0.1s linear; - -webkit-transition: visibility 0s 0.1s, opacity 0.1s linear; - -moz-transition: visibility 0s 0.1s, opacity 0.1s linear; + transition: visibility 0s 0.2s, opacity 0.2s linear; + -webkit-transition: visibility 0s 0.2s, opacity 0.2s linear; + -moz-transition: visibility 0s 0.2s, opacity 0.2s linear; +} + +.cesium-balloon-point-visible { + visibility: visible; + opacity: 1; + transition: opacity 0.2s linear; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; +} + +.cesium-balloon-point-hidden { + visibility: hidden; + opacity: 0; + transition: visibility 0s 0.2s, opacity 0.2s linear; + -webkit-transition: visibility 0s 0.2s, opacity 0.2s linear; + -moz-transition: visibility 0s 0.2s, opacity 0.2s linear; +} + +.cesium-balloon-close { + float: right; + height: 16px; + width: 16px; + display: block; + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgCAYAAAAbifjMAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAl9JREFUSEullL1OG0EUhbegXPc8AEXKSLigoKBISZkHQLZl8fcWLt3kp4gEic07+AEoIp7ABQWFSz+AS4rN983e3YzZmCBxpSPfe+ac2Zk74ylexpei7P8serPbovc0K3qVMJf7yljIuoGgvCl6d2Faggk4C0zBClRq1IatDsgS8gHBGnwOeivg98AIbNTqiaE0gV/W/CGonYHm2En0JMJ9QbjsT4kgyI/AfpTW+3JRWrut6ps9oUEzit8x1pg34BFoFOZy+SQrG+sEdvs6+OZrGlyVv3mer2qq18TBk+BTKASNsWM2qNM23j9BbGEU/Euzv3meb2GStmAjKBbBO/DWJi5TEz0KCr/QXlOF4LVjPAX1MRpcijnE1hJ3BZoDsGb586Dq/8Gv+io7ycegO8HYiWa1eoKuQ+JHUd4ieAYL4L3XIK7BPajQ3HTMeXwvykObE6eTjtFczrGQ7Y7BYNC/uLiYgaerq6tKmMs5FrJuICjPz8/vwrQEE3AWmIIVqNSoDVsdkOXl5eUDgjX453sAvwdGYKNWTwylCfyy5v++B2iOnURPIobDYR/CZbfvAfkRaO+EuVyU1m6r0uveZxTte6AQbMAj0CjM5fJJVnqdwG637wF5Y3BV/uZ5vqqpXhMHt/7OCkFj7JgN6rSN908QW2jfA4WgMfub5/kWJmkLNoKifQ/I39rEpd78GNtrqhC8doynoD5Gg0sxh9ha4q5AcwDWfP3ve8DVbK6yk+x8Dxg70axWT9B1SIzH41sEz2ABvPcaxDW4BxWam445j9FodGhz7LAGYS7nWMgiiuIPrDnc+wQrd0kAAAAASUVORK5CYII=) bottom; + text-indent: -99999px; } -.ceisum-balloon-close { - color: #f00; - text-align: right; +.cesium-balloon-close:hover { + background-position: 0 0; } diff --git a/Source/Widgets/Balloon/Balloon.js b/Source/Widgets/Balloon/Balloon.js index 285db5c809d4..08290272a388 100644 --- a/Source/Widgets/Balloon/Balloon.js +++ b/Source/Widgets/Balloon/Balloon.js @@ -58,26 +58,23 @@ define([ container = getElement(container); this._container = container; + container.setAttribute('data-bind', + 'css: { "cesium-balloon-wrapper-visible" : balloonVisible, "cesium-balloon-wrapper-hidden" : !balloonVisible }'); var el = document.createElement('div'); this._element = el; this._scene = scene; el.className = 'cesium-balloon-wrapper'; container.appendChild(el); - el.setAttribute('data-bind', - 'css: { "cesium-balloon-wrapper-visible" : balloonVisible, "cesium-balloon-wrapper-hidden" : !balloonVisible },\ - style: { "bottom" : _positionY, "left" : _positionX}'); + el.setAttribute('data-bind', 'style: { "bottom" : _positionY, "left" : _positionX}'); var contentWrapper = document.createElement('div'); contentWrapper.className = 'cesium-balloon-content'; el.appendChild(contentWrapper); - var ex = document.createElement('div'); var exA = document.createElement('a'); exA.href = '#'; - exA.textContent = '[x]'; - ex.appendChild(exA); - ex.className = 'ceisum-balloon-close'; - ex.setAttribute('data-bind', 'click: function(){balloonVisible = false;}'); - contentWrapper.appendChild(ex); + exA.className = 'cesium-balloon-close'; + exA.setAttribute('data-bind', 'click: function(){balloonVisible = false; return false;}'); + contentWrapper.appendChild(exA); el.appendChild(contentWrapper); this._content = document.createElement('div'); @@ -87,11 +84,13 @@ define([ contentWrapper.appendChild(balloon); var point = document.createElement('div'); point.className = 'cesium-balloon-point'; - el.appendChild(point); + point.setAttribute('data-bind', 'style: { "bottom" : _pointY, "left" : _pointX}'); + container.appendChild(point); var viewModel = new BalloonViewModel(scene, this._content, this._element, this._container); this._viewModel = viewModel; + this._point = point; var that = this; var mouseCallback = function(e) { clickOrTouch(that, e); @@ -101,6 +100,8 @@ define([ document.addEventListener('touchend', mouseCallback, false); knockout.applyBindings(this._viewModel, this._element); + knockout.applyBindings(this._viewModel, this._point); + knockout.applyBindings(this._viewModel, this._container); }; defineProperties(Balloon.prototype, { diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index 3b9771bbc7e7..ba90d991f8b6 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -16,15 +16,60 @@ define([ "use strict"; function shiftPosition(viewModel, position){ + var pointX; + var pointY; + var posX; + var posY; + var containerWidth = viewModel._container.clientWidth; var containerHeight = viewModel._container.clientHeight; var width = viewModel._balloonElement.offsetWidth; var height = viewModel._balloonElement.offsetHeight; - position.x = Math.min(Math.max((position.x - width/2), 0), containerWidth - width); - position.y = Math.min(Math.max((position.y + 20), 0), containerHeight - height); - return position; + var top = position.y > containerHeight; + var bottom = position.y < -10; + var left = position.x < 0; + var right = position.x > containerWidth; + if (bottom) { + posX = Math.min(Math.max((position.x - width/2), 0), containerWidth - width - 2); + posY = 15; + pointX = Math.min(Math.max((position.x - 11), 4), containerWidth - 29); + pointY = 4; + } else if (top) { + posX = Math.min(Math.max((position.x - width/2), 0), containerWidth - width - 2); + posY = containerHeight - height - 15; + pointX = Math.min(Math.max((position.x - 11), 4), containerWidth - 29); + pointY = containerHeight - 27; + } else if (left) { + posX = 15; + posY = Math.min(Math.max((position.y - height/2), 0), containerHeight - height); + pointX = 4; + pointY = Math.min(Math.max((position.y - 15), 4), containerHeight - 27); + } else if (right) { + posX = containerWidth - width - 15; + posY = Math.min(Math.max((position.y - height/2), 0), containerHeight - height); + pointX = containerWidth - 29; + pointY = Math.min(Math.max((position.y - 15), 4), containerHeight - 27); + } else { + posX = Math.min(Math.max((position.x - width/2), 0), containerWidth - width - 2); + posY = Math.min(Math.max((position.y + 25), 0), containerHeight - height); + + pointX = position.x - 11; + pointY = position.y + 15; + } + + return { + point: { + x: pointX, + y: pointY + }, + screen: { + x: posX, + y: posY + } + }; + } var BalloonViewModel = function(scene, contentElement, balloonElement, container) { @@ -36,13 +81,16 @@ define([ this._computeScreenPosition = undefined; this.balloonVisible = false; + this._pointVisible = false; this._positionX = '0'; this._positionY = '0'; + this._pointX = '0'; + this._pointY = '0'; this._updateContent = false; this._updatePosition = false; this._timerRunning = false; - knockout.track(this, ['balloonVisible', '_positionX', '_positionY']); + knockout.track(this, ['_pointVisible', 'balloonVisible', '_positionX', '_positionY', '_pointX', '_pointY']); var that = this; @@ -56,9 +104,12 @@ define([ if (typeof that._computeScreenPosition === 'function') { var screenPos = that._computeScreenPosition(); if (typeof screenPos !== 'undefined') { - screenPos = shiftPosition(that, screenPos); - that._positionX = screenPos.x + 'px'; - that._positionY = screenPos.y + 'px'; + var pos = shiftPosition(that, screenPos); + that._pointX = (pos.point.x) + 'px'; + that._pointY = (pos.point.y) + 'px'; + + that._positionX = pos.screen.x + 'px'; + that._positionY = pos.screen.y + 'px'; } } that.balloonVisible = true; @@ -67,9 +118,14 @@ define([ that._updateContent = false; } else if (typeof that._computeScreenPosition === 'function') { var screenPos = that._computeScreenPosition(); - screenPos = shiftPosition(that, screenPos); - that._positionX = screenPos.x + 'px'; - that._positionY = screenPos.y + 'px'; + if (typeof screenPos !== 'undefined') { + var pos = shiftPosition(that, screenPos); + that._pointX = (pos.point.x) + 'px'; + that._pointY = (pos.point.y) + 'px'; + + that._positionX = pos.screen.x + 'px'; + that._positionY = pos.screen.y + 'px'; + } } } }; From 6c910f992ea21b27ad4fcfa8a5c39307fb061550 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Fri, 26 Jul 2013 11:18:34 -0400 Subject: [PATCH 05/25] move to viewerDynamicObjectMixin --- Source/Scene/Scene.js | 2 +- Source/Widgets/Balloon/Balloon.css | 18 +--- Source/Widgets/Balloon/Balloon.js | 47 -------- Source/Widgets/Balloon/BalloonViewModel.js | 101 ++++++++++-------- Source/Widgets/Viewer/Viewer.js | 21 ---- .../Viewer/viewerDynamicObjectMixin.js | 37 ++++++- 6 files changed, 92 insertions(+), 134 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 6a557cc7b664..2c4156dc8c4b 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -699,7 +699,7 @@ define([ * * @returns {Cartesain2} The screen coordinates of the given position; */ - Scene.prototype.computeWindowPosition = function(position, result) { + Scene.prototype.computeScreenSpacePosition = function(position, result) { if (typeof position === 'undefined') { return undefined; } diff --git a/Source/Widgets/Balloon/Balloon.css b/Source/Widgets/Balloon/Balloon.css index c772cc12ac65..274536da12e0 100644 --- a/Source/Widgets/Balloon/Balloon.css +++ b/Source/Widgets/Balloon/Balloon.css @@ -7,11 +7,11 @@ background-color: #fff; border-radius: 3px; z-index: 1; - } .cesium-balloon { z-index: 2; + margin-top: 16px; } .cesium-balloon-content { @@ -47,22 +47,6 @@ -moz-transition: visibility 0s 0.2s, opacity 0.2s linear; } -.cesium-balloon-point-visible { - visibility: visible; - opacity: 1; - transition: opacity 0.2s linear; - -webkit-transition: opacity 0.2s linear; - -moz-transition: opacity 0.2s linear; -} - -.cesium-balloon-point-hidden { - visibility: hidden; - opacity: 0; - transition: visibility 0s 0.2s, opacity 0.2s linear; - -webkit-transition: visibility 0s 0.2s, opacity 0.2s linear; - -moz-transition: visibility 0s 0.2s, opacity 0.2s linear; -} - .cesium-balloon-close { float: right; height: 16px; diff --git a/Source/Widgets/Balloon/Balloon.js b/Source/Widgets/Balloon/Balloon.js index 08290272a388..a6d6e3708e2d 100644 --- a/Source/Widgets/Balloon/Balloon.js +++ b/Source/Widgets/Balloon/Balloon.js @@ -1,54 +1,19 @@ /*global define*/ define([ - '../../Core/Cartesian2', - '../../Core/Cartesian3', '../../Core/defineProperties', '../../Core/DeveloperError', '../../Core/destroyObject', - '../../Core/ScreenSpaceEventHandler', - '../../Core/ScreenSpaceEventType', '../getElement', './BalloonViewModel', '../../ThirdParty/knockout' ], function( - Cartesian2, - Cartesian3, defineProperties, DeveloperError, destroyObject, - ScreenSpaceEventHandler, - ScreenSpaceEventType, getElement, BalloonViewModel, knockout) { "use strict"; - var screenPosition = new Cartesian2(); - - function clickOrTouch(widget, e) { - var scene = widget._scene; - var viewModel = widget.viewModel; - - var clientX; - var clientY; - if (e.type === 'touchend') { - clientX = e.touches[0].clientX; - clientY = e.touches[0].clientY; - } else { - clientX = e.clientX; - clientY = e.clientY; - } - - var pickedObject = scene.pick({x: clientX, y: clientY}); - if (typeof pickedObject !== 'undefined' && typeof pickedObject.balloon !== 'undefined') { - if (typeof pickedObject.computeScreenSpacePosition === 'function') { - viewModel.computeScreenPosition = function() { return pickedObject.computeScreenSpacePosition(scene.getContext(), scene.getFrameState()); }; - } else if (typeof pickedObject.getPosition === 'function') { - var position = pickedObject.getPosition(); - viewModel.computeScreenPosition = function() { return scene.computeWindowPosition( position, screenPosition); }; - } - viewModel.content = pickedObject.balloon; - } - } var Balloon = function(container, scene) { if (typeof container === 'undefined') { @@ -62,7 +27,6 @@ define([ 'css: { "cesium-balloon-wrapper-visible" : balloonVisible, "cesium-balloon-wrapper-hidden" : !balloonVisible }'); var el = document.createElement('div'); this._element = el; - this._scene = scene; el.className = 'cesium-balloon-wrapper'; container.appendChild(el); el.setAttribute('data-bind', 'style: { "bottom" : _positionY, "left" : _positionX}'); @@ -91,13 +55,6 @@ define([ this._viewModel = viewModel; this._point = point; - var that = this; - var mouseCallback = function(e) { - clickOrTouch(that, e); - }; - - document.addEventListener('click', mouseCallback, false); - document.addEventListener('touchend', mouseCallback, false); knockout.applyBindings(this._viewModel, this._element); knockout.applyBindings(this._viewModel, this._point); @@ -130,9 +87,5 @@ define([ return destroyObject(this); }; - Balloon.prototype.render = function() { - this.viewModel.render(); - }; - return Balloon; }); \ No newline at end of file diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index ba90d991f8b6..491c98fa0ac4 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -1,20 +1,21 @@ /*global define*/ define([ '../../Core/Cartesian2', - '../../Core/Cartesian3', '../../Core/defaultValue', '../../Core/defineProperties', '../../Core/DeveloperError', '../../ThirdParty/knockout' ], function( Cartesian2, - Cartesian3, defaultValue, defineProperties, DeveloperError, knockout) { "use strict"; + var screenPosition = new Cartesian2(); + var pointMin = 4; + function shiftPosition(viewModel, position){ var pointX; var pointY; @@ -24,38 +25,46 @@ define([ var containerWidth = viewModel._container.clientWidth; var containerHeight = viewModel._container.clientHeight; + var pointMaxY = containerHeight - 28; + var pointMaxX = containerWidth - 28; + var pointXOffset = position.x - 11; + var width = viewModel._balloonElement.offsetWidth; var height = viewModel._balloonElement.offsetHeight; + var posMaxY = containerHeight - height; + var posMaxX = containerWidth - width - 2; + var posMin = 0; + var posXOffset = position.x - width/2; + var top = position.y > containerHeight; var bottom = position.y < -10; var left = position.x < 0; var right = position.x > containerWidth; if (bottom) { - posX = Math.min(Math.max((position.x - width/2), 0), containerWidth - width - 2); + posX = Math.min(Math.max(posXOffset, posMin), posMaxX); posY = 15; - pointX = Math.min(Math.max((position.x - 11), 4), containerWidth - 29); - pointY = 4; + pointX = Math.min(Math.max(pointXOffset, pointMin), pointMaxX); + pointY = pointMin; } else if (top) { - posX = Math.min(Math.max((position.x - width/2), 0), containerWidth - width - 2); + posX = Math.min(Math.max(posXOffset, posMin), posMaxX); posY = containerHeight - height - 15; - pointX = Math.min(Math.max((position.x - 11), 4), containerWidth - 29); - pointY = containerHeight - 27; + pointX = Math.min(Math.max(pointXOffset, pointMin), pointMaxX); + pointY = pointMaxY; } else if (left) { posX = 15; - posY = Math.min(Math.max((position.y - height/2), 0), containerHeight - height); - pointX = 4; - pointY = Math.min(Math.max((position.y - 15), 4), containerHeight - 27); + posY = Math.min(Math.max((position.y - height/2), posMin), posMaxY); + pointX = pointMin; + pointY = Math.min(Math.max((position.y - 15), pointMin), pointMaxY); } else if (right) { posX = containerWidth - width - 15; - posY = Math.min(Math.max((position.y - height/2), 0), containerHeight - height); - pointX = containerWidth - 29; - pointY = Math.min(Math.max((position.y - 15), 4), containerHeight - 27); + posY = Math.min(Math.max((position.y - height/2), posMin), posMaxY); + pointX = pointMaxX; + pointY = Math.min(Math.max((position.y - 15), pointMin), pointMaxY); } else { - posX = Math.min(Math.max((position.x - width/2), 0), containerWidth - width - 2); - posY = Math.min(Math.max((position.y + 25), 0), containerHeight - height); - - pointX = position.x - 11; + posX = Math.min(Math.max(posXOffset, posMin), posMaxX); + posY = Math.min(Math.max((position.y + 25), posMin), posMaxY); + pointX = pointXOffset; pointY = position.y + 15; } @@ -80,21 +89,20 @@ define([ this._content = contentElement.innerHTML; this._computeScreenPosition = undefined; - this.balloonVisible = false; - this._pointVisible = false; this._positionX = '0'; this._positionY = '0'; this._pointX = '0'; this._pointY = '0'; this._updateContent = false; - this._updatePosition = false; this._timerRunning = false; - knockout.track(this, ['_pointVisible', 'balloonVisible', '_positionX', '_positionY', '_pointX', '_pointY']); + this.balloonVisible = false; + + knockout.track(this, ['balloonVisible', '_positionX', '_positionY', '_pointX', '_pointY']); var that = this; - this._render = function() { + this._update = function() { if (!that._timerRunning) { if (that._updateContent) { that.balloonVisible = false; @@ -105,8 +113,8 @@ define([ var screenPos = that._computeScreenPosition(); if (typeof screenPos !== 'undefined') { var pos = shiftPosition(that, screenPos); - that._pointX = (pos.point.x) + 'px'; - that._pointY = (pos.point.y) + 'px'; + that._pointX = pos.point.x + 'px'; + that._pointY = pos.point.y + 'px'; that._positionX = pos.screen.x + 'px'; that._positionY = pos.screen.y + 'px'; @@ -120,8 +128,8 @@ define([ var screenPos = that._computeScreenPosition(); if (typeof screenPos !== 'undefined') { var pos = shiftPosition(that, screenPos); - that._pointX = (pos.point.x) + 'px'; - that._pointY = (pos.point.y) + 'px'; + that._pointX = pos.point.x + 'px'; + that._pointY = pos.point.y + 'px'; that._positionX = pos.screen.x + 'px'; that._positionY = pos.screen.y + 'px'; @@ -132,33 +140,32 @@ define([ }; defineProperties(BalloonViewModel.prototype, { - render: { + update: { get: function() { - return this._render; - } - }, - - content : { - get : function() { - return this._content; - }, - set : function(value) { - if (typeof value !== 'string') { - throw new DeveloperError('content value must be a string'); - } - this._content = value; - this._updateContent = true; - this.balloonVisible = true; + return this._update; } }, - computeScreenPosition: { + pickObject: { set: function(value) { - if (typeof value !== 'function') { - throw new DeveloperError('computeScreenPosition must be a function'); + var scene = this._scene; + if (typeof value.balloon === 'undefined') { + value.balloon = 'balloon data'; } - this._computeScreenPosition = value; + if (typeof value !== 'undefined' && typeof value.balloon === 'string') { + if (typeof value.computeScreenSpacePosition === 'function') { + this._computeScreenPosition = function() { return value.computeScreenSpacePosition(scene.getContext(), scene.getFrameState()); }; + } else if (typeof value.getPosition === 'function') { + var position = value.getPosition(); + this._computeScreenPosition = function() { return scene.computeScreenSpacePosition( position, screenPosition); }; + } + this._content = value.balloon; + this._updateContent = true; + this.balloonVisible = true; + } + } + } }); diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js index 461cc8574e88..2b28c24abcb6 100644 --- a/Source/Widgets/Viewer/Viewer.js +++ b/Source/Widgets/Viewer/Viewer.js @@ -12,7 +12,6 @@ define([ '../../DynamicScene/DataSourceDisplay', '../Animation/Animation', '../Animation/AnimationViewModel', - '../Balloon/Balloon', '../BaseLayerPicker/BaseLayerPicker', '../BaseLayerPicker/createDefaultBaseLayers', '../CesiumWidget/CesiumWidget', @@ -36,7 +35,6 @@ define([ DataSourceDisplay, Animation, AnimationViewModel, - Balloon, BaseLayerPicker, createDefaultBaseLayers, CesiumWidget, @@ -290,21 +288,11 @@ Either specify options.imageryProvider instead or set options.baseLayerPicker to timeline.container.style.right = 0; } - //Balloon - var balloon; - if (typeof options.balloon === 'undefined' || options.balloon !== false) { - var balloonContainer = document.createElement('div'); - balloonContainer.className = 'cesium-viewer-balloonContainer'; - viewerContainer.appendChild(balloonContainer); - balloon = new Balloon(balloonContainer, cesiumWidget.scene); - } - this._container = container; this._viewerContainer = viewerContainer; this._cesiumWidget = cesiumWidget; this._toolbar = toolbar; this._homeButton = homeButton; - this._balloon = balloon; this._sceneModePicker = sceneModePicker; this._baseLayerPicker = baseLayerPicker; this._animation = animation; @@ -387,12 +375,6 @@ Either specify options.imageryProvider instead or set options.baseLayerPicker to } }, - balloon : { - get : function() { - return this._balloon; - } - }, - /** * Gets the Timeline widget. * @memberof Viewer.prototype @@ -661,9 +643,6 @@ Either specify options.imageryProvider instead or set options.baseLayerPicker to */ Viewer.prototype.render = function() { this._cesiumWidget.render(); - if (typeof this._balloon !== 'undefined') { - this._balloon.render(); - } }; /** diff --git a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js index f55f6cdfb35d..84459559df39 100644 --- a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js +++ b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js @@ -6,6 +6,7 @@ define([ '../../Core/EventHelper', '../../Core/ScreenSpaceEventType', '../../Core/wrapFunction', + '../Balloon/Balloon', '../../DynamicScene/DynamicObjectView' ], function( defaultValue, @@ -14,6 +15,7 @@ define([ EventHelper, ScreenSpaceEventType, wrapFunction, + Balloon, DynamicObjectView) { "use strict"; @@ -45,15 +47,29 @@ define([ throw new DeveloperError('trackedObject is already defined by another mixin.'); } + + //Balloon + var balloonContainer = document.createElement('div'); + balloonContainer.className = 'cesium-viewer-balloonContainer'; + viewer._viewerContainer.appendChild(balloonContainer); + var balloon = new Balloon(balloonContainer, viewer.scene); + + viewer._balloon = balloon; + var eventHelper = new EventHelper(); var trackedObject; var dynamicObjectView; + var balloonedObject; //Subscribe to onTick so that we can update the view each update. function updateView(clock) { if (typeof dynamicObjectView !== 'undefined') { dynamicObjectView.update(clock.currentTime); } + + if (typeof balloonedObject !== 'undefined') { + viewer._balloon.viewModel.update(); + } } eventHelper.add(viewer.clock.onTick, updateView); @@ -66,6 +82,13 @@ define([ } } + function showBalloon(e) { + var pickedPrimitive = viewer.scene.pick(e.position); + if (typeof pickedPrimitive !== 'undefined') { + viewer.balloonedObject = pickedPrimitive; + } + } + function clearTrackedObject() { viewer.trackedObject = undefined; } @@ -77,7 +100,8 @@ define([ } //Subscribe to left clicks and zoom to the picked object. - viewer.screenSpaceEventHandler.setInputAction(pickAndTrackObject, ScreenSpaceEventType.LEFT_CLICK); + viewer.screenSpaceEventHandler.setInputAction(showBalloon, ScreenSpaceEventType.LEFT_CLICK); + viewer.screenSpaceEventHandler.setInputAction(pickAndTrackObject, ScreenSpaceEventType.RIGHT_CLICK); defineProperties(viewer, { /** @@ -96,6 +120,17 @@ define([ } viewer.scene.getScreenSpaceCameraController().enableTilt = typeof value === 'undefined'; } + }, + balloonedObject: { + get: function() { + return balloonedObject; + }, + set: function(value) { + if (balloonedObject !== value || !viewer._balloon.viewModel.balloonVisible) { + balloonedObject = value; + viewer._balloon.viewModel.pickObject = balloonedObject; + } + } } }); From 427d5129cda0b3606f332643b95570d42472db32 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Fri, 26 Jul 2013 11:39:31 -0400 Subject: [PATCH 06/25] documentation --- Source/Widgets/Balloon/Balloon.js | 40 +++++++++++++++++++ Source/Widgets/Balloon/BalloonViewModel.js | 23 +++++++++++ .../Viewer/viewerDynamicObjectMixin.js | 6 +++ 3 files changed, 69 insertions(+) diff --git a/Source/Widgets/Balloon/Balloon.js b/Source/Widgets/Balloon/Balloon.js index a6d6e3708e2d..7c0b5f71b6b9 100644 --- a/Source/Widgets/Balloon/Balloon.js +++ b/Source/Widgets/Balloon/Balloon.js @@ -15,11 +15,30 @@ define([ knockout) { "use strict"; + /** + * A widget for displaying data in a balloon pointing to a picked object + * + * @alias Balloon + * @constructor + * + * @param {Element|String} container The DOM element or ID that will contain the widget. + * @param {Scene} scene The Scene instance to use. + * + * @exception {DeveloperError} container is required. + * @exception {DeveloperError} scene is required. + * @exception {DeveloperError} Element with id "container" does not exist in the document. + * + * @see Fullscreen + */ var Balloon = function(container, scene) { if (typeof container === 'undefined') { throw new DeveloperError('container is required.'); } + if (typeof scene === 'undefined') { + throw new DeveloperError('scene is required.'); + } + container = getElement(container); this._container = container; @@ -62,12 +81,24 @@ define([ }; defineProperties(Balloon.prototype, { + /** + * Gets the parent container. + * @memberof Balloon.prototype + * + * @type {Element} + */ container : { get : function() { return this._container; } }, + /** + * Gets the view model. + * @memberof Balloon.prototype + * + * @type {BalloonViewModel} + */ viewModel : { get : function() { return this._viewModel; @@ -75,10 +106,19 @@ define([ } }); + /** + * @memberof Balloon + * @returns {Boolean} true if the object has been destroyed, false otherwise. + */ Balloon.prototype.isDestroyed = function() { return false; }; + /** + * Destroys the widget. Should be called if permanently + * removing the widget from layout. + * @memberof Balloon + */ Balloon.prototype.destroy = function() { var container = this._container; knockout.cleanNode(container); diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index 491c98fa0ac4..a2f8859504b8 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -81,6 +81,17 @@ define([ } + /** + * The view model for {@link Balloon}. + * @alias BalloonViewModel + * @constructor + + * @param {Scene} scene The scene instance to use. + * @param {Element} contentElement The element in which to display balloon content. + * @param {Element} balloonElement The element containing all elements that make up the balloon. + * @param {Element} container The element containing the balloon. + + */ var BalloonViewModel = function(scene, contentElement, balloonElement, container) { this._scene = scene; this._container = container; @@ -140,12 +151,24 @@ define([ }; defineProperties(BalloonViewModel.prototype, { + /** + * Updates the view of the balloon + * @memberof BalloonViewModel.prototype + * + * @type {Function} + */ update: { get: function() { return this._update; } }, + /** + * Sets the object for which to display the balloon + * @memberof BalloonViewModel + * + * @type {Object} + */ pickObject: { set: function(value) { var scene = this._scene; diff --git a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js index 84459559df39..eff528a92214 100644 --- a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js +++ b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js @@ -121,6 +121,11 @@ define([ viewer.scene.getScreenSpaceCameraController().enableTilt = typeof value === 'undefined'; } }, + /** + * Gets or sets the object instance for which to display a balloon + * @memberof viewerDynamicObjectMixin.prototype + * @type {Object} + */ balloonedObject: { get: function() { return balloonedObject; @@ -139,6 +144,7 @@ define([ eventHelper.removeAll(); viewer.screenSpaceEventHandler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK); + viewer.screenSpaceEventHandler.removeInputAction(ScreenSpaceEventType.RIGHT_CLICK); }); }; From 4b50b9aa2430310d8b63846660fe711d48eba786 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Fri, 26 Jul 2013 11:57:44 -0400 Subject: [PATCH 07/25] cleanup --- Source/DynamicScene/DynamicObject.js | 7 ------- Source/Widgets/Balloon/BalloonViewModel.js | 3 --- 2 files changed, 10 deletions(-) diff --git a/Source/DynamicScene/DynamicObject.js b/Source/DynamicScene/DynamicObject.js index 8d22e9370c1b..7c7fca4a3c86 100644 --- a/Source/DynamicScene/DynamicObject.js +++ b/Source/DynamicScene/DynamicObject.js @@ -179,13 +179,6 @@ define([ * @default undefined */ this.viewFrom = undefined; - - /** - * Gets or sets the HTML content of a balloon that will be visible upon clicking the object. - * @type {String} - * @default undefined - */ - this.balloon = undefined; }; /** diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index a2f8859504b8..193c3884d66d 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -172,9 +172,6 @@ define([ pickObject: { set: function(value) { var scene = this._scene; - if (typeof value.balloon === 'undefined') { - value.balloon = 'balloon data'; - } if (typeof value !== 'undefined' && typeof value.balloon === 'string') { if (typeof value.computeScreenSpacePosition === 'function') { this._computeScreenPosition = function() { return value.computeScreenSpacePosition(scene.getContext(), scene.getFrameState()); }; From 6595210b60c585b1462ffb1a9c203fbaae2bd8d6 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Fri, 26 Jul 2013 17:31:36 -0400 Subject: [PATCH 08/25] style changes --- Source/Widgets/Balloon/Balloon.css | 54 ++++++++++++---- Source/Widgets/Balloon/Balloon.js | 38 +++++++---- Source/Widgets/Balloon/BalloonViewModel.js | 63 ++++++++++++++----- .../Viewer/viewerDynamicObjectMixin.js | 10 +-- 4 files changed, 121 insertions(+), 44 deletions(-) diff --git a/Source/Widgets/Balloon/Balloon.css b/Source/Widgets/Balloon/Balloon.css index 274536da12e0..09527df2552d 100644 --- a/Source/Widgets/Balloon/Balloon.css +++ b/Source/Widgets/Balloon/Balloon.css @@ -1,21 +1,32 @@ .cesium-balloon-wrapper { position: absolute; - max-width: 25%; - max-height: 25%; - overflow: auto; font-size: 10pt; background-color: #fff; - border-radius: 3px; + border-radius: 5px; z-index: 1; + border: 1px solid #ccc; + padding: 5px +} + +.cesium-balloon-content { + overflow: auto; + margin-right: 20px; } -.cesium-balloon { +.cesium-balloon-point-container{ + position: absolute; + overflow: hidden; z-index: 2; - margin-top: 16px; } -.cesium-balloon-content { - padding: 5px; +.cesium-balloon-point-container-downup { + height: 16px; + width: 32px; +} + +.cesium-balloon-point-container-leftright { + height: 32px; + width: 16px; } .cesium-balloon-point { @@ -27,8 +38,27 @@ -ms-transform: rotate(45deg); -o-transform: rotate(45deg); transform: rotate(45deg); - padding: 1px; - position: absolute; + border: 1px solid #ccc; +} + +.cesium-balloon-point-down { + margin-left: 5px; + margin-top: -11px; +} + +.cesium-balloon-point-up { + margin-left: 5px; + margin-top: 5px; +} + +.cesium-balloon-point-left { + margin-top: 5px; + margin-left: 5px; +} + +.cesium-balloon-point-right { + margin-left: -11px; + margin-top: 5px; } .cesium-balloon-wrapper-visible { @@ -48,7 +78,9 @@ } .cesium-balloon-close { - float: right; + position: absolute; + top: 5px; + right: 5px; height: 16px; width: 16px; display: block; diff --git a/Source/Widgets/Balloon/Balloon.js b/Source/Widgets/Balloon/Balloon.js index 7c0b5f71b6b9..f963f351dad1 100644 --- a/Source/Widgets/Balloon/Balloon.js +++ b/Source/Widgets/Balloon/Balloon.js @@ -43,7 +43,7 @@ define([ this._container = container; container.setAttribute('data-bind', - 'css: { "cesium-balloon-wrapper-visible" : balloonVisible, "cesium-balloon-wrapper-hidden" : !balloonVisible }'); + 'css: { "cesium-balloon-wrapper-visible" : showBalloon, "cesium-balloon-wrapper-hidden" : !showBalloon }'); var el = document.createElement('div'); this._element = el; el.className = 'cesium-balloon-wrapper'; @@ -52,30 +52,42 @@ define([ var contentWrapper = document.createElement('div'); contentWrapper.className = 'cesium-balloon-content'; + contentWrapper.setAttribute('data-bind', 'style: {"max-width": _maxWidth, "max-height": _maxHeight}'); el.appendChild(contentWrapper); - var exA = document.createElement('a'); - exA.href = '#'; - exA.className = 'cesium-balloon-close'; - exA.setAttribute('data-bind', 'click: function(){balloonVisible = false; return false;}'); - contentWrapper.appendChild(exA); - el.appendChild(contentWrapper); + var ex = document.createElement('a'); + ex.href = '#'; + ex.className = 'cesium-balloon-close'; + ex.setAttribute('data-bind', 'click: function(){showBalloon = false; return false;}'); + el.appendChild(ex); + this._content = document.createElement('div'); - var balloon = document.createElement('div'); - balloon.className = 'cesium-balloon'; - balloon.appendChild(this._content); - contentWrapper.appendChild(balloon); + contentWrapper.appendChild(this._content); + var pointContainer = document.createElement('div'); + pointContainer.className = 'cesium-balloon-point-container'; + pointContainer.setAttribute('data-bind', + 'css: { "cesium-balloon-point-container-downup" : _down || _up, "cesium-balloon-point-container-leftright" : _left || _right},\ + style: { "bottom" : _pointY, "left" : _pointX}'); var point = document.createElement('div'); point.className = 'cesium-balloon-point'; - point.setAttribute('data-bind', 'style: { "bottom" : _pointY, "left" : _pointX}'); - container.appendChild(point); + point.setAttribute('data-bind', + 'css: { "cesium-balloon-point-down" : _down,\ + "cesium-balloon-point-up" : _up,\ + "cesium-balloon-point-left" : _left,\ + "cesium-balloon-point-right" : _right}'); + pointContainer.appendChild(point); + container.appendChild(pointContainer); var viewModel = new BalloonViewModel(scene, this._content, this._element, this._container); this._viewModel = viewModel; + this._pointContainer = pointContainer; this._point = point; + this._contentWrapper = contentWrapper; + knockout.applyBindings(this._viewModel, this._contentWrapper); knockout.applyBindings(this._viewModel, this._element); + knockout.applyBindings(this._viewModel, this._pointContainer); knockout.applyBindings(this._viewModel, this._point); knockout.applyBindings(this._viewModel, this._container); }; diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index 193c3884d66d..20e196557ae6 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -14,7 +14,7 @@ define([ "use strict"; var screenPosition = new Cartesian2(); - var pointMin = 4; + var pointMin = 0; function shiftPosition(viewModel, position){ var pointX; @@ -25,9 +25,12 @@ define([ var containerWidth = viewModel._container.clientWidth; var containerHeight = viewModel._container.clientHeight; - var pointMaxY = containerHeight - 28; - var pointMaxX = containerWidth - 28; - var pointXOffset = position.x - 11; + viewModel._maxWidth = Math.floor(viewModel._container.clientWidth*0.25) + 'px'; + viewModel._maxHeight = Math.floor(viewModel._container.clientHeight*0.25) + 'px'; + + var pointMaxY = containerHeight - 15; + var pointMaxX = containerWidth - 16; + var pointXOffset = position.x - 15; var width = viewModel._balloonElement.offsetWidth; var height = viewModel._balloonElement.offsetHeight; @@ -41,44 +44,64 @@ define([ var bottom = position.y < -10; var left = position.x < 0; var right = position.x > containerWidth; + if (bottom) { posX = Math.min(Math.max(posXOffset, posMin), posMaxX); posY = 15; pointX = Math.min(Math.max(pointXOffset, pointMin), pointMaxX); pointY = pointMin; + viewModel._down = true; + viewModel._up = false; + viewModel._left = false; + viewModel._right = false; } else if (top) { posX = Math.min(Math.max(posXOffset, posMin), posMaxX); - posY = containerHeight - height - 15; + posY = containerHeight - height - 14; pointX = Math.min(Math.max(pointXOffset, pointMin), pointMaxX); pointY = pointMaxY; + viewModel._down = false; + viewModel._up = true; + viewModel._left = false; + viewModel._right = false; } else if (left) { posX = 15; posY = Math.min(Math.max((position.y - height/2), posMin), posMaxY); pointX = pointMin; pointY = Math.min(Math.max((position.y - 15), pointMin), pointMaxY); + viewModel._down = false; + viewModel._up = false; + viewModel._left = true; + viewModel._right = false; } else if (right) { posX = containerWidth - width - 15; posY = Math.min(Math.max((position.y - height/2), posMin), posMaxY); pointX = pointMaxX; pointY = Math.min(Math.max((position.y - 15), pointMin), pointMaxY); + viewModel._down = false; + viewModel._up = false; + viewModel._left = false; + viewModel._right = true; } else { posX = Math.min(Math.max(posXOffset, posMin), posMaxX); posY = Math.min(Math.max((position.y + 25), posMin), posMaxY); pointX = pointXOffset; - pointY = position.y + 15; + pointY = Math.min(position.y + 10, posMaxY - 15); + viewModel._down = true; + viewModel._up = false; + viewModel._left = false; + viewModel._right = false; } return { point: { - x: pointX, - y: pointY + x: Math.floor(pointX), + y: Math.floor(pointY) }, screen: { - x: posX, - y: posY + x: Math.floor(posX), + y: Math.floor(posY) } }; - } /** @@ -107,16 +130,24 @@ define([ this._updateContent = false; this._timerRunning = false; - this.balloonVisible = false; + this.showBalloon = false; + this._down = true; + this._up = false; + this._left = false; + this._right = false; + + this._maxWidth = Math.floor(this._container.clientWidth*0.25) + 'px'; + this._maxHeight = Math.floor(this._container.clientHeight*0.25) + 'px'; - knockout.track(this, ['balloonVisible', '_positionX', '_positionY', '_pointX', '_pointY']); + knockout.track(this, ['showBalloon', '_positionX', '_positionY', '_pointX', '_pointY', + '_down', '_up', '_left', '_right', '_maxWidth', '_maxHeight']); var that = this; this._update = function() { if (!that._timerRunning) { if (that._updateContent) { - that.balloonVisible = false; + that.showBalloon = false; that._timerRunning = true; setTimeout(function () { that._contentElement.innerHTML = that._content; @@ -131,7 +162,7 @@ define([ that._positionY = pos.screen.y + 'px'; } } - that.balloonVisible = true; + that.showBalloon = true; that._timerRunning = false; }, 100); that._updateContent = false; @@ -181,7 +212,7 @@ define([ } this._content = value.balloon; this._updateContent = true; - this.balloonVisible = true; + this.showBalloon = true; } } diff --git a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js index eff528a92214..03bf48e26c82 100644 --- a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js +++ b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js @@ -63,13 +63,12 @@ define([ //Subscribe to onTick so that we can update the view each update. function updateView(clock) { - if (typeof dynamicObjectView !== 'undefined') { - dynamicObjectView.update(clock.currentTime); - } - if (typeof balloonedObject !== 'undefined') { viewer._balloon.viewModel.update(); } + if (typeof dynamicObjectView !== 'undefined') { + dynamicObjectView.update(clock.currentTime); + } } eventHelper.add(viewer.clock.onTick, updateView); @@ -116,6 +115,9 @@ define([ set : function(value) { if (trackedObject !== value) { trackedObject = value; + if (trackedObject !== balloonedObject.dynamicObject && typeof trackedObject !== 'undefined') { + viewer._balloon.viewModel.showBalloon = false; + } dynamicObjectView = typeof value !== 'undefined' ? new DynamicObjectView(value, viewer.scene, viewer.centralBody.getEllipsoid()) : undefined; } viewer.scene.getScreenSpaceCameraController().enableTilt = typeof value === 'undefined'; From ff4e7c01a12f342e69b1b0b2a67eec6020de2649 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Mon, 29 Jul 2013 10:35:14 -0400 Subject: [PATCH 09/25] documentation and specs --- Source/Widgets/Balloon/Balloon.js | 6 -- Source/Widgets/Balloon/BalloonViewModel.js | 86 ++++++++++++++++++- .../Viewer/viewerDynamicObjectMixin.js | 8 +- Specs/Widgets/Balloon/BalloonSpec.js | 56 ++++++++++++ Specs/Widgets/Balloon/BalloonViewModelSpec.js | 61 +++++++++++++ .../Viewer/viewerDynamicObjectMixinSpec.js | 34 +++++++- 6 files changed, 239 insertions(+), 12 deletions(-) create mode 100644 Specs/Widgets/Balloon/BalloonSpec.js create mode 100644 Specs/Widgets/Balloon/BalloonViewModelSpec.js diff --git a/Source/Widgets/Balloon/Balloon.js b/Source/Widgets/Balloon/Balloon.js index f963f351dad1..83b51fb426c0 100644 --- a/Source/Widgets/Balloon/Balloon.js +++ b/Source/Widgets/Balloon/Balloon.js @@ -25,7 +25,6 @@ define([ * @param {Scene} scene The Scene instance to use. * * @exception {DeveloperError} container is required. - * @exception {DeveloperError} scene is required. * @exception {DeveloperError} Element with id "container" does not exist in the document. * * @see Fullscreen @@ -35,10 +34,6 @@ define([ throw new DeveloperError('container is required.'); } - if (typeof scene === 'undefined') { - throw new DeveloperError('scene is required.'); - } - container = getElement(container); this._container = container; @@ -134,7 +129,6 @@ define([ Balloon.prototype.destroy = function() { var container = this._container; knockout.cleanNode(container); - this._viewModel.destroy(); container.removeChild(this._element); return destroyObject(this); }; diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index 20e196557ae6..c175d7889237 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -108,16 +108,32 @@ define([ * The view model for {@link Balloon}. * @alias BalloonViewModel * @constructor - + * * @param {Scene} scene The scene instance to use. * @param {Element} contentElement The element in which to display balloon content. * @param {Element} balloonElement The element containing all elements that make up the balloon. - * @param {Element} container The element containing the balloon. - + * @param {Element} [container = document.body] The element containing the balloon. + * + * @exception {DeveloperError} scene is required. + * @exception {DeveloperError} contentElement is required. + * @exception {DeveloperError} balloonElement is required. + * */ var BalloonViewModel = function(scene, contentElement, balloonElement, container) { + if (typeof scene === 'undefined') { + throw new DeveloperError('scene is required.'); + } + + if (typeof contentElement === 'undefined') { + throw new DeveloperError('contentElement is required.'); + } + + if (typeof balloonElement === 'undefined') { + throw new DeveloperError('balloonElement is required.'); + } + this._scene = scene; - this._container = container; + this._container = defaultValue(container, document.body); this._balloonElement = balloonElement; this._contentElement = contentElement; this._content = contentElement.innerHTML; @@ -182,6 +198,57 @@ define([ }; defineProperties(BalloonViewModel.prototype, { + /** + * Gets or sets the HTML element containing the balloon + * @memberof BalloonViewModel.prototype + * + * @type {Element} + */ + container : { + get : function() { + return this._container; + }, + set : function(value) { + if (!(value instanceof Element)) { + throw new DeveloperError('value must be a valid Element.'); + } + this._container = value; + } + }, + /** + * Gets or sets the HTML element that makes up the balloon + * @memberof BalloonViewModel.prototype + * + * @type {Element} + */ + balloonElement : { + get : function() { + return this._balloonElement; + }, + set : function(value) { + if (!(value instanceof Element)) { + throw new DeveloperError('value must be a valid Element.'); + } + this._balloonElement = value; + } + }, + /** + * Gets or sets the HTML element that displays the content of the balloon + * @memberof BalloonViewModel.prototype + * + * @type {Element} + */ + contentElement : { + get : function() { + return this._contentElement; + }, + set : function(value) { + if (!(value instanceof Element)) { + throw new DeveloperError('value must be a valid Element.'); + } + this._contentElement = value; + } + }, /** * Updates the view of the balloon * @memberof BalloonViewModel.prototype @@ -194,6 +261,17 @@ define([ } }, + /** + * Gets the scene to control. + * @memberof BalloonViewModel.prototype + * + * @type {Scene} + */ + scene : { + get : function() { + return this._scene; + } + }, /** * Sets the object for which to display the balloon * @memberof BalloonViewModel diff --git a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js index 03bf48e26c82..9cc094d71e01 100644 --- a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js +++ b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js @@ -31,13 +31,16 @@ define([ * * @exception {DeveloperError} viewer is required. * @exception {DeveloperError} trackedObject is already defined by another mixin. + * @exception {DeveloperError} balloonedObject is already defined by another mixin. * * @example * // Add support for working with DynamicObject instances to the Viewer. * var dynamicObject = ... //A DynamicObject instance + * var object = ... //A DynamicObject instance * var viewer = new Viewer('cesiumContainer'); * viewer.extend(viewerDynamicObjectMixin); * viewer.trackedObject = dynamicObject; //Camera will now track dynamicObject + * viewer.balloonedObject = object; //Balloon will now appear over object */ var viewerDynamicObjectMixin = function(viewer) { if (typeof viewer === 'undefined') { @@ -46,6 +49,9 @@ define([ if (viewer.hasOwnProperty('trackedObject')) { throw new DeveloperError('trackedObject is already defined by another mixin.'); } + if (viewer.hasOwnProperty('balloonedObject')) { + throw new DeveloperError('balloonedObject is already defined by another mixin.'); + } //Balloon @@ -115,7 +121,7 @@ define([ set : function(value) { if (trackedObject !== value) { trackedObject = value; - if (trackedObject !== balloonedObject.dynamicObject && typeof trackedObject !== 'undefined') { + if (typeof balloonedObject !== 'undefined' && trackedObject !== balloonedObject.dynamicObject && typeof trackedObject !== 'undefined') { viewer._balloon.viewModel.showBalloon = false; } dynamicObjectView = typeof value !== 'undefined' ? new DynamicObjectView(value, viewer.scene, viewer.centralBody.getEllipsoid()) : undefined; diff --git a/Specs/Widgets/Balloon/BalloonSpec.js b/Specs/Widgets/Balloon/BalloonSpec.js new file mode 100644 index 000000000000..0245b1241fee --- /dev/null +++ b/Specs/Widgets/Balloon/BalloonSpec.js @@ -0,0 +1,56 @@ +/*global defineSuite*/ +defineSuite([ + 'Widgets/Balloon/Balloon', + 'Core/Ellipsoid', + 'Scene/SceneTransitioner', + 'Specs/createScene', + 'Specs/destroyScene' + ], function( + Balloon, + Ellipsoid, + SceneTransitioner, + createScene, + destroyScene) { + "use strict"; + /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/ + + var scene; + beforeAll(function() { + scene = createScene(); + }); + + afterAll(function() { + destroyScene(scene); + }); + + it('constructor sets expected values', function() { + var balloon = new Balloon(document.body, scene); + expect(balloon.container).toBe(document.body); + expect(balloon.viewModel.scene).toBe(scene); + expect(balloon.isDestroyed()).toEqual(false); + balloon.destroy(); + expect(balloon.isDestroyed()).toEqual(true); + }); + + it('constructor works with string id container', function() { + var testElement = document.createElement('span'); + testElement.id = 'testElement'; + document.body.appendChild(testElement); + var balloon = new Balloon('testElement', scene); + expect(balloon.container).toBe(testElement); + document.body.removeChild(testElement); + balloon.destroy(); + }); + + it('throws if container is undefined', function() { + expect(function() { + return new Balloon(undefined, scene); + }).toThrow(); + }); + + it('throws if container string is undefined', function() { + expect(function() { + return new Balloon('testElement', scene); + }).toThrow(); + }); +}); \ No newline at end of file diff --git a/Specs/Widgets/Balloon/BalloonViewModelSpec.js b/Specs/Widgets/Balloon/BalloonViewModelSpec.js new file mode 100644 index 000000000000..b1daf92a75a7 --- /dev/null +++ b/Specs/Widgets/Balloon/BalloonViewModelSpec.js @@ -0,0 +1,61 @@ +/*global defineSuite*/ +defineSuite([ + 'Widgets/Balloon/BalloonViewModel', + 'Core/Ellipsoid', + 'Scene/SceneTransitioner', + 'Specs/createScene', + 'Specs/destroyScene' + ], function( + BalloonViewModel, + Ellipsoid, + SceneTransitioner, + createScene, + destroyScene) { + "use strict"; + /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/ + + var scene; + var contentElement = document.createElement('div'); + var balloonElement = document.createElement('div'); + var container = document.createElement('div'); + beforeAll(function() { + scene = createScene(); + }); + + afterAll(function() { + destroyScene(scene); + }); + + it('constructor sets default values', function() { + var viewModel = new BalloonViewModel(scene, contentElement, balloonElement); + expect(viewModel.scene).toBe(scene); + expect(viewModel.contentElement).toBe(contentElement); + expect(viewModel.balloonElement).toBe(balloonElement); + }); + + it('constructor sets expected values', function() { + var viewModel = new BalloonViewModel(scene, contentElement, balloonElement, container); + expect(viewModel.scene).toBe(scene); + expect(viewModel.contentElement).toBe(contentElement); + expect(viewModel.balloonElement).toBe(balloonElement); + expect(viewModel.container).toBe(container); + }); + + it('throws if scene is undefined', function() { + expect(function() { + return new BalloonViewModel(undefined); + }).toThrow(); + }); + + it('throws if contentElement is undefined', function() { + expect(function() { + return new BalloonViewModel(scene); + }).toThrow(); + }); + + it('throws if balloonElement is undefined', function() { + expect(function() { + return new BalloonViewModel(scene, contentElement); + }).toThrow(); + }); +}); \ No newline at end of file diff --git a/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js b/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js index 09fb1c436267..79fcec2719dc 100644 --- a/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js +++ b/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js @@ -51,6 +51,29 @@ defineSuite([ viewer.destroy(); }); + it('adds balloonedObject property', function() { + var viewer = new Viewer(container); + viewer.extend(viewerDynamicObjectMixin); + expect(viewer.balloonedObject).toBeUndefined(); + viewer.destroy(); + }); + + it('can get and set balloonedObject', function() { + var viewer = new Viewer(container); + viewer.extend(viewerDynamicObjectMixin); + + var dynamicObject = new DynamicObject(); + dynamicObject.position = new MockProperty(new Cartesian3(123456, 123456, 123456)); + + viewer.balloonedObject = dynamicObject; + expect(viewer.balloonedObject).toBe(dynamicObject); + + viewer.balloonedObject = undefined; + expect(viewer.balloonedObject).toBeUndefined(); + + viewer.destroy(); + }); + it('home button resets tracked object', function() { var viewer = new Viewer(container); viewer.extend(viewerDynamicObjectMixin); @@ -78,7 +101,7 @@ defineSuite([ }).toThrow(); }); - it('throws if dropTarget property already added by another mixin.', function() { + it('throws if trackedObject property already added by another mixin.', function() { var viewer = new Viewer(container); viewer.trackedObject = true; expect(function() { @@ -86,4 +109,13 @@ defineSuite([ }).toThrow(); viewer.destroy(); }); + + it('throws if balloonedObject property already added by another mixin.', function() { + var viewer = new Viewer(container); + viewer.balloonedObject = true; + expect(function() { + viewer.extend(viewerDynamicObjectMixin); + }).toThrow(); + viewer.destroy(); + }); }); \ No newline at end of file From b47f562341d8064d022b6ed57dab41e95165c8a1 Mon Sep 17 00:00:00 2001 From: Matthew Amato Date: Mon, 29 Jul 2013 17:00:47 -0400 Subject: [PATCH 10/25] Generate a simple balloon for GeoJson data to play with. --- Source/DynamicScene/GeoJsonDataSource.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Source/DynamicScene/GeoJsonDataSource.js b/Source/DynamicScene/GeoJsonDataSource.js index c1e2092bb1af..5e9db61fd363 100644 --- a/Source/DynamicScene/GeoJsonDataSource.js +++ b/Source/DynamicScene/GeoJsonDataSource.js @@ -72,6 +72,21 @@ define([ } var dynamicObject = dynamicObjectCollection.getOrCreateObject(id); dynamicObject.geoJson = geoJson; + + //TODO Generate the HTML on demand rather than up-front to save memory and processing time. + var balloonHtml = ''; + var properties = geoJson.properties; + if (typeof properties !== 'undefined') { + balloonHtml = ''; + for ( var key in properties) { + if (properties.hasOwnProperty(key)) { + balloonHtml += ''; + } + } + balloonHtml += '
' + key + '' + properties[key] + '
'; + } + + dynamicObject.balloon = new ConstantProperty(balloonHtml); return dynamicObject; } From 118bd489ea1af9ddf98423106b012eaaf66af39d Mon Sep 17 00:00:00 2001 From: hpinkos Date: Tue, 30 Jul 2013 10:21:05 -0400 Subject: [PATCH 11/25] refactor --- Source/Widgets/Balloon/Balloon.css | 4 +- Source/Widgets/Balloon/BalloonViewModel.js | 76 +++++++++---------- .../Viewer/viewerDynamicObjectMixin.js | 24 +++++- 3 files changed, 62 insertions(+), 42 deletions(-) diff --git a/Source/Widgets/Balloon/Balloon.css b/Source/Widgets/Balloon/Balloon.css index 09527df2552d..b84a43a5f7fe 100644 --- a/Source/Widgets/Balloon/Balloon.css +++ b/Source/Widgets/Balloon/Balloon.css @@ -5,7 +5,9 @@ border-radius: 5px; z-index: 1; border: 1px solid #ccc; - padding: 5px + padding: 5px; + min-height: 28px; + min-width: 28px; } .cesium-balloon-content { diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index c175d7889237..7f4fd22723eb 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -13,7 +13,6 @@ define([ knockout) { "use strict"; - var screenPosition = new Cartesian2(); var pointMin = 0; function shiftPosition(viewModel, position){ @@ -48,7 +47,7 @@ define([ if (bottom) { posX = Math.min(Math.max(posXOffset, posMin), posMaxX); posY = 15; - pointX = Math.min(Math.max(pointXOffset, pointMin), pointMaxX); + pointX = Math.min(Math.max(pointXOffset, pointMin), pointMaxX - 15); pointY = pointMin; viewModel._down = true; viewModel._up = false; @@ -57,7 +56,7 @@ define([ } else if (top) { posX = Math.min(Math.max(posXOffset, posMin), posMaxX); posY = containerHeight - height - 14; - pointX = Math.min(Math.max(pointXOffset, pointMin), pointMaxX); + pointX = Math.min(Math.max(pointXOffset, pointMin), pointMaxX - 15); pointY = pointMaxY; viewModel._down = false; viewModel._up = true; @@ -67,7 +66,7 @@ define([ posX = 15; posY = Math.min(Math.max((position.y - height/2), posMin), posMaxY); pointX = pointMin; - pointY = Math.min(Math.max((position.y - 15), pointMin), pointMaxY); + pointY = Math.min(Math.max((position.y - 16), pointMin), pointMaxY - 15); viewModel._down = false; viewModel._up = false; viewModel._left = true; @@ -76,7 +75,7 @@ define([ posX = containerWidth - width - 15; posY = Math.min(Math.max((position.y - height/2), posMin), posMaxY); pointX = pointMaxX; - pointY = Math.min(Math.max((position.y - 15), pointMin), pointMaxY); + pointY = Math.min(Math.max((position.y - 16), pointMin), pointMaxY - 15); viewModel._down = false; viewModel._up = false; viewModel._left = false; @@ -137,7 +136,7 @@ define([ this._balloonElement = balloonElement; this._contentElement = contentElement; this._content = contentElement.innerHTML; - this._computeScreenPosition = undefined; + this._position = undefined; this._positionX = '0'; this._positionY = '0'; @@ -167,31 +166,25 @@ define([ that._timerRunning = true; setTimeout(function () { that._contentElement.innerHTML = that._content; - if (typeof that._computeScreenPosition === 'function') { - var screenPos = that._computeScreenPosition(); - if (typeof screenPos !== 'undefined') { - var pos = shiftPosition(that, screenPos); - that._pointX = pos.point.x + 'px'; - that._pointY = pos.point.y + 'px'; + if (typeof that._position !== 'undefined') { + var pos = shiftPosition(that, that._position); + that._pointX = pos.point.x + 'px'; + that._pointY = pos.point.y + 'px'; - that._positionX = pos.screen.x + 'px'; - that._positionY = pos.screen.y + 'px'; - } + that._positionX = pos.screen.x + 'px'; + that._positionY = pos.screen.y + 'px'; } that.showBalloon = true; that._timerRunning = false; }, 100); that._updateContent = false; - } else if (typeof that._computeScreenPosition === 'function') { - var screenPos = that._computeScreenPosition(); - if (typeof screenPos !== 'undefined') { - var pos = shiftPosition(that, screenPos); - that._pointX = pos.point.x + 'px'; - that._pointY = pos.point.y + 'px'; + } else if (typeof that._position !== 'undefined') { + var pos = shiftPosition(that, that._position); + that._pointX = pos.point.x + 'px'; + that._pointY = pos.point.y + 'px'; - that._positionX = pos.screen.x + 'px'; - that._positionY = pos.screen.y + 'px'; - } + that._positionX = pos.screen.x + 'px'; + that._positionY = pos.screen.y + 'px'; } } }; @@ -249,6 +242,21 @@ define([ this._contentElement = value; } }, + /** + * Gets or sets the content of the balloon + * @memberof BalloonViewModel.prototype + * + * @type {Element} + */ + content: { + set : function(value) { + if (typeof value === 'undefined') { + throw new DeveloperError('value must defined'); + } + this._content = value; + this._updateContent = true; + } + }, /** * Updates the view of the balloon * @memberof BalloonViewModel.prototype @@ -278,23 +286,15 @@ define([ * * @type {Object} */ - pickObject: { + position: { + get: function() { + return this._position; + }, set: function(value) { - var scene = this._scene; - if (typeof value !== 'undefined' && typeof value.balloon === 'string') { - if (typeof value.computeScreenSpacePosition === 'function') { - this._computeScreenPosition = function() { return value.computeScreenSpacePosition(scene.getContext(), scene.getFrameState()); }; - } else if (typeof value.getPosition === 'function') { - var position = value.getPosition(); - this._computeScreenPosition = function() { return scene.computeScreenSpacePosition( position, screenPosition); }; - } - this._content = value.balloon; - this._updateContent = true; - this.showBalloon = true; + if (typeof value !== 'undefined') { + this._position = value; } - } - } }); diff --git a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js index 3b17566cc58c..ec0708427c75 100644 --- a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js +++ b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js @@ -1,5 +1,6 @@ /*global define*/ define([ + '../../Core/Cartesian2', '../../Core/defaultValue', '../../Core/DeveloperError', '../../Core/defineProperties', @@ -10,6 +11,7 @@ define([ '../Balloon/Balloon', '../../DynamicScene/DynamicObjectView' ], function( + Cartesian2, defaultValue, DeveloperError, defineProperties, @@ -44,6 +46,7 @@ define([ * viewer.trackedObject = dynamicObject; //Camera will now track dynamicObject * viewer.balloonedObject = object; //Balloon will now appear over object */ + var screenPosition = new Cartesian2(); var viewerDynamicObjectMixin = function(viewer) { if (typeof viewer === 'undefined') { throw new DeveloperError('viewer is required.'); @@ -68,10 +71,21 @@ define([ var dynamicObjectView; var balloonedObject; + function computeBalloonPosition(scene, object) { + if (typeof object.computeScreenSpacePosition === 'function') { + return object.computeScreenSpacePosition(scene.getContext(), scene.getFrameState()); + } else if (typeof object.getPosition === 'function') { + return scene.computeScreenSpacePosition(object.getPosition(), screenPosition); + } + } + + //Subscribe to onTick so that we can update the view each update. function updateView(clock) { if (typeof balloonedObject !== 'undefined') { - viewer._balloon.viewModel.update(); + var viewModel = viewer._balloon.viewModel; + viewModel.position = computeBalloonPosition(viewModel.scene, balloonedObject); + viewModel.update(); } if (typeof dynamicObjectView !== 'undefined') { dynamicObjectView.update(clock.currentTime); @@ -148,9 +162,13 @@ define([ return balloonedObject; }, set: function(value) { - if (balloonedObject !== value || !viewer._balloon.viewModel.balloonVisible) { + var viewModel = viewer._balloon.viewModel; + if ((balloonedObject !== value || !viewModel.showBalloon) && (typeof value !== 'undefined' && typeof value.balloon === 'string')) { balloonedObject = value; - viewer._balloon.viewModel.pickObject = balloonedObject; + var scene = viewModel.scene; + viewModel.position = computeBalloonPosition(scene, value); + viewModel.content = balloonedObject.balloon; + viewModel.showBalloon = true; } } } From 24aac76ff15faa2c24796568f6fa65f6c597ce05 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Tue, 30 Jul 2013 10:30:09 -0400 Subject: [PATCH 12/25] fix test --- Source/Widgets/Balloon/BalloonViewModel.js | 2 +- .../Viewer/viewerDynamicObjectMixin.js | 20 +++++++++++-------- .../Viewer/viewerDynamicObjectMixinSpec.js | 1 + 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index 7f4fd22723eb..5b962fa73adc 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -281,7 +281,7 @@ define([ } }, /** - * Sets the object for which to display the balloon + * Sets the screen position for which to display the balloon * @memberof BalloonViewModel * * @type {Object} diff --git a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js index ec0708427c75..6f45d834bfd4 100644 --- a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js +++ b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js @@ -82,8 +82,8 @@ define([ //Subscribe to onTick so that we can update the view each update. function updateView(clock) { - if (typeof balloonedObject !== 'undefined') { - var viewModel = viewer._balloon.viewModel; + var viewModel = viewer._balloon.viewModel; + if (typeof balloonedObject !== 'undefined' && viewModel.showBalloon) { viewModel.position = computeBalloonPosition(viewModel.scene, balloonedObject); viewModel.update(); } @@ -163,13 +163,17 @@ define([ }, set: function(value) { var viewModel = viewer._balloon.viewModel; - if ((balloonedObject !== value || !viewModel.showBalloon) && (typeof value !== 'undefined' && typeof value.balloon === 'string')) { - balloonedObject = value; - var scene = viewModel.scene; - viewModel.position = computeBalloonPosition(scene, value); - viewModel.content = balloonedObject.balloon; - viewModel.showBalloon = true; + if (balloonedObject !== value || !viewModel.showBalloon) { + if (typeof value !== 'undefined' && typeof value.balloon === 'string') { + var scene = viewModel.scene; + viewModel.position = computeBalloonPosition(scene, value); + viewModel.content = value.balloon; + viewModel.showBalloon = true; + } else { + viewModel.showBalloon = false; + } } + balloonedObject = value; } } }); diff --git a/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js b/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js index 79fcec2719dc..484f4998709f 100644 --- a/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js +++ b/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js @@ -64,6 +64,7 @@ defineSuite([ var dynamicObject = new DynamicObject(); dynamicObject.position = new MockProperty(new Cartesian3(123456, 123456, 123456)); + dynamicObject.balloon = 'content'; viewer.balloonedObject = dynamicObject; expect(viewer.balloonedObject).toBe(dynamicObject); From 504d81a4d8c4a3c85450a8bc032ad44fdb7ae8b9 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Tue, 30 Jul 2013 13:10:00 -0400 Subject: [PATCH 13/25] cleanup --- Source/Scene/Scene.js | 4 +- Source/Widgets/Balloon/Balloon.js | 5 +- Source/Widgets/Balloon/BalloonViewModel.js | 78 +++++++++++----------- 3 files changed, 41 insertions(+), 46 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 2c4156dc8c4b..93285b328664 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -695,9 +695,9 @@ define([ /** * Converts a cartesian position to a window position. * - * @param {Cartesian3} position The cartesian position + * @param {Cartesian3} position The cartesian position. * - * @returns {Cartesain2} The screen coordinates of the given position; + * @returns {Cartesian2} The screen coordinates of the given position. */ Scene.prototype.computeScreenSpacePosition = function(position, result) { if (typeof position === 'undefined') { diff --git a/Source/Widgets/Balloon/Balloon.js b/Source/Widgets/Balloon/Balloon.js index 83b51fb426c0..1f4f3a9bcaf7 100644 --- a/Source/Widgets/Balloon/Balloon.js +++ b/Source/Widgets/Balloon/Balloon.js @@ -80,10 +80,6 @@ define([ this._point = point; this._contentWrapper = contentWrapper; - knockout.applyBindings(this._viewModel, this._contentWrapper); - knockout.applyBindings(this._viewModel, this._element); - knockout.applyBindings(this._viewModel, this._pointContainer); - knockout.applyBindings(this._viewModel, this._point); knockout.applyBindings(this._viewModel, this._container); }; @@ -130,6 +126,7 @@ define([ var container = this._container; knockout.cleanNode(container); container.removeChild(this._element); + container.removeChild(this._pointContainer); return destroyObject(this); }; diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index 5b962fa73adc..58cae899960c 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -145,7 +145,14 @@ define([ this._updateContent = false; this._timerRunning = false; + /** + * Determines the visibility of the balloon + * @memberof BalloonViewModel.prototype + * + * @type {Boolean} + */ this.showBalloon = false; + this._down = true; this._up = false; this._left = false; @@ -156,38 +163,41 @@ define([ knockout.track(this, ['showBalloon', '_positionX', '_positionY', '_pointX', '_pointY', '_down', '_up', '_left', '_right', '_maxWidth', '_maxHeight']); + }; - var that = this; - - this._update = function() { - if (!that._timerRunning) { - if (that._updateContent) { - that.showBalloon = false; - that._timerRunning = true; - setTimeout(function () { - that._contentElement.innerHTML = that._content; - if (typeof that._position !== 'undefined') { - var pos = shiftPosition(that, that._position); - that._pointX = pos.point.x + 'px'; - that._pointY = pos.point.y + 'px'; + /** + * Updates the view of the balloon to match the position and content properties of the view model + * @memberof BalloonViewModel + */ + BalloonViewModel.prototype.update = function() { + if (!this._timerRunning) { + if (this._updateContent) { + this.showBalloon = false; + this._timerRunning = true; + var that = this; + setTimeout(function () { + that._contentElement.innerHTML = that._content; + if (typeof that._position !== 'undefined') { + var pos = shiftPosition(that, that._position); + that._pointX = pos.point.x + 'px'; + that._pointY = pos.point.y + 'px'; - that._positionX = pos.screen.x + 'px'; - that._positionY = pos.screen.y + 'px'; - } - that.showBalloon = true; - that._timerRunning = false; - }, 100); - that._updateContent = false; - } else if (typeof that._position !== 'undefined') { - var pos = shiftPosition(that, that._position); - that._pointX = pos.point.x + 'px'; - that._pointY = pos.point.y + 'px'; + that._positionX = pos.screen.x + 'px'; + that._positionY = pos.screen.y + 'px'; + } + that.showBalloon = true; + that._timerRunning = false; + }, 100); + this._updateContent = false; + } else if (typeof this._position !== 'undefined') { + var pos = shiftPosition(this, this._position); + this._pointX = pos.point.x + 'px'; + this._pointY = pos.point.y + 'px'; - that._positionX = pos.screen.x + 'px'; - that._positionY = pos.screen.y + 'px'; - } + this._positionX = pos.screen.x + 'px'; + this._positionY = pos.screen.y + 'px'; } - }; + } }; defineProperties(BalloonViewModel.prototype, { @@ -257,18 +267,6 @@ define([ this._updateContent = true; } }, - /** - * Updates the view of the balloon - * @memberof BalloonViewModel.prototype - * - * @type {Function} - */ - update: { - get: function() { - return this._update; - } - }, - /** * Gets the scene to control. * @memberof BalloonViewModel.prototype From 96e3a7589212c32f3455ac877c083f490fcd488a Mon Sep 17 00:00:00 2001 From: hpinkos Date: Tue, 30 Jul 2013 15:30:52 -0400 Subject: [PATCH 14/25] change position to world coordinate --- Source/Widgets/Balloon/BalloonViewModel.js | 39 +++++++++++++++---- .../Viewer/viewerDynamicObjectMixin.js | 31 +++++++++------ 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index 58cae899960c..cd8858c98037 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -14,6 +14,7 @@ define([ "use strict"; var pointMin = 0; + var screenSpacePos = new Cartesian2(); function shiftPosition(viewModel, position){ var pointX; @@ -137,7 +138,7 @@ define([ this._contentElement = contentElement; this._content = contentElement.innerHTML; this._position = undefined; - + this._computeScreenSpacePosition = scene.computeScreenSpacePosition; this._positionX = '0'; this._positionY = '0'; this._pointX = '0'; @@ -178,7 +179,8 @@ define([ setTimeout(function () { that._contentElement.innerHTML = that._content; if (typeof that._position !== 'undefined') { - var pos = shiftPosition(that, that._position); + var pos = that._computeScreenSpacePosition(that._position, screenSpacePos); + pos = shiftPosition(that, pos); that._pointX = pos.point.x + 'px'; that._pointY = pos.point.y + 'px'; @@ -190,7 +192,8 @@ define([ }, 100); this._updateContent = false; } else if (typeof this._position !== 'undefined') { - var pos = shiftPosition(this, this._position); + var pos = this._computeScreenSpacePosition(this._position, screenSpacePos); + pos = shiftPosition(this, pos); this._pointX = pos.point.x + 'px'; this._pointY = pos.point.y + 'px'; @@ -279,15 +282,35 @@ define([ } }, /** - * Sets the screen position for which to display the balloon + * Sets the function for converting the world position of the object to the screen space position. + * Expects the {Cartesian3} parameter for the position and the optional {Cartesian2} parameter for the result. + * Should return a {Cartesian2}. + * + * Defaults to scene.computeScreenSpacePosition. + * + * @example + * balloonViewModel.computeScreenSpacePosition = function(position, result) { + * return Cartesian2.clone(position, result); + * }; + * * @memberof BalloonViewModel * - * @type {Object} + * @type {Function} + */ + computeScreenSpacePosition: { + set: function(value) { + if (typeof value === 'function') { + this._computeScreenSpacePosition = value; + } + } + }, + /** + * Sets the world position of the object for which to display the balloon. + * @memberof BalloonViewModel + * + * @type {Cartesian3} */ position: { - get: function() { - return this._position; - }, set: function(value) { if (typeof value !== 'undefined') { this._position = value; diff --git a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js index 6f45d834bfd4..d559be8f074f 100644 --- a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js +++ b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js @@ -46,7 +46,7 @@ define([ * viewer.trackedObject = dynamicObject; //Camera will now track dynamicObject * viewer.balloonedObject = object; //Balloon will now appear over object */ - var screenPosition = new Cartesian2(); + var viewerDynamicObjectMixin = function(viewer) { if (typeof viewer === 'undefined') { throw new DeveloperError('viewer is required.'); @@ -71,20 +71,15 @@ define([ var dynamicObjectView; var balloonedObject; - function computeBalloonPosition(scene, object) { - if (typeof object.computeScreenSpacePosition === 'function') { - return object.computeScreenSpacePosition(scene.getContext(), scene.getFrameState()); - } else if (typeof object.getPosition === 'function') { - return scene.computeScreenSpacePosition(object.getPosition(), screenPosition); - } - } - - //Subscribe to onTick so that we can update the view each update. function updateView(clock) { var viewModel = viewer._balloon.viewModel; if (typeof balloonedObject !== 'undefined' && viewModel.showBalloon) { - viewModel.position = computeBalloonPosition(viewModel.scene, balloonedObject); + if (typeof balloonedObject.dynamicObject !== 'undefined') { + viewModel.position = balloonedObject.dynamicObject.position; + } else if (typeof balloonedObject.getPosition === 'function') { + viewModel.position = balloonedObject.getPosition(); + } viewModel.update(); } if (typeof dynamicObjectView !== 'undefined') { @@ -164,9 +159,21 @@ define([ set: function(value) { var viewModel = viewer._balloon.viewModel; if (balloonedObject !== value || !viewModel.showBalloon) { + value.balloon = defaultValue(value.balloon, 'this is some awesome content'); if (typeof value !== 'undefined' && typeof value.balloon === 'string') { var scene = viewModel.scene; - viewModel.position = computeBalloonPosition(scene, value); + if (typeof value.computeScreenSpacePosition === 'function') { + viewModel.computeScreenSpacePosition = function(){ + return value.computeScreenSpacePosition(scene.getContext(), scene.getFrameState()); + }; + } else if (typeof value.dynamicObject !== 'undefined') { + viewModel.computeScreenSpacePosition = scene.computeScreenSpacePosition; + viewModel.position = value.dynamicObject.position; + } else if (typeof value.getPosition === 'function') { + viewModel.computeScreenSpacePosition = scene.computeScreenSpacePosition; + viewModel.position = value.getPosition(); + } + viewModel.content = value.balloon; viewModel.showBalloon = true; } else { From f9bb02b093805deb479c0b46c37261151da8f4fa Mon Sep 17 00:00:00 2001 From: Matthew Amato Date: Wed, 31 Jul 2013 14:46:08 -0400 Subject: [PATCH 15/25] Updates to `viewerDynamicObjectMixin` To have it work properly with DynamicObjects. I also changed the max width/height of the balloon to be much bigger. --- Source/Widgets/Balloon/BalloonViewModel.js | 12 ++-- .../Viewer/viewerDynamicObjectMixin.js | 60 ++++++++----------- .../Viewer/viewerDynamicObjectMixinSpec.js | 14 ++--- 3 files changed, 37 insertions(+), 49 deletions(-) diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index cd8858c98037..1c5bba3da487 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -25,8 +25,8 @@ define([ var containerWidth = viewModel._container.clientWidth; var containerHeight = viewModel._container.clientHeight; - viewModel._maxWidth = Math.floor(viewModel._container.clientWidth*0.25) + 'px'; - viewModel._maxHeight = Math.floor(viewModel._container.clientHeight*0.25) + 'px'; + viewModel._maxWidth = Math.floor(viewModel._container.clientWidth*0.95) + 'px'; + viewModel._maxHeight = Math.floor(viewModel._container.clientHeight*0.50) + 'px'; var pointMaxY = containerHeight - 15; var pointMaxX = containerWidth - 16; @@ -138,7 +138,9 @@ define([ this._contentElement = contentElement; this._content = contentElement.innerHTML; this._position = undefined; - this._computeScreenSpacePosition = scene.computeScreenSpacePosition; + this._computeScreenSpacePosition = function(position, result) { + return scene.computeScreenSpacePosition(position, result); + }; this._positionX = '0'; this._positionY = '0'; this._pointX = '0'; @@ -159,8 +161,8 @@ define([ this._left = false; this._right = false; - this._maxWidth = Math.floor(this._container.clientWidth*0.25) + 'px'; - this._maxHeight = Math.floor(this._container.clientHeight*0.25) + 'px'; + this._maxWidth = Math.floor(this._container.clientWidth*0.95) + 'px'; + this._maxHeight = Math.floor(this._container.clientHeight*0.50) + 'px'; knockout.track(this, ['showBalloon', '_positionX', '_positionY', '_pointX', '_pointY', '_down', '_up', '_left', '_right', '_maxWidth', '_maxHeight']); diff --git a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js index d559be8f074f..3a819cb8bd46 100644 --- a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js +++ b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js @@ -74,13 +74,11 @@ define([ //Subscribe to onTick so that we can update the view each update. function updateView(clock) { var viewModel = viewer._balloon.viewModel; - if (typeof balloonedObject !== 'undefined' && viewModel.showBalloon) { - if (typeof balloonedObject.dynamicObject !== 'undefined') { - viewModel.position = balloonedObject.dynamicObject.position; - } else if (typeof balloonedObject.getPosition === 'function') { - viewModel.position = balloonedObject.getPosition(); - } + if (typeof balloonedObject !== 'undefined' && typeof balloonedObject.position !== 'undefined' && viewModel.showBalloon) { + viewModel.position = balloonedObject.position.getValueCartesian(clock.currentTime); viewModel.update(); + } else { + viewModel.showBalloon = false; } if (typeof dynamicObjectView !== 'undefined') { dynamicObjectView.update(clock.currentTime); @@ -99,13 +97,14 @@ define([ function showBalloon(e) { var pickedPrimitive = viewer.scene.pick(e.position); - if (typeof pickedPrimitive !== 'undefined') { - viewer.balloonedObject = pickedPrimitive; + if (typeof pickedPrimitive !== 'undefined' && typeof pickedPrimitive.dynamicObject !== 'undefined') { + viewer.balloonedObject = pickedPrimitive.dynamicObject; } } function clearTrackedObject() { viewer.trackedObject = undefined; + viewer.balloonedObject = undefined; } //Subscribe to the home button click if it exists, so that we can @@ -131,10 +130,11 @@ define([ set : function(value) { if (trackedObject !== value) { trackedObject = value; - if (typeof balloonedObject !== 'undefined' && trackedObject !== balloonedObject.dynamicObject && typeof trackedObject !== 'undefined') { + dynamicObjectView = typeof value !== 'undefined' ? new DynamicObjectView(value, viewer.scene, viewer.centralBody.getEllipsoid()) : undefined; + + if (balloonedObject !== trackedObject) { viewer._balloon.viewModel.showBalloon = false; } - dynamicObjectView = typeof value !== 'undefined' ? new DynamicObjectView(value, viewer.scene, viewer.centralBody.getEllipsoid()) : undefined; } var sceneMode = viewer.scene.getFrameState().mode; @@ -147,40 +147,32 @@ define([ } } }, + /** * Gets or sets the object instance for which to display a balloon * @memberof viewerDynamicObjectMixin.prototype - * @type {Object} + * @type {DynamicObject} */ - balloonedObject: { - get: function() { + balloonedObject : { + get : function() { return balloonedObject; }, - set: function(value) { + set : function(value) { var viewModel = viewer._balloon.viewModel; - if (balloonedObject !== value || !viewModel.showBalloon) { - value.balloon = defaultValue(value.balloon, 'this is some awesome content'); - if (typeof value !== 'undefined' && typeof value.balloon === 'string') { - var scene = viewModel.scene; - if (typeof value.computeScreenSpacePosition === 'function') { - viewModel.computeScreenSpacePosition = function(){ - return value.computeScreenSpacePosition(scene.getContext(), scene.getFrameState()); - }; - } else if (typeof value.dynamicObject !== 'undefined') { - viewModel.computeScreenSpacePosition = scene.computeScreenSpacePosition; - viewModel.position = value.dynamicObject.position; - } else if (typeof value.getPosition === 'function') { - viewModel.computeScreenSpacePosition = scene.computeScreenSpacePosition; - viewModel.position = value.getPosition(); - } - - viewModel.content = value.balloon; - viewModel.showBalloon = true; + var content; + if (typeof value !== 'undefined') { + if (typeof value.dynamicObject !== 'undefined' && typeof value.dynamicObject.position !== 'undefined') { + viewModel.position = value.dynamicObject.position.getValue(viewer.clock.currentTime); + } + if (typeof value.balloon !== 'undefined') { + content = value.balloon.getValue(viewer.clock.currentTime); } else { - viewModel.showBalloon = false; + content = value.id; } + viewModel.content = content; } balloonedObject = value; + viewModel.showBalloon = typeof content !== 'undefined'; } } }); @@ -188,7 +180,7 @@ define([ //Wrap destroy to clean up event subscriptions. viewer.destroy = wrapFunction(viewer, viewer.destroy, function() { eventHelper.removeAll(); - + balloon.destroy(); viewer.screenSpaceEventHandler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK); viewer.screenSpaceEventHandler.removeInputAction(ScreenSpaceEventType.RIGHT_CLICK); }); diff --git a/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js b/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js index 484f4998709f..1ef2a6282f04 100644 --- a/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js +++ b/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js @@ -28,10 +28,11 @@ defineSuite([ document.body.removeChild(container); }); - it('adds trackedObject property', function() { + it('adds proeprties', function() { var viewer = new Viewer(container); viewer.extend(viewerDynamicObjectMixin); - expect(viewer.trackedObject).toBeUndefined(); + expect(viewer.hasOwnProperty('trackedObject')).toEqual(true); + expect(viewer.hasOwnProperty('balloonedObject')).toEqual(true); viewer.destroy(); }); @@ -51,20 +52,13 @@ defineSuite([ viewer.destroy(); }); - it('adds balloonedObject property', function() { - var viewer = new Viewer(container); - viewer.extend(viewerDynamicObjectMixin); - expect(viewer.balloonedObject).toBeUndefined(); - viewer.destroy(); - }); - it('can get and set balloonedObject', function() { var viewer = new Viewer(container); viewer.extend(viewerDynamicObjectMixin); var dynamicObject = new DynamicObject(); dynamicObject.position = new MockProperty(new Cartesian3(123456, 123456, 123456)); - dynamicObject.balloon = 'content'; + dynamicObject.balloon = new MockProperty('content'); viewer.balloonedObject = dynamicObject; expect(viewer.balloonedObject).toBe(dynamicObject); From 763e2f1812c81b71c3660c5a87c794bd4603d607 Mon Sep 17 00:00:00 2001 From: Matthew Amato Date: Wed, 31 Jul 2013 14:55:19 -0400 Subject: [PATCH 16/25] Fix typo. --- Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js b/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js index 1ef2a6282f04..30a5f7064678 100644 --- a/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js +++ b/Specs/Widgets/Viewer/viewerDynamicObjectMixinSpec.js @@ -28,7 +28,7 @@ defineSuite([ document.body.removeChild(container); }); - it('adds proeprties', function() { + it('adds properties', function() { var viewer = new Viewer(container); viewer.extend(viewerDynamicObjectMixin); expect(viewer.hasOwnProperty('trackedObject')).toEqual(true); From 28d9ee90e33b6723d5bc8d167a742040d77cfbda Mon Sep 17 00:00:00 2001 From: hpinkos Date: Wed, 31 Jul 2013 16:26:50 -0400 Subject: [PATCH 17/25] Fix computeScreenSpacePosition for 3D --- Source/Scene/Scene.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index aa8f7105e0e3..0bd60fc01544 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -792,9 +792,13 @@ define([ return undefined; } - var uniformState = this._context.getUniformState(); - var modelViewProjectionMatrix = uniformState.getModelViewProjection(); - var viewportTransformation = uniformState.getViewportTransformation(); + var camera = this._camera; + var viewMatrix = camera.getViewMatrix(); + var projectionMatrix = camera.frustum.getProjectionMatrix(); + var modelViewProjectionMatrix = Matrix4.multiply(projectionMatrix, viewMatrix); + var canvas = this._canvas; + var viewportTransformation = Matrix4.computeViewportTransformation( + new BoundingRectangle(0.0, 0.0, canvas.clientWidth, canvas.clientHeight), 0.0, 1.0); var point = Transforms.pointToWindowCoordinates(modelViewProjectionMatrix, viewportTransformation, position, result); return point; From 959790c5dc7f50b9e9d1c4518982536ae74a9cc2 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Fri, 2 Aug 2013 17:48:57 -0400 Subject: [PATCH 18/25] fix balloon position in 2D and CV --- Source/Scene/Scene.js | 24 ------------------- Source/Widgets/Balloon/BalloonViewModel.js | 4 +++- .../Viewer/viewerDynamicObjectMixin.js | 14 +++++++---- 3 files changed, 12 insertions(+), 30 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 330616c3ea07..96e97f8dc3cb 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -784,30 +784,6 @@ define([ return this._pickFramebuffer.end(scratchRectangle); }; - /** - * Converts a cartesian position to a window position. - * - * @param {Cartesian3} position The cartesian position. - * - * @returns {Cartesian2} The screen coordinates of the given position. - */ - Scene.prototype.computeScreenSpacePosition = function(position, result) { - if (typeof position === 'undefined') { - return undefined; - } - - var camera = this._camera; - var viewMatrix = camera.getViewMatrix(); - var projectionMatrix = camera.frustum.getProjectionMatrix(); - var modelViewProjectionMatrix = Matrix4.multiply(projectionMatrix, viewMatrix); - var canvas = this._canvas; - var viewportTransformation = Matrix4.computeViewportTransformation( - new BoundingRectangle(0.0, 0.0, canvas.clientWidth, canvas.clientHeight), 0.0, 1.0); - var point = Transforms.pointToWindowCoordinates(modelViewProjectionMatrix, viewportTransformation, position, result); - - return point; - }; - /** * DOC_TBA * @memberof Scene diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index 1c5bba3da487..c4748bd52ac9 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -4,12 +4,14 @@ define([ '../../Core/defaultValue', '../../Core/defineProperties', '../../Core/DeveloperError', + '../../Scene/SceneTransforms', '../../ThirdParty/knockout' ], function( Cartesian2, defaultValue, defineProperties, DeveloperError, + SceneTransforms, knockout) { "use strict"; @@ -139,7 +141,7 @@ define([ this._content = contentElement.innerHTML; this._position = undefined; this._computeScreenSpacePosition = function(position, result) { - return scene.computeScreenSpacePosition(position, result); + return SceneTransforms.wgs84ToWindowCoordinates(scene, position, result); }; this._positionX = '0'; this._positionY = '0'; diff --git a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js index 3a819cb8bd46..b05c38c94f9b 100644 --- a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js +++ b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js @@ -73,16 +73,17 @@ define([ //Subscribe to onTick so that we can update the view each update. function updateView(clock) { + if (typeof dynamicObjectView !== 'undefined') { + dynamicObjectView.update(clock.currentTime); + } var viewModel = viewer._balloon.viewModel; - if (typeof balloonedObject !== 'undefined' && typeof balloonedObject.position !== 'undefined' && viewModel.showBalloon) { + if (typeof trackedObject === 'undefined' && typeof balloonedObject !== 'undefined' && + typeof balloonedObject.position !== 'undefined' && viewModel.showBalloon) { viewModel.position = balloonedObject.position.getValueCartesian(clock.currentTime); viewModel.update(); } else { viewModel.showBalloon = false; } - if (typeof dynamicObjectView !== 'undefined') { - dynamicObjectView.update(clock.currentTime); - } } eventHelper.add(viewer.clock.onTick, updateView); @@ -169,7 +170,10 @@ define([ } else { content = value.id; } - viewModel.content = content; + + if (value !== balloonedObject) { + viewModel.content = content; + } } balloonedObject = value; viewModel.showBalloon = typeof content !== 'undefined'; From d03cd330d3a85091e523daf2a619df6f6e08f1c5 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Mon, 5 Aug 2013 10:19:19 -0400 Subject: [PATCH 19/25] comments --- Source/Widgets/Balloon/BalloonViewModel.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index c4748bd52ac9..16d26689302b 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -180,6 +180,7 @@ define([ this.showBalloon = false; this._timerRunning = true; var that = this; + //timeout needed so that re-positioning occurs after showBalloon=false transition is complete setTimeout(function () { that._contentElement.innerHTML = that._content; if (typeof that._position !== 'undefined') { @@ -290,7 +291,7 @@ define([ * Expects the {Cartesian3} parameter for the position and the optional {Cartesian2} parameter for the result. * Should return a {Cartesian2}. * - * Defaults to scene.computeScreenSpacePosition. + * Defaults to SceneTransforms.wgs84ToWindowCoordinates * * @example * balloonViewModel.computeScreenSpacePosition = function(position, result) { From 075433daaa100031ffbd4a0f3976f90d7d949ddb Mon Sep 17 00:00:00 2001 From: hpinkos Date: Mon, 5 Aug 2013 11:33:33 -0400 Subject: [PATCH 20/25] cleanup --- Source/Scene/Scene.js | 2 -- Source/Widgets/Viewer/viewerDynamicObjectMixin.js | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 96e97f8dc3cb..482e60472584 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -19,7 +19,6 @@ define([ '../Core/GeometryInstance', '../Core/GeometryPipeline', '../Core/ColorGeometryInstanceAttribute', - '../Core/Transforms', '../Renderer/Context', '../Renderer/ClearCommand', '../Renderer/PassState', @@ -57,7 +56,6 @@ define([ GeometryInstance, GeometryPipeline, ColorGeometryInstanceAttribute, - Transforms, Context, ClearCommand, PassState, diff --git a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js index bae2d56edfad..a9e4f5f57677 100644 --- a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js +++ b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js @@ -41,7 +41,8 @@ define([ * // Add support for working with DynamicObject instances to the Viewer. * var dynamicObject = ... //A Cesium.DynamicObject instance * var viewer = new Cesium.Viewer('cesiumContainer'); - * viewer.extend(Cesium.viewerDynamicObjectMixin); * viewer.trackedObject = dynamicObject; //Camera will now track dynamicObject + * viewer.extend(Cesium.viewerDynamicObjectMixin); + * viewer.trackedObject = dynamicObject; //Camera will now track dynamicObject * viewer.balloonedObject = object; //Balloon will now appear over object */ From a767ede33dcb4db007cb032f0ab622fd9d1d3012 Mon Sep 17 00:00:00 2001 From: Matthew Amato Date: Tue, 6 Aug 2013 13:40:37 -0400 Subject: [PATCH 21/25] Clean up viewer use of Balloon 1. Improve GeoJsonDataSource balloon. 2. Make balloon work in all cases in the Viewer. 3. Make the balloon go away when data goes away. 4. Add Balloon.css to widgets.css 5. Misc clean up to viewerDynamicObjectMixin --- Source/DynamicScene/GeoJsonDataSource.css | 7 +++ Source/DynamicScene/GeoJsonDataSource.js | 35 ++++++----- Source/Widgets/Balloon/BalloonViewModel.js | 4 +- Source/Widgets/Viewer/Viewer.css | 1 + Source/Widgets/Viewer/viewerDragDropMixin.js | 8 +++ .../Viewer/viewerDynamicObjectMixin.js | 58 +++++++++---------- Source/Widgets/widgets.css | 4 +- 7 files changed, 69 insertions(+), 48 deletions(-) create mode 100644 Source/DynamicScene/GeoJsonDataSource.css diff --git a/Source/DynamicScene/GeoJsonDataSource.css b/Source/DynamicScene/GeoJsonDataSource.css new file mode 100644 index 000000000000..1f46387a4bd7 --- /dev/null +++ b/Source/DynamicScene/GeoJsonDataSource.css @@ -0,0 +1,7 @@ +.geoJsonDataSourceTable tr:nth-child(even) { + background: #EEEEEE; +} + +.geoJsonDataSourceTable tr:nth-child(odd) { + background: #CCCCCC; +} \ No newline at end of file diff --git a/Source/DynamicScene/GeoJsonDataSource.js b/Source/DynamicScene/GeoJsonDataSource.js index 5e9db61fd363..5e8d90abddc6 100644 --- a/Source/DynamicScene/GeoJsonDataSource.js +++ b/Source/DynamicScene/GeoJsonDataSource.js @@ -54,6 +54,13 @@ define([ return value; }; + ConstantPositionProperty.prototype.getValueCartographic = function(time, result) { + if (Array.isArray(this._value)) { + return Ellipsoid.WGS84.cartesianArrayToCartographicArray(this._value, result); + } + return Ellipsoid.WGS84.cartesianToCartographic(this._value, result); + }; + //GeoJSON specifies only the Feature object has a usable id property //But since "multi" geometries create multiple dynamicObject, //we can't use it for them either. @@ -70,23 +77,25 @@ define([ } id = finalId; } + var dynamicObject = dynamicObjectCollection.getOrCreateObject(id); dynamicObject.geoJson = geoJson; - - //TODO Generate the HTML on demand rather than up-front to save memory and processing time. - var balloonHtml = ''; - var properties = geoJson.properties; - if (typeof properties !== 'undefined') { - balloonHtml = ''; - for ( var key in properties) { - if (properties.hasOwnProperty(key)) { - balloonHtml += ''; + dynamicObject.balloon = { + getValue : function() { + var html; + var properties = geoJson.properties; + if (typeof properties !== 'undefined') { + html = '
' + key + '' + properties[key] + '
'; + for ( var key in properties) { + if (properties.hasOwnProperty(key)) { + html += ''; + } + } + html += '
' + key + '' + properties[key] + '
'; } + return html; } - balloonHtml += ''; - } - - dynamicObject.balloon = new ConstantProperty(balloonHtml); + }; return dynamicObject; } diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index 16d26689302b..7f524c248fdb 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -27,8 +27,8 @@ define([ var containerWidth = viewModel._container.clientWidth; var containerHeight = viewModel._container.clientHeight; - viewModel._maxWidth = Math.floor(viewModel._container.clientWidth*0.95) + 'px'; - viewModel._maxHeight = Math.floor(viewModel._container.clientHeight*0.50) + 'px'; + viewModel._maxWidth = Math.floor(viewModel._container.clientWidth * 0.50) + 'px'; + viewModel._maxHeight = Math.floor(viewModel._container.clientHeight * 0.50) + 'px'; var pointMaxY = containerHeight - 15; var pointMaxX = containerWidth - 16; diff --git a/Source/Widgets/Viewer/Viewer.css b/Source/Widgets/Viewer/Viewer.css index f5b682b0d776..b4b6fb3eca69 100644 --- a/Source/Widgets/Viewer/Viewer.css +++ b/Source/Widgets/Viewer/Viewer.css @@ -6,6 +6,7 @@ @import url(../HomeButton/HomeButton.css); @import url(../SceneModePicker/SceneModePicker.css); @import url(../Timeline/Timeline.css); +@import url(../../DynamicScene/GeoJsonDataSource.css); .cesium-viewer { font-family: sans-serif; diff --git a/Source/Widgets/Viewer/viewerDragDropMixin.js b/Source/Widgets/Viewer/viewerDragDropMixin.js index a0ec246f33c8..0be7d4334da6 100644 --- a/Source/Widgets/Viewer/viewerDragDropMixin.js +++ b/Source/Widgets/Viewer/viewerDragDropMixin.js @@ -149,6 +149,14 @@ define([ if (clearOnDrop) { viewer.dataSources.removeAll(); + + if (viewer.hasOwnProperty('balloonedObject')) { + viewer.balloonedObject = undefined; + } + + if (viewer.hasOwnProperty('trackedObject')) { + viewer.trackedObject = undefined; + } } var files = event.dataTransfer.files; diff --git a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js index a9e4f5f57677..3c13bd2c9c46 100644 --- a/Source/Widgets/Viewer/viewerDynamicObjectMixin.js +++ b/Source/Widgets/Viewer/viewerDynamicObjectMixin.js @@ -61,8 +61,9 @@ define([ var balloonContainer = document.createElement('div'); balloonContainer.className = 'cesium-viewer-balloonContainer'; viewer._viewerContainer.appendChild(balloonContainer); - var balloon = new Balloon(balloonContainer, viewer.scene); + var balloon = new Balloon(balloonContainer, viewer.scene); + var balloonViewModel = balloon.viewModel; viewer._balloon = balloon; var eventHelper = new EventHelper(); @@ -71,50 +72,44 @@ define([ var balloonedObject; //Subscribe to onTick so that we can update the view each update. - function updateView(clock) { + function onTick(clock) { if (typeof dynamicObjectView !== 'undefined') { dynamicObjectView.update(clock.currentTime); } - var viewModel = viewer._balloon.viewModel; - if (typeof trackedObject === 'undefined' && typeof balloonedObject !== 'undefined' && - typeof balloonedObject.position !== 'undefined' && viewModel.showBalloon) { - viewModel.position = balloonedObject.position.getValueCartesian(clock.currentTime); - viewModel.update(); - } else { - viewModel.showBalloon = false; + if (typeof balloonedObject !== 'undefined' && typeof balloonedObject.position !== 'undefined') { + balloonViewModel.position = balloonedObject.position.getValueCartesian(clock.currentTime); + balloonViewModel.update(); } } - eventHelper.add(viewer.clock.onTick, updateView); + eventHelper.add(viewer.clock.onTick, onTick); function pickAndTrackObject(e) { var pickedPrimitive = viewer.scene.pick(e.position); - if (typeof pickedPrimitive !== 'undefined' && - typeof pickedPrimitive.dynamicObject !== 'undefined' && - typeof pickedPrimitive.dynamicObject.position !== 'undefined') { + if (typeof pickedPrimitive !== 'undefined' && typeof pickedPrimitive.dynamicObject !== 'undefined' && typeof pickedPrimitive.dynamicObject.position !== 'undefined') { viewer.trackedObject = pickedPrimitive.dynamicObject; } } - function showBalloon(e) { + function pickAndShowBalloon(e) { var pickedPrimitive = viewer.scene.pick(e.position); if (typeof pickedPrimitive !== 'undefined' && typeof pickedPrimitive.dynamicObject !== 'undefined') { viewer.balloonedObject = pickedPrimitive.dynamicObject; } } - function clearTrackedObject() { + function onHomeButtonClicked() { viewer.trackedObject = undefined; viewer.balloonedObject = undefined; } - //Subscribe to the home button click if it exists, so that we can - //clear the trackedObject when it is clicked. + //Subscribe to the home button beforeExecute event if it exists, + // so that we can clear the trackedObject and balloon. if (typeof viewer.homeButton !== 'undefined') { - eventHelper.add(viewer.homeButton.viewModel.command.beforeExecute, clearTrackedObject); + eventHelper.add(viewer.homeButton.viewModel.command.beforeExecute, onHomeButtonClicked); } //Subscribe to left clicks and zoom to the picked object. - viewer.screenSpaceEventHandler.setInputAction(showBalloon, ScreenSpaceEventType.LEFT_CLICK); + viewer.screenSpaceEventHandler.setInputAction(pickAndShowBalloon, ScreenSpaceEventType.LEFT_CLICK); viewer.screenSpaceEventHandler.setInputAction(pickAndTrackObject, ScreenSpaceEventType.RIGHT_CLICK); defineProperties(viewer, { @@ -131,13 +126,11 @@ define([ if (trackedObject !== value) { trackedObject = value; dynamicObjectView = typeof value !== 'undefined' ? new DynamicObjectView(value, viewer.scene, viewer.centralBody.getEllipsoid()) : undefined; - - if (balloonedObject !== trackedObject) { - viewer._balloon.viewModel.showBalloon = false; - } + //Hide the balloon if it's not the object we are following. + balloonViewModel.showBalloon = balloonedObject === trackedObject; } - var sceneMode = viewer.scene.getFrameState().mode; + var sceneMode = viewer.scene.getFrameState().mode; if (sceneMode === SceneMode.COLUMBUS_VIEW || sceneMode === SceneMode.SCENE2D) { viewer.scene.getScreenSpaceCameraController().enableTranslate = typeof value === 'undefined'; } @@ -158,24 +151,25 @@ define([ return balloonedObject; }, set : function(value) { - var viewModel = viewer._balloon.viewModel; var content; + var position; if (typeof value !== 'undefined') { - if (typeof value.dynamicObject !== 'undefined' && typeof value.dynamicObject.position !== 'undefined') { - viewModel.position = value.dynamicObject.position.getValue(viewer.clock.currentTime); + if (typeof value.position !== 'undefined') { + position = value.position.getValueCartesian(viewer.clock.currentTime); } + if (typeof value.balloon !== 'undefined') { content = value.balloon.getValue(viewer.clock.currentTime); - } else { - content = value.id; } - if (value !== balloonedObject) { - viewModel.content = content; + if (typeof content === 'undefined') { + content = value.id; } + balloonViewModel.content = content; } balloonedObject = value; - viewModel.showBalloon = typeof content !== 'undefined'; + balloonViewModel.position = position; + balloonViewModel.showBalloon = typeof content !== 'undefined'; } } }); diff --git a/Source/Widgets/widgets.css b/Source/Widgets/widgets.css index c03114a1676a..b511ae10cba2 100644 --- a/Source/Widgets/widgets.css +++ b/Source/Widgets/widgets.css @@ -1,4 +1,5 @@ @import url(./Animation/Animation.css); +@import url(./Balloon/Balloon.css); @import url(./BaseLayerPicker/BaseLayerPicker.css); @import url(./CesiumWidget/CesiumWidget.css); @import url(./checkForChromeFrame.css); @@ -6,4 +7,5 @@ @import url(./HomeButton/HomeButton.css); @import url(./SceneModePicker/SceneModePicker.css); @import url(./Timeline/Timeline.css); -@import url(./Viewer/Viewer.css); \ No newline at end of file +@import url(./Viewer/Viewer.css); +@import url(../DynamicScene/GeoJsonDataSource.css); From 7c0132fcfc9d73df970c3b9205be17bd2cbc7589 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Tue, 6 Aug 2013 14:08:05 -0400 Subject: [PATCH 22/25] default position --- Source/Widgets/Balloon/Balloon.css | 8 ++ Source/Widgets/Balloon/Balloon.js | 3 +- Source/Widgets/Balloon/BalloonViewModel.js | 158 +++++++++++++-------- 3 files changed, 112 insertions(+), 57 deletions(-) diff --git a/Source/Widgets/Balloon/Balloon.css b/Source/Widgets/Balloon/Balloon.css index b84a43a5f7fe..b3c5ad86d8d3 100644 --- a/Source/Widgets/Balloon/Balloon.css +++ b/Source/Widgets/Balloon/Balloon.css @@ -79,6 +79,14 @@ -moz-transition: visibility 0s 0.2s, opacity 0.2s linear; } +.cesium-balloon-point-show{ + visibility: visible; +} + +.cesium-balloon-point-hide{ + visibility: hidden; +} + .cesium-balloon-close { position: absolute; top: 5px; diff --git a/Source/Widgets/Balloon/Balloon.js b/Source/Widgets/Balloon/Balloon.js index 1f4f3a9bcaf7..eeab2f95ab7f 100644 --- a/Source/Widgets/Balloon/Balloon.js +++ b/Source/Widgets/Balloon/Balloon.js @@ -61,7 +61,8 @@ define([ var pointContainer = document.createElement('div'); pointContainer.className = 'cesium-balloon-point-container'; pointContainer.setAttribute('data-bind', - 'css: { "cesium-balloon-point-container-downup" : _down || _up, "cesium-balloon-point-container-leftright" : _left || _right},\ + 'css: { "cesium-balloon-point-container-downup" : _down || _up, "cesium-balloon-point-container-leftright" : _left || _right,\ + "cesium-balloon-point-show" : showBalloon && showPoint, "cesium-balloon-point-hide" : !showBalloon || !showPoint},\ style: { "bottom" : _pointY, "left" : _pointX}'); var point = document.createElement('div'); point.className = 'cesium-balloon-point'; diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index 16d26689302b..0de557286e47 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -29,7 +29,6 @@ define([ viewModel._maxWidth = Math.floor(viewModel._container.clientWidth*0.95) + 'px'; viewModel._maxHeight = Math.floor(viewModel._container.clientHeight*0.50) + 'px'; - var pointMaxY = containerHeight - 15; var pointMaxX = containerWidth - 16; var pointXOffset = position.x - 15; @@ -47,51 +46,70 @@ define([ var left = position.x < 0; var right = position.x > containerWidth; - if (bottom) { - posX = Math.min(Math.max(posXOffset, posMin), posMaxX); - posY = 15; - pointX = Math.min(Math.max(pointXOffset, pointMin), pointMaxX - 15); - pointY = pointMin; - viewModel._down = true; - viewModel._up = false; - viewModel._left = false; - viewModel._right = false; - } else if (top) { - posX = Math.min(Math.max(posXOffset, posMin), posMaxX); - posY = containerHeight - height - 14; - pointX = Math.min(Math.max(pointXOffset, pointMin), pointMaxX - 15); - pointY = pointMaxY; - viewModel._down = false; - viewModel._up = true; - viewModel._left = false; - viewModel._right = false; - } else if (left) { - posX = 15; - posY = Math.min(Math.max((position.y - height/2), posMin), posMaxY); - pointX = pointMin; - pointY = Math.min(Math.max((position.y - 16), pointMin), pointMaxY - 15); - viewModel._down = false; - viewModel._up = false; - viewModel._left = true; - viewModel._right = false; - } else if (right) { - posX = containerWidth - width - 15; - posY = Math.min(Math.max((position.y - height/2), posMin), posMaxY); - pointX = pointMaxX; - pointY = Math.min(Math.max((position.y - 16), pointMin), pointMaxY - 15); - viewModel._down = false; - viewModel._up = false; - viewModel._left = false; - viewModel._right = true; + if (viewModel.showPoint) { + if (bottom) { + posX = Math.min(Math.max(posXOffset, posMin), posMaxX); + posY = 15; + pointX = Math.min(Math.max(pointXOffset, pointMin), pointMaxX - 15); + pointY = pointMin; + viewModel._down = true; + viewModel._up = false; + viewModel._left = false; + viewModel._right = false; + } else if (top) { + posX = Math.min(Math.max(posXOffset, posMin), posMaxX); + posY = containerHeight - height - 14; + pointX = Math.min(Math.max(pointXOffset, pointMin), pointMaxX - 15); + pointY = pointMaxY; + viewModel._down = false; + viewModel._up = true; + viewModel._left = false; + viewModel._right = false; + } else if (left) { + posX = 15; + posY = Math.min(Math.max((position.y - height/2), posMin), posMaxY); + pointX = pointMin; + pointY = Math.min(Math.max((position.y - 16), pointMin), pointMaxY - 15); + viewModel._down = false; + viewModel._up = false; + viewModel._left = true; + viewModel._right = false; + } else if (right) { + posX = containerWidth - width - 15; + posY = Math.min(Math.max((position.y - height/2), posMin), posMaxY); + pointX = pointMaxX; + pointY = Math.min(Math.max((position.y - 16), pointMin), pointMaxY - 15); + viewModel._down = false; + viewModel._up = false; + viewModel._left = false; + viewModel._right = true; + } else { + posX = Math.min(Math.max(posXOffset, posMin), posMaxX); + posY = Math.min(Math.max((position.y + 25), posMin), posMaxY); + pointX = pointXOffset; + pointY = Math.min(position.y + 10, posMaxY - 15); + viewModel._down = true; + viewModel._up = false; + viewModel._left = false; + viewModel._right = false; + } } else { - posX = Math.min(Math.max(posXOffset, posMin), posMaxX); - posY = Math.min(Math.max((position.y + 25), posMin), posMaxY); - pointX = pointXOffset; - pointY = Math.min(position.y + 10, posMaxY - 15); - viewModel._down = true; - viewModel._up = false; - viewModel._left = false; - viewModel._right = false; + if (bottom) { + posX = Math.min(Math.max(posXOffset, posMin), posMaxX); + posY = 0; + } else if (top) { + posX = Math.min(Math.max(posXOffset, posMin), posMaxX); + posY = containerHeight - height; + } else if (left) { + posX = 0; + posY = Math.min(Math.max((position.y - height/2), posMin), posMaxY); + } else if (right) { + posX = containerWidth - width; + posY = Math.min(Math.max((position.y - height/2), posMin), posMaxY); + } else { + posX = Math.min(Math.max(posXOffset, posMin), posMaxX); + posY = Math.min(Math.max(position.y, posMin), posMaxY); + } } return { @@ -149,6 +167,7 @@ define([ this._pointY = '0'; this._updateContent = false; this._timerRunning = false; + this._defaultPosition = {x: this._container.clientWidth, y: this._container.clientHeight/2}; /** * Determines the visibility of the balloon @@ -158,6 +177,14 @@ define([ */ this.showBalloon = false; + /** + * Determines the visibility of the balloon point if the balloon is visible + * @memberof BalloonViewModel.prototype + * + * @type {Boolean} + */ + this.showPoint = true; + this._down = true; this._up = false; this._left = false; @@ -166,7 +193,7 @@ define([ this._maxWidth = Math.floor(this._container.clientWidth*0.95) + 'px'; this._maxHeight = Math.floor(this._container.clientHeight*0.50) + 'px'; - knockout.track(this, ['showBalloon', '_positionX', '_positionY', '_pointX', '_pointY', + knockout.track(this, ['showPoint', 'showBalloon', '_positionX', '_positionY', '_pointX', '_pointY', '_down', '_up', '_left', '_right', '_maxWidth', '_maxHeight']); }; @@ -196,8 +223,16 @@ define([ that._timerRunning = false; }, 100); this._updateContent = false; - } else if (typeof this._position !== 'undefined') { - var pos = this._computeScreenSpacePosition(this._position, screenSpacePos); + } else if (this.showBalloon) { + var pos; + if (typeof this._position !== 'undefined'){ + pos = this._computeScreenSpacePosition(this._position, screenSpacePos); + this.showPoint = true; + } else { + pos = this._defaultPosition; + this.showPoint = false; + } + pos = shiftPosition(this, pos); this._pointX = pos.point.x + 'px'; this._pointY = pos.point.y + 'px'; @@ -269,9 +304,10 @@ define([ content: { set : function(value) { if (typeof value === 'undefined') { - throw new DeveloperError('value must defined'); + this._content = ''; + } else { + this._content = value; } - this._content = value; this._updateContent = true; } }, @@ -286,6 +322,20 @@ define([ return this._scene; } }, + /** + * Sets the default position of the balloon. + * @memberof BalloonViewModel.prototype + * + * @type {Cartesain2} + */ + defaultPosition : { + set : function(value) { + if (typeof value !== 'undefined') { + this._defaultPosition.x = value.x; + this._defaultPosition.y = value.y; + } + } + }, /** * Sets the function for converting the world position of the object to the screen space position. * Expects the {Cartesian3} parameter for the position and the optional {Cartesian2} parameter for the result. @@ -304,9 +354,7 @@ define([ */ computeScreenSpacePosition: { set: function(value) { - if (typeof value === 'function') { - this._computeScreenSpacePosition = value; - } + this._computeScreenSpacePosition = value; } }, /** @@ -317,9 +365,7 @@ define([ */ position: { set: function(value) { - if (typeof value !== 'undefined') { - this._position = value; - } + this._position = value; } } }); From 500e6c7b097c816ffe502941ce471d2fe097c6f1 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Tue, 6 Aug 2013 16:06:45 -0400 Subject: [PATCH 23/25] databind html content --- Source/Widgets/Balloon/Balloon.js | 5 ++-- Source/Widgets/Balloon/BalloonViewModel.js | 33 ++++------------------ 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/Source/Widgets/Balloon/Balloon.js b/Source/Widgets/Balloon/Balloon.js index eeab2f95ab7f..3c52492f7e67 100644 --- a/Source/Widgets/Balloon/Balloon.js +++ b/Source/Widgets/Balloon/Balloon.js @@ -58,11 +58,12 @@ define([ this._content = document.createElement('div'); contentWrapper.appendChild(this._content); + this._content.setAttribute('data-bind', 'html: _contentHTML'); var pointContainer = document.createElement('div'); pointContainer.className = 'cesium-balloon-point-container'; pointContainer.setAttribute('data-bind', 'css: { "cesium-balloon-point-container-downup" : _down || _up, "cesium-balloon-point-container-leftright" : _left || _right,\ - "cesium-balloon-point-show" : showBalloon && showPoint, "cesium-balloon-point-hide" : !showBalloon || !showPoint},\ + "cesium-balloon-point-show" : showPoint, "cesium-balloon-point-hide" : !showPoint},\ style: { "bottom" : _pointY, "left" : _pointX}'); var point = document.createElement('div'); point.className = 'cesium-balloon-point'; @@ -74,7 +75,7 @@ define([ pointContainer.appendChild(point); container.appendChild(pointContainer); - var viewModel = new BalloonViewModel(scene, this._content, this._element, this._container); + var viewModel = new BalloonViewModel(scene, this._element, this._container); this._viewModel = viewModel; this._pointContainer = pointContainer; diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index 25b1f7096afa..7a054b8574dc 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -130,24 +130,18 @@ define([ * @constructor * * @param {Scene} scene The scene instance to use. - * @param {Element} contentElement The element in which to display balloon content. * @param {Element} balloonElement The element containing all elements that make up the balloon. * @param {Element} [container = document.body] The element containing the balloon. * * @exception {DeveloperError} scene is required. - * @exception {DeveloperError} contentElement is required. * @exception {DeveloperError} balloonElement is required. * */ - var BalloonViewModel = function(scene, contentElement, balloonElement, container) { + var BalloonViewModel = function(scene, balloonElement, container) { if (typeof scene === 'undefined') { throw new DeveloperError('scene is required.'); } - if (typeof contentElement === 'undefined') { - throw new DeveloperError('contentElement is required.'); - } - if (typeof balloonElement === 'undefined') { throw new DeveloperError('balloonElement is required.'); } @@ -155,8 +149,8 @@ define([ this._scene = scene; this._container = defaultValue(container, document.body); this._balloonElement = balloonElement; - this._contentElement = contentElement; - this._content = contentElement.innerHTML; + this._content = ''; + this._contentHTML = ''; this._position = undefined; this._computeScreenSpacePosition = function(position, result) { return SceneTransforms.wgs84ToWindowCoordinates(scene, position, result); @@ -194,7 +188,7 @@ define([ this._maxHeight = Math.floor(this._container.clientHeight*0.50) + 'px'; knockout.track(this, ['showPoint', 'showBalloon', '_positionX', '_positionY', '_pointX', '_pointY', - '_down', '_up', '_left', '_right', '_maxWidth', '_maxHeight']); + '_down', '_up', '_left', '_right', '_maxWidth', '_maxHeight', '_contentHTML']); }; /** @@ -209,7 +203,7 @@ define([ var that = this; //timeout needed so that re-positioning occurs after showBalloon=false transition is complete setTimeout(function () { - that._contentElement.innerHTML = that._content; + that._contentHTML = that._content; if (typeof that._position !== 'undefined') { var pos = that._computeScreenSpacePosition(that._position, screenSpacePos); pos = shiftPosition(that, pos); @@ -278,23 +272,6 @@ define([ this._balloonElement = value; } }, - /** - * Gets or sets the HTML element that displays the content of the balloon - * @memberof BalloonViewModel.prototype - * - * @type {Element} - */ - contentElement : { - get : function() { - return this._contentElement; - }, - set : function(value) { - if (!(value instanceof Element)) { - throw new DeveloperError('value must be a valid Element.'); - } - this._contentElement = value; - } - }, /** * Gets or sets the content of the balloon * @memberof BalloonViewModel.prototype From ea0a8b0df8685b496de48071479fede277f8e2a9 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Wed, 7 Aug 2013 13:43:49 -0400 Subject: [PATCH 24/25] cleanup and documentation --- Source/Widgets/Balloon/BalloonViewModel.js | 131 +++++++++++++++------ 1 file changed, 96 insertions(+), 35 deletions(-) diff --git a/Source/Widgets/Balloon/BalloonViewModel.js b/Source/Widgets/Balloon/BalloonViewModel.js index 7a054b8574dc..5c7446f60611 100644 --- a/Source/Widgets/Balloon/BalloonViewModel.js +++ b/Source/Widgets/Balloon/BalloonViewModel.js @@ -18,23 +18,24 @@ define([ var pointMin = 0; var screenSpacePos = new Cartesian2(); - function shiftPosition(viewModel, position){ + function shiftPosition(viewModel, position, point, screen){ var pointX; var pointY; var posX; var posY; + var container = viewModel._container; + var containerWidth = container.clientWidth; + var containerHeight = container.clientHeight; - var containerWidth = viewModel._container.clientWidth; - var containerHeight = viewModel._container.clientHeight; - - viewModel._maxWidth = Math.floor(viewModel._container.clientWidth*0.50) + 'px'; - viewModel._maxHeight = Math.floor(viewModel._container.clientHeight*0.50) + 'px'; + viewModel._maxWidth = containerWidth*0.50 + 'px'; + viewModel._maxHeight = containerHeight*0.50 + 'px'; var pointMaxY = containerHeight - 15; var pointMaxX = containerWidth - 16; var pointXOffset = position.x - 15; - var width = viewModel._balloonElement.offsetWidth; - var height = viewModel._balloonElement.offsetHeight; + var balloonElement = viewModel._balloonElement; + var width = balloonElement.offsetWidth; + var height = balloonElement.offsetHeight; var posMaxY = containerHeight - height; var posMaxX = containerWidth - width - 2; @@ -112,16 +113,12 @@ define([ } } - return { - point: { - x: Math.floor(pointX), - y: Math.floor(pointY) - }, - screen: { - x: Math.floor(posX), - y: Math.floor(posY) - } - }; + + viewModel._pointX = pointX + 'px'; + viewModel._pointY = pointY + 'px'; + + viewModel._positionX = posX + 'px'; + viewModel._positionY = posY + 'px'; } /** @@ -150,18 +147,52 @@ define([ this._container = defaultValue(container, document.body); this._balloonElement = balloonElement; this._content = ''; - this._contentHTML = ''; this._position = undefined; + this._updateContent = false; + this._timerRunning = false; + this._defaultPosition = {x: this._container.clientWidth, y: this._container.clientHeight/2}; this._computeScreenSpacePosition = function(position, result) { return SceneTransforms.wgs84ToWindowCoordinates(scene, position, result); }; + /** + * Stores the HTML content of the balloon as a string. + * @memberof BalloonViewModel.prototype + * + * @type {String} + */ + this._contentHTML = ''; + + /** + * The x screen position of the balloon. + * @memberof BalloonViewModel.prototype + * + * @type {Number} + */ this._positionX = '0'; + + /** + * The y screen position of the balloon. + * @memberof BalloonViewModel.prototype + * + * @type {Number} + */ this._positionY = '0'; + + /** + * The x screen position of the balloon point. + * @memberof BalloonViewModel.prototype + * + * @type {Number} + */ this._pointX = '0'; + + /** + * The y screen position of the balloon point + * @memberof BalloonViewModel.prototype + * + * @type {Boolean} + */ this._pointY = '0'; - this._updateContent = false; - this._timerRunning = false; - this._defaultPosition = {x: this._container.clientWidth, y: this._container.clientHeight/2}; /** * Determines the visibility of the balloon @@ -172,20 +203,60 @@ define([ this.showBalloon = false; /** - * Determines the visibility of the balloon point if the balloon is visible + * Determines the visibility of the balloon point * @memberof BalloonViewModel.prototype * * @type {Boolean} */ this.showPoint = true; + /** + * True of the balloon point should be pointing down. + * @memberof BalloonViewModel.prototype + * + * @type {Boolean} + */ this._down = true; + + /** + * True of the balloon point should be pointing up. + * @memberof BalloonViewModel.prototype + * + * @type {Boolean} + */ this._up = false; + + /** + * True of the balloon point should be pointing left. + * @memberof BalloonViewModel.prototype + * + * @type {Boolean} + */ this._left = false; + + /** + * True if the balloon point should be pointing right. + * @memberof BalloonViewModel.prototype + * + * @type {Boolean} + */ this._right = false; - this._maxWidth = Math.floor(this._container.clientWidth*0.95) + 'px'; - this._maxHeight = Math.floor(this._container.clientHeight*0.50) + 'px'; + /** + * The maximum width of the balloon element. + * @memberof BalloonViewModel.prototype + * + * @type {Number} + */ + this._maxWidth = this._container.clientWidth*0.95 + 'px'; + + /** + * The maximum height of the balloon element. + * @memberof BalloonViewModel.prototype + * + * @type {Number} + */ + this._maxHeight = this._container.clientHeight*0.50 + 'px'; knockout.track(this, ['showPoint', 'showBalloon', '_positionX', '_positionY', '_pointX', '_pointY', '_down', '_up', '_left', '_right', '_maxWidth', '_maxHeight', '_contentHTML']); @@ -207,11 +278,6 @@ define([ if (typeof that._position !== 'undefined') { var pos = that._computeScreenSpacePosition(that._position, screenSpacePos); pos = shiftPosition(that, pos); - that._pointX = pos.point.x + 'px'; - that._pointY = pos.point.y + 'px'; - - that._positionX = pos.screen.x + 'px'; - that._positionY = pos.screen.y + 'px'; } that.showBalloon = true; that._timerRunning = false; @@ -228,11 +294,6 @@ define([ } pos = shiftPosition(this, pos); - this._pointX = pos.point.x + 'px'; - this._pointY = pos.point.y + 'px'; - - this._positionX = pos.screen.x + 'px'; - this._positionY = pos.screen.y + 'px'; } } }; From a033ba53df3148155a5cf3641da73e9e31f3b259 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Wed, 7 Aug 2013 13:45:35 -0400 Subject: [PATCH 25/25] fix tests --- Specs/Widgets/Balloon/BalloonViewModelSpec.js | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/Specs/Widgets/Balloon/BalloonViewModelSpec.js b/Specs/Widgets/Balloon/BalloonViewModelSpec.js index b1daf92a75a7..0029837603d2 100644 --- a/Specs/Widgets/Balloon/BalloonViewModelSpec.js +++ b/Specs/Widgets/Balloon/BalloonViewModelSpec.js @@ -15,7 +15,6 @@ defineSuite([ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/ var scene; - var contentElement = document.createElement('div'); var balloonElement = document.createElement('div'); var container = document.createElement('div'); beforeAll(function() { @@ -26,17 +25,9 @@ defineSuite([ destroyScene(scene); }); - it('constructor sets default values', function() { - var viewModel = new BalloonViewModel(scene, contentElement, balloonElement); - expect(viewModel.scene).toBe(scene); - expect(viewModel.contentElement).toBe(contentElement); - expect(viewModel.balloonElement).toBe(balloonElement); - }); - it('constructor sets expected values', function() { - var viewModel = new BalloonViewModel(scene, contentElement, balloonElement, container); + var viewModel = new BalloonViewModel(scene, balloonElement, container); expect(viewModel.scene).toBe(scene); - expect(viewModel.contentElement).toBe(contentElement); expect(viewModel.balloonElement).toBe(balloonElement); expect(viewModel.container).toBe(container); }); @@ -47,15 +38,9 @@ defineSuite([ }).toThrow(); }); - it('throws if contentElement is undefined', function() { - expect(function() { - return new BalloonViewModel(scene); - }).toThrow(); - }); - it('throws if balloonElement is undefined', function() { expect(function() { - return new BalloonViewModel(scene, contentElement); + return new BalloonViewModel(scene); }).toThrow(); }); }); \ No newline at end of file