123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610 |
- 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";
- const mobileWidth = 576;
- const lightboxHeight = 100;
- const textColor = "#ffffff";
- const highlightColor = "#48b";
- /**
- * Used to sort the credits by frequency of appearance
- * when they are later displayed.
- *
- * @alias CreditDisplay.CreditDisplayElement
- * @constructor
- *
- * @private
- */
- function CreditDisplayElement(credit, count) {
- this.credit = credit;
- this.count = defaultValue(count, 1);
- }
- function contains(credits, credit) {
- const len = credits.length;
- for (let i = 0; i < len; i++) {
- const 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.
- const previousCredit = creditDisplay._previousCesiumCredit;
- const 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;
- }
- const delimiterClassName = "cesium-credit-delimiter";
- function createDelimiterElement(delimiter) {
- const 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)) {
- const wrapper = document.createElement(elementWrapperTagName);
- wrapper._creditId = element._creditId;
- wrapper.appendChild(element);
- element = wrapper;
- }
- return element;
- }
- function displayCredits(container, credits, delimiter, elementWrapperTagName) {
- const childNodes = container.childNodes;
- let domIndex = -1;
- // Sort the credits such that more frequent credits appear first
- credits.sort(function (credit1, credit2) {
- return credit2.count - credit1.count;
- });
- for (let creditIndex = 0; creditIndex < credits.length; ++creditIndex) {
- const credit = credits[creditIndex].credit;
- if (defined(credit)) {
- domIndex = creditIndex;
- if (defined(delimiter)) {
- // credits may be separated by delimiters
- domIndex *= 2;
- if (creditIndex > 0) {
- const delimiterDomIndex = domIndex - 1;
- if (childNodes.length <= delimiterDomIndex) {
- container.appendChild(createDelimiterElement(delimiter));
- } else {
- const existingDelimiter = childNodes[delimiterDomIndex];
- if (existingDelimiter.className !== delimiterClassName) {
- container.replaceChild(
- createDelimiterElement(delimiter),
- existingDelimiter
- );
- }
- }
- }
- }
- const 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 {
- const 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) {
- const lightboxCredits = that._lightboxCredits;
- const width = that.viewport.clientWidth;
- const 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) {
- let style = `${selector} {`;
- for (const attribute in styles) {
- if (styles.hasOwnProperty(attribute)) {
- style += `${attribute}: ${styles[attribute]}; `;
- }
- }
- style += " }\n";
- return style;
- }
- function appendCss() {
- let 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",
- }
- );
- const head = document.head;
- const 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
- * const creditDisplay = new Cesium.CreditDisplay(creditContainer);
- */
- function CreditDisplay(container, delimiter, viewport) {
- //>>includeStart('debug', pragmas.debug);
- Check.defined("container", container);
- //>>includeEnd('debug');
- const that = this;
- viewport = defaultValue(viewport, document.body);
- const lightbox = document.createElement("div");
- lightbox.className = "cesium-credit-lightbox-overlay";
- viewport.appendChild(lightbox);
- const 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);
- const title = document.createElement("div");
- title.className = "cesium-credit-lightbox-title";
- title.textContent = "Data provided by:";
- lightboxCredits.appendChild(title);
- const closeButton = document.createElement("a");
- closeButton.onclick = this.hideLightbox.bind(this);
- closeButton.innerHTML = "×";
- closeButton.className = "cesium-credit-lightbox-close";
- lightboxCredits.appendChild(closeButton);
- const creditList = document.createElement("ul");
- lightboxCredits.appendChild(creditList);
- const cesiumCreditContainer = document.createElement("div");
- cesiumCreditContainer.className = "cesium-credit-logoContainer";
- cesiumCreditContainer.style.display = "inline";
- container.appendChild(cesiumCreditContainer);
- const screenContainer = document.createElement("div");
- screenContainer.className = "cesium-credit-textContainer";
- screenContainer.style.display = "inline";
- container.appendChild(screenContainer);
- const expandLink = document.createElement("a");
- expandLink.className = "cesium-credit-expand-link";
- expandLink.onclick = this.showLightbox.bind(this);
- expandLink.textContent = "Data attribution";
- container.appendChild(expandLink);
- appendCss();
- const 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._creditDisplayElementPool = [];
- this._creditDisplayElementIndex = 0;
- 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;
- }
- function setCredit(creditDisplay, credits, credit, count) {
- count = defaultValue(count, 1);
- let creditDisplayElement = credits.get(credit.id);
- if (!defined(creditDisplayElement)) {
- const pool = creditDisplay._creditDisplayElementPool;
- const poolIndex = creditDisplay._creditDisplayElementPoolIndex;
- if (poolIndex < pool.length) {
- creditDisplayElement = pool[poolIndex];
- creditDisplayElement.credit = credit;
- creditDisplayElement.count = count;
- } else {
- creditDisplayElement = new CreditDisplayElement(credit, count);
- pool.push(creditDisplayElement);
- }
- ++creditDisplay._creditDisplayElementPoolIndex;
- credits.set(credit.id, creditDisplayElement);
- } else if (creditDisplayElement.count < Number.MAX_VALUE) {
- creditDisplayElement.count += count;
- }
- }
- /**
- * 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;
- }
- let credits;
- if (!credit.showOnScreen) {
- credits = this._currentFrameCredits.lightboxCredits;
- } else {
- credits = this._currentFrameCredits.screenCredits;
- }
- setCredit(this, credits, 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');
- const 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');
- const defaultCredits = this._defaultCredits;
- const 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 () {
- const currentFrameCredits = this._currentFrameCredits;
- this._creditDisplayElementPoolIndex = 0;
- const screenCredits = currentFrameCredits.screenCredits;
- screenCredits.removeAll();
- const defaultCredits = this._defaultCredits;
- for (let i = 0; i < defaultCredits.length; ++i) {
- const defaultCredit = defaultCredits[i];
- setCredit(this, screenCredits, defaultCredit, Number.MAX_VALUE);
- }
- 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 () {
- const screenCredits = this._currentFrameCredits.screenCredits.values;
- displayCredits(
- this._screenContainer,
- screenCredits,
- this._delimiter,
- undefined
- );
- const 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.
- * <br /><br />
- * Once an object is destroyed, it should not be used; calling any function other than
- * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
- * assign the return value (<code>undefined</code>) 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.
- * <br /><br />
- *
- * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
- */
- CreditDisplay.prototype.isDestroyed = function () {
- return false;
- };
- CreditDisplay._cesiumCredit = undefined;
- CreditDisplay._cesiumCreditInitialized = false;
- let defaultCredit;
- function getDefaultCredit() {
- if (!defined(defaultCredit)) {
- let 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 <img> tag; use the full path instead
- if (
- logo.indexOf("http://") !== 0 &&
- logo.indexOf("https://") !== 0 &&
- logo.indexOf("data:") !== 0
- ) {
- const logoUrl = new Uri(logo);
- logo = logoUrl.path();
- }
- defaultCredit = new Credit(
- `<a href="https://cesium.com/" target="_blank"><img src="${logo}" title="Cesium ion"/></a>`,
- 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;
- },
- },
- });
- CreditDisplay.CreditDisplayElement = CreditDisplayElement;
- export default CreditDisplay;
|