util.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /**
  2. * Extend object a with the properties of object b.
  3. * If there's a conflict, object b takes precedence.
  4. *
  5. * @param {object} a
  6. * @param {object} b
  7. */
  8. export const extend = ( a, b ) => {
  9. for( let i in b ) {
  10. a[ i ] = b[ i ];
  11. }
  12. return a;
  13. }
  14. /**
  15. * Converts the target object to an array.
  16. *
  17. * @param {object} o
  18. * @return {object[]}
  19. */
  20. export const toArray = ( o ) => {
  21. return Array.prototype.slice.call( o );
  22. }
  23. /**
  24. * Utility for deserializing a value.
  25. *
  26. * @param {*} value
  27. * @return {*}
  28. */
  29. export const deserialize = ( value ) => {
  30. if( typeof value === 'string' ) {
  31. if( value === 'null' ) return null;
  32. else if( value === 'true' ) return true;
  33. else if( value === 'false' ) return false;
  34. else if( value.match( /^-?[\d\.]+$/ ) ) return parseFloat( value );
  35. }
  36. return value;
  37. }
  38. /**
  39. * Measures the distance in pixels between point a
  40. * and point b.
  41. *
  42. * @param {object} a point with x/y properties
  43. * @param {object} b point with x/y properties
  44. *
  45. * @return {number}
  46. */
  47. export const distanceBetween = ( a, b ) => {
  48. let dx = a.x - b.x,
  49. dy = a.y - b.y;
  50. return Math.sqrt( dx*dx + dy*dy );
  51. }
  52. /**
  53. * Applies a CSS transform to the target element.
  54. *
  55. * @param {HTMLElement} element
  56. * @param {string} transform
  57. */
  58. export const transformElement = ( element, transform ) => {
  59. element.style.transform = transform;
  60. }
  61. /**
  62. * Find the closest parent that matches the given
  63. * selector.
  64. *
  65. * @param {HTMLElement} target The child element
  66. * @param {String} selector The CSS selector to match
  67. * the parents against
  68. *
  69. * @return {HTMLElement} The matched parent or null
  70. * if no matching parent was found
  71. */
  72. export const closestParent = ( target, selector ) => {
  73. let parent = target.parentNode;
  74. while( parent ) {
  75. // There's some overhead doing this each time, we don't
  76. // want to rewrite the element prototype but should still
  77. // be enough to feature detect once at startup...
  78. let matchesMethod = parent.matches || parent.matchesSelector || parent.msMatchesSelector;
  79. // If we find a match, we're all set
  80. if( matchesMethod && matchesMethod.call( parent, selector ) ) {
  81. return parent;
  82. }
  83. // Keep searching
  84. parent = parent.parentNode;
  85. }
  86. return null;
  87. }
  88. /**
  89. * Handling the fullscreen functionality via the fullscreen API
  90. *
  91. * @see http://fullscreen.spec.whatwg.org/
  92. * @see https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode
  93. */
  94. export const enterFullscreen = () => {
  95. let element = document.documentElement;
  96. // Check which implementation is available
  97. let requestMethod = element.requestFullscreen ||
  98. element.webkitRequestFullscreen ||
  99. element.webkitRequestFullScreen ||
  100. element.mozRequestFullScreen ||
  101. element.msRequestFullscreen;
  102. if( requestMethod ) {
  103. requestMethod.apply( element );
  104. }
  105. }
  106. /**
  107. * Creates an HTML element and returns a reference to it.
  108. * If the element already exists the existing instance will
  109. * be returned.
  110. *
  111. * @param {HTMLElement} container
  112. * @param {string} tagname
  113. * @param {string} classname
  114. * @param {string} innerHTML
  115. *
  116. * @return {HTMLElement}
  117. */
  118. export const createSingletonNode = ( container, tagname, classname, innerHTML='' ) => {
  119. // Find all nodes matching the description
  120. let nodes = container.querySelectorAll( '.' + classname );
  121. // Check all matches to find one which is a direct child of
  122. // the specified container
  123. for( let i = 0; i < nodes.length; i++ ) {
  124. let testNode = nodes[i];
  125. if( testNode.parentNode === container ) {
  126. return testNode;
  127. }
  128. }
  129. // If no node was found, create it now
  130. let node = document.createElement( tagname );
  131. node.className = classname;
  132. node.innerHTML = innerHTML;
  133. container.appendChild( node );
  134. return node;
  135. }
  136. /**
  137. * Injects the given CSS styles into the DOM.
  138. *
  139. * @param {string} value
  140. */
  141. export const createStyleSheet = ( value ) => {
  142. let tag = document.createElement( 'style' );
  143. tag.type = 'text/css';
  144. if( value && value.length > 0 ) {
  145. if( tag.styleSheet ) {
  146. tag.styleSheet.cssText = value;
  147. }
  148. else {
  149. tag.appendChild( document.createTextNode( value ) );
  150. }
  151. }
  152. document.head.appendChild( tag );
  153. return tag;
  154. }
  155. /**
  156. * Returns a key:value hash of all query params.
  157. */
  158. export const getQueryHash = () => {
  159. let query = {};
  160. location.search.replace( /[A-Z0-9]+?=([\w\.%-]*)/gi, a => {
  161. query[ a.split( '=' ).shift() ] = a.split( '=' ).pop();
  162. } );
  163. // Basic deserialization
  164. for( let i in query ) {
  165. let value = query[ i ];
  166. query[ i ] = deserialize( unescape( value ) );
  167. }
  168. // Do not accept new dependencies via query config to avoid
  169. // the potential of malicious script injection
  170. if( typeof query['dependencies'] !== 'undefined' ) delete query['dependencies'];
  171. return query;
  172. }