import AssociativeArray from "../Core/AssociativeArray.js"; import buildModuleUrl from "../Core/buildModuleUrl.js"; import Check from "../Core/Check.js"; import Credit from "../Core/Credit.js"; import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; import destroyObject from "../Core/destroyObject.js"; import Uri from "../ThirdParty/Uri.js"; var mobileWidth = 576; var lightboxHeight = 100; var textColor = "#ffffff"; var highlightColor = "#48b"; function contains(credits, credit) { var len = credits.length; for (var i = 0; i < len; i++) { var existingCredit = credits[i]; if (Credit.equals(existingCredit, credit)) { return true; } } return false; } function swapCesiumCredit(creditDisplay) { // We don't want to clutter the screen with the Cesium logo and the Cesium ion // logo at the same time. Since the ion logo is required, we just replace the // Cesium logo or add the logo if the Cesium one was removed. var previousCredit = creditDisplay._previousCesiumCredit; var currentCredit = creditDisplay._currentCesiumCredit; if (Credit.equals(currentCredit, previousCredit)) { return; } if (defined(previousCredit)) { creditDisplay._cesiumCreditContainer.removeChild(previousCredit.element); } if (defined(currentCredit)) { creditDisplay._cesiumCreditContainer.appendChild(currentCredit.element); } creditDisplay._previousCesiumCredit = currentCredit; } var delimiterClassName = "cesium-credit-delimiter"; function createDelimiterElement(delimiter) { var delimiterElement = document.createElement("span"); delimiterElement.textContent = delimiter; delimiterElement.className = delimiterClassName; return delimiterElement; } function createCreditElement(element, elementWrapperTagName) { // may need to wrap the credit in another element if (defined(elementWrapperTagName)) { var wrapper = document.createElement(elementWrapperTagName); wrapper._creditId = element._creditId; wrapper.appendChild(element); element = wrapper; } return element; } function displayCredits(container, credits, delimiter, elementWrapperTagName) { var childNodes = container.childNodes; var domIndex = -1; for (var creditIndex = 0; creditIndex < credits.length; ++creditIndex) { var credit = credits[creditIndex]; if (defined(credit)) { domIndex = creditIndex; if (defined(delimiter)) { // credits may be separated by delimiters domIndex *= 2; if (creditIndex > 0) { var delimiterDomIndex = domIndex - 1; if (childNodes.length <= delimiterDomIndex) { container.appendChild(createDelimiterElement(delimiter)); } else { var existingDelimiter = childNodes[delimiterDomIndex]; if (existingDelimiter.className !== delimiterClassName) { container.replaceChild( createDelimiterElement(delimiter), existingDelimiter ); } } } } var element = credit.element; // check to see if the correct credit is in the right place if (childNodes.length <= domIndex) { container.appendChild( createCreditElement(element, elementWrapperTagName) ); } else { var existingElement = childNodes[domIndex]; if (existingElement._creditId !== credit._id) { // not the right credit, swap it in container.replaceChild( createCreditElement(element, elementWrapperTagName), existingElement ); } } } } // any remaining nodes in the container are unnecessary ++domIndex; while (domIndex < childNodes.length) { container.removeChild(childNodes[domIndex]); } } function styleLightboxContainer(that) { var lightboxCredits = that._lightboxCredits; var width = that.viewport.clientWidth; var height = that.viewport.clientHeight; if (width !== that._lastViewportWidth) { if (width < mobileWidth) { lightboxCredits.className = "cesium-credit-lightbox cesium-credit-lightbox-mobile"; lightboxCredits.style.marginTop = "0"; } else { lightboxCredits.className = "cesium-credit-lightbox cesium-credit-lightbox-expanded"; lightboxCredits.style.marginTop = Math.floor((height - lightboxCredits.clientHeight) * 0.5) + "px"; } that._lastViewportWidth = width; } if (width >= mobileWidth && height !== that._lastViewportHeight) { lightboxCredits.style.marginTop = Math.floor((height - lightboxCredits.clientHeight) * 0.5) + "px"; that._lastViewportHeight = height; } } function addStyle(selector, styles) { var style = selector + " {"; for (var attribute in styles) { if (styles.hasOwnProperty(attribute)) { style += attribute + ": " + styles[attribute] + "; "; } } style += " }\n"; return style; } function appendCss() { var style = ""; style += addStyle(".cesium-credit-lightbox-overlay", { display: "none", "z-index": "1", //must be at least 1 to draw over top other Cesium widgets position: "absolute", top: "0", left: "0", width: "100%", height: "100%", "background-color": "rgba(80, 80, 80, 0.8)", }); style += addStyle(".cesium-credit-lightbox", { "background-color": "#303336", color: textColor, position: "relative", "min-height": lightboxHeight + "px", margin: "auto", }); style += addStyle( ".cesium-credit-lightbox > ul > li a, .cesium-credit-lightbox > ul > li a:visited", { color: textColor, } ); style += addStyle(".cesium-credit-lightbox > ul > li a:hover", { color: highlightColor, }); style += addStyle(".cesium-credit-lightbox.cesium-credit-lightbox-expanded", { border: "1px solid #444", "border-radius": "5px", "max-width": "370px", }); style += addStyle(".cesium-credit-lightbox.cesium-credit-lightbox-mobile", { height: "100%", width: "100%", }); style += addStyle(".cesium-credit-lightbox-title", { padding: "20px 20px 0 20px", }); style += addStyle(".cesium-credit-lightbox-close", { "font-size": "18pt", cursor: "pointer", position: "absolute", top: "0", right: "6px", color: textColor, }); style += addStyle(".cesium-credit-lightbox-close:hover", { color: highlightColor, }); style += addStyle(".cesium-credit-lightbox > ul", { margin: "0", padding: "12px 20px 12px 40px", "font-size": "13px", }); style += addStyle(".cesium-credit-lightbox > ul > li", { "padding-bottom": "6px", }); style += addStyle(".cesium-credit-lightbox > ul > li *", { padding: "0", margin: "0", }); style += addStyle(".cesium-credit-expand-link", { "padding-left": "5px", cursor: "pointer", "text-decoration": "underline", color: textColor, }); style += addStyle(".cesium-credit-expand-link:hover", { color: highlightColor, }); style += addStyle(".cesium-credit-text", { color: textColor, }); style += addStyle( ".cesium-credit-textContainer *, .cesium-credit-logoContainer *", { display: "inline", } ); var head = document.head; var css = document.createElement("style"); css.innerHTML = style; head.insertBefore(css, head.firstChild); } /** * The credit display is responsible for displaying credits on screen. * * @param {HTMLElement} container The HTML element where credits will be displayed * @param {String} [delimiter= ' • '] The string to separate text credits * @param {HTMLElement} [viewport=document.body] The HTML element that will contain the credits popup * * @alias CreditDisplay * @constructor * * @example * var creditDisplay = new Cesium.CreditDisplay(creditContainer); */ function CreditDisplay(container, delimiter, viewport) { //>>includeStart('debug', pragmas.debug); Check.defined("container", container); //>>includeEnd('debug'); var that = this; viewport = defaultValue(viewport, document.body); var lightbox = document.createElement("div"); lightbox.className = "cesium-credit-lightbox-overlay"; viewport.appendChild(lightbox); var lightboxCredits = document.createElement("div"); lightboxCredits.className = "cesium-credit-lightbox"; lightbox.appendChild(lightboxCredits); function hideLightbox(event) { if (lightboxCredits.contains(event.target)) { return; } that.hideLightbox(); } lightbox.addEventListener("click", hideLightbox, false); var title = document.createElement("div"); title.className = "cesium-credit-lightbox-title"; title.textContent = "Data provided by:"; lightboxCredits.appendChild(title); var closeButton = document.createElement("a"); closeButton.onclick = this.hideLightbox.bind(this); closeButton.innerHTML = "×"; closeButton.className = "cesium-credit-lightbox-close"; lightboxCredits.appendChild(closeButton); var creditList = document.createElement("ul"); lightboxCredits.appendChild(creditList); var cesiumCreditContainer = document.createElement("div"); cesiumCreditContainer.className = "cesium-credit-logoContainer"; cesiumCreditContainer.style.display = "inline"; container.appendChild(cesiumCreditContainer); var screenContainer = document.createElement("div"); screenContainer.className = "cesium-credit-textContainer"; screenContainer.style.display = "inline"; container.appendChild(screenContainer); var expandLink = document.createElement("a"); expandLink.className = "cesium-credit-expand-link"; expandLink.onclick = this.showLightbox.bind(this); expandLink.textContent = "Data attribution"; container.appendChild(expandLink); appendCss(); var cesiumCredit = Credit.clone(CreditDisplay.cesiumCredit); this._delimiter = defaultValue(delimiter, " • "); this._screenContainer = screenContainer; this._cesiumCreditContainer = cesiumCreditContainer; this._lastViewportHeight = undefined; this._lastViewportWidth = undefined; this._lightboxCredits = lightboxCredits; this._creditList = creditList; this._lightbox = lightbox; this._hideLightbox = hideLightbox; this._expandLink = expandLink; this._expanded = false; this._defaultCredits = []; this._cesiumCredit = cesiumCredit; this._previousCesiumCredit = undefined; this._currentCesiumCredit = cesiumCredit; this._currentFrameCredits = { screenCredits: new AssociativeArray(), lightboxCredits: new AssociativeArray(), }; this._defaultCredit = undefined; this.viewport = viewport; /** * The HTML element where credits will be displayed. * @type {HTMLElement} */ this.container = container; } /** * Adds a credit to the list of current credits to be displayed in the credit container * * @param {Credit} credit The credit to display */ CreditDisplay.prototype.addCredit = function (credit) { //>>includeStart('debug', pragmas.debug); Check.defined("credit", credit); //>>includeEnd('debug'); if (credit._isIon) { // If this is the an ion logo credit from the ion server // Juse use the default credit (which is identical) to avoid blinking if (!defined(this._defaultCredit)) { this._defaultCredit = Credit.clone(getDefaultCredit()); } this._currentCesiumCredit = this._defaultCredit; return; } if (!credit.showOnScreen) { this._currentFrameCredits.lightboxCredits.set(credit.id, credit); } else { this._currentFrameCredits.screenCredits.set(credit.id, credit); } }; /** * Adds credits that will persist until they are removed * * @param {Credit} credit The credit to added to defaults */ CreditDisplay.prototype.addDefaultCredit = function (credit) { //>>includeStart('debug', pragmas.debug); Check.defined("credit", credit); //>>includeEnd('debug'); var defaultCredits = this._defaultCredits; if (!contains(defaultCredits, credit)) { defaultCredits.push(credit); } }; /** * Removes a default credit * * @param {Credit} credit The credit to be removed from defaults */ CreditDisplay.prototype.removeDefaultCredit = function (credit) { //>>includeStart('debug', pragmas.debug); Check.defined("credit", credit); //>>includeEnd('debug'); var defaultCredits = this._defaultCredits; var index = defaultCredits.indexOf(credit); if (index !== -1) { defaultCredits.splice(index, 1); } }; CreditDisplay.prototype.showLightbox = function () { this._lightbox.style.display = "block"; this._expanded = true; }; CreditDisplay.prototype.hideLightbox = function () { this._lightbox.style.display = "none"; this._expanded = false; }; /** * Updates the credit display before a new frame is rendered. */ CreditDisplay.prototype.update = function () { if (this._expanded) { styleLightboxContainer(this); } }; /** * Resets the credit display to a beginning of frame state, clearing out current credits. */ CreditDisplay.prototype.beginFrame = function () { var currentFrameCredits = this._currentFrameCredits; var screenCredits = currentFrameCredits.screenCredits; screenCredits.removeAll(); var defaultCredits = this._defaultCredits; for (var i = 0; i < defaultCredits.length; ++i) { var defaultCredit = defaultCredits[i]; screenCredits.set(defaultCredit.id, defaultCredit); } currentFrameCredits.lightboxCredits.removeAll(); if (!Credit.equals(CreditDisplay.cesiumCredit, this._cesiumCredit)) { this._cesiumCredit = Credit.clone(CreditDisplay.cesiumCredit); } this._currentCesiumCredit = this._cesiumCredit; }; /** * Sets the credit display to the end of frame state, displaying credits from the last frame in the credit container. */ CreditDisplay.prototype.endFrame = function () { var screenCredits = this._currentFrameCredits.screenCredits.values; displayCredits( this._screenContainer, screenCredits, this._delimiter, undefined ); var lightboxCredits = this._currentFrameCredits.lightboxCredits.values; this._expandLink.style.display = lightboxCredits.length > 0 ? "inline" : "none"; displayCredits(this._creditList, lightboxCredits, undefined, "li"); swapCesiumCredit(this); }; /** * Destroys the resources held by this object. Destroying an object allows for deterministic * release of resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ CreditDisplay.prototype.destroy = function () { this._lightbox.removeEventListener("click", this._hideLightbox, false); this.container.removeChild(this._cesiumCreditContainer); this.container.removeChild(this._screenContainer); this.container.removeChild(this._expandLink); this.viewport.removeChild(this._lightbox); return destroyObject(this); }; /** * Returns true if this object was destroyed; otherwise, false. *

* * @returns {Boolean} true if this object was destroyed; otherwise, false. */ CreditDisplay.prototype.isDestroyed = function () { return false; }; CreditDisplay._cesiumCredit = undefined; CreditDisplay._cesiumCreditInitialized = false; var defaultCredit; function getDefaultCredit() { if (!defined(defaultCredit)) { var logo = buildModuleUrl("Assets/Images/ion-credit.png"); // When hosting in a WebView, the base URL scheme is file:// or ms-appx-web:// // which is stripped out from the Credit's tag; use the full path instead if ( logo.indexOf("http://") !== 0 && logo.indexOf("https://") !== 0 && logo.indexOf("data:") !== 0 ) { var logoUrl = new Uri(logo); logo = logoUrl.getPath(); } defaultCredit = new Credit( '', true ); } if (!CreditDisplay._cesiumCreditInitialized) { CreditDisplay._cesiumCredit = defaultCredit; CreditDisplay._cesiumCreditInitialized = true; } return defaultCredit; } Object.defineProperties(CreditDisplay, { /** * Gets or sets the Cesium logo credit. * @memberof CreditDisplay * @type {Credit} */ cesiumCredit: { get: function () { getDefaultCredit(); return CreditDisplay._cesiumCredit; }, set: function (value) { CreditDisplay._cesiumCredit = value; CreditDisplay._cesiumCreditInitialized = true; }, }, }); export default CreditDisplay;