Project

General

Profile

1
/*! jssocials - v1.5.0 - 2017-04-30
2
* http://js-socials.com
3
* Copyright (c) 2017 Artem Tabalin; Licensed MIT */
4
(function(window, $, undefined) {
5

    
6
    var JSSOCIALS = "JSSocials",
7
        JSSOCIALS_DATA_KEY = JSSOCIALS;
8

    
9
    var getOrApply = function(value, context) {
10
        if($.isFunction(value)) {
11
            return value.apply(context, $.makeArray(arguments).slice(2));
12
        }
13
        return value;
14
    };
15

    
16
    var IMG_SRC_REGEX = /(\.(jpeg|png|gif|bmp|svg)$|^data:image\/(jpeg|png|gif|bmp|svg\+xml);base64)/i;
17
    var URL_PARAMS_REGEX = /(&?[a-zA-Z0-9]+=)?\{([a-zA-Z0-9]+)\}/g;
18

    
19
    var MEASURES = {
20
        "G": 1000000000,
21
        "M": 1000000,
22
        "K": 1000
23
    };
24

    
25
    var shares = {};
26

    
27
    function Socials(element, config) {
28
        var $element = $(element);
29

    
30
        $element.data(JSSOCIALS_DATA_KEY, this);
31

    
32
        this._$element = $element;
33

    
34
        this.shares = [];
35

    
36
        this._init(config);
37
        this._render();
38
    }
39

    
40
    Socials.prototype = {
41
        url: "",
42
        text: "",
43
        shareIn: "blank",
44

    
45
        showLabel: function(screenWidth) {
46
            return (this.showCount === false) ?
47
                (screenWidth > this.smallScreenWidth) :
48
                (screenWidth >= this.largeScreenWidth);
49
        },
50

    
51
        showCount: function(screenWidth) {
52
            return (screenWidth <= this.smallScreenWidth) ? "inside" : true;
53
        },
54

    
55
        smallScreenWidth: 640,
56
        largeScreenWidth: 1024,
57

    
58
        resizeTimeout: 200,
59

    
60
        elementClass: "jssocials",
61
        sharesClass: "jssocials-shares",
62
        shareClass: "jssocials-share",
63
        shareButtonClass: "jssocials-share-button",
64
        shareLinkClass: "jssocials-share-link",
65
        shareLogoClass: "jssocials-share-logo",
66
        shareLabelClass: "jssocials-share-label",
67
        shareLinkCountClass: "jssocials-share-link-count",
68
        shareCountBoxClass: "jssocials-share-count-box",
69
        shareCountClass: "jssocials-share-count",
70
        shareZeroCountClass: "jssocials-share-no-count",
71

    
72
        _init: function(config) {
73
            this._initDefaults();
74
            $.extend(this, config);
75
            this._initShares();
76
            this._attachWindowResizeCallback();
77
        },
78

    
79
        _initDefaults: function() {
80
            this.url = window.location.href;
81
            this.text = $.trim($("meta[name=description]").attr("content") || $("title").text());
82
        },
83

    
84
        _initShares: function() {
85
            this.shares = $.map(this.shares, $.proxy(function(shareConfig) {
86
                if(typeof shareConfig === "string") {
87
                    shareConfig = { share: shareConfig };
88
                }
89

    
90
                var share = (shareConfig.share && shares[shareConfig.share]);
91

    
92
                if(!share && !shareConfig.renderer) {
93
                    throw Error("Share '" + shareConfig.share + "' is not found");
94
                }
95

    
96
                return $.extend({ url: this.url, text: this.text }, share, shareConfig);
97
            }, this));
98
        },
99

    
100
        _attachWindowResizeCallback: function() {
101
            $(window).on("resize", $.proxy(this._windowResizeHandler, this));
102
        },
103

    
104
        _detachWindowResizeCallback: function() {
105
            $(window).off("resize", this._windowResizeHandler);
106
        },
107

    
108
        _windowResizeHandler: function() {
109
            if($.isFunction(this.showLabel) || $.isFunction(this.showCount)) {
110
                window.clearTimeout(this._resizeTimer);
111
                this._resizeTimer = setTimeout($.proxy(this.refresh, this), this.resizeTimeout);
112
            }
113
        },
114

    
115
        _render: function() {
116
            this._clear();
117

    
118
            this._defineOptionsByScreen();
119

    
120
            this._$element.addClass(this.elementClass);
121

    
122
            this._$shares = $("<div>").addClass(this.sharesClass)
123
                .appendTo(this._$element);
124

    
125
            this._renderShares();
126
        },
127

    
128
        _defineOptionsByScreen: function() {
129
            this._screenWidth = $(window).width();
130
            this._showLabel = getOrApply(this.showLabel, this, this._screenWidth);
131
            this._showCount = getOrApply(this.showCount, this, this._screenWidth);
132
        },
133

    
134
        _renderShares: function() {
135
            $.each(this.shares, $.proxy(function(_, share) {
136
                this._renderShare(share);
137
            }, this));
138
        },
139

    
140
        _renderShare: function(share) {
141
            var $share;
142

    
143
            if($.isFunction(share.renderer)) {
144
                $share = $(share.renderer());
145
            } else {
146
                $share = this._createShare(share);
147
            }
148

    
149
            $share.addClass(this.shareClass)
150
                .addClass(share.share ? "jssocials-share-" + share.share : "")
151
                .addClass(share.css)
152
                .appendTo(this._$shares);
153
        },
154

    
155
        _createShare: function(share) {
156
            var $result = $("<div>");
157
            var $shareLink = this._createShareLink(share).appendTo($result);
158

    
159
            if(this._showCount) {
160
                var isInsideCount = (this._showCount === "inside");
161
                var $countContainer = isInsideCount ? $shareLink : $("<div>").addClass(this.shareCountBoxClass).appendTo($result);
162
                $countContainer.addClass(isInsideCount ? this.shareLinkCountClass : this.shareCountBoxClass);
163
                this._renderShareCount(share, $countContainer);
164
            }
165

    
166
            return $result;
167
        },
168

    
169
        _createShareLink: function(share) {
170
            var shareStrategy = this._getShareStrategy(share);
171

    
172
            var $result = shareStrategy.call(share, {
173
                shareUrl: this._getShareUrl(share)
174
            });
175

    
176
            $result.addClass(this.shareLinkClass)
177
                .append(this._createShareLogo(share));
178

    
179
            if(this._showLabel) {
180
                $result.append(this._createShareLabel(share));
181
            }
182

    
183
            $.each(this.on || {}, function(event, handler) {
184
                if($.isFunction(handler)) {
185
                    $result.on(event, $.proxy(handler, share));
186
                }
187
            });
188

    
189
            return $result;
190
        },
191

    
192
        _getShareStrategy: function(share) {
193
            var result = shareStrategies[share.shareIn || this.shareIn];
194

    
195
            if(!result)
196
                throw Error("Share strategy '" + this.shareIn + "' not found");
197

    
198
            return result;
199
        },
200

    
201
        _getShareUrl: function(share) {
202
            var shareUrl = getOrApply(share.shareUrl, share);
203
            return this._formatShareUrl(shareUrl, share);
204
        },
205

    
206
        _createShareLogo: function(share) {
207
            var logo = share.logo;
208

    
209
            var $result = IMG_SRC_REGEX.test(logo) ?
210
                $("<img>").attr("src", share.logo) :
211
                $("<i>").addClass(logo);
212

    
213
            $result.addClass(this.shareLogoClass);
214

    
215
            return $result;
216
        },
217

    
218
        _createShareLabel: function(share) {
219
            return $("<span>").addClass(this.shareLabelClass)
220
                .text(share.label);
221
        },
222

    
223
        _renderShareCount: function(share, $container) {
224
            var $count = $("<span>").addClass(this.shareCountClass);
225

    
226
            $container.addClass(this.shareZeroCountClass)
227
                .append($count);
228

    
229
            this._loadCount(share).done($.proxy(function(count) {
230
                if(count) {
231
                    $container.removeClass(this.shareZeroCountClass);
232
                    $count.text(count);
233
                }
234
            }, this));
235
        },
236

    
237
        _loadCount: function(share) {
238
            var deferred = $.Deferred();
239
            var countUrl = this._getCountUrl(share);
240

    
241
            if(!countUrl) {
242
                return deferred.resolve(0).promise();
243
            }
244

    
245
            var handleSuccess = $.proxy(function(response) {
246
                deferred.resolve(this._getCountValue(response, share));
247
            }, this);
248

    
249
            $.getJSON(countUrl).done(handleSuccess)
250
                .fail(function() {
251
                    $.get(countUrl).done(handleSuccess)
252
                        .fail(function() {
253
                            deferred.resolve(0);
254
                        });
255
                });
256

    
257
            return deferred.promise();
258
        },
259

    
260
        _getCountUrl: function(share) {
261
            var countUrl = getOrApply(share.countUrl, share);
262
            return this._formatShareUrl(countUrl, share);
263
        },
264

    
265
        _getCountValue: function(response, share) {
266
            var count = ($.isFunction(share.getCount) ? share.getCount(response) : response) || 0;
267
            return (typeof count === "string") ? count : this._formatNumber(count);
268
        },
269

    
270
        _formatNumber: function(number) {
271
            $.each(MEASURES, function(letter, value) {
272
                if(number >= value) {
273
                    number = parseFloat((number / value).toFixed(2)) + letter;
274
                    return false;
275
                }
276
            });
277

    
278
            return number;
279
        },
280

    
281
        _formatShareUrl: function(url, share) {
282
            return url.replace(URL_PARAMS_REGEX, function(match, key, field) {
283
                var value = share[field] || "";
284
                return value ? (key || "") + window.encodeURIComponent(value) : "";
285
            });
286
        },
287

    
288
        _clear: function() {
289
            window.clearTimeout(this._resizeTimer);
290
            this._$element.empty();
291
        },
292

    
293
        _passOptionToShares: function(key, value) {
294
            var shares = this.shares;
295

    
296
            $.each(["url", "text"], function(_, optionName) {
297
                if(optionName !== key)
298
                    return;
299

    
300
                $.each(shares, function(_, share) {
301
                    share[key] = value;
302
                });
303
            });
304
        },
305

    
306
        _normalizeShare: function(share) {
307
            if($.isNumeric(share)) {
308
                return this.shares[share];
309
            }
310

    
311
            if(typeof share === "string") {
312
                return $.grep(this.shares, function(s) {
313
                    return s.share === share;
314
                })[0];
315
            }
316

    
317
            return share;
318
        },
319

    
320
        refresh: function() {
321
            this._render();
322
        },
323

    
324
        destroy: function() {
325
            this._clear();
326
            this._detachWindowResizeCallback();
327

    
328
            this._$element
329
                .removeClass(this.elementClass)
330
                .removeData(JSSOCIALS_DATA_KEY);
331
        },
332

    
333
        option: function(key, value) {
334
            if(arguments.length === 1) {
335
                return this[key];
336
            }
337

    
338
            this[key] = value;
339

    
340
            this._passOptionToShares(key, value);
341

    
342
            this.refresh();
343
        },
344

    
345
        shareOption: function(share, key, value) {
346
            share = this._normalizeShare(share);
347

    
348
            if(arguments.length === 2) {
349
                return share[key];
350
            }
351

    
352
            share[key] = value;
353
            this.refresh();
354
        }
355
    };
356

    
357

    
358
    $.fn.jsSocials = function(config) {
359
        var args = $.makeArray(arguments),
360
            methodArgs = args.slice(1),
361
            result = this;
362

    
363
        this.each(function() {
364
            var $element = $(this),
365
                instance = $element.data(JSSOCIALS_DATA_KEY),
366
                methodResult;
367

    
368
            if(instance) {
369
                if(typeof config === "string") {
370
                    methodResult = instance[config].apply(instance, methodArgs);
371
                    if(methodResult !== undefined && methodResult !== instance) {
372
                        result = methodResult;
373
                        return false;
374
                    }
375
                } else {
376
                    instance._detachWindowResizeCallback();
377
                    instance._init(config);
378
                    instance._render();
379
                }
380
            } else {
381
                new Socials($element, config);
382
            }
383
        });
384

    
385
        return result;
386
    };
387

    
388
    var setDefaults = function(config) {
389
        var component;
390

    
391
        if($.isPlainObject(config)) {
392
            component = Socials.prototype;
393
        } else {
394
            component = shares[config];
395
            config = arguments[1] || {};
396
        }
397

    
398
        $.extend(component, config);
399
    };
400

    
401
    var shareStrategies = {
402
        popup: function(args) {
403
            return $("<a>").attr("href", "#")
404
                .on("click", function() {
405
                    window.open(args.shareUrl, null, "width=600, height=400, location=0, menubar=0, resizeable=0, scrollbars=0, status=0, titlebar=0, toolbar=0");
406
                    return false;
407
                });
408
        },
409

    
410
        blank: function(args) {
411
            return $("<a>").attr({ target: "_blank", href: args.shareUrl });
412
        },
413

    
414
        self: function(args) {
415
            return $("<a>").attr({ target: "_self", href: args.shareUrl });
416
        }
417
    };
418

    
419
    window.jsSocials = {
420
        Socials: Socials,
421
        shares: shares,
422
        shareStrategies: shareStrategies,
423
        setDefaults: setDefaults
424
    };
425

    
426
}(window, jQuery));
427

    
428

    
429
(function(window, $, jsSocials, undefined) {
430

    
431
    $.extend(jsSocials.shares, {
432

    
433
        email: {
434
            label: "E-mail",
435
            logo: "fa fa-at",
436
            shareUrl: "mailto:{to}?subject={text}&body={url}",
437
            countUrl: "",
438
            shareIn: "self"
439
        },
440

    
441
        twitter: {
442
            label: "Tweet",
443
            logo: "fa fa-twitter",
444
            shareUrl: "https://twitter.com/share?url={url}&text={text}&via={via}&hashtags={hashtags}",
445
            countUrl: ""
446
        },
447

    
448
        facebook: {
449
            label: "Like",
450
            logo: "fa fa-facebook",
451
            shareUrl: "https://facebook.com/sharer/sharer.php?u={url}",
452
            countUrl: "https://graph.facebook.com/?id={url}",
453
            getCount: function(data) {
454
                return data.share && data.share.share_count || 0;
455
            }
456
        },
457

    
458
        vkontakte: {
459
            label: "Like",
460
            logo: "fa fa-vk",
461
            shareUrl: "https://vk.com/share.php?url={url}&title={title}&description={text}",
462
            countUrl: "https://vk.com/share.php?act=count&index=1&url={url}",
463
            getCount: function(data) {
464
                return parseInt(data.slice(15, -2).split(', ')[1]);
465
            }
466
        },
467

    
468
        googleplus: {
469
            label: "+1",
470
            logo: "fa fa-google",
471
            shareUrl: "https://plus.google.com/share?url={url}",
472
            countUrl: ""
473
        },
474

    
475
        linkedin: {
476
            label: "Share",
477
            logo: "fa fa-linkedin",
478
            shareUrl: "https://www.linkedin.com/shareArticle?mini=true&url={url}",
479
            countUrl: "https://www.linkedin.com/countserv/count/share?format=jsonp&url={url}&callback=?",
480
            getCount: function(data) {
481
                return data.count;
482
            }
483
        },
484

    
485
        pinterest: {
486
            label: "Pin it",
487
            logo: "fa fa-pinterest",
488
            shareUrl: "https://pinterest.com/pin/create/bookmarklet/?media={media}&url={url}&description={text}",
489
            countUrl: "https://api.pinterest.com/v1/urls/count.json?&url={url}&callback=?",
490
            getCount: function(data) {
491
                return data.count;
492
            }
493
        },
494

    
495
        stumbleupon: {
496
            label: "Share",
497
            logo: "fa fa-stumbleupon",
498
            shareUrl: "http://www.stumbleupon.com/submit?url={url}&title={title}",
499
            countUrl:  "https://cors-anywhere.herokuapp.com/https://www.stumbleupon.com/services/1.01/badge.getinfo?url={url}",
500
            getCount: function(data) {
501
                return data.result && data.result.views;
502
            }
503
        },
504

    
505
        telegram: {
506
            label: "Telegram",
507
            logo: "fa fa-telegram",
508
            shareUrl: "tg://msg?text={url} {text}",
509
            countUrl: "",
510
            shareIn: "self"
511
        },
512

    
513
        whatsapp: {
514
            label: "WhatsApp",
515
            logo: "fa fa-whatsapp",
516
            shareUrl: "whatsapp://send?text={url} {text}",
517
            countUrl: "",
518
            shareIn: "self"
519
        },
520

    
521
        line: {
522
            label: "LINE",
523
            logo: "fa fa-comment",
524
            shareUrl: "http://line.me/R/msg/text/?{text} {url}",
525
            countUrl: ""
526
        },
527

    
528
        viber: {
529
            label: "Viber",
530
            logo: "fa fa-volume-control-phone",
531
            shareUrl: "viber://forward?text={url} {text}",
532
            countUrl: "",
533
            shareIn: "self"
534
        },
535

    
536
        pocket: {
537
            label: "Pocket",
538
            logo: "fa fa-get-pocket",
539
            shareUrl: "https://getpocket.com/save?url={url}&title={title}",
540
            countUrl: ""
541
        },
542

    
543
        messenger: {
544
            label: "Share",
545
            logo: "fa fa-commenting",
546
            shareUrl: "fb-messenger://share?link={url}",
547
            countUrl: "",
548
            shareIn: "self"
549
        },
550
        rss: {
551
            label: "RSS",
552
            logo: "fa fa-rss",
553
            shareUrl: "/feeds/",
554
            countUrl: "",
555
            shareIn: "blank"
556
        }
557

    
558
    });
559

    
560
}(window, jQuery, window.jsSocials));
561

    
(6-6/7)