1/**
2 * @license
3 * jquery.socialshareprivacy.js | 2 Klicks fuer mehr Datenschutz
4 *
5 * http://www.heise.de/extras/socialshareprivacy/
6 * http://www.heise.de/ct/artikel/2-Klicks-fuer-mehr-Datenschutz-1333879.html
7 *
8 * Copyright (c) 2011 Hilko Holweg, Sebastian Hilbig, Nicolas Heiringhoff, Juergen Schmidt,
9 * Heise Zeitschriften Verlag GmbH & Co. KG, http://www.heise.de
10 *
11 * Copyright (c) 2012-2013 Mathias Panzenböck
12 *
13 * is released under the MIT License http://www.opensource.org/licenses/mit-license.php
14 *
15 * Spread the word, link to us if you can.
16 */
17(function ($, undefined) {
18	"use strict";
19
20	/*
21	 * helper functions
22	 */
23
24	/**
25	 * Build an absolute url using a base url.
26	 * The provided base url has to be a valid absolute url. It will not be validated!
27	 * If no base url is given the document location is used.
28	 * Schemes that behave other than http might not work.
29	 * This function tries to support file:-urls, but might fail in some cases.
30	 * email:-urls aren't supported at all (don't make sense anyway).
31	 */
32	function absurl (url, base) {
33		if (!base) base = document.baseURI || $("html > head > base").last().attr("href") || document.location.href;
34		if (!url) {
35			return base;
36		}
37		else if (/^[a-z][-+\.a-z0-9]*:/i.test(url)) {
38			// The scheme actually could contain any kind of alphanumerical unicode
39			// character, but JavaScript regular expressions don't support unicode
40			// character classes. Maybe /^[^:]+:/ or even /^.*:/ would be sufficient?
41			return url;
42		}
43		else if (url.slice(0,2) === '//') {
44			return /^[^:]+:/.exec(base)[0]+url;
45		}
46
47		var ch = url.charAt(0);
48		if (ch === '/') {
49			if (/^file:/i.test(base)) {
50				// file scheme has no hostname
51				return 'file://'+url;
52			}
53			else {
54				return /^[^:]+:\/*[^\/]+/i.exec(base)[0]+url;
55			}
56		}
57		else if (ch === '#') {
58			// assume "#" only occures at the end indicating the fragment
59			return base.replace(/#.*$/,'')+url;
60		}
61		else if (ch === '?') {
62			// assume "?" and "#" only occure at the end indicating the query
63			// and the fragment
64			return base.replace(/[\?#].*$/,'')+url;
65		}
66		else {
67			var path;
68			if (/^file:/i.test(base)) {
69				path = base.replace(/^file:\/{0,2}/i,'');
70				base = "file://";
71			}
72			else {
73				var match = /^([^:]+:\/*[^\/]+)(\/.*?)?(\?.*?)?(#.*)?$/.exec(base);
74				base = match[1];
75				path = match[2]||"/";
76			}
77
78			path = path.split("/");
79			path.pop();
80			if (path.length === 0) {
81				// Ensure leading "/". Of course this is only valid on
82				// unix like filesystems. More magic would be needed to
83				// support other filesystems.
84				path.push("");
85			}
86			path.push(url);
87			return base+path.join("/");
88		}
89	}
90
91	function formatNumber (number) {
92		number = Number(number);
93
94		var prefix = "";
95		var suffix = "";
96		if (number < 0) {
97			prefix = "-";
98			number = -number;
99		}
100
101		if (number === Infinity) {
102			return prefix + "Infinity";
103		}
104
105		if (number > 9999) {
106			number = number / 1000;
107			suffix = "K";
108		}
109
110		number = Math.round(number);
111		if (number === 0) {
112			return "0";
113		}
114
115		var buf = [];
116		while (number > 0) {
117			var part = String(number % 1000);
118
119			number = Math.floor(number / 1000);
120			if (number) {
121				while (part.length < 3) {
122					part = "0"+part;
123				}
124			}
125
126			buf.unshift(part);
127		}
128
129		return prefix + buf.join(",") + suffix;
130	}
131
132	// helper function that gets the title of the current page
133	function getTitle (options, uri, settings) {
134		var title = settings && settings.title;
135		if (typeof title === "function") {
136			title = title.call(this, options, uri, settings);
137		}
138
139		if (title) {
140			return title;
141		}
142
143		var title = $('meta[name="DC.title"]').attr('content');
144		var creator = $('meta[name="DC.creator"]').attr('content');
145
146		if (title && creator) {
147			return title + ' - ' + creator;
148		} else {
149			return title || $('meta[property="og:title"]').attr('content') || $('title').text();
150		}
151	}
152
153	function getDescription (options, uri, settings) {
154		var description = settings && settings.description;
155		if (typeof description === "function") {
156			description = description.call(this, options, uri, settings);
157		}
158
159		if (description) {
160			return description;
161		}
162
163		return abbreviateText(
164			$('meta[name="twitter:description"]').attr('content') ||
165			$('meta[itemprop="description"]').attr('content') ||
166			$('meta[name="description"]').attr('content') ||
167			$.trim($('article, p').first().text()) || $.trim($('body').text()), 3500);
168	}
169
170	var IMAGE_ATTR_MAP = {
171		META   : 'content',
172		IMG    : 'src',
173		A      : 'href',
174		IFRAME : 'src',
175		LINK   : 'href'
176	};
177
178	// find the largest image of the website
179	// if no image at all is found use googles favicon service, which
180	// defaults to a small globe (so there is always some image)
181	function getImage (options, uri, settings) {
182		var imgs, img = settings && settings.image;
183		if (typeof img === "function") {
184			img = img.call(this, options, uri, settings);
185		}
186
187		if (!img) {
188			imgs = $('meta[property="image"], meta[property="og:image"], meta[property="og:image:url"], meta[name="twitter:image"], link[rel="image_src"], itemscope *[itemprop="image"]').first();
189			if (imgs.length > 0) {
190				img = imgs.attr(IMAGE_ATTR_MAP[imgs[0].nodeName]);
191			}
192		}
193
194		if (img) {
195			return absurl(img);
196		}
197
198		imgs = $('img').filter(':visible').filter(function () {
199			return $(this).parents('.social_share_privacy_area').length === 0;
200		});
201		if (imgs.length === 0) {
202			img = $('link[rel~="shortcut"][rel~="icon"]').attr('href');
203			if (img) return absurl(img);
204			return 'http://www.google.com/s2/favicons?'+$.param({domain:location.hostname});
205		}
206		imgs.sort(function (lhs, rhs) {
207			return rhs.offsetWidth * rhs.offsetHeight - lhs.offsetWidth * lhs.offsetHeight;
208		});
209		// browser makes src absolute:
210		return imgs[0].src;
211	}
212
213	// abbreviate at last blank before length and add "\u2026" (horizontal ellipsis)
214	function abbreviateText (text, length) {
215		// length of UTF-8 encoded string
216		if (unescape(encodeURIComponent(text)).length <= length) {
217			return text;
218		}
219
220		// "\u2026" is actually 3 bytes long in UTF-8
221		// TODO: if any of the last 3 characters is > 1 byte long this truncates too much
222		var abbrev = text.slice(0, length - 3);
223
224		if (!/\W/.test(text.charAt(length - 3))) {
225			var match = /^(.*)\s\S*$/.exec(abbrev);
226			if (match) {
227				abbrev = match[1];
228			}
229		}
230		return abbrev + "\u2026";
231	}
232
233	var HTML_CHAR_MAP = {
234		'<': '&lt;',
235		'>': '&gt;',
236		'&': '&amp;',
237		'"': '&quot;',
238		"'": '&#39;'
239	};
240
241	function escapeHtml (s) {
242		return s.replace(/[<>&"']/g, function (ch) {
243			return HTML_CHAR_MAP[ch];
244		});
245	}
246
247	function getEmbed (options, uri, settings) {
248		var embed = settings && settings.embed;
249		if (typeof embed === "function") {
250			embed = embed.call(this, options, uri, settings);
251		}
252
253		if (embed) {
254			return embed;
255		}
256
257		embed = ['<iframe scrolling="no" frameborder="0" style="border:none;" allowtransparency="true"'];
258		var embed_url = $('meta[name="twitter:player"]').attr('content');
259
260		if (embed_url) {
261			var width  = $('meta[name="twitter:player:width"]').attr('content');
262			var height = $('meta[name="twitter:player:height"]').attr('content');
263
264			if (width)  embed.push(' width="',escapeHtml(width),'"');
265			if (height) embed.push(' height="',escapeHtml(height),'"');
266		}
267		else {
268			embed_url = uri + options.referrer_track;
269		}
270
271		embed.push(' src="',escapeHtml(embed_url),'"></iframe>');
272		return embed.join('');
273	}
274
275	// build URI from rel="canonical" or document.location
276	function getURI (options) {
277		var uri = document.location.href;
278		var canonical = $("link[rel=canonical]").attr("href") || $('head meta[property="og:url"]').attr("content");
279
280		if (canonical) {
281			uri = absurl(canonical);
282		}
283		else if (options && options.ignore_fragment) {
284			uri = uri.replace(/#.*$/,'');
285		}
286
287		return uri;
288	}
289
290	function buttonClickHandler (service_name) {
291		function onclick (event) {
292			var $container = $(this).parents('li.help_info').first();
293			var $share = $container.parents('.social_share_privacy_area').first().parent();
294			var options = $share.data('social-share-privacy-options');
295			var service = options.services[service_name];
296			var button_class = service.button_class || service_name;
297			var uri = options.uri;
298			if (typeof uri === 'function') {
299				uri = uri.call($share[0], options);
300			}
301			var $switch = $container.find('span.switch');
302			if ($switch.hasClass('off')) {
303				$container.addClass('info_off');
304				$switch.addClass('on').removeClass('off').html(service.txt_on||'\u00a0');
305				$container.find('img.privacy_dummy').replaceWith(
306					typeof(service.button) === "function" ?
307					service.button.call($container.parent().parent()[0],service,uri,options) :
308					service.button);
309				$share.trigger({type: 'socialshareprivacy:enable', serviceName: service_name, isClick: !event.isTrigger});
310			} else {
311				$container.removeClass('info_off');
312				$switch.addClass('off').removeClass('on').html(service.txt_off||'\u00a0');
313				$container.find('.dummy_btn').empty().
314					append($('<img/>').addClass(button_class+'_privacy_dummy privacy_dummy').
315						attr({
316							alt: service.dummy_alt,
317							src: service.path_prefix + (options.layout === 'line' ?
318								service.dummy_line_img : service.dummy_box_img)
319						}).click(onclick));
320				$share.trigger({type: 'socialshareprivacy:disable', serviceName: service_name, isClick: !event.isTrigger});
321			}
322		};
323		return onclick;
324	}
325
326	// display info-overlays a tiny bit delayed
327	function enterHelpInfo () {
328		var $info_wrapper = $(this);
329		if ($info_wrapper.hasClass('info_off')) return;
330		var timeout_id = window.setTimeout(function () {
331			$info_wrapper.addClass('display');
332			$info_wrapper.removeData('timeout_id');
333		}, 500);
334		$info_wrapper.data('timeout_id', timeout_id);
335	}
336
337	function leaveHelpInfo () {
338		var $info_wrapper = $(this);
339		var timeout_id = $info_wrapper.data('timeout_id');
340		if (timeout_id !== undefined) {
341			window.clearTimeout(timeout_id);
342		}
343		$info_wrapper.removeClass('display');
344	}
345
346	function permCheckChangeHandler () {
347		var $input = $(this);
348		var $share = $input.parents('.social_share_privacy_area').first().parent();
349		var options = $share.data('social-share-privacy-options');
350		if ($input.is(':checked')) {
351			options.set_perma_option($input.attr('data-service'), options);
352			$input.parent().addClass('checked');
353		} else {
354			options.del_perma_option($input.attr('data-service'), options);
355			$input.parent().removeClass('checked');
356		}
357	}
358
359	function enterSettingsInfo () {
360		var $settings = $(this);
361		var timeout_id = window.setTimeout(function () {
362			$settings.find('.settings_info_menu').removeClass('off').addClass('on');
363			$settings.removeData('timeout_id');
364		}, 500);
365		$settings.data('timeout_id', timeout_id);
366	}
367
368	function leaveSettingsInfo () {
369		var $settings = $(this);
370		var timeout_id = $settings.data('timeout_id');
371		if (timeout_id !== undefined) {
372			window.clearTimeout(timeout_id);
373		}
374		$settings.find('.settings_info_menu').removeClass('on').addClass('off');
375	}
376
377	function setPermaOption (service_name, options) {
378		$.cookie('socialSharePrivacy_'+service_name, 'perma_on', options.cookie_expires, options.cookie_path, options.cookie_domain);
379	}
380
381	function delPermaOption (service_name, options) {
382		$.cookie('socialSharePrivacy_'+service_name, null, -1, options.cookie_path, options.cookie_domain);
383	}
384
385	function getPermaOption (service_name, options) {
386		return !!options.get_perma_options(options)[service_name];
387	}
388
389	function getPermaOptions (options) {
390		var cookies = $.cookie();
391		var permas = {};
392		for (var name in cookies) {
393			var match = /^socialSharePrivacy_(.+)$/.exec(name);
394			if (match) {
395				permas[match[1]] = cookies[name] === 'perma_on';
396			}
397		}
398		return permas;
399	}
400
401
402	// extend jquery with our plugin function
403	function socialSharePrivacy (options) {
404
405		if (typeof options === "string") {
406			var command = options;
407			if (arguments.length === 1) {
408				switch (command) {
409					case "enable":
410						this.find('.switch.off').click();
411						break;
412
413					case "disable":
414						this.find('.switch.on').click();
415						break;
416
417					case "toggle":
418						this.find('.switch').click();
419						break;
420
421					case "options":
422						return this.data('social-share-privacy-options');
423
424					case "destroy":
425						this.trigger({type: 'socialshareprivacy:destroy'});
426						this.children('.social_share_privacy_area').remove();
427						this.removeData('social-share-privacy-options');
428						break;
429
430					default:
431						throw new Error("socialSharePrivacy: unknown command: "+command);
432				}
433			}
434			else {
435				var arg = arguments[1];
436				switch (command) {
437					case "enable":
438						this.each(function () {
439							var $self = $(this);
440							var options = $self.data('social-share-privacy-options');
441							$self.find('.'+(options.services[arg].class_name||arg)+' .switch.off').click();
442						});
443						break;
444
445					case "disable":
446						this.each(function () {
447							var $self = $(this);
448							var options = $self.data('social-share-privacy-options');
449							$self.find('.'+(options.services[arg].class_name||arg)+' .switch.on').click();
450						});
451						break;
452
453					case "toggle":
454						this.each(function () {
455							var $self = $(this);
456							var options = $self.data('social-share-privacy-options');
457							$self.find('.'+(options.services[arg].class_name||arg)+' .switch').click();
458						});
459						break;
460
461					case "option":
462						if (arguments.length > 2) {
463							var value = {};
464							value[arg] = arguments[2];
465							this.each(function () {
466								$.extend(true, $(this).data('social-share-privacy-options'), value);
467							});
468						}
469						else {
470							return this.data('social-share-privacy-options')[arg];
471						}
472						break;
473
474					case "options":
475						$.extend(true, options, arg);
476						break;
477
478					default:
479						throw new Error("socialSharePrivacy: unknown command: "+command);
480				}
481			}
482			return this;
483		}
484
485		return this.each(function () {
486			// parse options passed via data-* attributes:
487			var data = {};
488			if (this.lang) data.language = this.lang;
489			for (var i = 0, attrs = this.attributes; i < attrs.length; ++ i) {
490				var attr = attrs[i];
491				if (/^data-./.test(attr.name)) {
492					var path = attr.name.slice(5).replace(/-/g,"_").split(".");
493					var ctx = data, j = 0;
494					for (; j < path.length-1; ++ j) {
495						var name = path[j];
496						if (name in ctx) {
497							ctx = ctx[name];
498							if (typeof ctx === "string") {
499								ctx = (new Function("$", "return ("+ctx+");")).call(this, $);
500							}
501						}
502						else {
503							ctx = ctx[name] = {};
504						}
505					}
506					var name = path[j];
507					if (typeof ctx[name] === "object") {
508						ctx[name] = $.extend(true, (new Function("$", "return ("+attr.value+");")).call(this, $), ctx[name]);
509					}
510					else {
511						ctx[name] = attr.value;
512					}
513				}
514			}
515			// parse global option values:
516			if ('cookie_expires'   in data) data.cookie_expires  = Number(data.cookie_expires);
517			if ('perma_option'     in data) data.perma_option    = $.trim(data.perma_option).toLowerCase()    === "true";
518			if ('ignore_fragment'  in data) data.ignore_fragment = $.trim(data.ignore_fragment).toLowerCase() === "true";
519			if ('set_perma_option' in data) {
520				data.set_perma_option = new Function("service_name", "options", data.set_perma_option);
521			}
522			if ('del_perma_option' in data) {
523				data.del_perma_option = new Function("service_name", "options", data.del_perma_option);
524			}
525			if ('get_perma_option' in data) {
526				data.get_perma_option = new Function("service_name", "options", data.get_perma_option);
527			}
528			if ('get_perma_options' in data) {
529				data.get_perma_options = new Function("options", data.get_perma_options);
530			}
531			if ('order' in data) {
532				data.order = $.trim(data.order);
533				if (data.order) {
534					data.order = data.order.split(/\s+/g);
535				}
536				else {
537					delete data.order;
538				}
539			}
540			if (typeof data.services === "string") {
541				data.services = (new Function("$", "return ("+data.services+");")).call(this, $);
542			}
543			if ('options' in data) {
544				data = $.extend(data, (new Function("$", "return ("+data.options+");")).call(this, $));
545				delete data.options;
546			}
547			if ('services' in data) {
548				for (var service_name in data.services) {
549					var service = data.services[service_name];
550					if (typeof service === "string") {
551						data.services[service_name] = (new Function("$", "return ("+service+");")).call(this, $);
552					}
553					// only values of common options are parsed:
554					if (typeof service.status === "string") {
555						service.status = $.trim(service.status).toLowerCase() === "true";
556					}
557					if (typeof service.perma_option === "string") {
558						service.perma_option = $.trim(service.perma_option).toLowerCase() === "true";
559					}
560				}
561			}
562			// overwrite default values with user settings
563			var this_options = $.extend(true,{},socialSharePrivacy.settings,options,data);
564			var order = this_options.order || [];
565
566			var dummy_img  = this_options.layout === 'line' ? 'dummy_line_img' : 'dummy_box_img';
567			var any_on     = false;
568			var any_perm   = false;
569			var any_unsafe = false;
570			var unordered  = [];
571			for (var service_name in this_options.services) {
572				var service = this_options.services[service_name];
573				if (service.status) {
574					any_on = true;
575					if ($.inArray(service_name, order) === -1) {
576						unordered.push(service_name);
577					}
578					if (service.privacy !== 'safe') {
579						any_unsafe = true;
580						if (service.perma_option) {
581							any_perm = true;
582						}
583					}
584				}
585				if (!('language' in service)) {
586					service.language = this_options.language;
587				}
588				if (!('path_prefix' in service)) {
589					service.path_prefix = this_options.path_prefix;
590				}
591				if (!('referrer_track' in service)) {
592					service.referrer_track = '';
593				}
594			}
595			unordered.sort();
596			order = order.concat(unordered);
597
598			// check if at least one service is activated
599			if (!any_on) {
600				return;
601			}
602
603			// insert stylesheet into document and prepend target element
604			if (this_options.css_path) {
605				var css_path = (this_options.path_prefix||"") + this_options.css_path;
606				// IE fix (needed for IE < 9 - but done for all IE versions)
607				if (document.createStyleSheet) {
608					document.createStyleSheet(css_path);
609				} else if ($('link[href="'+css_path+'"]').length === 0) {
610					$('<link/>',{rel:'stylesheet',type:'text/css',href:css_path}).appendTo(document.head);
611				}
612			}
613
614			// get stored perma options
615			var permas;
616			if (this_options.perma_option && any_perm) {
617				if (this_options.get_perma_options) {
618					permas = this_options.get_perma_options(this_options);
619					}
620				else {
621					permas = {};
622					for (var service_name in this_options.services) {
623						permas[service_name] = this_options.get_perma_option(service_name, this_options);
624					}
625				}
626			}
627
628			// canonical uri that will be shared
629			var uri = this_options.uri;
630			if (typeof uri === 'function') {
631				uri = uri.call(this, this_options);
632			}
633
634			var $context = $('<ul class="social_share_privacy_area"></ul>').addClass(this_options.layout);
635			var $share = $(this);
636
637			$share.prepend($context).data('social-share-privacy-options',this_options);
638
639			for (var i = 0; i < order.length; ++ i) {
640				var service_name = order[i];
641				var service = this_options.services[service_name];
642
643				if (service && service.status) {
644					var class_name = service.class_name || service_name;
645					var button_class = service.button_class || service_name;
646					var $help_info;
647
648					if (service.privacy === 'safe') {
649						$help_info = $('<li class="help_info"><div class="info">' +
650							service.txt_info + '</div><div class="dummy_btn"></div></li>').addClass(class_name);
651						$help_info.find('.dummy_btn').
652							addClass(button_class).
653							append(service.button.call(this,service,uri,this_options));
654					}
655					else {
656						$help_info = $('<li class="help_info"><div class="info">' +
657							service.txt_info + '</div><span class="switch off">' + (service.txt_off||'\u00a0') +
658							'</span><div class="dummy_btn"></div></li>').addClass(class_name);
659						$help_info.find('.dummy_btn').
660							addClass(button_class).
661							append($('<img/>').addClass(button_class+'_privacy_dummy privacy_dummy').
662								attr({
663									alt: service.dummy_alt,
664									src: service.path_prefix + service[dummy_img]
665								}));
666
667						$help_info.find('.dummy_btn img.privacy_dummy, span.switch').click(
668							buttonClickHandler(service_name));
669					}
670					$context.append($help_info);
671				}
672			}
673
674			//
675			// append Info/Settings-area
676			//
677			if (any_unsafe) {
678				var $settings_info = $('<li class="settings_info"><div class="settings_info_menu off perma_option_off"><a>' +
679					'<span class="help_info icon"><span class="info">' + this_options.txt_help + '</span></span></a></div></li>');
680				var $info_link = $settings_info.find('> .settings_info_menu > a').attr('href', this_options.info_link);
681				if (this_options.info_link_target) {
682					$info_link.attr("target",this_options.info_link_target);
683				}
684				$context.append($settings_info);
685
686				$context.find('.help_info').on('mouseenter', enterHelpInfo).on('mouseleave', leaveHelpInfo);
687
688				// menu for permanently enabling of service buttons
689				if (this_options.perma_option && any_perm) {
690
691					// define container
692					var $container_settings_info = $context.find('li.settings_info');
693
694					// remove class that fomrats the i-icon, because perma-options are shown
695					var $settings_info_menu = $container_settings_info.find('.settings_info_menu');
696					$settings_info_menu.removeClass('perma_option_off');
697
698					// append perma-options-icon (.settings) and form (hidden)
699					$settings_info_menu.append(
700						'<span class="settings">' + this_options.txt_settings + '</span><form><fieldset><legend>' +
701						this_options.settings_perma + '</legend></fieldset></form>');
702
703					// write services with <input> and <label> and checked state from cookie
704					var $fieldset = $settings_info_menu.find('form fieldset');
705					for (var i = 0; i < order.length; ++ i) {
706						var service_name = order[i];
707						var service = this_options.services[service_name];
708
709						if (service && service.status && service.perma_option && service.privacy !== 'safe') {
710							var class_name = service.class_name || service_name;
711							var perma = permas[service_name];
712							var $field = $('<label><input type="checkbox"' + (perma ? ' checked="checked"/>' : '/>') +
713								service.display_name + '</label>');
714							$field.find('input').attr('data-service', service_name);
715							$fieldset.append($field);
716
717							// enable services when cookie set and refresh cookie
718							if (perma) {
719								$context.find('li.'+class_name+' span.switch').click();
720								this_options.set_perma_option(service_name, this_options);
721							}
722						}
723					}
724
725					// indicate clickable setings gear
726					$container_settings_info.find('span.settings').css('cursor', 'pointer');
727
728					// show settings menu on hover
729					$container_settings_info.on('mouseenter', enterSettingsInfo).on('mouseleave', leaveSettingsInfo);
730
731					// interaction for <input> to enable services permanently
732					$container_settings_info.find('fieldset input').on('change', permCheckChangeHandler);
733				}
734			}
735			$share.trigger({type: 'socialshareprivacy:create', options: this_options});
736		});
737	};
738
739	// expose helper functions:
740	socialSharePrivacy.absurl     = absurl;
741	socialSharePrivacy.escapeHtml = escapeHtml;
742	socialSharePrivacy.getTitle   = getTitle;
743	socialSharePrivacy.getImage   = getImage;
744	socialSharePrivacy.getEmbed   = getEmbed;
745	socialSharePrivacy.getDescription = getDescription;
746	socialSharePrivacy.abbreviateText = abbreviateText;
747	socialSharePrivacy.formatNumber   = formatNumber;
748
749	socialSharePrivacy.settings = {
750		'services'          : {},
751		'info_link'         : 'http://panzi.github.com/SocialSharePrivacy/',
752		'info_link_target'  : '',
753		'txt_settings'      : 'Settings',
754		'txt_help'          : 'If you activate these fields via click, data will be sent to a third party (Facebook, Twitter, Google, ...) and stored there. For more details click <em>i</em>.',
755		'settings_perma'    : 'Permanently enable share buttons:',
756		'layout'            : 'line', // possible values: 'line' (~120x20) or 'box' (~58x62)
757		'set_perma_option'  : setPermaOption,
758		'del_perma_option'  : delPermaOption,
759		'get_perma_options' : getPermaOptions,
760		'get_perma_option'  : getPermaOption,
761		'perma_option'      : !!$.cookie,
762		'cookie_path'       : '/',
763		'cookie_domain'     : document.location.hostname,
764		'cookie_expires'    : 365,
765		'path_prefix'       : '',
766		'css_path'          : "stylesheets/jquery.socialshareprivacy.css",
767		'uri'               : getURI,
768		'language'          : 'en',
769		'ignore_fragment'   : true
770	};
771
772	$.fn.socialSharePrivacy = socialSharePrivacy;
773}(jQuery));
774