chartjs/chartjs-adapter-luxon

Detect when loaded

freecorvette opened this issue · 4 comments

Hello, I'm loading:
https://cdn.jsdelivr.net/npm/chart.js
https://cdn.jsdelivr.net/npm/luxon
https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon

asnchronously and I need to detect when they are loaded, since each one depends on the previous and drawing the chart depends on all three. For chart and luxon, I'm able to find out when they are loaded using:

typeof Chart != "undefined"
and
// typeof Chart._adapters._date == "undefined"
typeof luxon !== "undefined"

however I'm not able to figure out a way to tell when chartjs-adapter-luxon was loaded. It seems to override some methods in Chart._adapters._date, but I don't know which ones. Do you have any advice?

Note: I cannot use the <script onload> event reliably, since it doesn't trigger on cached versions (i.e. when the browser requests witj If-modified-since and the server responds with status code 304 not modified).

@freecorvette having a look your issue, let me say that

typeof Chart !== "undefined" <---- tests CHART.JS
and
typeof Chart._adapters._date !== "undefined" <---- tests CHARTJS-ADAPTER-LUXON and not LUXON

Luxon is a backend library:

CHART.JS
   |
CHARTJS-ADAPTER-LUXON
   |
LUXON

That said, if you have (typeof Chart !== 'undefined' && typeof Chart._adapters._date !== "undefined') you should be almost sure to have everything loaded. Anyway I'm curious to know how you can guarantee that the adpater will load when chartjs and luxon are already loaded (if I'm not wrong this is mandatory).

@stockiNail I made a mistake in my original post.

  • typeof Chart !== "undefined" does indeed check that CHART.JS was loaded
  • typeof luxon !== "undefined" is the actual check for LUXON (and not typeof Chart._adapters._date !== "undefined" as originally posted -- I've edited the post as well)

The problem is that, right after loading CHART.JS (and CHART.JS only), typeof Chart._adapters._date !== "undefined" is also true. CHART.JS sets the date adapter. CHARTJS-ADAPTER-LUXON only redefines it. So the original question remains: how to check that CHARTJS-ADAPTER-LUXON was loaded?

To make sure CHARTJS-ADAPTER-LUXON loads only after both CHART.JS and LUXON are loaded, I'm currently loading CHART.JS first, then only when it's loaded I'm loading LUXON. And when LUXON is also loaded, I'm loading CHARTJS-ADAPTER-LUXON. I guess I could load CHART.JS and LUXON in parallel, but that's not a priority in the app, at the moment.

The problem is that, right after loading CHART.JS (and CHART.JS only), typeof Chart._adapters._date !== "undefined" is also true. CHART.JS sets the date adapter.

Yes, CHART.JS set a default adapter which is throwing an exception on all functions.

CHARTJS-ADAPTER-LUXON only redefines it. So the original question remains: how to check that CHARTJS-ADAPTER-LUXON was loaded?

Luxon adapter contains a private property (_id) set to 'luxon'.

_id: 'luxon', // DEBUG

Therefore, as workaround, you could check that property, something like:

const adapterInstance = new Chart._adapters._date();
if (adapterInstance._id === 'luxon') {
  ... // Luxon adapter is loaded
}

As said, _id is a private option.

EDIT

When I wrote "private", I meant because the property is starting with _ and not because there is the @private tag

@stockiNail, the private member check workaround works, thanks for the detailed answer! In case anyone else needs it, I'm posting below my loading setup (the whole point was to load all scripts asynchronously on the web page and do it in parallel as much as possible -- that is, chain only scripts that depend on each other):

<script type="text/javascript">
  function checkLoaded() {
    if (typeof $ === "undefined") {
      return "jquery not loaded";
    }
    if (typeof duDatepicker === "undefined") {
      return "duDatepicker not loaded";
    }
    if (typeof luxon === "undefined") {
      return "luxon not loaded";
    }
    if (typeof $.fn.chosen === "undefined") {
      return "chosen not loaded";
    }
    if (typeof Chart === "undefined") {
      return "chart not loaded";
    }
    if (new Chart._adapters._date()._id !== 'luxon') {
      return "chart date adapter not loaded";
    }
    if (typeof myInitPageFunction === "undefined") {
      return "main js not loaded";
    }
    return "";
  }

  function onScriptsLoaded(src) {
    if (onScriptsLoaded.run) {
      // prevent any stray call
      return;
    }
    var error = checkLoaded();
    if (error) {
      //console.log(error);
      // script onload won't fire on cached (HTTP 304) scripts, so set up a timer as well
      clearTimeout(onScriptsLoaded.t);
      onScriptsLoaded.t = setTimeout(onScriptsLoaded, 1000);
      return;
    }
    onScriptsLoaded.run = true;
    //console.log("all libs loaded, run page init function");
    $(myInitPageFunction);
  }

  function loadScript(params) {
    const script = document.createElement('script');
    script.id = params.id;
    script.src = params.url;
    script.async = params.async;
    script.onload = function() {
      if (params.scripts && params.scripts.length) {
        for (var i = 0; i < params.scripts.length; i++) {
          loadScript(params.scripts[i]);
        }
      }
      if (params.onload) {
        params.onload(this.id);
      }
    }

    document.head.append(script);
  }

  // load jQuery, THEN jQuery Chosen plugin
  loadScript({id: 'jquery', url: 'https://code.jquery.com/jquery-3.6.0.min.js', async: true, onload: onScriptsLoaded, scripts: [
    {id: 'jschosen', url: '/javascript/jquery/plugins/chosen/chosen.jquery.min.js', async: true, onload: onScriptsLoaded}
  ]});

  // load ChartJS, THEN Luxon, THEN ChartJS Adapter Luxon
  loadScript({id: 'chart', url: 'https://cdn.jsdelivr.net/npm/chart.js@^3', async: true, onload: onScriptsLoaded, scripts: [
    {id: 'jsluxon', url: 'https://cdn.jsdelivr.net/npm/luxon@^2', async: true, onload: onScriptsLoaded, scripts: [
      {id: 'jsluxon-adapter', url: 'https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@^1', async: true, onload: onScriptsLoaded}
    ]}
  ]});

  // load duDatePicker plugin
  loadScript({id: 'dudatepicker', url: '/javascript/jquery/plugins/material-date-range-picker/dist/duDatepicker.min.js', async: true, onload: onScriptsLoaded});

  // load page main script
  loadScript({id: 'main', url: '/javascript/main.min.js', async: true, onload: onScriptsLoaded});
</script>