text: /**
   * Service for URL templating.
   */
  
  import root from './root';
  import query from './query';
  import template from './template';
  import {each, merge, isArray, isFunction, isObject, isPlainObject, isString} from '../util';
  
  export default function Url(url, params) {
  
      var self = this || {}, options = url, transform;
  
      if (isString(url)) {
          options = {url, params};
      }
  
      options = merge({}, Url.options, self.$options, options);
  
      Url.transforms.forEach(handler => {
  
          if (isString(handler)) {
              handler = Url.transform[handler];
          }
  
          if (isFunction(handler)) {
              transform = factory(handler, transform, self.$vm);
          }
  
      });
  
      return transform(options);
  }
  
  /**
   * Url options.
   */
  
  Url.options = {
      url: '',
      root: null,
      params: {}
  };
  
  /**
   * Url transforms.
   */
  
  Url.transform = {template, query, root};
  Url.transforms = ['template', 'query', 'root'];
  
  /**
   * Encodes a Url parameter string.
   *
   * @param {Object} obj
   */
  
  Url.params = function (obj) {
  
      var params = [], escape = encodeURIComponent;
  
      params.add = function (key, value) {
  
          if (isFunction(value)) {
              value = value();
          }
  
          if (value === null) {
              value = '';
          }
  
          this.push(escape(key) + '=' + escape(value));
      };
  
      serialize(params, obj);
  
      return params.join('&').replace(/%20/g, '+');
  };
  
  /**
   * Parse a URL and return its components.
   *
   * @param {String} url
   */
  
  Url.parse = function (url) {
  
      var el = document.createElement('a');
  
      if (document.documentMode) {
          el.href = url;
          url = el.href;
      }
  
      el.href = url;
  
      return {
          href: el.href,
          protocol: el.protocol ? el.protocol.replace(/:$/, '') : '',
          port: el.port,
          host: el.host,
          hostname: el.hostname,
          pathname: el.pathname.charAt(0) === '/' ? el.pathname : '/' + el.pathname,
          search: el.search ? el.search.replace(/^\?/, '') : '',
          hash: el.hash ? el.hash.replace(/^#/, '') : ''
      };
  };
  
  function factory(handler, next, vm) {
      return options => {
          return handler.call(vm, options, next);
      };
  }
  
  function serialize(params, obj, scope) {
  
      var array = isArray(obj), plain = isPlainObject(obj), hash;
  
      each(obj, (value, key) => {
  
          hash = isObject(value) || isArray(value);
  
          if (scope) {
              key = scope + '[' + (plain || hash ? key : '') + ']';
          }
  
          if (!scope && array) {
              params.add(value.name, value.value);
          } else if (hash) {
              serialize(params, value, key);
          } else {
              params.add(key, value);
          }
      });
  }
  