JSPM

twobirds-core

7.2.7
  • 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

There are a lot of chained methods to that selector.

You also have a document.querySelectorAll() based tb.dom() selector, also with the chained methods you expect.

The difference between these selectors is: tb.dom() returns DOM nodes, tb() returns tb instances that are contained therein.

twoBirds can work with any selector lib ( jQuery etc. ) that returns array-like objects.

Read the API documentation included in the package.

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 and vars of course.

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 usually are saved in a DOM node or in other tB instances.

Instances

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

  • target: ... is the DOM node or another object ( e.g. a tB instance ) the tB instance is attached to.

  • namespace: ... is the namespace of the repo object, and should be set accordingly, since the regEx selector tb( /.../ ) checks against the "namespace" 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.

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, wether there is requirement loading or not.

The init handler defined in the constructor is the "boot method" of every tb instance.

...special convention inside twoBirds instances:

  • If a property name contains a dot ("."), it is treated as a namespace 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:

app/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 ...

    }

})();

Upon instanciation this class will create a tb instance, starting the requirement loading if necessary. 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:

// somewhere in you class 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: invokes .querySelectorAll(), but returns tB object(s) contained in the DOM nodes returned.

tb('body')



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

// find all demoapp.someElement instances
tb( app.someElement )



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

// always returns the root object

tb( /app.Bod/ ) // e.g. returns the app.body object, for its 'namespace' property should match the regEx



// OTHER:

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

tb( /./ ) // any namespace matches
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 contained in the package.

// 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:

// general
tb( pSelector ).trigger('eventName' [, data] [, bubble] );

// as in:
tb( app.Body ).trigger('eventName' , null, 'ld' ); // local on instance found & down	

// sample from code
this.trigger( 'scroll.update', null, 'u' ); // not in this instance but bubbling up			

Installation

simple: copy tb.js from /dist and insert into your project, or, using npm, follow instructions below. 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

Status:

  • core API stable
  • Best Practices stable but not documented yet. FAQ and Tutorials coming soon I hope.

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, and was said to be one since 2007.
  • 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, though I had to earn some money from time to time.

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.