1/* 2 * jquery.socialshareprivacy.js | 2 Klicks fuer mehr Datenschutz 3 * 4 * http://www.heise.de/extras/socialshareprivacy/ 5 * http://www.heise.de/ct/artikel/2-Klicks-fuer-mehr-Datenschutz-1333879.html 6 * 7 * Copyright (c) 2011 Hilko Holweg, Sebastian Hilbig, Nicolas Heiringhoff, Juergen Schmidt, 8 * Heise Zeitschriften Verlag GmbH & Co. KG, http://www.heise.de 9 * 10 * is released under the MIT License http://www.opensource.org/licenses/mit-license.php 11 * 12 * Spread the word, link to us if you can. 13 */ 14(function ($) { 15 16 "use strict"; 17 18 /* 19 * helper functions 20 */ 21 22 // abbreviate at last blank before length and add "\u2026" (horizontal ellipsis) 23 function abbreviateText(text, length) { 24 var abbreviated = decodeURIComponent(text); 25 if (abbreviated.length <= length) { 26 return text; 27 } 28 29 var lastWhitespaceIndex = abbreviated.substring(0, length - 1).lastIndexOf(' '); 30 abbreviated = encodeURIComponent(abbreviated.substring(0, lastWhitespaceIndex)) + "\u2026"; 31 32 return abbreviated; 33 } 34 35 // returns content of <meta name="" content=""> tags or '' if empty/non existant 36 function getMeta(name) { 37 var metaContent = $('meta[name="' + name + '"]').attr('content'); 38 return metaContent || ''; 39 } 40 41 // create tweet text from content of <meta name="DC.title"> and <meta name="DC.creator"> 42 // fallback to content of <title> tag 43 function getTweetText() { 44 var title = getMeta('DC.title'); 45 var creator = getMeta('DC.creator'); 46 47 if (title.length > 0 && creator.length > 0) { 48 title += ' - ' + creator; 49 } else { 50 title = $('title').text(); 51 } 52 53 return encodeURIComponent(title); 54 } 55 56 // build URI from rel="canonical" or document.location 57 function getURI() { 58 var uri = document.location.href; 59 var canonical = $("link[rel=canonical]").attr("href"); 60 61 if (canonical && canonical.length > 0) { 62 if (canonical.indexOf("http") < 0) { 63 canonical = document.location.protocol + "//" + document.location.host + canonical; 64 } 65 uri = canonical; 66 } 67 68 return uri; 69 } 70 71 function cookieSet(name, value, days, path, domain) { 72 var expires = new Date(); 73 expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000)); 74 document.cookie = name + '=' + value + '; expires=' + expires.toUTCString() + '; path=' + path + '; domain=' + domain; 75 } 76 function cookieDel(name, value, path, domain) { 77 var expires = new Date(); 78 expires.setTime(expires.getTime() - 100); 79 document.cookie = name + '=' + value + '; expires=' + expires.toUTCString() + '; path=' + path + '; domain=' + domain; 80 } 81 82 // extend jquery with our plugin function 83 $.fn.socialSharePrivacy = function (settings) { 84 var defaults = { 85 'services' : { 86 'facebook' : { 87 'status' : 'on', 88 'dummy_img' : 'images/dummy_facebook.png', 89 'txt_info' : '2 Klicks für mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie können Ihre Empfehlung an Facebook senden. Schon beim Aktivieren werden Daten an Dritte übertragen – siehe <em>i</em>.', 90 'txt_fb_off' : 'nicht mit Facebook verbunden', 91 'txt_fb_on' : 'mit Facebook verbunden', 92 'perma_option' : 'on', 93 'display_name' : 'Facebook', 94 'referrer_track' : '', 95 'language' : 'de_DE', 96 'action' : 'recommend' 97 }, 98 'twitter' : { 99 'status' : 'on', 100 'dummy_img' : 'images/dummy_twitter.png', 101 'txt_info' : '2 Klicks für mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie können Ihre Empfehlung an Twitter senden. Schon beim Aktivieren werden Daten an Dritte übertragen – siehe <em>i</em>.', 102 'txt_twitter_off' : 'nicht mit Twitter verbunden', 103 'txt_twitter_on' : 'mit Twitter verbunden', 104 'perma_option' : 'on', 105 'display_name' : 'Twitter', 106 'referrer_track' : '', 107 'tweet_text' : getTweetText, 108 'language' : 'en' 109 }, 110 'gplus' : { 111 'status' : 'on', 112 'dummy_img' : 'images/dummy_gplus.png', 113 'txt_info' : '2 Klicks für mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie können Ihre Empfehlung an Google+ senden. Schon beim Aktivieren werden Daten an Dritte übertragen – siehe <em>i</em>.', 114 'txt_gplus_off' : 'nicht mit Google+ verbunden', 115 'txt_gplus_on' : 'mit Google+ verbunden', 116 'perma_option' : 'on', 117 'display_name' : 'Google+', 118 'referrer_track' : '', 119 'language' : 'de' 120 } 121 }, 122 'info_link' : 'http://www.heise.de/ct/artikel/2-Klicks-fuer-mehr-Datenschutz-1333879.html', 123 'txt_help' : 'Wenn Sie diese Felder durch einen Klick aktivieren, werden Informationen an Facebook, Twitter oder Google in die USA übertragen und unter Umständen auch dort gespeichert. Näheres erfahren Sie durch einen Klick auf das <em>i</em>.', 124 'settings_perma' : 'Dauerhaft aktivieren und Datenüber­tragung zustimmen:', 125 'cookie_path' : '/', 126 'cookie_domain' : document.location.host, 127 'cookie_expires' : '365', 128 'css_path' : 'socialshareprivacy/socialshareprivacy.css', 129 'uri' : getURI 130 }; 131 132 // Standardwerte des Plug-Ings mit den vom User angegebenen Optionen ueberschreiben 133 var options = $.extend(true, defaults, settings); 134 135 var facebook_on = (options.services.facebook.status === 'on'); 136 var twitter_on = (options.services.twitter.status === 'on'); 137 var gplus_on = (options.services.gplus.status === 'on'); 138 139 // check if at least one service is "on" 140 if (!facebook_on && !twitter_on && !gplus_on) { 141 return; 142 } 143 144 // insert stylesheet into document and prepend target element 145 if (options.css_path.length > 0) { 146 // IE fix (noetig fuer IE < 9 - wird hier aber fuer alle IE gemacht) 147 if (document.createStyleSheet) { 148 document.createStyleSheet(options.css_path); 149 } else { 150 $('head').append('<link rel="stylesheet" type="text/css" href="' + options.css_path + '" />'); 151 } 152 } 153 154 return this.each(function () { 155 156 $(this).prepend('<ul class="social_share_privacy_area"></ul>'); 157 var context = $('.social_share_privacy_area', this); 158 159 // canonical uri that will be shared 160 var uri = options.uri; 161 if (typeof uri === 'function') { 162 uri = uri(context); 163 } 164 165 // 166 // Facebook 167 // 168 if (facebook_on) { 169 var fb_enc_uri = encodeURIComponent(uri + options.services.facebook.referrer_track); 170 var fb_code = '<iframe src="http://www.facebook.com/plugins/like.php?locale=' + options.services.facebook.language + '&href=' + fb_enc_uri + '&send=false&layout=button_count&width=120&show_faces=false&action=' + options.services.facebook.action + '&colorscheme=light&font&height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:145px; height:21px;" allowTransparency="true"></iframe>'; 171 var fb_dummy_btn = '<img src="' + options.services.facebook.dummy_img + '" alt="Facebook "Like"-Dummy" class="fb_like_privacy_dummy" />'; 172 173 context.append('<li class="facebook help_info"><span class="info">' + options.services.facebook.txt_info + '</span><span class="switch off">' + options.services.facebook.txt_fb_off + '</span><div class="fb_like dummy_btn">' + fb_dummy_btn + '</div></li>'); 174 175 var $container_fb = $('li.facebook', context); 176 177 $('li.facebook div.fb_like img.fb_like_privacy_dummy,li.facebook span.switch', context).live('click', function () { 178 if ($container_fb.find('span.switch').hasClass('off')) { 179 $container_fb.addClass('info_off'); 180 $container_fb.find('span.switch').addClass('on').removeClass('off').html(options.services.facebook.txt_fb_on); 181 $container_fb.find('img.fb_like_privacy_dummy').replaceWith(fb_code); 182 } else { 183 $container_fb.removeClass('info_off'); 184 $container_fb.find('span.switch').addClass('off').removeClass('on').html(options.services.facebook.txt_fb_off); 185 $container_fb.find('.fb_like').html(fb_dummy_btn); 186 } 187 }); 188 } 189 190 // 191 // Twitter 192 // 193 if (twitter_on) { 194 var text = options.services.twitter.tweet_text; 195 if (typeof text === 'function') { 196 text = text(); 197 } 198 // 120 is the max character count left after twitters automatic url shortening with t.co 199 text = abbreviateText(text, '120'); 200 201 var twitter_enc_uri = encodeURIComponent(uri + options.services.twitter.referrer_track); 202 var twitter_count_url = encodeURIComponent(uri); 203 var twitter_code = '<iframe allowtransparency="true" frameborder="0" scrolling="no" src="http://platform.twitter.com/widgets/tweet_button.html?url=' + twitter_enc_uri + '&counturl=' + twitter_count_url + '&text=' + text + '&count=horizontal&lang=' + options.services.twitter.language + '" style="width:130px; height:25px;"></iframe>'; 204 var twitter_dummy_btn = '<img src="' + options.services.twitter.dummy_img + '" alt=""Tweet this"-Dummy" class="tweet_this_dummy" />'; 205 206 context.append('<li class="twitter help_info"><span class="info">' + options.services.twitter.txt_info + '</span><span class="switch off">' + options.services.twitter.txt_twitter_off + '</span><div class="tweet dummy_btn">' + twitter_dummy_btn + '</div></li>'); 207 208 var $container_tw = $('li.twitter', context); 209 210 $('li.twitter div.tweet img,li.twitter span.switch', context).live('click', function () { 211 if ($container_tw.find('span.switch').hasClass('off')) { 212 $container_tw.addClass('info_off'); 213 $container_tw.find('span.switch').addClass('on').removeClass('off').html(options.services.twitter.txt_twitter_on); 214 $container_tw.find('img.tweet_this_dummy').replaceWith(twitter_code); 215 } else { 216 $container_tw.removeClass('info_off'); 217 $container_tw.find('span.switch').addClass('off').removeClass('on').html(options.services.twitter.txt_twitter_off); 218 $container_tw.find('.tweet').html(twitter_dummy_btn); 219 } 220 }); 221 } 222 223 // 224 // Google+ 225 // 226 if (gplus_on) { 227 // fuer G+ wird die URL nicht encoded, da das zu einem Fehler fuehrt 228 var gplus_uri = uri + options.services.gplus.referrer_track; 229 230 // we use the Google+ "asynchronous" code, standard code is flaky if inserted into dom after load 231 var gplus_code = '<div class="g-plusone" data-size="medium" data-href="' + gplus_uri + '"></div><script type="text/javascript">window.___gcfg = {lang: "' + options.services.gplus.language + '"}; (function() { var po = document.createElement("script"); po.type = "text/javascript"; po.async = true; po.src = "https://apis.google.com/js/plusone.js"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(po, s); })(); </script>'; 232 var gplus_dummy_btn = '<img src="' + options.services.gplus.dummy_img + '" alt=""Google+1"-Dummy" class="gplus_one_dummy" />'; 233 234 context.append('<li class="gplus help_info"><span class="info">' + options.services.gplus.txt_info + '</span><span class="switch off">' + options.services.gplus.txt_gplus_off + '</span><div class="gplusone dummy_btn">' + gplus_dummy_btn + '</div></li>'); 235 236 var $container_gplus = $('li.gplus', context); 237 238 $('li.gplus div.gplusone img,li.gplus span.switch', context).live('click', function () { 239 if ($container_gplus.find('span.switch').hasClass('off')) { 240 $container_gplus.addClass('info_off'); 241 $container_gplus.find('span.switch').addClass('on').removeClass('off').html(options.services.gplus.txt_gplus_on); 242 $container_gplus.find('img.gplus_one_dummy').replaceWith(gplus_code); 243 } else { 244 $container_gplus.removeClass('info_off'); 245 $container_gplus.find('span.switch').addClass('off').removeClass('on').html(options.services.gplus.txt_gplus_off); 246 $container_gplus.find('.gplusone').html(gplus_dummy_btn); 247 } 248 }); 249 } 250 251 // 252 // Der Info/Settings-Bereich wird eingebunden 253 // 254 context.append('<li class="settings_info"><div class="settings_info_menu off perma_option_off"><a href="' + options.info_link + '"><span class="help_info icon"><span class="info">' + options.txt_help + '</span></span></a></div></li>'); 255 256 // Info-Overlays mit leichter Verzoegerung einblenden 257 $('.help_info:not(.info_off)', context).live('mouseenter', function () { 258 var $info_wrapper = $(this); 259 var timeout_id = window.setTimeout(function () { $($info_wrapper).addClass('display'); }, 500); 260 $(this).data('timeout_id', timeout_id); 261 }); 262 $('.help_info', context).live('mouseleave', function () { 263 var timeout_id = $(this).data('timeout_id'); 264 window.clearTimeout(timeout_id); 265 if ($(this).hasClass('display')) { 266 $(this).removeClass('display'); 267 } 268 }); 269 270 var facebook_perma = (options.services.facebook.perma_option === 'on'); 271 var twitter_perma = (options.services.twitter.perma_option === 'on'); 272 var gplus_perma = (options.services.gplus.perma_option === 'on'); 273 274 // Menue zum dauerhaften Einblenden der aktiven Dienste via Cookie einbinden 275 // Die IE7 wird hier ausgenommen, da er kein JSON kann und die Cookies hier ueber JSON-Struktur abgebildet werden 276 if (((facebook_on && facebook_perma) 277 || (twitter_on && twitter_perma) 278 || (gplus_on && gplus_perma)) 279 && (!$.browser.msie || ($.browser.msie && $.browser.version > 7.0))) { 280 281 // Cookies abrufen 282 var cookie_list = document.cookie.split(';'); 283 var cookies = '{'; 284 var i = 0; 285 for (; i < cookie_list.length; i += 1) { 286 var foo = cookie_list[i].split('='); 287 cookies += '"' + $.trim(foo[0]) + '":"' + $.trim(foo[1]) + '"'; 288 if (i < cookie_list.length - 1) { 289 cookies += ','; 290 } 291 } 292 cookies += '}'; 293 cookies = JSON.parse(cookies); 294 295 // Container definieren 296 var $container_settings_info = $('li.settings_info', context); 297 298 // Klasse entfernen, die das i-Icon alleine formatiert, da Perma-Optionen eingeblendet werden 299 $container_settings_info.find('.settings_info_menu').removeClass('perma_option_off'); 300 301 // Perma-Optionen-Icon (.settings) und Formular (noch versteckt) einbinden 302 $container_settings_info.find('.settings_info_menu').append('<span class="settings">Einstellungen</span><form><fieldset><legend>' + options.settings_perma + '</legend></fieldset></form>'); 303 304 305 // Die Dienste mit <input> und <label>, sowie checked-Status laut Cookie, schreiben 306 var checked = ' checked="checked"'; 307 if (facebook_on && facebook_perma) { 308 var perma_status_facebook = cookies.socialSharePrivacy_facebook === 'perma_on' ? checked : ''; 309 $container_settings_info.find('form fieldset').append( 310 '<input type="checkbox" name="perma_status_facebook" id="perma_status_facebook"' 311 + perma_status_facebook + ' /><label for="perma_status_facebook">' 312 + options.services.facebook.display_name + '</label>' 313 ); 314 } 315 316 if (twitter_on && twitter_perma) { 317 var perma_status_twitter = cookies.socialSharePrivacy_twitter === 'perma_on' ? checked : ''; 318 $container_settings_info.find('form fieldset').append( 319 '<input type="checkbox" name="perma_status_twitter" id="perma_status_twitter"' 320 + perma_status_twitter + ' /><label for="perma_status_twitter">' 321 + options.services.twitter.display_name + '</label>' 322 ); 323 } 324 325 if (gplus_on && gplus_perma) { 326 var perma_status_gplus = cookies.socialSharePrivacy_gplus === 'perma_on' ? checked : ''; 327 $container_settings_info.find('form fieldset').append( 328 '<input type="checkbox" name="perma_status_gplus" id="perma_status_gplus"' 329 + perma_status_gplus + ' /><label for="perma_status_gplus">' 330 + options.services.gplus.display_name + '</label>' 331 ); 332 } 333 334 // Cursor auf Pointer setzen fuer das Zahnrad 335 $container_settings_info.find('span.settings').css('cursor', 'pointer'); 336 337 // Einstellungs-Menue bei mouseover ein-/ausblenden 338 $($container_settings_info.find('span.settings'), context).live('mouseenter', function () { 339 var timeout_id = window.setTimeout(function () { $container_settings_info.find('.settings_info_menu').removeClass('off').addClass('on'); }, 500); 340 $(this).data('timeout_id', timeout_id); 341 }); 342 $($container_settings_info, context).live('mouseleave', function () { 343 var timeout_id = $(this).data('timeout_id'); 344 window.clearTimeout(timeout_id); 345 $container_settings_info.find('.settings_info_menu').removeClass('on').addClass('off'); 346 }); 347 348 // Klick-Interaktion auf <input> um Dienste dauerhaft ein- oder auszuschalten (Cookie wird gesetzt oder geloescht) 349 $($container_settings_info.find('fieldset input')).live('click', function (event) { 350 var click = event.target.id; 351 var service = click.substr(click.lastIndexOf('_') + 1, click.length); 352 var cookie_name = 'socialSharePrivacy_' + service; 353 354 if ($('#' + event.target.id + ':checked').length) { 355 cookieSet(cookie_name, 'perma_on', options.cookie_expires, options.cookie_path, options.cookie_domain); 356 $('form fieldset label[for=' + click + ']', context).addClass('checked'); 357 } else { 358 cookieDel(cookie_name, 'perma_on', options.cookie_path, options.cookie_domain); 359 $('form fieldset label[for=' + click + ']', context).removeClass('checked'); 360 } 361 }); 362 363 // Dienste automatisch einbinden, wenn entsprechendes Cookie vorhanden ist 364 if (facebook_on && facebook_perma && cookies.socialSharePrivacy_facebook === 'perma_on') { 365 $('li.facebook span.switch', context).click(); 366 } 367 if (twitter_on && twitter_perma && cookies.socialSharePrivacy_twitter === 'perma_on') { 368 $('li.twitter span.switch', context).click(); 369 } 370 if (gplus_on && gplus_perma && cookies.socialSharePrivacy_gplus === 'perma_on') { 371 $('li.gplus span.switch', context).click(); 372 } 373 } 374 }); // this.each(function () 375 }; // $.fn.socialSharePrivacy = function (settings) { 376}(jQuery)); 377 378