mappers.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import { Tools } from 'babylonjs/Misc/tools';
  2. import { ViewerConfiguration } from './configuration';
  3. import { kebabToCamel } from '../helper/';
  4. /**
  5. * This is the mapper's interface. Implement this function to create your own mapper and register it at the mapper manager
  6. */
  7. export interface IMapper {
  8. map(rawSource: any): ViewerConfiguration;
  9. }
  10. /**
  11. * This is a simple HTML mapper.
  12. * This mapper parses a single HTML element and returns the configuration from its attributes.
  13. * it parses numbers and boolean values to the corresponding variable types.
  14. * The following HTML element:
  15. * <div test="1" random-flag="true" a.string.object="test"> will result in the following configuration:
  16. *
  17. * {
  18. * test: 1, //a number!
  19. * randomFlag: boolean, //camelCase and boolean
  20. * a: {
  21. * string: {
  22. * object: "test" //dot-separated object levels
  23. * }
  24. * }
  25. * }
  26. */
  27. class HTMLMapper implements IMapper {
  28. /**
  29. * Map a specific element and get configuration from it
  30. * @param element the HTML element to analyze.
  31. */
  32. map(element: HTMLElement): ViewerConfiguration {
  33. let config = {};
  34. for (let attrIdx = 0; attrIdx < element.attributes.length; ++attrIdx) {
  35. let attr = element.attributes.item(attrIdx);
  36. if (!attr) {
  37. continue;
  38. }
  39. // map "object.property" to the right configuration place.
  40. let split = attr.nodeName.split('.');
  41. split.reduce((currentConfig, key, idx) => {
  42. //convert html-style to json-style
  43. let camelKey = kebabToCamel(key);
  44. if (idx === split.length - 1) {
  45. let val: any = attr!.nodeValue; // firefox warns nodeValue is deprecated, but I found no sign of it anywhere.
  46. if (val === "true") {
  47. val = true;
  48. } else if (val === "false") {
  49. val = false;
  50. } else if (val === "undefined") {
  51. val = undefined;
  52. } else if (val === "null") {
  53. val = null;
  54. } else {
  55. var isnum = !isNaN(parseFloat(val)) && isFinite(val); ///^\d+$/.test(val);
  56. if (isnum) {
  57. let number = parseFloat(val);
  58. if (!isNaN(number)) {
  59. val = number;
  60. }
  61. }
  62. }
  63. currentConfig[camelKey] = val;
  64. } else {
  65. currentConfig[camelKey] = currentConfig[camelKey] || {};
  66. }
  67. return currentConfig[camelKey];
  68. }, config);
  69. }
  70. return config;
  71. }
  72. }
  73. /**
  74. * A simple string-to-JSON mapper.
  75. * This is the main mapper, used to analyze downloaded JSON-Configuration or JSON payload
  76. */
  77. class JSONMapper implements IMapper {
  78. map(rawSource: string) {
  79. return JSON.parse(rawSource);
  80. }
  81. }
  82. /**
  83. * The DOM Mapper will traverse an entire DOM Tree and will load the configuration from the
  84. * DOM elements and attributes.
  85. */
  86. class DOMMapper implements IMapper {
  87. /**
  88. * The mapping function that will convert HTML data to a viewer configuration object
  89. * @param baseElement the baseElement from which to start traversing
  90. * @returns a ViewerCOnfiguration object from the provided HTML Element
  91. */
  92. map(baseElement: HTMLElement): ViewerConfiguration {
  93. let htmlMapper = new HTMLMapper();
  94. let config = htmlMapper.map(baseElement);
  95. let traverseChildren = function(element: HTMLElement, partConfig) {
  96. let children = element.children;
  97. if (children.length) {
  98. for (let i = 0; i < children.length; ++i) {
  99. let item = <HTMLElement>children.item(i);
  100. // use the HTML Mapper to read configuration from a single element
  101. let configMapped = htmlMapper.map(item);
  102. let key = kebabToCamel(item.nodeName.toLowerCase());
  103. if (item.attributes.getNamedItem('array') && item.attributes.getNamedItem('array')!.nodeValue === 'true') {
  104. partConfig[key] = [];
  105. } else {
  106. if (element.attributes.getNamedItem('array') && element.attributes.getNamedItem('array')!.nodeValue === 'true') {
  107. partConfig.push(configMapped);
  108. } else if (partConfig[key]) {
  109. //exists already! probably an array
  110. element.setAttribute('array', 'true');
  111. let oldItem = partConfig[key];
  112. partConfig = [oldItem, configMapped];
  113. } else {
  114. partConfig[key] = configMapped;
  115. }
  116. }
  117. traverseChildren(item, partConfig[key] || configMapped);
  118. }
  119. }
  120. return partConfig;
  121. };
  122. traverseChildren(baseElement, config);
  123. return config;
  124. }
  125. }
  126. /**
  127. * The MapperManager manages the different implemented mappers.
  128. * It allows the user to register new mappers as well and use them to parse their own configuration data
  129. */
  130. export class MapperManager {
  131. private _mappers: { [key: string]: IMapper };
  132. /**
  133. * The default mapper is the JSON mapper.
  134. */
  135. public static DefaultMapper = 'json';
  136. constructor() {
  137. this._mappers = {
  138. "html": new HTMLMapper(),
  139. "json": new JSONMapper(),
  140. "dom": new DOMMapper()
  141. };
  142. }
  143. /**
  144. * Get a specific configuration mapper.
  145. *
  146. * @param type the name of the mapper to load
  147. */
  148. public getMapper(type: string) {
  149. if (!this._mappers[type]) {
  150. Tools.Error("No mapper defined for " + type);
  151. }
  152. return this._mappers[type];
  153. }
  154. /**
  155. * Use this functio to register your own configuration mapper.
  156. * After a mapper is registered, it can be used to parse the specific type fo configuration to the standard ViewerConfiguration.
  157. * @param type the name of the mapper. This will be used to define the configuration type and/or to get the mapper
  158. * @param mapper The implemented mapper
  159. */
  160. public registerMapper(type: string, mapper: IMapper) {
  161. this._mappers[type] = mapper;
  162. }
  163. /**
  164. * Dispose the mapper manager and all of its mappers.
  165. */
  166. public dispose() {
  167. this._mappers = {};
  168. }
  169. }
  170. /**
  171. * mapperManager is a singleton of the type MapperManager.
  172. * The mapperManager can be disposed directly with calling mapperManager.dispose()
  173. * or indirectly with using BabylonViewer.disposeAll()
  174. */
  175. export let mapperManager = new MapperManager();