Fork me on GitHub
Neon
Neon.js
Neon is a DSL Class system for Javascript.

What is it?

Neon (Ne) is a javascript Class system designed to wrap up object oriented logic around Javascript objects (which are not truly OO), and to establish a common base language for developers using such classes.

Get the code on Github!

Usage

For browsers:

copy

<script src="/vendor/neon.js"></script>

For node.js:

copy

npm install neon

...
var Ne = require('neon');

copy

//Namespace
UI = {};

// Create a simple Class
Class(UI, 'Widget')({
    HTML : '<div></div>',
    prototype : {
        init : function() { ... },
        render : function() { ... }
    }
});

// Inject functionality with Modules
Module('Draggable')({
    threshold : 10,
    prototype : {
        isDragging : false,
        startDrag : function() { ... }
    }
});

// Inherit and include to separate functionality
Class(UI, 'Overlay').inherits(UI.Widget).includes(Draggable)({
    HTML : '<div class="overlay"></div>',
    prototype : {
        color : 'white',
        init : function( config ) { this.setLabel( config.label ) ... },
        setLabel : function( labelText ) { ... }
    }
});

// Access Your Class variables
UI.Overlay.threshold    // => 10
UI.Overlay.HTML         // => '<div class="overlay"></div>'

// Catch params in the init method: init : function( config )
var overlay = new UI.Overlay({ color : 'black', label : 'This is my label!' });

// Use your instance
overlay.color;          // => 'black'
overlay.color = 'red';
overlay.setLabel( 'Changing the label!' );
overlay.close();

Features

Convenient DSL for creating classes

The syntax for creating classes is very similar with that of other OO languages.

copy

Class(NameSpace, 'ClassName')({
    classProperty : 'value',
    prototype : {
        init : function( initParams ) { ... },
        instanceMethod : function() { ... }
    }
});

var myInstance = new ClassName( initParams );

Inheritance

Inheritance is pretty much the reason to use OO, so it provides a nice way of subclassing. In esence, it's about extending javascript objects with the properties of the base class.

copy

Class(NameSpace, 'SubClass').inherits(NameSpace.SuperClass)({
    prototype : {
        init : function( initParams ) { ... } // overwrite SuperClass
    }
});

Modules (composition)

You can include modules or mixins in your classes. You can extend multiple modules in a single class.

copy

Module('MyModule')({
    prototype : {
        instanceProperty : false,
        instanceMethod : function() { ... }
    }
});

Class('MyClass').includes(MyModule)({
    prototype : {
        init : function( initParams ) { ... } // overwrite SuperClass
    }
});

var myInstance = new ClassName( initParams );
myInstance.instanceMethod();

API

Class and Module instantiation

copy

Class([namespace], [className]) // => Factory
Module([namespace], [className]) // => Factory

The optional namespace argument is a valid javascript object of which the created class will be a property, if no namespace is passed, it will be the global object.

The className argument is a string with the name of the class. It's possible to not pass a className, in which case the class will have an autogenerated name, this is common for creating anonymous classes. Note that you cannot have an anonymous class inside a namespace, the first argument will be interpreted as the className.

Class and Module definition

copy

factory({
    staticProperties...,
    prototype : {
        instanceProperties...
    }
}); // => Class

The class factory is an executable property, which accepts the class definition as a parameter.

The objects' properties will be copied by reference as static properties to the class.

The prototype property of the class definition will serve as the base prototype for the created objects. In fact, this is the standard prototype of Javascript. The properties in it will be copied by reference to all created objects.

Class initialization

copy

init : function( ... )

When instantiating classes, they will call the init method defined in them, if overriden (It doesn't do anything by default).

To create an instance of an object, use the standard Javascript object constructor new. The parameters are passed to the init function

copy

var x = new MyClass()

Class properties

copy

MyClass.className // => Returns the name of the class
MyClass.superClass // => Returns the actual parent class
myInstance.constructor // => Returns a reference to the object's class

Caveats

Be aware that since javascript's prototype works by reference, all non primitive values will point to the parent's prototype. To avoid this, make sure you assign a new value to those properties after initialization

What it doesn't do

super calls

The reasoning behind not including the super functionality is that it requires introspection. Even though it's possible, the library tries not to mess with the Javascript way of doing things. If you need to call the same method of a parent class, you can use the function property of the prototype and call it yourself, e.g.

copy

myMethod : function(params) {
    ParentClass.prototype.myMethod.call(this, params);
}

private scoping

When working in pure Javascript or using the Node module system, it's easy to hide functions by keeping them as a inner closures and not adding them to the prototype of the class. Neon does not have this functionality, since the classes are just wrapped objects and javascript does not have a way to hide properties However, the suggested convention is to name your private methods with an underscore as the first character (e.g. _myMethod) to indicate that the method is private.

Neon Standard Library

Web develpment has its own set of problems and the patterns they solve them, we decided to include these 3 main components that help a lot when developing for the browser and the server.

  • Event Support

    Event Support

    .js
  • Node Support

    Node Support

    .js
  • Widget

    Widget

    .js

Event Support & Custom Event Support

In javascript event-based programming is really important and one of the main patterns used to communicate components, it helps your code to stay decoupled while building complex programs.

Using EventSupport.js you can let your Neon objects become Event Emitters, so you can send and receive data.

Usage

First you need to add 2 scripts to you application after the Neon.js script.

copy

        <script type="text/javascript" src="CustomEvent.js"></script>
        <script type="text/javascript" src="CustomEventSupport.js"></script>

This 2 scripts will load a class called CustomEvent and a module CustomEventSupport, this module can be added to any class on you existing Neonjs app.

To convert any Neon.js Class into a Event Emitter Class just add the module to you class using includes.

copy

Class('MyClass').includes(CustomEventSupport)({
    prototype : {
        init : function( initParams ) { ... }
    }
});

This way your class and its instances will have new 3 new methods: bind, unbind and dispatch

Api

  • bind
    Allows you to listen a event of the class or instance.
    copy
    
    
    ...
        //let's bind to an Custom Event supported object
        myEmmiter.bind('my-event-name', function( ev ){
            console.log('My data: ', ev.data);
        });
    ...
        
    //Somewhere else:
    ...
        //now let's emmit some events
        myEmmiter.dispatch('my-event-name', {data: 'has been transmited!'});
    ...
    
    
  • unbind
    Allows you to spot listening a event from the class.

    Regular unbinding

    copy
    
        //Ok let's bind to an Custom Event supported object
        myEmmiter.bind('my-event-name', function( ev ){
            console.log('My data: ', ev.data);
        });
    
        //Ok I don't want to hear to this event anymore
        myEmmiter.unbind('my-event-name');
        });
    
    

    Selective unbinding

    copy
    
        //create our callback
        var handler = function( ev ){
            console.log('This will not be called after the selective unbind');
        }
        
        //bind it
        myEmmiter.bind('my-event-name', handler);
    
        //Regular bind
        myEmmiter.bind('my-event-name', function(ev){
            console.log('This will still work after the selective unbind');
        });
        
        //now let's just unbind this listener passing the binded handler
        myEmmiter.unbind('my-event-name', handler);
    
    

    Warning: As you see, to selectively unbind listeners you need to to pass the handler funcion pointer that was binded, if null is passed, all bindings to that event will be removed. This complies to ECMA spec.

  • dispatch
    Allows you to dispatch a event, all the functions that are register as listeners of this event at the moment will be called.
    copy
    
        //Let's bind
        myEmmiter.bind('my-event-name', function(ev){
            console.log('This will still work after the selective unbind');
        });
        
        //Let's emmit
        myEmmiter.dispatch('my-event-name', {data: 'has been transmited!'});
    
    

    dispatch will extend itself using the second parameter in this case {data: value} we recommend using this kind of construct to create your events, always try to pass all your event information using a data index in an object.

Example

In this example we changed the postion of a point, and bind other component to the change, this allows the binded component to trigger behaivor when the point emmits the changed so then we can repaint the point in a map or calculate something based on the new information.

copy

Class('Point').includes(CustomEventSupport)({
    prototype : {
        x : 0,
        y : 0,
        init : function(x, y) {
            this.x = x;
            this.y = y;
        },
        setPosition : function(x,y){
            this.x = x;
            this.y = y;        
            //emmit event
            this.dispatch('changed', {data: {x: x, y: y} });
        }
    }
});

var point = new Point(1,4);

//bind to the event
point.bind('changed', function(ev){
    console.log('point has changed with coordinates: ', 'x: '+ ev.data.x, 'y: '+ ev.data.y);
});

//set things in motion
point.setPosition(2, 4);

Custom Event Support also adds the events API to the class itself:

copy

Class('MyClass').includes(CustomEventSupport)({...});

...

MyClass.dispatch(...);

For more information about Custom events in JavaScript check: Custom events in JavaScript. Neon events are build based on W3C spec for events, it can be check here:Document Object Model (DOM) Level 3 Events Specification

Node Support.js

Node support lets you create a logic tree in an API glance of your classes and instances.

The idea behind node support is to keep your logic connected but separated, using dot notation to navigate the tree:

copy

//Create the parent class
var parentInstance = new ClassWithNodeSupport();

//Use Node Support to logically connect instances
parentInstance.appendChild( new ClassWithNodeSupport({
		name : 'childNode'
	})
);

//Access your nodes using dot notation :)
parentInstance.childNode.accessMethodsAndProperties();

Usage

First you need to add the NodeSupport script to you application after the Neon.js script.

copy

        <script type="text/javascript" src="NodeSupport.js"></script>

This script will load a class called NodeSupport, this module can be added to any class on you existing Neonjs app.

To add node support to your class just add the module to you class using includes.

copy

Class('MyClass').includes(NodeSupport)({
    prototype : {
        init : function( initParams ) { ... }
    }
});

This way your class and its instances will have new 7 new methods: appendChild, insertBefore, removeChild, setParent, getDescendants, getPreviousSibling, getNextSibling.

Api

  • appendChild([child])

    Allows to append a node into another node.

    copy
    
    
    Class('MyNodeSupportedClass').includes(NodeSupport)({
        prototype : {
            init : function( initParams ) { ... }
        }
    });
    
    var parentNode = new MyNodeSupportedClass();
    
    parentNode.appendChild( new MyNodeSupportedClass() );
    
    //The child node will be added to the children array
    parentNode.children[0];
    
    
    
  • insertBefore([child], [beforeChild])

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

    copy
    
    
    Class('MyNodeSupportedClass').includes(NodeSupport)({
        prototype : {
            init : function( initParams ) { ... }
        }
    });
    
    var parentNode = new MyNodeSupportedClass();
    
    parentNode.appendChild( new MyNodeSupportedClass() );
    
    //The child node will be added to the children array
    parentNode.children[0];
    
    
    
  • removeChild([child])

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

    copy
    
    
    Class('MyNodeSupportedClass').includes(NodeSupport)({
        prototype : {
            init : function( initParams ) { ... }
        }
    });
    
    var parentNode = new MyNodeSupportedClass();
    
    parentNode.appendChild( new MyNodeSupportedClass() );
    
    //The child node will be added to the children array
    parentNode.children[0];
    
    
    
  • setParent([parent])

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

    copy
    
    
    Class('MyNodeSupportedClass').includes(NodeSupport)({
        prototype : {
            init : function( initParams ) { ... }
        }
    });
    
    var parentNode = new MyNodeSupportedClass();
    
    parentNode.appendChild( new MyNodeSupportedClass() );
    
    //The child node will be added to the children array
    parentNode.children[0];
    
    
    
  • getDescendants()

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

    copy
    
    
    Class('MyNodeSupportedClass').includes(NodeSupport)({
        prototype : {
            init : function( initParams ) { ... }
        }
    });
    
    var parentNode = new MyNodeSupportedClass();
    
    parentNode.appendChild( new MyNodeSupportedClass() );
    
    //The child node will be added to the children array
    parentNode.children[0];
    
    
    
  • getPreviousSibling()

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

    copy
    
    
    Class('MyNodeSupportedClass').includes(NodeSupport)({
        prototype : {
            init : function( initParams ) { ... }
        }
    });
    
    var parentNode = new MyNodeSupportedClass();
    
    parentNode.appendChild( new MyNodeSupportedClass() );
    
    //The child node will be added to the children array
    parentNode.children[0];
    
    
    
  • getNexSibling()

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

    copy
    
    
    Class('MyNodeSupportedClass').includes(NodeSupport)({
        prototype : {
            init : function( initParams ) { ... }
        }
    });
    
    var parentNode = new MyNodeSupportedClass();
    
    parentNode.appendChild( new MyNodeSupportedClass() );
    
    //The child node will be added to the children array
    parentNode.children[0];
    
    
    

Widget.js

Widget is the Core Class that encapsulates all the basic behaviour of a "Widget". You should inherit from this class to create your own widgets with specialized functionalities. This way you ensure that all your widgets are consistent and respond to the same api.

The main idea behind constructing a new widget toolkit instead of using one of the many high quality widget toolkits avaliable is that we considered that currently, no widget system provides all the features that where required for this project.

Features of the widget system:
  1. A custom and easy to handle event binding, dispatching and manipulation, with some sort of bubbling support
  2. A module system which we can use to include specific behaviour to any widget and reuse the code where needed
  3. A tree structure support for the widgets that the event system could bubble, and that also serves as
  4. A navigation system.
  5. The widgets must be able to be grouped to form more complex widgets
  6. Remove the complexity of DOM manipulation and handling
  7. A way to wrap widgets at our convenience to reuse widgets avaliable and make them comly to our needs without the need to hack those widgets, that would force us to maintain the new versions of those widgets and that is a very complex task when widgets become so complex.
  8. A widget system that would allow us to start wrapping some widgets for a fast start and later code our own widgets at will.
  9. expose a consistent API that allow us to choose the use of widgets by API calls and user interaction at will and with the same clearance and capacity
  10. an easy way to allow subclasing widgets
  11. an easy way to provide new html, class, and css for a specific instance of a widget that would remove us the need to create complex inheritance structures that are hard to maintain.

Usage Example

The most basic usage of a widget is to simply create an instance and render it at a target element in this case body:

copy

var myWidgetInstance = new Breezi.Widget();
myWidgetInstance.render(document.body);

like this widget does renders does not display anything so lets give it something to display first:

copy

var myWidgetInstance = new Breezi.Widget();
myWidgetInstance.element.html('Im a simple widget');
myWidgetInstance.render(document.body);

this reveals that internally every widget has an element property that is initialized by default to a jQuery Instance this allow easy DOM manipulation, animation and operations handled by a high quality third party library.

Piecing it all together

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Odio, maxime, accusamus, incidunt, officiis minima unde veritatis sunt a facere ipsam quasi dolorum magni fuga et optio. Alias vero deserunt commodi.

Final notes

So, why build another Class library system?

Neon has been around since 2010, a time where many class libraries were still being developed. Naturally, many of them were released simultaneousley (especially after the spike in node usage.) More to the point, Neon was created in order to make frontend coding more accesible to backend developers, who already knew OO languages such as Ruby or PHP, likewise, it had the objective creating a common language in which they could communicate. Neon does not try to emulate other languages and preserves the good parts of Javascript, with a little bit of syntax sugar.