utility.js 13 KB

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