Guides

Here is on overview over all modules covered in these guides:

Module Description
betajs-scoped BetaJS-Scoped is a small module for scoped loading of modules and dependencies.
betajs BetaJS is a general-purpose JavaScript helper module. It contains a variety of helper functions and classes.
betajs-browser BetaJS-Browser is a client-side JavaScript framework for Browser-specific methods.
betajs-data BetaJS-Data is a general-purpose JavaScript framework for handling RESTful operations and ActiveRecord abstractions.
betajs-server BetaJS-Server is a server-side JavaScript framework extension for BetaJS.
betajs-dynamics BetaJS-Dynamics is a dynamic DOM templating engine.
betajs-ui BetaJS-UI is a library for enabling gestures and interactions such as drag and drop.
betajs-flash BetaJS-Flash is a Flash-JavaScript bridging framework
betajs-media BetaJS-Media is a JavaScript media framework
betajs-media-components BetaJS-Media-Components is a JavaScript media UI components framework
betajs-debug BetaJS-Debug is a library for debugging BetaJS-based applications.
grunt-betajs-templates Build BetaJS templates.
grunt-betajs-docs-compile Build BetaJS documentations based on JSDOC.
betajs-codemirror BetaJS-Codemirror is a Codemirror Plugin for the BetaJS Framework.
betajs-richeditor BetaJS-Richeditor is a rich editor plugin based on content editable using the BetaJS Framework.
betajs-chartjs BetaJS-ChartJS is a ChartJS Plugin for the BetaJS Framework.
betajs-shims This repository includes shims for ECMA Script that are not included in the official shims.
betajs-workers BetaJS-Workers is a light-weight library for accessing web workers uniformly and conveniently.
mock-ajax-server BetaJS Mock Ajax Server for Testing
mock-file-server BetaJS Mock File Server for Testing
betajs-compile BetaJS-Compile is a helper repository for building betajs modules.


betajs-scoped

BetaJS-Scoped is a small module for scoped loading of modules and dependencies.

Introduction to Scoped

The Scoped system allows you to resolve and define module dependencies, similar to RequireJS.

Setting up Scoped

The Scoped system needs to be loaded once in the beginning. It has no dependencies itself.

It can be either loaded directly by including Scoped:

   <script src="//cdn.rawgit.com/betajs/betajs-scoped/master/dist/scoped.min.js"></script>

Or you can compile it into one of your libraries.

You can even include it more than once without any issues.

How to use Scoped

The Scoped system allows you to handle multiple libraries with separate namespaces and scopes.

Scopes in the Scoped system are organized in a tree structure.

Subscopes of a given scope inherit all namespacing definitions.

Given a scope, you can access the following different main pre-defined namespaces:

  • global: this is the globally accessible namespace in JavaScript; in the browser, this would be window
  • root: the root namespace of the scope in the scope tree
  • local: the namespace local to the current scope
  • default: a namespace completely private to the current scope
  • parent: the local namespace of the parent scope

Namespace declarations are always given in the following notation: parent:path where parent is an existing namespace declaration and path is an object path identifier like Main.Sub.Subsub.

A globally registered jQuery would therefore be accessible via global:jQuery.

You can also register new namespace for your own use:

Scoped.binding("my_namespace", "parent:path")

You can then use my_namepace the same way you use the pre-defined namespaces.

In order to define a subscope, you'd write:

var MyScoped = Scoped.subScope();

A typical blueprint to structure your own library using Scoped is this:


(function () {

var Scoped = this.subScope();

Scoped.binding("module", "global:MyLibrary");
Scoped.binding("dependency1", "global:ExternalDependency1");
Scoped.binding("dependency2", "global:ExternalDependency2");

// Library code

}).call(Scoped);

This closure blueprint makes sure not to clutter the namespace of the rest of the environment.

One of the benefits of the Scoped system is that you can allow libraries to access external dependencies with different names and even overwrite the namespaces a library attaches to, so you can include two different versions of the same library without clashing their namespaces.

If you want a library to use or attach to different namespaces then its default ones, you need to overwrite them as follows before including the library:

    Scoped.nextScope().binding("module", "global:MyLibraryAlternateNS", {
        readonly: true
    });

Defining dependencies is based on three primitives:

  • Scoped.require: execute code once dependencies are resolved
  • Scoped.define: define a module in a namespace once dependencies are resolved
  • Scoped.extend: extend an existing module in a namespace once dependencies are resolved

The syntax is as follows:

Scoped.require(['ns1:dependency1', 'ns2:dependency2', 'ns3:dependency3'], function (D1, D2, D3) {
    // Execute once D1, D2, D3 are resolved.
});

Scoped.define('ns:module', ['ns1:dependency1', 'ns2:dependency2', 'ns3:dependency3'], function (D1, D2, D3) {
    // Execute once D1, D2, D3 are resolved.
    return {
        // Return ns:module definition.
    };
});

Scoped.extend('ns:module', ['ns1:dependency1', 'ns2:dependency2', 'ns3:dependency3'], function (D1, D2, D3) {
    // Execute once D1, D2, D3 are resolved.
    return {
        // Return ns:module extension.
    };
});

Debugging Scoped Programs

Debugging Scoped-based programs normally boils down to understanding why some dependencies are not being resolved.

The main reason for unresolved dependencies are typos.

The Scoped system has support for telling you which dependencies are not met at a certain point, so you can backtrack the root cause of the problem.

At any time, you can call the method Scoped.unresolved(namespace) that will return an array of unresolved definitions within the given namespace.

For instance, if you want to know which definitions are unresolved in the globally accessible namespace, you would call Scoped.unresolved("global:").

Compiling Libraries with Scoped

You can use the Scoped system to pre-compile libraries based on Scoped in order to only include required sub modules.

The grunt configuration looks as follows:

grunt.initConfig({
  scoped: {
      dist: {
          dest: "compiled.js",
          externals: ["namespace1", "namespace2"],
          src: [{
                 src: "source1.js",
                 bindings: {
                        "binding1": "namespace1",
                        "binding2": "namespace2"
                 },
                 full: false,
                 require: ["namespaceX", "namespaceY"]
          }, {
            ...
          }]
      }
  }
});

grunt.loadNpmTasks('betajs-scoped');

betajs

BetaJS is a general-purpose JavaScript helper module. It contains a variety of helper functions and classes.

Object Orientation

BetaJS adds basic object orientation with static methods, object methods, attributes, class variables,
inheritance, mixins, helper classes and more to JavaScript.

Define a class uses the following syntax

  ChildClass = ParentClass.extend(Namespace, InstanceExtension, ClassExtension, ClassStaticExtension);
  • ChildClass is the newly created Class object
  • ParentClass is an existing Class object; in many cases, this is BetaJS.Class
  • Namespace an optional namespace that the class should attach to; can be null; see below
  • InstanceExtension a json object defining methods and attributes; see below
  • ClassExtension a json object defining class methods and class variables; see below
  • ClassStaticExtension a json object defining class methods and class variables not shared with child classes

Here is a basic example:

  TestClass = BetaJS.Class.extend(null, {

    y: 0,

    fooBar: function (x) {
      console.log("Test Class Instance", "fooBar", x, y);
    }

  }, {

    staticFooBar: function (x) {
      console.log("Test Class", "staticFooBar", x);
    }

  });

And here is how we would use it:

  TestClass.staticFooBar(5);
  var first = new TestClass();
  first.y = 1;
  first.foobar(2);
  var second = new TestClass();
  second.y = 3;
  second.foobar(4);

Resulting in the following output:

  Test Class  staticFooBar  5
  Test Class Instance  fooBar  2  1
  Test Class Instance  fooBar  4  3

If we need inheritance by calling a parent method, we need the following extension syntax:

  TestClass2 = TestClass.extend(null, function (inherited) {
    return {

        fooBar: function (x) {
          console.log("TestClass2 Instance", "fooBar", x);
          inherited.fooBar.call(this, x);
        }

    };
  });

And here is how we would use it:

  var third = new TestClass2();
  third.y = 6;
  third.foobar(7);

Resulting in the following output:

  Test Class2 Instance  fooBar  7
  Test Class Instance  fooBar  7  6

In many cases, you'd want to write code that get executed upon creation of the class, as well as code for the explicit deallocation of the object.

  TestClass3 = TestClass2.extend(null, function (inherited) {
    return {

        constructor: function (y) {
          console.log("Create Object");
          inherited.constructor.call(this);
          this.y = y;
        },

        destroy: function () {
          console.log("Destroy Object");
          inherited.destroy.call(this);
        }

    };
  });

Here is how we would use it:

  var fourth = new TestClass3(8);
  fourth.foobar(9);
  fourth.destroy();

Resulting in the following output:

  Create Object
  Test Class2 Instance  fooBar  9
  Test Class Instance  fooBar  9  8
  Destroy Object

Notifications

Notifications allow you to send messages within an instance. This is particularly useful in combination with Mixins as it allows multiple methods to be executed when a certain event happens.

To trigger a notification, you call _notify within the scope of an instance method:

    this._notify("my_internal_event", param1, param2);

The same instance, derived instances as well as derived instances using mixins can now listen to it as follows:

   _notifications: {
      "my_internal_event": function (param1, param2) {
        // handler code
      }
   }

You can also outsource the function:

   _notifications: {
      "my_internal_event": "internal_event_call_handler"
   },

   internal_event_call_handler: function (param1, param2) {
     // handler code
   }

Mixins

A mixin is an additional set of methods and attributes that can be mixed in while extending a class:

  SubClass = ParentClass.extend(null, [Mixin1, Mixin2, ..., {
    // actual code of sub class
  }]);

A mixin itself is a simple json object:

  Mixin1 = {
    method1: function () {...},
    attr1: "foobar"
  };

Instances of SubClass contain all methods and attributes from all mixins:

    var instance = new SubClass();
    instance.method1();
    var a = instance.attr1;

Mixins cannot inherit from a parent class, but they can react to instance notifications, e.g.:

  Mixin2 = {
    _notifications: {
      "construct": function () {
        // TODO
      },
      "destroy": function () {
        // TODO
      }
    }
  };

Reflection

Here are some useful reflection methods that you can use at runtime.

Given an instance instance, you can execute the following reflections:

  • instance.cls: the class that instance is an instance of
  • instance.instance_of(cls): is this instance an instance of cls?
  • instance.destroyed(): is this instance already destroyed?
  • instance.weakDestroy(): destroy only if not already destroyed
  • instance.cid(): unique id of instance
  • instance.auto_destroy(obj): automatically destroy obj when destroying instance

Given a class cls, you can execute the following reflections:

  • cls.parent: the parent class
  • cls.children: all direct child classes
  • cls.classname: class name as a string
  • cls.ancestor_of(cls2): is cls an ancestor of cls2?
  • cls.is_class(cls2): is cls2 a class?
  • cls.is_class_instance(instance): is instance a class instance?
  • cls.is_instance_of(instance): is instance a class instance and an instance of this class?

Event System as Observer Pattern

The event system allows you to emit different events with custom event data from class instances
that other components can listen to.

Event emitters can either inherit from the BetaJS.Events.Events class or include the BetaJS.Events.EventsMixin mixin.

    var events = new BetaJS.Events.Events();

Events can be emitted by calling the trigger function. The first argument is the name of the event, all following arguments are custom data that is passed along.

    events.trigger("event_name", event_data1, event_data2);

Other components can listen to events by using the on method:

    events.on("event_name", function (event_arg1, event_arg2) {
        // Do something
    }, function_context);

The function context is optional.

Once the component is done listening for events, it can unregister by calling off:

    events.off("event_name", null, function_context);

Introduction to Properties

Instances of type Properties allow setting and getting of arbitrary attributes (including nested JSON).

    var properties = new BetaJS.Properties.Properties({
        a: "initial value"
    });

    properties.set("a", "second value");

    var x = properties.get("a");
    // x === "second value"

It implements the Events Mixin and emits change events whenever attributes are introduced, changed or removed.

    properties.on("change:a", function (value, oldValue) {
        console.log("a has been changed: ", value);
    });

    properties.set("a", "third value");
    // will trigger the change event

It supports uni-directional and bi-directional data-binding to other properties instances.

Finally, it supports computed attributes that depend on other attributes in the object.

Advanced Properties

Declaring a property and assigning new values to it


    var properties_a = new BetaJS.Properties.Properties();

    var x = properties_a.get("value_1");
    // x === undefined

    var properties_b = new BetaJS.Properties.Properties({
        value_2: "This is Value 2"
    });

    var x = properties_b.get("value_2");
    // x === "This is Value 2"

    properties_a.set("value_1", "Some Property Value");

    var x = properties_a.get("value_1");
    // x === "Some Property Value"

Nested Properties


    var properties = new BetaJS.Properties.Properties();

    properties.set("prop.value_a","This is value_a");

    var x = properties.get("prop.value_a");
    // x === "This is value_a"

Computed Properties

Computed Properties are Properties that use other Properties as a Basis and the value
of the computed Property will be changed automatically if the Properties they are based on are changed.
This is done automatically by the Events System.


    var properties = new BetaJS.Properties.Properties();

    properties.set("value1","This is value 1");
    properties.set("value2","This is value 2");

    properties.compute("computed_value", function () {
        return "The Values are: " + this.get("value1") + ", " + this.get("value2");
    },["value1", "value2"]);
    properties.get("computed_value");
    //Will compute to: "The Values are: This is value 1, This is value 2"

    properties.set("value1","Value 1 has changed");
    properties.get("computed_value");
    //Will now compute to: "The Values are: Value 1 has changed, This is value 2"

Collections

A collection is a dynamic list that allows for observers to listen for change events. Every item in a collection is based on a properties instance.

Create a Collection:

    var collection = new BetaJS.Collections.Collection();

Create a Collection with Data inside:

    var collection = new BetaJS.Collections.Collection({
        objects: [
            {title: "This is Item 1"},
            {title: "This is Item 2"},
            {title: "This is Item 3"}
        ]
    });

You can add, remove and access items as follows:

    collection.add(item);
    collection.remove(item);
    collection.getByIndex(0);

Item can either implement Properties or be a bare JSON object in which case it will be converted to a Properties instance.

It implements the Events Mixin and emits change events whenever items are added, changed or removed.

    collection.on("add", function (item) {
        // TODO: item has been added
    });

    collection.on("remove", function (item) {
        // TODO: item has been removed
    });

    collection.on("update", function (item, key, value) {
        // TODO: item has been updated
    });

Filtered Collections

Given a collection, you might want to derive one (or many) subcollections that filter the content of the base collection.

    var collection = new BetaJS.Collections.Collection();

You can then define the filtered collection as follows:

    var filtered_collection = new BetaJS.Collections.FilteredCollection(collection, function (item) {
        // return true if item should be contained in this collection.
    }, optional_context);

The filtered collection updates automatically when the parent collection is updated, and vice versa.

Example:

    var collection = new BetaJS.Collections.Collection(
        {
            item1: {
                string: "This is Item 1",
                value: 1
            }
            item2: {
                string: "This is Item 2",
                value: 2
            }
            item3: {
                string: "This is Item 3",
                value: 3
            }
        }
    );

    var filtered_collection = new BetaJS.Collections.FilteredCollection(collection, function (item) {
        return item.get("value") === 2;
        // The new collection will only contain item2 from the collection above.
        // i.e. filtered_collection.count() == 1
    }, optional_context);

    // However if you now add an item to the original collection that has also a value of 2, it will also be in the filtered collection, i.e.:
    collection.add({
        string : "This is a fourth Item also with value 2",
        value : 2
    })
    // i.e. filtered_collection.count() == 2

Enumerable Lists - Iterators

An iterator is an object that allows you to enumerate items of some list. The most commonly used iterator is based on an array:

    var iterator = new BetaJS.Iterators.ArrayIterator(["a", "b", "c"]);

An iterator instance exposes two methods: hasNext, telling you whether there are more elements in the iterator
that you haven't seen yet, and next, which returns the next element in the iterator and moves the internal
position forward.

A typical iterator use looks as follows, enumerating all items:

    while (iterator.hasNext()) {
        var item = iterator.next();
        // process the item
    }

Iterators in general do not know how many elements are in the list. If you need to know the number of elements,
you need to iterate through the whole list.

The main advantages of iterators over using arrays or objects are as follows:

  • Independence of the underlying data structure: the algorithm consuming the iterator does not depend on the data being an array
  • Stream-like data consumption: lazy iterators can build up the data as you move along in the iteration
  • Chaining iterators to create new iterators

If you have an object instead of an array, you can create an object key iterator as follows:

    var iterator = new BetaJS.Iterators.ObjectKeysIterator({a: 1, b: 2, c: 3});

As well as a value iterator:

    var iterator = new BetaJS.Iterators.ObjectValuesIterator({a: 1, b: 2, c: 3});

Advanced Iterators

In some cases, you want to perform certain transformations on a list before actually processing it. Such transformations
include filtering items, sorting the list, skipping items and limiting the number of items. You might even want to
combine some of these transformations.

Iterators can be chained in the following sense: given an iterator, you can automatically create a new iterator based on
it while performing a transformation on it.

For the sake of this paragraph, assume that we already have an iterator object called baseIterator (which could be an ArrayIterator).

For filtering items, you can use the FilteredIterator:

    var iterator = new BetaJS.Iterators.FilteredIterator(baseIterator, function (item) {
        // return true if item should be contained in iterator 
    }, this);

For sorting items, you can use the SortedIterator:

    var iterator = new BetaJS.Iterators.SortedIterator(baseIterator, function (item1, item2) {
        // return 1 if item1 > item2, -1 if item1 < item2 and 0 if item1 == item2 
    });

For skipping e.g. 10 items, you can use the SkipIterator:

    var iterator = new BetaJS.Iterators.SkipIterator(baseIterator, 10);

For limiting the result to e.g. 50 items, you can use the LimitIterator:

    var iterator = new BetaJS.Iterators.SkipIterator(baseIterator, 50);

You can also combine these iterators to, e.g. skip 10 items and return 50:

    var iterator = new BetaJS.Iterators.LimitIterator(new BetaJS.Iterators.SkipIterator(baseIterator, 10), 50);

Asynchronous Calls using Promises

A promise is an abstraction for handling asynchronous callbacks. It allows you to disconnect the handling of an asynchronous event from the emitter.

Normally, you'd write a function that requires an asynchronous callback as follows:

   function emitter(callback) {
      // some code that ends up calling callback()
   }

You would consume it as follows:

   emitter(function (...) {
     // some code that should be called in the callback
   });

Promises try to disconnect this as follows:

   function emitter() {
      var promise = BetaJS.Promise.create();
      // some code that ends up calling promise.asyncSuccess();
      return promise;
   }

You would consume it as follows:

   var promise = emitter();
   // do some other stuff
   promise.success(function (...) {
     // some code that should be called in the callback
   });

Now the handling of the event is disconnected from the emitting part.

Emitting Promises

Emitting promises is easy, and requires three steps:

  • Create a promise instance: var promise = BetaJS.Promise.create();
  • Return the promise instance in the end: return promise;
  • Call the asynchronous success and error handlers for callbacks: promise.asyncSuccess(value) and `promise.asyncError(error)

Handling Promises

Once you receive a promise from an emitter, you can react to both success and error callbacks:

   var promise = emitter();

   promise.success(function (value) {
     // code
   });

   promise.error(function (error) {
     // code
   });

Advanced Uses

The Promise module has additional functionality for advanced use. We highlight some of it here.

Often, you want to transform the successful result value into something else but leave the error message alone:

   var promise2 = promise1.mapSuccess(function (value) {
     return do-computation-with-value;
   });

This results in a new promise.

You can even combine two dependent promise calls this way:

   var promise3 = promise1.mapSuccess(function (value) {
     var promise2 = call_function_that_returns_a_promise(value);
     return promise2;
   });

The success call of promise3 then depends on the successful calls of promise1 and subsequently promise2.

Functional Helpers for Objects and Arrays

The Objs module includes support functions for handling javascript objects and arrays.

You can, for instance, iterate over arrays and objects as follows:

    BetaJS.Objs.iter({key1: "value1", key2: "value2"}, function (value, key) {
       console.log(key, " : ", value):
    });
    // Will print:
    //   key1 : value1
    //   key2 : value2

    BetaJS.Objs.iter(["value1", "value2"], function (value, index) {
       console.log(index, " : ", value):
    });
    // Will print:
    //   0 : value1
    //   1 : value2

You can also map all values in an array or object:

    var result = BetaJS.Objs.map({key1: 4, key2: 5}, function (value, key) {
       return value * value:
    });
    // result === {key1: 16, key2: 25}

    var result = BetaJS.Objs.map([4, 5], function (value, index) {
       return value;
    });
    // result === [16, 25]

Identifiable Objects

The BetaJS system has a small library for generating unique IDs. This can be used for all kinds of purposes, but primarily for uniquely identifiying objects.

Since JavaScript has no notion of using pointers (to objects) as keys in hashes, it is impossible to save an identifier to an object without artificial ids.

Unique ids can be generated as follows:

    var id = BetaJS.Ids.uniqueId(); // e.g. "123"
    var idx = BetaJS.Ids.unqueId("prefix"); // e.g. "prefix123"

Given an object, we can read (if already set) or set and read its unique id as follows:

   var obj = {...};
   var id = BetaJS.Objs.objectId(obj);

Note that objectId creates a new attribute within obj called cid.

Id Generators

There is also a collection of different id generating classes that you can use for specific use cases:

  • IdGenerator: Abstract Id Generator class
  • PrefixedIdGenerator: Takes a given generator and prefixes it with a string
  • RandomIdGenerator: Generates ids randomly; no collision test.
  • ConsecutiveIdGenerator: Generates ids in a consecutive fashion using ints.
  • TimedIdGenerator: Uses the current time as ids in a unique fashion.

Generators can be used as follows:

    var generator = new TimedIdGenerator();
    var id = generator.generate();

State Machines

The system includes an abstract state machine that can be used for internal business logic as well as in combination with the router.

A state machine consists of a state machine host instance which holds the current state instance. States itself are defined by subclassing the abstract state class.

To initialize a state machine, simply call:

   var host = new BetaJS.States.Host();

There are different ways to define new states. An adhoc way, if the state machine is used as a singleton, is as follows:

    host.register("A", {});

which registers a new state A.

A more general way is by directly subclassing it:

    var S = BetaJS.States.State.extend("BetaJS.Test.S");
    var A = S.extend("BetaJS.Test.A");
    var B = S.extend("BetaJS.Test.B");

After setting up the state machine, we start with an initial state:

   host.initialize("A");

The current state can now be accessed via host.state().

To transition to the next state, we simply call host.state().next("B").

Routers

The Router package adds Routing functionality to BetaJS.

This functionality is particularly useful in Single Page Web applications, as it creates an easy abstraction for navigating the internal structure of the application.

The Router in the BetaJS system is completely abstract.

Setup

A router is created by creating an instance of the Router object.

  var router = new BetaJS.Router.Router();

Binding Routes

The just created router object does nothing at this point, because it does not
know about any routes. So the next step is binding routes to router.

  /**
   * @param {string} name
   * @param {string} routeUrl
   */
  router.bind("test", "/test");

Navigation

It is now possible to navigate to "test" by calling the navigate method.

  router.navigate("/test");

Route Events

When the router navigates to "/test", it triggers an event. Learn more about events by looking at the BetaJS.Events.Events documentation. The name of the event is "dispatch:test". More generally, it is "dispatch:ROUTENAME".

Conducting an action based on the current route is as simple as listening for the proper event.

  var events = BetaJS.Events.Events();
  events.on("dispatch:test", function() {
    console.log("Navigated to test.");
  });

Routes with Arguments

Now, suppose you want to navigate to the fourth post on a blog site. First, bind the route.

  /**
   * ...
   * @param {function} callback
   */
  router.bind("post", "/posts/(id:[0-9]*)", function(args) {
    console.log("You navigated to blog post #" + args.id);
  });

This route binding looks different because it contains a URL argument. It is possible to bind arguments from the url, which will be passed when the Event is omitted. Additionally, the previous example shows a new way to execute code during routing. The callback function will be called when the route is navigated to. It receives arguments from the url as the first argument to the callback.

Naturally, much more can be done with routers, but this is an introduction to the basic features.

Routers and State Machines

You can combine routers with state machines. State machines in general provide a richer abstraction for state spaces while routers can be thought of a string serialization and parsing system for states.

Initialization

Initialize the router, the state machine and the binder as follows:

    var router = new BetaJS.Router.Router();
    var host = new BetaJS.States.Host();
    var binder = new BetaJS.Router.StateRouteBinder(router, host);

Registration

You can now register routes and state separately and connect some of them, or you bind them automatically:

    binder.register("simple", "/simple");
    binder.register("polymorphic", "/polymorphic/(key:first|second)");

This results in two routes, /simple and /polymorphic/(key:first|second) in the router, saved under the route names simple and polymorphic. Additionally states named Simple and Polymorphic are created within the state machine.

Navigation

You can now perform navigation using the router:

    router.navigate("/simple"); 
    router.navigate("/polymorphic/second");

As well as state transitions using the state machine:

    host.next("Simple");
    host.next("Polymorphic", {key: "first"});

Since the router and state machine are linked to each other, transitions to either one are reflected in the other.

betajs-browser

BetaJS-Browser is a client-side JavaScript framework for Browser-specific methods.

Browser Router

The Browser Router package adds routing functionality to the BetaJS Router that allows you to:

  • bind a router to location hash routes
  • bind a router to location routes
  • bind a router to the browser location history

We assume that we already have created a router:

  var router = new BetaJS.Router.Router();

Then, we can add the location hash route binder as follows:

  var hashRouteBinder = new BetaJS.Browser.HashRouteBinder(router);

Similarly, we can add a location route binder:

  var locationRouteBinder = new BetaJS.Browser.LocationRouteBinder(router);

Finally the history route binder allows you to control the browser history while navigating with your router:

  var historyRouteBinder = new BetaJS.Browser.HistoryRouteBinder(router);

Browser Environment

This module allows you to gather information about the browser environment, e.g.:

  • isIOS(): iOS device?
  • isAndroid(): Android device?
  • isEdge(): Edge browser?
  • isChrome(): Chrome browser?
  • isOpera(): Opera browser?
  • isInternetExplorer(): IE browser?
  • isFirefox(): Firefox browser?
  • isSafari(): Safari browser?
  • isMobile(): mobile device?
  • isDesktop(): desktop device?
  • iOSversion(): iOS version
  • isWindows(): Windows?
  • isMacOS(): Mac OS?
  • isUnix(): Unix?
  • isLinux(): Linux?
  • internetExplorerVersion(): IE version

Loading Assets dynamically

The loader allows you to asynchronously load scripts, styles and html:

    BetaJS.Browser.Loader.loadScript("//.../script.js", function () {
        // Loaded scripts.js
    });

    BetaJS.Browser.Loader.loadStyles("//.../styles.css", function () {
        // Loaded styles.css
    });

    BetaJS.Browser.Loader.loadHtml("//.../web.html", function (content) {
        // Loaded web.html
    });

Uploading Files

Different browsers require different techniques for asynchronously uploading of files and binary large objects (BLOBs).

This library takes care of that.

    var uploader = BetaJS.Browser.Upload.FileUploader.create({
        url: "//.../post",
        source: file_object_or_blob,
        serverSupportPostMessage: false // server supports postMessage fallback for old browsers? 
    });
    uploader.on("success", function (data) {
        // success data from server
    }, this).on("error", function (data) {
        // error data from server
    }, this).on("progress", function (uploaded, total) {
        // progress data
    }, this);
    uploader.upload();

There is also support for uploading multiple files at once:

    var multiUploader = new BetaJS.Browser.Upload.MultiUploader();
    multiUploader.addUploader(uploader1);
    multiUploader.addUploader(uploader2);
    multiUploader.addUploader(uploader3);
    multiUploader.upload();

betajs-data

BetaJS-Data is a general-purpose JavaScript framework for handling RESTful operations and ActiveRecord abstractions.

The Query System

The Query System allows you to formulate queries and evaluate them on a given instance.

We first introduce the query language itself:

/*
 * atoms :== [atom, ...]
 * atom :== string | int | bool | float
 * queries :== [query, ...]
 * query :== {pair, ...}
 * pair :== key: value | $or : queries | $and: queries
 * value :== atom | conditions
 * conditions :== {condition, ...}  
 * condition :== $in: atoms | $gt: atom | $lt: atom | $gte: atom | $le: atom | $sw: atom | $ct: atom | all with ic
 */

A typical query for matching men older than 16 with their first names starting with an "S" would be:

  {
    "gender": "male",
    "age": {
      "$gt": 16
    },
    "first_name": {
      "$sw": "S"
    }
  }

Given a query object query and instance instance, we can evaluate as follows:

  BetaJS.Data.Queries.evaluate(query, instance)

Assuming the query from above, we would have:

  evaluate(query, {"gender": "female", ...}) === false
  evaluate(query, {"age": 16, ...}) === false
  evaluate(query, {"first_name": "Guybrush", ...}) === false
  evaluate(query, {"gender": "male", "age": 17, "first_name": "Simon"}) === true

Constrained Queries

While the query language itself only allows you to specify a condition that instances have to satisfy, constrained queries allow you to additionally limit the number of query results as well as specify the order in which the query results come in.

Given a query, a constrainedQuery looks as follows:

   constrainedQuery = {
     query: query,
     options: {
       limit: integer or null,
       skip: integer or null,
       sort: {
         key1: 1 or -1,
         key2: 1 or -1,
         ...
       }
     }
   }

All options are optional.

The Query Engine

Different query systems have different query capabilities. For instance, one system might not be able to skip search results while another system might not be able to sort. The query engine solves the following problem: given a constrained query, a query system with well-known query capabilities, compute the most efficient super query that the underlying query system can perform, perform the super query and map the super query to the actual query at hand.

It is used internally by the stores.

The Store System

Stores define an abstract database interface. Stores derive from the abstract class BetaJS.Data.Stores.BaseStore which defines the following asynchronous methods:

Insert

   store.insert(instance).success(function (data) {
     // Instance was inserted, and the updated data of instance is data (including the id)
   }).error(function (error) {
     // Could not insert instance
   });

An instance is a json object. Every item in a store is given a unique id upon inserting it. It is then used to identify the item from thereon.

Update

   store.update(id, updatedData).success(function (data) {
     // Instance was updated, and the updated data of instance is data
   }).error(function (error) {
     // Could not update instance
   });

Remove

   store.remove(id).success(function () {
     // Instance was removed
   }).error(function (error) {
     // Could not removed instance
   });

Get

   store.get(id).success(function (instance) {
     // Instance was obtained
   }).error(function (error) {
     // Could not read instance
   });

Query

   store.query(query, constraints).success(function (iterator) {
     // Store was succesfully queried; the query result is an iterator over matched instances.
   }).error(function (error) {
     // Could not execute query
   });

Store Classes

The most important simple stores are:

  • new BetaJS.Data.Stores.MemoryStore(): stores all data in the temporary memory of the browser / NodeJS
  • new BetaJS.Data.Stores.LocalStore({}, localStorage): stores all data in the permanent memory of the browser
  • new BetaJS.Data.Stores.RemoteStore(...): stores all data in a store on a server across a network

Remote Store

The RemoteStore implements a RESTful access to a server-side store. It can be instantiated as follows:

  var store = new BetaJS.Stores.RemoteStore(ajax, restOptions, storeOptions);

Here, ajax needs to be an instance of BetaJS.Net.AbstractAjax, e.g. BetaJS.Browser.JQueryAjax if operated within the context of a browser.

Second, restOptions is a JSON object accepting the following optional configuration parameters:

  • methodMap: an associative map, mapping all store primitives to http methods
  • toMethod: a function, mapping store primitives to http methods
  • dataMap: an associative map, mapping all store primitives to functions returning POST data
  • toData: a function, mapping store primitives to POST data
  • getMap: an associative map, mapping all store primitives to functions returning GET data
  • toGet: a function, mapping store primitives to GET data
  • baseURI: the base URI for all REST calls
  • uriMap: an associative map, mapping all store primitives to functions returning the uri postfix
  • toURI: a function, mapping store primitives to the uri postfix

Delegator Stores

Delegator Stores are Store classes that delegate calls to other, underlying stores while transforming the requests in different useful ways:

  • SimulatorStore: Allows you to simulate the target store being online / offline.
  • ReadyStore: Allows you to queue up and postpone calls to the target store until you acknowledge it to be ready to accept actual calls.
  • ResilientStore: Allows you to automatically retry failed calls to the target store for a certain number of times.
  • TableStore: Allows you to treat a Table as a Store.
  • TransformationStore: Allows you to define mappings from and to the data scheme of the underlying store.
  • MultiplexerStore: Allows you to route calls to a number of underlying stores, depending on the context.
  • ContextualizedStore: Alows you to decode / encode contexts into calls to the underlying store.

Store Indices

Store indices are database indices. By adding indices for certain keys to your stores, you can speed up queries. Indices can be created as follows, given a store:

    store.indices.first_name = new BetaJS.Data.Stores.MemoryIndex(store, "first_name");
    store.indices.last_name = new BetaJS.Data.Stores.MemoryIndex(store, "last_name");

The Model System

The modelling system puts an abstraction layer on top of stores that allows us to treat database instances as properties such that changes in the properties instance are reflected back to the database and vice versa.

The role of json instances in the store system is provided by so-called Models whereas the role of the stores is provied by so-called Tables.

A Model class is a sub class of Properties. When you define your own model, you always define a scheme:

   var MyModel = BetaJS.Data.Modelling.Model.extend(null, {
   }, function (inherited) {
        return {
            _initializeScheme: function () {
                var scheme = inherited._initializeScheme.call(this);
                scheme.first_name = {
                    type: "string"
                };
                scheme.last_name = {
                    type: "string"
                };
                return scheme;
           }
        };
   });

You usually do not instantiate model instances directly. Instead, you create a Table instance based on a store (which stores the actual data):

   var myTable = new BetaJS.Data.Modelling.Table(store, MyModel);

Create new models

This table instance can now be used to create a new model:

   var myModel = myTable.newModel({
       first_name: "Donald",
       last_name: "Duck"
   });
   myModel.save().success(function () {...}).error(function () {...});

Delete a model

Model instances can be removed from the table:

  myModel.remove();

Update a model

Model instances are automatically updated and saved when you change their property attributes:

  myModel.set("first_name", "Daisy");

Obtain a model by id

Model instances can be obtained from the table as follows:

  myTable.findById(id).success(function (myModel) {
    ...
  }).error(function (error) {...});

Obtain a single model by a query

Model instances can be obtained from the table as follows:

  myTable.findBy({first_name: "Donald"}).success(function (myModel) {
    ...
  }).error(function (error) {...});

Obtain multiple models by a query

Model instances can be obtained from the table as follows:

  myTable.allBy(query, constraints).success(function (modelIterator) {
    ...
  }).error(function (error) {...});

Model Attribute Validations

Validators allow you to add value validations to your model scheme that prevent invalid attributes from being saved to the store. You can add validators to your model scheme as follows:

            _initializeScheme: function () {
                var scheme = inherited._initializeScheme.call(this);
                scheme.first_name = {
                    type: "string",
                    validate: new BetaJS.Data.Modelling.Validators.PresentValidator()
                };
                scheme.last_name = {
                    type: "string",
                    validate: new BetaJS.Data.Modelling.Validators.PresentValidator()
                };
                return scheme;
           }

The validate arguments admits both direct validator instances as well as an array of validator instances if you need to attach more than one validator.

The system currently supports the following validators:

  • PresentValidator: ensures that the value is not null or empty
  • LengthValidator({min_length, max_length}): ensures that the value is of a certain string length
  • EmailValidator: ensures that the value is an email
  • UniqueValidator(key): ensures that the value is unique in the store
  • ConditionalValidator(condition, validator): ensures that a validator is valid given that the condition is satisfied

Query Collections

Query Collections are collections which are based on a store or a table query. Its items are either instantiated properties from the query result (if it is based on a store) or instantied models (if it is based on a table).

Query Collections should be seen as a read only representation of a query. You should never add elements directly to a query collection. Instead, you should add the instance to the store or the table.

   var tableQC = new BetaJS.Data.Collections.TableQueryCollection(table, query, options);
   var storeQC = new BetaJS.Data.Collections.StoreQueryCollection(store, query, options);

The following (among other) options are supported:

  • active: should the query collection actively update itself? (default false)
  • incremental: whenever the query is updated, should only the diff be queried? (default true)
  • active_bounds: if the query collection is bounded and new items are added, should the bound be extended? (default true)
  • range: limit query results (default disabled)
  • auto: should the system immediately run the query (default false)

betajs-dynamics

BetaJS-Dynamics is a dynamic DOM templating engine.

Introduction

BetaJS Dynamics Overview

BetaJS Dynamics is a JavaScript frontend framework. It generally has two parts:

  • Dynamics : a JavaScript Controller interface
  • HTML Templates : HTML Template / Element containing a fragment of JS, so called partials, handlebars and subdynamics

Example

The Javascript Controller:


    var dynamic = new BetaJS.Dynamics.Dynamic({
        element: document.querySelector("some_element").innerHTML,
        initial : {
            attrs : {
                some_attribute : "This is some Text",
                some_boolean : true
            }
        }
    });

The HTML Element:

<script type='text/template'>
    <some_element ba-if="{{some_boolean}}">{{some_attribute}}</some_element>
</script>

Will evaluate to


    <some_element>This is some Text</some_element>

Hello World Example

Our "Hello World" a one-page html that loads the BetaJS framework and some other required files in the head. And runs a very basic "Hello World" script in the body.

If everything is set up correctly you should see "Hello World" if you load the helloworld.html displayed in the box below in the your browser, and making sure that all the assets are present as well.


    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title>Hello World</title>
        <script src="beta.js"></script>
        <script src="betajs-browser-noscoped.js"></script>
        <script src="betajs-dynamics.js"></script>
    </head>
    <body>

        <div id="helloworld">{{replaced_value}}</div>

        <script>

            var dynamic = new BetaJS.Dynamics.Dynamic({
                element: document.querySelector("#helloworld")
            });

            dynamic.set("replaced_value", "Hello World");
            dynamic.activate();

        </script>

    </body>
    </html>

Now lets go briefly through the individual parts:


    <div id="helloworld"> {{replaced_value}} </div>

Inside {{}} ist an attribute property that we can control from other parts of the application.


    dynamic = new BetaJS.Dynamics.Dynamic({
        element: document.querySelector("#helloworld")
    });

Here, we are creating a new dynamic, and make it responsible for handling the subtree under the dom node #helloworld.


    dynamic.set("replaced_value", "Hello World");

Here, we create set an attribute "replaced_value" and give it the value "Hello World". When the dynamic is loaded, the handlebars {{replaced_value}} will be replaced by its value in the DOM so that we see "Hello World" on the actual site.

Dynamic attributes are a key value store based on the BetaJS Properties class.

The HTML view will update itself automatically when attributes in the dynamic are changed.


    dynamic.activate();

This last part activates the dynamic and binds the HTML to the JS controller.

Core Concepts

Dynamics

Dynamics are at the very core of this framework, representing its view components.

Handlebars

Handlebars are a way to do one of two things:

  • Evaluate simple JavaScript expressions
  • Put variables (text) from the Javascipt dynamic into the DOM at the relevant places

    <div class="{{some_class}}">Hello</div>

Partials

Partials are a form of pseudocode that allow you to attach particular behaviour to nodes in the DOM/view.


    <div ba-if="">Hello</div>

Blueprints

These blueprints iareintented as a quick reference for how to integrate the most commonly used dynamic functionalities.

Javascript


BetaJS.Dynamics.Dynamic.extend("BetaJS.Dynamics.Dynamictemplate", {

    //Only use one of the following three concurrently
    templateUrl : "templates/template.html",
    template : "<div>Internal Template {{some_attribute}}</div>",
    element : document.querySelector(".someclass"),


    bindings : {
        parent_dynamic_attribute: "<:attribute_in_parent_dynamic"
    },
    scope : {
        parent_dynamic: "<"
    },


    attrs : {
        some_attribute: true,
        computed_attribute : 'default value',
        dependency1 : 'Hel',
        dependency2 : 'lo'

    },
    collections : {
        some_collection: ['one','two']
    },

    computed : {
        'computed_attribute:dependency1,dependency2' : function () {
            return dependency1 + dependency2;
        }
    }

    create : {
        //Reading and writing attribute values
        var attribute = this.get('some_attribute');
        this.set('some_attribute',false);

        //Manipulating Collections
        this.get('some_collection').add('three');
        this.get('some_collection').remove('one');

        //Accessing other Dynamics
        var scope_attr = this.scope.parent_dynamic.get('attribute_in_parent_dynamic');
        var bind_attr = this.get('parent_dynamic_attribute');
        if (scope_attr == bind_attr)
            console.log('This is set up correctly');

        //Accessing functions
        this.call('some_function');
    },

    functions : {
        some_function : () {
            this.element() //to access the element itself
        }
    },

    _afterActivate : function (element) {
        console.log('This message is displayed after the dynamic is activated');
        //element passes you the element where the Dynamic is active on as a DOM element
    }

});

HTML


<somedynamic>

<-- Basics -->

    <-- Data bindings -->
    <databinding>{{attribute}}</databinding>
        <-- This is a unidirectional binding, meaning the DOM Element will get updated -->
        <-- if the Attribute in the Dynamic changes, but it will not update the Attribute in the Dynamics if the DOM changes -->
    <databinding>{{=attribute}}</databinding>
    <-- This is a bidirectional binding -->

    <-- Click Events -->
    <button ba-click='some_function()'>Click me</button>
    <button ba-click='attribute = false'>Click me</button>
    <button ba-click='attribute = !attribute'>Click me</button>
    <button ba-tap='some_function()'>Click me</button> <-- Tap is for use in mobile -->

    <-- If/Show -->
    <div ba-if='{{true}}'>This DOM Element will always be loaded</div>
    <div ba-if='{{false}}'>This DOM Element will never be loaded</div>
    <div ba-if='{{attribute}}'>This DOM Element will be loaded if {{attribute}} evluates to 'true'</div>
    <div ba-if='{{attribute.subattribute}}'>This DOM Element will be loaded if {{attribute.subattribute}} evluates to 'true'</div>
    <div ba-if='{{attribute.get('property')}}'>This DOM Element will be loaded if {{attribute.get('property')}} evluates to 'true'</div>
    <div ba-if='{{attribute == 'load'}}'>This DOM Element will be loaded if {{attribute}} evluates to 'load'</div>
    <div ba-show='{{attribute}}'>This DOM Element will be displayed if {{attribute}} evluates to 'true'</div>

    <-- Classes -->
    <div ba-class='{{{
        'always' : true,
        'sometimes' : attribute,
        'othertimes' : attribute.get('property') == 'other',
        'never' : false
    }}}'>Some random text</div>

    <-- Repeat -->
    <div ba-repeat="{{item :: ['1','2','3']}}">
        <div>
            <div>The html inside the ba-repeat item will be repeated</div>
            <div>The item tag will contain the value of the array (1/2/3): {{item}}</div>
        </div>
    </div>

<-- Advanced Handlebar Examples -->

    <div class="{{attribute}}">Some random text</div>
    <div onmouseover="{{alert('You moved the mouse over this Element')}}"></div>
    <ba-{{attribute}}>Some random text</ba-{{/attribute}}>
    <yourcustomtag-{{attribute}}>Some random text</yourcustomtag-{{/attribute}}>
    //Note
    <{{attribute}}>This won't work, because of the way the Browser renders elements</{{/attribute}}>

</somedynamic>

Dynamics Javascript

Following is a more detailed description of the Javascript Contoller part of the Dynamics System.

For a quick Overview please Reference the Blueprint JS:link

Initializing Dynamics

You can create Dynamics both as instances and as classes which can be instantiated.

  • As one Instance of a BetaJS Dynamic
  • Create a new BetaJS Dynamics Class of which you can create multiple instances

Instance


    var dynamic = new BetaJS.Dynamics.Dynamic({
        templateUrl : "templates/template.html",
        element: document.body
    });

    //This is only necessary if this dynamic is stand-alone
    //If the dynamic is the child of another dynamic it is not necessary
    dynamic.activate();

Class

You can create new dynamics classes using the code below:


    BetaJS.Dynamics.Dynamic.extend("BetaJS.Dynamics.Classname", {

        templateUrl: "templates/classname.html",

    }).register("ba-htmltagname");

These classes can be instanciated in two ways,
either in the Javascript,


    var new_instance = new BetaJS.Dynamics.Classname();
    new_instance.activate();

or by just adding the DOM tagname :


    <ba-htmltagname></ba-htmltagname>

Convenience Methods:

These code snippets can be quickly copy&pasted:

  • templateUrl matching the classname

    BetaJS.Dynamics.Dynamic.extend("BetaJS.Dynamics.Classname", {

        templateUrl: "templates/%.html",
        //The code above will evaluate equivalent to the code below
        templateUrl: "templates/classname.html",
        //You can do multiples of this: templates/%/%.html will evaluate to
        //templates/classname/classname.html

    }).register("ba-htmltagname");
  • HTML Tag matching the classname

    BetaJS.Dynamics.Dynamic.extend("BetaJS.Dynamics.Classname", {

        templateUrl: "templates/classname.html",

    }).register();
    //The code above will evaluate equivalent to the code below
    }).register("ba-classname");

You can combine this as in the example below:


    BetaJS.Dynamics.Dynamic.extend("BetaJS.Dynamics.Classname", {

    templateUrl: "templates/%/%.html",

    }).register();

Templates

There are currently two ways to link dynamic code to a template:

  • Internal Templates defined inside the Javascript Code
  • External Templates defined in an external file

They are mutually exclusive.


    dynamic = new BetaJS.Dynamics.Dynamic({
        templateUrl : "templates/template.html",         //This contains the relative file path to an external template
        template : "<div>Internal Template</div>"
    });

Element

Instead of using templates, you can also link a dynamic to an already existing html element which will then be evaluated by the dynamics system.


    dynamic = new BetaJS.Dynamics.Dynamic({
        element : document.querySelector(".someclass")
    });

_afterActivate

The Code here gets exectued after the dynamic has finished loading in the DOM.

Accessing Dynamics

Scopes offer a way to directly access Attributes, Collections and Methods in other Dynamics.

You access other Dynamics by either directly addressing Parent or Children of certain dynamics.

Basic Usage Example:


    BetaJS.Dynamics.Dynamic.extend("BetaJS.Dynamics.Childdynamic", {

        template : "<div>{{text}}</div>"

        attrs : {text: "Hello World!"}

        functions : {
            call_me : function () {
                alert("You wanted an alert?");
            }
        }

    });


    BetaJS.Dynamics.Dynamic.extend("BetaJS.Dynamics.ScopeExample", {

        template : "<ba-childdynamic><ba-childdynamics>"

        scopes : {
            child_dynamic: ">[tagname='ba-childdynamic']",
        },

        create : {
            //Examples of Accessing another Dynamics
            var child_text = this.scopes.child_dynamic.get('text');
            // child_text will evaluate to "Hello World"

            this.scopes.child_dynamic.set('text','New Hello World Text');
            //The child dynamic evaluation will now be changed from
            //"<div>Hello World</div> to
            //"<div>New Hello World Text</div> to

            this.scopes.child_dynamic.call('call_me');
            // This will call the alert in the childdynamic (You wanted an alert?)

        }

    });

More examples:


    BetaJS.Dynamics.Dynamic.extend("BetaJS.Dynamics.ScopeExample", {

        scopes : {
            //This gives you the Dynamic
            parent_dynamic: "<",
            specific_child: ">[tagname='ba-tagname']",
            specific_in_all_children: ">+[tagname='ba-tagname']"

            //This gives you a query result of the Dynamics
            child_dynamics: ">",
            all_children: ">+",
            parents_children_dynamics: "<>",
            parents_childrens_children_dynamics: "<>>",
        },

        create : {

            //Examples of Accessing other Dynamics

            //Directly
            var scope_attr = this.scopes.parent_dynamic.get('attribute_in_parent_dynamic');
            // scope_attr is an attribute from the parent_dynamic

            //Query Collection
            var json_data = this.scopes.child_dynamics.get(0);
            // json_data is a JSON object that contains the attribute values from the first child_dynamic
            var json_data = this.scopes.child_dynamics.get(0).materialize();

        }

    });

Event Channels

Events Channels are a way to encapsulate message sending between dynamics. By default, there is a global channel that all dynamics can subscribe to:

BetaJS.Dynamics.Dynamic.extend(null, {
       channels: {
           "global:test": function () {
               // Execute when the event test has been triggered on the global channel
           }
       }
}).register("ba-receiver");

BetaJS.Dynamics.Dynamic.extend(null, {
       functions: {
           foobar: function () {
               // Trigger event test on the global channel
               this.channel("global").trigger("test");
           }
       }
}).register("ba-sender");

In some case, you might want to restrict a channel to one dynamic and all its subdynamics. To do so, you register your own channel in the parent dynamic and subscribe to it in the subdynamics:

BetaJS.Dynamics.Dynamic.extend(null, {
    registerchannels: ["mychannel"]
}).register("ba-parent");

BetaJS.Dynamics.Dynamic.extend(null, {
       channels: {
           "mychannel:test": function () {
               // Execute when the event test has been triggered within the parent dynamic mychannel
           }
       }
}).register("ba-childreceiver");

BetaJS.Dynamics.Dynamic.extend(null, {
       functions: {
           foobar: function () {
               // Trigger event test within the parent dynamic mychannel
               this.channel("mychannel").trigger("test");
           }
       }
}).register("ba-childsender");

DOM Partials

Partials

Partials are placed onto HTML DOM elements/nodes and are evaluated by the dynamics system, manipulating the related DOM elements/nodes and thus changing the view.

General layout:

<div ba-partialname="related attributes/code"><div>

Example 1 - ba-if:

The ba-if Partial is used to automatically add or remove DOM elements and subdynamics depending on the boolean value of a given condition.

<div ba-if="{{1 === 1}}"><h1>Hi</h1><div>

Will evaluate to

<div><h1>Hi</h1><div>

And

<div ba-if="{{1 === 2}}"><h1>Hi</h1><div>

Will evaluate to

<div><div>

Overview of Partials:

User Interaction:

  • ba-click
  • ba-tap
  • ba-return

Changing the View

  • ba-class
  • ba-if
  • ba-show

Others

  • ba-repeat
  • ba-on
  • ba-template-url
  • ba-ignore

ba-click

The ba-click partial executes the code passed into it when the user clicks on the related DOM element. (or calls a function defined in the dynamic)

Example 1:

Will call an alert pop-up

<div ba-click="alert('Hi')"><h1>Hi</h1><div>

Example 2:

Will Show/Hide the h1-element when you click on the surrounding div element.

<div class="some_class" ba-click="boolean != boolean">
    <h1 ba-show="boolean">Hi</h1>
<div>
    some_dynamic = new BetaJS.Dynamics.Dynamic({
        element: document.querySelector(".some_class"),
        attrs : {
            boolean : true
        }
    });

Example 3:

Will also call an alert pop-up

<div class="some_class" ba-click="some_function()">
    <h1>Hi</h1>
<div>
    some_dynamic = new BetaJS.Dynamics.Dynamic({
        element: document.querySelector(".some_class"),
        functions : {
            some_function : function () {
                alert('Another alert');
            }
        }
    });

ba-tap

The ba-tap partial is similar to the ba-click and is meant to be used with touchscreen interfaces for faster click response times.

Example 1:

Will call an alert pop-up

<div ba-tap="alert('Hi')"><h1>Hi</h1><div>

Example 2:

Will toggle the heading element when you click on the surrounding div element.

<div class="some_class" ba-tap="boolean != boolean">
    <h1 ba-show="boolean">Hi</h1>
<div>
    some_dynamic = new BetaJS.Dynamics.Dynamic({
        element: document.querySelector(".some_class"),
        attrs : {
            boolean : true
        }
    });

Example 3:

Will call an alert pop-up

<div class="some_class" ba-tap="some_function()">
    <h1>Hi</h1>
<div>
    some_dynamic = new BetaJS.Dynamics.Dynamic({
        element: document.querySelector(".some_class"),
        functions : {
            some_function : function () {
                alert('Another alert');
            }
        }
    });

ba-class

The ba-class partial dynamically sets the css class of a given element based on the boolean value of a given expression

Example

<div class="some_class" ba-class="{{{
            "class_a" : true,
            "class_b" : 1===2,
            "class_c" : some_property
        }}}">
    <h1>Hi</h1>
<div>
    some_dynamic = new BetaJS.Dynamics.Dynamic({
        element: document.querySelector(".some_class"),
        attrs : {
            some_property : true
        }
    });

Evaluates to

<div class="some_class class_a class_c"
    <h1>Hi</h1>
<div>

ba-show

The ba-show partial is used to automatically show or hide DOM Elements depending on the boolean value of a given condition.

Note

ba-show is really just a convenience method to show/hide DOM elements.

If ba-show evaluates to false it will apply the css property "display: none" to the DOM element it is placed on. In comparison, if ba-if evaluates to false it will also free all the resources of all underlying dynamics.

ba-if:

  • (+) Less memory usage
  • (-) Potentially slower loading time

ba-show:

  • (+) Faster loading time
  • (-) More memory

Example 1:

<div ba-show="{{1 === 1}}"><h1>Hi</h1><div>

Will evaluate to

<div><h1>Hi</h1><div>

And

<div ba-show="{{1 === 2}}"><h1>Hi</h1><div>

Will evaluate to

<div><h1 style='display: none'></h1><div>

Example 2:

<div class="some_class" ba-show="{{some_attribute}}">
    <h1>Hi</h1>
<div>
    some_dynamic = new BetaJS.Dynamics.Dynamic({
        element: document.querySelector(".some_class"),
        attrs : {
            some_attribute: true
        }
    });

Will evaluate to

<div class="some_class">
    <h1>Hi</h1>
<div>

Now if we call

    some_dynamic.set("some_attribute",false);

The View will be changed to

<div class="some_class">
    <h1 style='display: none'>Hi</h1>
<div>

ba-if

The ba-if partial is used to automatically display or hide DOM Elements depending on the boolean value of a given condition.

Note

If ba-if evaluates to false it will free all the resources of all underlying dynamics. In comparison ba-show does not do this.

ba-if:

  • (+) Less memory usage
  • (-) Potentially slower loading time

ba-show:

  • (+) Faster loading time
  • (-) More memory

Example 1:

<div ba-if="{{1 === 1}}"><h1>Hi</h1><div>

Will evaluate to

<div><h1 style="display:block;">Hi</h1><div>

And

<div ba-if="{{1 === 2}}"><h1>Hi</h1><div>

Will evaluate to

<div><h1 style="display:none;">Hi</h1><div>

Example 2:

<div class="some_class" ba-if="{{some_attribute}}">
    <h1>Hi</h1>
<div>
    some_dynamic = new BetaJS.Dynamics.Dynamic({
        element: document.querySelector(".some_class"),
        attrs : {
            some_attribute: true
        }
    });

Will evaluate to

<div class="some_class" style="display:block;">
    <h1>Hi</h1>
<div>

Now if we call

    some_dynamic.set("some_attribute",false);

The View will be changed to

<div class="some_class">
<div>

ba-ignore

The ba-ignore partial disables the loading of the dynamic it is placed on.

This is particulary useful if you need to bind an outside view engine to a particular dom element.

ba-repeat

Introduction

The ba-repeat partial repeats the containing DOM elements of the DOM Elements it is placed in a certain number of times.

Example 1:
<ul ba-repeat="{{ i :: [1,2] }}">
    <li>{{i}}</li>
</ul>

Evaluates to

<ul>
    <li>1</li>
    <li>2</li>
</ul>
Example 2:
    dynamic = new BetaJS.Dynamics.Dynamic({
        element: document.querySelector(".some_class"),
        collections : {
            named_collection: [
                {item_name : "Apple", item_index : "1"},
                {item_name : "Orange", item_index : "2"},
            ]
        }
    });
    <div class="some_class" ba-repeat="{{ item ::  named_collection}}">
        <div>
            <span>{{item.item_name}}</span>
            <span>{{item.item_index}}</span>
        </div>
    </div>

Evaluates to

<div>
    <div>
        <span>Apple</span>
        <span>1</span>
    </div>
    <div>
        <span>Orange</span>
        <span>2</span>
    </div>
</div>

Advanced

Filters

Filters are a way of defining rules to limit the number of items shown by the ba-repeat partial.

Example 1:
<ul ba-repeat="{{ i ~ i == 2 :: [1,2] }}">
    <li>{{i}}</li>
</ul>

Evaluates to

<ul>
    <li>2</li>
</ul>
Example 2:
    dynamic = new BetaJS.Dynamics.Dynamic({
        element: document.querySelector(".some_class"),
        collections : {
            named_collection: [
                {item_name : "Apple", item_index : "1"},
                {item_name : "Orange", item_index : "2"},
            ]
        }
    });
    <div class="some_class" ba-repeat="{{ item ~ item.item_index == 1 ::  named_collection}}">
        <div>
            <span>{{item.item_name}}</span>
            <span>{{item.item_index}}</span>
        </div>
    </div>

Evaluates to

<div>
    <div>
        <span>Apple</span>
        <span>1</span>
    </div>
</div>

Sort

Sort is a way to define a sorting algorithm for collection in the ba-repeat partial.

Example 1:
var dynamic = new BetaJS.Dynamics.Dynamic({
    element: root.get(0),
    collections: {
        items: [{test: "A"}, {test: "D"}, {test: "C"}, {test: "B"}]
    }
});
dynamic.get("items").set_compare(function (x, y) {
    return x.get("test") < y.get("test") ? 1 : -1;
});
<ul ba-repeat="{{ item :: items }}">
    <li>{{item}}</li>
</ul>

Evaluates to

<ul>
    <li>A</li>
    <li>B</li>
    <li>C</li>
    <li>D</li>
</ul>

ba-repeat-element

ba-repeat-element is a special form of ba-repeat, it repeats the element it is placed on itself. The ba-repeat is the preferred partial due to performance.

Example :
<ul>
    <li ba-repeat-element="{{ i :: [1,2] }}">{{i}}</li>
</ul>

Evaluates to

<ul>
    <li>1</li>
    <li>2</li>
</ul>

ba-attrs

The ba-attrs Partial allows you to set initial property values for a dynamic from within its DOM scope.

Example:

    <div class="some_class" ba-attrs="{{{foobar: 'xyz'}}}">
        <ba-innertest ba-attrs="{{{tester: 'abc'}}}">
        </ba-innertest>
    </div>
    <testouter>{{foobar}}</testouter><testinner>{{tester}}</testinner>
    BetaJS.Dynamics.Dynamic.extend(null, {
        template: '<outer>{{foobar}}</outer><inner>{{tester}}</inner>',
    }).register("ba-innertest");

    var dynamic = new BetaJS.Dynamics.Dynamic({
        element: document.querySelector('.some_class')
    });
    dynamic.activate();

Will evaluate to

    <div class="some_class">
        <outer>abc</outer><inner></inner>
    </div>
    <testouter></testouter><testinner>xyz</testinner>

ba-on

The ba-on partial executes the code passed into it when the specified DOM event is fired.

Example:

Will call an alert if somebody hovers the mouse over the element

<div ba-on:mouseover="alert('Hi')">Hover me<div>

ba-return

The ba-return partial executes the code passed into it, or calls a function from the related dynamic, when the user presses the return/enter key when he is in the input field this is placed on.

Example:

Will call an alert pop-up displaying the text in the input field.

<input ba-return="alert({{input_value}})" value={{input_value}} />

betajs-ui

BetaJS-UI is a library for enabling gestures and interactions such as drag and drop.

Interactions

Interactions allow you to register particular user interactions like drag, drop, swipe, pinch, scroll etc. on DOM elements.
You can react to events emitted by the respective interactions.
The interaction system is completely independent of any UI/UX framework.

All interactions are configuered as follows:

    var interaction = new InteractionClass(domElement, {
        options
    });

    interaction.on("event", function () {
        // React to particular interaction event.
    });

You can also instantiate multiple interactions at once when providing a jQuery selector for multiple elements:

    InteractionClass.multiple(jqueryElements, {
        options
    }, function (interaction) {
        interaction.on("event", function () {
            // React to particular interaction event.
        });
    });

Drag

The drag interaction allows the user to drag a dom element within constraints. This allows you to enable two types of drag interactions:

  • a standard drag: here, the element is cloned and positioned absolutely on the body, allowing the user to move it around freely
  • a restricted drag: here, the element is just moved within its parent container, to e.g. implement a swipe-like interaction

Finally, the drag interaction works well with the drop interaction, to fully implement a drag-and-drop-like system.

A standard drag, in which the element is being cloned (temporarily), can be instantiated as follows:

    var drag = BetaJS.UI.Interactions.Drag(domElement, {
        enabled : true,
        clone_element: true
    });

    drag.on("move", function (event) {
        event.actionable_modifier.csscls("focus", true);
        event.modifier.csscls("unfocus", true);
    });

Drop

The drop interaction allows you accept dropped elements originating from a drag interaction.

    var drag = new BetaJS.UI.Interactions.Drag(dragSourceElement, {
        droppable: true,
        enabled : true,
        clone_element: true,
        remove_element_on_drop : true
    });

    drag.on("move", function (event) {
        event.actionable_modifier.csscls("focus", true);
        event.modifier.csscls("unfocus", true);
    });

    var drop = new BetaJS.UI.Interactions.Drop(dropTargetElement, {
        enabled : true
    });
    drop.on("hover", function(dr) {
        dr.modifier.css("border", "4px solid green");
    });
    drop.on("dropped", function(event) {
        $("#drop").append(event.source.element.html());
    });

Pinch

The pinch interaction allows the user to zoom in/out within a particular dom element.

For this example, assume the following dom element:

    <div class="pinch" style="width: 200px; height: 200px">
        Pinch Me
    </div>

We initialize the pinch interaction:

    var pinch = new BetaJS.UI.Interactions.Pinch($(".pinch"), {
        enabled: true
    });

Once the user initiates a pinch, we need to react to it by resizing the underlying dom element, e.g. as follows:

    pinch.on("pinch", function (details) {
        $(".pinch").css("width", (parseInt($(".pinch").css("width"), 10) + details.delta_last.x) + "px");
        $(".pinch").css("height", (parseInt($(".pinch").css("height"), 10) + details.delta_last.y) + "px");
    });

Gestures

The gesture allows you to react to particular touch / click gestures and kick off interactions based on a successful gesture.

The gesture system is implemented as a competitive state engine. That particularly enables you to register multiple gestures on DOM elements, competing with each other.

A gesture is registered on a DOM element as follows:

    var gesture = new BetaJS.UI.Gestures.Gesture(domElement, BetaJS.UI.Gestures.defaultGesture({
        mouse_up_activate: false,
        wait_time: 750,
        wait_activate: true,
        disable_x: 10,
        disable_y: 10,
        enable_x: -1,
        enable_y: -1
    }));

This gesture, for instance, is activated by touching the particular dom element, waiting 750 ms and not moving more than 10 pixels both in horizontal and vertical drection.

You can react to a successful gesture or to an unsuccessful gesture as follows>

    gesture.on("activate", function () {
        // ...
    });
    gesture.on("deactivate", function () {
        // ...
    });

The most typical use case combines gestures with interactions. The interactions are configured slightly differently, to not automatically kick off once the user starts to interact with the DOM element. This part is now handled by the gesture.

Here is a typical example:

    var drag = new BetaJS.UI.Interactions.Drag(domElement, {
        enabled : true,
        clone_element: true,
        start_event: null                
    });
    gesture.on("activate", drag.start, drag);
    gesture.on("deactivate", drag.stop, drag);

Dynamics

Both the interaction and the gesture system are completely independent from any frontend system. That includes the dynamics system.

BetaJS UI contains a small optional bridging system that makes it particularly easy to apply both gestures and interactions to dom elements within the dynamics system, by applying partials to the respective dom elements:

    <div ba-gesture:drag="{{{data: user_data, options: drag_gesture_options}}}"
         ba-interaction:drag="{{{data: user_data, options: drag_interaction_options}}}">
    </div>

There can be multiple gestures and interactions applied to a single dom element.

The value part of both the gesture and the interaction partials contain a (dynamically generated) JSON object with a data parameter (which can be, for instance, an item from a ba-repeat partial) and an options parameter. While the options can be given explicitly as a JSON as well, it is recommended to outsource the options into the attributes of the dynamic:

BetaJS.Dynamics.Dynamic.activate({
    element: document.body,
    attrs: {
        drag_gesture_options: {
            mouse_up_activate: false,
            wait_time: 750,
            wait_activate: true,
            disable_x: 10,
            disable_y: 10,
            enable_x: -1,
            enable_y: -1,
            interaction: "drag"
        },
        drag_interaction_options: {
            type: "drag",
            clone_element: true,
            start_event: null,
            events: {
                "move": function (doodad, event) {
                    event.actionable_modifier.csscls("focus", true);
                    event.modifier.csscls("unfocus", true);
                }
            }
        }
    }
});

betajs-flash

BetaJS-Flash is a Flash-JavaScript bridging framework

Flash Bridging

This framework allows you to access flash objects and functions without recompiling the Flash file itself.

The framework marshals and unmarshals creating and destroying Flash objects, calling methods and accessing attributes.

Flash Registry

In order to be able to instantiate Flash objects and call methods on these objects, we need to create a Flash Registry and register all the entities that we want to call:

    var registry = new BetaJS.Flash.FlashClassRegistry();
    registry.register("flash.media.Video", ["attachNetStream"]);
    registry.register("flash.display.Sprite", ["addChild"]);
    registry.register("flash.net.NetStream", ["play", "addEventListener"]);
    registry.register("flash.net.NetConnection", ["connect", "addEventListener"]);

In this example, we register the classes flash.media.Video, flash.display.Sprite, flash.net.NetStream and flash.net.NetConnection,
and on those classes the instance methods attachNetStream, addChild, play, addEventListener and connect.

It suffices to only register the methods you are intending to call.

Embedding Flash

Once the registry is defined, we need to add our Flash embedding to the DOM.

We assume a container element like the following one:

    <div id='embed-here'></div>

We can then embed Flash as follows:

    var embedding = new BetaJS.Flash.FlashEmbedding($("#embed-here").get(0), {
        registry: registry,
        wrap: true
    }, {
        flashFile: "betajs-flash/dist/betajs-flash.swf"
    });

This call embeds a Flash embedding within the given container, using the registry we defined previously, and linking to general Flash bridging file.

Before accessing the embedding, we have to wait for the ready event:

    embedding.ready(function () {
        // Execute Flash functions on the embedding.
    });

Flash Objects

Once the embedding is ready, we can instantiate objects, call methods and access properties as we normally would in Flash.

Note that you can only access objects and method defined in the registry.

    var main = embedding.flashMain();
    var stage = main.get("stage");
    stage.set("scaleMode", "noScale");
    stage.set("align", "TL");
    var video = embedding.newObject("flash.media.Video", stage.get("stageWidth"), stage.get("stageHeight"));
    main.addChildVoid(video);

Flash Events

Many Flash objects provide so-called Event Listeners. The framework allows to create callbacks in JavaScript, that can be passed in as Event Listeners:

    var connection = embedding.newObject("flash.net.NetConnection");
    var cb = embedding.newCallback(function () {
        var stream = embedding.newObject("flash.net.NetStream", connection);
        video.attachNetStreamVoid(stream);
        stream.playVoid("movie.mp4");
    });
    connection.addEventListener("netStatus", cb);
    connection.connectVoid(null);

betajs-media

BetaJS-Media is a JavaScript media framework

Video Playback

The video playback abstractions of this module allow cross-browser playback of videos.

This module only handles the playback itself without putting any UI/UX state machine on the player.

The cross-browser capabilities itself are based on HTML5 video playback with the abilitiy to fallback to Flash.

Flash Player

The Flash Player component allows you to either polyfill an existing video element if necessary or to directly embed
a Flash polyfill into the DOM.

For polyfilling a video element, the polyfill call is invoked as follows:

    <video autoplay loop poster="movie.png" style="width:50%" muted>
        <source src="movie.mp4" type="video/mp4" />
    </video>
    BetaJS.Media.Player.FlashPlayer.polyfill($("video").get(0)).success(function (video) {
        // video has been successfully polyfilled or kept they way it was if possible
    });

You can even impose styles and typical attributes on the video element.

The Flash polyfill also allows you to use typical Flash protocols like RTMP:

    <video poster="movie.png">
        <source src="rtmp://localhost:1935/vod/video.flv" type="video/flv" />
    </video>

Instead of polyfilling an element, you can also explicitly embed the player:

   <div id='element'>
   </div>
    var player = BetaJS.Media.Player.FlashPlayer.attach($("#element").get(0), {
        autoplay : true,
        loop : true,
        muted: true,
        poster : "movie.png",
        sources: [{src: "movie.mp4"}]
    });

The player object itself understands the typical video-element methods and emits the typical dom events as well.

Video Player

Polyfilling with Flash helps to make videos play across more browser, but the access to the native video element
is not ideal and also not reliable in terms of emitted events and behaviour.

The VideoPlayerWrapper class provides a completely uniform interface to all video playback systems:

    <video>
    </video>
    BetaJS.Media.Player.VideoPlayerWrapper.create({
        element: $("video").get(0),
        poster: "movie.png",
        source: "movie.mp4",
        //forceflash: true
    }).success(function (instance) {
        // instance.play();
        // instance.pause();
        // instance.enterFullscreen();
    }).error(function (error) {
    });

Video Recording

The video playback abstractions of this module allow cross-browser playback of videos.

Currently, the system only allows for WebRTC recording.

This module only handles the recording itself without putting any UI/UX state machine on the recorder.

WebRTC Recording

The WebRTC Recorder abstraction is based on MediaRecorder on browsers that support it and manual encoding to webm on all others.

Given a video element in the DOM, it is initialized as follows:

    var view = BetaJS.Media.WebRTC.RecorderWrapper.create({
        video: $("video").get(0)
    });
    view.on("bound", function (stream) {
        view.startRecord();
        ...
        view.stopRecord();
    });
    view.on("data", function (video_blob, audio_blob) {
        // Handle data
    });
    view.bindMedia();

betajs-media-components

BetaJS-Media-Components is a JavaScript media UI components framework

Video Player Component

Our video player is a dynamic component named ba-videoplayer.

Its main parameters are poster and source, specifying the poster image as well as the video source file.

Depending on the browser, device and video format at hand, the system will automatically determine whether HTML 5 video can used or whether to fall back to Flash.

If you want to use Flash, make sure to initialize the Flash bridging framework of BetaJS:

    BetaJS.Flash.options = {
        flashFile: "http://betajs.com/betajs-flash.swf" // location of your flash file
    };

Embedding

As always with the dynamics system, there are two ways to to activate it.

You can embed it somewhere in the HTML dom and active the dynamic system on of its parent nodes (here: on the document body):

    <ba-videoplayer ba-loop ba-poster="..." ba-source="...">
    </ba-videoplayer>
    BetaJS.Dynamics.Dynamic.activate();

The other way is to define a parent dom node and embed it via JavaScript:

    <div id='parent'></div>
    var player = BetaJS.MediaComponents.VideoPlayer.Dynamics.Player({
        element: document.getElementById('parent'),
        attrs: {
            poster: "...",
            source: "..."
        }
    });
    player.activate();

The particular sizing of the player can be set by adding normal css classes or styles to the ba-videoplayer element.

Advanced Options

The advanced options allow you to configure the player even more precisely:

  • forceflash: Always use Flash instead of HTML5
  • noflash: Never use Flash
  • autoplay: Immediately play the video back
  • preload: Preload the video file immediately
  • loop: Loop the video file
  • nofullscreen: Disallow the fullscreen button
  • stretch: Stretch the video to fill the surrounding container

For example:

    <ba-videoplayer ba-stretch ba-nofullscreen ba-loop ba-poster="..." ba-source="...">
    </ba-videoplayer>

Themes

Themes are a combination of CSS classes, HTML templates and potentially subdynamics.

The default theme is automatically applied to the player if not otherwise specified and looks as follows:

The modern theme can be manually applied:

Locales

The system supports multiple locales, fully independent of the selected theme.

The current locales is auto-detected by analysing the browser at hand, but can also be overwritten explicitly:

    BetaJS.MediaComponents.Assets.strings.setLocale("de");

Whenever a particular string asset doesn't exist in the specified locale, the system automatically falls back to the default string in English.

You can also define your own locale:

    BetaJS.MediaComponents.Assets.strings.register({
        "ba-videoplayer-playbutton.tooltip": "Klaki ludi video." 
    }, ["language:esperanto"]);

betajs-debug

BetaJS-Debug is a library for debugging BetaJS-based applications.

Profiling Method Calls

The debugging profiles allows to hook single or all methods of a BetaJS object or class.

This is particularly helpful for understanding how often a particular method, object or class is being called.

If you want to hook class method foobar of class C, you can write:

    var hook = BetaJSDebug.Hooks.hookMethod("foobar", C, function (method, Cls, args, callContext) {
        // called when the method foobar is being called.
    }, function (method, Cls, args, callContext, result) {
        // called after the method foobar was called
    });

After you're done, you can unregister the hook as follows:

    BetaJSDebug.Hooks.unhookMethod(hook);

You can also hook instance methods. Assume that class C also has an instance method method:

    var hook = BetaJSDebug.Hooks.hookPrototypeMethod("method", C, function (method, Cls, args, callContext) {
        // called when the method foobar is being called.
    }, function (method, Cls, args, callContext, result) {
        // called after the method foobar was called
    });

Finally, you can also hook all class / instance methods:

    var hooks = BetaJSDebug.Hooks.hookMethods(C, function (method, Cls, args, callContext) {
        // called when the method foobar is being called.
    }, function (method, Cls, args, callContext, result) {
        // called after the method foobar was called
    });
    var hooks = BetaJSDebug.Hooks.hookPrototypeMethods(C, function (method, Cls, args, callContext) {
        // called when the method foobar is being called.
    }, function (method, Cls, args, callContext, result) {
        // called after the method foobar was called
    });
    BetaJSDebug.Hooks.unhookMethods(hooks);

Profiling Instantiations

Profiling instantiation allows you to find memory leaks, by counting how many instances are present at a particular time.

To start monitoring instantiation of a particular base class BaseClass, we create a sub class filter (more on that in a second) and set up the debugger:

    var debugger = BetaJSDebug.Instances.monitorInstances(BaseClass, filter);

Once you're done debugging, you can unregister the debugger as follows:

    BetaJSDebug.Instances.unmonitorInstances(debugger);

The easiest way to inspect the instance monitor at any point, is to convert the data to an HTML table (which can be added to the DOM, e.g. using jQuery):

    BetaJSDebug.Instances.toHTMLTable(debugger);

Filters allow you to restrict the sub classes of the BaseClass that you want to track. You can create basic filters and then use compound filters to combine them:

  • BetaJSDebug.Instances.allFilter(): matches all classes
  • BetaJSDebug.Instances.andFilter(filters): matches if all filters in the array match
  • BetaJSDebug.Instances.orFilter(filters): matches if one filter in the array matches
  • BetaJSDebug.Instances.regexFilter(regex): matches if the class name matches the regular expression
  • BetaJSDebug.Instances.ancestryFilter(filter): matches if the class or any of the ancestors matches the filter

grunt-betajs-templates

Build BetaJS templates.

Getting Started

Overview

In your project's Gruntfile, add a section named betajs_templates to the data object passed into grunt.initConfig().

Options

The namespace of each betajs_templates namespace must be specified. See
any of the examples for guidance on specifying the namespace option.

grunt.initConfig({
  betajs_templates: {
    dist: {
      files: {
        "dest/betajs-templates.js": [
          "src/my_first_template.html",
          "src/my_second_template.html",
          "src/my_last_templates.html"
        ]
      },
      options: {
        namespace: 'App.Templates'
      }
    },
  },
});

Naturally, it is possible to specify a different namespace for each subtask.
Multiple namespaces for different subtasks can be seen in the example below.

grunt.initConfig({
  betajs_templates: {
    dashboard: {
      files: {
        "dest/betajs-dashboard-templates.js": [
          "dashboard/*.html",
        ]
      },
      options: {
        namespace: 'App.Dashboard'
      }
    },
    homepage: {
      files: {
        "dest/betajs-homepage-templates.js": [
          "homepage/*.html"
        ]
      },
      options: {
        namespace: 'App.Homepage'
      }
    }
  }
});

grunt-betajs-docs-compile

Build BetaJS documentations based on JSDOC.

Getting Started

This plugin requires Grunt ~0.4.5

If you haven't used Grunt before, be sure to check out the Getting Started guide, as it explains how to create a Gruntfile as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command:

npm install grunt-betajs-docs-compile --save-dev

Once the plugin has been installed, it may be enabled via the jsdoc plugin inside your Gruntfile with this line of JavaScript:

grunt.loadNpmTasks('grunt-jsdoc');

The "jsdoc" task

        jsdoc : {
            dist : {
                src : sources,
                options : {
                    destination : '../',
                    template : 'node_modules/grunt-betajs-docs-compile',
                    configure : './jsdoc.conf.json',
                    tutorials : './tutorials'
                }
            }
        }

Additional configuration

Additional configuration can be done in the jsdoc.conf.json. It might looks like this:

{
    "tags": {
        "allowUnknownTags": true
    },
    "plugins": ["plugins/markdown"],

    "templates": {
        "cleverLinks": false,
        "monospaceLinks": false,
        "dateFormat": "ddd MMM Do YYYY",
        "outputSourceFiles": true,
        "outputSourcePath": true,
        "systemName": "FooBar",
        "footer": "",
        "copyright": "MIT License",
        "navType": "vertical",
        "theme": "cerulean",
        "linenums": true,
        "collapseSymbols": false,
        "inverseNav": true,
        "highlightTutorialCode": true,
        "protocol": "fred://"
    },
    "markdown": {
        "parser": "gfm",
        "hardwrap": true
    }
}

This is mostly preserved and copied from Ink-Docstrap. Additionally, you can use the following optional arguments:

{
    ...

    "templates": {
        ...

        "template": "my/local/template/directory",
        "emptyTutorials": false || true,
        "singleTutorials": false || true,
        "singleModules": false || true
    },

    ...

    "pages": {
        "about": {
            "title": "About",
            "source": "./about.md"
        }
    }
}

betajs-codemirror

BetaJS-Codemirror is a Codemirror Plugin for the BetaJS Framework.

Getting Started

The codemirror module registers a wrapper for the Codemirror Editor via the dynamics system. You can instantiate it as follows:


        <ba-codemirror ba-trim ba-language='html'>
            <div>
                <h1>H1 None</h1>
                <br />
                <strong>Strong 1</strong> Text 1 <code>Code 1</code><br />
                <code style="text-decoration: underline" >Code 2</code><strong> Strong 2</strong>
                <div style="font-size: 24px; font-family: Helvetica;">Text 2</div>
            </div>
        </ba-codemirror>

    BetaJS.Dynamics.Dynamic.activate();

betajs-richeditor

BetaJS-Richeditor is a rich editor plugin based on content editable using the BetaJS Framework.

Getting Started

The richeditor module registers a rich editor component via the dynamics system. You can instantiate it as follows:


        <ba-richeditor>
            <div>
                <h1>H1 None</h1>
                <br />
                <strong>Strong 1</strong> Text 1 <code>Code 1</code><br />
                <u>Code 2</u><strong> Strong 2</strong>
                <div style="font-size: 24px; font-family: Helvetica;">Text 2</div>
            </div>
        </ba-richeditor>

    BetaJS.Dynamics.Dynamic.activate();