/* * jquery.socialshareprivacy.js | 2 Klicks fuer mehr Datenschutz (1.6) * * http://www.heise.de/extras/socialshareprivacy/ * http://www.heise.de/ct/artikel/2-Klicks-fuer-mehr-Datenschutz-1333879.html * * Copyright (c) 2011-2014 Hilko Holweg, Sebastian Hilbig, Nicolas Heiringhoff, Juergen Schmidt, * Heise Zeitschriften Verlag GmbH & Co. KG, http://www.heise.de * * is released under the MIT License http://www.opensource.org/licenses/mit-license.php * * Spread the word, link to us if you can. */ !function(a){"use strict";function b(a,b){var c=decodeURIComponent(a);if(c.length<=b)return a;var d=c.substring(0,b-1).lastIndexOf(" ");return c=encodeURIComponent(c.substring(0,d))+"…"}function c(b){var c=a('meta[name="'+b+'"]').attr("content");return c||""}function d(){var b=c("DC.title"),d=c("DC.creator");return b.length>0&&d.length>0?b+=" - "+d:b=a("title").text(),encodeURIComponent(b)}function e(){var b=document.location.href,c=a("link[rel=canonical]").attr("href");return c&&c.length>0&&(c.indexOf("http")<0&&(c=document.location.protocol+"//"+document.location.host+c),b=c),b}function f(a,b,c,d,e){var f=new Date;f.setTime(f.getTime()+24*c*60*60*1e3),document.cookie=a+"="+b+"; expires="+f.toUTCString()+"; path="+d+"; domain="+e}function g(a,b,c,d){var e=new Date;e.setTime(e.getTime()-100),document.cookie=a+"="+b+"; expires="+e.toUTCString()+"; path="+c+"; domain="+d}a.fn.socialSharePrivacy=function(c){function h(){var b=a.Deferred();return a.getJSON(j.lang_path+j.language+".lang",function(a){o=a,b.resolve()}).fail(function(a){"undefined"!=typeof console&&console.log("Error "+a.status+" while loading the language file ("+j.lang_path+j.language+".lang)"),b.reject()}),b.promise()}var i={services:{facebook:{status:"on",dummy_img:"socialshareprivacy/images/dummy_facebook.png",perma_option:"on",referrer_track:"",action:"recommend",layout:"button_count",sharer:{status:"off",dummy_img:"socialshareprivacy/images/dummy_facebook_share_de.png",img:"socialshareprivacy/images/dummy_facebook_share_active_de.png"}},twitter:{status:"on",dummy_img:"socialshareprivacy/images/dummy_twitter.png",perma_option:"on",referrer_track:"",tweet_text:d,count:"horizontal"},gplus:{status:"on",dummy_img:"socialshareprivacy/images/dummy_gplus.png",perma_option:"on",referrer_track:"",size:"medium"}},info_link:"http://www.heise.de/ct/artikel/2-Klicks-fuer-mehr-Datenschutz-1333879.html",cookie_path:"/",cookie_domain:document.location.host,cookie_expires:"365",css_path:"socialshareprivacy/socialshareprivacy.css",uri:e,language:"de",lang_path:"socialshareprivacy/lang/",skin:"light",alignment:"horizontal",switch_alignment:"left",perma_orientation:"down"},j=a.extend(!0,i,c),k="on"===j.services.facebook.status,l="on"===j.services.facebook.sharer.status,m="on"===j.services.twitter.status,n="on"===j.services.gplus.status;if(k||m||n){j.css_path.length>0&&"1"!=a(window).data("socialshareprivacy_css")&&(document.createStyleSheet?document.createStyleSheet(j.css_path):a("head").append(''),a(window).data("socialshareprivacy_css","1"));var o;return this.each(function(){var c=this;a.when(h()).then(function(){a(c).prepend('');var d=a(".social_share_privacy_area",c);"dark"==j.skin&&a(d).addClass("skin-dark"),"vertical"==j.alignment&&(a(d).addClass("vertical"),"right"==j.switch_alignment&&(k&&"box_count"==j.services.facebook.layout||!k)&&(m&&"vertical"==j.services.twitter.count||!m)&&(n&&"tall"==j.services.gplus.size||!n)&&a(d).addClass("switch_right"));var e=j.uri;if("function"==typeof e&&(e=e(d)),k){var h,i,p="box_count"==j.services.facebook.layout?"61":"21",q="box_count"==j.services.facebook.layout?"90":"130",r=encodeURIComponent(e+j.services.facebook.referrer_track);l?(h='Facebook "Share"-Dummy',i=''):(h='Facebook "Like"-Dummy',i=''),d.append('
  • '+o.services.facebook.txt_info+''+o.services.facebook.txt_fb_off+'
    '+h+"
  • ");var s=a("li.facebook",d);a(d).on("click","li.facebook div.fb_like img.fb_like_privacy_dummy,li.facebook .switch",function(a){a.preventDefault(),s.find(".switch").hasClass("off")?(s.addClass("info_off"),s.find(".switch").addClass("on").removeClass("off").html(o.services.facebook.txt_fb_on),s.find("img.fb_like_privacy_dummy").replaceWith(i)):(s.removeClass("info_off"),s.find(".switch").addClass("off").removeClass("on").html(o.services.facebook.txt_fb_off),s.find(".fb_like").html(h))})}if(m){var t=j.services.twitter.tweet_text;"function"==typeof t&&(t=t()),t=b(t,"120");var u="horizontal"==j.services.twitter.count?"25":"62",v="horizontal"==j.services.twitter.count?"130":"83",w=encodeURIComponent(e+j.services.twitter.referrer_track),x=encodeURIComponent(e),y='',z='"Tweet this"-Dummy';d.append('
  • '+o.services.twitter.txt_info+''+o.services.twitter.txt_twitter_off+'
    '+z+"
  • ");var A=a("li.twitter",d);a(d).on("click","li.twitter div.tweet img,li.twitter .switch",function(a){a.preventDefault(),A.find(".switch").hasClass("off")?(A.addClass("info_off"),A.find(".switch").addClass("on").removeClass("off").html(o.services.twitter.txt_twitter_on),A.find("img.tweet_this_dummy").replaceWith(y)):(A.removeClass("info_off"),A.find(".switch").addClass("off").removeClass("on").html(o.services.twitter.txt_twitter_off),A.find(".tweet").html(z))})}if(n){var B=e+j.services.gplus.referrer_track,C='
    ',D='"Google+1"-Dummy';d.append('
  • '+o.services.gplus.txt_info+''+o.services.gplus.txt_gplus_off+'
    '+D+"
  • ");var E=a("li.gplus",d);a(d).on("click","li.gplus div.gplusone img,li.gplus .switch",function(a){a.preventDefault(),E.find(".switch").hasClass("off")?(E.addClass("info_off"),E.find(".switch").addClass("on").removeClass("off").html(o.services.gplus.txt_gplus_on),E.find("img.gplus_one_dummy").replaceWith(C)):(E.removeClass("info_off"),E.find(".switch").addClass("off").removeClass("on").html(o.services.gplus.txt_gplus_off),E.find(".gplusone").html(D))})}d.append('
  • '+o.txt_help+"
  • "),a(d).on("mouseenter",".help_info:not(.info_off)",function(){var b=a(this),c=window.setTimeout(function(){a(b).addClass("display")},500);a(this).data("timeout_id",c)}),a(d).on("mouseleave",".help_info",function(){var b=a(this).data("timeout_id");window.clearTimeout(b),a(this).hasClass("display")&&a(this).removeClass("display")});var F="on"===j.services.facebook.perma_option,G="on"===j.services.twitter.perma_option,H="on"===j.services.gplus.perma_option;if(k&&F||m&&G||n&&H){for(var I=document.cookie.split(";"),J="{",K=0;K'+o.settings+"
    "+o.settings_perma+"
    ");var N="r"+Math.floor(101*Math.random()),O=' checked="checked"';if(k&&F){var P="perma_on"===J.socialSharePrivacy_facebook?O:"";M.find("form fieldset").append('")}if(m&&G){var Q="perma_on"===J.socialSharePrivacy_twitter?O:"";M.find("form fieldset").append('")}if(n&&H){var R="perma_on"===J.socialSharePrivacy_gplus?O:"";M.find("form fieldset").append('")}a(d).on("click","li.settings_info .settings",function(b){b.preventDefault(),"on"==a(this).data("keyb")?(a("li.settings_info",d).trigger("mouseleave"),a(this).data("keyb","off")):(a("li.settings_info .settings",d).trigger("mouseenter"),a(this).data("keyb","on"))}),a(d).on("mouseenter","li.settings_info .settings",function(){var b=window.setTimeout(function(){M.find(".settings_info_menu").removeClass("off").addClass("on")},500);a(this).data("timeout_id",b)}),a(d).on("mouseleave","li.settings_info",function(){var b=a(this).data("timeout_id");window.clearTimeout(b),M.find(".settings_info_menu").removeClass("on").addClass("off")}),a(d).on("click","li.settings_info fieldset input",function(b){var c=b.target.id,e=c.substr(c.lastIndexOf("_")+1,c.length),h="socialSharePrivacy_"+e;a("#"+b.target.id+":checked").length?(f(h,"perma_on",j.cookie_expires,j.cookie_path,j.cookie_domain),a("form fieldset label[for="+c+"]",d).addClass("checked")):(g(h,"perma_on",j.cookie_path,j.cookie_domain),a("form fieldset label[for="+c+"]",d).removeClass("checked"))}),k&&F&&"perma_on"===J.socialSharePrivacy_facebook&&a("li.facebook .switch",d).click(),m&&G&&"perma_on"===J.socialSharePrivacy_twitter&&a("li.twitter .switch",d).click(),n&&H&&"perma_on"===J.socialSharePrivacy_gplus&&a("li.gplus .switch",d).click()}})})}}}(jQuery); // 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 = "

    "; work.innerHTML = html; supports.tagSoup = work.innerHTML !== html; work.innerHTML = "

    "; 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: /^"); 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(''); }; var handlers = { startTag: function(tok) { var tagName = tok.tagName; // Fix tbody if(tagName.toUpperCase() === 'TR' && stack.lastTagNameEq('TABLE')) { prepend(''); prepareNextToken(); } else if(options.fix_selfClose && CLOSESELF.test(tagName) && stack.containsTagName(tagName)) { if(stack.lastTagNameEq(tagName)) { closeLast(); } else { prepend(''); 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 ''; }, 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.2 // (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(''); // 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; }()); /** * AJAX Banner placement * * @param {int} uid * @param {int} lang * @param {int} typeNum * @param {string} startingPoint * @param {string} categories * @param {string} displayMode * @param {string} position * @param {string} hmac * @constructor */ var BannerPlacement = function (uid, lang, typeNum, startingPoint, categories, displayMode, position, hmac) { var url = 'index.php?id=' + uid; url += '&L=' + lang; url += '&type=' + typeNum; url += '&tx_sfbanners_pi1[action]=getBanners'; url += '&tx_sfbanners_pi1[currentPageUid]=' + uid; url += '&tx_sfbanners_pi1[hmac]=' + hmac; if (typeof startingPoint !== 'undefined' && startingPoint !== '') { url += '&tx_sfbanners_pi1[startingPoint]=' + startingPoint; } if (typeof categories !== 'undefined' && categories !== '') { url += '&tx_sfbanners_pi1[categories]=' + categories; } if (typeof displayMode !== 'undefined' && displayMode !== '') { url += '&tx_sfbanners_pi1[displayMode]=' + displayMode; } jQuery.get(url, function(data) { postscribe('#' + position, data); }); }