Home Reference Source

scripts/utils/url.js

/* globals window, document */

'use strict'

import { loop } from './loop'

/**
 * This class is responsible for parsing and matching urls
 */
export class Url {
  /**
   * Creates an instance of Url
   * @param {String} url A url string. If no url is provided window.location.href is used
   */
  constructor ( url ) {
    /**
     * Contains regular expressions used for splitting the url
     * @type {Object}
     * @private
     */
    this.__regexp = {
      protocol: '(?<protocol>[^:]+):\\/\\/',
      host: '(?<host>[^\\/\\?#]+)',
      path: '(?<path>\\/[^\\?#]*)?',
      parameters: '(?<parameters>\\?[^#]*)?',
      hash: '(?<hash>#.*)?'
    }

    /**
     * Contains the url provided
     * @type {String}
     * @private
     */
    this.__url = ''

    /**
     * Contains all the parts of the url
     * @type {Object}
     * @private
     */
    this.__parts = {
      protocol: null,
      host: null,
      path: null,
      parameters: null,
      hash: null,
      username: null,
      password: null
    }

    this.set( url )
  }

  /**
   * Parse a url to the correct format
   * @param {String} url The url string to be parsed
   * @return {String} The parsed version of the url
   * @private
   */
  __parseUrl ( url ) {
    let div = document.createElement( 'div' )
    div.innerHTML = '<a></a>'
    div.firstChild.href = url // Ensures that the href is properly escaped
    return div.firstChild.href
  }

  /**
   * Split the url into parts based on the url regular expression
   * @private
   */
  __split () {
    let regexp = new RegExp( '^' + this.__regexp.protocol + this.__regexp.host + this.__regexp.path + this.__regexp.parameters + this.__regexp.hash + '$', 'i' )
    let parts = this.__url.match( regexp )
    if ( parts ) {
      this.__parts.protocol = parts.groups[ 'protocol' ] || null
      this.__parts.path = parts.groups[ 'path' ] || null
      this.__parts.hash = ( parts.groups[ 'hash' ] ) ? parts.groups[ 'hash' ].substring( 1 ) : null

      this.__parts.host = parts.groups[ 'host' ] || null
      // Split host, username and password if needed
      if ( this.__parts.host ) {
        let hostParts = this.__parts.host.split( '@' )
        if ( hostParts[ 1 ] ) {
          // We have a username and password so we need to update those parts
          let authentication = hostParts[ 0 ].split( ':' )
          this.__parts.username = authentication[ 0 ]
          this.__parts.password = authentication[ 1 ]
          this.__parts.host = hostParts[ 1 ]
        } else {
          // We dont have a username and password so we need to update those parts
          this.__parts.username = null
          this.__parts.password = null
          this.__parts.host = hostParts[ 0 ]
        }
      }

      this.__parts.parameters = parts.groups[ 'parameters' ] || null
      // Split the parameters into an object
      if ( this.__parts.parameters ) {
        let parameters = {}
        let groups = this.__parts.parameters.split( '?' )[ 1 ].split( '&' )
        loop( groups, ( group ) => {
          let pair = group.split( '=' )
          if ( pair[ 0 ] !== '' ) {
            parameters[ pair[ 0 ] ] = ( pair[ 1 ] !== undefined ) ? pair[ 1 ] : true
          }
        } )
        this.__parts.parameters = parameters
      } else {
        this.__parts.parameters = {}
      }
    }
  }

  /**
   * Returns the protocol of the url
   * @return {String} The string containing the protocol
   */
  get protocol () {
    return this.__parts.protocol
  }
  /**
   * Returns the host and port of the url
   * @return {String} The string containing the hostname and port
   */
  get host () {
    return this.__parts.host
  }
  /**
   * Returns the path of the url
   * @return {String} The string containing the path
   */
  get path () {
    return this.__parts.path
  }
  /**
   * Returns the parameters of the url
   * @return {Object} The query string parameters of the url
   */
  get parameters () {
    return this.__parts.parameters
  }
  /**
   * Returns the hash of the url
   * @return {String} The hash of the url
   */
  get hash () {
    return this.__parts.hash
  }
  /**
   * Returns the username from the url
   * @return {String} The username from the url
   */
  get username () {
    return this.__parts.username
  }
  /**
   * Returns the password from the url
   * @return {String} The password from the url
   */
  get password () {
    return this.__parts.password
  }
  /**
   * Returns all the parts of the url
   * @return {Object} The object containing all the url parts
   */
  get parts () {
    return this.__parts
  }

  /**
   * Return the url
   * @return {String} The provided url
   */
  toString () {
    return this.__url
  }

  /**
   * Set the url string
   * @param {String} url The url string
   * @return {Url} The instance of the Url class
   */
  set ( url ) {
    this.__url = ( url ) ? this.__parseUrl( url ) : window.location.href
    this.__split()
    return this
  }
}