viewerDragDropMixin.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*global define*/
  2. define([
  3. '../../Core/defaultValue',
  4. '../../Core/defined',
  5. '../../Core/defineProperties',
  6. '../../Core/DeveloperError',
  7. '../../Core/Event',
  8. '../../Core/wrapFunction',
  9. '../../DataSources/CzmlDataSource',
  10. '../../DataSources/GeoJsonDataSource',
  11. '../getElement'
  12. ], function(
  13. defaultValue,
  14. defined,
  15. defineProperties,
  16. DeveloperError,
  17. Event,
  18. wrapFunction,
  19. CzmlDataSource,
  20. GeoJsonDataSource,
  21. getElement) {
  22. "use strict";
  23. /**
  24. * A mixin which adds default drag and drop support for CZML files to the Viewer widget.
  25. * Rather than being called directly, this function is normally passed as
  26. * a parameter to {@link Viewer#extend}, as shown in the example below.
  27. * @exports viewerDragDropMixin
  28. *
  29. * @param {Viewer} viewer The viewer instance.
  30. * @param {Object} [options] Object with the following properties:
  31. * @param {Element|String} [options.dropTarget=viewer.container] The DOM element which will serve as the drop target.
  32. * @param {Boolean} [options.clearOnDrop=true] When true, dropping files will clear all existing data sources first, when false, new data sources will be loaded after the existing ones.
  33. *
  34. * @exception {DeveloperError} Element with id <options.dropTarget> does not exist in the document.
  35. * @exception {DeveloperError} dropTarget is already defined by another mixin.
  36. * @exception {DeveloperError} dropEnabled is already defined by another mixin.
  37. * @exception {DeveloperError} dropError is already defined by another mixin.
  38. * @exception {DeveloperError} clearOnDrop is already defined by another mixin.
  39. *
  40. * @example
  41. * // Add basic drag and drop support and pop up an alert window on error.
  42. * var viewer = new Cesium.Viewer('cesiumContainer');
  43. * viewer.extend(Cesium.viewerDragDropMixin);
  44. * viewer.dropError.addEventListener(function(viewerArg, source, error) {
  45. * window.alert('Error processing ' + source + ':' + error);
  46. * });
  47. */
  48. var viewerDragDropMixin = function(viewer, options) {
  49. //>>includeStart('debug', pragmas.debug);
  50. if (!defined(viewer)) {
  51. throw new DeveloperError('viewer is required.');
  52. }
  53. if (viewer.hasOwnProperty('dropTarget')) {
  54. throw new DeveloperError('dropTarget is already defined by another mixin.');
  55. }
  56. if (viewer.hasOwnProperty('dropEnabled')) {
  57. throw new DeveloperError('dropEnabled is already defined by another mixin.');
  58. }
  59. if (viewer.hasOwnProperty('dropError')) {
  60. throw new DeveloperError('dropError is already defined by another mixin.');
  61. }
  62. if (viewer.hasOwnProperty('clearOnDrop')) {
  63. throw new DeveloperError('clearOnDrop is already defined by another mixin.');
  64. }
  65. //>>includeEnd('debug');
  66. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  67. //Local variables to be closed over by defineProperties.
  68. var dropEnabled = true;
  69. var dropError = new Event();
  70. var clearOnDrop = defaultValue(options.clearOnDrop, true);
  71. var dropTarget = defaultValue(options.dropTarget, viewer.container);
  72. dropTarget = getElement(dropTarget);
  73. defineProperties(viewer, {
  74. /**
  75. * Gets or sets the element to serve as the drop target.
  76. * @memberof viewerDragDropMixin.prototype
  77. * @type {Element}
  78. */
  79. dropTarget : {
  80. //TODO See https://github.com/AnalyticalGraphicsInc/cesium/issues/832
  81. get : function() {
  82. return dropTarget;
  83. },
  84. set : function(value) {
  85. //>>includeStart('debug', pragmas.debug);
  86. if (!defined(value)) {
  87. throw new DeveloperError('value is required.');
  88. }
  89. //>>includeEnd('debug');
  90. unsubscribe(dropTarget, handleDrop);
  91. dropTarget = value;
  92. subscribe(dropTarget, handleDrop);
  93. }
  94. },
  95. /**
  96. * Gets or sets a value indicating if drag and drop support is enabled.
  97. * @memberof viewerDragDropMixin.prototype
  98. * @type {Element}
  99. */
  100. dropEnabled : {
  101. get : function() {
  102. return dropEnabled;
  103. },
  104. set : function(value) {
  105. if (value !== dropEnabled) {
  106. if (value) {
  107. subscribe(dropTarget, handleDrop);
  108. } else {
  109. unsubscribe(dropTarget, handleDrop);
  110. }
  111. dropEnabled = value;
  112. }
  113. }
  114. },
  115. /**
  116. * Gets the event that will be raised when an error is encountered during drop processing.
  117. * @memberof viewerDragDropMixin.prototype
  118. * @type {Event}
  119. */
  120. dropError : {
  121. get : function() {
  122. return dropError;
  123. }
  124. },
  125. /**
  126. * Gets or sets a value indicating if existing data sources should be cleared before adding the newly dropped sources.
  127. * @memberof viewerDragDropMixin.prototype
  128. * @type {Boolean}
  129. */
  130. clearOnDrop : {
  131. get : function() {
  132. return clearOnDrop;
  133. },
  134. set : function(value) {
  135. clearOnDrop = value;
  136. }
  137. }
  138. });
  139. function handleDrop(event) {
  140. stop(event);
  141. if (clearOnDrop) {
  142. viewer.dataSources.removeAll();
  143. }
  144. var files = event.dataTransfer.files;
  145. var length = files.length;
  146. for (var i = 0; i < length; i++) {
  147. var file = files[i];
  148. var reader = new FileReader();
  149. reader.onload = createOnLoadCallback(viewer, file);
  150. reader.onerror = createDropErrorCallback(viewer, file);
  151. reader.readAsText(file);
  152. }
  153. }
  154. //Enable drop by default;
  155. subscribe(dropTarget, handleDrop);
  156. //Wrap the destroy function to make sure all events are unsubscribed from
  157. viewer.destroy = wrapFunction(viewer, viewer.destroy, function() {
  158. viewer.dropEnabled = false;
  159. });
  160. //Specs need access to handleDrop
  161. viewer._handleDrop = handleDrop;
  162. };
  163. function stop(event) {
  164. event.stopPropagation();
  165. event.preventDefault();
  166. }
  167. function unsubscribe(dropTarget, handleDrop) {
  168. var currentTarget = dropTarget;
  169. if (defined(currentTarget)) {
  170. currentTarget.removeEventListener('drop', handleDrop, false);
  171. currentTarget.removeEventListener('dragenter', stop, false);
  172. currentTarget.removeEventListener('dragover', stop, false);
  173. currentTarget.removeEventListener('dragexit', stop, false);
  174. }
  175. }
  176. function subscribe(dropTarget, handleDrop) {
  177. dropTarget.addEventListener('drop', handleDrop, false);
  178. dropTarget.addEventListener('dragenter', stop, false);
  179. dropTarget.addEventListener('dragover', stop, false);
  180. dropTarget.addEventListener('dragexit', stop, false);
  181. }
  182. function createOnLoadCallback(viewer, file) {
  183. return function(evt) {
  184. var fileName = file.name;
  185. try {
  186. var dataSource;
  187. var loadPromise;
  188. if (/\.czml$/i.test(fileName)) {
  189. dataSource = new CzmlDataSource(fileName);
  190. dataSource.load(JSON.parse(evt.target.result), fileName);
  191. } else if (/\.geojson$/i.test(fileName) || /\.json$/i.test(fileName) || /\.topojson$/i.test(fileName)) {
  192. dataSource = new GeoJsonDataSource(fileName);
  193. loadPromise = dataSource.load(JSON.parse(evt.target.result), {
  194. sourceUri : fileName
  195. });
  196. } else {
  197. viewer.dropError.raiseEvent(viewer, fileName, 'Unrecognized file: ' + fileName);
  198. return;
  199. }
  200. viewer.dataSources.add(dataSource);
  201. if (defined(loadPromise)) {
  202. loadPromise.otherwise(function(error) {
  203. viewer.dropError.raiseEvent(viewer, fileName, error);
  204. });
  205. }
  206. } catch (error) {
  207. viewer.dropError.raiseEvent(viewer, fileName, error);
  208. }
  209. };
  210. }
  211. function createDropErrorCallback(viewer, file) {
  212. return function(evt) {
  213. viewer.dropError.raiseEvent(viewer, file.name, evt.target.error);
  214. };
  215. }
  216. return viewerDragDropMixin;
  217. });