CreditDisplay.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  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 defaultValue from '../Core/defaultValue.js';
  6. import defined from '../Core/defined.js';
  7. import defineProperties from '../Core/defineProperties.js';
  8. import destroyObject from '../Core/destroyObject.js';
  9. var mobileWidth = 576;
  10. var lightboxHeight = 100;
  11. var textColor = '#ffffff';
  12. var highlightColor = '#48b';
  13. function contains(credits, credit) {
  14. var len = credits.length;
  15. for (var i = 0; i < len; i++) {
  16. var existingCredit = credits[i];
  17. if (Credit.equals(existingCredit, credit)) {
  18. return true;
  19. }
  20. }
  21. return false;
  22. }
  23. function swapCesiumCredit(creditDisplay) {
  24. // We don't want to clutter the screen with the Cesium logo and the Cesium ion
  25. // logo at the same time. Since the ion logo is required, we just replace the
  26. // Cesium logo or add the logo if the Cesium one was removed.
  27. var previousCredit = creditDisplay._previousCesiumCredit;
  28. var currentCredit = creditDisplay._currentCesiumCredit;
  29. if (Credit.equals(currentCredit, previousCredit)) {
  30. return;
  31. }
  32. if (defined(previousCredit)) {
  33. creditDisplay._cesiumCreditContainer.removeChild(previousCredit.element);
  34. }
  35. if (defined(currentCredit)) {
  36. creditDisplay._cesiumCreditContainer.appendChild(currentCredit.element);
  37. }
  38. creditDisplay._previousCesiumCredit = currentCredit;
  39. }
  40. var delimiterClassName = 'cesium-credit-delimiter';
  41. function createDelimiterElement(delimiter) {
  42. var delimiterElement = document.createElement('span');
  43. delimiterElement.textContent = delimiter;
  44. delimiterElement.className = delimiterClassName;
  45. return delimiterElement;
  46. }
  47. function createCreditElement(element, elementWrapperTagName) {
  48. // may need to wrap the credit in another element
  49. if (defined(elementWrapperTagName)) {
  50. var wrapper = document.createElement(elementWrapperTagName);
  51. wrapper._creditId = element._creditId;
  52. wrapper.appendChild(element);
  53. element = wrapper;
  54. }
  55. return element;
  56. }
  57. function displayCredits(container, credits, delimiter, elementWrapperTagName) {
  58. var childNodes = container.childNodes;
  59. var domIndex = -1;
  60. for (var creditIndex = 0; creditIndex < credits.length; ++creditIndex) {
  61. var credit = credits[creditIndex];
  62. if (defined(credit)) {
  63. domIndex = creditIndex;
  64. if (defined(delimiter)) {
  65. // credits may be separated by delimiters
  66. domIndex *= 2;
  67. if (creditIndex > 0) {
  68. var delimiterDomIndex = domIndex - 1;
  69. if (childNodes.length <= delimiterDomIndex) {
  70. container.appendChild(createDelimiterElement(delimiter));
  71. } else {
  72. var existingDelimiter = childNodes[delimiterDomIndex];
  73. if (existingDelimiter.className !== delimiterClassName) {
  74. container.replaceChild(createDelimiterElement(delimiter), existingDelimiter);
  75. }
  76. }
  77. }
  78. }
  79. var element = credit.element;
  80. // check to see if the correct credit is in the right place
  81. if (childNodes.length <= domIndex) {
  82. container.appendChild(createCreditElement(element, elementWrapperTagName));
  83. } else {
  84. var existingElement = childNodes[domIndex];
  85. if (existingElement._creditId !== credit._id) {
  86. // not the right credit, swap it in
  87. container.replaceChild(createCreditElement(element, elementWrapperTagName), existingElement);
  88. }
  89. }
  90. }
  91. }
  92. // any remaining nodes in the container are unnecessary
  93. ++domIndex;
  94. while (domIndex < childNodes.length) {
  95. container.removeChild(childNodes[domIndex]);
  96. }
  97. }
  98. function styleLightboxContainer(that) {
  99. var lightboxCredits = that._lightboxCredits;
  100. var width = that.viewport.clientWidth;
  101. var height = that.viewport.clientHeight;
  102. if (width !== that._lastViewportWidth) {
  103. if (width < mobileWidth) {
  104. lightboxCredits.className = 'cesium-credit-lightbox cesium-credit-lightbox-mobile';
  105. lightboxCredits.style.marginTop = '0';
  106. } else {
  107. lightboxCredits.className = 'cesium-credit-lightbox cesium-credit-lightbox-expanded';
  108. lightboxCredits.style.marginTop = Math.floor((height - lightboxCredits.clientHeight) * 0.5) + 'px';
  109. }
  110. that._lastViewportWidth = width;
  111. }
  112. if (width >= mobileWidth && height !== that._lastViewportHeight) {
  113. lightboxCredits.style.marginTop = Math.floor((height - lightboxCredits.clientHeight) * 0.5) + 'px';
  114. that._lastViewportHeight = height;
  115. }
  116. }
  117. function addStyle(selector, styles) {
  118. var style = selector + ' {';
  119. for (var attribute in styles) {
  120. if (styles.hasOwnProperty(attribute)) {
  121. style += attribute + ': ' + styles[attribute] + '; ';
  122. }
  123. }
  124. style += ' }\n';
  125. return style;
  126. }
  127. function appendCss() {
  128. var style = '';
  129. style += addStyle('.cesium-credit-lightbox-overlay', {
  130. display : 'none',
  131. 'z-index' : '1', //must be at least 1 to draw over top other Cesium widgets
  132. position : 'absolute',
  133. top : '0',
  134. left : '0',
  135. width : '100%',
  136. height : '100%',
  137. 'background-color' : 'rgba(80, 80, 80, 0.8)'
  138. });
  139. style += addStyle('.cesium-credit-lightbox', {
  140. 'background-color' : '#303336',
  141. color : textColor,
  142. position : 'relative',
  143. 'min-height' : lightboxHeight + 'px',
  144. margin : 'auto'
  145. });
  146. style += addStyle('.cesium-credit-lightbox > ul > li a, .cesium-credit-lightbox > ul > li a:visited', {
  147. color : textColor
  148. });
  149. style += addStyle('.cesium-credit-lightbox > ul > li a:hover', {
  150. color : highlightColor
  151. });
  152. style += addStyle('.cesium-credit-lightbox.cesium-credit-lightbox-expanded', {
  153. border : '1px solid #444',
  154. 'border-radius' : '5px',
  155. 'max-width' : '370px'
  156. });
  157. style += addStyle('.cesium-credit-lightbox.cesium-credit-lightbox-mobile', {
  158. height : '100%',
  159. width : '100%'
  160. });
  161. style += addStyle('.cesium-credit-lightbox-title', {
  162. padding : '20px 20px 0 20px'
  163. });
  164. style += addStyle('.cesium-credit-lightbox-close', {
  165. 'font-size' : '18pt',
  166. cursor : 'pointer',
  167. position : 'absolute',
  168. top : '0',
  169. right : '6px',
  170. color : textColor
  171. });
  172. style += addStyle('.cesium-credit-lightbox-close:hover', {
  173. color : highlightColor
  174. });
  175. style += addStyle('.cesium-credit-lightbox > ul', {
  176. margin : '0',
  177. padding : '12px 20px 12px 40px',
  178. 'font-size' : '13px'
  179. });
  180. style += addStyle('.cesium-credit-lightbox > ul > li', {
  181. 'padding-bottom' : '6px'
  182. });
  183. style += addStyle('.cesium-credit-lightbox > ul > li *', {
  184. padding : '0',
  185. margin : '0'
  186. });
  187. style += addStyle('.cesium-credit-expand-link', {
  188. 'padding-left' : '5px',
  189. cursor : 'pointer',
  190. 'text-decoration' : 'underline',
  191. color : textColor
  192. });
  193. style += addStyle('.cesium-credit-expand-link:hover', {
  194. 'color' : highlightColor
  195. });
  196. style += addStyle('.cesium-credit-text', {
  197. color : textColor
  198. });
  199. style += addStyle('.cesium-credit-textContainer *, .cesium-credit-logoContainer *', {
  200. display : 'inline'
  201. });
  202. var head = document.head;
  203. var css = document.createElement('style');
  204. css.innerHTML = style;
  205. head.insertBefore(css, head.firstChild);
  206. }
  207. /**
  208. * The credit display is responsible for displaying credits on screen.
  209. *
  210. * @param {HTMLElement} container The HTML element where credits will be displayed
  211. * @param {String} [delimiter= ' • '] The string to separate text credits
  212. * @param {HTMLElement} [viewport=document.body] The HTML element that will contain the credits popup
  213. *
  214. * @alias CreditDisplay
  215. * @constructor
  216. *
  217. * @example
  218. * var creditDisplay = new Cesium.CreditDisplay(creditContainer);
  219. */
  220. function CreditDisplay(container, delimiter, viewport) {
  221. //>>includeStart('debug', pragmas.debug);
  222. Check.defined('container', container);
  223. //>>includeEnd('debug');
  224. var that = this;
  225. viewport = defaultValue(viewport, document.body);
  226. var lightbox = document.createElement('div');
  227. lightbox.className = 'cesium-credit-lightbox-overlay';
  228. viewport.appendChild(lightbox);
  229. var lightboxCredits = document.createElement('div');
  230. lightboxCredits.className = 'cesium-credit-lightbox';
  231. lightbox.appendChild(lightboxCredits);
  232. function hideLightbox(event) {
  233. if (lightboxCredits.contains(event.target)) {
  234. return;
  235. }
  236. that.hideLightbox();
  237. }
  238. lightbox.addEventListener('click', hideLightbox, false);
  239. var title = document.createElement('div');
  240. title.className = 'cesium-credit-lightbox-title';
  241. title.textContent = 'Data provided by:';
  242. lightboxCredits.appendChild(title);
  243. var closeButton = document.createElement('a');
  244. closeButton.onclick = this.hideLightbox.bind(this);
  245. closeButton.innerHTML = '&times;';
  246. closeButton.className = 'cesium-credit-lightbox-close';
  247. lightboxCredits.appendChild(closeButton);
  248. var creditList = document.createElement('ul');
  249. lightboxCredits.appendChild(creditList);
  250. var cesiumCreditContainer = document.createElement('div');
  251. cesiumCreditContainer.className = 'cesium-credit-logoContainer';
  252. cesiumCreditContainer.style.display = 'inline';
  253. container.appendChild(cesiumCreditContainer);
  254. var screenContainer = document.createElement('div');
  255. screenContainer.className = 'cesium-credit-textContainer';
  256. screenContainer.style.display = 'inline';
  257. container.appendChild(screenContainer);
  258. var expandLink = document.createElement('a');
  259. expandLink.className = 'cesium-credit-expand-link';
  260. expandLink.onclick = this.showLightbox.bind(this);
  261. expandLink.textContent = 'Data attribution';
  262. container.appendChild(expandLink);
  263. appendCss();
  264. var cesiumCredit = Credit.clone(CreditDisplay.cesiumCredit);
  265. this._delimiter = defaultValue(delimiter, ' • ');
  266. this._screenContainer = screenContainer;
  267. this._cesiumCreditContainer = cesiumCreditContainer;
  268. this._lastViewportHeight = undefined;
  269. this._lastViewportWidth = undefined;
  270. this._lightboxCredits = lightboxCredits;
  271. this._creditList = creditList;
  272. this._lightbox = lightbox;
  273. this._hideLightbox = hideLightbox;
  274. this._expandLink = expandLink;
  275. this._expanded = false;
  276. this._defaultCredits = [];
  277. this._cesiumCredit = cesiumCredit;
  278. this._previousCesiumCredit = undefined;
  279. this._currentCesiumCredit = cesiumCredit;
  280. this._currentFrameCredits = {
  281. screenCredits : new AssociativeArray(),
  282. lightboxCredits : new AssociativeArray()
  283. };
  284. this._defaultCredit = undefined;
  285. this.viewport = viewport;
  286. /**
  287. * The HTML element where credits will be displayed.
  288. * @type {HTMLElement}
  289. */
  290. this.container = container;
  291. }
  292. /**
  293. * Adds a credit to the list of current credits to be displayed in the credit container
  294. *
  295. * @param {Credit} credit The credit to display
  296. */
  297. CreditDisplay.prototype.addCredit = function(credit) {
  298. //>>includeStart('debug', pragmas.debug);
  299. Check.defined('credit', credit);
  300. //>>includeEnd('debug');
  301. if (credit._isIon) {
  302. // If this is the an ion logo credit from the ion server
  303. // Juse use the default credit (which is identical) to avoid blinking
  304. if (!defined(this._defaultCredit)) {
  305. this._defaultCredit = Credit.clone(getDefaultCredit());
  306. }
  307. this._currentCesiumCredit = this._defaultCredit;
  308. return;
  309. }
  310. if (!credit.showOnScreen) {
  311. this._currentFrameCredits.lightboxCredits.set(credit.id, credit);
  312. } else {
  313. this._currentFrameCredits.screenCredits.set(credit.id, credit);
  314. }
  315. };
  316. /**
  317. * Adds credits that will persist until they are removed
  318. *
  319. * @param {Credit} credit The credit to added to defaults
  320. */
  321. CreditDisplay.prototype.addDefaultCredit = function(credit) {
  322. //>>includeStart('debug', pragmas.debug);
  323. Check.defined('credit', credit);
  324. //>>includeEnd('debug');
  325. var defaultCredits = this._defaultCredits;
  326. if (!contains(defaultCredits, credit)) {
  327. defaultCredits.push(credit);
  328. }
  329. };
  330. /**
  331. * Removes a default credit
  332. *
  333. * @param {Credit} credit The credit to be removed from defaults
  334. */
  335. CreditDisplay.prototype.removeDefaultCredit = function(credit) {
  336. //>>includeStart('debug', pragmas.debug);
  337. Check.defined('credit', credit);
  338. //>>includeEnd('debug');
  339. var defaultCredits = this._defaultCredits;
  340. var index = defaultCredits.indexOf(credit);
  341. if (index !== -1) {
  342. defaultCredits.splice(index, 1);
  343. }
  344. };
  345. CreditDisplay.prototype.showLightbox = function() {
  346. this._lightbox.style.display = 'block';
  347. this._expanded = true;
  348. };
  349. CreditDisplay.prototype.hideLightbox = function() {
  350. this._lightbox.style.display = 'none';
  351. this._expanded = false;
  352. };
  353. /**
  354. * Updates the credit display before a new frame is rendered.
  355. */
  356. CreditDisplay.prototype.update = function() {
  357. if (this._expanded) {
  358. styleLightboxContainer(this);
  359. }
  360. };
  361. /**
  362. * Resets the credit display to a beginning of frame state, clearing out current credits.
  363. */
  364. CreditDisplay.prototype.beginFrame = function() {
  365. var currentFrameCredits = this._currentFrameCredits;
  366. var screenCredits = currentFrameCredits.screenCredits;
  367. screenCredits.removeAll();
  368. var defaultCredits = this._defaultCredits;
  369. for (var i = 0; i < defaultCredits.length; ++i) {
  370. var defaultCredit = defaultCredits[i];
  371. screenCredits.set(defaultCredit.id, defaultCredit);
  372. }
  373. currentFrameCredits.lightboxCredits.removeAll();
  374. if (!Credit.equals(CreditDisplay.cesiumCredit, this._cesiumCredit)) {
  375. this._cesiumCredit = Credit.clone(CreditDisplay.cesiumCredit);
  376. }
  377. this._currentCesiumCredit = this._cesiumCredit;
  378. };
  379. /**
  380. * Sets the credit display to the end of frame state, displaying credits from the last frame in the credit container.
  381. */
  382. CreditDisplay.prototype.endFrame = function() {
  383. var screenCredits = this._currentFrameCredits.screenCredits.values;
  384. displayCredits(this._screenContainer, screenCredits, this._delimiter, undefined);
  385. var lightboxCredits = this._currentFrameCredits.lightboxCredits.values;
  386. this._expandLink.style.display = lightboxCredits.length > 0 ? 'inline' : 'none';
  387. displayCredits(this._creditList, lightboxCredits, undefined, 'li');
  388. swapCesiumCredit(this);
  389. };
  390. /**
  391. * Destroys the resources held by this object. Destroying an object allows for deterministic
  392. * release of resources, instead of relying on the garbage collector to destroy this object.
  393. * <br /><br />
  394. * Once an object is destroyed, it should not be used; calling any function other than
  395. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  396. * assign the return value (<code>undefined</code>) to the object as done in the example.
  397. *
  398. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  399. */
  400. CreditDisplay.prototype.destroy = function() {
  401. this._lightbox.removeEventListener('click', this._hideLightbox, false);
  402. this.container.removeChild(this._cesiumCreditContainer);
  403. this.container.removeChild(this._screenContainer);
  404. this.container.removeChild(this._expandLink);
  405. this.viewport.removeChild(this._lightbox);
  406. return destroyObject(this);
  407. };
  408. /**
  409. * Returns true if this object was destroyed; otherwise, false.
  410. * <br /><br />
  411. *
  412. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  413. */
  414. CreditDisplay.prototype.isDestroyed = function() {
  415. return false;
  416. };
  417. CreditDisplay._cesiumCredit = undefined;
  418. CreditDisplay._cesiumCreditInitialized = false;
  419. var defaultCredit;
  420. function getDefaultCredit() {
  421. if (!defined(defaultCredit)) {
  422. var logo = buildModuleUrl('Assets/Images/ion-credit.png');
  423. defaultCredit = new Credit('<a href="https://cesium.com/" target="_blank"><img src="' + logo + '" title="Cesium ion"/></a>', true);
  424. }
  425. if (!CreditDisplay._cesiumCreditInitialized) {
  426. CreditDisplay._cesiumCredit = defaultCredit;
  427. CreditDisplay._cesiumCreditInitialized = true;
  428. }
  429. return defaultCredit;
  430. }
  431. defineProperties(CreditDisplay, {
  432. /**
  433. * Gets or sets the Cesium logo credit.
  434. * @memberof CreditDisplay
  435. * @type {Credit}
  436. */
  437. cesiumCredit : {
  438. get : function() {
  439. getDefaultCredit();
  440. return CreditDisplay._cesiumCredit;
  441. },
  442. set : function(value) {
  443. CreditDisplay._cesiumCredit = value;
  444. CreditDisplay._cesiumCreditInitialized = true;
  445. }
  446. }
  447. });
  448. export default CreditDisplay;