document.write("<IFRAME SRC=\"http://ad.doubleclick.net/adi/N6410.1081839.AUDIENCETRADING.CO/B6572042.3;sz=728x90;ord=1149959229525018990;click=http://pixel.mathtag.com/click/img?mt_aid=1149959229525018990&mt_id=295713&mt_adid=108042&mt_uuid=ecf35134-0123-4900-bc00-f03d84e21554&mt_3pck=http%3A//track.pubmatic.com/AdServer/AdDisplayTrackerServlet%3FclickData%3DJnB1YklkPTMzMzA1JnNpdGVJZD0zMzgyNSZhZElkPTM5OTk0JmthZHNpemVpZD03JmNhbXBhaWduSWQ9MTIxNiZjcmVhdGl2ZUlkPTAmYWRTZXJ2ZXJJZD0yNDM%3D_url%3D&redirect=\" WIDTH=728 HEIGHT=90 MARGINWIDTH=0 MARGINHEIGHT=0 HSPACE=0 VSPACE=0 FRAMEBORDER=0 SCROLLING=no BORDERCOLOR='#000000'><SCRIPT language='JavaScript1.1' SRC=\"http://ad.doubleclick.net/adj/N6410.1081839.AUDIENCETRADING.CO/B6572042.3;sz=728x90;ord=1149959229525018990;click=http://pixel.mathtag.com/click/img?mt_aid=1149959229525018990&mt_id=295713&mt_adid=108042&mt_uuid=ecf35134-0123-4900-bc00-f03d84e21554&mt_3pck=http%3A//track.pubmatic.com/AdServer/AdDisplayTrackerServlet%3FclickData%3DJnB1YklkPTMzMzA1JnNpdGVJZD0zMzgyNSZhZElkPTM5OTk0JmthZHNpemVpZD03JmNhbXBhaWduSWQ9MTIxNiZjcmVhdGl2ZUlkPTAmYWRTZXJ2ZXJJZD0yNDM%3D_url%3D&redirect=\"><\/SCRIPT><NOSCRIPT><A HREF=\"http://pixel.mathtag.com/click/img?mt_aid=1149959229525018990&mt_id=295713&mt_adid=108042&mt_uuid=ecf35134-0123-4900-bc00-f03d84e21554&mt_3pck=http%3A//track.pubmatic.com/AdServer/AdDisplayTrackerServlet%3FclickData%3DJnB1YklkPTMzMzA1JnNpdGVJZD0zMzgyNSZhZElkPTM5OTk0JmthZHNpemVpZD03JmNhbXBhaWduSWQ9MTIxNiZjcmVhdGl2ZUlkPTAmYWRTZXJ2ZXJJZD0yNDM%3D_url%3D&redirect=http://ad.doubleclick.net/jump/N6410.1081839.AUDIENCETRADING.CO/B6572042.3;sz=728x90;ord=1149959229525018990?\"><IMG SRC=\"http://ad.doubleclick.net/ad/N6410.1081839.AUDIENCETRADING.CO/B6572042.3;sz=728x90;ord=1149959229525018990?\" BORDER=0 WIDTH=728 HEIGHT=90 ALT=\"Advertisement\"><\/A><\/NOSCRIPT><\/IFRAME>");
<!DOCTYPE html>
<html lang="en-us"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Evil Ads</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://media.krxd.net/derek/postscribe.js"></script>
<body>
<div id="ad"></div>
<script type="text/javascript">
$(function() {
postscribe('#ad', '<scr' + 'ipt src="js"></scr' + 'ipt>', {
done: function() {
console.log('postscribe done');
},
error: function(e) {
console.log('postscribe error', e);
}
});
});
</script>
</body></html>
// An html parser written in JavaScript
// Based on http://ejohn.org/blog/pure-javascript-html-parser/
(function() {
var supports = (function() {
var supports = {};
var html;
var work = this.document.createElement('div');
html = "<P><I></P></I>";
work.innerHTML = html;
supports.tagSoup = work.innerHTML !== html;
work.innerHTML = "<P><i><P></P></i></P>";
supports.selfClose = work.childNodes.length === 2;
return supports;
})();
// Regular Expressions for parsing tags and attributes
var startTag = /^<([\-A-Za-z0-9_]+)((?:\s+[\w\-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;
var endTag = /^<\/([\-A-Za-z0-9_]+)[^>]*>/;
var attr = /([\-A-Za-z0-9_]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
var fillAttr = /^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noresize|noshade|nowrap|readonly|selected)$/i;
var DEBUG = false;
function htmlParser(stream, options) {
stream = stream || '';
// Options
options = options || {};
for(var key in supports) {
if(supports.hasOwnProperty(key)) {
if(options.autoFix) {
options['fix_'+key] = true;//!supports[key];
}
options.fix = options.fix || options['fix_'+key];
}
}
var stack = [];
var append = function(str) {
stream += str;
};
var prepend = function(str) {
stream = str + stream;
};
// Order of detection matters: detection of one can only
// succeed if detection of previous didn't
var detect = {
comment: /^<!--/,
endTag: /^<\//,
atomicTag: /^<\s*(script|style|noscript)[\s>]/i,
startTag: /^</,
chars: /^[^<]/
};
// Detection has already happened when a reader is called.
var reader = {
comment: function() {
var index = stream.indexOf("-->");
if ( index >= 0 ) {
return {
content: stream.substr(4, index),
length: index + 3
};
}
},
endTag: function() {
var match = stream.match( endTag );
if ( match ) {
return {
tagName: match[1],
length: match[0].length
};
}
},
atomicTag: function() {
var start = reader.startTag();
if(start) {
var rest = stream.slice(start.length);
// for optimization, we check first just for the end tag
if(rest.match(new RegExp("<\/\\s*" + start.tagName + "\\s*>", "i"))) {
// capturing the content is inefficient, so we do it inside the if
var match = rest.match(new RegExp("([\\s\\S]*?)<\/\\s*" + start.tagName + "\\s*>", "i"));
if(match) {
// good to go
return {
tagName: start.tagName,
attrs: start.attrs,
content: match[1],
length: match[0].length + start.length
};
}
}
}
},
startTag: function() {
var match = stream.match( startTag );
if ( match ) {
var attrs = {};
match[2].replace(attr, function(match, name) {
var value = arguments[2] || arguments[3] || arguments[4] ||
fillAttr.test(name) && name || null;
attrs[name] = value;
});
return {
tagName: match[1],
attrs: attrs,
unary: !!match[3],
length: match[0].length
};
}
},
chars: function() {
var index = stream.indexOf("<");
return {
length: index >= 0 ? index : stream.length
};
}
};
var readToken = function() {
// Enumerate detects in order
for (var type in detect) {
if(detect[type].test(stream)) {
if(DEBUG) { console.log('suspected ' + type); }
var token = reader[type]();
if(token) {
if(DEBUG) { console.log('parsed ' + type, token); }
// Type
token.type = token.type || type;
// Entire text
token.text = stream.substr(0, token.length);
// Update the stream
stream = stream.slice(token.length);
return token;
}
return null;
}
}
};
var readTokens = function(handlers) {
var tok;
while(tok = readToken()) {
// continue until we get an explicit "false" return
if(handlers[tok.type] && handlers[tok.type](tok) === false) {
return;
}
}
};
var clear = function() {
var rest = stream;
stream = '';
return rest;
};
var rest = function() {
return stream;
};
if(options.fix) {
(function() {
// Empty Elements - HTML 4.01
var EMPTY = /^(AREA|BASE|BASEFONT|BR|COL|FRAME|HR|IMG|INPUT|ISINDEX|LINK|META|PARAM|EMBED)$/i;
// Elements that you can| intentionally| leave open
// (and which close themselves)
var CLOSESELF = /^(COLGROUP|DD|DT|LI|OPTIONS|P|TD|TFOOT|TH|THEAD|TR)$/i;
var stack = [];
stack.last = function() {
return this[this.length - 1];
};
stack.lastTagNameEq = function(tagName) {
var last = this.last();
return last && last.tagName &&
last.tagName.toUpperCase() === tagName.toUpperCase();
};
stack.containsTagName = function(tagName) {
for(var i = 0, tok; tok = this[i]; i++) {
if(tok.tagName === tagName) {
return true;
}
}
return false;
};
var correct = function(tok) {
if(tok && tok.type === 'startTag') {
// unary
tok.unary = EMPTY.test(tok.tagName) || tok.unary;
}
return tok;
};
var readTokenImpl = readToken;
var peekToken = function() {
var tmp = stream;
var tok = correct(readTokenImpl());
stream = tmp;
return tok;
};
var closeLast = function() {
var tok = stack.pop();
// prepend close tag to stream.
prepend('</'+tok.tagName+'>');
};
var handlers = {
startTag: function(tok) {
var tagName = tok.tagName;
// Fix tbody
if(tagName.toUpperCase() === 'TR' && stack.lastTagNameEq('TABLE')) {
prepend('<TBODY>');
prepareNextToken();
} else if(options.fix_selfClose &&
CLOSESELF.test(tagName) &&
stack.containsTagName(tagName)) {
if(stack.lastTagNameEq(tagName)) {
closeLast();
} else {
prepend('</'+tok.tagName+'>');
prepareNextToken();
}
} else if (!tok.unary) {
stack.push(tok);
}
},
endTag: function(tok) {
var last = stack.last();
if(last) {
if(options.fix_tagSoup && !stack.lastTagNameEq(tok.tagName)) {
// cleanup tag soup
closeLast();
} else {
stack.pop();
}
} else if (options.fix_tagSoup) {
// cleanup tag soup part 2: skip this token
skipToken();
}
}
};
var skipToken = function() {
// shift the next token
readTokenImpl();
prepareNextToken();
};
var prepareNextToken = function() {
var tok = peekToken();
if(tok && handlers[tok.type]) {
handlers[tok.type](tok);
}
};
// redefine readToken
readToken = function() {
prepareNextToken();
return correct(readTokenImpl());
};
})();
}
return {
append: append,
readToken: readToken,
readTokens: readTokens,
clear: clear,
rest: rest,
stack: stack
};
}
htmlParser.supports = supports;
htmlParser.tokenToString = function(tok) {
var handler = {
comment: function(tok) {
return '<--' + tok.content + '-->';
},
endTag: function(tok) {
return '</'+tok.tagName+'>';
},
atomicTag: function(tok) {
console.log(tok);
return handler.startTag(tok) +
tok.content +
handler.endTag(tok);
},
startTag: function(tok) {
var str = '<'+tok.tagName;
for (var key in tok.attrs) {
var val = tok.attrs[key];
// escape quotes
str += ' '+key+'="'+(val ? val.replace(/(^|[^\\])"/g, '$1\\\"') : '')+'"';
}
return str + (tok.unary ? '/>' : '>');
},
chars: function(tok) {
return tok.text;
}
};
return handler[tok.type](tok);
};
htmlParser.escapeAttributes = function(attrs) {
var escapedAttrs = {};
// escape double-quotes for writing html as a string
for(var name in attrs) {
var value = attrs[name];
escapedAttrs[name] = value && value.replace(/(^|[^\\])"/g, '$1\\\"');
}
return escapedAttrs;
};
for(var key in supports) {
htmlParser.browserHasFlaw = htmlParser.browserHasFlaw || (!supports[key]) && key;
}
this.htmlParser = htmlParser;
})();
// postscribe.js 1.1.1
// (c) Copyright 2012 to the present, Krux
// postscribe is freely distributable under the MIT license.
// For all details and documentation:
// http://krux.github.com/postscribe
(function() {
var global = this;
if(global.postscribe) {
return;
}
// Debug write tasks.
var DEBUG = true;
// Turn on to debug how each chunk affected the DOM.
var DEBUG_CHUNK = false;
// # Helper Functions
var slice = Array.prototype.slice;
// A function that intentionally does nothing.
function doNothing() {}
// Is this a function?
function isFunction(x) {
return "function" === typeof x;
}
// Loop over each item in an array-like value.
function each(arr, fn, _this) {
var i, len = (arr && arr.length) || 0;
for(i = 0; i < len; i++) {
fn.call(_this, arr[i], i);
}
}
// Loop over each key/value pair in a hash.
function eachKey(obj, fn, _this) {
var key;
for(key in obj) {
if(obj.hasOwnProperty(key)) {
fn.call(_this, key, obj[key]);
}
}
}
// Set properties on an object.
function set(obj, props) {
eachKey(props, function(key, value) {
obj[key] = value;
});
return obj;
}
// Set default options where some option was not specified.
function defaults(options, _defaults) {
options = options || {};
eachKey(_defaults, function(key, val) {
if(options[key] == null) {
options[key] = val;
}
});
return options;
}
// Convert value (e.g., a NodeList) to an array.
function toArray(obj) {
try {
return slice.call(obj);
} catch(e) {
var ret = [];
each(obj, function(val) {
ret.push(val);
});
return ret;
}
}
// Test if token is a script tag.
function isScript(tok) {
return (/^script$/i).test(tok.tagName);
}
// # Class WriteStream
// Stream static html to an element, where "static html" denotes "html without scripts".
// This class maintains a *history of writes devoid of any attributes* or "proxy history".
// Injecting the proxy history into a temporary div has no side-effects,
// other than to create proxy elements for previously written elements.
// Given the `staticHtml` of a new write, a `tempDiv`'s innerHTML is set to `proxy_history + staticHtml`.
// The *structure* of `tempDiv`'s contents, (i.e., the placement of new nodes beside or inside of proxy elements),
// reflects the DOM structure that would have resulted if all writes had been squashed into a single write.
// For each descendent `node` of `tempDiv` whose parentNode is a *proxy*, `node` is appended to the corresponding *real* element within the DOM.
// Proxy elements are mapped to *actual* elements in the DOM by injecting a data-id attribute into each start tag in `staticHtml`.
var WriteStream = (function(){
// Prefix for data attributes on DOM elements.
var BASEATTR = 'data-ps-';
// get / set data attributes
function data(el, name, value) {
var attr = BASEATTR + name;
if(arguments.length === 2) {
// Get
var val = el.getAttribute(attr);
// IE 8 returns a number if it's a number
return val == null ? val : String(val);
} else if( value != null && value !== '') {
// Set
el.setAttribute(attr, value);
} else {
// Remove
el.removeAttribute(attr);
}
}
function WriteStream(root, options) {
var doc = root.ownerDocument;
set(this, {
root: root,
options: options,
win: doc.defaultView || doc.parentWindow,
doc: doc,
parser: global.htmlParser('', { autoFix: true }),
// Actual elements by id.
actuals: [root],
// Embodies the "structure" of what's been written so far, devoid of attributes.
proxyHistory: '',
// Create a proxy of the root element.
proxyRoot: doc.createElement(root.nodeName),
scriptStack: [],
writeQueue: []
});
data(this.proxyRoot, 'proxyof', 0);
}
WriteStream.prototype.write = function() {
[].push.apply(this.writeQueue, arguments);
// Process writes
// When new script gets pushed or pending this will stop
// because new writeQueue gets pushed
var arg;
while(!this.deferredRemote &&
this.writeQueue.length) {
arg = this.writeQueue.shift();
if(isFunction(arg)) {
this.callFunction(arg);
} else {
this.writeImpl(arg);
}
}
};
WriteStream.prototype.callFunction = function(fn) {
var tok = { type: "function", value: fn.name || fn.toString() };
this.onScriptStart(tok);
fn.call(this.win, this.doc);
this.onScriptDone(tok);
};
WriteStream.prototype.writeImpl = function(html) {
this.parser.append(html);
var tok, tokens = [];
// stop if we see a script token
while((tok = this.parser.readToken()) && !isScript(tok)) {
tokens.push(tok);
}
this.writeStaticTokens(tokens);
if(tok) {
this.handleScriptToken(tok);
}
};
// ## Contiguous non-script tokens (a chunk)
WriteStream.prototype.writeStaticTokens = function(tokens) {
var chunk = this.buildChunk(tokens);
if(!chunk.actual) {
// e.g., no tokens, or a noscript that got ignored
return;
}
chunk.html = this.proxyHistory + chunk.actual;
this.proxyHistory += chunk.proxy;
this.proxyRoot.innerHTML = chunk.html;
if(DEBUG_CHUNK) {
chunk.proxyInnerHTML = this.proxyRoot.innerHTML;
}
this.walkChunk();
if(DEBUG_CHUNK) {
chunk.actualInnerHTML = this.root.innerHTML; //root
}
return chunk;
};
WriteStream.prototype.buildChunk = function (tokens) {
var nextId = this.actuals.length,
// The raw html of this chunk.
raw = [],
// The html to create the nodes in the tokens (with id's injected).
actual = [],
// Html that can later be used to proxy the nodes in the tokens.
proxy = [];
each(tokens, function(tok) {
raw.push(tok.text);
if(tok.attrs) { // tok.attrs <==> startTag or atomicTag or cursor
// Ignore noscript tags. They are atomic, so we don't have to worry about children.
if(!(/^noscript$/i).test(tok.tagName)) {
var id = nextId++;
// Actual: inject id attribute: replace '>' at end of start tag with id attribute + '>'
actual.push(
tok.text.replace(/(\/?>)/, ' '+BASEATTR+'id='+id+' $1')
);
// Don't proxy scripts: they have no bearing on DOM structure.
if(tok.attrs.id !== "ps-script") {
// Proxy: strip all attributes and inject proxyof attribute
proxy.push(
// ignore atomic tags (e.g., style): they have no "structural" effect
tok.type === 'atomicTag' ? '' :
'<'+tok.tagName+' '+BASEATTR+'proxyof='+id+(tok.unary ? '/>' : '>')
);
}
}
} else {
// Visit any other type of token
// Actual: append.
actual.push(tok.text);
// Proxy: append endTags. Ignore everything else.
proxy.push(tok.type === 'endTag' ? tok.text : '');
}
});
return {
tokens: tokens,
raw: raw.join(''),
actual: actual.join(''),
proxy: proxy.join('')
};
};
WriteStream.prototype.walkChunk = function() {
var node, stack = [this.proxyRoot];
// use shift/unshift so that children are walked in document order
while((node = stack.shift()) != null) {
var isElement = node.nodeType === 1;
var isProxy = isElement && data(node, 'proxyof');
// Ignore proxies
if(!isProxy) {
if(isElement) {
// New actual element: register it and remove the the id attr.
this.actuals[data(node, 'id')] = node;
data(node, 'id', null);
}
// Is node's parent a proxy?
var parentIsProxyOf = node.parentNode && data(node.parentNode, 'proxyof');
if(parentIsProxyOf) {
// Move node under actual parent.
this.actuals[parentIsProxyOf].appendChild(node);
}
}
// prepend childNodes to stack
stack.unshift.apply(stack, toArray(node.childNodes));
}
};
// ### Script tokens
WriteStream.prototype.handleScriptToken = function(tok) {
var remainder = this.parser.clear();
if(remainder) {
// Write remainder immediately behind this script.
this.writeQueue.unshift(remainder);
}
tok.src = tok.attrs.src || tok.attrs.SRC;
if(tok.src && this.scriptStack.length) {
// Defer this script until scriptStack is empty.
// Assumption 1: This script will not start executing until
// scriptStack is empty.
this.deferredRemote = tok;
} else {
this.onScriptStart(tok);
}
// Put the script node in the DOM.
var _this = this;
this.writeScriptToken(tok, function() {
_this.onScriptDone(tok);
});
};
WriteStream.prototype.onScriptStart = function(tok) {
tok.outerWrites = this.writeQueue;
this.writeQueue = [];
this.scriptStack.unshift(tok);
};
WriteStream.prototype.onScriptDone = function(tok) {
// Pop script and check nesting.
if(tok !== this.scriptStack[0]) {
this.options.error({ message: "Bad script nesting or script finished twice" });
return;
}
this.scriptStack.shift();
// Append outer writes to queue and process them.
this.write.apply(this, tok.outerWrites);
// Check for pending remote
// Assumption 2: if remote_script1 writes remote_script2 then
// the we notice remote_script1 finishes before remote_script2 starts.
// I think this is equivalent to assumption 1
if(!this.scriptStack.length && this.deferredRemote) {
this.onScriptStart(this.deferredRemote);
this.deferredRemote = null;
}
};
// Build a script and insert it into the DOM.
// Done is called once script has executed.
WriteStream.prototype.writeScriptToken = function(tok, done) {
var el = this.buildScript(tok);
if(tok.src) {
// Fix for attribute "SRC" (capitalized). IE does not recognize it.
el.src = tok.src;
this.scriptLoadHandler(el, done);
}
try {
this.insertScript(el);
if(!tok.src) {
done();
}
} catch(e) {
this.options.error(e);
done();
}
};
// Build a script element from an atomic script token.
WriteStream.prototype.buildScript = function(tok) {
var el = this.doc.createElement(tok.tagName);
// Set attributes
eachKey(tok.attrs, function(name, value) {
el.setAttribute(name, value);
});
// Set content
if(tok.content) {
el.text = tok.content;
}
return el;
};
// Insert script into DOM where it would naturally be written.
WriteStream.prototype.insertScript = function(el) {
// Append a span to the stream. That span will act as a cursor
// (i.e. insertion point) for the script.
this.writeImpl('<span id="ps-script"/>');
// Grab that span from the DOM.
var cursor = this.doc.getElementById("ps-script");
// Replace cursor with script.
cursor.parentNode.replaceChild(el, cursor);
};
WriteStream.prototype.scriptLoadHandler = function(el, done) {
function cleanup() {
el = el.onload = el.onreadystatechange = el.onerror = null;
done();
}
// Error handler
var error = this.options.error;
// Set handlers
set(el, {
onload: function() { cleanup(); },
onreadystatechange: function() {
if(/^(loaded|complete)$/.test( el.readyState )) {
cleanup();
}
},
onerror: function() {
error({ message: 'remote script failed ' + el.src });
cleanup();
}
});
};
return WriteStream;
}());
// Public-facing interface and queuing
var postscribe = (function() {
var nextId = 0;
var queue = [];
var active = null;
function nextStream() {
var args = queue.shift();
if(args) {
args.stream = runStream.apply(null, args);
}
}
function runStream(el, html, options) {
active = new WriteStream(el, options);
// Identify this stream.
active.id = nextId++;
active.name = options.name || active.id;
postscribe.streams[active.name] = active;
// Override document.write.
var doc = el.ownerDocument;
var stash = { write: doc.write, writeln: doc.writeln };
function write(str) {
str = options.beforeWrite(str);
active.write(str);
options.afterWrite(str);
}
set(doc, { write: write, writeln: function(str) { write(str + '\n'); } });
// Override window.onerror
var oldOnError = active.win.onerror || doNothing;
// This works together with the try/catch around WriteStream::insertScript
// In modern browsers, exceptions in tag scripts go directly to top level
active.win.onerror = function(msg, url, line) {
options.error({ msg: msg + ' - ' + url + ':' + line });
oldOnError.apply(active.win, arguments);
};
// Write to the stream
active.write(html, function streamDone() {
// restore document.write
set(doc, stash);
// restore window.onerror
active.win.onerror = oldOnError;
options.done();
active = null;
nextStream();
});
return active;
}
function postscribe(el, html, options) {
if(isFunction(options)) {
options = { done: options };
}
options = defaults(options, {
done: doNothing,
error: function(e) { throw e; },
beforeWrite: function(str) { return str; },
afterWrite: doNothing
});
el =
// id selector
(/^#/).test(el) ? global.document.getElementById(el.substr(1)) :
// jquery object. TODO: loop over all elements.
el.jquery ? el[0] : el;
var args = [el, html, options];
el.postscribe = {
cancel: function() {
if(args.stream) {
// TODO: implement this
args.stream.abort();
} else {
args[1] = doNothing;
}
}
};
queue.push(args);
if(!active) {
nextStream();
}
return el.postscribe;
}
return set(postscribe, {
// Streams by name.
streams: {},
// Queue of streams.
queue: queue,
// Expose internal classes.
WriteStream: WriteStream
});
}());
// export postscribe
global.postscribe = postscribe;
}());