• This repository has been archived on 23/Sep/2020
  • Stars
    star
    352
  • Rank 120,622 (Top 3 %)
  • Language
    CoffeeScript
  • License
    MIT License
  • Created almost 14 years ago
  • Updated about 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

scaleApp is a JavaScript framework for scalable and maintainable One-Page-Applications

What is scaleApp?

scaleApp is a tiny JavaScript framework for scalable and maintainable One-Page-Applications / Single-Page-Applications. The framework allows you to easily create complex web applications.

Build Status Dependency Status NPM version Coverage Status

You can dynamically start and stop/destroy modules that acts as small parts of your whole application.

Architecture overview

scaleApp is based on a decoupled, event-driven architecture that is inspired by the talk of Nicholas C. Zakas - "Scalable JavaScript Application Architecture" (Slides). There also is a little Article that describes the basic ideas.

scaleApp architecture

Module

A module is a completely independent part of your application. It has absolutely no reference to another piece of the app. The only thing the module knows is your sandbox. The sandbox is used to communicate with other parts of the application.

Sandbox

The main purpose of the sandbox is to use the facade pattern. In that way you can hide the features provided by the core and only show a well defined custom static long term API to your modules. This is actually one of the most important concept for creating mainainable apps. Change plugins, implementations etc. but keep your API stable for your modules. For each module a separate sandbox will be created.

Core

The core is responsible for starting and stopping your modules. It also handles the messages by using the Publish/Subscribe (Mediator) pattern

Plugin

Plugins can extend the core or the sandbox with additional features. For example you could extend the core with basic functionalities (like DOM manipulation) or just aliases the features of a base library (e.g. jQuery).

Features

  • loose coupling of modules
  • small (about 300 sloc / 8,7k min / 3.3k gz)
  • no dependencies
  • modules can be tested separately
  • replacing any module without affecting other modules
  • extendable with plugins
  • browser and Node.js support
  • flow control
  • AMD & CommonJS support
  • framework-agnostic

Extendable

scaleApp itself is very small but it can be extended with plugins. There already are some plugins available:

  • mvc - simple MVC
  • i18n - multi language UIs
  • permission - take care of method access
  • state - Finite State Machine
  • submodule - cascade modules
  • dom - DOM manipulation
  • strophe - XMPP communication
  • modulestate - event emitter for init and destroy
  • util - helper methods like mixin, uniqueId etc.
  • ls - list modules, instances & plugins

You can easily define your own plugin (see plugin section).

Download

Latest stable 0.4.x version

or use the CDN:

<script src="//cdnjs.cloudflare.com/ajax/libs/scaleapp/0.4.4/scaleapp.min.js" ></script>

Old stable 0.3.x version

Note

There are some API changes in version 0.4.x (see Changelog). Docs for v0.3.9 can be found within the tar/zip file.

Unstable version

git clone git://github.com/flosse/scaleApp.git

Quick Start

Link scaleApp.min.js in your HTML file:

<script src="scaleApp.min.js"></script>

or use the CDN:

<script src="//cdnjs.cloudflare.com/ajax/libs/scaleapp/0.4.4/scaleapp.min.js" ></script>

If you're going to use it with node:

npm install scaleapp --save
var sa = require("scaleapp");

or use bower:

bower install scaleapp

Create your own Sandbox

First of all create your own sandbox. By doing that you're able to guarantee a stable maintainable API for your modules.

var MySandbox = function(core, instanceId, options, moduleId) {

  // define your API
  this.myFooProperty = "bar";

  // e.g. provide the Mediator methods 'on', 'emit', etc.
  core._mediator.installTo(this);

  // ... or define your custom communication methods
  this.myEmit = function(channel, data){
    core.emit(channel + '/' + instanceId, data);
  };

  // maybe you'd like to expose the instance ID
  this.id = instanceId;

  return this;
};

// ... and of course you can define shared methods etc.
MySandbox.prototype.foo = function() { /*...*/ };

Create a core

Now create a new core instance with your sandbox:

var core = new scaleApp.Core(MySandbox);

Register modules

core.register( "myModuleId", function( sandbox ){
  return {
    init:    function(){ /*...*/ },
    destroy: function(){ /*...*/ }
  };
});

As you can see the module is a function that takes the sandbox as a parameter and returns an object that has two functions init and destroy (the latter is optional). Of course your module can be any usual class with those two functions.

var MyGreatModule = function(sandbox){
  return {
    init:    function(){ alert("Hello world!"); }
    destroy: function(){ alert("Bye bye!");     }
  };
};

core.register("myGreatModule", MyGreatModule);

The init function is called by the framework when the module is supposed to start. The destroy function is called when the module has to shut down.

Asynchronous initialization

You can also init or destroy you module in a asynchronous way:

var MyAsyncModule = function(sandbox){
  return {
    init: function(options, done){
      doSomethingAsync(function(err){
        // ...
        done(err);
      });
    },
    destroy: function(done){
      doSomethingElseAsync(done);
    }
  };
};

core.register("myGreatModule", MyGreatModule);
core.start("myGreatModule", { done:function(){
  alert("now the initialization is done");
}});

Start modules

After your modules are registered, start your modules:

core
  .start( "myModuleId" )
  .start( "anOtherModule", function(err){
    // 'anOtherModule' is running now
  });

Start options

You may also want to start several instances of a module:

core.start( "myModuleId", {instanceId: "myInstanceId" } );
core.start( "myModuleId", {instanceId: "anOtherInstanceId" });

All you attach to options is accessible within your module:

core.register( "mod", function(sandbox){
  return {
    init: function(opt){
      (opt.myProperty === "myValue")  // true
    },
    destroy: function(){ /*...*/ }
  };
});

core.start("mod", {
  instanceId: "test",
  options: { myProperty: "myValue" }
});

If all your modules just needs to be instanciated once, you can simply starting them all:

core.start();

To start some special modules at once you can pass an array with the module names:

core.start(["moduleA","moduleB"]);

You can also pass a callback function:

core.start(function(){
  // do something when all modules were initialized
});

Moreover you can use a separate sandbox for each instance:

var MySandbox = function(){/*...*/};
core.start("module", { sandbox: MySandbox });

Stopping

It's obvious:

core.stop("moduleB");
core.stop(); // stops all running instances

Publish/Subscribe

If the module needs to communicate with others, you can use the emit and on methods.

emit

The emit function takes three parameters whereas the last one is optional:

  • topic : the channel name you want to emit to
  • data : the data itself
  • cb : callback method

The emit function is accessible through the sandbox (as long as you exposed the Mediator methods of course):

sandbox.emit( "myEventTopic", myData );

on

A message handler could look like this:

var messageHandler = function( data, topic ){
  switch( topic ){
    case "somethingHappend":
      sandbox.emit( "myEventTopic", processData(data) );
      break;
    case "aNiceTopic":
      justProcess( data );
      break;
  }
};

... and it can listen to one or more channels:

sub1 = sandbox.on( "somthingHappend", messageHandler );
sub2 = sandbox.on( "aNiceTopic", messageHandler );

Or just do it at once:

sandbox.on({
  topicA: cbA,
  topicB: cbB,
  topicC: cbC
});

You can also subscribe to several channels at once:

sandbox.on(["a", "b"], cb);

If you prefer a shorter method name you can use the alias on.

attache and detache

A subscription can be detached and attached again:

sub.detach(); // don't listen any more
sub.attach(); // receive upcoming messages

Unsubscribe

You can unsubscribe a function from a channel

sandbox.off("a-channel", callback);

And you can remove a callback function from all channels

sandbox.off(callback);

Or remove all subscriptions from a channel:

sandbox.off("channelName");

Flow control

Series

var task1 = function(next){
  setTimeout(function(){
    console.log("task1");
    next(null, "one");
  },0);
};

var task2 = function(next){
  console.log("task2");
  next(null, "two");
};

scaleApp.util.runSeries([task1, task2], function(err, result){
  // result is ["one", "two"]
});

// console output is:
// "task1"
// "task2"

Parallel

var task1 = function(next){
  setTimeout(function(){
    console.log("task1");
    next(null, "a");
  },0);
};

var task2 = function(next){
  console.log("task2");
  next(null, "b");
};

scaleApp.util.runParallel([task1, task2],function(err,result){
  // result is ["a", "b"]
});

// console output is:
// "task2"
// "task1"

There is also a little helper tool to run the same async task again and again in parallel for different values:

var vals = ["a","b", "c"];
var worker = function(val, next){
  console.log(val);
  doSomeAsyncValueProcessing(val,function(err,result){
    next(err, result);
  });
};

scaleApp.util.doForAll(args, worker, function(err, res){
  // fini
});

Waterfall

var task1 = function(next){
  setTimeout(function(){
    next(null, "one", "two");
  },0);
};

var task2 = function(res1, res2, next){
  // res1 is "one"
  // res2 is "two"
  next(null, "yeah!");
};

scaleApp.util.runWaterfall([task1, task2], function(err, result){
  // result is "yeah!"
});

Plugins

There are some plugins available within the plugins folder. For more information look at the plugin README.

Register plugins

A single plugin can be registered with it option object in that way:

core.use(plugin,options);

If you want to register multiple plugins at once:

core.use([
  plugin1,
  plugin2,
  { plugin: plugin3, options: options3 }
]);

Write your own plugin

It's easy:

core.use(function(core){
  core.helloWorld = function(){ alert("helloWorld"); };
};

Here a more complex example:

core.use(function(core, options, done){

  // extend the core
  core.myCoreFunction = function(){ alert("Hello core plugin") };
  core.myBoringProperty = "boring";

  // extend the sandbox class
  core.Sandbox.prototype.myMethod = function(){/*...*/};

  // define a method that gets called when a module starts
  var onModuleInit = function(instanceSandbox, options, done){

    // e.g. define sandbox methods dynamically
    if (options.mySwitch){
      instanceSandbox.appendFoo = function(){
       core.getContainer.append("foo");
      };
    }

    // or load a something asynchronously
    core.myAsyncMethod(function(data){

      // do something...
      // now tell scaleApp that you're done
      done();
    });
  };

  // define a method that gets called when a module stops
  var onModuleDestroy = function(done){
    myCleanUpMethod(function(){
      done()
    });
  };

  // don't forget to return your methods
  return {
    init: onModuleInit,
    destroy: onModuleDestroy
  };

});

Usage:

core.myCoreFunction() // alerts "Hello core plugin"

var MyModule = function(sandbox){
  init: function(){ sandbox.appendFoo(); },  // appends "foo" to the container
};

Build browser bundles

If you want scaleApp bundled with special plugins type

grunt custom[:PLUGIN_NAME]

e.g. grunt custom:dom:mvc creates the file scaleApp.custom.js that contains scaleApp itself the dom plugin and the mvc plugin.

API

scaleApp

  • scaleApp.VERSION - the current version of scaleApp
  • scaleApp.Mediator - the Mediator class
  • scaleApp.Sandbox - the Sandbox class
  • scaleApp.Core - the Core class

Core

// use default sandbox
var core = new scaleApp.Core();

// use your own sandbox
var core = new scaleApp.Core(yourSandboxClass);
  • core.register(moduleName, module, options) - register a module
  • core.use(plugin, options) - register a plugin
  • core.use(pluginArray) - registers an array of plugins
  • core.boot(callback) - initialize plugins (will be executed automatically on Β΄startΒ΄)
  • core.start(moduleId, options, callback) - start a module
  • core.stop(instanceId, callback) - stop a module

Mediator

// create a mediator
var mediator = new scaleApp.Mediator();

// create a mediator with a custom context object
var mediator = new scaleApp.Mediator(context);

// create a mediator with cascaded channels
var mediator = new scaleApp.Mediator(null, true);
  • mediator.emit(channel, data, callback)
  • mediator.on(channel, callback, context)
  • mediator.off(channel, callback)
  • mediator.installTo(context, force)
  • mediator.send(channel, payload, callback)
  • mediator.pipe(source, target, mediator)
// subscribe
var subscription = mediator.on(channel, callback, context);
  • subscription.detach - stop listening
  • subscription.attach - resume listening
var fn  = function(){ /*...*/ };
var obj = { emit: fn };

// the installTo method prevents existing properties by default
mediator.installTo(obj);
obj.emit === fn // true

// set the second paramater to 'true'
// to force the mediator to override existing propeties
mediator.installTo(obj, true);
obj.emit === mediator.emit // true

Sandbox

This is the default sandbox of scaleApp. It's a better idea to use your own one.

var sandbox =  new scaleApp.Sandbox(core, instanceId, options, moduleId)` - create a Sandbox
  • sandbox.emit is mediator.emit
  • sandbox.on is mediator.on
  • sandbox.off is mediator.off

Changelog

see CHANGELOG.md

Testing

npm test

Examples

Within the examples directory you can find some basic examples that might help you.

Licence

scaleApp is licensed under the MIT license. For more information have a look at LICENCE.txt.

More Repositories

1

rust-web-framework-comparison

A comparison of some web frameworks and libs written in Rust
Rust
4,385
star
2

sloc

simple tool to count SLOC (source lines of code)
CoffeeScript
935
star
3

rust-os-comparison

A comparison of operating systems written in Rust
593
star
4

json-file-store

A simple JSON store for Node.js
JavaScript
193
star
5

clean-architecture-with-rust

Full-Stack Clean Architecture implementation example written in Rust
Rust
65
star
6

rust-json-file-store

A simple JSON file store written in Rust.
Rust
62
star
7

text-editors-written-in-rust

62
star
8

listOfToDoManagers

Collection of known web based ToDo/Task/Project/GTD-Managers
58
star
9

rust-sun

A Rust library for calculating sun positions
Rust
44
star
10

node-plc

Node.js module to connect to PLCs
C
26
star
11

linuxconsole

http://sourceforge.net/projects/linuxconsole/
C
25
star
12

FAST

Free All Scrambled Thoughs
JavaScript
24
star
13

node-xmpp-serviceadmin

Service Administration (XEP-0133) library for node-xmpp
CoffeeScript
18
star
14

go-modbus

DONT USE IT: A free modbus library for go
Go
12
star
15

r2d2-cypher

Cypher support for the r2d2 connection pool
Rust
10
star
16

node-xmpp-joap

Jabber Object Access Protocol (XEP-0075) library for node-xmpp
CoffeeScript
8
star
17

oven

simple cookie middleware for Iron
Rust
7
star
18

semanticExperiments

Experiments with semantic web technologies
JavaScript
5
star
19

npm-source-sans-pro

CSS
5
star
20

node-ezmlm

Node.js wrapper for ezmlm
CoffeeScript
5
star
21

inversePendulumExperiments

Arduino
5
star
22

bits

Easy bit manipulation
CoffeeScript
4
star
23

als-aid

Advanced Life Support - Mobile App
CSS
4
star
24

xmpp

Clone of the XMPP Standards Foundation XEP repository.
Python
3
star
25

react-vm-flower

React.js component to render a VM flower with SVG
CoffeeScript
3
star
26

smackScalaHelpers

A collection of little helpers written in Scala for the XMPP Smack library
Scala
2
star
27

r2d2-jfs

Rust
2
star
28

multi-vlc

Control multiple VLC instances with only one web interface
CoffeeScript
2
star
29

node-xmpp-logger

Logging over XMPP
CoffeeScript
2
star
30

hello-xmpp

A collection of "Hello world!" examples written with several XMPP-libraries
C
2
star
31

nickel-sqlite

A SQLite middleware for nickel.rs
Rust
2
star
32

node-jid

Parse and handle XMPP/Jabber Identifiers
JavaScript
2
star
33

wasm-browser-extension-test

JavaScript
2
star
34

egui-font-rendering-bug

Rust
1
star
35

npm-strophe-plugins

JavaScript
1
star