123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- import buildModuleUrl from '../../Core/buildModuleUrl.js';
- import Check from '../../Core/Check.js';
- import Color from '../../Core/Color.js';
- import defined from '../../Core/defined.js';
- import defineProperties from '../../Core/defineProperties.js';
- import destroyObject from '../../Core/destroyObject.js';
- import knockout from '../../ThirdParty/knockout.js';
- import getElement from '../getElement.js';
- import subscribeAndEvaluate from '../subscribeAndEvaluate.js';
- import InfoBoxViewModel from './InfoBoxViewModel.js';
- /**
- * A widget for displaying information or a description.
- *
- * @alias InfoBox
- * @constructor
- *
- * @param {Element|String} container The DOM element or ID that will contain the widget.
- *
- * @exception {DeveloperError} Element with id "container" does not exist in the document.
- */
- function InfoBox(container) {
- //>>includeStart('debug', pragmas.debug);
- Check.defined('container', container);
- //>>includeEnd('debug')
- container = getElement(container);
- var infoElement = document.createElement('div');
- infoElement.className = 'cesium-infoBox';
- infoElement.setAttribute('data-bind', '\
- css: { "cesium-infoBox-visible" : showInfo, "cesium-infoBox-bodyless" : _bodyless }');
- container.appendChild(infoElement);
- var titleElement = document.createElement('div');
- titleElement.className = 'cesium-infoBox-title';
- titleElement.setAttribute('data-bind', 'text: titleText');
- infoElement.appendChild(titleElement);
- var cameraElement = document.createElement('button');
- cameraElement.type = 'button';
- cameraElement.className = 'cesium-button cesium-infoBox-camera';
- cameraElement.setAttribute('data-bind', '\
- attr: { title: "Focus camera on object" },\
- click: function () { cameraClicked.raiseEvent(this); },\
- enable: enableCamera,\
- cesiumSvgPath: { path: cameraIconPath, width: 32, height: 32 }');
- infoElement.appendChild(cameraElement);
- var closeElement = document.createElement('button');
- closeElement.type = 'button';
- closeElement.className = 'cesium-infoBox-close';
- closeElement.setAttribute('data-bind', '\
- click: function () { closeClicked.raiseEvent(this); }');
- closeElement.innerHTML = '×';
- infoElement.appendChild(closeElement);
- var frame = document.createElement('iframe');
- frame.className = 'cesium-infoBox-iframe';
- frame.setAttribute('sandbox', 'allow-same-origin allow-popups allow-forms'); //allow-pointer-lock allow-scripts allow-top-navigation
- frame.setAttribute('data-bind', 'style : { maxHeight : maxHeightOffset(40) }');
- frame.setAttribute('allowfullscreen', true);
- infoElement.appendChild(frame);
- var viewModel = new InfoBoxViewModel();
- knockout.applyBindings(viewModel, infoElement);
- this._container = container;
- this._element = infoElement;
- this._frame = frame;
- this._viewModel = viewModel;
- this._descriptionSubscription = undefined;
- var that = this;
- //We can't actually add anything into the frame until the load event is fired
- frame.addEventListener('load', function() {
- var frameDocument = frame.contentDocument;
- //We inject default css into the content iframe,
- //end users can remove it or add their own via the exposed frame property.
- var cssLink = frameDocument.createElement('link');
- cssLink.href = buildModuleUrl('Widgets/InfoBox/InfoBoxDescription.css');
- cssLink.rel = 'stylesheet';
- cssLink.type = 'text/css';
- //div to use for description content.
- var frameContent = frameDocument.createElement('div');
- frameContent.className = 'cesium-infoBox-description';
- frameDocument.head.appendChild(cssLink);
- frameDocument.body.appendChild(frameContent);
- //We manually subscribe to the description event rather than through a binding for two reasons.
- //1. It's an easy way to ensure order of operation so that we can adjust the height.
- //2. Knockout does not bind to elements inside of an iFrame, so we would have to apply a second binding
- // model anyway.
- that._descriptionSubscription = subscribeAndEvaluate(viewModel, 'description', function(value) {
- // Set the frame to small height, force vertical scroll bar to appear, and text to wrap accordingly.
- frame.style.height = '5px';
- frameContent.innerHTML = value;
- //If the snippet is a single element, then use its background
- //color for the body of the InfoBox. This makes the padding match
- //the content and produces much nicer results.
- var background = null;
- var firstElementChild = frameContent.firstElementChild;
- if (firstElementChild !== null && frameContent.childNodes.length === 1) {
- var style = window.getComputedStyle(firstElementChild);
- if (style !== null) {
- var backgroundColor = style['background-color'];
- var color = Color.fromCssColorString(backgroundColor);
- if (defined(color) && color.alpha !== 0) {
- background = style['background-color'];
- }
- }
- }
- infoElement.style['background-color'] = background;
- // Measure and set the new custom height, based on text wrapped above.
- var height = frameContent.getBoundingClientRect().height;
- frame.style.height = height + 'px';
- });
- });
- //Chrome does not send the load event unless we explicitly set a src
- frame.setAttribute('src', 'about:blank');
- }
- defineProperties(InfoBox.prototype, {
- /**
- * Gets the parent container.
- * @memberof InfoBox.prototype
- *
- * @type {Element}
- */
- container : {
- get : function() {
- return this._container;
- }
- },
- /**
- * Gets the view model.
- * @memberof InfoBox.prototype
- *
- * @type {InfoBoxViewModel}
- */
- viewModel : {
- get : function() {
- return this._viewModel;
- }
- },
- /**
- * Gets the iframe used to display the description.
- * @memberof InfoBox.prototype
- *
- * @type {HTMLIFrameElement}
- */
- frame : {
- get : function() {
- return this._frame;
- }
- }
- });
- /**
- * @returns {Boolean} true if the object has been destroyed, false otherwise.
- */
- InfoBox.prototype.isDestroyed = function() {
- return false;
- };
- /**
- * Destroys the widget. Should be called if permanently
- * removing the widget from layout.
- */
- InfoBox.prototype.destroy = function() {
- var container = this._container;
- knockout.cleanNode(this._element);
- container.removeChild(this._element);
- if (defined(this._descriptionSubscription)) {
- this._descriptionSubscription.dispose();
- }
- return destroyObject(this);
- };
- export default InfoBox;
|