Parsim URL

純粋なJavaScriptで記述された1つの便利なユーティリティ、URLを共有したいと思います。 実際、これはwindow.location



とほとんど同じように機能する小さなURLパーサーですが、操作中にブラウザーページをリロードしません。



同時に、JavaScriptのゲッターとセッターに関するいくつかの言葉を話します。



UPD1:労働者の要求に応じて、例を挙げます。

// URL = 'http://my.site.com/somepath/'

var u = new URL( 'relative/path/index.html' )

u.href // my.site.com/somepath/relative/path/index.html

u.href = '/absolute/path.php?a=8#some-hash'

u.href // my.site.com/absolute/path.php?a=8#some-hash

u.hash // #some-hash

u.protocol = 'https:'

u.href // my.site.com/absolute/path.php?a=8#some-hash

u.host = 'another.site.com:8080'

u.href // another.site.com:8080/absolute/path.php?a=8#some-hash

u.port // 8080

// ,



* This source code was highlighted with Source Code Highlighter .






FF3 +(おそらく2+で試していない)とIE6 + (これが私のノウハウ:-))で動作します。

この記事では、完全にクロスブラウザーの実装についても分析していますが、使用中は少し面倒です。

// URL = 'http://my.site.com/somepath/'

var u = new URL( 'relative/path/index.html' )

u.href() // my.site.com/somepath/relative/path/index.html

u.href( '/absolute/path.php?a=8#some-hash' )

u.href() // my.site.com/absolute/path.php?a=8#some-hash

// ..



* This source code was highlighted with Source Code Highlighter .








はい、リスティングの全文を掲載します。申し訳ありませんが、ご参加ください。





UPD2:ライブラリの目標を簡単に説明します。

このツールは、実際のニーズから正確に生まれました。

また、TinyMCEなどの大規模なJSプロジェクトで、この目的のいくつかの職人的開発を見てきました。 RTEでは、多くの場合、リソースリンクを扱います。 また、これらのリンクはリアルタイムで処理する必要があります。



具体的には、現在のURLを解析し、検索で新しいパラメーターを変更/追加してから、リダイレクトを行う必要がありました。



あなたはもっと思いつくことができます。



問題



実際、問題は何ですか? 問題は次のとおりです。

  1. window.location



    オブジェクトを使用することはできません。 わずかな変更で現在のページをリロードします
  2. コンストラクターLocation



    介してこのようなオブジェクトをもう1つ作成することはできません-atat! ブラウザのドライバーによって禁止されています!
  3. オブジェクト自体の動作はかなり重要です。
  4. さて、私は既製の実装を見つけられませんでした:)


私は行動の非自明性に言及しました。 ここにあります:



図:URLパーツの関係



URLの一部を変更すると、他の部分も更新する必要があります。



別れ



本質的に、window.locationのwindow.location



を作成するので、そこから表記をドラッグします。 例を見てみましょう:



図:URLパーツの解析



コメントなし:)



好むと好まざるとにかかわらず、RegExpは不可欠です



もちろん、主な作業は正規表現によって行われます。

var pattern = "^(([^:/\\?#]+):)?(//(([^:/\\?#]*)(?::([^/\\?#]*))?))?([^\\?#]*)(\\?([^#]*))?(#(.*))?$" ;



* This source code was highlighted with Source Code Highlighter .






より詳細に:

var pattern =

// Match #0. URL (#0 - HREF, window.location).

// , #0 == "https://example.com:8080/some/path/index.html?p=1&q=2&r=3#some-hash"

"^" +

// Match #1 & #2. SCHEME (#1 - PROTOCOL, window.location).

// , #1 == "https:", #2 == "https"

"(([^:/\\?#]+):)?" +

// Match #3-#6. AUTHORITY (#4 = HOST, #5 = HOSTNAME #6 = PORT, window.location)

// , #3 == "//example.com:8080", #4 == "example.com:8080", #5 == "example.com", #6 == "8080"

"(" +

"//(([^:/\\?#]*)(?::([^/\\?#]*))?)" +

")?" +

// Match #7. PATH (#7 = PATHNAME, window.location).

// , #7 == "/some/path/index.html"

"([^\\?#]*)" +

// Match #8 & #9. QUERY (#8 = SEARCH, window.location).

// , #8 == "?p=1&q=2&r=3", #9 == "p=1&q=2&r=3"

"(\\?([^#]*))?" +

// Match #10 & #11. FRAGMENT (#10 = HASH, window.location).

// , #10 == "#some-hash", #11 == "some-hash"

"(#(.*))?" + "$" ;




* This source code was highlighted with Source Code Highlighter .






ご想像のとおり、このRegExpはJavaScriptだけでなく、他の何百もの言語でも機能します。 健康のために使用してください! ;)



試行番号1



function URL(url) {

url = url || "" ;

this .parse(url);

}

URL.prototype = {

// this.href, this.parse()

href: "" ,

// - , this.update()

protocol: "" ,

host: "" ,

hostname: "" ,

port: "" ,

pathname: "" ,

search: "" ,

hash: "" ,



parse: function (url) {

url = url || this .href;

var pattern = "^(([^:/\\?#]+):)?(//(([^:/\\?#]*)(?::([^/\\?#]*))?))?([^\\?#]*)(\\?([^#]*))?(#(.*))?$" ;

var rx = new RegExp(pattern);

var parts = rx.exec(url);



this .href = parts[0] || "" ;

this .protocol = parts[1] || "" ;

this .host = parts[4] || "" ;

this .hostname = parts[5] || "" ;

this .port = parts[6] || "" ;

this .pathname = parts[7] || "/" ;

this .search = parts[8] || "" ;

this .hash = parts[10] || "" ;



this .update();

},



update: function () {

// protocol - ,

if (! this .protocol)

this .protocol = window.location.protocol;



// relative pathname/URL - relative, ""

this .pathname = this .pathname.replace(/^\s*/g, '' );

if (! this .host && this .pathname && !/^\ //.test(this.pathname)) {

// , . .

var _p = window.location.pathname.split( '/' );

_p[_p.length - 1] = this .pathname;

this .pathname = _p.join( '/' );

};



// hostname - ,

if (! this .hostname)

this .hostname = window.location.hostname;



this .host = this .hostname + (( "" + this .port) ? ":" + this .port : "" );

this .href = this .protocol + '//' + this .host + this .pathname + this .search + this .hash;

},



/**

* window.location. URL.

*/

assign: function (url) {

this .parse(url);

window.location.assign( this .href);

},



/**

* window.location. URL, history

*/

replace: function (url) {

this .parse(url);

window.location.replace( this .href);

}

}




* This source code was highlighted with Source Code Highlighter .






詳細に





すべては問題ありませんが、URLの一部(ポートなどparse(...)



を変更した後は、 常にupdate(...)



parse(...)



呼び出す
必要があります。 これはひどいです。 結局、ユーザーはこれを行うのを忘れることができ、それからすべてがtara-tararaに飛びます。



残念ながら、この実装は避けられません。 しかし、あなたはすべてを異なって行うことができます:)



試行番号2



そして今、私は許容可能なオプションを提供します。 ゲッターとセッターが必要です。 最も明白な方法は、パラメーターごとにメソッドを作成することです( getProtocol() & setProtocol(newProtocol)



。 しかし、私はこのアプローチがかさばるので好きではありません。



もっとJavaScriptの方法でやってみましょう。 protocol(...)



メソッドが1つあり、パラメータなしで呼び出すとゲッターになり、パラメータを1つ使用するとセッターになります。



回路内の実際のデータを非表示にします。

var URL;



// . , .. parseURL updateURL.

( function () {



URL = function (url) {

// , . URL - , .

var href, protocol, host, hostname, port, pathname, search, hash;



// - , .

// Get/set href - set parseURL.call(this),

// .. parseURL URL - this.

this .href = function (val) {

if ( typeof val != "undefined" ) {

href = val;

parseURL.call( this );

}

return href;

}



// Get/set protocol

// set href, set protocol updateURL.call(this), .

this .protocol = function (val) {

if ( typeof val != "undefined" ) {

// - protocol , window.location

if (!val)

val = protocol || window.location.protocol;

protocol = val;

updateURL.call( this );

}

return protocol;

}



// Get/set host

// , host, hostname port - .

// set host.

this .host = function (val) {

if ( typeof val != "undefined" ) {

val = val || '' ;

var v = val.split( ':' );

var h = v[0], p = v[1] || '' ;

host = val;

hostname = h;

port = p;

updateURL.call( this );

}

return host;

}



// Get/set hostname

// host, hostname port.

this .hostname = function (val) {

if ( typeof val != "undefined" ) {

if (!val)

val = hostname || window.location.hostname;

hostname = val;

host = val + (( "" + port) ? ":" + port : "" );

updateURL.call( this );

}

return hostname;

}



// Get/set port

// host, hostname port.

this .port = function (val) {

if ( typeof val != "undefined" ) {

port = val;

host = hostname + (( "" + port) ? ":" + port : "" );

updateURL.call( this );

}

return port;

}



// Get/set pathname

// pathname .

// relative pathname, .. set' pathname,

// '/', .

this .pathname = function (val) {

if ( typeof val != "undefined" ) {

if (val.indexOf( "/" ) != 0) { // relative url

var _p = (pathname || window.location.pathname).split( "/" );

_p[_p.length - 1] = val;

val = _p.join( "/" );

}

pathname = val;

updateURL.call( this );

}

return pathname;

}



// Get/set search

this .search = function (val) {

if ( typeof val != "undefined" ) {

search = val;

}

return search;

}



// Get/set hash

this .hash = function (val) {

if ( typeof val != "undefined" ) {

hash = val;

}

return hash;

}



url = url || "" ;

parseURL.call( this , url);

}



URL.prototype = {

/**

* window.location. URL.

*/

assign: function (url) {

parseURL.call( this , url);

window.location.assign( this .href());

},



/**

* window.location. URL, history

*/

replace: function (url) {

parseURL.call( this , url);

window.location.replace( this .href());

}

}



// , URL .

// - URL.

// , .. .

function parseURL(url) {

if ( this ._innerUse)

return ;



url = url || this .href();

var pattern = "^(([^:/\\?#]+):)?(//(([^:/\\?#]*)(?::([^/\\?#]*))?))?([^\\?#]*)(\\?([^#]*))?(#(.*))?$" ;

var rx = new RegExp(pattern);

var parts = rx.exec(url);



// Prevent infinite recursion

this ._innerUse = true ;



this .href(parts[0] || "" );

this .protocol(parts[1] || "" );

//this.host(parts[4] || "");

this .hostname(parts[5] || "" );

this .port(parts[6] || "" );

this .pathname(parts[7] || "/" );

this .search(parts[8] || "" );

this .hash(parts[10] || "" );



delete this ._innerUse;



updateURL.call( this );

}



// , URL .

// - URL.

// , .. .

// , , setter'.

function updateURL() {

if ( this ._innerUse)

return ;



// Prevent infinite recursion

this ._innerUse = true ;



this .href( this .protocol() + '//' + this .host() + this .pathname() + this .search() + this .hash());



delete this ._innerUse;

}



})()




* This source code was highlighted with Source Code Highlighter .






一般に、コードは自己文書化されているため、重要な点のみを説明します。





さて、すぐに例に。 結局のところ、主なことは、このことを実際に見ることです。

// URL = 'http://my.site.com/somepath/'

var u = new URL( 'relative/path/index.html' )

u.href() // my.site.com/somepath/relative/path/index.html

u.href( '/absolute/path.php?a=8#some-hash' )

u.href() // my.site.com/absolute/path.php?a=8#some-hash

u.hash() // #some-hash

u.protocol( 'https:' )

u.href() // my.site.com/absolute/path.php?a=8#some-hash

u.host( 'another.site.com:8080' )

u.href() // another.site.com:8080/absolute/path.php?a=8#some-hash

u.port() // 8080

// ,



* This source code was highlighted with Source Code Highlighter .






行くぞ すべてが機能します。

実際、これは機能するバージョンです。 バージョン1.0 finalと呼びます。

それでは、バージョン2.0アルファに移りましょう。または、truゲッターとセッターが登場します。



試行番号3



コードを提供し、興味深い点を検討します。

var URL;



( function () {

var isIE = window.navigator.userAgent.indexOf( 'MSIE' ) != -1;



URL = function (url) {

var data = {href: '' , protocol: '' , host: '' , hostname: '' , port: '' , pathname: '' , search: '' , hash: '' };



var gs = {

getHref: function () {

return data.href;

},

setHref: function (val) {

data.href = val;

parseURL.call( this );

return data.href;

},



getProtocol: function () {

return data.protocol;

},

setProtocol: function (val) {

if (!val)

val = data.protocol || window.location.protocol; // update || init

data.protocol = val;

updateURL.call( this );

return data.protocol;

},



getHost: function () {

return data.host;

},

setHost: function (val) {

val = val || '' ;

var v = val.split( ':' );

var h = v[0], p = v[1] || '' ;

data.host = val;

data.hostname = h;

data.port = p;

updateURL.call( this );

return data.host;

},



getHostname: function () {

return data.hostname;

},

setHostname: function (val) {

if (!val)

val = data.hostname || window.location.hostname; // update || init

data.hostname = val;

data.host = val + (( "" + data.port) ? ":" + data.port : "" );

updateURL.call( this );

return data.hostname;

},



getPort: function () {

return data.port;

},

setPort: function (val) {

data.port = val;

data.host = data.hostname + (( "" + data.port) ? ":" + data.port : "" );

updateURL.call( this );

return data.port;

},



getPathname: function () {

return data.pathname;

},

setPathname: function (val) {

if (val.indexOf( "/" ) != 0) { // relative url

var _p = (data.pathname || window.location.pathname).split( "/" );

_p[_p.length - 1] = val;

val = _p.join( "/" );

}

data.pathname = val;

updateURL.call( this );

return data.pathname;

},



getSearch: function () {

return data.search;

},

setSearch: function (val) {

return data.search = val;

},



getHash: function () {

return data.hash;

},

setHash: function (val) {

return data.hash = val;

}

};



if (isIE) { // IE5.5+

var el= document .createElement( 'div' );

el.style.display= 'none' ;

document .body.appendChild(el);

el.assign = URL.prototype.assign;

el.replace = URL.prototype.replace;

var keys = [ "href" , "protocol" , "host" , "hostname" , "port" , "pathname" , "search" , "hash" ];

el.onpropertychange= function (){

var pn = event .propertyName;

var pv = event .srcElement[ event .propertyName];

if ( this ._holdOnMSIE || pn == '_holdOnMSIE' )

return pv;

this ._holdOnMSIE = true ;

for ( var i = 0, l = keys.length; i < l; i++)

el[keys[i]] = data[keys[i]];

this ._holdOnMSIE = false ;

for ( var i = 0, l = keys.length; i < l; i++) {

var key = keys[i];

if (pn == key) {

var sKey = 'set' + key.substr(0, 1).toUpperCase() + key.substr(1);

return gs[sKey].call(el, pv);

}

}

}

url = url || "" ;

parseURL.call(el, url);

return el;

} else if (URL.prototype.__defineSetter__) { // FF

var keys = [ "href" , "protocol" , "host" , "hostname" , "port" , "pathname" , "search" , "hash" ];

for ( var i = 0, l = keys.length; i < l; i++) {

( function (i) {

var key = keys[i];

var gKey = 'get' + key.substr(0, 1).toUpperCase() + key.substr(1);

var sKey = 'set' + key.substr(0, 1).toUpperCase() + key.substr(1);

URL.prototype.__defineGetter__(key, gs[gKey]);

URL.prototype.__defineSetter__(key, gs[sKey]);

})(i);

}

url = url || "" ;

parseURL.call( this , url);

}

}



URL.prototype = {

assign: function (url) {

parseURL.call( this , url);

window.location.assign( this .href);

},



replace: function (url) {

parseURL.call( this , url);

window.location.replace( this .href);

}

}



function parseURL(url) {

if ( this ._innerUse)

return ;



url = url || this .href;

var pattern = "^(([^:/\\?#]+):)?(//(([^:/\\?#]*)(?::([^/\\?#]*))?))?([^\\?#]*)(\\?([^#]*))?(#(.*))?$" ;

var rx = new RegExp(pattern);

var parts = rx.exec(url);



// Prevent infinite recursion

this ._innerUse = true ;



this .href = parts[0] || "" ;

this .protocol = parts[1] || "" ;

//this.host = parts[4] || "";

this .hostname = parts[5] || "" ;

this .port = parts[6] || "" ;

this .pathname = parts[7] || "/" ;

this .search = parts[8] || "" ;

this .hash = parts[10] || "" ;



if (!isIE)

delete this ._innerUse;

else

this ._innerUse = false ;



updateURL.call( this );

}



function updateURL() {

if ( this ._innerUse)

return ;



// Prevent infinite recursion

this ._innerUse = true ;



this .href = this .protocol + '//' + this .host + this .pathname + this .search + this .hash;



if (!isIE)

delete this ._innerUse;

else

this ._innerUse = false ;

}



})()




* This source code was highlighted with Source Code Highlighter .






ゲッター/セッターの作成を検討してください。



例2



// URL = 'http://my.site.com/somepath/'

var u = new URL( 'relative/path/index.html' )

u.href // my.site.com/somepath/relative/path/index.html

u.href = '/absolute/path.php?a=8#some-hash'

u.href // my.site.com/absolute/path.php?a=8#some-hash

u.hash // #some-hash

u.protocol = 'https:'

u.href // my.site.com/absolute/path.php?a=8#some-hash

u.host = 'another.site.com:8080'

u.href // another.site.com:8080/absolute/path.php?a=8#some-hash

u.port // 8080

// ,



* This source code was highlighted with Source Code Highlighter .






FF3 +、IE6 +で動作します。 Safari / Chrome用にめちゃくちゃにすることができます。 Operaについて-わかりません。 RTFMが必要です。



このように



私は何か有用なことをして、この記事を書いて私の一日を無駄にしないことを望みます:-)

PS:はい、異なるブラウザのゲッターとセッターに関する別の記事を書くと思います。 Firefoxだけが生きているわけではありません(小さなPR:Habrahabrに私の考えの流れを負担させないために-私のブログへようこそ-http://web-by-kott.blogspot.com/。開始)



All Articles