Skip to content

Commit

Permalink
Added documentation and warnings message on inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
Søren Brønsted committed Jan 21, 2020
1 parent 4c43e69 commit 49177d6
Show file tree
Hide file tree
Showing 60 changed files with 1,115 additions and 297 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ doc/coverage
old_webpack.config.js
.eslintrc.json
public/.htaccess
build/
Binary file removed doc/detail state.dia
Binary file not shown.
Binary file removed doc/list state.dia
Binary file not shown.
Binary file added doc/menu/model.dia
Binary file not shown.
Binary file added doc/menu/model.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed doc/model.dia
Binary file not shown.
Binary file added doc/mvc/BaseControllerState.dia
Binary file not shown.
Binary file added doc/mvc/BaseControllerState.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/mvc/DetailControllerState.dia
Binary file not shown.
Binary file added doc/mvc/DetailControllerState.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/mvc/ListControllerState.dia
Binary file not shown.
Binary file added doc/mvc/ListControllerState.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/mvc/model.dia
Binary file not shown.
Binary file added doc/mvc/model.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions jsdoc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"opts": {
"destination": "build/doc",
"recurse": true
},
"plugins": ["plugins/markdown"],
"recurseDepth": 10,
"source": {
"include": ["lib"],
"includePattern": ".+\\.js$",
"excludePattern": "(^|\\/|\\\\)_"
},
"sourceType": "module",
"tags": {
"allowUnknownTags": true,
"dictionaries": ["jsdoc","closure"]
},
"templates": {
"cleverLinks": false,
"monospaceLinks": false,
"default": {
"staticFiles": {
"include": [
"./doc"
]
}
}
}
}
96 changes: 79 additions & 17 deletions lib/src/menu/Menu.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,78 @@

/**
* @file
* @todo example html markup wuth corresponding menu data
*/
const collect = require('collect.js');

/**
* Menu is the data for anchors of type menu. This is organized as at tree, where the root node
* is not part of the menu structure.
* Menu is the data for html anchors of data-type menu. This is organized as at tree.
* A menu has an uniq id, an url and a optional parameter name. When the parameter name is 'uid' the value of the uid is
* appended to the url path. Otherwise it is appended as parameter to the url.
* The menu maintains it state whether it is visible or not and selected or not.
*/
class Menu {
constructor(uid, uri, parameterName) {
/**
* @param {int} uid
* A uniq id
* @param {string} url
* A url for this menu
* @param {string} parameterName
* The name of parameter.
*/
constructor(uid, url, parameterName) {
this.uid = uid;
this._visible = false; // Is visible if any of the nodes in a subtree is selected
this._selected = false; // Selected marks the path of selected nodes to the leaf

this._uri = uri;
this._url = url;
this._children = collect([]);
this._parameterName = parameterName;
this._parameterValue = null;
}

/**
* Tells whether this menu is visible or not.
* @returns {boolean}
*/
get visible() {
return this._visible;
}

/**
* Tells whether this menu is selected or not.
* @returns {boolean}
*/
get selected() {
return this._selected;
}

get uri() {
/**
* Gets the calculated url for this menu.
* @returns {string}
* The calculated url
*/
get url() {
if (this._parameterName !== undefined) {
if (this._parameterValue != null) {
if (this._parameterName === 'uid') {
return this._uri.concat('/', this._parameterValue);
return this._url.concat('/', this._parameterValue);
}
return this._uri.concat('?', this._parameterName, '=', this._parameterValue);
return this._url.concat('?', this._parameterName, '=', this._parameterValue);
}
else {
return '';
}
}
return this._uri;
return this._url;
}

/**
* Find a menu with by uid
* @param {int} uid
* The search uid
* @returns {Menu|null}
* If found it returns the menu otherwise null
*/
getByUid(uid) {
if (typeof uid === 'string') {
uid = parseInt(uid);
Expand All @@ -57,11 +90,16 @@ class Menu {
return result;
}

/**
* Set the value of the parameter name for this menu and its children.
* @param {collect} args
* A set of parameter values.
*/
setParameter(args) {
if (args.has(this._parameterName)) {
if (this._parameterName === 'uid') {
let uri = '/detail/'+args.get('proxy');
if (this._uri === uri) {
let url = '/detail/'+args.get('proxy');
if (this._url === url) {
this._parameterValue = args.get(this._parameterName);
}
}
Expand All @@ -74,36 +112,54 @@ class Menu {
});
}

/**
* Add a child menu to this menu
* @param {Menu} data
* The child menu to be added
* @returns {Menu}
* The given child menu
*/
push(data) {
this._children.push(data);
return data;
}

select(uri) {
/**
* Find a menu in this menu or its children which matches a given url
* @param {string} url
* The given url
*/
select(url) {
// Find the node which is selected
if (this.uri === uri) {
if (this.url === url) {
this._selected = true;
// If this node has children then select the first node also
if (this._children.isNotEmpty()) {
this._children.first()._selected = true;
}
}
else {
this._children.each(child => child.select(uri));
this._children.each(child => child.select(url));
if (this.uid > 0) {
this._selected = this._children.first(elem => elem.selected) != null;
}
}
}

hasMatch(uri) {
/**
* Find a menu in this menu or its children which matches a given url
* @param {string} url
* @returns {boolean}
* True when found otherwise false
*/
hasMatch(url) {
let found = false;
if (this.uri === uri) {
if (this.url === url) {
found = true;
}
else {
this._children.each(child => {
if (child.hasMatch(uri)) {
if (child.hasMatch(url)) {
found = true;
return false;
}
Expand All @@ -112,6 +168,9 @@ class Menu {
return found;
}

/**
* Ensure that the menu and it's intermediate children are visible.
*/
ensureVisible() {
// Children off root ar allways visible
if (this.uid === 0) {
Expand All @@ -130,6 +189,9 @@ class Menu {
this._children.each(child => child.ensureVisible());
}

/**
* Resets this menu and all it's to a known state which not visible and not selected
*/
reset() {
this._visible = false;
this._selected = false;
Expand Down
20 changes: 19 additions & 1 deletion lib/src/menu/MenuCtrl.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@

const Router = require('../mvc/Router.js').Router;
const Observer = require('../mvc/Observer.js').Observer;
const CurrentViewState = require('../mvc/CurrentViewState.js').CurrentViewState;

/**
* The MenuCtrl controls a view and a model MenuProxy. It is always active and it reacts to events from its subjects.
*/
class MenuCtrl extends Observer {

/**
* The constructor which adds it self as listener to the provided subjects.
* @param {Router} router
* The router subject
* @param {MenuProxy} proxy
* The menuproxy subject
* @param {View} view
* The view subject
* @param {CurrentViewState} currentViewState
* The current view state subject
*/
constructor(router, proxy, view, currentViewState) {
super();
this._router = router;
Expand All @@ -17,6 +30,11 @@ class MenuCtrl extends Observer {
this._view.addEventListener(this);
}

/**
* Handle and incoming event
* @param {Event} event
* The incoming event
*/
handleEvent(event) {
if (event.sender === Router.name) {
this._show(event);
Expand Down
40 changes: 27 additions & 13 deletions lib/src/menu/MenuProxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,34 @@ const Menu = require('./Menu.js').Menu;
const Subject = require('../mvc/Subject.js').Subject;
const Event = require('../mvc/Event.js').Event;

/**
* This class is the model for the menu system and contains the current state of the for it.
* It hold the root for the menu hierarchy.
*/
class MenuProxy extends Subject {
constructor() {
super();
this.eventOk = `proxyOk`;
this._root = new Menu(0, '/');
this._populate(this._root);
this.init(this._root);
this.populate();
}

populate() {
this.fire(new Event(this.constructor.name, this.eventOk, this._root));
}

/**
* Set parameter on menu items by parameter names.
* Set a parameter on menu items by parameter names.
* Parameters can come from the query part or from the uri path.
*
* If the last segment is a number it will match parameters of uid and
* if more than 2 segments it will also match second last segment with ${segment}_uid
*
* It is only the first parameter from the query part which is used.
* @param uri
* @param {string} url
* A given url
*/
setParameter(uri) {
let parsedUri = URI.parse(uri);
setParameter(url) {
let parsedUri = URI.parse(url);
let pathSegments = parsedUri.path.split('/');
if (pathSegments.length <= 1) {
return;
Expand Down Expand Up @@ -62,23 +66,33 @@ class MenuProxy extends Subject {

/**
* Select an menu item and ensure it is visible
* @param uri
* @param {string} url
* A given url
*/
select(uri) {
select(url) {
// Don't do anytime if uri does not match
if (!this._root.hasMatch(uri)) {
if (!this._root.hasMatch(url)) {
return;
}
// reset everything
this._root.reset();
// select the node
this._root.select(uri);
this._root.select(url);
// ensure visible
this._root.ensureVisible();
}

_populate(root) {
throw Error("You must override populate");
/**
* Initialize the menu hierarchy. Eg:
* @param {Menu} root
* The root of the menu hierarchy
* @example
* let child = root.push(new Menu(1, '/some/url'));
* let grandChild = child.push(new Menu(5, '/some/url'));
* grandchild.push(new Menu(15, '/some/url'));
*/
init(root) {
throw Error("You must override the init method");
}
}
exports.MenuProxy = MenuProxy;
10 changes: 8 additions & 2 deletions lib/src/menu/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
'use strict';

/**
* The menu package.
*
* <caption>Class diagram</caption>
* <img src="menu/model.png">
* @module menu
* @todo insert class model
*/
module.exports = Object.assign(
require('./Menu.js'),
require('./MenuProxy.js'),
Expand Down
Loading

0 comments on commit 49177d6

Please sign in to comment.