Lens 2 Internal Documentation

See the front page of the API documentation for how to create and run Lens applications.

Loading Lens Dynamically

http://lens2.lens/dist/latest/lens.js is a compiled version of Lens. The webserver automatically compiles Lens using the RequireJS optimizer if any files have changed since the last time lens was requested. This compilation step takes about 10 seconds. If you're not working on Lens itself, that should be fine-- it'll just be slow to load the first time you load an app after pull a new version of Lens. However, if you're doing work on Lens, you probably don't want to wait 10 seconds on each refresh. In that case, you can use require.js to load the uncompiled Lens dynamically. Use this script tag:

<script type="text/javascript"
        src="http://lens2.lens/lib/require.js"
        data-main="http://lens2.lens/src/lens.js"
        data-app-main="demo.js"></script>

Where data-app-main is the path to your main Javascript file (which can then call Lens.init and use Lens). The caveat of this method is that Lens will leak the global variables require, define, and requirejs, which will cause conflicts if your app uses RequireJS. All the apps in apps/demos load Lens this way, so it's recommended that you work with the demo apps while you're editing Lens, and then use http://lens2.lens/dist/latest/lens.js when working on non-demo apps.

Lens Toolchain

The toolchain consists of a set of scripts in the scripts directory. They are designed to be run with the lens directory as the working directory.

Documentation

The documentation can be generated with scripts/gen-docs.sh. You'll need to have a jsdoc command in your PATH that run jsdoc-toolkit. On Ubuntu, sudo apt-get install jsdoc-toolkit will do the trick. The API docs can be opened with scripts/api-docs.sh and the internal docs can be opened with scripts/internal-docs.sh

Unit Tests

The test suite can be run with scripts/test.sh. The test suite is not currently maintained.

Lens Conventions

Source Layout

README.md         This file
docs/             Autogenerated documentation
docs/api/             Public API documentation
docs/internal/        Internal documentation
scripts/          Development toolchain scripts
src/              Source code
    lens.js           The main loader for Lens -- apps include this directly
    ARCore.js         The loader for ARCore. Loaded by default by Lens.init
    Components.js     Loaded by apps that want all Components.
    LAF.js            Loaded by apps that want the LAF toolkit.
    common/           Common utilities and classes shared by all of Lens
    ARCore/           Core modules that expose specific backend capabilities.
    Components/       Standalone UI componenets
    LAF/              The Lens Look-and-Feel toolkit.
        fonts/            Font options for the LAF toolkit.
        icons/            Icon set options for the LAF toolkit.
        palettes/         Palette options for the LAF toolkit.
        widgets/          Widgets provided by the LAF toolkit.
lib/              Third-party javascript libraries.
test/             Unit test suite

Module Conventions

When creating a new module, use one of the following templates.

Public Module

/**
 * My Module!
 * @namespace
 * @name Lens.MyModule
 */
define(["dep1", "dep2"], function(dep1, dep2) {
    "use strict";

    var privateStaticMethod() {
        // ...
    }

    var privateStaticVariable = 10;

    var MyModule = /** @lends Lens.MyModule */ {

        /** Does cool stuff */
        publicFunction: function() {
            // ..
        }
    }

    Object.freeze(MyModule);

    // Choose one:
    // Lens._addMember(MyModule, "MyModule");                   // for ARCore
    // Lens._addMember(MyModule, "MyModule", Lens.LAF);         // for LAF
    // Lens._addMember(MyModule, "MyModule", Lens.Components);  // for Componenets

    return MyModule;
});

Public Classes

define(["dep1", "dep2", "lib/underscore"], function(dep1, dep2, _) {
    "use strict";

    var privateStaticMethod() {
        // ...
    };

    var privateStaticVariable = 10;

    /**
     * Builds a new MyClass
     * @class A really cool class
     * @property {Number} roProp A read-only property
     * @property {Number} otherProp A property that doesn't change
     * @memberOf Lens
     */
    var MyClass = function() {
        /** @alias Lens.MyClass.prototype */
        var self = {};

        var privateMember = 10;
        var privateFunction = function() {};

        /**
         * My awesome public function.
         */
        self.publicMethod = function() {};

        /**
         * My function that should only be called within Lens. This won't
         * show up in the API documentation, but will show up in the
         * internal documentation.
         *
         * @private
         */
        self._internalMethod = function(){};

        // this is how we define getters
        Object.defineProperty(self, "roProp", {
            get: function() { return privateMember * 10; }
        });

        // if your property doesn't change after initialization, just add
        // it directly to self. It'll be read-only because self gets frozen.
        self.otherProp = 20;

        Object.freeze(self);
        return self;
    };

    _.extend(MyClass, /** @lends Lens.MyClass */ {
        /** An awesome class function */
        publicClassFunction: function() {
            // ...
        }
    });

    Object.freeze(MyClass);

    // Choose one:
    // Lens._addMember(MyClass, "MyClass");                   // for ARCore
    // Lens._addMember(MyClass, "MyClass", Lens.LAF);         // for LAF
    // Lens._addMember(MyClass, "MyClass", Lens.Components);  // for Componenets

    return MyClass;
});

Documented Internal Classes

This is for classes whose constructor should not be made available to apps, but should still show up in documentation. This is for data structures like Touch, where apps shouldn't be creating their own objects, but need documentation on the class because Lens will give developers pre-made instances. Note the use of @privconstructor

define(["dep1", "dep2", "lib/underscore"], function(dep1, dep2, _) {
    "use strict";

    /**
     * Builds a new MyClass
     * @privconstructor
     * @class A really cool class
     * @name MyClass
     */
    var MyClass = function() {
        /** @alias MyClass.prototype */
        var self = {};

        var privateMember = 10;
        var privateFunction = function() {};

        /**
         * My awesome public function.
         */
        self.publicMethod = function() {};

        Object.freeze(self);
        return self;
    };

    return MyClass;
});

Internal Module

Modules that should not be part of the public Lens API.

/**
 * My Internal Module!
 * @namespace
 * @name InternalModule
 * @private
 */
define(["dep1", "dep2"], function(dep1, dep2) {
    "use strict";

    var privateFunction() {
        // stuff
    }

    var InternalModule = /** @lends InternalModule */ {
        /** Does cool stuff */
        publicFunction: function() {
            // stuff
        }
    }

    return InternalModule;
});

Internal Classes

Classes that should not be part of the public Lens API.

define(["dep1", "dep2", "lib/underscore"], function(dep1, dep2, _) {
    "use strict";

    /**
     * Builds a new MyClass
     * @class A really cool class
     * @name MyClass
     * @private
     */
    var MyClass = function() {
        /** @alias MyClass.prototype */
        var self = {};

        var privateMember = 10;
        var privateFunction = function() {};

        /**
         * My awesome public function.
         */
        self.publicMethod = function() {};

        Object.freeze(self);
        return self;
    };

    return MyClass;
});

Coding Conventions

  • Indentation must be with 4 spaces.

  • The indentation style is Compact control readability style. This is essentially One True Brace with a newline before else:

    if(statement) {
        someCode()
    }
    else if(otherStatement) {
        someMoreCode();
    }
    else {
        someOtherCode();
    }
  • Lens defines only the global variable Lens. Everything else must be defined inside the Lens namespace. No changes should be made to the prototypes of built-in classes.

  • Lens uses RequireJS modules. Lens' version of RequireJS is stored in Lens.require and Lens.define. No code should depend on any globals other than Lens, rather, they should use Lens.require to load in e.g. jQuery. Code should also not assume that other Lens modules are loaded: if your code depends on Lens.Dispatcher, you must explicitly use a Lens.require call to load the dispatcher and then reference it via the argument passed to your Lens.require callback.

  • Lens modules should use underscores for the module/file name and be imported in UpperCamelCase, e.g. Lens.require('foo_bar', function(FooBar){}).

  • Third-party libraries should be modified to act as RequireJS modules. These modifications should be clearly marked with // LENS MODIFICATION

  • If a module can be run in a browser-only (no backend) mode, it should register a function to enable this mode by calling Lens.onDev();

  • Semicolons must be explicitly included at the end of lines. Do not rely on Javascript's automatic semicolon insertion.

  • All files must use strict mode. Start each define function with "use strict";

  • console.log should never be used. Instead, require common/util/log and create a logger for your module.

Documentation Conventions

  • JSDoc3 is used for documentation. All exported methods and variables must have a doc comment.

  • Modules and functions that should be excluded from the API (but included in the internal docs) must be marked with @private. See Module Conventions for how to do this.

Acknowledgements

Lens LAF draws on existing work:

Reactivity and databases are taken from Meteor: