From Bright Pattern Documentation
Revision as of 04:03, 29 May 2024 by BpDeeplTranslateMaintenance (talk | contribs) (Updated via BpDeleteTranslateTags script)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
• 5.19 • 5.8


Standalone TogetherJS Configuration

Quick Start

If you have Bright Pattern chat deployed on the web page, then together.js library is already loaded by the chat code. If you require to use it in a standalone manner, then include the following two items on all of your web pages that can be navigated while cobrowsing.

First the JavaScript:

<script>
  // TogetherJS configuration would go here, but we'll talk about that
  // later
</script>
<script src="https://<tenantdomain>/chat-client-json/build/togetherjs/togetherjs.js"></script>

You can put that wherever; e.g., right before </body>.

The next step is to put a button on your site that lets a user start TogetherJS:

<button onclick="TogetherJS(this); return false;">Start TogetherJS</button>

Or if you don't like onclick:

<button id="start-togetherjs">Start TogetherJS</button>
<script>
$(function () {
  $("#start-togetherjs").click(TogetherJS);
});
</script>

Calling TogetherJS() will start the tool, or stop the tool if it is already started.

You should put the togetherjs.js script on every page in your site, even if you don't include the “Start TogetherJS” button. As long as the script is on a page then two people can collaborate on that page. If you forget it on a page, then if someone visits that page while in a TogetherJS session they will essentially go “offline” until they come back to another page that includes togetherjs.js

Note that togetherjs.js is not the entire code for TogetherJS, it's only a fairly small file that loads the rest of TogetherJS on demand. You can place the <script> anywhere on the page – generally before </body> is considered the best place to put scripts.

Configuring TogetherJS

As mentioned there are several configuration parameters. To see the exact list of settings and their defaults, look at TogetherJS._defaultConfiguration in together.js

There are a couple ways of configuring TogetherJS. The one that we prefer is to set a global variable before togetherjs.js is included. Each variable is named TogetherJSConfig_*. This makes it fairly easy to add or remove variables. Note however that once togetherjs.js is loaded that these variables might not be respected. So do:

<script>
var TogetherJSConfig_something = "foo";
// more config...
</script>
<script src="https://<tenantdomain>/chat-client-json/build/togetherjs/togetherjs.js">
</script>

The other way to set a variable after TogetherJS is loaded is TogetherJS.config("variable", value). Some variables cannot be updated after TogetherJS has started, but if this is the case then you should get an error.

The Configuration

TogetherJSConfig_siteName: This is the name of your site. It defaults to the title of the page, but often a more abbreviated title is appropriate. This is used in some help text.

TogetherJSConfig_toolName: This is the name that you are giving this tool. If you use this then “TogetherJS” won't be in the UI. So you might want to use `TogetherJSConfig_toolName = “Collaboration”.

TogetherJSConfig_hubBase: This is where the hub lives. The hub is a simple server that echoes messages back and forth between clients. It‘s the only real server component of TogetherJS (besides the statically hosted scripts). It’s also really boring. If you wanted to use a hub besides ours you can override it here. The primary reason would be for privacy; though we do not look at any traffic, by hosting the hub yourself you can be more assured that it is private. You'll find that a hub with a valid https certificate is very useful, as mixed http/https is strictly forbidden with WebSockets.

TogetherJSConfig_dontShowClicks: This should be set to a jQuery selector or set to true. This will disable the visual click display indicating that a user has clicked on the defined element. For example: “canvas”, “h1”, “p”, etc. Setting TogetherJSConfig_dontShowClicks = true will globally disable all clicks.

TogetherJSConfig_cloneClicks: This should be set to a jQuery selector or set to true. Whenever someone clicks on an element matching this selector, that click will be repeated (as an actual click) on everyone else‘s browser. This is useful for cases when a click typically doesn’t do anything, but shows or hides or switches the view of the page. Note that any control that toggles will definitely not work here! If you have tab buttons that show different things you might use TogetherJSConfig_cloneClicks = ".tab". Setting TogetherJSConfig_cloneClicks = true will globally clone clicks.

TogetherJSConfig_enableShortcut: If you want to try TogetherJS out on an application, but don't want to put up a “Start TogetherJS” button, you can use TogetherJSConfig_enableShortcut = true and then an event handler will be put into place that will start TogetherJS when you hit alt-T alt-T (twice in a row!). TogetherJS will still automatically start when someone opens an invitation link.

TogetherJSConfig_useMinimizedCode: Typically if you use togetherjs.js you'll get the unminimized and uncombined code, with each module loaded lazily. If you use togetherjs.js you get the combined code. But if you want to override that more dynamically, you can use this setting.

TogetherJSConfig_findRoom: To see this in action, check out the examples. This setting assigns people to a room. If you use a single string, this will be the name of the room; for instance: TogetherJSConfig_findRoom = "my_site_com_users". You can also assign people to a series of rooms with maximum occupancy (what our examples do): TogetherJSConfig_findRoom = {prefix: "my_site_com_users", max: 5} Note: if you change this setting, test in a new tab (old browser tabs carry session information that will confuse you).

TogetherJSConfig_autoStart: If true then start TogetherJS automatically. Note TogetherJS already starts automatically when a session is continued, so if you just always call TogetherJS() then you might cause TogetherJS to stop. Note you must set this as TogetherJSConfig_autoStart = true, not using TogetherJS.config("autoStart", true) (it must be set when TogetherJS loads). Anyone who autostarts a session will not be considered the session creator.

TogetherJSConfig_suppressJoinConfirmation: When a person is invited to a session, they'll be asked if they want to join in browsing with the other person. Set this to true and they won't be asked to confirm joining. Useful when combined with findRoom.

TogetherJSConfig_suppressInvite: When a person starts a session, usually a window will pop open with the invite link so they can send it to someone. If this is true then that window doesn't automatically pop open (but it is still available).

TogetherJSConfig_inviteFromRoom: This adds the ability from the profile menu to invite people who are hanging out in another room (using TogetherJS). This is kind of (but not exactly) how the “Get Help” button works on this site. This is still an experimental feature.

TogetherJSConfig_includeHashInUrl: When true (default false), TogetherJS treats the entire URL, including the hash, as the identifier of the page; i.e., if you one person is on http://example.com/#view1 and another person is at http://example.com/#view2 then these two people are considered to be at completely different URLs. You‘d want to use this in single-page apps where being at the same base URL doesn’t mean the two people are looking at the same thing.

TogetherJSConfig_disableWebRTC: Disables/removes the button to do audio chat via WebRTC.

TogetherJSConfig_youtube: If true, then YouTube videos will be synchronized (i.e., when one person plays or pauses a video, it will play for all people). This will also load up the YouTube iframe API.

TogetherJSConfig_ignoreMessages: Contains a list of all the messages that will be ignored when console logging. Defaults to the list [“cursor-update”, “keydown”, “scroll-update”]. Will ignore all messages if set to true.

TogetherJSConfig_ignoreForms: Contains a list of all the forms that will be ignored, defaults to [“:password”]. Will ignore all forms if set to true.

Start TogetherJS Button

The button you add to your site to start TogetherJS will typically look like this:

<button id="start-togetherjs" type="button"
 onclick="TogetherJS(this); return false"
 data-end-togetherjs-html="End TogetherJS">
  Start TogetherJS
</button>
  1. If you give your button the same id across your site, TogetherJS will know what the start/end TogetherJS button is. It's not essential, but TogetherJS uses this to zoom the controls into and out of the button.
  2. onclick="TogetherJS(this); return false" – this starts TogetherJS, and by passing this TogetherJS knows what button it started from. This lets it animate out of the button. It'll also work fine with document.getElementById("start-togetherjs").addEventListener("click", TogetherJS, false)
  3. data-end-togetherjs-html is what TogetherJS will insert into the content of the button after it is started. You can use this to switch Start to End, or whatever language you use. As a special case “Start TogetherJS” is changed to “End TogetherJS”.
  4. The class togetherjs-started will be added to the button while TogetherJS is active. You might want to use this to style the background color to red to show that it changes to ending the session.

Scope of the session

TogetherJS sessions are connected to the domain you start them on (specifically the origin). So if part of your site is on another domain, people won‘t be able to talk across those domains. Even a page that is on https when another is on http will cause the session to be lost.

Extending TogetherJS

While configuration covers some of what you can do to customize TogetherJS, you may also need to integrate TogetherJS with your application, or sync your application data between clients.

Configuring events

Like other configuration, you can configure the event handlers and hooks we describe before togetherjs.js is loaded. Event handlers are just a smidge different. You'd normally add even handler like TogetherJS.on("ready", function () {...}). To do it as configuration:

TogetherJSConfig_on = {
  ready: function () {}
};

Or if you want to set things up one-by-one you can do:

TogetherJSConfig_on_ready = function () {};

Additionally you may want to add event listeners to TogetherJS.hub; these are done like:

TogetherJS_hub_on = {
  "my-event": function (msg) {
  }
};

Communication Channel

If you have a component you want to synchronize between two clients, you'll want to use the TogetherJS communication channel. This is a broadcast channel – any message you send is sent to everyone else in the session (which can also be no one), and includes people who are on different pages.

All messages are JSON objects with a type property. Custom application messages are put into their own namespace. So imagine you want to keep an element hidden or visible on all clients, in a synchronized way, and when the element visibility changes an event is fired inside your app, MyApp.emit("visibilityChange", element, isVisible):

TogetherJSConfig_on_ready = function () {
  MyApp.on("visibilityChange", fireTogetherJSVisibility);
};
TogetherJSConfig_on_close = function () {
  MyApp.off("visibilityChange", fireTogetherJSVisibility);
};

Now when TogetherJS is activated we'll call fireTogetherJSVisibility(el, isVisible). Now we have to write that function:

function fireTogetherJSVisibility(element, isVisible) {
  TogetherJS.send({type: "visibilityChange", isVisible: isVisible, element: element});
}

Well, that‘s not quite right, we have to send a JSON object, and we can’t send element. Instead we need to give an identifier for the element. TogetherJS has a helpful function for that, which will require us to import the elementFinder module:

function fireTogetherJSVisibility(element, isVisible) {
  var elementFinder = TogetherJS.require("elementFinder");
  var location = elementFinder.elementLocation(element);
  TogetherJS.send({type: "visibilityChange", isVisible: isVisible, element: location});
}

Then we also have to listen for the message. We can setup this listener right away (without using the ready/close TogetherJS events) because when TogetherJS isn't on then the event will just not fire:

TogetherJS.hub.on("visibilityChange", function (msg) {
  var elementFinder = TogetherJS.require("elementFinder");
  // If the element can't be found this will throw an exception:
  var element = elementFinder.findElement(msg.element);
  MyApp.changeVisibility(element, msg.isVisible);
});

This has two major problems though: when you call MyApp.changeVisibility it will probably fire a visibilityChange event, which will cause another fireTogetherJSVisibility call. The result may or may not be circular, but it‘s definitely not efficient. Another problem is that you can get messages from peers who are at a different URL. We’ll use a simple global variable to handle the first case, and msg.sameUrl to fix the second:

var visibilityChangeFromRemote = false;

function fireTogetherJSVisibility(element, isVisible) {
  if (visibilityChangeFromRemote) {
    return;
  }
  var elementFinder = TogetherJS.require("elementFinder");
  var location = elementFinder.elementLocation(element);
  TogetherJS.send({type: "visibilityChange", isVisible: isVisible, element: location});
}

TogetherJS.hub.on("visibilityChange", function (msg) {
  if (! msg.sameUrl) {
    return;
  }
  var elementFinder = TogetherJS.require("elementFinder");
  // If the element can't be found this will throw an exception:
  var element = elementFinder.findElement(msg.element);
  visibilityChangeFromRemote = true;
  try {
    MyApp.changeVisibility(element, msg.isVisible);
  } finally {
    visibilityChangeFromRemote = false;
  }
});

Now we‘re getting close, except for one last problem: these events sync everything when the users are on the same page, but there may be a late comer whose page won’t be in sync with everything else. An event togetherjs.hello will fire when a person appears on a new page, and we can use to that send all our state. To do this we'll imagine the MyApp object has a function like MyApp.allToggleElements() that returns a list of elements that we'd be expected to sync.

TogetherJS.hub.on("togetherjs.hello", function (msg) {
  if (! msg.sameUrl) {
    return;
  }
  MyApp.allToggleElements.forEach(function (el) {
    var isVisible = $(el).is(":visible");
    fireTogetherJSVisibility(el, isVisible);
  });
});

You‘ll notice that multiple clients might do this reset. This is an open question for us, and in the future we’ll provide a higher-level API for this kind of initialization.

Implementing those visibility function from jQuery

Let‘s say your app doesn’t have all these methods, and you are just using plain ol‘ jQuery. Here’s how you might implement them each; you'll just have to start using $(el).syncShow() and $(el).syncHide() to do your showing and hiding:

$.fn.syncShow = function () {
  this.show();
  this.trigger("visibilityChange");
};

$.fn.syncHide = function () {
  this.hide();
  this.trigger("visibilityChange");
};

$(document).on("visibilityChange", function () {
  MyApp.emit("visibilityChange", this, $(this).is(":visible"));
});

MyApp.changeVisibility = function (el, isVisible) {
  if (isVisible && ! el.is(":visible")) {
    el.syncShow();
  } else if ((! isVisible) && el.is(":visible")) {
    el.syncHide();
  }
};

Setting identity information

There‘s a good chance your application has its own identity, and you know the name of the user, and perhaps have an avatar. (If you don’t have an avatar but do have an email, you might want to use that to make a Gravatar.)

To do this you configure TogetherJS with some functions:

TogetherJSConfig_getUserName = function () {return 'User Name';};

This returns the user‘s name (or nick). Return null if you can’t determine the name.

TogetherJSConfig_getUserAvatar = function () {return avatarUrl;};

This returns a URL to the user‘s avatar. It should be 40px square. Again return null if you aren’t sure.

TogetherJSConfig_getUserColor = function () {return '#ff00ff';};

This returns the user's preferred color that represents them. This should be a CSS color.

The names might confuse you: you are providing functions that TogetherJS will call to get the user‘s name, avatar, and color. It doesn’t return the name the user has set through TogetherJS (that would be TogetherJS.require("peers").Self.name).

If any of these values are updated while in the page (like if you have a login process that doesn't cause a page reload) then call TogetherJS.refreshUserData() and the respective getUser* callbacks will all be called again.

TogetherJS.reinitialize()

You can run this to try to reinitialize anything TogetherJS initializes on page load. In particular you can use it if there are new code editors or video elements that should be sync'd, but were added dynamically to the page. E.g.:

$("#content").append('<video src="foo.mov">');
TogetherJS.reinitialize();

TogetherJS events

The TogetherJS object is an event emitter. It uses the style of TogetherJS.on("event", handler). The available events:

  • TogetherJS.on("ready", function () {}): emitted when TogetherJS is fully started up.
  • TogetherJS.on("close", function () {}): emitted when TogetherJS is closed. This is not emitted when the page simply closes or navigates elsewhere. It is only closed when TogetherJS is specifically stopped.

Deferring Initialization

TogetherJS starts up automatically as soon as it can, especially when continuing a session. Sometimes this is problematic, like an application that bootstraps all of its UI after page load. To defer this initialization, define a function TogetherJSConfig_callToStart like:

TogetherJSConfig_callToStart = function (callback) {
  MyApp.onload = callback;
};

In this example when MyApp.onload() is called, TogetherJS will start to initialize itself. Note that calling TogetherJS.reinitialize() might be sufficient for your application's needs if it does a lot of setup after the page loads.

Invitation

Sometimes instead of having the user invite someone to TogetherJS you might want to handle the invitation internally in your app. So typically when the person started TogetherJS, you'd want to find some other person they want to collaborate with and send the TogetherJS link to them. To get at the TogetherJS link:

TogetherJSConfig_on_ready = function () {
  sendTogetherJSURLToServer(TogetherJS.shareUrl());
};

If you call TogetherJS.shareUrl() before TogetherJS is initialized it will return null.

Getting At The Innards

You can still get at TogetherJS, even if you can't rely on the internals not to change underneath you. (You would be well recommended to deploy your own copy of the client if you do this stuff.)

Most of the TogetherJS features are implemented as individual modules, so it should be possible to introduce your own module to do many of the same things. The most important thing is the session module, and sending and receiving messages.

To get the session module (or any module) you can run this after TogetherJS starts:

var session = TogetherJS.require("session");

This assumes that the module has already been loaded… but that assumption would be correct once TogetherJS has started.