5

I am a developer on the Primefaces Extensions project and I use a core Primefaces JS function to get a location of a resource in Javascript for the DocumentViewer and CKEditor components. The getFacesResource() function is in PrimeFaces core.js and looks like this..

   /**
     * Builds a resource URL for given parameters.
     *
     * @param {string} name The name of the resource. For example: primefaces.js
     * @param {string} library The library of the resource. For example: primefaces
     * @param {string} version The version of the library. For example: 5.1
     * @returns {string} The resource URL.
     */
getFacesResource : function(name, library, version) {
    // just get sure - name shoudln't start with a slash
    if (name.indexOf('/') === 0)
    {
        name = name.substring(1, name.length);
    }

    var scriptURI = $('script[src*="/' + PrimeFaces.RESOURCE_IDENTIFIER + '/core.js"]').attr('src');
    // portlet
    if (!scriptURI) {
        scriptURI = $('script[src*="' + PrimeFaces.RESOURCE_IDENTIFIER + '=core.js"]').attr('src');
    }

    scriptURI = scriptURI.replace('core.js', name);

What it does is it finds the "core.js" script in the DOM and then removes core.js so it has the proper location for a new URL you construct for looking up a resource.

Why do we need to do this?

The CKEDitor and DocumentViewer components are complex components that load lots of plugins, language files, and more. We can't modify the core JS files that these plugins use else every time we upgrade we would have to edit their core source. So for DocumentViewer to load a language pack the library value has "documentviewer/locale/en-GB.locale.txt" to load the English language pack for PDF.js that DocumentViewer uses. That however needs to become a real resource so PrimeFaces.getFacesResource('documentviewer/locale/en-GB.locale.txt'); turns the URL into a proper resource the PDF.JS code can find for example:

https://www.primefaces.org/showcase-ext/javax.faces.resource/documentviewer/locale/en-GB.locale.txt.jsf?ln=primefaces-extensions&v=6.2.5

It handles knowing the full URL, appending the library version and knowing whether the current server is serving .jsf or .xhtml extension etc.

Problem:

I am using OmniFaces CombinedResourceHandler which takes all of your JS files and creates 1 single script from all the JS files it finds. So 10 different JS files becomes now 1 resource for performance like this...

/javax.faces.resource/XXX.js.xhtml?ln=omnifaces.combined&v=1532916484000

Now this breaks the core PrimeFaces code because it can't lookup the core.js because core.js does not exist on the page anymore.

I tried excluding core.js from being combined using this switch..

<context-param>
    <param-name>org.omnifaces.COMBINED_RESOURCE_HANDLER_EXCLUDED_RESOURCES</param-name>
    <param-value>primefaces:core.js</param-value>
</context-param>

That loads PrimeFaces core.js after the omnifaces.combined.js which breaks the whole page. To confirm the output is this...

<script src="/primeext-showcase/javax.faces.resource/XXX.js.jsf?ln=omnifaces.combined&amp;v=1533319992000"></script>
<script src="/primeext-showcase/javax.faces.resource/core.js.jsf?ln=primefaces&amp;v=6.2">

So my question is how with pure JavaScript can I replace/fix the PrimeFaces.getFacesResource() JS function to work whether OmniFaces has combined the scripts or not?

Melloware
  • 10,435
  • 2
  • 32
  • 62
  • 1
    What is it you that you think you need the core.js to be 'relocated'? – Kukeltje Aug 03 '18 at 21:04
  • @Kukeltje I added the "Why do we need this?" section above to clarify what is doing and why we need it. – Melloware Aug 04 '18 at 11:45
  • I still fail to see why you need the core.js to be separate for a language file to be loaded – Kukeltje Aug 04 '18 at 12:13
  • You pass one parameter to the function so the others are explicitly null. Maybe just pass an additional/explicit library? – Kukeltje Aug 04 '18 at 12:25
  • Ok, I see the script needs core.js. Can't this be done otherwise? – Kukeltje Aug 04 '18 at 14:31
  • That is what I am trying to figure out. Is there a better way to determine a Faces Resource URL using pure JavaScript. This function only works because it finds core.js in the DOM and just replaces it with my resource. – Melloware Aug 04 '18 at 15:34
  • Why a pure javascript solution? Why can't the component serve it? – Kukeltje Aug 04 '18 at 18:10
  • Maybe you could override the prototype? https://stackoverflow.com/a/39640312/1199132 – Aritz Aug 06 '18 at 09:14
  • 1
    thanks guys i was able to solve it by rewriting the method using better regex's and tested it against both scenarios and everything is now working perfectly. – Melloware Aug 06 '18 at 11:59

1 Answers1

1

I was able to rewrite the method to not assume a core.js was found on the page using regular expressions. It now works when PrimeFaces is in normal mode and will work if using the OmniFaces CombinedResourceHandler. I will be submitting this as a patch to PrimeFaces.

Here is the final working method:

/**
    * Builds a resource URL for given parameters.
    * 
    * @param {string}
    *        name The name of the resource. For example: primefaces.js
    * @param {string}
    *        library The library of the resource. For example: primefaces
    * @param {string}
    *        version The version of the library. For example: 5.1
    * @returns {string} The resource URL.
    */
   getFacesResource : function(name, library, version) {
      // just get sure - name shoudln't start with a slash
      if (name.indexOf('/') === 0) {
         name = name.substring(1, name.length);
      }

      // find any JS served JSF resource
      var scriptURI = $('script[src*="/' + PrimeFaces.RESOURCE_IDENTIFIER + '/"]').first().attr('src');
      // portlet
      if (!scriptURI) {
        scriptURI = $('script[src*="' + PrimeFaces.RESOURCE_IDENTIFIER + '="]').first().attr('src');
      }

      // find script...normal is '/core.js' and portlets are '=core.js'
      var scriptRegex = new RegExp('\\/' + PrimeFaces.RESOURCE_IDENTIFIER + '(\\/|=)(.*?)\\.js');

      // find script to replace e.g. 'core.js'
      var currentScriptName = scriptRegex.exec(scriptURI)[2] + '.js';

      // replace core.js with our custom name
      scriptURI = scriptURI.replace(currentScriptName, name);

      // find the library like ln=primefaces
      var libraryRegex = new RegExp('[?&]([^&=]*)ln=(.*?)(&|$)');

      // find library to replace e.g. 'ln=primefaces'
      var currentLibraryName = 'ln=' + libraryRegex.exec(scriptURI)[2];

      // In a portlet environment, url parameters may be namespaced.
      var namespace = '';
      var urlParametersAreNamespaced = !(scriptURI.indexOf('?' + currentLibraryName) > -1 || scriptURI.indexOf('&'
            + currentLibraryName) > -1);

      if (urlParametersAreNamespaced) {
         namespace = new RegExp('[?&]([^&=]+)' + currentLibraryName + '($|&)').exec(scriptURI)[1];
      }

      // If the parameters are namespaced, the namespace must be included
      // when replacing parameters.
      scriptURI = scriptURI.replace(namespace + currentLibraryName, namespace + 'ln=' + library);

      if (version) {
         var extractedVersion = new RegExp('[?&]' + namespace + 'v=([^&]*)').exec(scriptURI)[1];
         scriptURI = scriptURI.replace(namespace + 'v=' + extractedVersion, namespace + 'v=' + version);
      }

      var prefix = window.location.protocol + '//' + window.location.host;
      return scriptURI.indexOf(prefix) >= 0 ? scriptURI : prefix + scriptURI;
   },
Melloware
  • 10,435
  • 2
  • 32
  • 62