Skip to content
Snippets Groups Projects
Select Git revision
  • 6e6a98c7c77a96cd6ea91b6f45a861943d564e7f
  • master default protected
  • yassin.elhakoun-master-patch-15592
  • yassin.elhakoun-master-patch-40090
  • pk
  • high-order-functions
6 results

util.js

Blame
  • Forked from programmation_sequentielle / cours
    Source project has a limited visibility.
    util.js 5.93 KiB
    /**
     * Extend object a with the properties of object b.
     * If there's a conflict, object b takes precedence.
     *
     * @param {object} a
     * @param {object} b
     */
    export const extend = ( a, b ) => {
    
    	for( let i in b ) {
    		a[ i ] = b[ i ];
    	}
    
    	return a;
    
    }
    
    /**
     * querySelectorAll but returns an Array.
     */
    export const queryAll = ( el, selector ) => {
    
    	return Array.from( el.querySelectorAll( selector ) );
    
    }
    
    /**
     * classList.toggle() with cross browser support
     */
    export const toggleClass = ( el, className, value ) => {
    	if( value ) {
    		el.classList.add( className );
    	}
    	else {
    		el.classList.remove( className );
    	}
    }
    
    /**
     * Utility for deserializing a value.
     *
     * @param {*} value
     * @return {*}
     */
    export const deserialize = ( value ) => {
    
    	if( typeof value === 'string' ) {
    		if( value === 'null' ) return null;
    		else if( value === 'true' ) return true;
    		else if( value === 'false' ) return false;
    		else if( value.match( /^-?[\d\.]+$/ ) ) return parseFloat( value );
    	}
    
    	return value;
    
    }
    
    /**
     * Measures the distance in pixels between point a
     * and point b.
     *
     * @param {object} a point with x/y properties
     * @param {object} b point with x/y properties
     *
     * @return {number}
     */
    export const distanceBetween = ( a, b ) => {
    
    	let dx = a.x - b.x,
    		dy = a.y - b.y;
    
    	return Math.sqrt( dx*dx + dy*dy );
    
    }
    
    /**
     * Applies a CSS transform to the target element.
     *
     * @param {HTMLElement} element
     * @param {string} transform
     */
    export const transformElement = ( element, transform ) => {
    
    	element.style.transform = transform;
    
    }
    
    /**
     * Element.matches with IE support.
     *
     * @param {HTMLElement} target The element to match
     * @param {String} selector The CSS selector to match
     * the element against
     *
     * @return {Boolean}
     */
    export const matches = ( target, selector ) => {
    
    	let matchesMethod = target.matches || target.matchesSelector || target.msMatchesSelector;
    
    	return !!( matchesMethod && matchesMethod.call( target, selector ) );
    
    }
    
    /**
     * Find the closest parent that matches the given
     * selector.
     *
     * @param {HTMLElement} target The child element
     * @param {String} selector The CSS selector to match
     * the parents against
     *
     * @return {HTMLElement} The matched parent or null
     * if no matching parent was found
     */
    export const closest = ( target, selector ) => {
    
    	// Native Element.closest
    	if( typeof target.closest === 'function' ) {
    		return target.closest( selector );
    	}
    
    	// Polyfill
    	while( target ) {
    		if( matches( target, selector ) ) {
    			return target;
    		}
    
    		// Keep searching
    		target = target.parentNode;
    	}
    
    	return null;
    
    }
    
    /**
     * Handling the fullscreen functionality via the fullscreen API
     *
     * @see http://fullscreen.spec.whatwg.org/
     * @see https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode
     */
    export const enterFullscreen = element => {
    
    	element = element || document.documentElement;
    
    	// Check which implementation is available
    	let requestMethod = element.requestFullscreen ||
    						element.webkitRequestFullscreen ||
    						element.webkitRequestFullScreen ||
    						element.mozRequestFullScreen ||
    						element.msRequestFullscreen;
    
    	if( requestMethod ) {
    		requestMethod.apply( element );
    	}
    
    }
    
    /**
     * Creates an HTML element and returns a reference to it.
     * If the element already exists the existing instance will
     * be returned.
     *
     * @param {HTMLElement} container
     * @param {string} tagname
     * @param {string} classname
     * @param {string} innerHTML
     *
     * @return {HTMLElement}
     */
    export const createSingletonNode = ( container, tagname, classname, innerHTML='' ) => {
    
    	// Find all nodes matching the description
    	let nodes = container.querySelectorAll( '.' + classname );
    
    	// Check all matches to find one which is a direct child of
    	// the specified container
    	for( let i = 0; i < nodes.length; i++ ) {
    		let testNode = nodes[i];
    		if( testNode.parentNode === container ) {
    			return testNode;
    		}
    	}
    
    	// If no node was found, create it now
    	let node = document.createElement( tagname );
    	node.className = classname;
    	node.innerHTML = innerHTML;
    	container.appendChild( node );
    
    	return node;
    
    }
    
    /**
     * Injects the given CSS styles into the DOM.
     *
     * @param {string} value
     */
    export const createStyleSheet = ( value ) => {
    
    	let tag = document.createElement( 'style' );
    	tag.type = 'text/css';
    
    	if( value && value.length > 0 ) {
    		if( tag.styleSheet ) {
    			tag.styleSheet.cssText = value;
    		}
    		else {
    			tag.appendChild( document.createTextNode( value ) );
    		}
    	}
    
    	document.head.appendChild( tag );
    
    	return tag;
    
    }
    
    /**
     * Returns a key:value hash of all query params.
     */
    export const getQueryHash = () => {
    
    	let query = {};
    
    	location.search.replace( /[A-Z0-9]+?=([\w\.%-]*)/gi, a => {
    		query[ a.split( '=' ).shift() ] = a.split( '=' ).pop();
    	} );
    
    	// Basic deserialization
    	for( let i in query ) {
    		let value = query[ i ];
    
    		query[ i ] = deserialize( unescape( value ) );
    	}
    
    	// Do not accept new dependencies via query config to avoid
    	// the potential of malicious script injection
    	if( typeof query['dependencies'] !== 'undefined' ) delete query['dependencies'];
    
    	return query;
    
    }
    
    /**
     * Returns the remaining height within the parent of the
     * target element.
     *
     * remaining height = [ configured parent height ] - [ current parent height ]
     *
     * @param {HTMLElement} element
     * @param {number} [height]
     */
    export const getRemainingHeight = ( element, height = 0 ) => {
    
    	if( element ) {
    		let newHeight, oldHeight = element.style.height;
    
    		// Change the .stretch element height to 0 in order find the height of all
    		// the other elements
    		element.style.height = '0px';
    
    		// In Overview mode, the parent (.slide) height is set of 700px.
    		// Restore it temporarily to its natural height.
    		element.parentNode.style.height = 'auto';
    
    		newHeight = height - element.parentNode.offsetHeight;
    
    		// Restore the old height, just in case
    		element.style.height = oldHeight + 'px';
    
    		// Clear the parent (.slide) height. .removeProperty works in IE9+
    		element.parentNode.style.removeProperty('height');
    
    		return newHeight;
    	}
    
    	return height;
    
    }