Why Doesn't Decodeuri("A+B") == "A B"

When are you supposed to use escape instead of encodeURI / encodeURIComponent?

escape()

Don't use it!
escape() is defined in section B.2.1.1 escape and the introduction text of Annex B says:

... All of the language features and behaviours specified in this annex have one or more undesirable characteristics and in the absence of legacy usage would be removed from this specification. ...

... Programmers should not use or assume the existence of these features and behaviours when writing new ECMAScript code....

Behaviour:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/escape

Special characters are encoded with the exception of: @*_+-./

The hexadecimal form for characters, whose code unit value is 0xFF or less, is a two-digit escape sequence: %xx.

For characters with a greater code unit, the four-digit format %uxxxx is used. This is not allowed within a query string (as defined in RFC3986):

query       = *( pchar / "/" / "?" )
pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
pct-encoded = "%" HEXDIG HEXDIG
sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
/ "*" / "+" / "," / ";" / "="

A percent sign is only allowed if it is directly followed by two hexdigits, percent followed by u is not allowed.

encodeURI()

Use encodeURI when you want a working URL. Make this call:

encodeURI("http://www.example.org/a file with spaces.html")

to get:

http://www.example.org/a%20file%20with%20spaces.html

Don't call encodeURIComponent since it would destroy the URL and return

http%3A%2F%2Fwww.example.org%2Fa%20file%20with%20spaces.html

Note that encodeURI, like encodeURIComponent, does not escape the ' character.

encodeURIComponent()

Use encodeURIComponent when you want to encode the value of a URL parameter.

var p1 = encodeURIComponent("http://example.org/?a=12&b=55")

Then you may create the URL you need:

var url = "http://example.net/?param1=" + p1 + "¶m2=99";

And you will get this complete URL:

http://example.net/?param1=http%3A%2F%2Fexample.org%2F%Ffa%3D12%26b%3D55¶m2=99

Note that encodeURIComponent does not escape the ' character. A common bug is to use it to create html attributes such as href='MyUrl', which could suffer an injection bug. If you are constructing html from strings, either use " instead of ' for attribute quotes, or add an extra layer of encoding (' can be encoded as %27).

For more information on this type of encoding you can check: http://en.wikipedia.org/wiki/Percent-encoding

Converting between strings and ArrayBuffers

Update 2016 - five years on there are now new methods in the specs (see support below) to convert between strings and typed arrays using proper encoding.

TextEncoder

The TextEncoder represents:

The TextEncoder interface represents an encoder for a specific method,
that is a specific character encoding, like utf-8, iso-8859-2, koi8,
cp1261, gbk, ...
An encoder takes a stream of code points as input and
emits a stream of bytes.

Change note since the above was written: (ibid.)

Note: Firefox, Chrome and Opera used to have support for encoding
types other than utf-8 (such as utf-16, iso-8859-2, koi8, cp1261, and
gbk). As of Firefox 48 [...], Chrome 54 [...] and Opera 41, no
other encoding types are available other than utf-8, in order to match
the spec.*

*) Updated specs (W3) and here (whatwg).

After creating an instance of the TextEncoder it will take a string and encode it using a given encoding parameter:

if (!("TextEncoder" in window))   alert("Sorry, this browser does not support TextEncoder...");
var enc = new TextEncoder(); // always utf-8console.log(enc.encode("This is a string converted to a Uint8Array"));

What is the shortest function for reading a cookie by name in JavaScript?

Shorter, more reliable and more performant than the current best-voted answer:

const getCookieValue = (name) => (
document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)')?.pop() || ''
)

A performance comparison of various approaches is shown here:

https://jsben.ch/AhMN6

Some notes on approach:

The regex approach is not only the fastest in most browsers, it yields the shortest function as well. Additionally it should be pointed out that according to the official spec (RFC 2109), the space after the semicolon which separates cookies in the document.cookie is optional and an argument could be made that it should not be relied upon. Additionally, whitespace is allowed before and after the equals sign (=) and an argument could be made that this potential whitespace should be factored into any reliable document.cookie parser. The regex above accounts for both of the above whitespace conditions.

Adding a parameter to the URL with JavaScript

A basic implementation which you'll need to adapt would look something like this:

function insertParam(key, value) {
key = encodeURIComponent(key);
value = encodeURIComponent(value);

// kvp looks like ['key1=value1', 'key2=value2', ...]
var kvp = document.location.search.substr(1).split('&');
let i=0;

for(; i<kvp.length; i++){
if (kvp[i].startsWith(key + '=')) {
let pair = kvp[i].split('=');
pair[1] = value;
kvp[i] = pair.join('=');
break;
}
}

if(i >= kvp.length){
kvp[kvp.length] = [key,value].join('=');
}

// can return this or...
let params = kvp.join('&');

// reload page with new params
document.location.search = params;
}

This is approximately twice as fast as a regex or search based solution, but that depends completely on the length of the querystring and the index of any match


the slow regex method I benchmarked against for completions sake (approx +150% slower)

function insertParam2(key,value)
{
key = encodeURIComponent(key); value = encodeURIComponent(value);

var s = document.location.search;
var kvp = key+"="+value;

var r = new RegExp("(&|\\?)"+key+"=[^\&]*");

s = s.replace(r,"$1"+kvp);

if(!RegExp.$1) {s += (s.length>0 ? '&' : '?') + kvp;};

//again, do what you will here
document.location.search = s;
}

Enabling 4-digit pincode autofill using cookies

For cookies I use my "simpleCookie" object with the set / getVal methods to read or save a cookie.

ex:

simpleCookie.setVal( 'my cookie', 'ab/kjf;c', 3 )

let valueX = simpleCookie.getVal('my cookie')) // return 'ab/kjf;c'

simpleCookie.setVal( 'my cookie', '', -1) remove the cookie

this object is achieved via an IIEF function, and I strongly advise you to use the mozilla documentation

Since automatic form validation exists, I no longer use a text box to indicate an input error, but I have diverted its "normal" use a bit because I find it very restrictive, as you will see in my code.

When at the base of your question, you just have to find a match between the name entered and a possible cookie with the same name, then save this cookie if the form is valid.

Oh, and I also put some css to simplify writing html (no more need for <br>)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login form</title>
<style>
fieldset { margin: 1em; width: 15em; }
fieldset * { display: block; float: left; clear: both; margin-top: 0.2em; }
fieldset label { margin-top: 0.7em; }
fieldset button { margin-top: 2em; }
fieldset button:last-of-type { clear: none; float: right; }
</style>
</head>
<body>
<form id="login-form" action="">
<fieldset>
<label>Name:</label>
<input type="text" name="name" autocomplete="off" pattern="[A-Za-z0-9]{1,20}">
<label>Password:</label>
<input type="password" name="pass" pattern="[A-Za-z0-9]{1,20}">
<label>4-digit PIN:</label>
<input type="text" name="pinCode" autocomplete="off" pattern="[0-9]{4}">
<button type="reset">clear</button>
<button type="submit">Log In</button>
</fieldset>
</form>

<script src="simpleCoolie.js"></script> <!-- the cookie object (IIFE) -->
<script src="login_form.js"></script>
</body>
</html>

simpleCoolie.js :

// the cookie object (IIFE)
const simpleCookie = (function()
{
const OneDay_ms = 24 *60 *60 *1000 // one Day in milliseconds
return {
setVal:(cName, cValue='', exDays=10)=> // 10 days is default cookie recovery,
{ // negative value remove the cookie
cName = encodeURIComponent(cName.trim())
cValue = encodeURIComponent(cValue.trim())
if (cName)
{
let dte = new Date()
dte.setTime(dte.getTime() + (exDays *OneDay_ms))
document.cookie = `${cName}=${cValue};expires=${dte.toUTCString()};SameSite=Strict;path=/`
} }
, getVal:cName=>
{
cName = encodeURIComponent(cName.trim())
let xCookie = document.cookie.split('; ').find(x=>x.startsWith(`${cName}=`))
return xCookie ? decodeURIComponent(xCookie.split('=')[1]) : ''
} }
})()

login_form.js :

const formLogin        = document.getElementById('login-form')
, msgErrorDuration = 5000
, checkInputs =
[...formLogin.querySelectorAll('input[pattern]')]
.map(el=>
{
let pattern = el.pattern
el.removeAttribute('pattern')
return { name:el.name, pattern }
});

// generic set checking for report validyty
const getCheckingValidity=(formElement, patternValue)=>
{
formElement.pattern = patternValue
formElement.required = true
return formElement.reportValidity()
}
// generic checking remove after delay
const unCheckElement=(formElement,isOK)=>
{
formElement.removeAttribute('pattern')
formElement.required = false

if(!isOK)
{
formElement.setCustomValidity('')
if(document.activeElement === formElement ) // bugg fix: Firefox doesnt remove error message after delay
{ // (other browser do)
formElement.blur(); // double flip focus
formElement.focus(); // --> will remove message bubble
}
}
}

// client-side form validation mecanism to get error message for each input
formLogin.name.oninvalid=_=>
{
formLogin.name.setCustomValidity('Please enter a name')
setTimeout(unCheckElement, msgErrorDuration, formLogin.name, false)
}
formLogin.pass.oninvalid=_=>
{
formLogin.pass.setCustomValidity("can't do anything without password !")
setTimeout(unCheckElement, msgErrorDuration, formLogin.pass, false)
}
formLogin.pinCode.oninvalid=_=>
{
if (formLogin.pinCode.value==='')
{ formLogin.pinCode.setCustomValidity("PIN code can't be empty !") }
else
{ formLogin.pinCode.setCustomValidity('PIN code must be 4 digits') }
setTimeout(unCheckElement, msgErrorDuration, formLogin.pinCode, false)
}

formLogin.onsubmit=e=>
{
let validForm = true

for (let Elm of checkInputs)
{
validForm = validForm && getCheckingValidity(formLogin[Elm.name], Elm.pattern )
if (validForm)
{ unCheckElement(formLogin[Elm.name], true) }
else break
}
if (validForm)
{ simpleCookie.setVal( formLogin.name.value, formLogin.pinCode.value ) }
else
{ e.preventDefault() } // disable form submiting
}

formLogin.name.oninput=()=> // check for cookie pin code on name
{
formLogin.pinCode.value = simpleCookie.getVal(formLogin.name.value)
}

In 2009, session/localStorage arrived, which can replace cookies, especially for this kind of use.

To not have to redo all the previous logic, I created here a module called pseudoCookie which actually uses localStorage

here is the complete code to test with it:

// the pseudo cookie object (IIFE)
const pseudoCookie = (function() // use localStorage
{
return {
setVal:(cName, cValue='', exDays=10)=> // negative value remove the value in localstorage
{ // the values are kept until your browser or your system crashes
cName = encodeURIComponent(cName.trim())
cValue = encodeURIComponent(cValue.trim())
if (cName) {
if (exDays < 0) localStorage.removeItem(cName)
else localStorage.setItem(cName, cValue)
} }
, getVal:cName=>
{
cName = encodeURIComponent(cName.trim())
let xCookie = localStorage.getItem(cName)
return xCookie ? decodeURIComponent(xCookie) : ''
} }
})()

and the part to change in JS:

formLogin.onsubmit=e=>
{
let validForm = true

for (let Elm of checkInputs)
{
validForm = validForm && getCheckingValidity(formLogin[Elm.name], Elm.pattern )
if (validForm)
{ unCheckElement(formLogin[Elm.name], true) }
else break
}
if (validForm)
{ pseudoCookie.setVal( formLogin.name.value, formLogin.pinCode.value ) }
else
{ e.preventDefault() } // disable form submiting
}

formLogin.name.oninput=()=> // check for cookie pin code on name
{
formLogin.pinCode.value = pseudoCookie.getVal(formLogin.name.value)
}


Related Topics



Leave a reply



Submit