Skip to main content
1-Visitor
August 18, 2016
Question

How can I reference my js libraries in custom extension widget

  • August 18, 2016
  • 2 replies
  • 13593 views

Hi Guys,

I have one question.

How can one add a js library for a custom widget extension without copying it directly into the extension.runtime.js?

We have made a custom extension which uses d3.js and d3-tip.js (newest version, not that depricated and modified version). We had to copy its contents directly into the extension.runtime.js.

I have looked at CustomChartWidgets (which they messed up FontLabel widget btw..) and there is a subfolder in ui folder:

ui/barChart/include/d3/d3.js

in barChart.runtime.js is following function (jquery):

(function () {

    // loader for extension libs

    $('head').append('<link rel="stylesheet" href="../Common/extensions/chartWidget_ExtensionPackage/ui/barChart/include/nvd3/nv.d3.css" type="text/css">');

    $('head').append('<script type="text/javascript" src="../Common/extensions/chartWidget_ExtensionPackage/ui/barChart/include/d3/d3.js"></script>');

}());

so I made the same, but you know referencing my extension and my libraries.

(function(){

  $("body").append('<script type="text/javascript” src="../Common/extensions/NetworkSchema/ui/include/d3/d3.js"></script>');

  $("body").append('<script type="text/javascript” src="../Common/extensions/NetworkSchema/ui/include/d3/d3_tip.js"></script>');

  }());

I also made a pure js version of it:

document.getElementsByTagName('body')[0].appendChild('<script type="text/javascript” src="../Common/extensions/NetworkSchema/ui/include/d3/d3.js"></script>');

document.getElementsByTagName('body')[0].appendChild('<script type="text/javascript” src="../Common/extensions/NetworkSchema/ui/include/d3/d3_tip.js"></script>');

But neither of those seem to do the trick.

Any advice?

One followup - how does the concat function (which makes that combined.extension.js) handles duplicate libraries?

If I add one extension which contains let say myLibrary.js and then I add another which has the same library (directly and/or as reference/link), what could happen?

Is that being taken care off somehow?

Is there any way how to reference from my extension js libraries that I need?

Let say that I have access to target machine, so I can copy libraries directly into the Thingworx/Common directory. Is that wise even?

Why I need to do it?

We had append d3.js directly into our extension.runtime.js, but there must be a bug in TW 6.5.2 extension import parsing function.

There are special characters inside our extension (d3.js contains special characters like euler numbers, pi etc...) and after extension has been added combine.extension.js

contained jibberish like this:


var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π;


instead of this:


var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π;

so we had to manually repair entire combined.etension.js, which is pain

further more, if we add another extension now, it makes that CAT process again and we end up at the same place.

We have not noticed this behaveuior in TW 6.6 and 7.1

If I make my libraries being referenced indirectly, this might help us avoid that issue.

Thanks

Tomas

2 replies

5-Regular Member
August 18, 2016

Hello,

Have you seen the small blurb about this in the Extension Development guide found here: http://support.ptc.com/WCMS/files/170215/en/thingworx_extension_development_user_guide.pdf​ ?

"Third-Party JavaScript Libraries If the custom widget needs to use third-party JavaScript libraries, the best practice is to create a subfolder in the widget folder (for example, /ui/<widgetname>/<jslibrary>/) and put the third-party library files there. These files can be referenced from the ide.js and runtime.js files using the following relative path: ../Common/extensions/<extensionname>/ui/<widgetname>/<jslibrary>/"

I hope this helps!

Tori

tcoufal1-VisitorAuthor
1-Visitor
August 18, 2016

Yes I've seen it,

but Javascript has no include, import or require statement, so how can I reference it?

Only way I know how is to do it via DOM manipulation, e.g.

document.getElementesByTagName(myTag)[0] or via document.getElementById(myTage) have not tried that one though.

But again, it does not seem to work. I cannot see my library code anywhere. 😕

Tomas

1-Visitor
October 27, 2016

Basically, there are few options with various advantages and shortcomings:

1)  You can copy your code into the this.afterRender method and hook ti to main scope. Your html widget node already exists in this.jqElement​ a you can work it out from there. But that means you have will probably have to maintain runtime and ide versions in sync.

2) You can add it to metadata.xml as resource.

          <UIResources>

                <FileResource description="" file="widget.ide.js" isDevelopment="true" isRuntime="false" type="JS"/>

                <FileResource description="" file="widget.ide.css" isDevelopment="true" isRuntime="false" type="CSS"/>

                <FileResource description="" file="widget.runtime.js" isDevelopment="false" isRuntime="true" type="JS"/>

                <FileResource description="" file="widget.runtime.css" isDevelopment="false" isRuntime="true" type="CSS"/>

                <FileResource description="" file="my.js" isDevelopment="false" isRuntime="true" type="JS"/>

                <FileResource description="" file="my.js" isDevelopment="true" isRuntime="true" type="JS"/>

            </UIResources>

Thingworx takes those referenced files and bundles them into one huge JS file for Composer and second JS file for Runtime. That means you drag you extension code every time mashup loads, even when widget is not present. That is not very practical! Your widget.ide.js​ and widget.runtime.js​ should be only lightweight wrappers for custom code so methods 1 and 2 are out of the question for any serious case.

3) During build proces TW takes whole UI folder and everything in it. Long story short, you can reference your file directly like this '<script src="../Common/extensions/MyWidgetExtension/ui/widget/widget.js" type="text/javascript"></script>​ this is what I add as tag in renderHtml method. This makes my bundled widget.ide.js​ and widget.runtime.js files very light and when the widget is present in mashup, browser puls down custom code. Same can be done with multiple files. But you have to take in account that you can have several instances of widget that render the same code. That is probably Ok because it goes from browser cache. But since my code is jQuery extension I test if (jQuery().widget)​ (Note that this is only example and jQuery().widget is part of included jQueryUI library and you can cause lot of troubles messing with global scope, try to prefix your widget name.) and it is already loaded I just skip rendering script tag. So only first widget initializes my custom code. This is very simple and lot of custom dynamic loaders such as jQuery.getScript() can be implemented.


Minify you production code is also good idea.

Method 3 is best option I could come up with, there might be other (better) options.

You can also explore my code here:

tw-semaphorewidget-ext/semaphore.runtime.js at master · Foxoncz/tw-semaphorewidget-ext · GitHub

1-Visitor
October 27, 2016

Hi Jan,

3) it's not always the best option, it will depend on your solution, usually it's best to load all the "crap" with one shot, instead of dynamic JS loading.

Carles

1-Visitor
October 27, 2016

You are right, in case there are not lot of changes in UI widgets extension (Combined Extension JS is not rebuild often) and you use only that specific widget and not library with lots of widgets. Best option would be number 2, where your clients get combined file once and then it is served from cache. But one little change and they are pulling megs of data. But is is save to assume that once it is deployed, there not a lot of changes.

Wrap it up:

  • Option number 2 when single widget per extension and deployed to production with minimal changes.
  • Option number 3 several widgets per extension, not all are always used. Slim combined js file prepared for widget changes.

Either way files are cached, one way only single big file, other way several small files.

Would you agree?

Thanks.