text: import {
    get,
    set
  } from 'ember-metal';
  
  import { Object as EmberObject } from 'ember-runtime';
  import EmberLocation from './api';
  
  /**
  @module ember
  @submodule ember-routing
  */
  
  let popstateFired = false;
  
  /**
    Ember.HistoryLocation implements the location API using the browser's
    history.pushState API.
  
    @class HistoryLocation
    @namespace Ember
    @extends Ember.Object
    @private
  */
  export default EmberObject.extend({
    implementation: 'history',
  
    init() {
      this._super(...arguments);
  
      let base = document.querySelector('base');
      let baseURL = base ? base.getAttribute('href') : '';
  
      set(this, 'baseURL', baseURL);
      set(this, 'location', get(this, 'location') || window.location);
  
      this._popstateHandler = undefined;
    },
  
    /**
      Used to set state on first call to setURL
  
      @private
      @method initState
    */
    initState() {
      let history = get(this, 'history') || window.history;
      set(this, 'history', history);
  
      if (history && 'state' in history) {
        this.supportsHistory = true;
      }
  
      this.replaceState(this.formatURL(this.getURL()));
    },
  
    /**
      Will be pre-pended to path upon state change
  
      @property rootURL
      @default '/'
      @private
    */
    rootURL: '/',
  
    /**
      Returns the current `location.pathname` without `rootURL` or `baseURL`
  
      @private
      @method getURL
      @return url {String}
    */
    getURL() {
      let location = get(this, 'location');
      let path = location.pathname;
  
      let rootURL = get(this, 'rootURL');
      let baseURL = get(this, 'baseURL');
  
      // remove trailing slashes if they exists
      rootURL = rootURL.replace(/\/$/, '');
      baseURL = baseURL.replace(/\/$/, '');
  
      // remove baseURL and rootURL from start of path
      let url = path
        .replace(new RegExp('^' + baseURL + '(?=/|$)'), '')
        .replace(new RegExp('^' + rootURL + '(?=/|$)'), '');
  
      let search = location.search || '';
      url += search;
      url += this.getHash();
  
      return url;
    },
  
    /**
      Uses `history.pushState` to update the url without a page reload.
  
      @private
      @method setURL
      @param path {String}
    */
    setURL(path) {
      let state = this.getState();
      path = this.formatURL(path);
  
      if (!state || state.path !== path) {
        this.pushState(path);
      }
    },
  
    /**
      Uses `history.replaceState` to update the url without a page reload
      or history modification.
  
      @private
      @method replaceURL
      @param path {String}
    */
    replaceURL(path) {
      let state = this.getState();
      path = this.formatURL(path);
  
      if (!state || state.path !== path) {
        this.replaceState(path);
      }
    },
  
    /**
      Get the current `history.state`. Checks for if a polyfill is
      required and if so fetches this._historyState. The state returned
      from getState may be null if an iframe has changed a window's
      history.
  
      @private
      @method getState
      @return state {Object}
    */
    getState() {
      if (this.supportsHistory) {
        return get(this, 'history').state;
      }
  
      return this._historyState;
    },
  
    /**
     Pushes a new state.
  
     @private
     @method pushState
     @param path {String}
    */
    pushState(path) {
      let state = { path: path };
  
      get(this, 'history').pushState(state, null, path);
  
      this._historyState = state;
  
      // used for webkit workaround
      this._previousURL = this.getURL();
    },
  
    /**
     Replaces the current state.
  
     @private
     @method replaceState
     @param path {String}
    */
    replaceState(path) {
      let state = { path: path };
      get(this, 'history').replaceState(state, null, path);
  
      this._historyState = state;
  
      // used for webkit workaround
      this._previousURL = this.getURL();
    },
  
    /**
      Register a callback to be invoked whenever the browser
      history changes, including using forward and back buttons.
  
      @private
      @method onUpdateURL
      @param callback {Function}
    */
    onUpdateURL(callback) {
      this._removeEventListener();
  
      this._popstateHandler = () => {
        // Ignore initial page load popstate event in Chrome
        if (!popstateFired) {
          popstateFired = true;
          if (this.getURL() === this._previousURL) { return; }
        }
        callback(this.getURL());
      };
  
      window.addEventListener('popstate', this._popstateHandler);
    },
  
    /**
      Used when using `{{action}}` helper.  The url is always appended to the rootURL.
  
      @private
      @method formatURL
      @param url {String}
      @return formatted url {String}
    */
    formatURL(url) {
      let rootURL = get(this, 'rootURL');
      let baseURL = get(this, 'baseURL');
  
      if (url !== '') {
        // remove trailing slashes if they exists
        rootURL = rootURL.replace(/\/$/, '');
        baseURL = baseURL.replace(/\/$/, '');
      } else if (baseURL.match(/^\//) && rootURL.match(/^\//)) {
        // if baseURL and rootURL both start with a slash
        // ... remove trailing slash from baseURL if it exists
        baseURL = baseURL.replace(/\/$/, '');
      }
  
      return baseURL + rootURL + url;
    },
  
    /**
      Cleans up the HistoryLocation event listener.
  
      @private
      @method willDestroy
    */
    willDestroy() {
      this._removeEventListener();
    },
  
    /**
      @private
  
      Returns normalized location.hash
  
      @method getHash
    */
    getHash: EmberLocation._getHash,
  
    _removeEventListener() {
      if (this._popstateHandler) {
        window.removeEventListener('popstate', this._popstateHandler);
      }
    }
  });
  