JSPM

twobirds-core

7.1.13
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 28
  • Score
    100M100P100Q115065F
  • License GPL-3.0+

twoBirds Web Component Framework

Package Exports

  • twobirds-core

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (twobirds-core) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

twoBirds

Welcome Birdies ;-)

I dont want to read a lot - give me a kick-start

Intro

twoBirds is a lightweight, component-based, event-driven JavaScript framework that maps nested objects to DOM nodes.

twoBirds strictly follows the KISS doctrine, it is the minimum possible solution for an application framework.

It consists of 3 parts:

1.) a simple client repository object structure + instanciation mechanism

demoapp/myClass.js

var demoapp = demoapp || {};


demoapp.myClass = function(){};

demoapp.myClass.prototype = {}

as you see, any plain old javascript class is a tB class :-)

index.html

<body data-tb="demoapp.myClass">

this will make an instance of above mentioned class and put it in the dom :-)

or, somewhere in your js code:

new tb(
    demoapp.myClass,
    { ... config data ... },
    document.body
);

same as above, but done at run-time

2.) a selector to adress instances of these objects on the page

tb( document.body )    // will return any tb instances that are contained in document.body

3.) a trigger mechanism to communicate with the selected instance on the page

tb( 'body' ).trigger( 'myEventName', <eventData>, <bubble> );

hint: bubble = 'l' for local, 'd' for down, 'u' for up ('l' being default)

twoBirds allows building nested structures of tB instances of repository classes that all look the same codewise, but add up to complex functionality.

All instances of these classes are stored in DOM nodes or other tB instances / other objects.

twoBirds has a selector ot its own ( tb.dom() ), but can work with any selector lib that returns array-like objects.

Comparision: twoBirds can be compared to Flight, Polymer, React, ember and backboneJS.

Unlike these frameworks twoBirds ...

  • ... allows for complete separation of code and design.
  • ... requirement loading is an inherent part of the system.
  • ... recursively nests application instances transparently.
  • ... doesnt come with prebuilt widgets - every tB instance is a widget
  • ... strictly follows the KISS principle

Description

General

As seen from a twoBirds perspective, a website / webapp consists of the HTML DOM and twoBirds JS objects attached to DOM nodes.

Not every DOM node necessarily has a tB object attached to it, usually the tB objects reflect the higher order elements of a site, like "header", "login", "footer".

Also, twoBirds objects can be nested into each other.

Each of the nested instances may or may not add additional HTML / DOM nodes to the element, but together they form a logical unit.

As shown later in the examples, you can find and address all these objects on the current page displayed, and trigger events on each element.

Repository

In twoBirds, on the client side you have a repository of plain JS classes.

These are used to create instances.

The instances are saved in the DOM nodes or in other tB instances.

Instances

There are some property names in twoBirds instances that are reserved:

  • target: ... is the DOM node the tB instance is attached to. In nested objects it is inherited from the parent, but AT RUNTIME can be set to another DOM node as well if necessary. You cannot set this property in a repo object, since it would make no sense.

  • namespace: ... is the namespace of the repo object, and should be set accordingly, since both the regEx selector tb(/.../) as well as the .instanceOf("namespace") method checks against the "name" property.

  • handlers: ... is a plain object, where { key: value } is { eventName: function myHandler( pEvent ){ /*...*/ } }. If for some reasons you need more than one handler for an eventName, eventName also can be an array of callback functions. Internally they are converted to array anyway.

  • tb: ... (reserved for internal use, overwriting deletes nested objects).

As for handlers, there currently is 1 event name that is reserved:

  • init: function(){ /* all requirement loading for all nestings is done, now construct the object as necessary */ }

This event will be sent to every newly created instance, when all required files have been loaded.

There is a special convention inside twoBirds instances:

  • If a property name contains a dot ("."), it is treated as a namepace which should contain a JS object or function. twoBirds will check whether this namespace already exists, then ...

IF NOT: twoBirds will convert the property name to a subdir string like so

"app.Body" ==> "/app/Body.js"

...and starts loading the file.

IF IT EXISTS OR WHEN ITS LOADED:

twoBirds will check whether the namespace points to a function or a plain object.

If it is a function, it will be executed in the context of the current instance (this), and given the property value as a single parameter.

If it is a plain object, the property value will be replaced with it, and when "tb.init" fires the handler will receive the previous contents of the property as a single parameter.

Now lets see all of this in context:

demoapp/Body.js

tb.namespace('app', true).Body = (function(){

    // Constructor
    function Body(){
        
        var that = this;

        that.handlers = {
            init
        };

    }

    Body.prototype = {

        namespace: 'demoapp.Body',

        'tb.Require': [
            '/demoapp/body.css'
        ]

    };

    return Body;

    // Private Functions
    function init(){
        
        var that = this;

        // initialize the instance and trigger further actions ...

    }

})();

The function will execute, starting the requirement loading. Further execution is halted until all required files have loaded. The "init" event will fire then.

  • HINT: Properties that contain a dot (.) are said to be misleading because they look like a namespace. In twoBirds, what looks like a namespace IS a namespace - and will be treated as such.

ON EVENT / AT RUNTIME:

You can also insert a twoBirds instance into an already existing instance at runtime, in this case inside some event handler or method you add this code:

this.tbElement = new tb(
    'app.someElement'
);

API / Examples

ON BOOT:

<html>
    <head>
        <script src="http://<yourPathTo>/tb.js"></script>
    </head>
    <body data-tb="app.Body">
    </body>
</html>

By default upon startup twoBirds will lookup DOM nodes containing a "data-tb" attribute, and treats them as a tB class.

An instance of the class will be created and attached to the DOM node.

If the corresponding class doesnt exist in the repository, on-demand loading is performed.

tb() Selector and inner structure example

// tb() selector always returns a jQuery-like result set

// PARAMETER TYPES



// STRING: uses .querySelectorAll(), but returns tB object(s) contained in the DOM nodes.

tb('body')



// OBJECT: instances of a repo object inside page structure

// find all demoapp.Body sub-instances ( only one )
tb( app.Body )

// find all demoapp.someElement sub-instances ( may return many )
tb( app.someElement )



// REGEXP: as object, but matching to instance 'namespace' property 

// always returns the root object

tb( /app.Bod/ ) // returns the app.body object, its 'namespace' matches the regEx



// OTHER:

// both of the following return all toplevel objects in the current DOM, as expected.

tb( /./ ) 
tb( '*' ) // invoking document.querySelectorAll()



// THIS:

this // in handler code, this always points to the current instance



// CHAINING:

// currently these chained selectors exist,
// and can be used to get other page objects,
// positioned relatively to a selector result or 'this'

tb('body').children('div') // all children of body tB object that reside inside a div HTML element
tb('body').descendants() // all descendants of body tB object
tb( ... ).parent() // closest parent, in this case body tb object
tb( ... ).parents() // array of all parent tB objects, nearest first
tb( ... ).prev() // the previous tb instance in this.parent().children()
tb( ... ).next() // the next tb instance in this.parent().children()
tb( ... ).first() // the previous tb instance in this.parent().children()
tb( ... ).last() // the next tb instance in this.parent().children()
...for a complete list see the API documentation, there are about 20 of them ATM.

// CHAINED SELECTOR RETURNS ARE ALWAYS UNIQUE!

Adding or removing event handler functions

... roughly resembles jQuery:

function myHandler( e ){
    // do whatever
};

// add handlers (one = only one execution, delete handler afterwards)
tb('body').on('myevent', myHandler);
or
tb('body').one('myevent', myHandler);

// remove handler
tb('body').off('myevent', myHandler);

tb(selector).trigger(event, data, bubble)

  • communication between object instances on the page

some trigger snippets:

// get the $('body') DOM node, 
// retrieve its tB toplevel object, 
// and trigger '<myevent>' on it
tb('body').trigger('<myevent>' [, data] [, bubble])

// find all demoapp.body instances (only one), 
// trigger <myevent> bubbling down locally.
tb( app.Body ).trigger('<myevent>' ,null ,'ld' )	

// find all app.SomeElement instances, 
// and trigger 'scroll.update' on it, meaning its a local event that doesnt bubble. 
tb( app.SomeElement ).trigger('scroll.update' );			

Installation

simple: copy tb.js from /dist and insert into your project. Have fun!

Use case

  • easily adding JS functionality to server side rendered HTML
  • migrating from an existing server side rendered website to a single page application
  • any size from embedded small functionality to enterprise apps

Features

  • component style web programming
  • distributed programming
  • async on demand loading, recursive
  • web-component programming, defining repository objects

Status:

  • core API stable
  • Best Practices stable but not documented yet. FAQ and BP docs coming soon.

History

  • twoBirds was created 2004 to be able to build a complex web application for an insurance company (similar to google calc, which came later).
  • It was meant to be a Web Component Framework from the beginning.
  • It was first made public as V2 in 2007 ( Ajaxian and sorry for the character mess there, the page is outdated obviously ).
  • It was constantly under development.

I dont want to read a lot - give me a kick-start

On console do...

(git clone this repository)

(goto project directory)

npm install grunt cd src

( you can use whatever you like as a web server, just one option using php here: )

php -S 0.0.0.0:3000 &

Example

browser > localhost:3000/index.html browser > ( select "test" )

( open dev tools, e.g. firebug )

  • inspect DOM to see how twoBirds instances reside in DOM structure, on HTML tab right-click on a div and select 'inspect in DOM'
  • right-click on an "app.child" div, select 'inspect in DOM' to see how twoBirds instances can also reside inside each other
  • go to the 'network' tab and reload to see the sequence of requirement loading

( in the file system )

  • view 'test.html' file to see the app code ( in this case, its lack of ;-) )
  • view js files in /src/app/ to see the app code for those objects that are lazy loaded

In case of questions contact me.