12 Replies Latest reply on Oct 27, 2016 3:44 AM by carlesc RSS
    tcoufal Communicator

    How can I reference my js libraries in custom extension widget

    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

      • Re: How can I reference my js libraries in custom extension widget
        ttielebein Collaborator

        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

          • Re: How can I reference my js libraries in custom extension widget
            tcoufal Communicator

            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

            • Re: How can I reference my js libraries in custom extension widget
              tcoufal Communicator

              One question,

              is necessary to remove the extension prior importing new one?

              Or simple version change in metadata.xml will suffice?

              • Re: How can I reference my js libraries in custom extension widget
                tcoufal Communicator

                Sorry to bother you, but I am still stacked on referencing external javascript libraries.

                It says:

                files using the following relative path: ../Common/extensions/<extensionname>/ui/<widgetname>/<jslibrary>/",

                but How.

                This a basic runtime.js structure

                 

                TW.Runtime.Widgets.networktopology= function () {

                  var valueElem;

                  this.renderHtml = function () {

                  // return any HTML you want rendered for your widget

                  // If you want it to change depending on properties that the user

                  // has set, you can use this.getProperty(propertyName). In

                  // this example,we'll just return static HTML

                  return  '<div class="widget-content widget-networktopology">' +

                  '<span class="networktopology-property">' + this.getProperty('NetworkTopology Property') + '</span>' +

                  '</div>' +

                  '<script src="../Common/extensions/PNnetwork/ui/networktopology/jslibrary/test.js" type="text/javascript"></script>';

                  };

                 

                  this.afterRender = function () {

                  // NOTE: this.jqElement is the jquery reference to your html dom element

                  //  that was returned in renderHtml()

                 

                  // get a reference to the value element

                  valueElem = this.jqElement.find('.networktopology-property');

                  // update that DOM element based on the property value that the user set

                  // in the mashup builder

                  valueElem.text(this.getProperty('NetworkTopology Property'));

                  };

                 

                // this is called on your widget anytime bound data changes

                  this.updateProperty = function (updatePropertyInfo) {

                  // TargetProperty tells you which of your bound properties changed

                  if (updatePropertyInfo.TargetProperty === 'NetworkTopology Property') {

                  valueElem.text(updatePropertyInfo.SinglePropertyValue);

                  this.setProperty('NetworkTopology Property', updatePropertyInfo.SinglePropertyValue);

                  }

                  };

                };

                 

                 

                I have placed my reference as HTML tag, but that does not work...

                Any suggestions?

                 

                Thanks

                 

                TOmas

                  • Re: How can I reference my js libraries in custom extension widget
                    carlesc Ninja

                    Hi Tomas,

                     

                    When I want to reference a library, I just write it as a resource, here a sample where I have a common.js file shared between different widgets:

                     

                    <Widget name="wup_common" description="Common Shared Resources">
                    <UIResources>
                    <!-- Studio/Runtime -->
                    <FileResource type="JS"file="wup_common.js"description="" isDevelopment="true" isRuntime="true" />
                    <!-- Runtime -->
                    <FileResource type="CSS"file="wup_common.runtime.css"description="" isDevelopment="false" isRuntime="true" />
                    <FileResource type="JS"file="wup_common.runtime.js"description="" isDevelopment="false" isRuntime="true" />

                     

                     

                    </UIResources>
                    </Widget>
                      • Re: How can I reference my js libraries in custom extension widget
                        tcoufal Communicator

                        Thanks Carles,

                        I will try that.

                        Turned out that referencing via return '<script>' actually worked, but in my case (TW 7.1.0) Tomcat needs to be restarted. I'll get errors (Service invoke failed on remove extension) otherwise. It removes the content of of .../extension/myExtension but not the folder it self. Than import of an extension fail.

                         

                        If I change the version number in XML it work, although some other problems have come up.

                         

                        Thanks.

                  • Re: How can I reference my js libraries in custom extension widget
                    jgabriel Apprentice

                    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