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.