socialite_js-source 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,708 @@
1
+ /*!
2
+ * Socialite v2.0
3
+ * http://socialitejs.com
4
+ * Copyright (c) 2011 David Bushell
5
+ * Dual-licensed under the BSD or MIT licenses: http://socialitejs.com/license.txt
6
+ */
7
+ window.Socialite = (function(window, document, undefined)
8
+ {
9
+ 'use strict';
10
+
11
+ var uid = 0,
12
+ instances = [ ],
13
+ networks = { },
14
+ widgets = { },
15
+ rstate = /^($|loaded|complete)/,
16
+ euc = window.encodeURIComponent;
17
+
18
+ var socialite = {
19
+
20
+ settings: { },
21
+
22
+ trim: function(str)
23
+ {
24
+ return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g,'');
25
+ },
26
+
27
+ hasClass: function(el, cn)
28
+ {
29
+ return (' ' + el.className + ' ').indexOf(' ' + cn + ' ') !== -1;
30
+ },
31
+
32
+ addClass: function(el, cn)
33
+ {
34
+ if (!socialite.hasClass(el, cn)) {
35
+ el.className = (el.className === '') ? cn : el.className + ' ' + cn;
36
+ }
37
+ },
38
+
39
+ removeClass: function(el, cn)
40
+ {
41
+ el.className = socialite.trim(' ' + el.className + ' '.replace(' ' + cn + ' ', ' '));
42
+ },
43
+
44
+ /**
45
+ * Copy properties of one object to another
46
+ */
47
+ extendObject: function(to, from, overwrite)
48
+ {
49
+ for (var prop in from) {
50
+ var hasProp = to[prop] !== undefined;
51
+ if (hasProp && typeof from[prop] === 'object') {
52
+ socialite.extendObject(to[prop], from[prop], overwrite);
53
+ } else if (overwrite || !hasProp) {
54
+ to[prop] = from[prop];
55
+ }
56
+ }
57
+ },
58
+
59
+ /**
60
+ * Return elements with a specific class
61
+ *
62
+ * @param context - containing element to search within
63
+ * @param cn - class name to search for
64
+ *
65
+ */
66
+ getElements: function(context, cn)
67
+ {
68
+ // copy to a new array to avoid a live NodeList
69
+ var i = 0,
70
+ el = [ ],
71
+ gcn = !!context.getElementsByClassName,
72
+ all = gcn ? context.getElementsByClassName(cn) : context.getElementsByTagName('*');
73
+ for (; i < all.length; i++) {
74
+ if (gcn || socialite.hasClass(all[i], cn)) {
75
+ el.push(all[i]);
76
+ }
77
+ }
78
+ return el;
79
+ },
80
+
81
+ /**
82
+ * Return data-* attributes of element as a query string (or object)
83
+ *
84
+ * @param el - the element
85
+ * @param noprefix - (optional) if true, remove "data-" from attribute names
86
+ * @param nostr - (optional) if true, return attributes in an object
87
+ *
88
+ */
89
+ getDataAttributes: function(el, noprefix, nostr)
90
+ {
91
+ var i = 0,
92
+ str = '',
93
+ obj = { },
94
+ attr = el.attributes;
95
+ for (; i < attr.length; i++) {
96
+ var key = attr[i].name,
97
+ val = attr[i].value;
98
+ if (val.length && key.indexOf('data-') === 0) {
99
+ if (noprefix) {
100
+ key = key.substring(5);
101
+ }
102
+ if (nostr) {
103
+ obj[key] = val;
104
+ } else {
105
+ str += euc(key) + '=' + euc(val) + '&';
106
+ }
107
+ }
108
+ }
109
+ return nostr ? obj : str;
110
+ },
111
+
112
+ /**
113
+ * Copy data-* attributes from one element to another
114
+ *
115
+ * @param from - element to copy from
116
+ * @param to - element to copy to
117
+ * @param noprefix - (optional) if true, remove "data-" from attribute names
118
+ * @param nohyphen - (optional) if true, convert hyphens to underscores in the attribute names
119
+ *
120
+ */
121
+ copyDataAttributes: function(from, to, noprefix, nohyphen)
122
+ {
123
+ // `nohyphen` was needed for Facebook's <fb:like> elements - remove as no longer used?
124
+ var attr = socialite.getDataAttributes(from, noprefix, true);
125
+ for (var i in attr) {
126
+ to.setAttribute(nohyphen ? i.replace(/-/g, '_') : i, attr[i]);
127
+ }
128
+ },
129
+
130
+ /**
131
+ * Create iframe element
132
+ *
133
+ * @param src - iframe URL (src attribute)
134
+ * @param instance - (optional) socialite instance to activate on iframe load
135
+ *
136
+ */
137
+ createIframe: function(src, instance)
138
+ {
139
+ // Socialite v2 has slashed the amount of manual iframe creation, we should aim to avoid this entirely
140
+ var iframe = document.createElement('iframe');
141
+ iframe.style.cssText = 'overflow: hidden; border: none;';
142
+ socialite.extendObject(iframe, { src: src, allowtransparency: 'true', frameborder: '0', scrolling: 'no' }, true);
143
+ if (instance) {
144
+ iframe.onload = iframe.onreadystatechange = function ()
145
+ {
146
+ if (rstate.test(iframe.readyState || '')) {
147
+ iframe.onload = iframe.onreadystatechange = null;
148
+ socialite.activateInstance(instance);
149
+ }
150
+ };
151
+ }
152
+ return iframe;
153
+ },
154
+
155
+ /**
156
+ * Returns true if network script has loaded
157
+ */
158
+ networkReady: function(name)
159
+ {
160
+ return networks[name] ? networks[name].loaded : undefined;
161
+ },
162
+
163
+ /**
164
+ * Append network script to the document
165
+ */
166
+ appendNetwork: function(network)
167
+ {
168
+ // the activation process is getting a little confusing for some networks
169
+ // it would appear a script load event does not mean its global object exists yet
170
+ // therefore the first call to `activateAll` may have no effect whereas the second call does, e.g. via `window.twttr.ready`
171
+
172
+ if (!network || network.appended) {
173
+ return;
174
+ }
175
+ // `network.append` and `network.onload` can cancel progress
176
+ if (typeof network.append === 'function' && network.append(network) === false) {
177
+ network.appended = network.loaded = true;
178
+ socialite.activateAll(network);
179
+ return;
180
+ }
181
+
182
+ if (network.script) {
183
+ network.el = document.createElement('script');
184
+ socialite.extendObject(network.el, network.script, true);
185
+ network.el.async = true;
186
+ network.el.onload = network.el.onreadystatechange = function()
187
+ {
188
+ if (rstate.test(network.el.readyState || '')) {
189
+ network.el.onload = network.el.onreadystatechange = null;
190
+ network.loaded = true;
191
+ if (typeof network.onload === 'function' && network.onload(network) === false) {
192
+ return;
193
+ }
194
+ socialite.activateAll(network);
195
+ }
196
+ };
197
+ document.body.appendChild(network.el);
198
+ }
199
+ network.appended = true;
200
+ },
201
+
202
+ /**
203
+ * Remove network script from the document
204
+ */
205
+ removeNetwork: function(network)
206
+ {
207
+ if (!socialite.networkReady(network.name)) {
208
+ return false;
209
+ }
210
+ if (network.el.parentNode) {
211
+ network.el.parentNode.removeChild(network.el);
212
+ }
213
+ return !(network.appended = network.loaded = false);
214
+ },
215
+
216
+ /**
217
+ * Remove and re-append network script to the document
218
+ */
219
+ reloadNetwork: function(name)
220
+ {
221
+ // This is a last-ditch effort for half-baked scripts
222
+ var network = networks[name];
223
+ if (network && socialite.removeNetwork(network)) {
224
+ socialite.appendNetwork(network);
225
+ }
226
+ },
227
+
228
+ /**
229
+ * Create new Socialite instance
230
+ *
231
+ * @param el - parent element that will hold the new instance
232
+ * @param widget - widget the instance belongs to
233
+ *
234
+ */
235
+ createInstance: function(el, widget)
236
+ {
237
+ var proceed = true,
238
+ instance = {
239
+ el : el,
240
+ uid : uid++,
241
+ widget : widget
242
+ };
243
+ instances.push(instance);
244
+ if (widget.process !== undefined) {
245
+ proceed = (typeof widget.process === 'function') ? widget.process(instance) : false;
246
+ }
247
+ if (proceed) {
248
+ socialite.processInstance(instance);
249
+ }
250
+ instance.el.setAttribute('data-socialite', instance.uid);
251
+ instance.el.className = 'socialite ' + widget.name + ' socialite-instance';
252
+ return instance;
253
+ },
254
+
255
+ /**
256
+ * Process a socialite instance to an intermediate state prior to load
257
+ */
258
+ processInstance: function(instance)
259
+ {
260
+ var el = instance.el;
261
+ instance.el = document.createElement('div');
262
+ instance.el.className = el.className;
263
+ socialite.copyDataAttributes(el, instance.el);
264
+ // stop over-zealous scripts from activating all instances
265
+ if (el.nodeName.toLowerCase() === 'a' && !el.getAttribute('data-default-href')) {
266
+ instance.el.setAttribute('data-default-href', el.getAttribute('href'));
267
+ }
268
+ var parent = el.parentNode;
269
+ parent.insertBefore(instance.el, el);
270
+ parent.removeChild(el);
271
+ },
272
+
273
+ /**
274
+ * Activate a socialite instance
275
+ */
276
+ activateInstance: function(instance)
277
+ {
278
+ if (instance && !instance.loaded) {
279
+ instance.loaded = true;
280
+ if (typeof instance.widget.activate === 'function') {
281
+ instance.widget.activate(instance);
282
+ }
283
+ socialite.addClass(instance.el, 'socialite-loaded');
284
+ return instance.onload ? instance.onload(instance.el) : null;
285
+ }
286
+ },
287
+
288
+ /**
289
+ * Activate all socialite instances belonging to a network
290
+ */
291
+ activateAll: function(network)
292
+ {
293
+ if (typeof network === 'string') {
294
+ network = networks[network];
295
+ }
296
+ for (var i = 0; i < instances.length; i++) {
297
+ var instance = instances[i];
298
+ if (instance.init && instance.widget.network === network) {
299
+ socialite.activateInstance(instance);
300
+ }
301
+ }
302
+ },
303
+
304
+ /**
305
+ * Load socialite instances
306
+ *
307
+ * @param context - (optional) containing element to search within
308
+ * @param el - (optional) individual or an array of elements to load
309
+ * @param w - (optional) widget name
310
+ * @param onload - (optional) function to call after each socialite instance has loaded
311
+ * @param process - (optional) process but don't load network (if true)
312
+ *
313
+ */
314
+ load: function(context, el, w, onload, process)
315
+ {
316
+ // use document as context if unspecified
317
+ context = (context && typeof context === 'object' && context.nodeType === 1) ? context : document;
318
+
319
+ // if no elements search within the context and recurse
320
+ if (!el || typeof el !== 'object') {
321
+ socialite.load(context, socialite.getElements(context, 'socialite'), w, onload, process);
322
+ return;
323
+ }
324
+
325
+ var i;
326
+
327
+ // if array of elements load each one individually
328
+ if (/Array/.test(Object.prototype.toString.call(el))) {
329
+ for (i = 0; i < el.length; i++) {
330
+ socialite.load(context, el[i], w, onload, process);
331
+ }
332
+ return;
333
+ }
334
+
335
+ // nothing was found...
336
+ if (el.nodeType !== 1) {
337
+ return;
338
+ }
339
+
340
+ // if widget name not specified search within the element classes
341
+ if (!w || !widgets[w]) {
342
+ w = null;
343
+ var classes = el.className.split(' ');
344
+ for (i = 0; i < classes.length; i++) {
345
+ if (widgets[classes[i]]) {
346
+ w = classes[i];
347
+ break;
348
+ }
349
+ }
350
+ if (!w) {
351
+ return;
352
+ }
353
+ }
354
+
355
+ // find or create the Socialite instance
356
+ var instance,
357
+ widget = widgets[w],
358
+ sid = parseInt(el.getAttribute('data-socialite'), 10);
359
+ if (!isNaN(sid)) {
360
+ for (i = 0; i < instances.length; i++) {
361
+ if (instances[i].uid === sid) {
362
+ instance = instances[i];
363
+ break;
364
+ }
365
+ }
366
+ } else {
367
+ instance = socialite.createInstance(el, widget);
368
+ }
369
+
370
+ // return if just processing (or no instance found)
371
+ if (process || !instance) {
372
+ return;
373
+ }
374
+
375
+ // initialise the instance
376
+ if (!instance.init) {
377
+ instance.init = true;
378
+ instance.onload = (typeof onload === 'function') ? onload : null;
379
+ widget.init(instance);
380
+ }
381
+
382
+ // append the parent network (all instances will be activated onload)
383
+ // or activate immediately if network has already loaded
384
+ if (!widget.network.appended) {
385
+ socialite.appendNetwork(widget.network);
386
+ } else {
387
+ if (socialite.networkReady(widget.network.name)) {
388
+ socialite.activateInstance(instance);
389
+ }
390
+ }
391
+ },
392
+
393
+ /**
394
+ * Load a single element
395
+ *
396
+ * @param el - an individual element
397
+ * @param w - (optional) widget for this socialite instance
398
+ * @param onload - (optional) function to call once each instance has loaded
399
+ *
400
+ */
401
+ activate: function(el, w, onload)
402
+ {
403
+ // skip the first few steps
404
+ window.Socialite.load(null, el, w, onload);
405
+ },
406
+
407
+ /**
408
+ * Process elements to an intermediate state prior to load
409
+ *
410
+ * @param context - containing element to search within
411
+ * @param el - (optional) individual or an array of elements to load
412
+ * @param w - (optional) widget name
413
+ *
414
+ */
415
+ process: function(context, el, w)
416
+ {
417
+ // stop before widget initialises instance
418
+ window.Socialite.load(context, el, w, null, true);
419
+ },
420
+
421
+ /**
422
+ * Add a new social network
423
+ *
424
+ * @param name - unique name for network
425
+ * @param params - additional data and callbacks
426
+ *
427
+ */
428
+ network: function(n, params)
429
+ {
430
+ networks[n] = {
431
+ name : n,
432
+ el : null,
433
+ appended : false,
434
+ loaded : false,
435
+ widgets : { }
436
+ };
437
+ if (params) {
438
+ socialite.extendObject(networks[n], params);
439
+ }
440
+ },
441
+
442
+ /**
443
+ * Add a new social widget
444
+ *
445
+ * @param name - name of owner network
446
+ * @param w - unique name for widget
447
+ * @param params - additional data and callbacks
448
+ *
449
+ */
450
+ widget: function(n, w, params)
451
+ {
452
+ params.name = n + '-' + w;
453
+ if (!networks[n] || widgets[params.name]) {
454
+ return;
455
+ }
456
+ params.network = networks[n];
457
+ networks[n].widgets[w] = widgets[params.name] = params;
458
+ },
459
+
460
+ /**
461
+ * Change the default Socialite settings for each network
462
+ */
463
+ setup: function(params)
464
+ {
465
+ socialite.extendObject(socialite.settings, params, true);
466
+ }
467
+
468
+ };
469
+
470
+ return socialite;
471
+
472
+ })(window, window.document);
473
+
474
+ /**
475
+ * Socialite Extensions - Pick 'n' Mix!
476
+ */
477
+ (function(window, document, Socialite, undefined)
478
+ {
479
+
480
+ // default to the Queen's English
481
+ Socialite.setup({
482
+ facebook: {
483
+ lang: 'en_GB',
484
+ appId: null
485
+ },
486
+ twitter: {
487
+ lang: 'en'
488
+ },
489
+ googleplus: {
490
+ lang: 'en-GB'
491
+ }
492
+ });
493
+
494
+
495
+ // Facebook
496
+ // http://developers.facebook.com/docs/reference/plugins/like/
497
+ // http://developers.facebook.com/docs/reference/javascript/FB.init/
498
+
499
+ Socialite.network('facebook', {
500
+ script: {
501
+ src : '//connect.facebook.net/{{language}}/all.js',
502
+ id : 'facebook-jssdk'
503
+ },
504
+ append: function(network)
505
+ {
506
+ var fb = document.createElement('div'),
507
+ settings = Socialite.settings.facebook,
508
+ events = { onlike: 'edge.create', onunlike: 'edge.remove', onsend: 'message.send' };
509
+ fb.id = 'fb-root';
510
+ document.body.appendChild(fb);
511
+ network.script.src = network.script.src.replace('{{language}}', settings.lang);
512
+ window.fbAsyncInit = function() {
513
+ window.FB.init({
514
+ appId: settings.appId,
515
+ xfbml: true
516
+ });
517
+ for (var e in events) {
518
+ if (typeof settings[e] === 'function') {
519
+ window.FB.Event.subscribe(events[e], settings[e]);
520
+ }
521
+ }
522
+ };
523
+ }
524
+ });
525
+
526
+ Socialite.widget('facebook', 'like', {
527
+ init: function(instance)
528
+ {
529
+ var el = document.createElement('div');
530
+ el.className = 'fb-like';
531
+ Socialite.copyDataAttributes(instance.el, el);
532
+ instance.el.appendChild(el);
533
+ if (window.FB && window.FB.XFBML) {
534
+ window.FB.XFBML.parse(instance.el);
535
+ }
536
+ }
537
+ });
538
+
539
+
540
+ // Twitter
541
+ // https://dev.twitter.com/docs/tweet-button/
542
+ // https://dev.twitter.com/docs/intents/events/
543
+ // https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingSocial#twitter
544
+
545
+ Socialite.network('twitter', {
546
+ script: {
547
+ src : '//platform.twitter.com/widgets.js',
548
+ id : 'twitter-wjs',
549
+ charset : 'utf-8'
550
+ },
551
+ append: function()
552
+ {
553
+ var notwttr = (typeof window.twttr !== 'object'),
554
+ settings = Socialite.settings.twitter,
555
+ events = ['click', 'tweet', 'retweet', 'favorite', 'follow'];
556
+ if (notwttr) {
557
+ window.twttr = (t = { _e: [], ready: function(f) { t._e.push(f); } });
558
+ }
559
+ window.twttr.ready(function(twttr)
560
+ {
561
+ for (var i = 0; i < events.length; i++) {
562
+ var e = events[i];
563
+ if (typeof settings['on' + e] === 'function') {
564
+ twttr.events.bind(e, settings['on' + e]);
565
+ }
566
+ }
567
+ Socialite.activateAll('twitter');
568
+ });
569
+ return notwttr;
570
+ }
571
+ });
572
+
573
+ var twitterInit = function(instance)
574
+ {
575
+ var el = document.createElement('a');
576
+ el.className = instance.widget.name + '-button';
577
+ Socialite.copyDataAttributes(instance.el, el);
578
+ el.setAttribute('href', instance.el.getAttribute('data-default-href'));
579
+ el.setAttribute('data-lang', instance.el.getAttribute('data-lang') || Socialite.settings.twitter.lang);
580
+ instance.el.appendChild(el);
581
+ };
582
+
583
+ var twitterActivate = function(instance)
584
+ {
585
+ if (window.twttr && typeof window.twttr.widgets === 'object' && typeof window.twttr.widgets.load === 'function') {
586
+ window.twttr.widgets.load();
587
+ }
588
+ };
589
+
590
+ Socialite.widget('twitter', 'share', { init: twitterInit, activate: twitterActivate });
591
+ Socialite.widget('twitter', 'follow', { init: twitterInit, activate: twitterActivate });
592
+ Socialite.widget('twitter', 'hashtag', { init: twitterInit, activate: twitterActivate });
593
+ Socialite.widget('twitter', 'mention', { init: twitterInit, activate: twitterActivate });
594
+
595
+ Socialite.widget('twitter', 'embed', {
596
+ process: function(instance)
597
+ {
598
+ instance.innerEl = instance.el;
599
+ if (!instance.innerEl.getAttribute('data-lang')) {
600
+ instance.innerEl.setAttribute('data-lang', Socialite.settings.twitter.lang);
601
+ }
602
+ instance.el = document.createElement('div');
603
+ instance.el.className = instance.innerEl.className;
604
+ instance.innerEl.className = '';
605
+ instance.innerEl.parentNode.insertBefore(instance.el, instance.innerEl);
606
+ instance.el.appendChild(instance.innerEl);
607
+ },
608
+ init: function(instance)
609
+ {
610
+ instance.innerEl.className = 'twitter-tweet';
611
+ },
612
+ activate: twitterActivate
613
+ });
614
+
615
+
616
+ // Google+
617
+ // https://developers.google.com/+/plugins/+1button/
618
+ // Google does not support IE7
619
+
620
+ Socialite.network('googleplus', {
621
+ script: {
622
+ src: '//apis.google.com/js/plusone.js'
623
+ },
624
+ append: function(network)
625
+ {
626
+ if (window.gapi) {
627
+ return false;
628
+ }
629
+ window.___gcfg = {
630
+ lang: Socialite.settings.googleplus.lang,
631
+ parsetags: 'explicit'
632
+ };
633
+ }
634
+ });
635
+
636
+ var googleplusInit = function(instance)
637
+ {
638
+ var el = document.createElement('div');
639
+ el.className = 'g-' + instance.widget.gtype;
640
+ Socialite.copyDataAttributes(instance.el, el);
641
+ instance.el.appendChild(el);
642
+ instance.gplusEl = el;
643
+ };
644
+
645
+ var googleplusEvent = function(instance, callback) {
646
+ return (typeof callback !== 'function') ? null : function(data) {
647
+ callback(instance.el, data);
648
+ };
649
+ };
650
+
651
+ var googleplusActivate = function(instance)
652
+ {
653
+ var type = instance.widget.gtype;
654
+ if (window.gapi && window.gapi[type]) {
655
+ var settings = Socialite.settings.googleplus,
656
+ params = Socialite.getDataAttributes(instance.el, true, true),
657
+ events = ['onstartinteraction', 'onendinteraction', 'callback'];
658
+ for (var i = 0; i < events.length; i++) {
659
+ params[events[i]] = googleplusEvent(instance, settings[events[i]]);
660
+ }
661
+ window.gapi[type].render(instance.gplusEl, params);
662
+ }
663
+ };
664
+
665
+ Socialite.widget('googleplus', 'one', { init: googleplusInit, activate: googleplusActivate, gtype: 'plusone' });
666
+ Socialite.widget('googleplus', 'share', { init: googleplusInit, activate: googleplusActivate, gtype: 'plus' });
667
+ Socialite.widget('googleplus', 'badge', { init: googleplusInit, activate: googleplusActivate, gtype: 'plus' });
668
+
669
+
670
+ // LinkedIn
671
+ // http://developer.linkedin.com/plugins/share-button/
672
+
673
+ Socialite.network('linkedin', {
674
+ script: {
675
+ src: '//platform.linkedin.com/in.js'
676
+ }
677
+ });
678
+
679
+ var linkedinInit = function(instance)
680
+ {
681
+ var el = document.createElement('script');
682
+ el.type = 'IN/' + instance.widget.intype;
683
+ Socialite.copyDataAttributes(instance.el, el);
684
+ instance.el.appendChild(el);
685
+ if (typeof window.IN === 'object' && typeof window.IN.parse === 'function') {
686
+ window.IN.parse(instance.el);
687
+ Socialite.activateInstance(instance);
688
+ }
689
+ };
690
+
691
+ Socialite.widget('linkedin', 'share', { init: linkedinInit, intype: 'Share' });
692
+ Socialite.widget('linkedin', 'recommend', { init: linkedinInit, intype: 'RecommendProduct' });
693
+
694
+ })(window, window.document, window.Socialite);
695
+
696
+ /**
697
+ * Execute any queued functions (don't enqueue before the document has loaded!)
698
+ */
699
+ (function() {
700
+ var s = window._socialite;
701
+ if (/Array/.test(Object.prototype.toString.call(s))) {
702
+ for (var i = 0, len = s.length; i < len; i++) {
703
+ if (typeof s[i] === 'function') {
704
+ s[i]();
705
+ }
706
+ }
707
+ }
708
+ })();