// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // JavaScript Dynamic Content shim for Windows Store apps (function () { if (window.MSApp && MSApp.execUnsafeLocalFunction) { // Some nodes will have an "attributes" property which shadows the Node.prototype.attributes property // and means we don't actually see the attributes of the Node (interestingly the VS debug console // appears to suffer from the same issue). // var Element_setAttribute = Object.getOwnPropertyDescriptor(Element.prototype, "setAttribute").value; var Element_removeAttribute = Object.getOwnPropertyDescriptor(Element.prototype, "removeAttribute").value; var HTMLElement_insertAdjacentHTMLPropertyDescriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "insertAdjacentHTML"); var Node_get_attributes = Object.getOwnPropertyDescriptor(Node.prototype, "attributes").get; var Node_get_childNodes = Object.getOwnPropertyDescriptor(Node.prototype, "childNodes").get; var detectionDiv = document.createElement("div"); function getAttributes(element) { return Node_get_attributes.call(element); } function setAttribute(element, attribute, value) { try { Element_setAttribute.call(element, attribute, value); } catch (e) { // ignore } } function removeAttribute(element, attribute) { Element_removeAttribute.call(element, attribute); } function childNodes(element) { return Node_get_childNodes.call(element); } function empty(element) { while (element.childNodes.length) { element.removeChild(element.lastChild); } } function insertAdjacentHTML(element, position, html) { HTMLElement_insertAdjacentHTMLPropertyDescriptor.value.call(element, position, html); } function inUnsafeMode() { var isUnsafe = true; try { detectionDiv.innerHTML = ""; } catch (ex) { isUnsafe = false; } return isUnsafe; } function cleanse(html, targetElement) { var cleaner = document.implementation.createHTMLDocument("cleaner"); empty(cleaner.documentElement); MSApp.execUnsafeLocalFunction(function () { insertAdjacentHTML(cleaner.documentElement, "afterbegin", html); }); var scripts = cleaner.documentElement.querySelectorAll("script"); Array.prototype.forEach.call(scripts, function (script) { switch (script.type.toLowerCase()) { case "": script.type = "text/inert"; break; case "text/javascript": case "text/ecmascript": case "text/x-javascript": case "text/jscript": case "text/livescript": case "text/javascript1.1": case "text/javascript1.2": case "text/javascript1.3": script.type = "text/inert-" + script.type.slice("text/".length); break; case "application/javascript": case "application/ecmascript": case "application/x-javascript": script.type = "application/inert-" + script.type.slice("application/".length); break; default: break; } }); function cleanseAttributes(element) { var attributes = getAttributes(element); if (attributes && attributes.length) { // because the attributes collection is live it is simpler to queue up the renames var events; for (var i = 0, len = attributes.length; i < len; i++) { var attribute = attributes[i]; var name = attribute.name; if ((name[0] === "o" || name[0] === "O") && (name[1] === "n" || name[1] === "N")) { events = events || []; events.push({ name: attribute.name, value: attribute.value }); } } if (events) { for (var i = 0, len = events.length; i < len; i++) { var attribute = events[i]; removeAttribute(element, attribute.name); setAttribute(element, "x-" + attribute.name, attribute.value); } } } var children = childNodes(element); for (var i = 0, len = children.length; i < len; i++) { cleanseAttributes(children[i]); } } cleanseAttributes(cleaner.documentElement); var cleanedNodes = []; if (targetElement.tagName === 'HTML') { cleanedNodes = Array.prototype.slice.call(document.adoptNode(cleaner.documentElement).childNodes); } else { if (cleaner.head) { cleanedNodes = cleanedNodes.concat(Array.prototype.slice.call(document.adoptNode(cleaner.head).childNodes)); } if (cleaner.body) { cleanedNodes = cleanedNodes.concat(Array.prototype.slice.call(document.adoptNode(cleaner.body).childNodes)); } } return cleanedNodes; } function cleansePropertySetter(property, setter) { var propertyDescriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, property); var originalSetter = propertyDescriptor.set; Object.defineProperty(HTMLElement.prototype, property, { get: propertyDescriptor.get, set: function (value) { if(window.WinJS && window.WinJS._execUnsafe && inUnsafeMode()) { originalSetter.call(this, value); } else { var that = this; var nodes = cleanse(value, that); MSApp.execUnsafeLocalFunction(function () { setter(propertyDescriptor, that, nodes); }); } }, enumerable: propertyDescriptor.enumerable, configurable: propertyDescriptor.configurable, }); } cleansePropertySetter("innerHTML", function (propertyDescriptor, target, elements) { empty(target); for (var i = 0, len = elements.length; i < len; i++) { target.appendChild(elements[i]); } }); cleansePropertySetter("outerHTML", function (propertyDescriptor, target, elements) { for (var i = 0, len = elements.length; i < len; i++) { target.insertAdjacentElement("afterend", elements[i]); } target.parentNode.removeChild(target); }); } }());