Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F5968211
URL.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
17 KB
Referenced Files
None
Subscribers
None
URL.js
View Options
/* global Symbol */
// URL Polyfill
// Draft specification: https://url.spec.whatwg.org
// Notes:
// - Primarily useful for parsing URLs and modifying query parameters
// - Should work in IE8+ and everything more modern, with es5.js polyfills
(
function
(
global
)
{
'use strict'
;
function
isSequence
(
o
)
{
if
(
!
o
)
return
false
;
if
(
'Symbol'
in
global
&&
'iterator'
in
global
.
Symbol
&&
typeof
o
[
Symbol
.
iterator
]
===
'function'
)
return
true
;
if
(
Array
.
isArray
(
o
))
return
true
;
return
false
;
}
;(
function
()
{
// eslint-disable-line no-extra-semi
// Browsers may have:
// * No global URL object
// * URL with static methods only - may have a dummy constructor
// * URL with members except searchParams
// * Full URL API support
var
origURL
=
global
.
URL
;
var
nativeURL
;
try
{
if
(
origURL
)
{
nativeURL
=
new
global
.
URL
(
'http://example.com'
);
if
(
'searchParams'
in
nativeURL
)
{
var
url
=
new
URL
(
'http://example.com'
);
url
.
search
=
'a=1&b=2'
;
if
(
url
.
href
===
'http://example.com/?a=1&b=2'
)
{
url
.
search
=
''
;
if
(
url
.
href
===
'http://example.com/'
)
{
return
;
}
}
}
if
(
!
(
'href'
in
nativeURL
))
{
nativeURL
=
undefined
;
}
nativeURL
=
undefined
;
}
// eslint-disable-next-line no-empty
}
catch
(
_
)
{}
// NOTE: Doesn't do the encoding/decoding dance
function
urlencoded_serialize
(
pairs
)
{
var
output
=
''
,
first
=
true
;
pairs
.
forEach
(
function
(
pair
)
{
var
name
=
encodeURIComponent
(
pair
.
name
);
var
value
=
encodeURIComponent
(
pair
.
value
);
if
(
!
first
)
output
+=
'&'
;
output
+=
name
+
'='
+
value
;
first
=
false
;
});
return
output
.
replace
(
/%20/g
,
'+'
);
}
// https://url.spec.whatwg.org/#percent-decode
var
cachedDecodePattern
;
function
percent_decode
(
bytes
)
{
// This can't simply use decodeURIComponent (part of ECMAScript) as that's limited to
// decoding to valid UTF-8 only. It throws URIError for literals that look like percent
// encoding (e.g. `x=%`, `x=%a`, and `x=a%2sf`) and for non-UTF8 binary data that was
// percent encoded and cannot be turned back into binary within a JavaScript string.
//
// The spec deals with this as follows:
// * Read input as UTF-8 encoded bytes. This needs low-level access or a modern
// Web API, like TextDecoder. Old browsers don't have that, and it'd a large
// dependency to add to this polyfill.
// * For each percentage sign followed by two hex, blindly decode the byte in binary
// form. This would require TextEncoder to not corrupt multi-byte chars.
// * Replace any bytes that would be invalid under UTF-8 with U+FFFD.
//
// Instead we:
// * Use the fact that UTF-8 is designed to make validation easy in binary.
// You don't have to decode first. There are only a handful of valid prefixes and
// ranges, per RFC 3629. <https://datatracker.ietf.org/doc/html/rfc3629#section-3>
// * Safely create multi-byte chars with decodeURIComponent, by only passing it
// valid and full characters (e.g. "%F0" separately from "%F0%9F%92%A9" throws).
// Anything else is kept as literal or replaced with U+FFFD, as per the URL spec.
if
(
!
cachedDecodePattern
)
{
// In a UTF-8 multibyte sequence, non-initial bytes are always between %80 and %BF
var
uContinuation
=
'%[89AB][0-9A-F]'
;
// The length of a UTF-8 sequence is specified by the first byte
//
// One-byte sequences: 0xxxxxxx
// So the byte is between %00 and %7F
var
u1Bytes
=
'%[0-7][0-9A-F]'
;
// Two-byte sequences: 110xxxxx 10xxxxxx
// So the first byte is between %C0 and %DF
var
u2Bytes
=
'%[CD][0-9A-F]'
+
uContinuation
;
// Three-byte sequences: 1110xxxx 10xxxxxx 10xxxxxx
// So the first byte is between %E0 and %EF
var
u3Bytes
=
'%E[0-9A-F]'
+
uContinuation
+
uContinuation
;
// Four-byte sequences: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
// So the first byte is between %F0 and %F7
var
u4Bytes
=
'%F[0-7]'
+
uContinuation
+
uContinuation
+
uContinuation
;
var
anyByte
=
'%[0-9A-F][0-9A-F]'
;
// Match some consecutive percent-escaped bytes. More precisely, match
// 1-4 bytes that validly encode one character in UTF-8, or 1 byte that
// would be invalid in UTF-8 in this location.
cachedDecodePattern
=
new
RegExp
(
'('
+
u4Bytes
+
')|('
+
u3Bytes
+
')|('
+
u2Bytes
+
')|('
+
u1Bytes
+
')|('
+
anyByte
+
')'
,
'gi'
);
}
return
bytes
.
replace
(
cachedDecodePattern
,
function
(
match
,
u4
,
u3
,
u2
,
u1
,
uBad
)
{
return
(
uBad
!==
undefined
)
?
'\uFFFD'
:
decodeURIComponent
(
match
);
});
}
// NOTE: Doesn't do the encoding/decoding dance
//
// https://url.spec.whatwg.org/#concept-urlencoded-parser
function
urlencoded_parse
(
input
,
isindex
)
{
var
sequences
=
input
.
split
(
'&'
);
if
(
isindex
&&
sequences
[
0
].
indexOf
(
'='
)
===
-
1
)
sequences
[
0
]
=
'='
+
sequences
[
0
];
var
pairs
=
[];
sequences
.
forEach
(
function
(
bytes
)
{
if
(
bytes
.
length
===
0
)
return
;
var
index
=
bytes
.
indexOf
(
'='
);
if
(
index
!==
-
1
)
{
var
name
=
bytes
.
substring
(
0
,
index
);
var
value
=
bytes
.
substring
(
index
+
1
);
}
else
{
name
=
bytes
;
value
=
''
;
}
name
=
name
.
replace
(
/\+/g
,
' '
);
value
=
value
.
replace
(
/\+/g
,
' '
);
pairs
.
push
({
name
:
name
,
value
:
value
});
});
var
output
=
[];
pairs
.
forEach
(
function
(
pair
)
{
output
.
push
({
name
:
percent_decode
(
pair
.
name
),
value
:
percent_decode
(
pair
.
value
)
});
});
return
output
;
}
function
URLUtils
(
url
)
{
if
(
nativeURL
)
return
new
origURL
(
url
);
var
anchor
=
document
.
createElement
(
'a'
);
anchor
.
href
=
url
;
return
anchor
;
}
function
URLSearchParams
(
init
)
{
var
$this
=
this
;
this
.
_list
=
[];
if
(
init
===
undefined
||
init
===
null
)
{
// no-op
}
else
if
(
init
instanceof
URLSearchParams
)
{
// In ES6 init would be a sequence, but special case for ES5.
this
.
_list
=
urlencoded_parse
(
String
(
init
));
}
else
if
(
typeof
init
===
'object'
&&
isSequence
(
init
))
{
Array
.
from
(
init
).
forEach
(
function
(
e
)
{
if
(
!
isSequence
(
e
))
throw
TypeError
();
var
nv
=
Array
.
from
(
e
);
if
(
nv
.
length
!==
2
)
throw
TypeError
();
$this
.
_list
.
push
({
name
:
String
(
nv
[
0
]),
value
:
String
(
nv
[
1
])});
});
}
else
if
(
typeof
init
===
'object'
&&
init
)
{
Object
.
keys
(
init
).
forEach
(
function
(
key
)
{
$this
.
_list
.
push
({
name
:
String
(
key
),
value
:
String
(
init
[
key
])});
});
}
else
{
init
=
String
(
init
);
if
(
init
.
substring
(
0
,
1
)
===
'?'
)
init
=
init
.
substring
(
1
);
this
.
_list
=
urlencoded_parse
(
init
);
}
this
.
_url_object
=
null
;
this
.
_setList
=
function
(
list
)
{
if
(
!
updating
)
$this
.
_list
=
list
;
};
var
updating
=
false
;
this
.
_update_steps
=
function
()
{
if
(
updating
)
return
;
updating
=
true
;
if
(
!
$this
.
_url_object
)
return
;
// Partial workaround for IE issue with 'about:'
if
(
$this
.
_url_object
.
protocol
===
'about:'
&&
$this
.
_url_object
.
pathname
.
indexOf
(
'?'
)
!==
-
1
)
{
$this
.
_url_object
.
pathname
=
$this
.
_url_object
.
pathname
.
split
(
'?'
)[
0
];
}
$this
.
_url_object
.
search
=
urlencoded_serialize
(
$this
.
_list
);
updating
=
false
;
};
}
Object
.
defineProperties
(
URLSearchParams
.
prototype
,
{
append
:
{
value
:
function
(
name
,
value
)
{
this
.
_list
.
push
({
name
:
name
,
value
:
value
});
this
.
_update_steps
();
},
writable
:
true
,
enumerable
:
true
,
configurable
:
true
},
'delete'
:
{
value
:
function
(
name
)
{
for
(
var
i
=
0
;
i
<
this
.
_list
.
length
;)
{
if
(
this
.
_list
[
i
].
name
===
name
)
this
.
_list
.
splice
(
i
,
1
);
else
++
i
;
}
this
.
_update_steps
();
},
writable
:
true
,
enumerable
:
true
,
configurable
:
true
},
get
:
{
value
:
function
(
name
)
{
for
(
var
i
=
0
;
i
<
this
.
_list
.
length
;
++
i
)
{
if
(
this
.
_list
[
i
].
name
===
name
)
return
this
.
_list
[
i
].
value
;
}
return
null
;
},
writable
:
true
,
enumerable
:
true
,
configurable
:
true
},
getAll
:
{
value
:
function
(
name
)
{
var
result
=
[];
for
(
var
i
=
0
;
i
<
this
.
_list
.
length
;
++
i
)
{
if
(
this
.
_list
[
i
].
name
===
name
)
result
.
push
(
this
.
_list
[
i
].
value
);
}
return
result
;
},
writable
:
true
,
enumerable
:
true
,
configurable
:
true
},
has
:
{
value
:
function
(
name
)
{
for
(
var
i
=
0
;
i
<
this
.
_list
.
length
;
++
i
)
{
if
(
this
.
_list
[
i
].
name
===
name
)
return
true
;
}
return
false
;
},
writable
:
true
,
enumerable
:
true
,
configurable
:
true
},
set
:
{
value
:
function
(
name
,
value
)
{
var
found
=
false
;
for
(
var
i
=
0
;
i
<
this
.
_list
.
length
;)
{
if
(
this
.
_list
[
i
].
name
===
name
)
{
if
(
!
found
)
{
this
.
_list
[
i
].
value
=
value
;
found
=
true
;
++
i
;
}
else
{
this
.
_list
.
splice
(
i
,
1
);
}
}
else
{
++
i
;
}
}
if
(
!
found
)
this
.
_list
.
push
({
name
:
name
,
value
:
value
});
this
.
_update_steps
();
},
writable
:
true
,
enumerable
:
true
,
configurable
:
true
},
entries
:
{
value
:
function
()
{
return
new
Iterator
(
this
.
_list
,
'key+value'
);
},
writable
:
true
,
enumerable
:
true
,
configurable
:
true
},
keys
:
{
value
:
function
()
{
return
new
Iterator
(
this
.
_list
,
'key'
);
},
writable
:
true
,
enumerable
:
true
,
configurable
:
true
},
values
:
{
value
:
function
()
{
return
new
Iterator
(
this
.
_list
,
'value'
);
},
writable
:
true
,
enumerable
:
true
,
configurable
:
true
},
forEach
:
{
value
:
function
(
callback
)
{
var
thisArg
=
(
arguments
.
length
>
1
)
?
arguments
[
1
]
:
undefined
;
this
.
_list
.
forEach
(
function
(
pair
)
{
callback
.
call
(
thisArg
,
pair
.
value
,
pair
.
name
);
});
},
writable
:
true
,
enumerable
:
true
,
configurable
:
true
},
toString
:
{
value
:
function
()
{
return
urlencoded_serialize
(
this
.
_list
);
},
writable
:
true
,
enumerable
:
false
,
configurable
:
true
},
sort
:
{
value
:
function
sort
()
{
var
entries
=
this
.
entries
();
var
entry
=
entries
.
next
();
var
keys
=
[];
var
values
=
{};
while
(
!
entry
.
done
)
{
var
value
=
entry
.
value
;
var
key
=
value
[
0
];
keys
.
push
(
key
);
if
(
!
(
Object
.
prototype
.
hasOwnProperty
.
call
(
values
,
key
)))
{
values
[
key
]
=
[];
}
values
[
key
].
push
(
value
[
1
]);
entry
=
entries
.
next
();
}
keys
.
sort
();
for
(
var
i
=
0
;
i
<
keys
.
length
;
i
++
)
{
this
[
"delete"
](
keys
[
i
]);
}
for
(
var
j
=
0
;
j
<
keys
.
length
;
j
++
)
{
key
=
keys
[
j
];
this
.
append
(
key
,
values
[
key
].
shift
());
}
}
}
});
function
Iterator
(
source
,
kind
)
{
var
index
=
0
;
this
.
next
=
function
()
{
if
(
index
>=
source
.
length
)
return
{
done
:
true
,
value
:
undefined
};
var
pair
=
source
[
index
++
];
return
{
done
:
false
,
value
:
kind
===
'key'
?
pair
.
name
:
kind
===
'value'
?
pair
.
value
:
[
pair
.
name
,
pair
.
value
]};
};
}
if
(
'Symbol'
in
global
&&
'iterator'
in
global
.
Symbol
)
{
Object
.
defineProperty
(
URLSearchParams
.
prototype
,
global
.
Symbol
.
iterator
,
{
value
:
URLSearchParams
.
prototype
.
entries
,
writable
:
true
,
enumerable
:
true
,
configurable
:
true
});
Object
.
defineProperty
(
Iterator
.
prototype
,
global
.
Symbol
.
iterator
,
{
value
:
function
()
{
return
this
;
},
writable
:
true
,
enumerable
:
true
,
configurable
:
true
});
}
function
URL
(
url
,
base
)
{
if
(
!
(
this
instanceof
global
.
URL
))
throw
new
TypeError
(
"Failed to construct 'URL': Please use the 'new' operator."
);
if
(
base
)
{
url
=
(
function
()
{
if
(
nativeURL
)
return
new
origURL
(
url
,
base
).
href
;
var
iframe
;
try
{
var
doc
;
// Use another document/base tag/anchor for relative URL resolution, if possible
if
(
Object
.
prototype
.
toString
.
call
(
window
.
operamini
)
===
"[object OperaMini]"
)
{
iframe
=
document
.
createElement
(
'iframe'
);
iframe
.
style
.
display
=
'none'
;
document
.
documentElement
.
appendChild
(
iframe
);
doc
=
iframe
.
contentWindow
.
document
;
}
else
if
(
document
.
implementation
&&
document
.
implementation
.
createHTMLDocument
)
{
doc
=
document
.
implementation
.
createHTMLDocument
(
''
);
}
else
if
(
document
.
implementation
&&
document
.
implementation
.
createDocument
)
{
doc
=
document
.
implementation
.
createDocument
(
'http://www.w3.org/1999/xhtml'
,
'html'
,
null
);
doc
.
documentElement
.
appendChild
(
doc
.
createElement
(
'head'
));
doc
.
documentElement
.
appendChild
(
doc
.
createElement
(
'body'
));
}
else
if
(
window
.
ActiveXObject
)
{
doc
=
new
window
.
ActiveXObject
(
'htmlfile'
);
doc
.
write
(
'<head></head><body></body>'
);
doc
.
close
();
}
if
(
!
doc
)
throw
Error
(
'base not supported'
);
var
baseTag
=
doc
.
createElement
(
'base'
);
baseTag
.
href
=
base
;
doc
.
getElementsByTagName
(
'head'
)[
0
].
appendChild
(
baseTag
);
var
anchor
=
doc
.
createElement
(
'a'
);
anchor
.
href
=
url
;
return
anchor
.
href
;
}
finally
{
if
(
iframe
)
iframe
.
parentNode
.
removeChild
(
iframe
);
}
}());
}
// An inner object implementing URLUtils (either a native URL
// object or an HTMLAnchorElement instance) is used to perform the
// URL algorithms. With full ES5 getter/setter support, return a
// regular object For IE8's limited getter/setter support, a
// different HTMLAnchorElement is returned with properties
// overridden
var
instance
=
URLUtils
(
url
||
''
);
// Detect for ES5 getter/setter support
// (an Object.defineProperties polyfill that doesn't support getters/setters may throw)
var
ES5_GET_SET
=
(
function
()
{
if
(
!
(
'defineProperties'
in
Object
))
return
false
;
try
{
var
obj
=
{};
Object
.
defineProperties
(
obj
,
{
prop
:
{
get
:
function
()
{
return
true
;
}
}
});
return
obj
.
prop
;
}
catch
(
_
)
{
return
false
;
}
}());
var
self
=
ES5_GET_SET
?
this
:
document
.
createElement
(
'a'
);
var
query_object
=
new
URLSearchParams
(
instance
.
search
?
instance
.
search
.
substring
(
1
)
:
null
);
query_object
.
_url_object
=
self
;
Object
.
defineProperties
(
self
,
{
href
:
{
get
:
function
()
{
return
instance
.
href
;
},
set
:
function
(
v
)
{
instance
.
href
=
v
;
tidy_instance
();
update_steps
();
},
enumerable
:
true
,
configurable
:
true
},
origin
:
{
get
:
function
()
{
if
(
this
.
protocol
.
toLowerCase
()
===
"data:"
)
{
return
null
}
if
(
'origin'
in
instance
)
return
instance
.
origin
;
return
this
.
protocol
+
'//'
+
this
.
host
;
},
enumerable
:
true
,
configurable
:
true
},
protocol
:
{
get
:
function
()
{
return
instance
.
protocol
;
},
set
:
function
(
v
)
{
instance
.
protocol
=
v
;
},
enumerable
:
true
,
configurable
:
true
},
username
:
{
get
:
function
()
{
return
instance
.
username
;
},
set
:
function
(
v
)
{
instance
.
username
=
v
;
},
enumerable
:
true
,
configurable
:
true
},
password
:
{
get
:
function
()
{
return
instance
.
password
;
},
set
:
function
(
v
)
{
instance
.
password
=
v
;
},
enumerable
:
true
,
configurable
:
true
},
host
:
{
get
:
function
()
{
// IE returns default port in |host|
var
re
=
{
'http:'
:
/:80$/
,
'https:'
:
/:443$/
,
'ftp:'
:
/:21$/
}[
instance
.
protocol
];
return
re
?
instance
.
host
.
replace
(
re
,
''
)
:
instance
.
host
;
},
set
:
function
(
v
)
{
instance
.
host
=
v
;
},
enumerable
:
true
,
configurable
:
true
},
hostname
:
{
get
:
function
()
{
return
instance
.
hostname
;
},
set
:
function
(
v
)
{
instance
.
hostname
=
v
;
},
enumerable
:
true
,
configurable
:
true
},
port
:
{
get
:
function
()
{
return
instance
.
port
;
},
set
:
function
(
v
)
{
instance
.
port
=
v
;
},
enumerable
:
true
,
configurable
:
true
},
pathname
:
{
get
:
function
()
{
// IE does not include leading '/' in |pathname|
if
(
instance
.
pathname
.
charAt
(
0
)
!==
'/'
)
return
'/'
+
instance
.
pathname
;
return
instance
.
pathname
;
},
set
:
function
(
v
)
{
instance
.
pathname
=
v
;
},
enumerable
:
true
,
configurable
:
true
},
search
:
{
get
:
function
()
{
return
instance
.
search
;
},
set
:
function
(
v
)
{
if
(
instance
.
search
===
v
)
return
;
instance
.
search
=
v
;
tidy_instance
();
update_steps
();
},
enumerable
:
true
,
configurable
:
true
},
searchParams
:
{
get
:
function
()
{
return
query_object
;
},
enumerable
:
true
,
configurable
:
true
},
hash
:
{
get
:
function
()
{
return
instance
.
hash
;
},
set
:
function
(
v
)
{
instance
.
hash
=
v
;
tidy_instance
();
},
enumerable
:
true
,
configurable
:
true
},
toString
:
{
value
:
function
()
{
return
instance
.
toString
();
},
enumerable
:
false
,
configurable
:
true
},
valueOf
:
{
value
:
function
()
{
return
instance
.
valueOf
();
},
enumerable
:
false
,
configurable
:
true
}
});
function
tidy_instance
()
{
var
href
=
instance
.
href
.
replace
(
/#$|\?$|\?(?=#)/g
,
''
);
if
(
instance
.
href
!==
href
)
instance
.
href
=
href
;
}
function
update_steps
()
{
query_object
.
_setList
(
instance
.
search
?
urlencoded_parse
(
instance
.
search
.
substring
(
1
))
:
[]);
query_object
.
_update_steps
();
}
return
self
;
}
if
(
origURL
)
{
for
(
var
i
in
origURL
)
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
origURL
,
i
)
&&
typeof
origURL
[
i
]
===
'function'
)
URL
[
i
]
=
origURL
[
i
];
}
}
global
.
URL
=
URL
;
global
.
URLSearchParams
=
URLSearchParams
;
})();
// Patch native URLSearchParams constructor to handle sequences/records
// if necessary.
(
function
()
{
if
(
new
global
.
URLSearchParams
([[
'a'
,
1
]]).
get
(
'a'
)
===
'1'
&&
new
global
.
URLSearchParams
({
a
:
1
}).
get
(
'a'
)
===
'1'
)
return
;
var
orig
=
global
.
URLSearchParams
;
global
.
URLSearchParams
=
function
(
init
)
{
if
(
init
&&
typeof
init
===
'object'
&&
isSequence
(
init
))
{
var
o
=
new
orig
();
Array
.
from
(
init
).
forEach
(
function
(
e
)
{
if
(
!
isSequence
(
e
))
throw
TypeError
();
var
nv
=
Array
.
from
(
e
);
if
(
nv
.
length
!==
2
)
throw
TypeError
();
o
.
append
(
nv
[
0
],
nv
[
1
]);
});
return
o
;
}
else
if
(
init
&&
typeof
init
===
'object'
)
{
o
=
new
orig
();
Object
.
keys
(
init
).
forEach
(
function
(
key
)
{
o
.
set
(
key
,
init
[
key
]);
});
return
o
;
}
else
{
return
new
orig
(
init
);
}
};
})();
}(
self
));
File Metadata
Details
Attached
Mime Type
text/x-Algol68
Expires
Sat, Oct 3, 12:54 (2 d, 9 h ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ce/eb/dac1e53d20fa288ac7b1579f5840
Default Alt Text
URL.js (17 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment