パスを操作する便利関数を作った
グリモンで遊んでるんだけど、どうもGM_xmlhttpRequestは相対URLを使えないようなので仕方なくパス操作系のライブラリを作ってみた。
いかにもありそうなのにいいものが見つからなかったのは検索スキルが低いからなのか。
GM_xmlhttpRequestに絶対URLで渡すことが目的だったので、
- http://〜/foo/bar.htmlからhttp://〜/foo/を得る
- http://〜/foo/と../baz/quux.jpgからhttp://〜/quux.jpgを得る
- ついでにhttp://〜/foo/////bar/./baz.htmlみたいなのはhttp://〜/foo/bar/baz.htmlに正規化する
ということが実現できるだけの機能を実装。
basenameとdirnameはPHPのbasename()とdirname()の挙動に合わせた。
canonicalize、collapse、joinはPerlのFile::Specを参考にした。
API
使い方は次のような感じ。インスタンス化するようなものではないけど、まとめたかったのでオブジェクトにした。
var dir = Path.dirname('http://example.com/foo/bar.html'); alert(dir); // http://example.com/foo var abs = Path.join(dir, '../bar/baz.png'); alert(abs); // http://example.com/bar/baz.png
- basename(path)
- /foo/bar/baz.htmlからbaz.htmlを取得する。PHPの同名関数と同じ。
- dirname(path)
- /foo/bar/baz.htmlから/foo/barを取得する。PHPの同名関数と同じ。
- canonicalize(path)
- /foo/////bar/./baz.htmlみたいなやつを/foo/bar/baz.htmlに正規化する。但し../は解決しない。
- collapse(path)
- /foo/bar/../baz/quux.htmlみたいなやつを/foo/baz/quux.htmlにする。
- join(dir, path)
- 2つのパスを結合する。canonicalizeやcollapseは適当にやってくれる。/foo/barと../baz/quux.htmlを結合すると/foo/baz/quux.htmlになる。
注意点としてはjoinの一つ目はディレクトリを前提としているので、たとえ/foo/bar.htmlなんてパスであったとしても、ディレクトリと見なして後ろにくっつける。都合が悪ければ渡す前にdirnameすべし。
もうひとつ、dirnameが返すパスの末尾にはスラッシュは含まれない("/"のときは除く)。
ソースコード
var Path = { // get filename part from path basename : function(path) { path = path.replace(/\/$/, ''); if (path === '') return path; return path.match(/[^/]+$/); }, // get directory part from path dirname : function(path) { if (path == '/') return path; if (path == '../') return '.'; if (!path.match(/\//)) path = './' + path; var dirname = path.replace(/\/$/, '').replace(/\/[^/]*$/, ''); if (dirname === '') return '/'; return dirname; }, // join paths join : function(base, path) { var host = null; if (base.match(/^(\w+:\/\/[^\/]+)(.*)/)) { host = RegExp.$1; base = RegExp.$2; } var result = host == null ? '' : host; result += Path.collapse(Path.canonicalize(base + '/' + path)); return result; }, // canonicalize path such as "aaa/./bbb//ccc" // but doesn't resolve updir "../" canonicalize : function(path) { path = path.replace(/\/+/g, '/').replace(/(\/\.)+(\/|$)/g, '/'); if (path !== './') path = path.replace(/^(\.\/)+/, ''); path = path.replace(/^\/(\.\.\/)+/, '/').replace(/^\/\.\.$/, '/'); if (path !== '/') path = path.replace(/\/$/, ''); return path; }, // resolve "../" collapse : function(path) { var parts = path.indexOf('/') === 0 ? path.substr(1).split(/\//) : path.split(/\//); var collapsed = new Array(); var over = 0; for (var i = 0; i < parts.length; i++) { if (parts[i] == '..') { if (collapsed.length) collapsed.pop(); else over++; } else { collapsed.push(parts[i]); } } if (path.indexOf('/') === 0) return '/' + collapsed.join('/'); while (over-- > 0) collapsed.unshift('..'); return collapsed.join('/'); } };
"Path"は大胆だったかもしれない。