I have read the posts here and here, but can't get the encoding right.
In javascript, I am writing a link to an <input> which is then read with document.getElementById('downloadlink').value and placed into an iframe src to be executed. 
The code works fine on my localhost but not live server ,where the file is never loaded due to an invalid name (note: the http 302 Moved Temporarily is due to an .htaccess redirect due to an invalid file request). I am sure this is an issue with encoding ampersands. My understanding is that the input needs the ampersands (and other html entities) encoded. I am using php.js equivalents of htmlspecialchars() and htmlspecialchars_decode() for this (again this works on localhost). 
For the  img.src, my understanding is that I want ampersands encoded to & However on my localhost, it works without them but not with them encoded. On my live site, it doesn't work either way.
To encode them, I have tried:
url = url.replace(/&/g, "&"); 
It's time to stop tearing my hair out and ask for help. Anyone?
The iframe (with encoding from js htmlspecialchars) NOTE: I can't get the & to stay encoded for the display - they get replaced on save.
<iframe src="www.waldorfteacherresources.com/getfile.php?file=g2-saints-martin-009.jpg&mode=download&hv=4443f86959bf104e1df0eac204b8aaf226ae533b&wtrpath=docs " id="iframe" height="0" width="0" hidden=""></iframe>
The code
function downloadfile() {
    if (document.getElementById("downloadlink")) {
    var div = document.getElementById('datadiv');
    var url = hx(document.getElementById('downloadlink').value);
    var ifrm = document.createElement("iframe");
    ifrm.setAttribute("src", url);
    ifrm.setAttribute("id", "iframe");
    ifrm.height = 0;
    ifrm.width = 0;
    ifrm.hidden = true;
    div.appendChild(ifrm);
    }
}
// support functions to encode / decode 
// encodes output - equivalent of php hx function
// hx notation is a shortcut for htmlspecialchars() with all options set
function hx( string,  flags,  charsetEncoding,  double_encode) {
    if (typeof flags == "undefined"){
        flags = 0;
    }
    if (typeof charsetEncoding == "undefined" ){
    charsetEncoding = "UTF-8";
    }    
    if (typeof double_encode == "undefined"){
        double_encode = true;
    }
    // constants not valid until php v 5.4
    var ENT_HTML401 = 0;
    var ENT_HTML5 = (16 | 32);
    var ENT_COMPAT = 2;
    if ( flags == 0) {
     flags = ENT_COMPAT |  ENT_HTML401;
    }
     string = htmlspecialchars( string,  flags,  charsetEncoding,  double_encode);
    return  string;
}
// decodes output of hx() / htmlspecialchars() - shortcut notation for htmlspecialchars_decode()
function hdx(string) {
    return htmlspecialchars_decode(string);
}
function htmlspecialchars(string, quote_style, charset, double_encode) {
  //       discuss at: http://phpjs.org/functions/htmlspecialchars/
  var optTemp = 0,
    i = 0,
    noquotes = false;
  if (typeof quote_style === 'undefined' || quote_style === null) {
    quote_style = 2;
  }
  string = string.toString();
  if (double_encode !== false) { // Put this first to avoid double-encoding
    string = string.replace(/&/g, '&');
  }
  string = string.replace(/</g, '<')
    .replace(/>/g, '>');
  var OPTS = {
    'ENT_NOQUOTES': 0,
    'ENT_HTML_QUOTE_SINGLE': 1,
    'ENT_HTML_QUOTE_DOUBLE': 2,
    'ENT_COMPAT': 2,
    'ENT_QUOTES': 3,
    'ENT_IGNORE': 4
  };
  if (quote_style === 0) {
    noquotes = true;
  }
  if (typeof quote_style !== 'number') { // Allow for a single string or an array of string flags
    quote_style = [].concat(quote_style);
    for (i = 0; i < quote_style.length; i++) {
      // Resolve string input to bitwise e.g. 'ENT_IGNORE' becomes 4
      if (OPTS[quote_style[i]] === 0) {
        noquotes = true;
      } else if (OPTS[quote_style[i]]) {
        optTemp = optTemp | OPTS[quote_style[i]];
      }
    }
    quote_style = optTemp;
  }
  if (quote_style & OPTS.ENT_HTML_QUOTE_SINGLE) {
    string = string.replace(/'/g, ''');
  }
  if (!noquotes) {
    string = string.replace(/"/g, '"');
  }
  return string;
}
function htmlspecialchars_decode(string, quote_style) {
    //       discuss at: http://phpjs.org/functions/htmlspecialchars_decode/
    var optTemp = 0,
        i = 0,
        noquotes = false;
    if (typeof quote_style === 'undefined') {
    quote_style = 2;
    }
    string = string.toString().replace(/</g, '<').replace(/>/g, '>');
    var OPTS = {
    'ENT_NOQUOTES': 0,
    'ENT_HTML_QUOTE_SINGLE': 1,
    'ENT_HTML_QUOTE_DOUBLE': 2,
    'ENT_COMPAT': 2,
    'ENT_QUOTES': 3,
    'ENT_IGNORE': 4
    };
    if (quote_style === 0) {
    noquotes = true;
    }
    if (typeof quote_style !== 'number') { // Allow for a single string or an array of string flags
    quote_style = [].concat(quote_style);
    for (i = 0; i < quote_style.length; i++) {
        // Resolve string input to bitwise e.g. 'PATHINFO_EXTENSION' becomes 4
        if (OPTS[quote_style[i]] === 0) {
        noquotes = true;
        } else if (OPTS[quote_style[i]]) {
        optTemp = optTemp | OPTS[quote_style[i]];
        }
    }
    quote_style = optTemp;
    }
    if (quote_style & OPTS.ENT_HTML_QUOTE_SINGLE) {
    string = string.replace(/'/g, "'");
    }
    if (!noquotes) {
    string = string.replace(/"/g, '"');
    }
    // Put this in last place to avoid escape being double-decoded
    string = string.replace(/&/g, '&');
    return string;
}
The request headers
    GET /www.example.com/getfile.php?file=myfile.jpg&mode=download&hv=939afca0cdaafd55a1e1471da7463be9acbf5478&wtrpath=docs HTTP/1.1
    Host: www.example.com
    Connection: keep-alive
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36
    Referer: http://www.waldorfteacherresources.com/index.php?grade=2&page=Saints
    Accept-Encoding: gzip, deflate, sdch
    Accept-Language: en-US,en;q=0.8
    Cookie: id=XXX;
    PHPSESSID=.... session info here
The response
    HTTP/1.1 302 Moved Temporarily
    Date: Sun, 21 Feb 2016 21:16:05 GMT
    Server: Apache
    X-Powered-By: PHP/5.5.32
    **** this is an .htaccess redirect due to an invalid file request
    Location: /index.php
    Cache-Control: max-age=86400
    Expires: Thu, 01 Jan 1970 00:00:00 GMT
    Vary: Accept-Encoding
    Content-Encoding: gzip
    Content-Length: 767
    Keep-Alive: timeout=3, max=100
    Connection: Keep-Alive
    Content-Type: text/html; charset=UTF-8
 
    