Finding Closure from jquery

Starting to use Closure Tools with:

plovr - PlastronJS - the G

Who is this guy?

Rhys Brett-Bowen

Closure Compiler

Advanced Optimizations

  • Aggressive renaming
  • Dead code removal
  • Function inlining
  • sourcemaps

What does this mean?

  • More code - less bytes
  • We can be verbose - the code won't be
  • We can use longer name(space)s
  • Unused code is removed
  • Code will run faster

It's awesome!

Prove it!

What's the catch

What's the catch?

Code must be AO compatible
  • foo['bar'] !== foo.bar
  • {foo: 'bar'} becomes {a: 'bar'}
  • but {'foo': 'bar'} stays {'foo': 'bar'}
Use property when internal only and strings when value is external. Read more

What's the catch?

Code must be AO compatible
var Class = function() {};
Class.number = 1;
  • must provide Class.number to use externally (enums)
goog.provide('Class.Number')
/** @enum {number} */
Class.Number = {
    ONE: 1,
    TWO: 2
};
  • provide access through prototype
  • put under another namespace
  • What about using libraries that aren't compatible...

    Closure Library

    Built for the compiler
    • Classical inheritance
    • Type annotations
    • Dependency management
    • goog.ui.Component

    Classical inheritance

    don't be afraid
    goog.provide('Class');
    goog.require('Super');
    /**
     * @constructor
     * @extends {Super}
     */
    Class = function() {
        goog.base(this);
    };
    goog.inherits(Class, Super);
    
    /**
     * @inheritDoc
     */
    Class.prototype.method = function(arg) {
        return goog.base(this, 'method', arg);
    };
    

    Classical inheritance

    Looks long, but autocomplete!
    • Code organization
    • Easy override functions
    • Well understood
    • Performance!

    Type annotations

    • Annotating
    • Optional... mostly
    • Don't need to be explicit
    We'll see some in the code

    Dependency management

    goog.provide('namespace');
    goog.require('othernamespace');
    
    uncompiled
    • goog.provide will create the objects needed for the namespace if they don't exist
    • goog.require calls in files from goog.addDependency if not already done
    • Need the goog.addDependecy file made using depswriter.py... or let plovr do it
    compiled
    • compiler will include all dependencies in a single file in the correct order
    want Dependency Injection? try Loader

    WeatherDemo

    Plovr

    no egrets

    Setup with Plovr

    • bundles compiler, templates and library
    • Simple configuration
    • Test runner
    • Documentation generation
    • Modules

    WeatherDemo config

    {
        "id": "weatherdemo",
        "paths": ["js/", "lib/", "templates/"],
        "inputs": "js/main.js",
        "mode": "ADVANCED",
        "level": "VERBOSE",
        "output_file": "compiled.js"
    }

    add in to project

    add in to index.html
    <!-- or mode=compile -->
    <script src="http://localhost:9810/compile?id=weatherdemo&mode=raw"></script>
    run from the command line
    java -jar plovr.jar serve weatherdemo.json
    Done!
    ... or create the compiled js file
    java -jar plovr.jar build weatherdemo.json

    Compiling might take some time...

    but it's worth it, so use your time wisely

    Setup a main function

    main.js
    goog.provide('WeatherDemo.main');
    WeatherDemo.main = function() {};
    // stops the name from compiling
    goog.exportSymbol('WeatherDemo.main', WeatherDemo.main);
    index.html
    <script>WeatherDemo.main();</script>

    Back to jQuery

    jQuery has a great interface
    
    $('.class').children().addClass('classChilren');
                

    Closure Library...

    • Namespacing great for discoverability
    • Functional instead of OO
    but...
    
    goog.array.forEach(
        goog.dom.getChildren(goog.dom.getElementByClass('class')),
            function(child) {
                goog.dom.classes.add(child, 'classChildren');
            });
                

    Learn a new library?

    or just use an interface...
    
    $('.class').children().addClass('classChilren');
                
    The G!

    Using the G

    goog.require('G');
    • Just like jQuery - almost
    • use $$.method instead of $.method
    • $$.setSelectorEngine(engine) for better selector support
    • Even some (simple) jQuery plugins may work - just add to $$.fn

    PlastronJS

    PlastronJS

    MVC for Closure Library
    • Define schemas on models
    • Controls built on goog.ui.Component
    • Control level event system
    • Convenient model bindings
    • Mediator
    • Store
    • also router and syncs

    Schema for models

    WeatherDemo.model.City.Schema = {
      'celcius': {
        get: function(temp) {
          return 5 / 9 * (temp - 32);
        },
        /** @this {mvc.Model} */
        set: function(num, opt_silent) {
          this.set('temp', num * 9 / 5 + 32, opt_silent);
        },
        require: ['temp']
      }
    };
    or convenience functions
    this.alias('farenheit', 'temp');
    read documentation for more

    Mediator

    • app wide messaging
    • namespace messages
    • can register objects to broadcast, then will decorate with broadcast method
    • can initialize and dispose based on available message
    • message matching with customizable wildcards
    WeatherDemo.Mediator.register(this, 'chooseCity');
    // then
    this.broadcast('chooseCity', this.getModel().get('id'));
    WeatherDemo.Mediator.on('chooseCity', function(id) {...});

    Store

    • single store for retrieval of models
    • can create models given a type
    • will retrieve models given an id (and update new models id when given)
    WeatherDemo.Store = new mvc.Store(WeatherDemo.model.City);
    WeatherDemo.Store.get(city['id']);

    Let's talk about components

    • mvc.Layout built on goog.ui.Component
      • Component level events
      • Handy selectors
      • More to come
    • mvc.Control built on mvc.Layout
      • bindings to a model
      • can handle models and collections

    2 types of component

    // render to create a component DOM structure
    myComponent.render(el);
    
    // this will fire:
    myComponent.createDom(); // create the DOM structure, and set this.element_;
    // decorate to use existing DOM structure
    myComponent.decorate(el);
    
    // this will fire:
    myComponent.canDecorate(el);// return if DOM structure is eligible
    myComponent.decorateInternal(el); // any transforms on the DOOM needed
    Then enterDocument() will fire.
    Component Lifecycle

    Control events

    • All control events use event delegation (to root element)
    • Handlers are fired based on priority first (then order)
    • returns an object that can .fire() the handler and .off()
    this.on(eventType, fn, opt_selector, opt_handler, opt_priority).fire();

    Convenient model bindings

    • bind methods bind to model (and return bound event objects)
      • this.bind - mvc.Model
      • this.bindAll - mvc.Model
      • this.modelChange - mvc.Collection
      • this.anyModelChange - mvc.Collection
    • automatically clears bindings when disposed
    • setModel() will change bindings to the new model
      single.setModel(WeatherDemo.Store.get(id));

    model bindings

      this.autobind('.city', 'City: {$city}');
      this.autobind('.temp', '{$farenheit} °F');
      this.bind('temp', function(temp) {
        $(this.getElement()).css('left', temp * 10 - 300);
      }).fire();

    autobind does it all!

    this.autobind('', {
        reqs: ['celcius'],
        reqClass: ['hot', 'mild'],
        chooseClass: function(celcius) {
          if (celcius > 30)
            return 'hot';
          if (celcius < 20)
            return 'mild';
        }
      });

    even two way binding

      this.autobind('.city', '{$city}');
      this.autobind('.temp', '{$temp}');
      this.autobind('.celcius', '{$celcius}');
      
      // used to blur on enter so autobind will apply
      this.on(goog.events.EventType.KEYUP, function(e) {
        if (e.keyCode === goog.events.KeyCodes.ENTER) {
          e.target.blur();
        }
      }, 'input');

    autolist

    this.autolist(WeatherDemo.control.Simple, content);
    or override placement:
    WeatherDemo.control.List.prototype.placeChild_ = function(
        contentEl, child, index) {
      $(child.getElement()).css({
        'top': index * 45
      });
    };

    Let's see some code

    More tools!

    • templates
    • linting
    • stylesheets
    • class minification
    • testing

    Closure Templates

    • Compiled
    • Extensible functions
    • Client and Server-side
    • Translatable strings

    Closure Linter

    Linting

    gjslint -r js
    
    ...
    Found 17 errors, including 1 new errors, in 7 files (0 files OK).
    
    ... The script can be run by executing: fixjsstyle -r js 
    
    fixjsstyle -r js

    gjslint -r js
    ...
    Found 8 errors, including 0 new errors, in 5 files (2 files OK).
    

    Closure Stylesheets

    • Variables
    • Extensible functions
    • Mixins
    • Conditionals
    • Minification (including class name)
    • Linting

    Class minification

    • Use Closure Stylesheets (coming soon in plovr)
    • '-' separator
    • goog.getCssName()
    • {css className} in templates
    // renaming separated by -
    
    goog.getCssName('class-subclass') ==
        goog.getCssName('class') + '-' + goog.getCssName('sublcass');
    
    // select by class in The G and PlastronJS
    
    $(goog.getCssName('-class'));

    What's next?

    Buy this book!

    Questions?