This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Documentation

alt-seven, the client-side JavaScript framework

1 - Overview

Here’s where you find out if alt-seven is useful for you.

This page provides a brief overview of the alt-seven framework, how it came about, and why you might want to use it.

Just what is alt-seven?

In short, alt-seven is a reactive JavaScript framework intended for building single page applications (SPA). Alt-seven attempts to simplify SPA JavaScript development by relying on core JavaScript language constructs like Template Literals for templating rather than extensions like JSX that require transpiling prior to deployment. Alt-seven has no external code dependencies, though it does offer integration with different third party libraries, and contains hooks to allow you to intgrate others in certain places. Because there is no parsing/transpiling/packaging step in alt-seven development, there is no tree shaking at this point.

How was alt-seven created?

The framework started as an exploration in reactive JavaScript development to see if it was possible to build a reactive framework for SPAs using Template Literals for templating simple, core JavaScript language, and without a build process as a necessary step for deployment. After proving those concepts, the author built the core of the framework and created some simple applications to show it off.

Why would I want it?

If you are looking for a reactive JavaScript framework for a single page application project, you should give alt-seven a look. If you are learning about JavaScript-based Web application development, you should also give alt-seven a look, as it demonstrates a lot of concepts in modern JavaScript.

  • What is it good for?: Alt-seven provides an easy way to build reactive applications in JavaScript without having to concern yourself with a lot of the details of reactive development. Some useful features:

    • remote package that provides an easy way to communicate with a back-end server via JSON
    • model for storing data
    • authentication mechanism with some back-end code examples to use for different back-ends, including Node.JS
    • logging system with multiple log levels
    • in-browser console that allows you to see log messages in the browser in real-time ( this is really useful for integrating back-end logging in real-time so you can see back-end log messages in the browser )
    • error capture
    • application router
    • simple custom event binding for components
    • publish/subscribe event model
  • What is it not good for?: If you are looking for a framework that compiles, packages, and obfuscates your application code, that’s not alt-seven.

  • What is it not yet good for?: Alt-seven has not yet been used to build a large scale deployed application, so if you are looking for a framework that is already been proved in large scale deployments, you will need to look to other frameworks like React.JS and Vue.JS.

Where should I go next?

2 - Getting Started

Getting started with the alt-seven framework.

This page covers your first steps with the alt-seven framework.

Prerequisites

Alt-seven is a client-side JavaScript framework. It runs in the context of a web browser, and typically- though not necessarily- runs on the Internet in the context of a web application served by a web server such as NGINX, IIS, or Apache Web Server.

Installation

The simplest method to install alt-seven is via npm.

$ npm install alt-seven

You can also download alt-seven from GitHub:

https://github.com/shoestringlab/altseven

Setup

Depending on how you installed alt-seven, you will need to reference it from your root HTML file in your project.

  • If you installed via npm, the framework will be located at: node_modules/altseven from the project root.

  • If you downloaded via git clone, the framework will be located in the /dist folder of the cloned alt-seven project.

  • If you downloaded an archive from GitHub, the framework will be located in the /dist folder inside the archive.

In the case of downloading via npm, you may want to create an alias that you use to reference the location of altseven.

For example, in a Node.js application using Express, you may set up a client-side reference like so:

`app.use( "/lib/altseven", express.static( 'node_modules/altseven' ) );`

Try it out!

2.1 - Simple Example

Example code for a very simple alt-seven application.

This page shows code for a very simple alt-seven application.

index.html

<!doctype html>
<html>
<head>
	<title>alt-7 example</title>
	<script type="module">
		import {application} from './app.js';
		var app = application();
	</script>
</head>
<body>
	<div name="main">

	</div>
</body>
</html>

app.js

import {a7} from '/dist/a7.js';

var app = {
  main: (function() {
    return {
      init: function(state) {

        // render the hello page
        app.components.Hello( { id: 'hello', selector: "div[name='main']" } );

      }
    };
  })(),
  components: (function() {

    function Hello(props) {
      var hello = a7.components.Constructor(a7.components.View, [props], true);

      hello.state = {
        text: " World!"
      };

      hello.template = function(){
        return `<h3>Hello ${hello.state.text}</h3>`;
      };

      return hello;
    }

    return {
      Hello: Hello
    };

  })()
};

export var application = function init() {

  var options = {};

  var p = new Promise(function(resolve, reject) {
    a7.init(options, resolve, reject);
  });
  p.then(function(state) {
	app.main.init();
    a7.log.info("App init.");
  });
  p['catch'](function(message) {
    console.log(message);
  });

  return app;
};

3 - Concepts

Things You Should Know about how alt-seven works.

This page shares some underlying concepts about how alt-seven works.

ES Modules

Alt-seven uses the ECMAScript standard for module imports. Familiarize yourself with how to structure individual modules for export and how to import them into another module.

Configuration Options

Alt-seven offers many application level configuration options that are specified when the application is initialized. As you can see in the simple example, no options are required, so you can pass options = {};, or you can specify any number of options on initialization that affects how the application functions.

Configuring Your Application

Template Literals

Template literals are a key concept in ES6 for building web applications. They are string literals delimited by backticks (`) that allow you to create multi-line templates. See the documentation on template literals at the Mozilla Developer Network.

Template literals allow a developer to build complex HTML displays without resorting to third-party extensions like JSX. One of the initial questions during the development of alt-seven was whether event attributes could be embedded in the template literals and become part of the active application. The answer was that event attributes would not generate event listeners, so something else needed to be done, and that something is to use the data- attribute notation to add event attributes that can be parsed and used to generate event listeners once the HTML is generated.

In the example below, alt-seven sees the data-onclick attributes, parses them, and adds event listeners for them. In this case, the view component is called gallery and it has a section called gallery.eventHandlers that contains the event handlers listed in the template literal. In this way, the developer can specify dynamic interactivity that happens as HTML is generated on the fly in the application.

Note that for the sake of legacy application porting, alt-seven also include support for Mustache and Handlebars as templating libraries.

`
<div>
  <img id="galleryImage" data-zoomable="true" src="/assets/img/` + state.images[state.currentIndex] + `">
</div>

<div id="galleryNavBar" class="slidecontainer,inline">
  <input type="range" min="0" max="` + (state.images.length - 1) + `" 
  value="` + state.currentIndex + `" class="slider" id="gallerySlider" data-onchange="changeImage">
</div>
<div class="galleryNav">
  <span data-onclick="decrementIndex"><i data-feather="arrow-left"></i></span>
  ` + (parseInt( state.currentIndex, 10 ) + 1 ) + ` of ` + state.images.length + `
  <span data-onclick="incrementIndex"><i data-feather="arrow-right"></i></span>
</div>		
`
  gallery.eventHandlers = {
		changeImage: function( event ){
			gallery.setState( Object.assign( gallery.getState(), { currentIndex: event.target.value }));
		},
		decrementIndex: function( event, x ){
			let value = (x === undefined ? 1 : x );
			let state = gallery.getState();
			gallery.setState( Object.assign( state, { currentIndex: Math.max( 0, state.currentIndex - value ) } ));
		},
		incrementIndex: function( event, x ){
			let value = (x === undefined ? 1 : x );
			let state = gallery.getState();
			gallery.setState( Object.assign( state, { currentIndex: Math.min( state.images.length-1, state.currentIndex + value ) } ));
		}
	};

Events

Alt-seven uses events to control program flow. Naming of events is left up to the programmer, but a standard convention is to organize events by area of the application. For example, all events related to users might be contained inside the “user.” space. In a given view, clicking links may call events that then call other components to carry out the intended actions.

  a7.events.subscribe( "user.confirm", function( obj ){
    // code to confirm the user account
  });

  a7.events.subscribe( "user.profile", function( obj ){
    // code to retrieve and display the user profile
  });

  // called in a view
  a7.events.publish( "user.profile", { userID: user.userID } );

URL Routes

Alt-seven includes a URL router to allow bookmarking and browser history inside your application. Routes are mapped to events and can be specified in a single file.

Here is an example of route mapping:

export var routes = [
	[ '/comment/showform', 'comment.showForm' ],
	[ '/', 'main.home' ]
];

Routes can then be called from anywhere in the application. Routes may also pass an arbitrary data structure as parameters to be passed to the event mapped to the route.

	// simple opening of a route
	a7.router.open( "/" );

	// pass a parameter to the event
	a7.router.open( "/comment/showForm", { message: "Say something..." });

4 - Core Tasks

What can you do with alt-seven?

This page shows some core tasks that you can tackle using alt-seven.

4.1 - User Authentication

A brief overview of the built-in user authentication system in alt-sever.

User Authentication

Alt-seven includes a built-in authentication mechanism that allows you to authenticate users against your back end server. Rather than using a custom authentication system, alt-seven uses HTTP Basic Authentication, sending an Authorization header with encoded username and password to the server via remote fetch. The framework sends back an X-Token response header that contains an encoded payload with the authenticated user’s information.

Because the authentication methods are included in the framework, all the programmer needs to do is configure the loginURL, logoutURL, and refreshURL in the application configuration, then call the auth.login event in the framework. The method then calls the specified loginURL and passes the username and password to the authentication URL. See more information in the authentication tutorial.

Authentication Tutorial

5 - Tutorials

Show your user how to work through some end to end examples.

This page shows tutorials available.

5.1 - Configuration

A short tutorial that demonstrates how to configure your alt-seven application.

alt-seven configuration options tutorial

You configure your alt-seven application by passing an options structure to the a7.init method. There are a host of options that you can tailor to your needs. Below is an example call to a7.init() with all possible options specified. The default value for a given option is bracketed like so [templateLiterals],

var remoteModules = {}; // format specified in Remote section
var options = {
  model: ["altseven"], // defaults to the internal model. Detail in the Model tutorial
  auth: {
    sessionTimeout: 15 * 60 * 1000 // 15 minutes. The built-in remote function calls the refresh URL at this interval
  },
  console: {
    enabled: true,
    wsServer: "ws://example.com/ws"|"", // server-side websocket logger, specified in Console tutorial
    container: ["gadgetui.display.FloatingPane"|""],//if enabled = false, defaults to ""
    top: [100],
    left: [500],
    width: [500],
    height: [300]
  },
  logging: {
    logLevel: ["ERROR,FATAL,INFO"], // all option: "DEBUG,INFO,WARN,FATAL,TRACE"
    toBrowserConsole: true|[false] // passes the log message to the console
  },
  remote: {
    loginURL: "/login", // default: "", remote method should return a json object { success: true|false, user: Object }.
    logoutURL: "/logout",  // default: "", remote method should return a json object { success: true|false }
    refreshURL: "/refresh", // default: "", remote method should return a json object { success: true|false }
    useTokens: [true]|false // useTokens looks for token, format specified in the authentication section
    modules: {} // format specified in Remote section
  },
  router: {
    useEvents: [true]|false, // option calls events when a route is matched. Specified in the Routing tutorial
    routes: [
    	['/auth/showlogin', 'auth.showLogin'] // routes is an array of paths to match. More details in the Routing tutorial
    ]
  },
  ui: {
    renderer: "Handlebars|Mustache|[templateLiterals]", // templating engine. templateLiterals is the internal engine
    debounceTime: 20, // time in ms before a render request of an object can be queued
    timeout: 3600000 // 1 hour , time before a cached view expires
  }
};

a7.init(options);

See the app.js file in the LacesIDE project for a complete way to implement the a7.init(options) method in the context of an application.

5.2 - Authentication

A short tutorial that demonstrates how to use the built-in authentication system in alt-seven.

alt-seven authentication tutorial

Configuration

In order to enable built-in authentication, you must configure your application with the correct options. Here is a sample of the authentication options to be passed to the framework:

	remote: {
		modules: app.remote,
		loginURL: "/api/auth/login",
		logoutURL: "/api/auth/logout",
		refreshURL: "api/auth/refresh"
	},

In addition to the authentication methods, the a7 model must be enabled for authentication to work. See the page on configuring your application to understand all the options available to you.

Built-in Events and Remote Methods

The event system contains a set of pre-defined events and corresponding remote methods for the built-in authentication system. For login, you need to pass a params object to the event in this format { username: "", password: "", success: ""|func, failure: ""|func }.

    a7.events.subscribe("auth.login", function(params) {
      a7.remote.invoke("auth.login", params);
    });
    a7.events.subscribe("auth.logout", function(params) {
      a7.remote.invoke("auth.logout", params);
    });
    a7.events.subscribe("auth.refresh", function(params) {
      a7.remote.invoke("auth.refresh", params);
    });
    a7.events.subscribe("auth.sessionTimeout", function() {
      a7.security.invalidateSession();
    });
    a7.events.subscribe("auth.invalidateSession", function() {
      a7.security.invalidateSession();
    });

Calling the login event

In the login form, the programmer can then specify success and failure parameters, which can be methods, event names, or router destinations. In the below example, you send the username and password to the auth.login event and specify success and failure methods to run after the authentication method returns from the server.

    a7.events.publish('auth.login', {
      username: loginform.state.username,
      password: loginform.state.password,
      success: function( json ){
        main.runApp();
      },
      failure: function( json ){
        state.message = json.message;
        state.username = "";
        state.password = "";
        loginform.setState( state );
        loginform.fireEvent("mustRender");
		  }
    });

Options for success and failure params

Success and failure could also be specified as events, such as:

  success: "auth.loginSuccess",
  failure: "auth.loginFailure"

or as router destinations:

  success: "auth/loginSuccess",
  failure: "auth/loginFailure"

The programmer must then create the events or router destinations named:

  a7.events.subscribe( "auth.loginSuccess", function( obj ){
    // handle login success tasks
  });

You can see more details on working with the router in the routing tutorial.

Creating remote authentication methods

You can build remote methods for authentication on the platform of your choice. The remote methods must satisfy these contracts:

The User object

Alt-seven has a built-in User object that it employs for its authentication system. The User object by default is an empty object that is populated with the keys returned in the login response from the remote method. In order to facilitate authentication checking, you can specify a set of default keys, such as userID, to populate the anonymous User object when a system user is not authenticated. When the user is authenticated, the login response sets the user into the a7 model and into sessionStorage.user. The logout response clears the existing user and sets an anonymous user into the model and sessionStorage.user.

// the User object may be extended in the future with events you can listen to, so this would be the preferred method for creating a user object
let user = a7.components.Constructor(
			a7.components.User,
			[{ userID: '' }],
			true // add events
		)

// or simply
let user = a7.components.User( { userID: '' } ); // no events will be added to the user object

login

Input arguments: String username String password

Return: Object user

Note that the login remote method uses Basic Authentication, sending the username and password as Base64-encoded strings in the method header. The server authentication method must check the Authorization header to get the username and password. See the code from a7.remote below:

	login: function( params ){
		a7.log.trace( "remote call: auth.login" );
		var request,
				args = { 	method: 'POST',
						headers: {
							"Authorization": "Basic " + a7.util.base64.encode64( params.username + ":" + params.password )
						}
				};

		request = new Request( _options.loginURL , args );

		var promise = fetch( request );
 // continues ...

The returned user object, in JSON format, will be parsed by the response in the remote method, the keys from the user object will be added to the built-in alt-seven User object, and the user object will be set in the model as so a7.model.set( "user", user ).

If you are using tokens for the remote methods, the response will look for the ‘X-Token’ response header and set the returned token into sessionStorage.token. This token will be used to keep the user’s session alive using the refresh() remote method.

Let’s look at the remote authentication method in the LacesIDE open source application. It provides a good example of how to work with the auth meachanism in NodeJS. Other languages and platforms will vary. The controller methods for login, logout, and refresh are listed below. They contain the heart of the functionality you need to understand. You can look through the LacesIDE source code if you want to see the full details of the implementation. Start with routes/api.auth.js and go from there.

The remote method extracts the username and password from the Authorization header, Base64 decodes the authorization string, then splits it into the username and password components. In this example, userservice.getByUsername(username) retrieves the user record from the database, the password is hashed with crypto.pbkdf2Sync, and the hash is compared to the password hash stored in the databaase. Note that the hash and salt are removed from the user record for security before the user record is returned to the client. LacesIDE uses tokens, so the X-Token is set with the value of a generated token, and the response is encoded and returned.


	login: function (request, response) {
			let authorization = request.header("Authorization") || "";
			let username = "";
			let password = "";
			if (authorization.length) {
				let authArray = authorization.split(" ");
				authorization = Base64.decode(authArray[1]);
				username = authorization.split(":")[0];
				password = authorization.split(":")[1];
			}

			userservice.getByUsername(username)
				.then(function (results) {
					let valid = false;
					if (results) {
						let hash = 	crypto.pbkdf2Sync(password, results.salt, 1000, 64, `sha512`).toString(`hex`);
						valid = ( hash === results.hash );
					}
					if (valid) {
						let user = results;
						// remove the hash and salt from the token so we don't send it outside the system
						delete user.hash;
						delete user.salt;
						response.setHeader("X-Token", utils.generateToken(user));
						response.send({ user: user, success: true });
					} else {
						throw ({ success: false, error: "Invalid username/password combination." });
					}
				})
				.catch(function (error) {
					console.log(error);
					response.send(JSON.stringify(error));
				});
		},
		logout: function (request, response) {
			response.send({ success: true });
		},
		refresh: function (request, response) {
			//let token = request.header( "X-Token" );
			response.send({ success: true });
		}

Session Tokens

Alt-seven uses the session tokens to authenticate a user during the logged in session. Note that the timeout of the server side session should match the timeout of the timeout value in the alt-seven application so the session stays active as long as the user is logged in and the browser window stays open. This is the default behavior of the application.

While the user is authenticated, alt-seven will automatically include the X-Token header in every remote request, and will pass the current token for the user in that header. The token should be in string format in order for the system to pass it back and forth in the X-Token header. Beyond that, the format and contents of the token are left to the programmer. Below are the generateToken and checkAuthToken methods from the LacesIDE application. In this case, you can see that the token is the user object with a key expires added to it. The expires key is used to check the expiration of the session.

generateToken: function( user ){
      const hash = new SHA3(512);

      let authtoken = Object.assign( {}, user );
      let now = new Date();
      authtoken.expires =  new Date( new Date( now ).getTime() + securityConfig.ttl );
      console.log( "current date " +  new Date( now ) );
      console.log( "expires " + authtoken.expires );
      let base64Token = Base64.encode( JSON.stringify( authtoken ) );
      //let base64Token = Base64.encode( authtoken );
      hash.update( base64Token );
      let myhash = hash.digest( 'hex' );
      console.log( myhash );

      return JSON.stringify( { token: base64Token, hash: myhash } );
    },

    checkAuthToken: function( authtoken, timeout ){
			const hash = new SHA3(512);
			let auth;
			authtoken = JSON.parse( authtoken );
			let token = authtoken.token;
			let hashCode = authtoken.hash;
			hash.update( token );
			let myhash = hash.digest( 'hex' );
			if( hashCode == myhash ){
				// token is valid
				auth = JSON.parse( Base64.decode( token ) );
			}
			return auth;
    },

LacesIDE uses the Express framework, which enables the user of interceptors to check HTTP inbound requests before they are routed to the rest of the application. The application combines an httpinterceptor with route-based security to determine wheter a user is allowed to access a given URL path in the application. Below is the checkHTTPAuth method from the interceptor, used to authenticate the user with each request. If you use the alt-seven authentication system with tokens, how you implement session management on the server is up to you. This code from LacesIDE provides one method of implementing session management in NodeJS.

checkHTTPAuth: function (request, response, next) {

			// is it an open route?
			let openRoute = securityConfig.routes.open.find(function (route) {
				//console.log( "route: " + route );
				return request.url.match(route);
			});

			// is it a secured route?
			let securedRoute = securityConfig.routes.secured.find(function (route) {
				//console.log( "route: " + route );
				return request.url.match(route);
			});

			console.log("Checking auth");
			// check token
			let token = request.headers["x-token"];
			// anonymous access
			if (token === undefined || token.length === 0) {
				// not a logged in user
				if (openRoute !== undefined) {
					// if this is an open route, pass them along
					next();
				} else if (securedRoute !== undefined) {
					notAuthorized(request, response);
				} else {
					// route not found
					console.log("WARN: Route not found for URL:" + request.url);
					console.log("Pass through allowed.");
					next();
				}
			} else {
				let auth = utils.checkAuthToken(token, securityConfig.ttl);

				userservice.read(auth.userID)
					.then(function (results) {
						let valid = false;
						let user = results;
						let now = new Date();
						console.log("userID: " + user.userID);
						let expires = new Date(auth.expires).getTime() ;
						let nowtime = now.getTime();
						console.log( "sessiontime: " + expires - nowtime );
						// if there is a valid logged in user, pass them
						if (user.userID.length > 0 && (new Date(auth.expires).getTime() - now.getTime() > 0)) {
							valid = true;
							// remove the password hash from the token so we don't send it outside the system
							delete user.hash;
							delete user.salt;

							// this call sets a user into the request
							request.user = user;
							// set a new header token
							response.setHeader("X-Token", utils.generateToken(user));
							console.log("X-Token set");
							// move on to the route handlers
							//next();
						} else if (user.userID.length > 0) {
							console.log("Expires: " + new Date(auth.expires).getTime());
							console.log("Now: " + now.getTime());
							console.log("Authorization expired.");
							response.messages = "Authorization expired.";
						}

						if (securedRoute !== undefined) {
							console.log(request.url + " matches " + securedRoute);
							console.log("User validated? " + valid);
							if (valid === true) {
								// securedRoute and logged in user, forward them along
								next();
							} else {
								notAuthorized(request, response);
							}
						} else {

							if (openRoute === undefined) {
								console.log("WARN: Route not found for URL:" + request.url);
								console.log("Pass through allowed.");
							}
							// open routes pass through
							next();
						}
					})
					.catch(function (error) {
						console.log(error);
						response.status(500);
						response.send("Error");
					});
			}
		}

Logout

Logout, like login, is handled automatically by the alt-seven framework. Simply call the auth.login event and pass the success and failure arguments, which, like the login arguments, can be functions, events, or router paths.

	a7.events.publish('auth.logout', {
		success: "/auth/logoutsuccess",
		failure: "/auth/logoutfailure"
	});

Note that the auth.logout method sets the Authorization header with the username and password from the event arguments, like the login method, but these are optional arguments. You can include those arguments if you have the username and password cached on the client and you want to use it on the server side.

	a7.events.publish('auth.logout', {
		username: username,
		password: password,
		success: "/auth/logoutsuccess",
		failure: "/auth/logoutfailure"
	});

5.3 - The Console

A short tutorial that shows how to implement the alt-seven browser console.

The alt-seven console shows all of the logging information sent to a7.log in a scrolling window. Normally, this option is enabled and placed in a floating window to give the programmer visibility into what the framework is doing without having to open the browser console. The console also has the option of receiving remote logging messages from a websocket server. This feature is meant to enable the programmer to send log events from the server so the programmer can see them in real time without having to review log events on the server. This feature can be very valuable in situations where it is unclear what is happening on the server otherwise.

Configuration

There is only minimal configuration required for the console. You can specify size and location on the screen, or you can leave the defaults. For more detail, look at the Configuration tutorial.

In the below example, floatingpane is the FloatingPane component of the gadget-ui open source library. For the moment, only gadget-ui components have been tested with the console.

import {floatingpane} from '/node_modules/gadget-ui/dist/gadget-ui.es.js';

	console: {
      enabled: true,
      container: floatingpane
    },

This feature is currently under revision and documentation will be updated when revisions are complete.

5.4 - Implementing Events

A short tutorial that shows how to implement events in alt-seven.

Events are a core concept in writing alt-seven-based applications. While it is not strictly necessary to use events at all, alt-seven is meant to be an event-driven framework,and using the publish/subcribe event system is part of that intention.

Configuration

In order to use events, you need to include them in the application startup so they are available. In LacesIDE, you can see the includes of the event modules in the app.js entry point of the application.

import {events} from '/js/app.events.js';
import {appLibraryEvents} from '/js/event/applibraries.js';
import {appEvents} from '/js/event/apps.js';
import {authEvents} from '/js/event/auth.js';
import {libraryEvents} from '/js/event/libraries.js';
import {mainEvents} from '/js/event/main.js';
import {menuEvents} from '/js/event/menu.js';
import {profileEvents} from '/js/event/profile.js';
import {sandboxEvents} from '/js/event/sandbox.js';
import {userEvents} from '/js/event/user.js';

//initialize pub/sub events
events();
appLibraryEvents();
appEvents();
authEvents();
libraryEvents();
mainEvents();
menuEvents();
profileEvents();
sandboxEvents();
userEvents();

Note that the events are not passed into the a7.init() function, they are simply included here for availability when the application starts.

Modules

Events are typically organized into modules of related events for convenience. Below is a section of the profile.js event module from LacesIDE.

import { a7 } from '/lib/altseven/dist/a7.js';
import { ui } from '/js/app.ui.js';
//import {main} from '/js/app.main.js';
import * as utils from '/js/app.utils.js';

export var profileEvents = function init() {

	a7.events.subscribe("profile.update", function (obj) {
			a7.remote.invoke("profile.update", obj)
				.then(function (response) {
					// get json response and pass to handler to resolve
					return response.json();
				})
				.then(function (json) {
					if (json.success) {
						utils.showNotice("Profile saved.", "#pTab1Notice");
						a7.events.publish("profile.refreshProfile");
					}else{
						utils.showNotice("Profile not saved.", "#pTab1Notice");
					}
				});
		});
};

In this case, the profile.update event calls the remote method of the same name and updates the picture for the user’s profile. You can see in the profile.js view how that call is implemented. Below is a fragment of that code that shows how to publish an event. You call a7.events.publish( eventname, args ) where args is an object that contains the arguments you want to pass to the event subscription.

		updatePic: function (file) {
			let user = a7.model.get("user");
			user.profilePic = file.path + file.filename;
			a7.events.publish("profile.update", user);
		},

While many of the event subscriptions in the LacesIDE application work with remote methods, it is not necessary to call remote methods in the event. You can look at the sandbox.execute subscription in the sandbox.js module for an example of why you might want to use events aside from interacting with remote methods.

The event system contains a set of pre-defined events for the built-in authentication system. You can see more details about these events in the Authentication tutorial.

    a7.events.subscribe("auth.login", function(params) {
      a7.remote.invoke("auth.login", params);
    });
    a7.events.subscribe("auth.logout", function(params) {
      a7.remote.invoke("auth.logout", params);
    });
    a7.events.subscribe("auth.refresh", function(params) {
      a7.remote.invoke("auth.refresh", params);
    });
    a7.events.subscribe("auth.sessionTimeout", function() {
      a7.security.invalidateSession();
    });
    a7.events.subscribe("auth.invalidateSession", function() {
      a7.security.invalidateSession();
    });

5.5 - The Model

A short tutorial that shows how to work with the alt-seven model.

What is the Model?

In alt-seven, the model refers to two separate things. First there is the a7.model component that interacts directly wit alt-seven based applications. Second, there is a model implementation, which could be any model object that exposes the required methods (listed below from a7.model) necessary to meet the contract for what alt-seven expects in a model. When the framework was built, it used its own internal model, a7.Components.Model, as the implementation of the model. It also allowed the use of the model from the open source gadget-ui library, but any model that meets the contract for the methods below should work. Programmers can choose to implement their own model if it serves their needs.

The alt-seven model includes a variety of experimental features that are not currently exposed but may be included in future releases of the framework.

	destroy : function(){
		return _methods[ "destroy" ].apply( _model, arguments );
	},
	get : function(){
		return _methods[ "get" ].apply( _model, arguments );
	},
	set : function(){
		return _methods[ "set" ].apply( _model, arguments );
	},
	exists : function(){
		return _methods[ "exists" ].apply( _model, arguments );
	},

Working with the Model

As it stands today, the four methods listed above are the only methods exposed in a7.model, so implementation is very simple.

	// get the current user from the model
	let user = a7.model.get("user")

	// set the current user into the model
	a7.model.set( "user", user )

	// check if a key exists in the model
	let userExists = a7.model.exists("user")

	// delete the key from the model
	a7.model.delete("user")

5.6 - Remote Resources

A short tutorial that shows how to work with server-side remote resources.

Alt-seven has a remote module that facilitates working with remote resources on your server. First, you need to specify where your remote methods live, in the application config. Below is a section of the application confif in app.js in the LacesIDE application. app.remote is specified as a object containing a key for each remote module.

var app = {
	main: main,
	auth: auth,
	remote: {
	apps: apps,
	applibraries: applibraries,
	libraries: libraries,
	profile: profile,
	user: user
	},
	ui: ui,
	utils: utils
};

The object is then used in the options for the application init, like so, also from LacesIDE:

	remote: {
			modules: app.remote,
			loginURL: '/api/auth/login',
			logoutURL: '/api/auth/logout',
			refreshURL: '/api/auth/refresh',
			useTokens: true, // defaults to true for the auth system
		},

See the Configuration tutorial for details on the options here. Note that useTokens defaults to true, as it enables session management through the built-in alt-seven authentication system. See the Authentication tutorial for more information there.

Remote methods are typically structured as modules for related methods. Again, drawing from LacesIDE, below you can see the profile module:

import { a7 } from '/lib/altseven/dist/a7.js';

export { update };

var update = function (obj) {

	var request;

	var params = {
		method: 'PUT',
		headers: {
			'Accept': 'application/json, application/xml, text/play, text/html, *.*',
			'Content-Type': 'application/json; charset=utf-8'
		},
		body: JSON.stringify({
			profilePic: obj.profilePic
		})
	};

	return a7.remote.fetch("/api/user/" + obj.userID + "/profile", params, true);
};

The remote methods in LacesIDE generally work in conjunction with the events system. In this case, the remote methods themsleves tend to be very small, containing only the information needed to make the remote call, and a fetch call to the remote resource that is returned to the method caller. As you can see here, using the built-in authentication system simplifies the remote calls by hiding everything associated with authentication and session management. It is all handled seamelessly in the background. If you want to learn more about the details of the authentication system, check the Authentication tutoral.

When remote resources are used in conjuncion with the events system, usually the heavy lifting is done in the calling event.

	a7.events.subscribe("profile.update", function (obj) {
			a7.remote.invoke("profile.update", obj)
				.then(function (response) {
					// get json response and pass to handler to resolve
					return response.json();
				})
				.then(function (json) {
					if (json.success) {
						utils.showNotice("Profile saved.", "#pTab1Notice");
						a7.events.publish("profile.refreshProfile");
					}else{
						utils.showNotice("Profile not saved.", "#pTab1Notice");
					}
				});
		});

In this example, the profile.update event calls the remote resource, then handles the response. While it is not necessary to use events like this to call remote resources, this is the standard program flow in alt-seven.

5.7 - The URL Router

A short tutorial that shows how to work with the alt-seven URL router.

The purpose of routes is to give the user a URL in the browser’s location bar.

Routes mapped in an application using events look like this:

export var routes = [
	[ '/auth/loginSuccess', 'auth.loginSuccess' ],
	['/u/:username/profile', 'profile.show'],
	[ '/', 'main.home' ]
];

Routes mapped in an application using functions without events look like this:

export var routes = [
	['/auth/loginSuccess', loginSuccess],
	['/u/:username/profile', showUserProfile],
	['/', home]
];

Using router.open()

// Define a handler function for the /u/:username/profile route
function showUserProfile(params) {
  console.log('Showing user profile for:', params.username);
}

// Navigate to the /u/johndoe/profile route
a7Router.open('/u/johndoe/profile', { username: 'johndoe' });

In this example, we first define a handler function for the /u/:username/profile route. Then, we add this route and parameters to router.open(), which updates the browser’s URL and calls the appropriate handler.

Using router.match()

The router.match() method allows you to handle route changes dynamically without updating the browser’s URL. This is useful for handling hashchange events or external navigation:

// Function to handle route changes
function handleRouteChange() {
  let path = window.location.pathname + window.location.search;
  a7Router.match(path);
}
// Bind the handleRouteChange function to hashchange events
window.addEventListener('hashchange', handleRouteChange);

// Simulate navigation to /u/johndoe/profile using hashchange event
window.location.hash = '/u/johndoe/profile';

In this example, we first define a handler function for the /u/:username/profile route and add it. Then, we create a handleRouteChange() function that uses router.match() to handle changes in the URL path.

By using these methods, you can dynamically navigate within your application and handle route changes programmatically or through external events.

6 - Reference

Low level reference docs for your project.

API Reference and Other Configuration

6.1 - API Reference

The API reference details the methods available in the alt-seven framework.

The API reference details the methods available in the alt-seven framework.

Modules

Component Description
a7 The main module of the alt-seven framework.
a7.components Contains components for various functionalities like models, users, and views.
a7.components.model The built-in model of the alt-seven framework used by default to implement model functionality in applications.
a7.components.User A generic object with no pre-set properties for holding arbitrary user-specific properties.
a7.components.View The base component for all views in alt-seven applications, allowing event bindings and dynamic rendering through template literals or functions.
Method Description
getMemento() Returns the arbitrary properties set by the application.
config() Configuration method (not described).
getState() Returns a structure representing the current state of the view.
setState( args ) Sets the args structure as the current state of the view.
addChild( view ) Adds another view as a child of the current view.
removeChild( view ) Removes a view from the stack of child views of the current view.
clearChildren() Removes all child views of the current view.
render() Renders the current view, returning an HTML string.
getParent() Returns the parent view of the current view.
shouldRender() Determines whether the current view should be rendered based on skipRender status.
onRendered() Automatically called by the framework after rendering child views.
checkRenderStatus() Checks if a document node exists and handles session timers or unregisters the view if not.
a7.console Handles logging messages to the console.
Method Description
addMessage ( message, date, source, level ) Sends a message to the console with specified details.
a7.error Captures runtime errors in the application.
Method Description
captureError (msg, url, lineNo, columnNo, error) Handles capturing and logging of runtime errors.
a7.events Manages event publishing and subscribing within the application.
Method Description
publish ( topic, info ) Calls an event with provided information.
subscribe( topic, listener ) Creates an event listener for specified topics.
a7.log Logs messages at different levels in the application.
Method Description
error( message ) Logs an error-level message.
fatal( message ) Logs a fatal-level message.
info( message ) Logs an info-level message.
trace( message ) Logs a trace-level message.
warn( message ) Logs a warn-level message.
a7.model Manages application data and instances, including views.
Method Description
init( options ) Initializes the model with specified options.
destroy( key ) Deletes an entry from the model using a key.
get( key ) Retrieves an entry from the model by key.
set( key, value ) Sets or updates an entry in the model using a key and value.
exists( key ) Checks if an entry exists in the model with a specific key.
a7.remote Handles remote HTTP calls and WebSocket connections.
Method Description
init( modules ) Initializes the remote module with specified modules.
fetch( uri, params, secure) Makes an HTTP request to a specified URI and returns a promise.
invoke( moduleAction, params) Invokes an action in a specified module with provided parameters.
getToken Retrieves the session token for the current user.
invalidateToken Invalidates the session token for the current user.
getSessionTimer Returns the timer object tracking the user’s session.
webSocket( wsServer, messageHandler, isJSON ) Establishes a WebSocket connection with the specified server and handler function.
auth Methods for handling authentication.
Method Description
login(params) Calls a server-side authentication endpoint.
logout(params) Calls a server-side logout endpoint.
refresh(params) Keeps the current user session alive by calling a specified server-side endpoint.
a7.router Manages routing and navigation within applications.
Method Description
init(options) Initializes the router with specified options.
open(path, params) Opens a specified path and publishes mapped events.
add(path, handler) Maps a new route to a handler function.
find(path) Retrieves a route map by path.
match(path) Checks if a path is mapped in the routes.
a7.security Handles security-related functionalities such as session management and authentication status checks.
Method Description
init() Initializes the security module.
invalidateSession() Ends the current session by clearing the user object.
isAuthenticated() Checks if the current user is authenticated.
a7.ui Manages UI selectors, views, and event bindings.
Property Description
selectors Collection of document selectors set by the application.
views Collection of View objects managed by the application.
Method Description
init() Initializes the UI module.
getEvents() Retrieves all browser events handled by the UI module.
getSelector(name) Returns a specified selector by name.
setSelector(name, selector) Sets a new selector or updates an existing one by name.
getNode(selector) Retrieves a DOM node using a selector.
register(view) Registers a view in the application.
unregister(id) Unregisters a view from the application.
getView(id) Retrieves a view by its ID.
enqueueForRender(id) Adds a view to the render queue for rendering.
removeView(id) Removes a view from the application using its ID.
a7.util Provides utility functions for various tasks.
Method Description
split (val) Splits the input string by commas into an array.
extractLast(term) Retrieves the last term from a comma-separated list.
encode64(input) Encodes a string using Base64 encoding.
decode64(input) Decodes a Base64 encoded string.
leadingZero(n) Prepends a zero to a single-digit number.
dynamicSort( property ) Dynamically sorts an array of objects based on the specified property.
yesNo( val ) Converts a value to “Yes” or “No”.
isValidDate( d ) Checks if the input is a valid date object.
id() Generates a unique ID string.
tryCatch(fn,ctx,args) Attempts to execute a function and catches any errors.
getNumberValue(value) Retrieves a number value from an input or returns null if invalid.
isNumeric(num) Checks if the input is numeric.
getOffset(selector) Retrieves the offset of a DOM element specified by a selector.

7 - Contribution Guidelines

How to contribute to the docs

8 - Examples

See alt-seven in action.

Some examples of alt-seven in action.

LacesIDE

LacesIDE is an open source JavaScript editor and sandbox environment that you can install and run locally or on your own public server. It was built using a slightly older version of alt-seven, but it demonstrates the features of the framework quite well. The whole thing is less than five thousand line of code, small enough to review the whole thing, but big enough to give you a good idea of how you can organize an alt-seven application.