TL;DR
For loading outdated versions of the Visitor Chat JavaScript Api ensure the release version of RequireJS does finish loading it’s data-main
script before fetching the Visitor Api.
Update – this post’s solution is now obsolete (1. Aug. 2014)
The co-existence problems of the two RequireJS libraries have been resolved with the version 2.1 of the VisitorAPI. The VisitorAPI library is distributed from now on as a single JavaScript file too. That source file is AMD loader compatible. Thus the VisitorAPI v. 2.1 no longer introduces global variables in the JavaScript context.
The whole loading code condenses down to:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
requirejs.config({ paths: { /// load the Visitor API from the module named "LiveSupport" LiveSupport: '//YALST_PRODUCT_SERVER/YALST_DIR/visitor_api/v2.1/LiveSupport.min', /// or alternatively load the anonymous module yalstAPI: '//YALST_PRODUCT_SERVER/YALST_DIR/visitor_api/v2.1/LiveSupportAnonymous.min' } } ); require(["LiveSupport"], function(LiveSupportLibrary) { LiveSupportLibrary.require(["api/VisitorAPI"], function(api) { // use the VisitorAPI provided in the api argument } ); } ); |
The details are discussed in this tutorial.
The Original Problem
The RequireJS library adds support for Javascript modules which is missing in the current webbrowser implementations.
The yalst Visitor Api uses a renamed RequireJS (version 2.1.9 at the time of writing) for code organisation and loading, and so may the client pages themselves.
However since RequireJS does not provide a noConflict()
method both versions can interfere on the page resulting in Javascript errors.
RequireJS in the Visitor Api
The Visitor Chat JavaScript Api provides the namespace LiveSupport
which
- under the namespace
LiveSupport.VisitorAPI
contains the documented public methods and
- under the root contains it’s own version of RequireJS.
Thus in addition to the documented use these methods are available in the Api too:
|
LiveSupport.define() LiveSupport.require() LiveSupport.requirejs() |
The api is put in this namespace by the RequireJS optimiser r.js using a build.js file like this:
|
({ appDir: ".", baseUrl: ".", paths: { requireLib: "vendor/require" // path to vanilla RequireJS 2.1.9 }, namespace: "LiveSupport", modules: [ { name: "LiveSupport", include: ['requireLib'], create: true } ] }) |
The Solution
The client page can use it’s own (non-namespaced) version of RequireJS, given it completes loading the data-main
script before the Visitor Api’s namespaced version.
An Example
The example page index-sync.html uses RequireJS in require.min.js with the data-main
script main.js to load jQuery, underscore and Lo-Dash.
In this example the <script>
tag for the Visitor Api is created “by hand” [1] in a function called from the data-main
script main.js. This tag loads the Visitor Api’s own namespaced RequireJS in LiveSupport.js with the data-main
script LiveSupportMain.js which in turn loads the actual api source files.
Among the api files is yet an own version of underscore which by using _.noConflict()
is guaranteed to be free of side effects for the client page.
The directory structure and the network activity log of a web page containing both the vanilla version of RequireJS and the namespaced version of the Visitor Api is shown here:
Here is the code to accomplish sequential loading of the libraries:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
|
//index-sync.html <script src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.9/require.min.js" data-main="main.js"></script> <script>// <![CDATA[ function onDataMainScriptLoaded() { /// the page's own RequireJS library is now loaded if (typeof LiveSupport == "undefined"){ LiveSupport = {}; } if (typeof LiveSupport.VisitorAPI == "undefined"){ LiveSupport.VisitorAPI = {}; } if (typeof LiveSupport.VisitorAPI._methodQueue == "undefined"){ LiveSupport.VisitorAPI._methodQueue = []; } LiveSupport.VisitorAPI.invoke = function(method, parameterArray, callback){ if (typeof method != "string"){ if (typeof callback == "function"){ callback(new Error("invalid argument: 1 must be string")); } return; } LiveSupport.VisitorAPI._methodQueue.push({ method: method , parameters: parameterArray , callback: callback }); }; var YALST_INSTALLATION_URL = "//YourYalstDomain/YourYalstDirectory/"; /// if using loadScript.js //loadScript(YALST_INSTALLATION_URL + "visitor_api/v2/built/LiveSupport.js" // , {"data-main" : YALST_INSTALLATION_URL + "visitor_api/v2/built/LiveSupportMain"} //); /// "by hand" otherwise var scripts = document.getElementsByTagName('script'); var anchorElement = scripts[scripts.length - 1]; var script = document.createElement("script"); script.src = YALST_INSTALLATION_URL + "visitor_api/v2/built/LiveSupport.js"; script.setAttribute("data-main" , YALST_INSTALLATION_URL + "visitor_api/v2/built/LiveSupportMain"); anchorElement.parentNode.insertBefore(script, anchorElement); } // ]]></script> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
//main.js requirejs.config({ baseUrl: 'lib', paths: { app: '../app', jquery: 'http://code.jquery.com/jquery-1.10.2', underscore: 'underscore' }, shim: { underscore: { exports: '_' } } }); // Start the main app logic. requirejs(['jquery', 'underscore'], function($, _) { $.noConflict(); _.noConflict(); // see to load the Visitor Api window.onDataMainScriptLoaded(); jQuery(window.onReady); } ); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
//LiveSupportMain.js LiveSupport.requirejs.config({ //skipDataMain: true, waitSeconds: 15, paths: { underscore: 'vendor/underscore' }, shim: { underscore: { exports: '_' } } }); LiveSupport.require(['underscore'], function(_) { _.noConflict(); } ); /// Load the Api into the root object LiveSupport /// LiveSupport.define(['api/VisitorAPI'], function(VisitorApi) { /// initialize the API … |
[1] To define the order in which the libraries are fetched by the browser
loadScript.js could be used for dynamic injection of
<script>
tags into the DOM and handling of the
load
and
readystatechange
events.