Why are browsers inserting a `\9` into this style attribute - javascript

var div = document.createElement('div');
div.style.cssText = "background-image: url('http://imgs.com/mouse.png ')";
div.style.cssText
//=> "background-image: url("http://imgs.com/mouse.png\9 ");"
The above code, with that being a tab after the .png suffix, when run in either Chrome or Firefox console will output the shown line with with \9 appended to the URL. Why is that?
I've read that \9 is some sort of a hack for targeting only specific versions of IE, but why would Chrome and Firefox be automatically inserting that?
PS: For additional browser fun, can you guess what happens if that URL is relative and you want to resolve it with the classic <a> href trick? Here's what would happen on the fictional http://imgs.com domain:
var a = document.createElement('a');
// Actually parsed from the above response, not assigned manually
a.href = "/mouse.png\\9 ";
a.href
//=> "http://imgs.com/mouse.png/9" (in Chrome)
//=> "http://imgs.com/mouse.png%5C9" (in Firefox)
So while the URL with the \9 suffix would still actually work, the resolved URLs will now point to incorrect locations.

When the console has to print out source code, it tries to do so in a way that's unambiguous. A tab character is not obviously a tab, and there are many other special characters with that same problem. Therefore, non-printable or non-obvious characters are rendered using an appropriate escape sequence, where "appropriate" means "appropriate to the language context".
CSS escapes look like \nnnnnn where the ns are hex digits. There can be from one to six such digits. The escape \9 is the escape for the tab character.
Note that in actual CSS source text, you too can use actual tab characters and the notation \9 interchangeably and get precisely the same results, because the CSS parser interprets \9 as a single tab character, just like an actual tab character.
The described behavior of the console is not in any standard (because the developer tools themselves are not standardized), but it's the sort of thing that any designer of such tools is very likely to do.

Related

Applescript with do Javascript and passed Applescript Variable

I have written a script that has automated the creation of products on a website I administer. In my process I upload JPEG images of the products and pull the Keywords that are tagged in the JPEG to add into the product information. In this process I use Applescript to Activate Safari and process a Javascript line of code. The line of code includes the a variable that is derived from Applescript Shell Script.
Code below
tell application "Finder"
set sourceFolder to folder POSIX file "/Users/<username>/Desktop/Upload/Temp/HighRes/"
set theFiles to files of sourceFolder
set inputPath to "/Users/<username>/Desktop/Upload/Temp/"
end tell
repeat with afile in theFiles
set filename to name of afile
set fname to text 1 thru ((offset of "." in filename) - 1) of filename
--INPUT CODE TO BE LOOPED OVER BELOW--
--Add Image Keywords from Metadata--
try
set pathVAR1 to "/Users/<username>/Desktop/Upload/Temp/HighRes/"
set pathVAR2 to pathVAR1 & filename
set myvar to do shell script "mdls -name kMDItemKeywords " & quoted form of pathVAR2
set var1 to ((offset of "(" in myvar) + 1)
set var2 to ((length of myvar) - 1)
set myKeywords to ((characters var1 thru var2 of myvar) as string)
--Inputs the Keywords from the Image Metadata--
tell application "Safari"
activate
do JavaScript "document.getElementById('ctl00_cphMainContent_txtKeyWords').value = \"" & myKeywords & "\";" in current tab of window 1
end tell
end try
--END OF CODE TO BE LOOPED OVER--
end repeat
==End Code==
Problem:
The code below is not passing the variable myKeywords to Safari, but if I run a dialog it will appear in the dialog.
do JavaScript "document.getElementById('ctl00_cphMainContent_txtKeyWords').value = \"" & myKeywords & "\";" in current tab of window 1
I don't have a specific solution that will definitely solve your problem, but I do have a number of observations about your script with recommendations on how it can be changed to improve its speed, robustness and adherence to principles of best practice.
Get rid of that try block. You have no idea what's happening in your script when things go wrong if you're masking the errors with unnecessary error-catching. The only line that needs to be enclosed in try...end try is do shell script, but only put it in once you know your code is working. In general, try blocks should only be used:
when your script has the potential to throw an error that is entirely predictable and explainable, and you understand the reasons why and under what conditions the error occurs, allowing you to implement an effective error-handling method;
around the fewest possible number of lines of code within which the error arises, leaving all lines of code whose existence doesn't depend on the result of the error-prone statement(s);
after your script has been written, tested, and debugged, where placing the try block(s) no longer serves to force a script to continue executing in the wake of an inconvenient error of unknown origin, but has a clear and well-defined function to perform in harmony with your code, and not against it.
As a general rule in AppleScript, don't use Finder to perform file system operations if you can avoid it: it's slow, and blocks while it's performing the operations, meaning you can't interact with the GUI during this time. Use System Events instead. It's a faceless application that won't stop other things operating when it's performing a task; it's fast, in the context of AppleScript and Finder in particular, and isn't prone to timing out quite so much as Finder does; it handles posix paths natively (including expansion of tildes), without any coercion necessary using POSIX file; it returns alias objects, which are the universal class of file object that every other scriptable application understands.
There are a couple of instances where Finder is still necessary. System Events cannot reveal a file; nor can it get you the currently selected files in Finder. But it's simple enough to have Finder retrieve the selection as an alias list, then switch to System Events to do the actual file handling on this list.
This is curious:
set filename to name of afile
set fname to text 1 thru ((offset of "." in filename) - 1) of filename
Am I right in thinking that fname is intending to hold just the base file name portion of the file name, and this operation is designed to strip off the extension ? It's a pretty good first attempt, and well done for using text here to itemise the components of the string rather than characters. But, it would, of course, end up chopping off a lot more than just the file extension if the file name had more than one "." in it, which isn't uncommon.
One way to safely castrate the end of the file name is to use text item delimiters:
set filename to the name of afile
set fname to the filename
set my text item delimiters to "."
if "." is in the filename then set fname to text items 1 thru -2 of the filename as text
You should then be mindful or resetting the text item delimiters afterwards or there'll be consequences later on when you try and concatenate strings together.
Another way of chopping of the extension without utilising text item delimiters is string scanning, which is where you iterate through the characters of a string performing operations or tests as you go, and achieving the desired outcome. It's speedier than it sounds and a powerful technique for very complex string searching and manipulations:
set filename to the name of afile
set fname to the filename
repeat while the last character of fname ≠ "."
set fname to text 1 thru -2 of fname
end
set fname to text 1 thru -2 of fname
You could also retrieve the name extension property of the file, get its length, and remove (1 + that) many characters from the end of the file's name. There a myriad ways to achieve the same outcome.
This is wrong in this particular instance:
set myKeywords to ((characters var1 thru var2 of myvar) as string)
characters produces a list, which you then have to concatenate back into a string, and this is unsafe if you aren't sure what the text item delimiters are set to. As you haven't made a reference to it in your script, it should be set to an empty string, which would result in the joining of the characters back into words produce the expected result. However, this could easily not be the case, if, say, you performed the first technique of file extension castration and neglected to set the text item limiters back—then the resulting string would have a period between every single letter.
As a policy in AppleScript (which you can personally choose to adhere to or ignore), it's considered by some as poor form if you perform list to string coercion operations without first setting the text item delimiters to a definitive value.
But you needn't do so here, because rather than using characters, use text:
set myKeywords to text var1 thru var2 of myvar
You're performing a shell command that looks like this: mdls -name kMDItemKeywords <file>, and then the two lines of AppleScript that follow awkwardly try and trim off the leading and trailing parentheses around the text representation of a bash array. Instead, you can turn on the -raw flag for mdls, which simplifies the output by stripping off the name of the key for you. This then places the parentheses as the very first and very last characters; however, since there's a load of dead whitespace in the output as well, you might as well get bash to perform all the clean up for you:
mdls -raw -name kMDItemContentTypeTree <file> | grep -E -io '[^()",[:blank:]]+'
This disregards parentheses, double quotes, commas, and whitespace, so all you get back is a list of keywords, one per line, and without any extra baggage. If you needed to itemise them, you can set a variable to the paragraphs of the output from the do shell script command, which splits the text into lines placing each keyword into a list. But it seems here that you need text and don't mind it being multilinear.
When I started to write this answer, I didn't have an inkling as to what was causing the specific issue that brought you here. Having gone through the details of how mdls formats its output, I now see the issue is with the fact that the myKeywords string will contain a bunch of double quotes, and you've surrounded the placement of the myKeywords entity in your JavaScript expression with double quotes. All of these quotes are only being escaped equally and once only in the AppleScript environment but not in the JavaScript environment, which results in each neighbouring double quote acting as an open-close pair. I ran a similar command in bash to obtain an array of values (kMDContentTreeType), and then processed the text in the way AppleScript does, before opening the JavaScript console in my browser and pasting it to illustrate what's going on:
Anything in red is contained inside a string; everything else is therefore taken as a JavaScript identifier or object (or it would be if the messed up quotes didn't also mess up the syntax, and then result in an unterminated string that's still expecting one last quote to pair with.
I think the solution is to use a continuation character "\" for backward compatibility with older browsers: so you would need to have each line (except the last one) appended with a backslash, and you need to change the pair of double quotes surrounding the myKeywords value in your JavaScript expression to a pair of single quotes. In newer browsers, you can forgo the headache of appending continuation marks to each line and instead replace the pair of outside double quotes with a pair of backticks (`) instead:
❌'This line throws
an EOF error in
JavaScript';
✅'This line is \
processed successfully \
in JavaScript';
✅`This line is also
processed successfully
in JavaScript`;
I had tried the backticks ( ` ) suggested by CJK but that did not work for me. The main issue being raised was that the kMDItemKeywords returned escaped characters.
Heart,
Studio,
Red,
\"RF126-10.tif\",
Tree,
\"Heart Tree\",
occasion,
Farm,
birds,
\"Red Farm Studio\",
\"all occasion\",
all
I was able to get rid of the escaped characters using the following:
NEW CODE
set myKeywords to do shell script "echo " & quoted form of myKeywords & " | tr -d '[:cntrl:]'| tr '[:upper:]' '[:lower:]' | tr -d '\"'"
UPDATED CODE FOR JAVASCRIPT
--Inputs the Keywords from the Image Metadata--
tell application "Safari"
activate
do JavaScript "document.getElementById('ctl00_cphMainContent_txtKeyWords').value = '" & myKeywords & "';" in current tab of window 1
end tell
RESULT
--> " heart, studio, red, rf126-10.tif, tree, heart tree, occasion, farm, birds, red farm studio, all occasion, all"

String.replace strange behaviour in IE11

I have encountered a strange issue with IE11. Consider the following (part of the riot.js framework):
var s = "{JSON.stringify(\\{ amount: Math.floor(reward.amount) \\})}";
var s1 = s.replace(/\\{/g, '\uFFF0');
When running this code on localhost, it runs fine. But when running from our staging environment, the \{ fragment is replaced not by \uFFF0 (codepoint 65520) but by \uFFFD (codepoint 65533). That means it fails later when trying to replace the special character back to {.
The replace method is the browser's native one. The file that contain both the HTML (string is a DOM attribute) and the javascript is returned by the server with charset=utf-8 header and encoded as such. In staging environment, it is bundled with other files (not compression or mangling though) and still encoded in utf-8.
I have no idea why it does that, or why it's not systematic.
\uFFFD, or as visualized: �, is the browsers's way of showing you the character in the string is an invalid character.
The string itself will still contain \uFFF0, but since that character is not defined, the browser renders �, instead.
For me, in the console,
Google Chrome shows: ￰ (White box, black border, with a question mark).
Safari shows: ￰ (White box, black border, with a question mark).
Internet explorer shows: ￰ (White box, black border).
Edge shows: ￰ (White box, black border).
Firefox shows: nothing.
They're all the same string. Just a different visual representation, depending on the browser.
var s = "{JSON.stringify(\\{ amount: Math.floor(reward.amount) \\})}",
s1 = s.replace(/\\{/g, '\uFFF0'),
charCode = s1.charCodeAt(16);
document.write(charCode + ' ' + String.fromCharCode(charCode));
document.write('|');
document.write('￰'.charCodeAt(0));
document.write('|');
document.write('x'.replace('x', '\uFFF0').charCodeAt(0));
(For me, in Chrome, this snippet shows me: 65520 ￰|65520|65520)
I tracked the issue down to UglifyJs. By default, it replaces escaped unicode chars with the actual character. So:
var s1 = s.replace(/\\{/g, '\uFFF0');
Becomes this in the bundled file:
var s1 = s.replace(/\\{/g, '￰');
Which is not visible when debugging with source map, and does not behave well in IE11.
Solution is to add ASCIIOnly: true option to Uglify.
NB: Uglify doc refers to this option as either ascii-only or ascii_only, but the only variant really taken into consideration is ASCIIOnly.

whay backaward slash in the parameter element of the javascript object?

I was inspecting this site in firebug. Inside the third <script/> tag in the head section of the page , I found an object variable declared in the following way ( truncated here however by me) :
var EM={
"ajaxurl":"http:\/\/ipsos.com.au\/wp-admin\/admin-ajax.php",
"bookingajaxurl":"http:\/\/ipsos.com.au\/wp-admin\/admin-ajax.php",
"locationajaxurl":"http:\/\/ipsos.com.au\/wp-admin\/admin-ajax.php?action=locations_search",
"firstDay":"1","locale":"en"};
The utility of the variable is unknown to me. What struck me is the 3 urls presented there. Why are the backward slashes present there? Couldn't it be something like :
"ajaxurl" : "http://ipsos.com.au/wp-admin/admin-ajax.php"
?
In a script element there are various character sequences (depending on the version of HTML) that will terminate the element. </script> will always do this.
<\/script> will not.
Escaping / characters will not change the meaning of the JS, but will prevent any such HTML from ending the script.
The \/\/ is to avoid the below scenario:
when the url looks something similar to "ajaxurl" : "http://google.com/search?q=</script>"
Try copy paste the url in browsers address bar. This is handled correctly. Otherwise, You might end up getting script errors and page might not work as you've expected.
imagine DOM manipulators replacing the value as it is in the src attribute of the script tag and then the javascript engine reporting multiple errors because that particular script referenced might not get loaded due to incorrectly defined src value
Hope this helps.
Life would be hectic without these lil things
It is used to escape the characters..
The backslash () can be used to insert apostrophes, new lines, quotes, and other special characters into a string.
var str = " Hello "World" !! ";
alert(str)
This won't work..
You have to escape them first
var str = " Hello \"World\" !! ";
alert(str) ; \\ This works
In terms of Javascript / and <\/ are identical inside a string. As far as HTML is concerned </ starts an end tag but <\/ does not.

IE8 window.open name - doesn't like JavaScript encoding?

I'm calling window.open() like this:
window.open('blank.html', 'New_Window\x3a_Jamie', 'width=800,height=800');
What I've done in the code is taken the window's name and JavaScript encoded it using the Microsoft Web Protection library. I'm also replacing spaces with underscores because I read that IE doesn't like spaces in window names. FYI the original string was "New Window: Jamie" and it looks like the ":" gets encoded as "\x3a". The window opens in FireFox just fine, but the window does not open in IE8. Does IE8 just not like this encoding, or the character or what? Are there rules around what characters can appear in the window name for IE8?
Are there rules around what characters can appear in the window name for IE8?
Yes. Although it doesn't seem to be documented, IE has always required that a window name be composed of alphanumerics and underscore. A colon won't be accepted, whether read from an encoded string literal or not.
If you really needed to map an arbitrary string to a unique name-safe version you'd have to do something like encoding every non-alphanumeric character into an escape sequence, eg:
function encodeToName(s) {
return s.replace(/[^A-Za-z0-9]/g, function(match) {
var c= match[0].charCodeAt(0).toString(16);
return '_'+(new Array(5-c.length).join('0'))+c;
});
}
alert(encodeToName('New Window: Jamie'));
// 'New_0020Window_003A_0020Jamie'
I agree with casablanca though, it seems very unlikely you should actually need to do this. The user is never going to get to see the window name, so w1 is just as good. It's rare enough that you need window names at all.
I think it wants the window name to be something that'd work as an identifier. Thus, "New_Window_Jamie" would probably be OK.
Do you really need a window name? From the docs:
Such string can be used to be the target of links and forms when the target attribute of an <a> element or of a <form> is specified. This string parameter should not contain any blank space.
That's about the only use of specifying a name, and though I don't see any restrictions apart from "no spaces", it would be safe to just stick to letters, digits and underscores.

Proper encoding of href for window.open() in JavaScript

What is the proper cross-browser encoding for the href when using window.open() in JavaScript? First I was using
var href = "http://127.0.0.1:8000/etf/admin/escola/t34atividade/?pop=1&copy=1";
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
IE8 opens: http://127.0.0.1:8000/etf/admin/escola/t34atividade/?pop=1©=1
FireFox opens: http://127.0.0.1:8000/etf/admin/escola/t34atividade/?pop=1&copy=1
var href = "http://127.0.0.1:8000/etf/admin/escola/t34atividade/?pop=1&copy=1";
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
IE8 opens: http://127.0.0.1:8000/etf/admin/escola/t34atividade/?pop=1&copy=1
FireFox opens: http://127.0.0.1:8000/etf/admin/escola/t34atividade/?pop=1&copy=1
Use the Javascript "encodeURIComponent" function for each piece of the URI that's not part of the URI syntax (that is, separator slashes, the question mark for the query string, parameter separator ampersands, etc).
URI encoding is not the same as HTML escaping. For example, you don't escape an ampersand in a URL as &.
IE8 appears to be trying to coerce the query string argument &copy=1 to the entity ©, which is the copyright symbol (©). That is actually funny. Just like Microsoft to encumber the user with "help".
Pointy is correct about encoding. Be careful also that you don't have a code minifier that removes everything on a line after a pair of double slashes (//); I've seen those wreck pages before.
The simplest solution I found was to stop using copy as a GET parameter. The problem is that &copy is actually an HTML entity for the copyright symbol. IE applies an entity replacement converting it to the symbol even though it's in JavaScript code. Apparently Firefox does not perform the entity replacement. According to a comment in this blog what IE is doing might be correct, but to avoid all the mess I just renamed my parameter to clone.
http://nedbatchelder.com/blog/200812/accidental_html_entities_in_urls.html

Categories

Resources