PageSourceSearch

https://www.republiquetogolaise.tg/components/com_jfbconnect/includes/jfbconnect.js?v=6

js republiquetogolaise.tg collected 2026-10-07 19:32:18 UTC 47,521 bytes, 1,089 lines download raw bytes

1/**
2 * @package JFBConnect
3 * @copyright (C) 2009-2017 by Source Coast - All rights reserved
4 * @website http://www.sourcecoast.com/
5 * @website http://www.sourcecoast.com/joomla-facebook/
6 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
7 */
8
9/**
10 * Ensure global _gaq Google Anlaytics queue has be initialized.
11 * @type {Array}
12 */
13;
14var _gaq = _gaq || [];
15
16var jfbc = {
17    base: null,
18    return_url: null,
19    token: 't', // have a default, but this should be filled in
20    login: {
21        show_modal: false,
22        scope: null, // Permissions JFBConnect requests
23        logout_facebook: false,
24        logged_in: true,
25        login_started: false, // Prevents multiple concurrent login attempts, mainly caused by the auto-login feature enabled
26        use_popup: true,
27
28        provider: function (name)
29        {
30            if ((name == 'facebook') && jfbc.login.use_popup)
31            {
32                jfbc.debug.log('using popup' + jfbc.login.use_popup);
33                jfbc.login.facebook();
34            }
35            else
36                self.location = jfbc.base + 'index.php?option=com_jfbconnect&task=authenticate.login&provider=' + name + '&return=' + jfbc.return_url + '&' + jfbc.token + '=1';
37        },
38
39        // Special method for Facebook. Should not be called directly in case it changes. Use jfbc.login.provider('facebook') instead
40        facebook: function ()
41        {
42            FB.login(function (response)
43            {
44                if (!jfbc.login.logged_in)
45                    jfbc.login.facebook_onlogin();
46                else
47                    jfbc.permissions.fetch();
48            }, {
49                scope: jfbc.login.scope
50            });
51        },
52
53        // v5.2 - Move to jfbc.login.provider(xyz)
54        google: function ()
55        {
56            jfbc.login.provider('google');
57        },
58
59        // Deprecated. Use jfbc.login.login_custom();
60        login_custom: function ()
61        {
62            jfbc.debug.log("jfbc.login.login_custom is deprecated. Please use jfbc.login.provider('facebook') instead");
63            jfbc.login.provider('facebook');
64
65        },
66
67        // Action to perform after authentication on FB has occurred
68        facebook_onlogin: function ()
69        {
70            if (!jfbc.login.login_started)
71            {
72                jfbc.login.login_started = true;
73                FB.getLoginStatus(function (response)
74                {
75                    if (response.status === 'connected')
76                    {
77                        jfbcJQuery(document).one("jfbc-permissions-fetched", function ()
78                        {
79                            jfbc.debug.log("Login checks");
80//                            if (jfbc.permissions.check('email'))
81//                            {
82                            jfbc.debug.log("Logging in");
83                            if (jfbc.login.show_modal == '1')
84                            {
85                                // First, hide the SCLogin modal if it's there
86                                jfbcJQuery('#login-modal').modal('hide');
87                                jfbcJQuery("#jfbcLoginModal").css({"margin-left": function ()
88                                {
89                                    return -(jfbcJQuery("#jfbcLoginModal").width() / 2)
90                                }});
91                                jfbcJQuery("#jfbcLoginModal").modal();
92                            }
93                            self.location = jfbc.base + 'index.php?option=com_jfbconnect&task=authenticate.login&provider=facebook&return=' + jfbc.return_url + '&' + jfbc.token + '=1';
94//                            }
95                            jfbc.debug.log("Done with checks");
96                        });
97                        // Start the check_permissions asynchronous check. This will fire the code above.
98                        jfbc.permissions.fetch();
99                    }
100                });
101            }
102            jfbc.login.login_started = false;
103        },
104
105        logout_button_click: function ()
106        {
107            if (jfbc.login.logout_facebook)
108            {
109                FB.getLoginStatus(function (response)
110                {
111                    if (response.status === 'connected')
112                    {
113                        FB.logout(function (response)
114                        {
115                            jfbcJQuery(document).trigger("jfbc-provider-logout-done");
116                        });
117                    }
118                    else
119                    {
120                        jfbcJQuery(document).trigger("jfbc-provider-logout-done");
121                    }
122                });
123            }
124            else
125            {
126                jfbcJQuery(document).trigger("jfbc-provider-logout-done");
127            }
128        },
129        logout: function (redirect)
130        {
131            jfbcJQuery(document).one("jfbc-provider-logout-done", function ()
132            {
133                window.location = jfbc.base + 'index.php?option=com_users&task=user.logout&return=' + redirect + '&' + jfbc.token + '=1';
134            });
135            jfbc.login.logout_button_click();
136        }
137    },
138    permissions: {
139        // scope: comma-separated list of permissions to check
140        check: function (scope)
141        {
142            var c = jfbc.cookie.get('jfbconnect_permissions_granted');
143            if (c === null)
144                return false;
145
146            var permissions = [];
147            permissions = jfbcJQuery.parseJSON(c);
148
149            var checkScope = scope.split(',');
150            var scopeFound = true;
151            jfbcJQuery.each(checkScope, function (k, v)
152            {
153                if (jfbcJQuery.inArray(v, permissions) == -1)
154                    scopeFound = false;
155            });
156            return scopeFound;
157        },
158
159        // Called at initialization
160        // If called independently, use the jfbcJQuery.one("jfbc-permissions-fetched", ..) code to check new values
161        fetch: function ()
162        {
163            jfbc.debug.log("permissions_fetch");
164
165            FB.api('/me/permissions', function (response)
166            {
167                var permissions = "";
168                if (response.data !== undefined && jfbcJQuery.isArray(response.data))
169                {
170                    jfbcJQuery.each(response.data, function (k, v)
171                    {
172                        // Check for v2.0 of Graph API
173                        if ('permission' in v)
174                        {
175                            if (v.status == "granted")
176                                permissions = permissions + '","' + v.permission;
177                        }
178                        else
179                        {
180                            jfbcJQuery.each(v, function (perm, value)
181                            {
182                                permissions = permissions + '","' + perm;
183                            });
184                        }
185                    });
186                    // Can't use JSON.stringify as it's incompatible with IE7 :(
187                    permissions = permissions + '"';
188                    permissions = permissions.substring(2, permissions.length);
189
190                    jfbc.cookie.set('jfbconnect_permissions_granted', "[" + permissions + "]");
191                }
192                jfbcJQuery(document).trigger("jfbc-permissions-fetched");
193            });
194        },
195        // newScope = comma-separated list of scope to request
196        update_scope: function (newScope)
197        {
198            var jfbcScope = jfbc.login.scope.split(',');
199            newScope = newScope.split(',');
200            newScope = jfbcJQuery.merge(jfbcScope, newScope);
201            newScope = jfbcJQuery.grep(newScope, function (v, k)
202            { // Create a new scope array with no duplicates
203                return jfbcJQuery.inArray(v, newScope) === k;
204            });
205            jfbc.login.scope = newScope.join(',');
206            jfbc.debug.log("update_scope: Now set to " + jfbc.login.scope);
207        }
208    },
209
210    social: {
211        facebook: {
212            comment: {
213                create: function (response)
214                {
215                    var title = window.btoa(document.title);
216                    var url = 'option=com_jfbconnect&task=social.comment&type=create&href=' + encodeURIComponent(escape(response.href)) + '&commentID=' + response.commentID + '&title=' + title;
217                    jfbc.util.ajax(url, null);
218                },
219                remove: function (response)
220                {
221                    var title = window.btoa(document.title);
222                    var url = 'option=com_jfbconnect&task=social.comment&type=remove&href=' + encodeURIComponent(escape(response.href)) + '&commentID=' + response.commentID + '&title=' + title;
223                    jfbc.util.ajax(url, null);
224                }
225            },
226            like: {
227                create: function (response)
228                {
229                    var title = window.btoa(document.title);
230                    var url = 'option=com_jfbconnect&task=social.share&provider=facebook&share=like&type=create&href=' + encodeURIComponent(escape(response)) + '&title=' + title;
231                    jfbc.util.ajax(url, null);
232                },
233                remove: function (response)
234                {
235                    var title = window.btoa(document.title);
236                    var url = 'option=com_jfbconnect&task=social.share&provider=facebook&share=like&type=remove&href=' + encodeURIComponent(escape(response)) + '&title=' + title;
237                    jfbc.util.ajax(url, null);
238                }
239            }
240        },
241        google: {
242            plusone: function (response)
243            {
244                var type;
245                if (response.state == "on")
246                    type = "create";
247                else if (response.state == "off")
248                    type = "remove";
249                else
250                    return;
251
252                var url = 'option=com_jfbconnect&task=social.share&provider=google&share=plusone&type=' + type + '&href=' + encodeURIComponent(escape(response.href)) + '&title=' + document.title;
253                jfbc.util.ajax(url, null);
254            }
255        },
256        linkedin: {
257            share: function ()
258            {
259                var href = document.location.href;
260                var url = 'option=com_jfbconnect&task=social.share&provider=linkedin&share=share&type=create&href=' + encodeURIComponent(escape(href)) + '&title=' + document.title;
261                jfbc.util.ajax(url, null);
262            }
263        },
264        twitter: {
265            tweet: function (intentEvent)
266            {
267                if (!intentEvent) return;
268//            var id = intentEvent.tweet_id;
269//            var username = intentEvent.screen_name;
270                var href = document.location.href;
271                var url = 'option=com_jfbconnect&task=social.share&provider=twitter&share=tweet&type=create&href=' + encodeURIComponent(escape(href)) + '&title=' + document.title;
272                jfbc.util.ajax(url, null);
273            }
274        },
275        /**
276         * Tracks Facebook likes, unlikes and sends by suscribing to the Facebook
277         * JSAPI event model. Note: This will not track facebook buttons using the
278         * iFrame method.
279         */
280        googleAnalytics: {
281            trackFacebook: function ()
282            {
283                var opt_pageUrl = window.location;
284                try
285                {
286                    if (FB && FB.Event && FB.Event.subscribe)
287                    {
288                        FB.Event.subscribe('edge.create', function (targetUrl)
289                        {
290                            _gaq.push(['_trackSocial', 'facebook', 'like',
291                                targetUrl, opt_pageUrl]);
292                        });
293                        FB.Event.subscribe('edge.remove', function (targetUrl)
294                        {
295                            _gaq.push(['_trackSocial', 'facebook', 'unlike',
296                                targetUrl, opt_pageUrl]);
297                        });
298                        FB.Event.subscribe('message.send', function (targetUrl)
299                        {
300                            _gaq.push(['_trackSocial', 'facebook', 'send',
301                                targetUrl, opt_pageUrl]);
302                        });
303                        FB.Event.subscribe('comment.create', function (targetUrl)
304                        {
305                            _gaq.push(['_trackSocial', 'facebook', 'comment',
306                                targetUrl, opt_pageUrl]);
307                        });
308                        FB.Event.subscribe('comment.remove', function (targetUrl)
309                        {
310                            _gaq.push(['_trackSocial', 'facebook', 'uncomment',
311                                targetUrl, opt_pageUrl]);
312                        });
313                    }
314                }
315                catch (e)
316                {
317                }
318            }
319        },
320
321        // Not published yet. Need to figure out the best way to incorporate this into pages
322        feedPost: function (title, caption, description, url, picture)
323        {
324//            javascript:jfbc.social.feedPost('SourceCoast JE', 'My caption', 'Great extensions!', 'http://www.sourcecoast.com/', 'https://www.sourcecoast.com/images/stories/extensions/jfbconnect/home_jfbconn.png');
325            var obj = {
326                method: 'feed',
327                link: url,
328                picture: picture,
329                name: title, // page title?
330                caption: caption,
331                description: description
332            };
333
334            function callback(response)
335            {
336            }
337
338            FB.ui(obj, callback);
339        },
340
341        share: function ()
342        {
343            var element = jfbcJQuery('.jfbcsocialshare');
344            if (!element.length)
345                element = jfbcJQuery('<div class="jfbcsocialshare"></div>').appendTo('body');
346            jfbcJQuery('.jfbcsocialshare').fadeIn(1000);
347        }
348    },
349
350    canvas: {
351        checkFrame: function ()
352        {
353            if (top == window)
354            { // crude check for any frame
355                if (window.location.search == "")
356                    top.location.href = window.location.href + '?jfbcCanvasBreakout=1';
357                else
358                    top.location.href = window.location.href + '&jfbcCanvasBreakout=1';
359            }
360        }
361    },
362
363    request: {
364        currentId: null,
365        popup: function (jfbcReqId)
366        {
367            jfbc.request.currentId = jfbcReqId;
368            data = jfbcRequests[jfbcReqId];
369            FB.ui({method: 'apprequests', display: 'popup',
370                message: data.message,
371                title: data.title,
372                data: jfbcReqId
373            }, jfbc.request.fbCallback);
374
375        },
376
377        fbCallback: function (response)
378        {
379            if (response != null)
380            {
381                var rId = response.request;
382                var to = response.to;
383
384                var toQuery = "";
385                for (var i = 0; i < to.length; i++)
386                    toQuery += "&to[]=" + to[i];
387
388                var query = 'option=com_jfbconnect&task=request.requestSent&requestId=' + rId + toQuery + '&jfbcId=' + jfbc.request.currentId;
389                jfbc.util.ajax(query, jfbc.request.redirectToThanks);
390            }
391        },
392
393        redirectToThanks: function ()
394        {
395            data = jfbcRequests[jfbc.request.currentId];
396            if (data.thanksUrl != "" && (window.location.href != data.thanksUrl))
397                window.location.href = data.thanksUrl;
398        }
399    },
400
401    opengraph: {
402        triggerAction: function (action, href, params)
403        {
404            if (params != undefined)
405            {
406                params = {params: params}; // Wrap it in an extra parameters object so that the query params become an array keyed off params
407                params = jfbcJQuery.param(params);
408            }
409            else
410                params = '';
411
412            var url = 'option=com_jfbconnect&task=opengraph.ajaxAction&action=' + action + '&href=' + encodeURIComponent(href) + '&' + params;
413            jfbc.debug.log('opengraph.triggerAction url: ' + url);
414            jfbc.util.ajax(url, jfbc.opengraph.actionPopup);
415        },
416        actionPopup: function (message)
417        {
418            if (message != '')
419            {
420                jfbc.debug.log("Open Graph Action response: " + message);
421                var element = jfbcJQuery('#ogActionPopup');
422                if (!element.length)
423                    element = jfbcJQuery('<div id="ogActionPopup"></div>').appendTo('body');
424                element.html(message).hide();
425                jfbcJQuery('#ogActionPopup').fadeIn(1000);
426                setTimeout("jfbcJQuery('#ogActionPopup').fadeOut(1000)", 15000);
427            }
428        }
429    },
430
431    share: {
432        messages: {
433            action_text: "Post to Timeline",
434            close: "Close",
435            custom_message: "Message",
436            custom_message_placeholder: "Write something about the %s",
437            error: "Error",
438            success: "Posted to Facebook!",
439            view_on_facebook: "View %s story on Facebook"
440        },
441        build_with_at_text: function ()
442        {
443            var message_data = jfbcJQuery("#composer-message-data");
444            if (message_data.length === 0)
445            {
446                return;
447            }
448            var component = jfbcJQuery("<span />");
449            if (jfbc.share.friends.tagged !== undefined && !jfbcJQuery.isEmptyObject(jfbc.share.friends.tagged))
450            {
451                var friends = jfbcJQuery("<span />").addClass("friends").text(" " + jfbc.share.friends.autocomplete.messages.intro_text + " ");
452                jfbcJQuery.each(jfbc.share.friends.tagged, function (id, values)
453                {
454                    friends.append(jfbcJQuery("<a />").addClass("friend").attr({href: values.link, target: "_blank"}).text(values.name).data("fbid", id));
455                });
456                if (!friends.is(":empty"))
457                {
458                    component.append(friends);
459                }
460                friends = null;
461            }
462            if (jfbc.share.place.tagged !== undefined)
463            {
464                component.append(jfbcJQuery("<span />").addClass("place").text(" " + jfbc.share.place.autocomplete.messages.intro_text + " ").append(jfbcJQuery("<a />").attr({href: jfbc.share.place.tagged.link, target: "_blank"}).text(jfbc.share.place.tagged.name)));
465            }
466
467            // clear any previously stored elements and their handlers
468            message_data.empty();
469            if (component.is(":empty"))
470            {
471                // hide the extra component
472                message_data.hide();
473            } else
474            {
475                message_data.show();
476                message_data.append(component.html());
477            }
478        },
479
480        story_uri: function (story_id)
481        {
482            if (jfbc.user.link !== undefined)
483            {
484                return jfbc.user.link + "/activity/" + story_id;
485            } else
486            {
487                return "https://www.facebook.com/" + story_id;
488            }
489        },
490        form_handler: function (e)
491        {
492            if (e.preventDefault)
493            {
494                e.preventDefault(); // Don't POST the form
495            }
496            var params = {
497                message: jfbc.util.encode_data(jfbcJQuery.trim(jfbcJQuery("#composer-message").val())),
498                explicitly_shared: jfbcJQuery.trim(jfbcJQuery("#explicitly_shared").val())
499            };
500            var actionId = jfbcJQuery("#jfbc-action-id").val()
501            if (jfbc.share.place.tagged !== undefined && jfbc.share.place.tagged.id !== undefined)
502            {
503                params.place = jfbc.share.place.tagged.id;
504            }
505            if (jfbc.share.friends.tagged !== undefined && !jfbcJQuery.isEmptyObject(jfbc.share.friends.tagged))
506            {
507                params.tags = Object.keys(jfbc.share.friends.tagged).join(",");
508            }
509            jfbcJQuery("#jfbc-share-modal").modal("hide");
510            jfbc.opengraph.triggerAction(actionId, window.location, params);
511            // Disable automatically now. Need to do this only if the action is successful
512            //jfbcJQuery('button#share-button').addClass('disabled');
513            return false;
514        },
515
516        display_modal: function (actionId, capabilities)
517        {
518            jfbc.debug.log(capabilities);
519            if (jfbc.login.logged_in)
520            {
521                if (jfbc.permissions.check('publish_actions')) // Check for permission, and request if not granted
522                {
523                    jfbc.debug.log("display_modal: Permission found! Showing.");
524                    jfbc.share.show_composer(actionId, capabilities);
525                    return true;
526                } else
527                {
528                    jfbcJQuery(document).one("jfbc-permissions-fetched", function ()
529                    {
530                        jfbc.debug.log("display_modal: Permission fetched and found! Showing.");
531                        if (jfbc.permissions.check('publish_actions')) // Check for permission, and request if not granted
532                            jfbc.share.show_composer(actionId, capabilities);
533                    });
534                }
535            }
536            // If we get here, we're either not logged in or don't have the right permissions.
537            // Log the user in, or update their permissions and auto-show the modal (using the on-event above)
538            jfbc.permissions.update_scope('publish_actions');
539            jfbc.login.provider('facebook');
540            return false;
541        },
542
543        show_composer: function (actionId, capabilities)
544        {
545            jfbc.util.loadJQueryUi();
546            /*            if (!jfbc.share.jquery_ui_loaded)
547             {
548             jfbcJQuery.getScript(jfbc.base + "media/sourcecoast/js/jquery-ui.min.js").done(function ()
549             {
550             jfbc.share.jquery_ui_loaded = true;
551             jfbc.share.add_composer(actionId, capabilities);
552             });
553             }
554             else*/
555            jfbc.share.add_composer(actionId, capabilities);
556        },
557
558        // add the modal code to the page and pop it up. Do not call directly. Use display_modal above.
559        add_composer: function (actionId, capabilities)
560        {
561            jfbc.share.friends.tagged = {};
562            jfbc.debug.log("add_composer: Action " + actionId + ", capabilities: " + capabilities);
563
564            var form = jfbcJQuery("<form />").attr({id: "jfbc-share-composer"}).submit(jfbc.share.form_handler);
565            form.append(jfbcJQuery("<input />").attr({"id": "jfbc-action-id", "type": "hidden"}).val(actionId));
566
567            var modal_body = jfbcJQuery("<div />").addClass("modal-body");
568            if (jfbcJQuery.inArray("messages", capabilities) != -1)
569            {
570                modal_body.append(jfbcJQuery("<div />").attr("id", "composer-message-group").addClass("control-group")
571                    .append(
572                        jfbcJQuery("<label />").addClass("control-label").attr("for", "composer-message").text(jfbc.share.messages.custom_message)
573                    ).append(
574                        jfbcJQuery("<div />").addClass("controls").append(jfbcJQuery("<input />").addClass("input-xxlarge").attr({id: "composer-message", type: "text", maxlength: 1000, autocomplete: "off", placeholder: "Add a comment" }))
575                    ).append(
576                        jfbcJQuery("<span />").attr("id", "composer-message-data").hide())
577                );
578            }
579            if (jfbcJQuery.inArray("tags", capabilities) != -1)
580            {
581                modal_body.append(jfbcJQuery("<div />").attr("id", "autocomplete-fields").append(
582                        jfbc.share.friends.build_form_fields()
583                    ).append(
584                        jfbc.share.place.build_form_fields()
585                    ));
586                modal_body.append(jfbcJQuery("<div />").addClass("btn-group").attr("id", "composer-buttons").append(
587                        jfbc.share.place.build_toggle()
588                    ).append(
589                        jfbc.share.friends.build_toggle()
590                    ));
591            }
592            if (jfbcJQuery.inArray("explicitly_shared", capabilities) != -1)
593                form.append(jfbcJQuery("<input />").attr({"id": "explicitly_shared", "type": "hidden"}).val("true"));
594            else
595                form.append(jfbcJQuery("<input />").attr({"id": "explicitly_shared", "type": "hidden"}).val("false"));
596
597            var dataDismiss;
598            if (jfbc.jqcompat)
599                dataDismiss = 'sc-modal';
600            else
601                dataDismiss = 'modal';
602            modal = jfbcJQuery("<div />").addClass("sourcecoast modal").attr({id: "jfbc-share-modal", role: "dialog", "aria-labelledby": "modal-title"});
603            modal.append(jfbcJQuery("<div />").addClass("modal-header").append(jfbcJQuery("<button />").addClass("close").attr({type: "button", "data-dismiss": dataDismiss, "aria-hidden": "true"}).text("×")).append(jfbcJQuery("<h3 />").attr("id", "modal-title").text(jfbc.share.messages.action_text)));
604            modal.append(modal_body);
605            modal_body = null;
606            modal.append(jfbcJQuery("<div />").addClass("modal-footer").append(jfbcJQuery("<button />").addClass("btn").attr({"data-dismiss": dataDismiss, "aria-hidden": "true"}).text(jfbc.share.messages.close)).append(jfbcJQuery("<button />").addClass("btn btn-primary").attr("type", "submit").text(jfbc.share.messages.action_text)));
607            form.append(modal);
608
609            if (jfbcJQuery("#jfbc-share-composer").length) // Replace any existing modals with the newly created one
610                jfbcJQuery("#jfbc-share-composer").replaceWith(form);
611            else
612                jfbcJQuery('body').append(form);
613
614            jfbcJQuery("#jfbc-share-modal").modal();
615
616        },
617
618        // search for a place matching text near a fixed point
619        place: {
620            messages: {
621                add_location: "Add location",
622                icon: "Facebook Location icon",
623                placeholder: "Where are you?"
624            },
625            build_form_fields: function ()
626            {
627                return jfbcJQuery("<div />").attr("id", "composer-place-group").append(jfbcJQuery("<input />").attr({type: "search", role: "combobox", id: "composer-place-field", autocomplete: "off", placeholder: jfbc.share.place.messages.placeholder, "aria-label": jfbc.share.place.messages.placeholder})).hide();
628            },
629            build_toggle: function ()
630            {
631                var button = jfbcJQuery("<button />").attr({
632                    "id": "toggle-place",
633                    "type": "button",
634                    "title": jfbc.share.place.messages.add_location,
635                    "aria-controls": "composer-place-group"
636                }).addClass("btn");
637                button.append(jfbcJQuery("<img />").attr({
638                    "alt": jfbc.share.place.messages.icon,
639                    "src": jfbc.base + "media/sourcecoast/images/opengraph/location.png",
640                    "width": 32,
641                    "height": 32
642                }));
643                button.one("click", jfbc.share.place.autocomplete.init);
644                button.click(jfbc.share.place.handle_click);
645                return button;
646            },
647            handle_click: function ()
648            {
649                var button = jfbcJQuery("#toggle-place");
650                jfbcJQuery("#composer-place-group").toggle();
651                if (button.hasClass("active"))
652                {
653                    button.removeClass("active");
654                } else
655                {
656                    button.addClass("active");
657                    var friend_button = jfbcJQuery("#toggle-friends");
658                    // one active button display at a time
659                    if (friend_button.hasClass("active"))
660                    {
661                        friend_button.click();
662                    }
663                    jfbc.share.place.clear();
664                    jfbc.share.place.autocomplete.search_field.focus();
665                }
666            },
667            clear: function ()
668            {
669                delete jfbc.share.place.tagged;
670                jfbcJQuery("#composer-place-field").val("");
671                jfbc.share.build_with_at_text();
672            },
673            autocomplete: {
674                search_params: {}, // persist parameters between searches
675                messages: {
676                    were_here: "%s were here",
677                    intro_text: "at"
678                },
679                init: function ()
680                {
681                    jfbc.share.place.autocomplete.search_field = jfbcJQuery("#composer-place-field");
682                    if (jfbc.share.place.autocomplete.search_field.length === 0)
683                    {
684                        return;
685                    }
686                    jfbc.share.place.autocomplete.search_field.attr("placeholder", jfbc.share.place.autocomplete.messages.placeholder);
687                    jfbc.share.place.autocomplete.search_field.autocomplete({
688                        appendTo: "#composer-place-group",
689                        autoFocus: true,
690                        minLength: 3,
691                        focus: function (event, ui)
692                        {
693                            // add consistency between mouse and keyboard events
694                            if (event.keyCode !== undefined)
695                            {
696                                var menu = $(this).data("ui-autocomplete").menu.element, focused = menu.find("li:has(a.ui-state-focus)");
697                                menu.find(".ui-state-focus").removeClass("ui-state-focus");
698                                focused.addClass("ui-state-focus");
699                                menu = focused = null;
700                            }
701                            return false;
702                        },
703                        select: function (event, ui)
704                        {
705                            jfbc.share.place.tagged = {id: ui.item.value, name: ui.item.label, link: ui.item.link};
706                            jfbc.share.build_with_at_text();
707                            jfbc.share.place.autocomplete.search_field.autocomplete("close");
708                            jfbcJQuery("#toggle-place").click();
709                            return false;
710                        },
711                        source: function (request, response)
712                        {
713                            if (request.term === undefined || request.term.length < 3)
714                            {
715                                return;
716                            }
717                            var params = {option: 'com_jfbconnect', task: 'ajax.places', q: request.term};
718                            params[jfbc.token] = 1;
719                            jfbcJQuery.getJSON(jfbc.base + "index.php?" + jfbcJQuery.param(jfbcJQuery.extend({}, jfbc.share.place.autocomplete.search_params, params))).done(function (results)
720                            {
721                                response(results)
722                            }).fail(function ()
723                            {
724                                response([])
725                            });
726                        }
727                    });
728                    jfbc.share.place.autocomplete.search_field.data("ui-autocomplete")._renderItem = jfbc.share.place.autocomplete.renderItem;
729                    jfbc.share.place.autocomplete.search_field.attr("aria-haspopup", "true");
730                    if ("geolocation" in navigator)
731                    {
732                        navigator.geolocation.getCurrentPosition(function (position)
733                        {
734                            jfbc.share.place.autocomplete.update_coordinates(position.coords.latitude, position.coords.longitude);
735                        });
736                    }
737                },
738                // override jQuery UI autocomplete default item listing
739                renderItem: function (ul, place)
740                {
741                    var li = jfbcJQuery("<li />").addClass("place").attr({"role": "option", "aria-label": place.label}).mouseenter(function ()
742                    {
743                        jfbcJQuery(this).addClass("ui-state-focus")
744                    }).mouseleave(function ()
745                    {
746                        jfbcJQuery(this).removeClass("ui-state-focus")
747                    });
748                    if (place.picture !== undefined)
749                    {
750                        li.append(jfbcJQuery("<img />").attr({src: place.picture, alt: place.label, width: 25, height: 25}));
751                    }
752
753                    var text_pieces = [place.label];
754                    if (place.location !== undefined)
755                    {
756                        if (place.location.street !== undefined)
757                        {
758                            text_pieces.push(place.location.street);
759                        }
760                        if (place.location.area !== undefined)
761                        {
762                            text_pieces.push(place.location.area);
763                        }
764                    }
765                    li.append(jfbcJQuery("<a />").addClass("text").text(text_pieces.join(" • ")));
766
767                    if (place.were_here_count !== undefined)
768                    {
769                        li.append(jfbcJQuery("<div />").addClass("subtext").text(jfbc.share.place.autocomplete.messages.were_here.replace(/%s/i, jfbc.util.format_number(place.were_here_count))));
770                    }
771
772                    return li.appendTo(ul);
773                },
774                update_coordinates: function (latitude, longitude)
775                {
776                    if (latitude === undefined || longitude === undefined)
777                    {
778                        return;
779                    }
780                    jfbc.share.place.autocomplete.search_params.center = latitude + "," + longitude;
781                }
782            }
783        },
784
785        friends: {
786            tagged: {},
787            messages: {
788                icon: "Facebook silhouette icon",
789                tag_friends: "Tag friends",
790                placeholder: "Who are you with?"
791            },
792            build_form_fields: function ()
793            {
794                return jfbcJQuery("<div />").addClass("form-inline").attr("id", "composer-friends-group").append(jfbcJQuery("<ul />").addClass("unstyled inline").attr("id", "composer-friends-group-fields").append(jfbcJQuery("<li />").append(
795                    jfbcJQuery("<input />").attr({type: "search", role: "combobox", id: "composer-friends-field", autocomplete: "off", placeholder: jfbc.share.friends.messages.placeholder, "aria-label": jfbc.share.friends.messages.placeholder})
796                ))).hide();
797            },
798            build_toggle: function ()
799            {
800                var button = jfbcJQuery("<button />").attr({
801                    "id": "toggle-friends",
802                    "type": "button",
803                    "title": jfbc.share.friends.messages.tag_friends,
804                    "aria-controls": "composer-friends-group"
805                }).addClass("btn");
806                button.append(jfbcJQuery("<img />").attr({
807                    "alt": jfbc.share.friends.messages.icon,
808                    "src": jfbc.base + "media/sourcecoast/images/opengraph/friend.png",
809                    "width": 32,
810                    "height": 32
811                }));
812                button.one("click", jfbc.share.friends.autocomplete.init);
813                button.click(jfbc.share.friends.handle_click);
814                return button;
815            },
816            handle_click: function ()
817            {
818                var button = jfbcJQuery("#toggle-friends");
819                jfbcJQuery("#composer-friends-group").toggle();
820                if (button.hasClass("active"))
821                {
822                    button.removeClass("active");
823                } else
824                {
825                    button.addClass("active");
826                    var place_button = jfbcJQuery("#toggle-place");
827                    // one active button display at a time
828                    if (place_button.hasClass("active"))
829                    {
830                        place_button.click();
831                    }
832                    jfbc.share.friends.autocomplete.search_field.focus();
833                }
834            },
835            // search for friends matching text
836            autocomplete: {
837                messages: {
838                    intro_text: "with",
839                    remove: "Remove %s"
840                },
841                init: function ()
842                {
843                    jfbc.share.friends.autocomplete.search_field = jfbcJQuery("#composer-friends-field");
844                    if (jfbc.share.friends.autocomplete.search_field.length === 0)
845                    {
846                        return;
847                    }
848                    jfbc.share.friends.autocomplete.search_field.autocomplete({
849                        appendTo: "#composer-friends-group",
850                        autoFocus: true,
851                        minLength: 2,
852                        focus: function (event, ui)
853                        {
854                            // add consistency between mouse and keyboard events
855                            if (event.keyCode !== undefined)
856                            {
857                                var menu = $(this).data("ui-autocomplete").menu.element, focused = menu.find("li:has(a.ui-state-focus)");
858                                menu.find(".ui-state-focus").removeClass("ui-state-focus");
859                                focused.addClass("ui-state-focus");
860                                menu = focused = null;
861                            }
862                            return false;
863                        },
864                        select: function (event, ui)
865                        {
866                            jfbc.share.friends.tagged[ui.item.value] = {name: ui.item.label, link: ui.item.link};
867                            jfbc.share.build_with_at_text();
868                            jfbcJQuery("#composer-friends-group-fields").prepend(jfbcJQuery("<li />").data("fbid", ui.item.value).addClass("friend").append(jfbcJQuery("<a />").attr({href: ui.item.link, target: "_blank"}).text(ui.item.label)).append(jfbcJQuery("<button />").addClass("btn btn-link").attr({type: "button", title: jfbc.share.friends.autocomplete.messages.remove.replace(/%s/i, ui.item.label)}).text("×").click(jfbc.share.friends.autocomplete.remove_friend)));
869                            jfbc.share.friends.autocomplete.search_field.autocomplete("close");
870                            jfbc.share.friends.autocomplete.search_field.val("");
871                            jfbc.share.friends.autocomplete.search_field.focus();
872                            //jfbcJQuery("#toggle-friends").click();
873                            return false;
874                        },
875                        source: function (request, response)
876                        {
877                            if (request.term === undefined || request.term.length < 2)
878                            {
879                                return;
880                            }
881                            var params = {option: 'com_jfbconnect', task: 'ajax.friends', q: request.term};
882                            params[jfbc.token] = 1;
883                            jfbcJQuery.getJSON(jfbc.base + "index.php?" + jfbcJQuery.param(params)).done(function (results)
884                            {
885                                jfbc.debug.log(response);
886                                response(results);
887                            }).fail(function ()
888                            {
889                                jfbc.debug.log(response);
890                                response([])
891                            });
892                        }
893                    });
894                    jfbc.share.friends.autocomplete.search_field.data("ui-autocomplete")._renderItem = jfbc.share.friends.autocomplete.renderItem;
895                    jfbc.share.friends.autocomplete.search_field.attr("aria-haspopup", "true");
896                },
897                remove_friend: function ()
898                {
899                    var friend = jfbcJQuery(this).closest(".friend");
900                    if (friend.length === 0)
901                    {
902                        return;
903                    }
904                    delete jfbc.share.friends.tagged[friend.data("fbid")];
905                    friend.remove();
906                    jfbc.share.build_with_at_text();
907                    jfbc.share.friends.autocomplete.search_field.focus();
908                },
909                // override jfbcJQuery UI autocomplete default item listing
910                renderItem: function (ul, friend)
911                {
912                    return jfbcJQuery("<li />").addClass("friend").attr({"role": "option", "aria-label": friend.label}).mouseenter(function ()
913                    {
914                        jfbcJQuery(this).addClass("ui-state-focus")
915                    }).mouseleave(function ()
916                    {
917                        jfbcJQuery(this).removeClass("ui-state-focus")
918                    }).append(
919                            jfbcJQuery("<img />").attr({src: (friend.picture === undefined) ? "https:\/\/graph.facebook.com\/" + friend.value + "\/picture" : friend.picture, alt: friend.label, width: 25, height: 25})).append(
920                            jfbcJQuery("<a />").addClass("text").text(friend.label)
921                        ).appendTo(ul);
922                }
923            }
924        }
925    },
926    popup: {
927        display: function (ret)
928        {
929            var deferred = jfbcJQuery.Deferred();
930            var data = jfbcJQuery.parseJSON(ret);
931            jfbcJQuery(data.target).html(data.html);
932
933            var buttons = {};
934            if (data.buttons)
935            {
936                for (var i = 0; i < data.buttons.length; i++)
937                {
938                    var button = data.buttons[i];
939                    buttons[button.name] = {
940                        text: button.name,
941                        id: button.id,
942                        click: function ()
943                        {
944                            var action = jfbc.get(button.action);
945                            action();
946                        }
947                    };
948                }
949            }
950            buttons["Cancel"] = {
951                text: "Cancel",
952                id: 'jfbc-popup-close',
953                click: function ()
954                {
955                    jfbcJQuery(this).dialog("close");
956                }
957            };
958            jfbcJQuery(data.target).dialog({buttons: buttons, title: data.title,
959                width: '50%',
960                close: function ()
961                {
962                    jfbcJQuery(data.target).html("");
963                }
964            });
965            jfbcJQuery(data.target).css('display', 'block');
966            // Return our promise. This is always resolved (no fail), but need the promise so it can be done after AJAX calls
967            deferred.resolve();
968            return deferred;
969        }
970    },
971
972    get: function (prop)
973    {
974        var path = prop.split('.');
975        var value = window;
976        for (var i = 0; i < path.length; i++)
977        {
978            if (value[path[i]])
979                value = value[path[i]];
980        }
981        return value;
982    },
983
984    util: {
985        thousands_separator: ",",
986        // format an integer such as 1234 to a string with a thousands separator: 1,234
987        format_number: function (num)
988        {
989            return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1" + jfbc.util.thousands_separator);
990        },
991        encode_data: function (data)
992        {
993            return encodeURIComponent(data).replace(/\-/g, "%2D").replace(/\_/g, "%5F").replace(/\./g, "%2E").replace(/\!/g, "%21").replace(/\~/g, "%7E").replace(/\*/g, "%2A").replace(/\'/g, "%27").replace(/\(/g, "%28").replace(/\)/g, "%29");
994        },
995        // Abstracted originally for use in different Mootools libraries. Remains in case future jfbcJQuery changes introduced
996        ajax: function (url, callback)
997        {
998            url = url + '&' + jfbc.token + '=1';
999            return jfbcJQuery.ajax({url: jfbc.base + 'index.php', data: url}).done(callback);
1000        },
1001
1002        jqueryUiLoaded: false,
1003        loadJQueryUi: function ()
1004        {
1005            if (!jfbc.util.jqueryUiLoaded)
1006            {
1007                jfbcJQuery.getScript(jfbc.base + "media/sourcecoast/js/jquery-ui.min.js").done(function ()
1008                {
1009                    jfbc.util.jqueryUiLoaded = true;
1010                });
1011            }
1012        }
1013    },
1014    debug: {
1015        enable: 0,
1016        log: function (string)
1017        {
1018            if (jfbc.debug.enable == 1)
1019                console.log("JFBConnect logger: " + string);
1020        },
1021        stats: function ()
1022        {
1023            var element = jfbcJQuery('#jfbcAdminStats');
1024            if (!element.length)
1025                element = jfbcJQuery('<div id="jfbcAdminStats"></div>').appendTo('body');
1026            jfbcJQuery('#jfbcAdminStats').fadeIn(1000);
1027        }
1028    },
1029    cookie: {
1030        get: function (sKey)
1031        {
1032            return unescape(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
1033        },
1034        set: function (sKey, sValue)
1035        {
1036            if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey))
1037            {
1038                return false;
1039            }
1040            document.cookie = escape(sKey) + "=" + escape(sValue) + "; path=/";
1041            return true;
1042        }
1043    },
1044
1045
1046    init: function ()
1047    {
1048        if (typeof jfbcJQuery == "undefined")
1049        {
1050            jfbcJQuery = jQuery;
1051            jfbc.jqcompat = false;
1052        }
1053        else
1054            jfbc.jqcompat = true;
1055
1056        if (jfbc.login.logged_in && jfbc.login.logout_facebook)
1057        {
1058            jfbcJQuery(document).ready(function ()
1059            {
1060                jfbcJQuery(".sclogout-button form").submit(function (e)
1061                {
1062                    e.preventDefault();
1063                    jfbcJQuery(document).one("jfbc-provider-logout-done", null, {caller: this }, function (e)
1064                    {
1065                        e.data.caller.submit();
1066                    });
1067                    jfbc.login.logout_button_click();
1068                    return false;
1069                });
1070            });
1071        }
1072
1073
1074        jfbcJQuery(document).ready(function ()
1075        {
1076            jfbc.social.share();
1077            if (jfbcJQuery('#social-toolbar').length)
1078            {
1079                jfbc.util.loadJQueryUi();
1080                jfbcJQuery('#social-toolbar button').click(function ()
1081                {
1082                    var method = jfbcJQuery(this).attr("name");
1083                    jfbc.toolbar[method].display();
1084                });
1085            }
1086        });
1087    }
1088
1089};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.