utility.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /*
  2. The MIT License (MIT)
  3. Copyright (c) 2014-2020 Nikolai Suslov and the Krestianstvo.org project contributors. (https://github.com/NikolaySuslov/livecodingspace/blob/master/LICENSE.md)
  4. Virtual World Framework Apache 2.0 license (https://github.com/NikolaySuslov/livecodingspace/blob/master/licenses/LICENSE_VWF.md)
  5. */
  6. /// Kernel utility functions.
  7. ///
  8. /// @module vwf/utility
  9. class Utility {
  10. constructor() {
  11. //console.log("Utility constructor");
  12. let self = this;
  13. // -- transforms ---------------------------------------------------------------------------
  14. /// Transformation functions for vwf.utility.transform. Invoke these as:
  15. ///
  16. /// utility.transform( object, utility.transforms.*transform* )
  17. ///
  18. /// to apply the transform utility.transforms.*transform* to object.
  19. this.transforms = {
  20. // -- transit --------------------------------------------------------------------------
  21. /// A vwf.utility.transform transformation function to convert an object for proper JSON
  22. /// serialization. Array-like objects are converted to actual Arrays. All other objects
  23. /// are unchanged. Invoke as: utility.transform( object, utility.transforms.transit ).
  24. ///
  25. /// @param {Object} object
  26. /// The object being transformed or one of its descendants.
  27. ///
  28. /// @returns {Object}
  29. /// An Array-like converted to an Array, or *object*.
  30. transit: function( object ) {
  31. // Determine if an object is Array-like (but not an Array) to identify objects that
  32. // need to be converted to Arrays for normalization.
  33. function isArraylike( candidate ) {
  34. var arraylike = false;
  35. // Filter with typeof and instanceof since they're much faster than toString().
  36. // Then check for typed arrays (Int8Array, Uint8Array, ...) or the Arguments
  37. // object using the type string.
  38. if ( typeof candidate == "object" && candidate != null && ! ( candidate instanceof Array ) ) {
  39. var typeString = Object.prototype.toString.call( candidate ) // eg, "[object *Type*]"
  40. arraylike = ( typeString.slice( -6 ) == "Array]" || typeString == "[object Arguments]" );
  41. }
  42. return arraylike;
  43. };
  44. // Convert typed arrays to regular arrays.
  45. if ((window.THREE === !undefined) &&
  46. (object instanceof THREE.Vector2 || object instanceof THREE.Vector3 || object instanceof THREE.Vector4)) {
  47. return AFRAME.utils.coordinates.stringify(object)
  48. } else {
  49. return isArraylike(object) ?
  50. Array.prototype.slice.call(object) : object;
  51. }
  52. },
  53. // -- hash -----------------------------------------------------------------------------
  54. /// A vwf.utility.transform transformation function to normalize an object so that it
  55. /// can be serialized and hashed with consistent results. Numeric precision is reduced
  56. /// to match the precision retained by the reflector. Non-Array objects are reordered so
  57. /// that their keys are in alphabetic order. Other objects are unchanged. Invoke as:
  58. /// utility.transform( object, utility.transforms.hash ).
  59. ///
  60. /// @param {Object} object
  61. /// The object being transformed or one of its descendants.
  62. ///
  63. /// @returns {Object}
  64. /// A reduced-precision number, an Object with alphabetic keys, or *object*.
  65. hash: function( object ) {
  66. // Apply the `transit` transform first to convert Array-likes into Arrays.
  67. object = exports.transforms.transit( object );
  68. // Reduce numeric precision slightly to match what passes through the reflector.
  69. if ( typeof object == "number" ) {
  70. return Number( object.toPrecision(15) );
  71. }
  72. // Order objects alphabetically.
  73. else if ( typeof object == "object" && object != null && ! ( object instanceof Array ) ) {
  74. var ordered = {};
  75. Object.keys( object ).sort().forEach( function( key ) {
  76. ordered[key] = object[key];
  77. } );
  78. return ordered;
  79. }
  80. return object;
  81. },
  82. }
  83. }
  84. // -- transform ----------------------------------------------------------------------------
  85. /// Recursively transform an arbitrary object using the provided transformation function.
  86. /// Containers are duplicated where necessary so that the original object and any contained
  87. /// objects are not modified. Unchanged objects will be referenced directly in the result.
  88. ///
  89. /// @param {Object} object
  90. /// The object to transform. `Object` and `Array` contents are recursively transformed
  91. /// using the same transformation function.
  92. /// @param {Function} transformation
  93. /// The transformation function: `function( object, names, depth, finished ) { return object }`
  94. /// Call `finished()` from the transformation function to stop recursion for the current
  95. /// object.
  96. /// @param {(Number|String)[]} [names]
  97. /// Array of the names or indexes that refer to `object` in its container and in its
  98. /// containers' containers, parent first (recursive calls only).
  99. /// @param {Number} [depth]
  100. /// Recursion depth (recursive calls only).
  101. ///
  102. /// @returns {Object}
  103. /// The transformed object.
  104. transform ( object, transformation /* ( object, names, depth, finished ) */, names, depth ) {
  105. names = names || [];
  106. depth = depth || 0;
  107. var finished = false, item;
  108. var result = object = transformation( object, names, depth, function() { finished = true } );
  109. if ( typeof object === "object" && object !== null && ! finished ) {
  110. if ( object instanceof Array ) {
  111. // Recursively transform the elements if the object is an Array.
  112. for ( var index = 0; index < object.length; index++ ) {
  113. if ( ( item = this.transform( object[index], transformation,
  114. [ index ].concat( names ), depth + 1 ) ) !== object[index] ) {
  115. // If the item changed, and it's the first change in the array, then
  116. // duplicate the array.
  117. if ( result === object ) {
  118. result = [].concat( object ); // shallow copy into a new Array
  119. }
  120. // Assign the transformed item.
  121. result[index] = item;
  122. }
  123. }
  124. } else {
  125. // Recursively transform the properties if the object is an Object.
  126. Object.keys( object ).forEach( function( key ) {
  127. if ( ( item = this.transform( object[key], transformation,
  128. [ key ].concat( names ), depth + 1 ) ) !== object[key] ) {
  129. // If the item changed, and it's the first change in the object, then
  130. // duplicate the object.
  131. if ( result === object ) {
  132. result = {};
  133. Object.keys( object ).forEach( function( k ) {
  134. result[ k ] = object[ k ]; // shallow copy into a new Object
  135. } );
  136. // Also copy the non-enumerable `length` property for an `arguments`
  137. // object.
  138. var lengthDescriptor = Object.getOwnPropertyDescriptor( object, "length" );
  139. if ( lengthDescriptor && ! lengthDescriptor.enumerable ) {
  140. Object.defineProperty( result, "length", lengthDescriptor );
  141. }
  142. }
  143. // Assign the transformed item.
  144. result[key] = item;
  145. }
  146. }, this );
  147. }
  148. }
  149. return result;
  150. }
  151. // -- exceptionMessage ---------------------------------------------------------------------
  152. /// Format the stack trace for readability.
  153. ///
  154. /// @param {Error} error
  155. /// An Error object, generally provided by a catch statement.
  156. ///
  157. /// @returns {String}
  158. /// An error message that may be written to the console or a log.
  159. exceptionMessage ( error ) {
  160. // https://github.com/eriwen/javascript-stacktrace sniffs the browser type from the
  161. // exception this way.
  162. if ( error instanceof ReferenceError) {
  163. return "\n " + error.stack;
  164. }
  165. else if ( error.arguments && error.stack ) { // Chrome
  166. return "\n " + error.stack;
  167. } else if ( window && window.opera ) { // Opera
  168. return error.toString();
  169. } else if ( error.stack ) { // Firefox
  170. return "\n " + error.toString() + "\n" + // somewhat like Chrome's
  171. error.stack.replace( /^/mg, " " );
  172. } else { // default
  173. return error.toString();
  174. }
  175. }
  176. // -- resolveURI ---------------------------------------------------------------------------
  177. /// Convert a relative URI to an absolute URI.
  178. ///
  179. /// @param {String} uri
  180. /// The URI to resolve. If uri is relative, it will be interpreted with respect to
  181. /// baseURI, or with respect to the document if baseURI is not provided.
  182. /// @param {String} [baseURI]
  183. /// An optional URI that provides the reference for uri. If baseURI is not provided, uri
  184. /// will be interpreted with respect to the document. If baseURI is relative, it will be
  185. /// interpreted with respect to the document before resolving uri.
  186. ///
  187. /// @returns {String}
  188. /// uri as an absolute URI.
  189. resolveURI ( uri, baseURI ) {
  190. var doc = document;
  191. // if ( baseURI ) {
  192. // // Create a temporary document anchored at baseURI.
  193. // var doc = document.implementation.createHTMLDocument( "resolveURI" );
  194. // // Insert a <base/> with the reference URI: <head><base href=*baseURI*/></head>.
  195. // var base = doc.createElement( "base" );
  196. // base.href = this.resolveURI( baseURI ); // resolve wrt the document
  197. // var head = doc.getElementsByTagName( "head" )[0];
  198. // head.appendChild( base );
  199. // }
  200. // Create an <a/> and resolve the URI.
  201. // var a = doc.createElement( "a" );
  202. // a.href = uri;
  203. // return a.href;
  204. return uri
  205. }
  206. // -- merge --------------------------------------------------------------------------------
  207. /// Merge fields from the `source` objects into `target`.
  208. merge ( target /* [, source1 [, source2 ... ] ] */ ) {
  209. for ( var index = 1; index < arguments.length; index++ ) {
  210. var source = arguments[index];
  211. Object.keys( source ).forEach( function( key ) {
  212. if ( source[key] !== undefined ) {
  213. target[key] = source[key];
  214. }
  215. } );
  216. }
  217. return target;
  218. }
  219. validObject ( obj ) {
  220. var objType = ( {} ).toString.call( obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase();
  221. return ( objType != 'null' && objType != 'undefined' );
  222. }
  223. hasFileType ( value ) {
  224. return ( this.fileType( value ) !== undefined )
  225. }
  226. fileType ( filename ) {
  227. var fileFormat = undefined;
  228. var temp = filename.split( '.' );
  229. if ( temp.length > 1 ) {
  230. fileFormat = temp.pop();
  231. if ( fileFormat.length > 5 ) {
  232. fileFormat = undefined;
  233. }
  234. }
  235. return fileFormat;
  236. }
  237. ifPrototypeGetId ( appID, prototypes, nodeID, childID ) {
  238. var prototypeID = undefined;
  239. if ( ( nodeID == 0 && childID != appID ) || prototypes[ nodeID ] !== undefined ) {
  240. if ( nodeID != 0 || childID != appID ) {
  241. prototypeID = nodeID ? nodeID : childID;
  242. if ( prototypes[ prototypeID ] !== undefined ) {
  243. prototypeID = childID;
  244. }
  245. return prototypeID;
  246. }
  247. }
  248. return undefined;
  249. }
  250. isString ( s ) {
  251. return ( typeof( s ) === 'string' || s instanceof String );
  252. }
  253. isFunction ( obj ) {
  254. return ( typeof obj === 'function' || obj instanceof Function );
  255. }
  256. }
  257. export {
  258. Utility
  259. }