Skip to content

Instantly share code, notes, and snippets.

@cmawhorter
Last active November 12, 2015 05:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cmawhorter/b688401e00220c4a1af2 to your computer and use it in GitHub Desktop.
Save cmawhorter/b688401e00220c4a1af2 to your computer and use it in GitHub Desktop.
Older versions of GWT used document.createElement incorrectly which leads to an Invalid Character error
// If you're supporting an app built with GWT and running into Invalid Character errors
// in older version of IE, this patch should fix it with no GWT code changes.
//
// Just include thes script before before including your gwt <script src="app.nocache.js">
//
// Yay. GWT magically working again.
// NOTE: Does a console.warn "Init document.createElement interception..." prior to running.
// NOTE: If the patch ever encounters an error it will fall back to the native browser
// document.createElement method as a last attempt to work. If this happens, it will
// output the error to console.warn.
// UPDATE: added better support for old ies after running some IE8/9/10 vms
(function() {
// we only care about IE
// TODO: don't run for IE11
if (navigator.appName != 'Microsoft Internet Explorer') {
return;
}
// FIXME: doesn't currently support innerHTML. do we need to? e.g. document.createElement('<div><b>some content</b><div/>')
var parentWindow = parent ? parent.window : window;
var LOGGING_ENABLED = (parentWindow.location.hash || '').indexOf('debug-createelement') > -1;
var reParsedElement = /<\s*(.+?)\b([^>]+?)?>/
, reParsedElementAttrs = /\b([a-zA-Z_:][-a-zA-Z0-9_:.]*?)\s*(=\s*(["'])([^\3]*?)\3)?\s*\b/g // FIXME: requires dummy attr to be appended to handle certain situations
, reTrim = /^\s+|\s+$/g
, reTrimQuotes = /^['"]|['"]$/g
, _dummyAttribute = '____dummy'
, _originalDocumentCreateElement = document.createElement
, _callOriginalMethod
, _patchedMethod
, _buildElement
, _parseAttributes
, _parseAttribute
, _setAttribute
, _trim
, _log;
// reParsedElementAttrs attr names http://razzed.com/2009/01/30/valid-characters-in-attribute-names-in-htmlxml/
_trim = function(str) {
if (str && str.trim) {
return str.trim();
}
else {
return (str || '').replace(reTrim, '');
}
};
_log = function(message, o1, o2, o3) {
if (LOGGING_ENABLED && window.console && console.log) {
switch (arguments.length) {
case 1:
console.log(message);
break;
case 2:
console.log(message, ' ', o1);
break;
case 3:
console.log(message, ' ', o1, ' ', o2);
break;
default:
case 4:
console.log(message, ' ', o1, ' ', o2, ' ', o3);
break;
}
}
};
_callOriginalMethod = function() {
return _originalDocumentCreateElement.apply(document, arguments);
};
_setAttribute = function(el, attrName, attrValue) {
if (el.setAttribute) {
el.setAttribute(attrName, attrValue);
}
else {
el[attrName] = attrValue;
}
};
_parseAttribute = function(attrString) {
var parsedAttr = (attrString || '').split('=')
, attr;
attr = {
name: _trim(parsedAttr[0] || ''),
value: void 0 === parsedAttr[1] ? null : _trim(parsedAttr[1] || '').replace(reTrimQuotes, ''),
};
_log('_parseAttribute parsed', parsedAttr, attr.name, attr.value);
return attr;
};
_parseAttributes = function(elementAttrs) {
elementAttrs = (elementAttrs || '') + ' ' + _dummyAttribute;
var parsedElementAttrs = elementAttrs.match(reParsedElementAttrs);
_log('_parseAttributes parsed', parsedElementAttrs);
if (parsedElementAttrs && parsedElementAttrs.length) {
var attrs = [];
for (var i=0; i < parsedElementAttrs.length; i++) {
var parsedAttr = _parseAttribute(parsedElementAttrs[i]);
attrs.push(parsedAttr);
}
return attrs;
}
else {
return [];
}
};
_buildElement = function(elementString) {
var parsedElement = (elementString || '').match(reParsedElement);
_log('_buildElement parsed', parsedElement);
if (parsedElement && parsedElement.length && parsedElement[1]) {
var elementName = parsedElement[1].toUpperCase()
, elementAttrs = _parseAttributes(parsedElement[2] || '')
, element;
_log('Patched building element', elementName);
element = _callOriginalMethod(elementName);
for (var i=0; i < elementAttrs.length; i++) {
var attr = elementAttrs[i];
_log('\t-> attr ', attr.name, attr.value);
if (_dummyAttribute !== attr.name) {
_setAttribute(element, attr.name, attr.value);
}
else {
_log('Skipping dummy attr');
}
}
return element;
}
else {
return null;
}
};
_patchedMethod = function(elementString) {
var element;
elementString = _trim(elementString || '');
_log('_patchedMethod', elementString, elementString[0], elementString[0] == '<');
if (elementString[0] && elementString[0] == '<') {
// ut-oh
element = _buildElement(elementString);
}
if (!element) {
_log('Falling back to native element', elementString);
element = _callOriginalMethod(elementString);
}
// passed arg looks alright enough or there was some error.
// just pass to native method as-is
_log('Element being returned', element);
return element;
};
window.console && console.warn('Init document.createElement interception... Logging is', LOGGING_ENABLED ? 'ON' : 'OFF');
document.createElement = function giveGWTAHelpingHandBecauseItNeedsIt(elementString) {
_log('Intercepting document.createElement', elementString, typeof elementString);
try {
return _patchedMethod(elementString);
}
catch(err) {
window.console && console.warn('Patched document.createElement failed. Fallback called.', err);
return _callOriginalMethod(elementString);
}
};
})();
@cmawhorter
Copy link
Author

I should add: This gist is public domain and MIT if public domain is not supported by your locale.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment