Project

General

Profile

1
(function(window, $, undefined) {
2

    
3
    var JSSOCIALS = "JSSocials",
4
        JSSOCIALS_DATA_KEY = JSSOCIALS;
5

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

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

    
16
    var MEASURES = {
17
        "G": 1000000000,
18
        "M": 1000000,
19
        "K": 1000
20
    };
21

    
22
    var shares = {};
23

    
24
    function Socials(element, config) {
25
        var $element = $(element);
26

    
27
        $element.data(JSSOCIALS_DATA_KEY, this);
28

    
29
        this._$element = $element;
30

    
31
        this.shares = [];
32

    
33
        this._init(config);
34
        this._render();
35
    }
36

    
37
    Socials.prototype = {
38
        url: "",
39
        text: "",
40
        shareIn: "blank",
41

    
42
        showLabel: function(screenWidth) {
43
            return (this.showCount === false) ?
44
                (screenWidth > this.smallScreenWidth) :
45
                (screenWidth >= this.largeScreenWidth);
46
        },
47

    
48
        showCount: function(screenWidth) {
49
            return (screenWidth <= this.smallScreenWidth) ? "inside" : true;
50
        },
51

    
52
        smallScreenWidth: 640,
53
        largeScreenWidth: 1024,
54

    
55
        resizeTimeout: 200,
56

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

    
69
        _init: function(config) {
70
            this._initDefaults();
71
            $.extend(this, config);
72
            this._initShares();
73
            this._attachWindowResizeCallback();
74
        },
75

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

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

    
87
                var share = (shareConfig.share && shares[shareConfig.share]);
88

    
89
                if(!share && !shareConfig.renderer) {
90
                    throw Error("Share '" + shareConfig.share + "' is not found");
91
                }
92

    
93
                return $.extend({ url: this.url, text: this.text }, share, shareConfig);
94
            }, this));
95
        },
96

    
97
        _attachWindowResizeCallback: function() {
98
            $(window).on("resize", $.proxy(this._windowResizeHandler, this));
99
        },
100

    
101
        _detachWindowResizeCallback: function() {
102
            $(window).off("resize", this._windowResizeHandler);
103
        },
104

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

    
112
        _render: function() {
113
            this._clear();
114

    
115
            this._defineOptionsByScreen();
116

    
117
            this._$element.addClass(this.elementClass);
118

    
119
            this._$shares = $("<div>").addClass(this.sharesClass)
120
                .appendTo(this._$element);
121

    
122
            this._renderShares();
123
        },
124

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

    
131
        _renderShares: function() {
132
            $.each(this.shares, $.proxy(function(_, share) {
133
                this._renderShare(share);
134
            }, this));
135
        },
136

    
137
        _renderShare: function(share) {
138
            var $share;
139

    
140
            if($.isFunction(share.renderer)) {
141
                $share = $(share.renderer());
142
            } else {
143
                $share = this._createShare(share);
144
            }
145

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

    
152
        _createShare: function(share) {
153
            var $result = $("<div>");
154
            var $shareLink = this._createShareLink(share).appendTo($result);
155

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

    
163
            return $result;
164
        },
165

    
166
        _createShareLink: function(share) {
167
            var shareStrategy = this._getShareStrategy(share);
168

    
169
            var $result = shareStrategy.call(share, {
170
                shareUrl: this._getShareUrl(share)
171
            });
172

    
173
            $result.addClass(this.shareLinkClass)
174
                .append(this._createShareLogo(share));
175

    
176
            if(this._showLabel) {
177
                $result.append(this._createShareLabel(share));
178
            }
179

    
180
            $.each(this.on || {}, function(event, handler) {
181
                if($.isFunction(handler)) {
182
                    $result.on(event, $.proxy(handler, share));
183
                }
184
            });
185

    
186
            return $result;
187
        },
188

    
189
        _getShareStrategy: function(share) {
190
            var result = shareStrategies[share.shareIn || this.shareIn];
191

    
192
            if(!result)
193
                throw Error("Share strategy '" + this.shareIn + "' not found");
194

    
195
            return result;
196
        },
197

    
198
        _getShareUrl: function(share) {
199
            var shareUrl = getOrApply(share.shareUrl, share);
200
            return this._formatShareUrl(shareUrl, share);
201
        },
202

    
203
        _createShareLogo: function(share) {
204
            var logo = share.logo;
205

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

    
210
            $result.addClass(this.shareLogoClass);
211

    
212
            return $result;
213
        },
214

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

    
220
        _renderShareCount: function(share, $container) {
221
            var $count = $("<span>").addClass(this.shareCountClass);
222

    
223
            $container.addClass(this.shareZeroCountClass)
224
                .append($count);
225

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

    
234
        _loadCount: function(share) {
235
            var deferred = $.Deferred();
236
            var countUrl = this._getCountUrl(share);
237

    
238
            if(!countUrl) {
239
                return deferred.resolve(0).promise();
240
            }
241

    
242
            var handleSuccess = $.proxy(function(response) {
243
                deferred.resolve(this._getCountValue(response, share));
244
            }, this);
245

    
246
            $.getJSON(countUrl).done(handleSuccess)
247
                .fail(function() {
248
                    $.get(countUrl).done(handleSuccess)
249
                        .fail(function() {
250
                            deferred.resolve(0);
251
                        });
252
                });
253

    
254
            return deferred.promise();
255
        },
256

    
257
        _getCountUrl: function(share) {
258
            var countUrl = getOrApply(share.countUrl, share);
259
            return this._formatShareUrl(countUrl, share);
260
        },
261

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

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

    
275
            return number;
276
        },
277

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

    
285
        _clear: function() {
286
            window.clearTimeout(this._resizeTimer);
287
            this._$element.empty();
288
        },
289

    
290
        _passOptionToShares: function(key, value) {
291
            var shares = this.shares;
292

    
293
            $.each(["url", "text"], function(_, optionName) {
294
                if(optionName !== key)
295
                    return;
296

    
297
                $.each(shares, function(_, share) {
298
                    share[key] = value;
299
                });
300
            });
301
        },
302

    
303
        _normalizeShare: function(share) {
304
            if($.isNumeric(share)) {
305
                return this.shares[share];
306
            }
307

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

    
314
            return share;
315
        },
316

    
317
        refresh: function() {
318
            this._render();
319
        },
320

    
321
        destroy: function() {
322
            this._clear();
323
            this._detachWindowResizeCallback();
324

    
325
            this._$element
326
                .removeClass(this.elementClass)
327
                .removeData(JSSOCIALS_DATA_KEY);
328
        },
329

    
330
        option: function(key, value) {
331
            if(arguments.length === 1) {
332
                return this[key];
333
            }
334

    
335
            this[key] = value;
336

    
337
            this._passOptionToShares(key, value);
338

    
339
            this.refresh();
340
        },
341

    
342
        shareOption: function(share, key, value) {
343
            share = this._normalizeShare(share);
344

    
345
            if(arguments.length === 2) {
346
                return share[key];
347
            }
348

    
349
            share[key] = value;
350
            this.refresh();
351
        }
352
    };
353

    
354

    
355
    $.fn.jsSocials = function(config) {
356
        var args = $.makeArray(arguments),
357
            methodArgs = args.slice(1),
358
            result = this;
359

    
360
        this.each(function() {
361
            var $element = $(this),
362
                instance = $element.data(JSSOCIALS_DATA_KEY),
363
                methodResult;
364

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

    
382
        return result;
383
    };
384

    
385
    var setDefaults = function(config) {
386
        var component;
387

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

    
395
        $.extend(component, config);
396
    };
397

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

    
407
        blank: function(args) {
408
            return $("<a>").attr({ target: "_blank", href: args.shareUrl });
409
        },
410

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

    
416
    window.jsSocials = {
417
        Socials: Socials,
418
        shares: shares,
419
        shareStrategies: shareStrategies,
420
        setDefaults: setDefaults
421
    };
422

    
423
}(window, jQuery));
424

    
(1-1/2)