New Total.js version 2.4
New Year brings a new version of Total.js framework v2.4. The framework is awesome tool for creating web applications, e-commerce, REST services, Internet of Things or desktop web applications running on Electron.
I have created more than 200 Total.js projects over last 4 years. Most of the projects is still up and running with great results. Give Total.js a try if you are new to Node.js, it's easy to start with.
NEW: Reusable Components
I have added a great new feature called components. Components are server-side reusable components which can contain CSS
, JS
and HTML
in single file. The framework parses each component and splits CSS
+ JS
to separate static files which will be merged and compiled. The framework processes HTML
content as a classic view
(the component has access to all view
commands).
- supports a localization markup e.g.
@(Join our newsletter)
- supports server-side scripting
- supports (almost) all commands from views
CSS
and JS
are compiled
HTML
is minified
- IMPORTANT: download latest version of
debug.js
Directory structure:
/components/
/controllers/
/views/
...
File structure of components:
/components/newsletter.html <!-- HTML with inline <style> (optional) and <script> (optional) -->
/components/newsletter.js <!-- optional, its a server-side implementation (the server-side code can be implemented inline like the example below) -->
Component /components/newsletter.html
:
<script type="text/totaljs">
// This is a server-side implementation of the component (optional), the 'type="text/totaljs"' is important
exports.install = function() {
F.route('/api/newsletter/', json_newsletter, ['post']);
};
function json_newsletter() {
var self = this;
self.body.ip = self.ip;
self.body.created = F.datetime;
NOSQL('newsletter').insert(self.body);
self.json(SUCCESS(true));
}
</script>
<style>
.newsletter { padding: 50px; background-color: #D0D0D0; border-radius: 4px; }
.newsletter-input { font-size: 20px; padding: 20px; width: 100%; }
.newsletter-success { padding: 50px; background-color: green; border-radius: 4px; color: white; }
</style>
<div class="newsletter">
<div>Join our newsletter:</div>
<div><input type="text" class="newsletter-input" placeholder="Type your email address" /></div>
@{if settings.info}
<div>This is not a SPAM.</div>
@{fi}
</div>
<script>
// This is a client-side implementaion of component (optional)
$(document).on('keypress', '.newsletter-input', function(e) {
if (e.keyCode !== 13)
return;
var el = $(this).closest('.newsletter');
$.post('/api/newsletter/', { email: this.value }, function(response) {
el.replaceWith('<div class="newsletter-success">THANK YOU!</div>');
});
});
</script>
Usage in views - /views/myview.html
:
<h2>Newsletter</h2>
@{component('newsletter')};
<!-- OR -->
<h2>Newsletter with a custom settings</h2>
@{component('newsletter', { info: true })}; <!--info is accessible as @{settings.info} as shown above-->
Then the framework renders CSS/JS links into the @{head}
/ @{import('head', ...)}
tag when the view contains some components.
Convertors
Convertors are really helpful for converting values e.g. from String
to Number
when a QueryString (GET/POST+PUT+DELETE form-urlencoded) is parsed.
Create a definition file: convertors.js
:
F.convert('page', Number);
F.convert('newsletter', Boolean);
F.convert('q', val => val.toLowerCase());
Some controller:
// ?page=30&limit=50&newsletter=true&search=TOTAL_FRAMEWORK
function some_action() {
var self = this;
console.log(typeof(self.query.page)); // Number
console.log(typeof(self.query.limit)); // String (no convertor defined for limit)
console.log(typeof(self.query.newsletter)); // Boolean
console.log(self.query.q)); // total_framework
self.plain('IT WORKS!');
}
Operations
The operations are registered asynchronous functions with a better error handling via ErrorBuilder
(similar to schemas).
Benefits:
- error handling
- simple and async usage
- global access
- a same usage like operations in Schemas
Registering operations:
NEWOPERATION('myoperation', function(error, value, callback) {
// error === ErrorBuilder
// value === custom value
// callback === callback([value])
error.push('You don\'t have any privileges');
callback(SUCCESS(true));
});
Calling operations:
OPERATION('myoperation', { my: 'custom value (OPTIONAL)' }, function(err, response) {
console.log(err, response);
});
OPERATION('another.operation', function(err, response) {
// If the operation doesn't exist the "err" argument will contain error
console.log(err, response);
});
Up-To-Date
This feature downloads and evaluates specific Total.js dependencies (controllers, modules, etc.). It uses same functionality like INSTALL()
method but with additional interval
argument.
Create a definition file uptodate.js
:
// WebCounter module
// 5 days up-to-date interval
UPTODATE('module', 'https://modules.totaljs.com/latest/webcounter.js', '5 days');
// Request stats module
// 5 days up-to-date interval + update callback
UPTODATE('module', 'https://modules.totaljs.com/latest/reqstats.js', '5 days', function(err) {
console.log('UPDATED', err);
});
// Total.js monitoring
// Custom options + 5 days up-to-date interval
UPTODATE('module', 'https://modules.totaljs.com/latest/monitor.js', { url: '/$mymonitor/' }, '5 days');
// A component
// 1 day interval
UPTODATE('component', 'https://.../newsletter.html', '1 day');
// Supports event:
F.on('uptodate', function(type, name) {
console.log('UPDATED', type, name);
});
Up-To-Date behaviour can also be used in /dependencies
file:
module (3 days) : https://modules.totaljs.com/latest/monitor.js
How to create e.g. up-to-date module?
- create a module e.g.
mymodule.js
- upload your module on your server
- link your module with your app
UPTODATE('module', 'https://..../mymodule.js', '20 minutes')
- IMPORTANT: framework cleans routes (
F.route()
, F.file()
or F.websocket()
) automatically
Content of mymodule.js
:
exports.install = function(options) {
// initialize your timers, workers, routes, etc.
};
exports.uninstall = function(options) {
if (options === 'uptodate')
console.log('UP-TO-DATE removes older version of this module');
// clean your timers, workers, etc.
});
Configuration files support custom options for dependencies
options
is used in these methods: INSTALL()
and UPTODATE()
- and in all dependencies when they are loaded
E.g. /config
:
// type#name (Object) : { custom options }
module#monitor (Object) : { url: '/$mymonitor/', token: '123456' }
ShellSort replaces QuickSort algorithm
I have performed some tests with sorting algorithms and ShellSort is about 10-15% faster than QuickSort. Note: NoSQL embedded database uses this new sorting algorithm.
I have added back the X-Powered-By
header with Total.js
value (but without Total.js version). You can disable this behaviour via Total.js configuration file:
default-xpoweredby :
// Or modify it to e.g.:
default-xpoweredby : Brutal Framework
String.parseTerminal()
I have added a new String prototype String.parseTerminal([fields], fnLine(values, index, length, realIndex), [skip], [take])
for parsing terminal output.
var output = '';
output += 'COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME';
output += 'total: 21109 root 11u IPv4 413694867 0t0 TCP localhost.localdomain:8000 (LISTEN)';
output.parseTerminal(['PID', 'USER'], function(values) {
console.log('PID:', values[0]);
console.log('USER:', values[1]);
});
// or
output.parseTerminal(function(values) {
console.log(values);
// ['total:', '21109', 'root', '11u', 'IPv4', '413694867', '0t0', 'TCP', 'localhost.localdomain:8000', '(LISTEN)']
}, 1);
New alias for GETSCHEMA()
I have added a new alias for GETSCHEMA()
method, the new alias is $$$([group], name)
and it's more readable (for me). So you can choose what is better for you:
var user = $$$('User').create();
// vs.
var user = GETSCHEMA('User').create();
Improved scripting
I have improved F.script()
and now it can return a compiled function.
require('total.js');
var fn = F.script('next(value + 10)');
fn(10, function(err, response) {
console.log(err, response); // --> null, 20
});
fn(100, function(err, response) {
console.log(err, response); // --> null, 110
});
Mail Message supports unsubscribe method
The method adds List-Unsubscribe
header.
var mail = F.mail(...);
mail.unsubscribe('https://.../unsubscribe/?id=39483948398');
// or
var mail = F.logmail(...);
mail.unsubscribe('https://.../unsubscribe/?id=39483948398');
New Year's cleaning
- Removed
restrictions
(this feature can be created via middleware)
- Removed
behaviours
(it wasn't as useful as expected)
Improvements
- Better performance.
- Improved compression of CSS files.
- A framework codebase has been optimized and decreased in size by about 8 kB.
- The framework is ready for Node v7.
F.datetime
has a better lifecycle.
- Improved memory consumption in
controller.memorize()
and F.localize()
.
Fixes
- A critical bug with authorization (
authorize
and unauthorize
flags) in WebSocket
- JS minificator
- Fixed streaming cache in a
debug
mode
Chat support $70 for 3 months
I'd like to help to answer your questions about Total.js platform, development, solving issues, etc.. All earned money will be used for Total.js platform and its products. Chat is provided by Hangouts, Gitter or Slack.
Total.js website has more than 13 K unique visitors per month and I offer you a great promoting of your company on Total.js homepage, in the partner's section and in all GitHub readme's files. Support the framework, become a sponsor. If you are interested, please contact info@totaljs.com.