Pull to refresh

Снова разбираем URL

Как было сказано в одном из комментариев на эту вечную тему: «Примерно раз в год, кто-то изобретает этот велосипед, и спешит поделиться с Хабром». Новый год уже наступил, и мне хотелось бы поддержать эту добрую традицию. Предлагаемый мной способ решает задачу, что называется «в лоб» и к нему хорошо подходит крылатая фраза одного из персонажей замечательной советской комедии «Бриллиантовая рука»: «Зато дешево, надежно и практично!».

Если внимательно посмотреть на структуру URL, то можно заметить, что она имеет скорее «позиционную», чем «синтаксическую» природу и поэтому я не буду пытаться разбирать URL при помощи регулярных выражений, а просто разобью URL на части и, строго в соответствии с названиями методов Array.prototype, просто «распихаю» части URL по свойствам одноименного объекта.

Как уже упоминалось, типичная структура URL представляет собой следующую строку:

структура url

Спецификация URL предусматривает наличие еще двух компонентов адреса — логин и пароль, но поскольку я предлагаю решение практической задачи, то эта часть в «парсере» опущена. Мне не приходилось сталкиваться с URL подобного вида, поскольку так решать задачу авторизации можно, пожалуй, только 15 января. Типичных практических задач, с которыми мне приходится иметь дело обычно всего две:

  1. Получить значение параметра из search
  2. Изменить значение одного или нескольких параметров в search и вернуть обновленный URL для последующего применения

Листинг функции небольшой, поэтому приведу его полностью:

function toUrl(url, key, value) {
  if(url=='') url=window.location.href; 
  if(url.indexOf('//')<0) url='//'+url;

  // protocol
  var u=url.split(/\/?\//); 
  url={}; url.protocol=u.shift()+'//';

  // hostname
  url.hostname=u.shift().split(':'); 
  if(url.hostname.length>1) url.port=':'+url.hostname.pop()
  url.hostname=url.hostname.pop().split('@').pop();

  // сохраним порядок для последующей свертки
  url.pathname='/'; url.pagename=''; 

  try { 
    // hash
    url.search=u.pop().split(/#/); 
    if(url.search.length==2) url.sh='#'+url.search.pop();
    url.search=url.search.shift().split(/\?/);
    
    // pagename
    url.pagename=url.search.shift(); url.search=url.search[0]
  
    // pathname
    url.pathname+=u.join('/')+(u.length?'/':'');
  
    // разбор search, (c) Steven Benner, 2010
    try {
      var u = {};
      url.search.replace(
        new RegExp('([^?=&]+)(=([^&]*))?', 'g'),
        function($0, $1, $2, $3) { 
           u[$1] = decodeURIComponent($3); 
        }
      );
      url.search=u;
    } catch(e) { 
      delete url.search; 
    }
  } catch(e) { };

  // вернем URL как объект, где search так же
  // представлен объектом {key:value}
  if(!key && !value) return(url);

  // возвращаем значение ключа
  if(key && !value) return(url.search[key]);

  // roll up url
  var roll = function(url,search){
    var out='';
    for(var key in url) {
      out+=(search?'&'+key+'=':'')+(key=='search'?'?'+roll(url[key],1).substr(1):url[key]);
    };
    return out;
  };
  
  // устанавливаем значение ключа
  if(value) url.search[key]=value.toString(); 
  
  // сворачиваемся, с учетом порядка следования свойств
  return(roll(url));    
}

// применение
console.log(toUrl('http://site.my:81/home/path/page.htm?a=1&b=2#hash','a',5));

Если передан только URL, то функция возвращает как раз то, о чем я писал выше — объект, в котором вся структура URL «распихана» по свойствам. В дополнении к стандартному представлению из pathname выделен pagename, т.е. страница сайта, которая бывает востребована на практике.

Конечно, совсем без регулярных выражений не обошлось — разбор search параметров был предложен еще в 2010 году Стивеном Беннером (Steven Benner). Все же не стоит плодить велосипеды сверх необходимого.

Поздравляю всех читателей этого замечательного сайта с Новым Годом и Рождеством!
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.