utility.js 13 KB

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