智慧水务管理系统 - 精河县供水工程综合管理平台

CreditDisplay.js 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. import AssociativeArray from "../Core/AssociativeArray.js";
  2. import buildModuleUrl from "../Core/buildModuleUrl.js";
  3. import Check from "../Core/Check.js";
  4. import Credit from "../Core/Credit.js";
  5. import defined from "../Core/defined.js";
  6. import destroyObject from "../Core/destroyObject.js";
  7. import Uri from "urijs";
  8. const mobileWidth = 576;
  9. const lightboxHeight = 100;
  10. const textColor = "#ffffff";
  11. const highlightColor = "#48b";
  12. /**
  13. * Used to sort the credits by frequency of appearance
  14. * when they are later displayed.
  15. *
  16. * @alias CreditDisplay.CreditDisplayElement
  17. * @constructor
  18. *
  19. * @private
  20. */
  21. function CreditDisplayElement(credit, count) {
  22. this.credit = credit;
  23. this.count = count ?? 1;
  24. }
  25. function contains(credits, credit) {
  26. const len = credits.length;
  27. for (let i = 0; i < len; i++) {
  28. const existingCredit = credits[i];
  29. if (Credit.equals(existingCredit, credit)) {
  30. return true;
  31. }
  32. }
  33. return false;
  34. }
  35. function swapCesiumCredit(creditDisplay) {
  36. // We don't want to clutter the screen with the Cesium logo and the Cesium ion
  37. // logo at the same time. Since the ion logo is required, we just replace the
  38. // Cesium logo or add the logo if the Cesium one was removed.
  39. const previousCredit = creditDisplay._previousCesiumCredit;
  40. const currentCredit = creditDisplay._currentCesiumCredit;
  41. if (Credit.equals(currentCredit, previousCredit)) {
  42. return;
  43. }
  44. if (defined(previousCredit)) {
  45. creditDisplay._cesiumCreditContainer.removeChild(previousCredit.element);
  46. }
  47. if (defined(currentCredit)) {
  48. creditDisplay._cesiumCreditContainer.appendChild(currentCredit.element);
  49. }
  50. creditDisplay._previousCesiumCredit = currentCredit;
  51. }
  52. const delimiterClassName = "cesium-credit-delimiter";
  53. function createDelimiterElement(delimiter) {
  54. const delimiterElement = document.createElement("span");
  55. delimiterElement.textContent = delimiter;
  56. delimiterElement.className = delimiterClassName;
  57. return delimiterElement;
  58. }
  59. function createCreditElement(element, elementWrapperTagName) {
  60. // may need to wrap the credit in another element
  61. if (defined(elementWrapperTagName)) {
  62. const wrapper = document.createElement(elementWrapperTagName);
  63. wrapper._creditId = element._creditId;
  64. wrapper.appendChild(element);
  65. element = wrapper;
  66. }
  67. return element;
  68. }
  69. function displayCredits(container, credits, delimiter, elementWrapperTagName) {
  70. const childNodes = container.childNodes;
  71. let domIndex = -1;
  72. // Sort the credits such that more frequent credits appear first
  73. credits.sort(function (credit1, credit2) {
  74. return credit2.count - credit1.count;
  75. });
  76. for (let creditIndex = 0; creditIndex < credits.length; ++creditIndex) {
  77. const credit = credits[creditIndex].credit;
  78. if (defined(credit)) {
  79. domIndex = creditIndex;
  80. if (defined(delimiter)) {
  81. // credits may be separated by delimiters
  82. domIndex *= 2;
  83. if (creditIndex > 0) {
  84. const delimiterDomIndex = domIndex - 1;
  85. if (childNodes.length <= delimiterDomIndex) {
  86. container.appendChild(createDelimiterElement(delimiter));
  87. } else {
  88. const existingDelimiter = childNodes[delimiterDomIndex];
  89. if (existingDelimiter.className !== delimiterClassName) {
  90. container.replaceChild(
  91. createDelimiterElement(delimiter),
  92. existingDelimiter,
  93. );
  94. }
  95. }
  96. }
  97. }
  98. const element = credit.element;
  99. // check to see if the correct credit is in the right place
  100. if (childNodes.length <= domIndex) {
  101. container.appendChild(
  102. createCreditElement(element, elementWrapperTagName),
  103. );
  104. } else {
  105. const existingElement = childNodes[domIndex];
  106. if (existingElement._creditId !== credit._id) {
  107. // not the right credit, swap it in
  108. container.replaceChild(
  109. createCreditElement(element, elementWrapperTagName),
  110. existingElement,
  111. );
  112. }
  113. }
  114. }
  115. }
  116. // any remaining nodes in the container are unnecessary
  117. ++domIndex;
  118. while (domIndex < childNodes.length) {
  119. container.removeChild(childNodes[domIndex]);
  120. }
  121. }
  122. function styleLightboxContainer(that) {
  123. const lightboxCredits = that._lightboxCredits;
  124. const width = that.viewport.clientWidth;
  125. const height = that.viewport.clientHeight;
  126. if (width !== that._lastViewportWidth) {
  127. if (width < mobileWidth) {
  128. lightboxCredits.className =
  129. "cesium-credit-lightbox cesium-credit-lightbox-mobile";
  130. lightboxCredits.style.marginTop = "0";
  131. } else {
  132. lightboxCredits.className =
  133. "cesium-credit-lightbox cesium-credit-lightbox-expanded";
  134. lightboxCredits.style.marginTop = `${Math.floor(
  135. (height - lightboxCredits.clientHeight) * 0.5,
  136. )}px`;
  137. }
  138. that._lastViewportWidth = width;
  139. }
  140. if (width >= mobileWidth && height !== that._lastViewportHeight) {
  141. lightboxCredits.style.marginTop = `${Math.floor(
  142. (height - lightboxCredits.clientHeight) * 0.5,
  143. )}px`;
  144. that._lastViewportHeight = height;
  145. }
  146. }
  147. function appendCss(container) {
  148. const style = /*css*/ `
  149. .cesium-credit-lightbox-overlay {
  150. display: none;
  151. z-index: 1;
  152. position: absolute;
  153. top: 0;
  154. left: 0;
  155. width: 100%;
  156. height: 100%;
  157. background-color: rgba(80, 80, 80, 0.8);
  158. }
  159. .cesium-credit-lightbox {
  160. background-color: #303336;
  161. color: ${textColor};
  162. position: relative;
  163. min-height: ${lightboxHeight}px;
  164. margin: auto;
  165. }
  166. .cesium-credit-lightbox > ul > li a,
  167. .cesium-credit-lightbox > ul > li a:visited,
  168. .cesium-credit-wrapper a,
  169. .cesium-credit-wrapper a:visited {
  170. color: ${textColor};
  171. }
  172. .cesium-credit-lightbox > ul > li a:hover {
  173. color: ${highlightColor};
  174. }
  175. .cesium-credit-lightbox.cesium-credit-lightbox-expanded {
  176. border: 1px solid #444;
  177. border-radius: 5px;
  178. max-width: 470px;
  179. }
  180. .cesium-credit-lightbox.cesium-credit-lightbox-mobile {
  181. height: 100%;
  182. width: 100%;
  183. }
  184. .cesium-credit-lightbox-title {
  185. padding: 20px 20px 0 20px;
  186. }
  187. .cesium-credit-lightbox-close {
  188. font-size: 18pt;
  189. cursor: pointer;
  190. position: absolute;
  191. top: 0;
  192. right: 6px;
  193. color: ${textColor};
  194. }
  195. .cesium-credit-lightbox-close:hover {
  196. color: ${highlightColor};
  197. }
  198. .cesium-credit-lightbox > ul {
  199. margin: 0;
  200. padding: 12px 20px 12px 40px;
  201. font-size: 13px;
  202. }
  203. .cesium-credit-lightbox > ul > li {
  204. padding-bottom: 6px;
  205. }
  206. .cesium-credit-lightbox > ul > li * {
  207. padding: 0;
  208. margin: 0;
  209. }
  210. .cesium-credit-expand-link {
  211. padding-left: 5px;
  212. cursor: pointer;
  213. text-decoration: underline;
  214. color: ${textColor};
  215. }
  216. .cesium-credit-expand-link:hover {
  217. color: ${highlightColor};
  218. }
  219. .cesium-credit-text {
  220. color: ${textColor};
  221. }
  222. .cesium-credit-delimiter {
  223. padding: 0 5px;
  224. }
  225. .cesium-credit-textContainer *,
  226. .cesium-credit-logoContainer * {
  227. display: inline;
  228. }
  229. .cesium-credit-textContainer a:hover {
  230. color: ${highlightColor}
  231. }
  232. .cesium-credit-textContainer .cesium-credit-wrapper:first-of-type {
  233. padding-left: 5px;
  234. }
  235. `;
  236. function getShadowRoot(container) {
  237. if (container.shadowRoot) {
  238. return container.shadowRoot;
  239. }
  240. if (container.getRootNode) {
  241. const root = container.getRootNode();
  242. if (root instanceof ShadowRoot) {
  243. return root;
  244. }
  245. }
  246. return undefined;
  247. }
  248. const shadowRootOrDocumentHead = getShadowRoot(container) ?? document.head;
  249. const styleElem = document.createElement("style");
  250. styleElem.innerHTML = style;
  251. shadowRootOrDocumentHead.appendChild(styleElem);
  252. }
  253. /**
  254. * The credit display is responsible for displaying credits on screen.
  255. *
  256. * @param {HTMLElement} container The HTML element where credits will be displayed
  257. * @param {string} [delimiter= '•'] The string to separate text credits
  258. * @param {HTMLElement} [viewport=document.body] The HTML element that will contain the credits popup
  259. *
  260. * @alias CreditDisplay
  261. * @constructor
  262. *
  263. * @example
  264. * // Add a credit with a tooltip, image and link to display onscreen
  265. * const credit = new Cesium.Credit(`<a href="https://cesium.com/" target="_blank"><img src="/images/cesium_logo.png" title="Cesium"/></a>`, true);
  266. * viewer.creditDisplay.addStaticCredit(credit);
  267. *
  268. * @example
  269. * // Add a credit with a plaintext link to display in the lightbox
  270. * const credit = new Cesium.Credit('<a href="https://cesium.com/" target="_blank">Cesium</a>');
  271. * viewer.creditDisplay.addStaticCredit(credit);
  272. */
  273. function CreditDisplay(container, delimiter, viewport) {
  274. //>>includeStart('debug', pragmas.debug);
  275. Check.defined("container", container);
  276. //>>includeEnd('debug');
  277. const that = this;
  278. viewport = viewport ?? document.body;
  279. const lightbox = document.createElement("div");
  280. lightbox.className = "cesium-credit-lightbox-overlay";
  281. viewport.appendChild(lightbox);
  282. const lightboxCredits = document.createElement("div");
  283. lightboxCredits.className = "cesium-credit-lightbox";
  284. lightbox.appendChild(lightboxCredits);
  285. function hideLightbox(event) {
  286. if (lightboxCredits.contains(event.target)) {
  287. return;
  288. }
  289. that.hideLightbox();
  290. }
  291. lightbox.addEventListener("click", hideLightbox, false);
  292. const title = document.createElement("div");
  293. title.className = "cesium-credit-lightbox-title";
  294. title.textContent = "Data provided by:";
  295. lightboxCredits.appendChild(title);
  296. const closeButton = document.createElement("a");
  297. closeButton.onclick = this.hideLightbox.bind(this);
  298. closeButton.innerHTML = "&times;";
  299. closeButton.className = "cesium-credit-lightbox-close";
  300. lightboxCredits.appendChild(closeButton);
  301. const creditList = document.createElement("ul");
  302. lightboxCredits.appendChild(creditList);
  303. const cesiumCreditContainer = document.createElement("div");
  304. cesiumCreditContainer.className = "cesium-credit-logoContainer";
  305. cesiumCreditContainer.style.display = "inline";
  306. container.appendChild(cesiumCreditContainer);
  307. const screenContainer = document.createElement("div");
  308. screenContainer.className = "cesium-credit-textContainer";
  309. screenContainer.style.display = "inline";
  310. container.appendChild(screenContainer);
  311. const expandLink = document.createElement("a");
  312. expandLink.className = "cesium-credit-expand-link";
  313. expandLink.onclick = this.showLightbox.bind(this);
  314. expandLink.textContent = "Data attribution";
  315. container.appendChild(expandLink);
  316. appendCss(container);
  317. const cesiumCredit = Credit.clone(CreditDisplay.cesiumCredit);
  318. this._delimiter = delimiter ?? "•";
  319. this._screenContainer = screenContainer;
  320. this._cesiumCreditContainer = cesiumCreditContainer;
  321. this._lastViewportHeight = undefined;
  322. this._lastViewportWidth = undefined;
  323. this._lightboxCredits = lightboxCredits;
  324. this._creditList = creditList;
  325. this._lightbox = lightbox;
  326. this._hideLightbox = hideLightbox;
  327. this._expandLink = expandLink;
  328. this._expanded = false;
  329. this._staticCredits = [];
  330. this._cesiumCredit = cesiumCredit;
  331. this._previousCesiumCredit = undefined;
  332. this._currentCesiumCredit = cesiumCredit;
  333. this._creditDisplayElementPool = [];
  334. this._creditDisplayElementIndex = 0;
  335. this._currentFrameCredits = {
  336. screenCredits: new AssociativeArray(),
  337. lightboxCredits: new AssociativeArray(),
  338. };
  339. this._defaultCredit = undefined;
  340. this.viewport = viewport;
  341. /**
  342. * The HTML element where credits will be displayed.
  343. * @type {HTMLElement}
  344. */
  345. this.container = container;
  346. }
  347. function setCredit(creditDisplay, credits, credit, count) {
  348. count = count ?? 1;
  349. let creditDisplayElement = credits.get(credit.id);
  350. if (!defined(creditDisplayElement)) {
  351. const pool = creditDisplay._creditDisplayElementPool;
  352. const poolIndex = creditDisplay._creditDisplayElementPoolIndex;
  353. if (poolIndex < pool.length) {
  354. creditDisplayElement = pool[poolIndex];
  355. creditDisplayElement.credit = credit;
  356. creditDisplayElement.count = count;
  357. } else {
  358. creditDisplayElement = new CreditDisplayElement(credit, count);
  359. pool.push(creditDisplayElement);
  360. }
  361. ++creditDisplay._creditDisplayElementPoolIndex;
  362. credits.set(credit.id, creditDisplayElement);
  363. } else if (creditDisplayElement.count < Number.MAX_VALUE) {
  364. creditDisplayElement.count += count;
  365. }
  366. }
  367. /**
  368. * Adds a {@link Credit} that will show on screen or in the lightbox until
  369. * the next frame. This is mostly for internal use. Use {@link CreditDisplay.addStaticCredit} to add a persistent credit to the screen.
  370. *
  371. * @see CreditDisplay.addStaticCredit
  372. *
  373. * @param {Credit} credit The credit to display in the next frame.
  374. */
  375. CreditDisplay.prototype.addCreditToNextFrame = function (credit) {
  376. //>>includeStart('debug', pragmas.debug);
  377. Check.defined("credit", credit);
  378. //>>includeEnd('debug');
  379. if (credit.isIon()) {
  380. // If this is the an ion logo credit from the ion server
  381. // Just use the default credit (which is identical) to avoid blinking
  382. if (!defined(this._defaultCredit)) {
  383. this._defaultCredit = Credit.clone(getDefaultCredit());
  384. }
  385. this._currentCesiumCredit = this._defaultCredit;
  386. return;
  387. }
  388. let credits;
  389. if (!credit.showOnScreen) {
  390. credits = this._currentFrameCredits.lightboxCredits;
  391. } else {
  392. credits = this._currentFrameCredits.screenCredits;
  393. }
  394. setCredit(this, credits, credit);
  395. };
  396. /**
  397. * Adds a {@link Credit} that will show on screen or in the lightbox until removed with {@link CreditDisplay.removeStaticCredit}.
  398. *
  399. * @param {Credit} credit The credit to added
  400. *
  401. * @example
  402. * // Add a credit with a tooltip, image and link to display onscreen
  403. * const credit = new Cesium.Credit(`<a href="https://cesium.com/" target="_blank"><img src="/images/cesium_logo.png" title="Cesium"/></a>`, true);
  404. * viewer.creditDisplay.addStaticCredit(credit);
  405. *
  406. * @example
  407. * // Add a credit with a plaintext link to display in the lightbox
  408. * const credit = new Cesium.Credit('<a href="https://cesium.com/" target="_blank">Cesium</a>');
  409. * viewer.creditDisplay.addStaticCredit(credit);
  410. */
  411. CreditDisplay.prototype.addStaticCredit = function (credit) {
  412. //>>includeStart('debug', pragmas.debug);
  413. Check.defined("credit", credit);
  414. //>>includeEnd('debug');
  415. const staticCredits = this._staticCredits;
  416. if (!contains(staticCredits, credit)) {
  417. staticCredits.push(credit);
  418. }
  419. };
  420. /**
  421. * Removes a static credit shown on screen or in the lightbox.
  422. *
  423. * @param {Credit} credit The credit to be removed.
  424. */
  425. CreditDisplay.prototype.removeStaticCredit = function (credit) {
  426. //>>includeStart('debug', pragmas.debug);
  427. Check.defined("credit", credit);
  428. //>>includeEnd('debug');
  429. const staticCredits = this._staticCredits;
  430. const index = staticCredits.indexOf(credit);
  431. if (index !== -1) {
  432. staticCredits.splice(index, 1);
  433. }
  434. };
  435. /**
  436. * @private
  437. */
  438. CreditDisplay.prototype.showLightbox = function () {
  439. this._lightbox.style.display = "block";
  440. this._expanded = true;
  441. };
  442. /**
  443. * @private
  444. */
  445. CreditDisplay.prototype.hideLightbox = function () {
  446. this._lightbox.style.display = "none";
  447. this._expanded = false;
  448. };
  449. /**
  450. * Updates the credit display before a new frame is rendered.
  451. */
  452. CreditDisplay.prototype.update = function () {
  453. if (this._expanded) {
  454. styleLightboxContainer(this);
  455. }
  456. };
  457. /**
  458. * Resets the credit display to a beginning of frame state, clearing out current credits.
  459. */
  460. CreditDisplay.prototype.beginFrame = function () {
  461. const currentFrameCredits = this._currentFrameCredits;
  462. this._creditDisplayElementPoolIndex = 0;
  463. const screenCredits = currentFrameCredits.screenCredits;
  464. const lightboxCredits = currentFrameCredits.lightboxCredits;
  465. screenCredits.removeAll();
  466. lightboxCredits.removeAll();
  467. const staticCredits = this._staticCredits;
  468. for (let i = 0; i < staticCredits.length; ++i) {
  469. const staticCredit = staticCredits[i];
  470. const creditCollection = staticCredit.showOnScreen
  471. ? screenCredits
  472. : lightboxCredits;
  473. if (
  474. staticCredit.isIon() &&
  475. Credit.equals(CreditDisplay.cesiumCredit, this._cesiumCredit)
  476. ) {
  477. // If this is an ion logo credit from the ion server,
  478. // make sure to de-duplicate with the default ion credit
  479. continue;
  480. }
  481. setCredit(this, creditCollection, staticCredit, Number.MAX_VALUE);
  482. }
  483. if (!Credit.equals(CreditDisplay.cesiumCredit, this._cesiumCredit)) {
  484. this._cesiumCredit = Credit.clone(CreditDisplay.cesiumCredit);
  485. }
  486. this._currentCesiumCredit = this._cesiumCredit;
  487. };
  488. /**
  489. * Sets the credit display to the end of frame state, displaying credits from the last frame in the credit container.
  490. */
  491. CreditDisplay.prototype.endFrame = function () {
  492. const screenCredits = this._currentFrameCredits.screenCredits.values;
  493. displayCredits(
  494. this._screenContainer,
  495. screenCredits,
  496. this._delimiter,
  497. undefined,
  498. );
  499. const lightboxCredits = this._currentFrameCredits.lightboxCredits.values;
  500. this._expandLink.style.display =
  501. lightboxCredits.length > 0 ? "inline" : "none";
  502. displayCredits(this._creditList, lightboxCredits, undefined, "li");
  503. swapCesiumCredit(this);
  504. };
  505. /**
  506. * Destroys the resources held by this object. Destroying an object allows for deterministic
  507. * release of resources, instead of relying on the garbage collector to destroy this object.
  508. * <br /><br />
  509. * Once an object is destroyed, it should not be used; calling any function other than
  510. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  511. * assign the return value (<code>undefined</code>) to the object as done in the example.
  512. *
  513. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  514. */
  515. CreditDisplay.prototype.destroy = function () {
  516. this._lightbox.removeEventListener("click", this._hideLightbox, false);
  517. this.container.removeChild(this._cesiumCreditContainer);
  518. this.container.removeChild(this._screenContainer);
  519. this.container.removeChild(this._expandLink);
  520. this.viewport.removeChild(this._lightbox);
  521. return destroyObject(this);
  522. };
  523. /**
  524. * Returns true if this object was destroyed; otherwise, false.
  525. * <br /><br />
  526. *
  527. * @returns {boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  528. */
  529. CreditDisplay.prototype.isDestroyed = function () {
  530. return false;
  531. };
  532. CreditDisplay._cesiumCredit = undefined;
  533. CreditDisplay._cesiumCreditInitialized = false;
  534. let defaultCredit;
  535. function getDefaultCredit() {
  536. if (!defined(defaultCredit)) {
  537. let logo = buildModuleUrl("Assets/Images/ion-credit.png");
  538. // When hosting in a WebView, the base URL scheme is file:// or ms-appx-web://
  539. // which is stripped out from the Credit's <img> tag; use the full path instead
  540. if (
  541. logo.indexOf("http://") !== 0 &&
  542. logo.indexOf("https://") !== 0 &&
  543. logo.indexOf("data:") !== 0
  544. ) {
  545. const logoUrl = new Uri(logo);
  546. logo = logoUrl.path();
  547. }
  548. defaultCredit = new Credit(
  549. `<a href="https://cesium.com/" target="_blank"><img src="${logo}" style="vertical-align: -7px" title="Cesium ion"/></a>`,
  550. true,
  551. );
  552. }
  553. if (!CreditDisplay._cesiumCreditInitialized) {
  554. CreditDisplay._cesiumCredit = defaultCredit;
  555. CreditDisplay._cesiumCreditInitialized = true;
  556. }
  557. return defaultCredit;
  558. }
  559. Object.defineProperties(CreditDisplay, {
  560. /**
  561. * Gets or sets the Cesium logo credit.
  562. * @memberof CreditDisplay
  563. * @type {Credit}
  564. */
  565. cesiumCredit: {
  566. get: function () {
  567. getDefaultCredit();
  568. return CreditDisplay._cesiumCredit;
  569. },
  570. set: function (value) {
  571. CreditDisplay._cesiumCredit = value;
  572. CreditDisplay._cesiumCreditInitialized = true;
  573. },
  574. },
  575. });
  576. CreditDisplay.CreditDisplayElement = CreditDisplayElement;
  577. export default CreditDisplay;