3

Microsoft are quoted as saying that javascript is now a first class citizen in Visual Studio and the "Universal Windows platform" but I have yet to find a way of upgrading the decade+ old JScript engine used in IIS/Classic ASP script. So, my question is, does anyone know if there is a way to do this?

Why?

I'd like to use JSON.parse, for example, in a classic ASP page (that uses javascript not VBScript). Currently, I'm including a copy of Crockford's old json script which is okay but these days should be unnecessary.

CodaCoder
  • 31
  • 5
  • Upgrade your IE on the sever to IE 11 usually helps but classic ASP is such an old tech. – Lex Li Jan 20 '16 at 02:50
  • So what's stopping you from using [JSON.Parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse)? – Paul Jan 20 '16 at 08:48
  • @Lex Li: I have one dev env using Win10 (so IE11 and Edge are installed) but Classic ASP pages still are using the older JScript AFAICT and I still needed the Crockford lib to be included. – CodaCoder Jan 20 '16 at 14:43
  • @Paul: Nothing, if I include a 3rd party lib. The point is, it's not the native javascript object and implementation of a modern javascript engine. That's why things like [aspjson](http://aspjson.com/) exist. – CodaCoder Jan 20 '16 at 14:47

2 Answers2

5

Why? Well, as you probably know, hosts with Chakra available don't have it enabled by default. According to MSDN documentation:

Starting with JScript 5.8, by default, the JScript scripting engine supports the language feature set as it existed in version 5.7. This is to maintain compatibility with the earlier versions of the engine. To use the complete language feature set of version 5.8, the Windows Script interface host has to invoke IActiveScriptProperty::SetProperty.

From what I've been able to understand, this means you'd have to code your own custom script execution host to evaluate your existing code with Chakra. -_-

As thoroughly enthralling as such a kludge sounds, it's much easier to clone whatever object and methods you need from elsewhere. The htmlfile COM object can expose objects and methods not available to the current script host, simply by forcing it into a compatibility mode.

// classic WSH JScript version
var htmlfile = new ActiveXObject('htmlfile'), JSON;
htmlfile.write('<meta http-equiv="x-ua-compatible" content="IE=9" />');
htmlfile.close(JSON = htmlfile.parentWindow.JSON);

And voila! Now you can JSON.parse() or JSON.stringify() till your heart's content, without having to include json2.js, and without having to go through the enormous effort of invoking IActiveScript::SetProperty.

A quick note about that code snippet above: htmlfile.write('<meta... etc />') works in Classic JScript, but .NET hosts struggle with the write() and writeln() methods for some reason. IHTMLDocument2_write() and IHTMLDocument2_writeln() should be used instead if you ever switch to .aspx and JScript.NET.

// JScript.NET version
var htmlfile:Object = new ActiveXObject('htmlfile'), JSON:Object = {};
htmlfile.IHTMLDocument2_write('<meta http-equiv="x-ua-compatible" content="IE=9" />');
htmlfile.close(JSON = htmlfile.parentWindow.JSON);

I'd also like to point out that other more modern ECMAscript methods can be imported in a similar way. Here's a demo of a few other methods that aren't natively available in JScript 5.7 but can be cloned from htmlfile in IE9 standards mode. Save this with an .asp extension visit it in your web browser:

<%@ Language="JScript" %>
<h3>Output:</h3>
<textarea style="width: 100%; height: 5em"><%
var htmlfile = Server.CreateObject('htmlfile');
htmlfile.write('<meta http-equiv="x-ua-compatible" content="IE=9" />');

// expose more modern methods from htmlfile
var JSON = htmlfile.parentWindow.JSON;
String.prototype.trim = htmlfile.parentWindow.String.prototype.trim;
Array.prototype.indexOf = htmlfile.parentWindow.Array.prototype.indexOf;
Array.prototype.forEach = htmlfile.parentWindow.Array.prototype.forEach;
Object.keys = htmlfile.parentWindow.Object.keys;

htmlfile.close(); // no longer needed

// demonstrate JSON.parse() and String.trim()
var strJSON = '{ "item1": "          val1 needs trimmed.          " }';
var objFromJSON = JSON.parse(strJSON);
Response.Write('JSON and String.trim() demo result: ' + objFromJSON.item1.trim() + '\n');

// demonstrate Array.indexOf()
var arr = [2, 4, 6, 8, 10];
Response.Write('Array.indexOf(val) demo result: ' + arr.indexOf(4) + '\n');

// demonstrate Object.keys() and Array.forEach()
var demo = { "foo": "bar", "baz ": "qux" };
demo.getKey = function(val) {
    var obj = this, result;
    Object.keys(obj).forEach(function(i) {
        if (obj[i] === val) result = i;
    });
    return result;
}
Response.Write('Object.keys(obj).forEach(fn) demo result: ' + demo.getKey('qux'));
%></textarea>

Output:

JSON and String.trim() demo result: val1 needs trimmed.
Array.indexOf(val) demo result: 1
Object.keys(obj).forEach(fn) demo result: baz
rojo
  • 24,000
  • 5
  • 55
  • 101
  • That's an intriguing approach - shame I couldn't get it to work. I can do Server.CreateObject("htmlfile") (also tried ActiveXObject) from the ASP but htmlfile.write fails with "htmlfile: Invalid argument". Note: Checking in visual studio debugger shows the htmlfile object is created and I see the write method is available – CodaCoder Jan 26 '16 at 23:27
  • @CodaCoder Try the `htmlfile.IHTMLDocument2_write()` method instead and see if you have better luck. – rojo Jan 27 '16 at 00:25
  • "Object doesn't support this property or method" :( – CodaCoder Jan 27 '16 at 15:22
  • @CodaCoder The function definition for `htmlfile.write` is `System.Void write(Params System.Object[] psarray)` according to PowerShell. You could try `htmlfile.write(['']);` (putting the string into a 1-element array). I also wonder if it's possible that you've got a quoting conflict somehow? – rojo Jan 27 '16 at 16:13
  • You know what, that ran through my head and then I promptly forgot to try it... I'll be back :) – CodaCoder Jan 27 '16 at 16:27
  • Do you have a link for those APIs? – CodaCoder Jan 27 '16 at 16:29
  • I've not been able to find any documentation for the `htmlfile` COM object. [This, maybe](https://msdn.microsoft.com/en-us/library/aa752574%28v=vs.85%29.aspx)? I use powershell to investigate the methods. `powershell "new-object -COM htmlfile | gm | ?{ $_.Name -eq 'write' } | select -expand Definition"`. To see all the methods, `powershell "new-object -COM htmlfile | gm"`. Anyway, I don't see what could be invalid about the argument. If you store the meta string as a variable and output the variable, does it output correctly, or at least give you any clue about the cause of the failure? – rojo Jan 27 '16 at 16:39
  • Thanks for all your help rojo - tried the meta string in a var, same results (I tried a simple div too) - same issue(s). I'm about done with this - turning into a time sink :/ – CodaCoder Jan 27 '16 at 17:17
  • @CodaCoder Man, either you introduced a syntax error, or your webserver doesn't include full Classic ASP support. I installed [IIS 10.0 Express](https://www.microsoft.com/en-us/download/details.aspx?id=48264) for testing, saved the demo code block above with an .asp extension (exchanging console methods for server). I inserted the block within ` – rojo Jan 29 '16 at 03:20
  • 1
    Perhaps but I doubt it's a syntax error, the error is invalid arg and deploying ASP is pretty trivial [following this](https://technet.microsoft.com/en-us/library/cc753918%28v=ws.10%29.aspx). FWIW, I'm testing on a Win7Pro box with IIS7.5 and IE11. And seriously, with over 400K users of the app which uses both ASP.NET and classic, I'd know if the deployment was an issue. – CodaCoder Jan 29 '16 at 14:09
  • Well, I've confirmed that you're not crazy, that there is something going on. Script tests successfully on IIS 10.0 Express both with Win 7 Enterprise and Win 10 Pro. But on IIS 7.5 running on Server 2008, the script fails with a `TypeError` at `'htmlfile.parentWindow.String.prototype' is null or not an object.` The `htmlfile.write()` method didn't error, though. Weird! `o°/` Well, save this answer for a year or two, and maybe it'll eventually be useful after your next IIS upgrade. And I sincerely apologize for accusing you of a PEBCAK error. – rojo Jan 29 '16 at 16:42
  • 2
    For anyone with the same issue I found a solution here: https://social.msdn.microsoft.com/Forums/en-US/20cfbfa5-b3ee-4337-9fc6-dd59bbf05708/htmlfilewrite-invalid-argument-on-iis-classicasp-windows-10?forum=windowsazuredevelopment " I just sorted it out! Turns out it was due to a setting 'Load User Profile' in the sites app pool Process Model group (advanced settings). From what I've read the suggestion is to have it set to load the user profile. Setting it to false allowed it to work and fixed the problem!" – Tim Williams May 25 '19 at 02:59
1

Setting "Load User Profile" to false didn't work for me, it breaks the whole application pool (probably because it's using ApplicationPoolIdentity).

What worked for me though, is creating the htmlfile object in global.asa like this:

<object runat="server" scope="application" id="JScriptHelper" progid="htmlfile"></object>
<script language="VBScript" runat="server">
Sub Application_OnStart
    JScriptHelper.write "<meta http-equiv=""x-ua-compatible"" content=""IE=9"" />"
End Sub
</script>

(I'm not sure if it's my configuration, but some functions are very slow. For example, finding the location of a unique value in an array with 60000 elements by using lastIndexOf takes 5 seconds, whereas a polyfill does it in less than 100ms.)

remonedo
  • 27
  • 3