{"id":13393025,"url":"https://github.com/flosse/scaleApp","last_synced_at":"2025-03-13T19:31:20.166Z","repository":{"id":58226673,"uuid":"1340006","full_name":"flosse/scaleApp","owner":"flosse","description":"scaleApp is a JavaScript framework for scalable and maintainable One-Page-Applications","archived":true,"fork":false,"pushed_at":"2019-10-14T06:07:08.000Z","size":1526,"stargazers_count":351,"open_issues_count":7,"forks_count":53,"subscribers_count":30,"default_branch":"master","last_synced_at":"2024-05-16T12:02:00.909Z","etag":null,"topics":["coffeescript","event-driven","framework","javascript","onepage","unmaintained"],"latest_commit_sha":null,"homepage":"http://www.scaleapp.org/","language":"CoffeeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/flosse.png","metadata":{"files":{"readme":"README.markdown","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-02-07T23:45:13.000Z","updated_at":"2024-02-20T18:35:44.000Z","dependencies_parsed_at":"2022-09-02T18:41:16.439Z","dependency_job_id":null,"html_url":"https://github.com/flosse/scaleApp","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flosse%2FscaleApp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flosse%2FscaleApp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flosse%2FscaleApp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flosse%2FscaleApp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/flosse","download_url":"https://codeload.github.com/flosse/scaleApp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243469141,"owners_count":20295694,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["coffeescript","event-driven","framework","javascript","onepage","unmaintained"],"created_at":"2024-07-30T17:00:41.285Z","updated_at":"2025-03-13T19:31:19.594Z","avatar_url":"https://github.com/flosse.png","language":"CoffeeScript","readme":"# What is scaleApp?\n\nscaleApp is a tiny JavaScript framework for scalable and maintainable\n[One-Page-Applications / Single-Page-Applications](http://en.wikipedia.org/wiki/Single-page_application).\nThe framework allows you to easily create complex web applications.\n\n[![Build Status](https://secure.travis-ci.org/flosse/scaleApp.png?branch=master)](http://travis-ci.org/flosse/scaleApp)\n[![Dependency Status](https://gemnasium.com/flosse/scaleApp.png?branch=master)](https://gemnasium.com/flosse/scaleApp)\n[![NPM version](https://badge.fury.io/js/scaleapp.png)](http://badge.fury.io/js/scaleapp)\n[![Coverage Status](https://coveralls.io/repos/flosse/scaleApp/badge.png?branch=master)](https://coveralls.io/r/flosse/scaleApp?branch=master)\n\nYou can dynamically start and stop/destroy modules that acts as small parts of\nyour whole application.\n\n## Architecture overview\n\nscaleApp is based on a decoupled, event-driven architecture that is inspired by\nthe talk of Nicholas C. Zakas -\n[\"Scalable JavaScript Application Architecture\"](https://www.youtube.com/watch?v=vXjVFPosQHw)\n([Slides](http://www.slideshare.net/nzakas/scalable-javascript-application-architecture)).\nThere also is a little [Article](http://www.ubelly.com/2011/11/scalablejs/) that\ndescribes the basic ideas.\n\n![scaleApp architecture](https://raw.github.com/flosse/scaleApp/master/architecture.png)\n\n### Module\n\nA module is a completely independent part of your application.\nIt has absolutely no reference to another piece of the app.\nThe only thing the module knows is your sandbox.\nThe sandbox is used to communicate with other parts of the application.\n\n### Sandbox\n\nThe main purpose of the sandbox is to use the\n[facade pattern](https://en.wikipedia.org/wiki/Facade_pattern).\nIn that way you can hide the features provided by the core and only show\na well defined custom static long term API to your modules.\nThis is actually one of the most important concept for creating\nmainainable apps. Change plugins, implementations etc.\nbut keep your API stable for your modules.\nFor each module a separate sandbox will be created.\n\n### Core\n\nThe core is responsible for starting and stopping your modules.\nIt also handles the messages by using the\n[Publish/Subscribe (Mediator) pattern](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern)\n\n### Plugin\n\nPlugins can extend the core or the sandbox with additional features.\nFor example you could extend the core with basic functionalities\n(like DOM manipulation) or just aliases the features of a base library (e.g. jQuery).\n\n## Features\n\n+ loose coupling of modules\n+ small (about 300 sloc / 8,7k min / 3.3k gz)\n+ no dependencies\n+ modules can be tested separately\n+ replacing any module without affecting other modules\n+ extendable with plugins\n+ browser and [Node.js](http://nodejs.org/) support\n+ flow control\n+ [AMD](https://en.wikipedia.org/wiki/Asynchronous_module_definition) \u0026 [CommonJS](http://www.commonjs.org/) support\n+ framework-agnostic\n\n### Extendable\n\nscaleApp itself is very small but it can be extended with plugins. There already\nare some plugins available:\n\n- `mvc` - simple MVC\n- `i18n` - multi language UIs\n- `permission` - take care of method access\n- `state` - Finite State Machine\n- `submodule` - cascade modules\n- `dom` - DOM manipulation\n- `strophe` - XMPP communication\n- `modulestate` - event emitter for `init` and `destroy`\n- `util` - helper methods like `mixin`, `uniqueId` etc.\n- `ls` - list modules, instances \u0026 plugins\n\nYou can easily define your own plugin (see plugin section).\n\n# Download\n\n## Latest stable 0.4.x version\n\n- [scaleApp 0.4.4.tar.gz](https://github.com/flosse/scaleApp/tarball/v0.4.4)\n- [scaleApp 0.4.4.zip](https://github.com/flosse/scaleApp/zipball/v0.4.4)\n\nor use the [CDN](http://en.wikipedia.org/wiki/Content_delivery_network):\n\n```html\n\u003cscript src=\"//cdnjs.cloudflare.com/ajax/libs/scaleapp/0.4.4/scaleapp.min.js\" \u003e\u003c/script\u003e\n```\n\n## Old stable 0.3.x version\n\n- [scaleApp 0.3.9.tar.gz](https://github.com/flosse/scaleApp/tarball/v0.3.9)\n- [scaleApp 0.3.9.zip](https://github.com/flosse/scaleApp/zipball/v0.3.9)\n\n### Note\n\nThere are some API changes in version 0.4.x (see Changelog).\nDocs for v0.3.9 can be found within the tar/zip file.\n\n## Unstable version\n\n- [scaleApp-master.zip](https://github.com/flosse/scaleApp/archive/master.zip)\n\n```shell\ngit clone git://github.com/flosse/scaleApp.git\n```\n\n# Quick Start\n\nLink `scaleApp.min.js` in your HTML file:\n\n```html\n\u003cscript src=\"scaleApp.min.js\"\u003e\u003c/script\u003e\n```\n\nor use the [CDN](http://en.wikipedia.org/wiki/Content_delivery_network):\n\n```html\n\u003cscript src=\"//cdnjs.cloudflare.com/ajax/libs/scaleapp/0.4.4/scaleapp.min.js\" \u003e\u003c/script\u003e\n```\n\nIf you're going to use it with node:\n\n```shell\nnpm install scaleapp --save\n```\n\n```javascript\nvar sa = require(\"scaleapp\");\n```\n\nor use [bower](http://twitter.github.com/bower/):\n\n    bower install scaleapp\n\n## Create your own Sandbox\n\nFirst of all create your own sandbox.\nBy doing that you're able to guarantee a\nstable maintainable API for your modules.\n\n```javascript\nvar MySandbox = function(core, instanceId, options, moduleId) {\n\n  // define your API\n  this.myFooProperty = \"bar\";\n\n  // e.g. provide the Mediator methods 'on', 'emit', etc.\n  core._mediator.installTo(this);\n\n  // ... or define your custom communication methods\n  this.myEmit = function(channel, data){\n    core.emit(channel + '/' + instanceId, data);\n  };\n\n  // maybe you'd like to expose the instance ID\n  this.id = instanceId;\n\n  return this;\n};\n\n// ... and of course you can define shared methods etc.\nMySandbox.prototype.foo = function() { /*...*/ };\n```\n\n## Create a core\n\nNow create a new core instance with your sandbox:\n\n```javascript\nvar core = new scaleApp.Core(MySandbox);\n```\n\n## Register modules\n\n```javascript\ncore.register( \"myModuleId\", function( sandbox ){\n  return {\n    init:    function(){ /*...*/ },\n    destroy: function(){ /*...*/ }\n  };\n});\n```\n\nAs you can see the module is a function that takes the sandbox as a parameter\nand returns an object that has two functions `init` and `destroy` (the latter is\noptional).\nOf course your module can be any usual class with those two functions.\n\n```javascript\nvar MyGreatModule = function(sandbox){\n  return {\n    init:    function(){ alert(\"Hello world!\"); }\n    destroy: function(){ alert(\"Bye bye!\");     }\n  };\n};\n\ncore.register(\"myGreatModule\", MyGreatModule);\n```\n\nThe `init` function is called by the framework when the module is supposed to\nstart. The `destroy` function is called when the module has to shut down.\n\n\n## Asynchronous initialization\n\nYou can also init or destroy you module in a asynchronous way:\n\n```javascript\nvar MyAsyncModule = function(sandbox){\n  return {\n    init: function(options, done){\n      doSomethingAsync(function(err){\n        // ...\n        done(err);\n      });\n    },\n    destroy: function(done){\n      doSomethingElseAsync(done);\n    }\n  };\n};\n\ncore.register(\"myGreatModule\", MyGreatModule);\ncore.start(\"myGreatModule\", { done:function(){\n  alert(\"now the initialization is done\");\n}});\n```\n\n## Start modules\n\nAfter your modules are registered, start your modules:\n\n```javascript\ncore\n  .start( \"myModuleId\" )\n  .start( \"anOtherModule\", function(err){\n    // 'anOtherModule' is running now\n  });\n```\n\n### Start options\n\nYou may also want to start several instances of a module:\n\n```javascript\ncore.start( \"myModuleId\", {instanceId: \"myInstanceId\" } );\ncore.start( \"myModuleId\", {instanceId: \"anOtherInstanceId\" });\n```\n\nAll you attach to `options` is accessible within your module:\n\n```javascript\ncore.register( \"mod\", function(sandbox){\n  return {\n    init: function(opt){\n      (opt.myProperty === \"myValue\")  // true\n    },\n    destroy: function(){ /*...*/ }\n  };\n});\n\ncore.start(\"mod\", {\n  instanceId: \"test\",\n  options: { myProperty: \"myValue\" }\n});\n```\n\nIf all your modules just needs to be instanciated once, you can simply starting\nthem all:\n\n```javascript\ncore.start();\n```\n\nTo start some special modules at once you can pass an array with the module\nnames:\n\n```javascript\ncore.start([\"moduleA\",\"moduleB\"]);\n```\n\nYou can also pass a callback function:\n\n```javascript\ncore.start(function(){\n  // do something when all modules were initialized\n});\n```\n\nMoreover you can use a separate sandbox for each instance:\n\n```javascript\nvar MySandbox = function(){/*...*/};\ncore.start(\"module\", { sandbox: MySandbox });\n```\n\n## Stopping\n\nIt's obvious:\n\n```javascript\ncore.stop(\"moduleB\");\ncore.stop(); // stops all running instances\n```\n\n## Publish/Subscribe\n\nIf the module needs to communicate with others, you can use the `emit` and\n`on` methods.\n\n### emit\n\nThe `emit` function takes three parameters whereas the last one is optional:\n- `topic` : the channel name you want to emit to\n- `data`  : the data itself\n- `cb`    : callback method\n\nThe emit function is accessible through the sandbox\n(as long as you exposed the Mediator methods of course):\n\n```javascript\nsandbox.emit( \"myEventTopic\", myData );\n```\n\n### on\n\nA message handler could look like this:\n\n```javascript\nvar messageHandler = function( data, topic ){\n  switch( topic ){\n    case \"somethingHappend\":\n      sandbox.emit( \"myEventTopic\", processData(data) );\n      break;\n    case \"aNiceTopic\":\n      justProcess( data );\n      break;\n  }\n};\n```\n\n... and it can listen to one or more channels:\n\n```javascript\nsub1 = sandbox.on( \"somthingHappend\", messageHandler );\nsub2 = sandbox.on( \"aNiceTopic\", messageHandler );\n```\nOr just do it at once:\n\n```javascript\nsandbox.on({\n  topicA: cbA,\n  topicB: cbB,\n  topicC: cbC\n});\n```\n\nYou can also subscribe to several channels at once:\n\n```javascript\nsandbox.on([\"a\", \"b\"], cb);\n```\n\nIf you prefer a shorter method name you can use the alias `on`.\n\n#### attache and detache\n\nA subscription can be detached and attached again:\n\n```javascript\nsub.detach(); // don't listen any more\nsub.attach(); // receive upcoming messages\n```\n\n#### Unsubscribe\n\nYou can unsubscribe a function from a channel\n\n```javascript\nsandbox.off(\"a-channel\", callback);\n```\n\nAnd you can remove a callback function from all channels\n\n```javascript\nsandbox.off(callback);\n```\n\nOr remove all subscriptions from a channel:\n\n```javascript\nsandbox.off(\"channelName\");\n```\n\n## Flow control\n\n### Series\n\n```javascript\nvar task1 = function(next){\n  setTimeout(function(){\n    console.log(\"task1\");\n    next(null, \"one\");\n  },0);\n};\n\nvar task2 = function(next){\n  console.log(\"task2\");\n  next(null, \"two\");\n};\n\nscaleApp.util.runSeries([task1, task2], function(err, result){\n  // result is [\"one\", \"two\"]\n});\n\n// console output is:\n// \"task1\"\n// \"task2\"\n```\n\n### Parallel\n\n```javascript\nvar task1 = function(next){\n  setTimeout(function(){\n    console.log(\"task1\");\n    next(null, \"a\");\n  },0);\n};\n\nvar task2 = function(next){\n  console.log(\"task2\");\n  next(null, \"b\");\n};\n\nscaleApp.util.runParallel([task1, task2],function(err,result){\n  // result is [\"a\", \"b\"]\n});\n\n// console output is:\n// \"task2\"\n// \"task1\"\n```\n\nThere is also a little helper tool to run the same async task\nagain and again in parallel for different values:\n\n```javascript\nvar vals = [\"a\",\"b\", \"c\"];\nvar worker = function(val, next){\n  console.log(val);\n  doSomeAsyncValueProcessing(val,function(err,result){\n    next(err, result);\n  });\n};\n\nscaleApp.util.doForAll(args, worker, function(err, res){\n  // fini\n});\n```\n\n### Waterfall\n\n```javascript\nvar task1 = function(next){\n  setTimeout(function(){\n    next(null, \"one\", \"two\");\n  },0);\n};\n\nvar task2 = function(res1, res2, next){\n  // res1 is \"one\"\n  // res2 is \"two\"\n  next(null, \"yeah!\");\n};\n\nscaleApp.util.runWaterfall([task1, task2], function(err, result){\n  // result is \"yeah!\"\n});\n```\n\n## Plugins\n\nThere are some plugins available within the `plugins` folder.\nFor more information look at the\n[plugin README](https://github.com/flosse/scaleApp/blob/master/plugins/README.md).\n\n### Register plugins\n\nA single plugin can be registered with it option object in that way:\n\n```javascript\ncore.use(plugin,options);\n```\nIf you want to register multiple plugins at once:\n\n```javascript\ncore.use([\n  plugin1,\n  plugin2,\n  { plugin: plugin3, options: options3 }\n]);\n```\n\n### Write your own plugin\n\nIt's easy:\n\n```javascript\ncore.use(function(core){\n  core.helloWorld = function(){ alert(\"helloWorld\"); };\n};\n```\n\nHere a more complex example:\n\n```javascript\ncore.use(function(core, options, done){\n\n  // extend the core\n  core.myCoreFunction = function(){ alert(\"Hello core plugin\") };\n  core.myBoringProperty = \"boring\";\n\n  // extend the sandbox class\n  core.Sandbox.prototype.myMethod = function(){/*...*/};\n\n  // define a method that gets called when a module starts\n  var onModuleInit = function(instanceSandbox, options, done){\n\n    // e.g. define sandbox methods dynamically\n    if (options.mySwitch){\n      instanceSandbox.appendFoo = function(){\n       core.getContainer.append(\"foo\");\n      };\n    }\n\n    // or load a something asynchronously\n    core.myAsyncMethod(function(data){\n\n      // do something...\n      // now tell scaleApp that you're done\n      done();\n    });\n  };\n\n  // define a method that gets called when a module stops\n  var onModuleDestroy = function(done){\n    myCleanUpMethod(function(){\n      done()\n    });\n  };\n\n  // don't forget to return your methods\n  return {\n    init: onModuleInit,\n    destroy: onModuleDestroy\n  };\n\n});\n```\n\nUsage:\n\n```javascript\ncore.myCoreFunction() // alerts \"Hello core plugin\"\n\nvar MyModule = function(sandbox){\n  init: function(){ sandbox.appendFoo(); },  // appends \"foo\" to the container\n};\n```\n\n# Build browser bundles\n\nIf you want scaleApp bundled with special plugins type\n\n```shell\ngrunt custom[:PLUGIN_NAME]\n```\ne.g. `grunt custom:dom:mvc` creates the file `scaleApp.custom.js` that\ncontains scaleApp itself the dom plugin and the mvc plugin.\n\n# API\n\n## scaleApp\n\n- `scaleApp.VERSION` - the current version of scaleApp\n- `scaleApp.Mediator` - the Mediator class\n- `scaleApp.Sandbox` - the Sandbox class\n- `scaleApp.Core` - the Core class\n\n## Core\n\n```javascript\n// use default sandbox\nvar core = new scaleApp.Core();\n\n// use your own sandbox\nvar core = new scaleApp.Core(yourSandboxClass);\n```\n\n- `core.register(moduleName, module, options)` - register a module\n- `core.use(plugin, options)` - register a plugin\n- `core.use(pluginArray)` - registers an array of plugins\n- `core.boot(callback)` - initialize plugins\n   (will be executed automatically on ´start´)\n- `core.start(moduleId, options, callback)` - start a module\n- `core.stop(instanceId, callback)` - stop a module\n\n## Mediator\n\n```javascript\n// create a mediator\nvar mediator = new scaleApp.Mediator();\n\n// create a mediator with a custom context object\nvar mediator = new scaleApp.Mediator(context);\n\n// create a mediator with cascaded channels\nvar mediator = new scaleApp.Mediator(null, true);\n```\n\n- `mediator.emit(channel, data, callback)`\n- `mediator.on(channel, callback, context)`\n- `mediator.off(channel, callback)`\n- `mediator.installTo(context, force)`\n- `mediator.send(channel, payload, callback)`\n- `mediator.pipe(source, target, mediator)`\n\n```javascript\n// subscribe\nvar subscription = mediator.on(channel, callback, context);\n```\n- `subscription.detach` - stop listening\n- `subscription.attach` - resume listening\n\n```javascript\nvar fn  = function(){ /*...*/ };\nvar obj = { emit: fn };\n\n// the installTo method prevents existing properties by default\nmediator.installTo(obj);\nobj.emit === fn // true\n\n// set the second paramater to 'true'\n// to force the mediator to override existing propeties\nmediator.installTo(obj, true);\nobj.emit === mediator.emit // true\n```\n\n## Sandbox\n\nThis is the default sandbox of scaleApp.\nIt's a better idea to use your own one.\n\n```javascript\nvar sandbox =  new scaleApp.Sandbox(core, instanceId, options, moduleId)` - create a Sandbox\n```\n- `sandbox.emit` is `mediator.emit`\n- `sandbox.on` is `mediator.on`\n- `sandbox.off` is `mediator.off`\n\n# Changelog\n\nsee `CHANGELOG.md`\n\n# Testing\n\n```shell\nnpm test\n```\n\n# Examples\n\nWithin the [`examples`](https://github.com/flosse/scaleApp/tree/master/examples)\ndirectory you can find some basic examples that might help you.\n\n# Licence\n\nscaleApp is licensed under the MIT license.\nFor more information have a look at\n[LICENCE.txt](https://raw.github.com/flosse/scaleApp/master/LICENCE.txt).\n","funding_links":[],"categories":["CoffeeScript","framework"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflosse%2FscaleApp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fflosse%2FscaleApp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflosse%2FscaleApp/lists"}