Missouri State University

Skip to content Skip to navigation
a b c d e f g h i j k l m n o p q r s t u v w x y z

Web Strategy and Development Blog

  • Web strategy and development
  • Services Offered
  • @msuweb

How to Write Closure-compiler Extern Files – Part 1 The Basics

September 12, 2013 by Web Strategy and Development

One of the requirements for using ADVANCED_OPTIMIZATIONS with Closure-compiler is providing the compiler with type information for all of the functions and variables that are not directly defined by your code. This is typically done by using an extern file. Extern files are analogous to C/Java header files in that they provide forward type declarations. While writing an extern file is easy, there are a few techniques used that are less than obvious.

This article assumes that you are already familiar with Closure-compiler, it’s restrictions and how to use JSDoc tags.

Let’s start with the basics rules. Externs:

  1. Are still valid javascript – they just don’t do anything
  2. Should not contain actual values or definitions – just the type information
  3. Are included in the compilation with the –externs flag which can occur multiple times

In their simplest form, extern files are just a list of symbol names.

var myNamespace;
myNamespace.subgroup;
var myGlobalVar;

var myNamespace; myNamespace.subgroup; var myGlobalVar;

This alone is enough to prevent the compiler from renaming these symbols when referenced in your code. In fact, you won’t get errors or warnings either. But don’t do this – you haven’t provided any type information to the compiler and stopping here would bypass many of the coding checks of the compiler as it will assume that these symbols can be literally any type.

Instead, we need to provide type information as well. Here’s how to get started:

Namespaces

Since coding best practice dictates that we should not pollute the global namespace, let’s start by declaring our own namespace.

/**
 * A typical namespace declaration
 * Here we use an assignment so the compiler will infer the type.
 * @const
 */
var myNamespace = {};
 
/**
 * Further namespace segmentation
 * @const
 */
myNamespace.subgroup = {};

/** * A typical namespace declaration * Here we use an assignment so the compiler will infer the type. * @const */ var myNamespace = {}; /** * Further namespace segmentation * @const */ myNamespace.subgroup = {};

Namespaces are (usually) just empty objects. Using @const improves type checking and enables further optimizations within the compiler.

Variables

/**
 * A global variable - note there is no assignment needed
 * @type {number}
 */
var myGlobalVar;
 
/**
 * My variable as a static namespace property - still no assignment
 * @type {number}
 */
myNamespace.myVal;

/** * A global variable - note there is no assignment needed * @type {number} */ var myGlobalVar; /** * My variable as a static namespace property - still no assignment * @type {number} */ myNamespace.myVal;

Functions

/**
 * A global function - note that the definition is completely missing
 * @param {number} param1
 */
function myGlobalFunction(param1) {}
 
/**
 * A global variable with a function expression type
 * Just a different way to write the type information
 * @type {function(number)}
 */
var myGlobalFunctionExpr2;
 
/**
 * A global variable assigned a function expression
 * We have an assignment here so that we can have an @param tag reference
 * @param {number} param1
 */
var myGlobalFunctionExpr3 = function (param1) {};
 
/**
 * A function expression as a static property on a namespace
 * @param {number} param1
 */
myNamespace.functionExpr = function (param1) {};

/** * A global function - note that the definition is completely missing * @param {number} param1 */ function myGlobalFunction(param1) {} /** * A global variable with a function expression type * Just a different way to write the type information * @type {function(number)} */ var myGlobalFunctionExpr2; /** * A global variable assigned a function expression * We have an assignment here so that we can have an @param tag reference * @param {number} param1 */ var myGlobalFunctionExpr3 = function (param1) {}; /** * A function expression as a static property on a namespace * @param {number} param1 */ myNamespace.functionExpr = function (param1) {};

One gotcha with extern functions: they MUST contain at least one JSDoc tag or the compiler will not type check them (it will assume they take any number of arguments of any type and can return anything).

/**
 * This function takes no arguments
 * It also doesn't return anything
 * This tag is just to force the compiler to type check it
 * @return {undefined}
 */
function myParameterlessFunction() {}

/** * This function takes no arguments * It also doesn't return anything * This tag is just to force the compiler to type check it * @return {undefined} */ function myParameterlessFunction() {}

Constructors and prototypes

With the exception of the @constructor tag, these are just variations on the above definitions.

/**
 * My type constructor - I will be called with the new keyword
 * @constructor
 * @param {boolean=} param1 - an optional argument
 */
myNamespace.MyType = function(param1) {};
 
/**
 * An instance method
 * @return {number}
 */
myNamespace.MyType.prototype.myInstanceFunction = function() {};

/** * My type constructor - I will be called with the new keyword * @constructor * @param {boolean=} param1 - an optional argument */ myNamespace.MyType = function(param1) {}; /** * An instance method * @return {number} */ myNamespace.MyType.prototype.myInstanceFunction = function() {};

But what about constructors that can be optionally called WITHOUT using the new keyword? You add an @return tag to denote this case.

/**
 * My type constructor - I may be called without the new keyword
 * I usually contain code such as:
 *   if (!(this instanceOf myNamespace.MyType2))
 *       return new myNamespace.MyType2();
 * The exclamation mark is used because I will never return null
 * @constructor
 * @return {!myNamespace.MyType2}
 */
myNamespace.MyType2 = function() {};

/** * My type constructor - I may be called without the new keyword * I usually contain code such as: * if (!(this instanceOf myNamespace.MyType2)) * return new myNamespace.MyType2(); * The exclamation mark is used because I will never return null * @constructor * @return {!myNamespace.MyType2} */ myNamespace.MyType2 = function() {};

Filed Under: Technical Tagged With: closure-compiler, javascript

Latest Campus Map Source Code

May 1, 2013 by Web Strategy and Development

Campus map

The latest refresh of the Missouri State Campus Map continues to utilize the Google Maps API v3. The JavaScript for the Campus map has been rewritten from scratch. Now all three versions of the map use a common source code:

  • Desktop view
  • Mobile view
  • Embeded view

The source code was also rewritten to make better use of events. The shuttle layer code in particular was completely redesigned.

The map code is annotated with JSDoc tags for Closure-compiler and supports ADVANCED_OPTIMIZATION compilation with the experimental type-based optimizations.

  • Uses the Missouri State common js library
  • Common map code: map_v2_uncompressed.js
  • Soy utilities: soyutils.js
  • Soy templates: map_v2_templates.soy.js

Map controls – specific to each view

  • Desktop controls
  • Mobile controls
  • Embeded controls

 

Filed Under: Mobile, Redesign, Technical Tagged With: javascript, map

Closure Compiler: Which Compilation Level is Right for Me?

September 26, 2012 by Web Strategy and Development

Cross posted to the Closure Tools Blog

When first starting with Closure Compiler, it is easy to see the names for the compilation levels and assume that advanced is better than simple. While it is true that advanced optimization generally produces a smaller file, that does not mean it is the best fit for all projects.

What is really the difference?

There are quite a few differences, but the most significant is dead-code elimination. With advanced optimizations the compiler removes any code that it knows you are not using. Perfect! Who would not want that? Turns out a lot of people because the compiler can only correctly eliminate code when you specifically tell it about ALL of the other code used in your project AND ALL of the ways that your code is used by other scripts. Everything should be compiled together at the same time. That is a pretty big gotcha.

Here is a classic example:

<html>
<head>
  <title>Advanced Optimization Gotchas</title>
  <!-- an external library -->
  <script src="jquery-1.7.2.js"></script>
  <script>
    //This section is compiled
    function ChangeBackground() {
      $('body').css('background-color', 'pink');
    }
    //Export for external use
    window['ChangeBackground'] = ChangeBackground;
  </script>
</head>
<body>
  <!-- external use of compiled code -->
  <a onclick="ChangeBackground()">Pinkify</a>
</body>
</html>

<html> <head> <title>Advanced Optimization Gotchas</title> <!-- an external library --> <script src="jquery-1.7.2.js"></script> <script> //This section is compiled function ChangeBackground() { $('body').css('background-color', 'pink'); } //Export for external use window['ChangeBackground'] = ChangeBackground; </script> </head> <body> <!-- external use of compiled code --> <a onclick="ChangeBackground()">Pinkify</a> </body> </html>

In this case we have to explicitly tell the compiler about jQuery during compilation with an extern file and we have to tell it that our ChangeBackground function is called from external code. While this is a contrived example, it illustrates a case where it probably was not worth the time to ensure compatibility with the advanced optimization level.

General decision factors

So how do you actually decide which optimization level is right for your project? Below are some of the most common factors in that decision:

Simple Optimizations Advanced Optimizations
Looking for a replacement JavaScript compressor Looking for every last byte of savings in delivered code size or in execution time
Compiling a library where the vast majority of functions are part of the publicly exposed API Authoring a very large application with multiple modules
Unwilling to make substantial changes to code style Starting a new project or are willing to make substantial changes to coding style and patterns
Using external libraries that do not have existing extern files and are not compatible with advanced optimizations Wanting the best possible obfuscation of your code to protect intellectual property
On a tight timeline that does not allow for troubleshooting obscure errors after compilation Authoring a large library but want to support users who only use a small subset of the code

Coding style factors

Most of us are proud of our JavaScript. In fact we may have some slick coding patterns that make our code elegant to read and maintain, however, not all JavaScript coding patterns compile equally with Closure Compiler advanced optimizations. If your code contains any of the following (and you are unwilling to change this) then simple optimizations would probably be the best choice for you:

  • Mixed property access methods
    Closure Compiler treats properties accessed with dotted notation (obj.prop) differently than when accessed via brackets or quoted notation (obj[‘prop’]). In fact it sees them as completely different properties. This item is first on the list for a reason: it is almost always the biggest hurdle. Because of this, the following patterns are all places which can cause problems with advanced optimizations:

    1. Building method names with strings
      var name = 'replace';
      obj[name] = function() {};
      obj[name + 'With'] = function() {};
      Obj.replaceWith(); //Mixed access problem

      var name = 'replace'; obj[name] = function() {}; obj[name + 'With'] = function() {}; Obj.replaceWith(); //Mixed access problem

    2. Testing for property existence with strings
      obj.prop = 'exists';
      if ('prop' in obj) … //Mixed access problem

      obj.prop = 'exists'; if ('prop' in obj) … //Mixed access problem

    3. Using a property name in a loop
      obj.prop = function() {};
      for (var propName in obj) {
        if(propName == 'prop') { //Mixed access problem
        }
      }

      obj.prop = function() {}; for (var propName in obj) { if(propName == 'prop') { //Mixed access problem } }

  • Using the “this” keyword outside of constructors or prototype methods
    var obj = {};
    //Static method using “this”
    obj.prop = function() { this.newprop = 'exists' };
    obj.prop();
    alert(obj.newprop);
    ...

    var obj = {}; //Static method using “this” obj.prop = function() { this.newprop = 'exists' }; obj.prop(); alert(obj.newprop); ...

    In advanced optimizations, Closure Compiler can move and refactor code in unexpected ways. Some of them include functions which are inlined and properties which are collapsed to variables. In many of these cases, it changes which object the “this” keyword references. These cases have workarounds, but without special attention your code will likely not execute as intended. To illustrate, under advanced optimizations the compiler might change the above code to:

    var obj = {};
    var a = function() { this.newprop = 'exists' };
    a();
    //Property does not exist - it is defined on the window object
    alert(obj.newprop);

    var obj = {}; var a = function() { this.newprop = 'exists' }; a(); //Property does not exist - it is defined on the window object alert(obj.newprop);

Choose wisely

Regardless of the choice between simple or advanced optimizations, you can still use the many compile time code checks and warnings for your code. From a missing semicolon to a misspelled property, the compiler can assist in identifying problems with your code before your users do it for you.

So to recap, advanced is not always better than simple. Modifying an existing code base to be compatible with Closure Compiler advanced optimizations can be a daunting challenge, and it definitely is not the best choice for every project.

Filed Under: Technical, web strategy and development Tagged With: javascript

iFrames, HTML5 and Older Versions of Internet Explorer

May 20, 2011 by Web Strategy and Development

iframes and HTML5 templates have significant hurdles with older Internet Explorer versions. For Internet Explorer 8 and older, the frameBorder and scrolling attributes must be set on the iframe as the equivalent CSS properties cannot accomplish the same thing. Unfortunately, these properties are not valid in HTML5.

To make matters worse, changes to the frameBorder attribute have no effect once the tag has been parsed (or added to the DOM for dynamic elements). So the typical workaround of adding the non-compliant attributes through script won’t work either. Today I finally came up with a workaround for this issue and was able to have my standards compliant iframe tag and make it work in older Internet Explorer versions:

<iframe id="MyIframe" name="MyIframe" src="http://my.iframe.src/"></iframe>
<script>
if(document.attachEvent) {
  var OldIframe = document.getElementById("MyIframe");
  var NewIframe = OldIframe.cloneNode(true);
  //no scrollbars - overflow:hidden works for other browsers
  NewIframe.setAttribute("scrolling", "no");
  // IE8 and lower add a special border on frames.
  // It's actually in addition to the css border property.
  if(!document.addEventListener) {
    NewIframe.setAttribute("frameBorder", 0);
  }
  OldIframe.parentNode.replaceChild(NewIframe, OldIframe);
}
</script>

<iframe id="MyIframe" name="MyIframe" src="http://my.iframe.src/"></iframe> <script> if(document.attachEvent) { var OldIframe = document.getElementById("MyIframe"); var NewIframe = OldIframe.cloneNode(true); //no scrollbars - overflow:hidden works for other browsers NewIframe.setAttribute("scrolling", "no"); // IE8 and lower add a special border on frames. // It's actually in addition to the css border property. if(!document.addEventListener) { NewIframe.setAttribute("frameBorder", 0); } OldIframe.parentNode.replaceChild(NewIframe, OldIframe); } </script>

All I’m doing is making a copy of the iframe, setting the new attributes, and replacing the original iframe in the DOM with my new one. The document.addEventListener check will succeed on almost any modern browser and is an effective way to target IE8 and older (although there are numerous other and more exact ways to do this).

And now I have my standards compliant iframe tag and have it work in Internet Explorer 7 and 8.

Update 5/24/2011: After more testing it appears IE9 also requires similar treatment to remove scrollbars. The code sample above has been updated.

Filed Under: Technical, web strategy and development Tagged With: html5, internet explorer, javascript

Speed Up Your Map with Closure-Compiler

March 26, 2011 by Web Strategy and Development

Google recently posted a presentation I gave on using Closure Compiler to optimize your maps.

[youTubeVideo url=”https://www.youtube.com/watch?v=-dFqoXXe5VM”]

Slides: Speed up your map with Closure Compiler

Filed Under: Technical Tagged With: javascript, map

Next Page »

Subscribe

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Follow @MSUWeb

My Tweets

Calendar

  • Complete Calendar

Categories

  • Accessibility
  • brand
  • email marketing
  • Mobile
  • News
  • Redesign
    • Academic websites
    • Web redesign 2010
    • Web redesign 2015
  • Social media
    • Social media kit
  • template
    • updates
  • Training
  • Video
  • Web Press
  • web strategy and development
    • Technical
  • WordPress blogs

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org

Connect with web strategy and development

  • Twitter

Make your Missouri statementMake your Missouri statement
  • Last Modified: July 2, 2018
  • Accessibility
  • Disclaimer
  • Disclosures
  • EO/AA/M/F/Veterans/Disability/Sexual Orientation/Gender Identity
  • © 2013 Board of Governors, Missouri State University
  • Contact Information
 

Loading Comments...