3 Replies Latest reply on Feb 21, 2013 12:06 PM by dschlenk

    GateIn 3.5 portlet with jquery and google chart tools jsapi

    dschlenk

      I'm attempting to write a portlet that uses the google chart tools javascript library but am having problems getting it to work in the new 3.5 way of using javascript.

       

      I found a loader plugin for RequireJS that theoretically makes this easier: https://github.com/millermedeiros/requirejs-plugins

      But I'm not having any luck getting that integrated into my gatein-resources.xml file.

       

      The goog.js plugin in that project looks like this:

       

      define(['async', 'propertyParser'], function (async, propertyParser) {

          var rParts = /^([^,]+)(?:,([^,]+))?(?:,(.+))?/;

          function parseName(name){

              var match = rParts.exec(name),

                  data = {

                      moduleName : match[1],

                      version : match[2] || '1'

                  };

              data.settings = propertyParser.parseProperties(match[3]);

              return data;

          }

          return {

              load : function(name, req, onLoad, config){

                  if (config.isBuild) {

                      onLoad(null); //avoid errors on the optimizer

                  } else {

                      var data = parseName(name),

                          settings = data.settings;

       

       

                      settings.callback = onLoad;

       

       

                      req(['async!'+ (document.location.protocol === 'https:'? 'https' : 'http') +'://www.google.com/jsapi'], function(){

                          google.load(data.moduleName, data.version, settings);

                      });

                  }

              }

          };

      });

       

      Which makes me think it depends on async.js and propertyParser.js, which are as follows:

      async.js:

      define(function(){

          var DEFAULT_PARAM_NAME = 'callback',

              _uid = 0;

          function injectScript(src){

              var s, t;

              s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = src;

              t = document.getElementsByTagName('script')[0]; t.parentNode.insertBefore(s,t);

          }

          function formatUrl(name, id){

              var paramRegex = /!(.+)/,

                  url = name.replace(paramRegex, ''),

                  param = (paramRegex.test(name))? name.replace(/.+!/, '') : DEFAULT_PARAM_NAME;

              url += (url.indexOf('?') < 0)? '?' : '&';

              return url + param +'='+ id;

          }

          function uid() {

              _uid += 1;

              return '__async_req_'+ _uid +'__';

          }

          return{

              load : function(name, req, onLoad, config){

                  if(config.isBuild){

                      onLoad(null); //avoid errors on the optimizer

                  }else{

                      var id = uid();

                      window[id] = onLoad; //create a global variable that stores onLoad so callback function can define new module after async load

                      injectScript(formatUrl(name, id));

                  }

              }

          };

      });

      propertyParser.js:

      define(function(){

          var rProps = /([\w-]+)\s*:\s*(?:(\[[^\]]+\])|([^,]+)),?/g, //match "foo:bar" and "lorem:[ipsum,dolor]" capturing name as $1 and val as $2 or $3

              rArr = /^\[([^\]]+)\]$/; //match "[foo,bar]" capturing "foo,bar"

          function parseProperties(str){

              var match, obj = {};

              while (match = rProps.exec(str)) {

                  obj[ match[1] ] = typecastVal(match[2] || match[3]);

              }

              return obj;

          }

          function typecastVal(val){

              if (rArr.test(val)){

                  val = val.replace(rArr, '$1').split(',');

              } else if (val === 'null'){

                  val = null;

              } else if (val === 'false'){

                  val = false;

              } else if (val === 'true'){

                  val = true;

              } else if (val === '' || val === "''" || val === '""'){

                  val = '';

              } else if (! isNaN(val)) {

                  //isNaN('') == false

                  val = +val;

              }

              return val;

          }

          //API

          return {

              parseProperties : parseProperties,

              typecastVal : typecastVal

          };

      });

       

      Both async.js and goog.js seem to implement the loader plugin API (which is minimally defined as having a load : function(name, req, onLoad, config) function as stated in the RequireJS API doc.

      Looking at the Module resources section of the JavaScript in GateIn documentation for 3.5, it looks like I should do the following in gatein-resources.xml to make these modules available in my portal:

      <module>

        <name>async</name>

        <script>

                                    <path>/js/goog/async.js</path>

        </script>

      </module>

      <module>

                          <name>propertyParser</name>

        <script>

                                    <path>/js/goog/propertyParser.js</path>

        </script>

      </module>

      <module>

        <name>goog</name>

        <script>

                                    <path>/js/goog/goog.js</path>

        </script>

        <depends>

        <module>async</module>

        </depends>

        <depends>

                                    <module>propertyParser</module>

        </depends>

      </module>

      and then add this bit to the config for portlet I want to use the google stuff in:

      <portlet>

                          <name>IncidentPortlet</name>

        <module>

        <script><path>/js/incident.js</path></script>

        <depends>

        <module>async</module>

        </depends>

        <depends>

        <module>goog</module>

                                              <as>google</as>

                                              <resource>visualization,1,packages:[corechart,table]</resource>

        </depends>

        <depends>

        <module>jquery</module>

        </depends>

        <depends>

        <module>module</module>

        </depends>

        </module>

      </portlet>


      Of note is that I pass the specific google resources to load with the <resource> tag, which in normal RequiresJS would be found after the !.

       

      The contents of my /js/incident.js file, which I wrote in native GateIn Module form is:

      (function(google,$){

                var data = new google.visualization.DataTable();

        // load data and draw a table

         // ...

      });

       

      When I attempt to access the page I get a variety of errors in the Chrome javascript console:

       

      Resource interpreted as Script but transferred with MIME type text/html: "http://httpd-dev.pe.spanlink.com:8001/portal/classic/home/async.js". bootstrap-min.js:32

      Uncaught SyntaxError: Unexpected token < async.js:1

      Uncaught TypeError: Cannot read property 'normalize' of undefined bootstrap-min.js:22

      Uncaught Error: Load timeout for modules: SHARED/goog!visualization,1,packages:[corechart,table]_unnormalized2,SHARED/goog!visualization,1,packages:[corechart,table],async!http://www.google.com/jsapi_unnormalized3 http://requirejs.org/docs/errors.html#timeout

       

      async.js is for some reason not being loaded from the correct place and therefore loads the normal home page which causes the error in the first and second lines, and probably indirectly breaks the rest as well.

       

      What am I doing wrong? Do the plugins need to be modified to work correctly with GateIn? Do I need to use native AMD style instead of the GateIn format for my incident.js file?