app.js
fileApplication should import, wire up and use the following modules:
./js/graphus.js
)./js/headus.js
)./js/nodus.js
)./js/linkus.js
)./js/dialogus.js
)App should have a set of functions:
init()
isFirstRun()
addCustomListeners()
listenForHeadusEvents()
listenForNodusEvents()
listenForLinkusEvents()
listenForDialogusEvents()
listenForGraphusEvents()
showBody()
changeCurrentNodeBy(...)
showMany()
init()
Function should initialize the application. Have to be called only once on each page load after all the event handlers are registered, for the side effects only. It should run the following in that order:
If the app is running for the first time, data
should be fetched from the example_graph.json
file. Otherwise, it should be loaded from the localStorage
, checked for validity with graphus.isValidGraph(data)
, to make sure the data is of expected shape, and passed to graphus.init(data)
.
Also, if the app is running for the first time, it should call dialogus.open('splash', {version, canClose: true})
.
isFirstRun()
Predicate function that returns true
if the application is running for the first time, false
otherwise. Have to be called only once on each page load after all the event handlers are registered. To determine if the app is running for the first time, should check if the localStorage
has the graph_app_data
entry. If it does, the app is not running for the first time.
addCustomListeners()
Function should add all the event listeners for the custom events by calling the following functions in that order:
listenForHeadusEvents()
listenForNodusEvents()
listenForLinkusEvents()
listenForDialogusEvents()
listenForGraphusEvents()
Have to be called only once on each page load, for side effects only.
listenForHeadusEvents()
Function should add all the event listeners for the headus
module custom events. It has to be called only once on each page load, for side effects only.
Add addnodetrigger
event handler calling the dialogus.open('add node', {?name: event.detail.name, canClose: true})
method. If the name
is not provided or if it is provided but the call to graphus.isNameTaken(name)
returns true
, the name
property on the data argument is to be skipped.
Add querynode
event handler calling the changeCurrentNodeBy({name}, true)
function for the given event.detail.query
.
Add menutrigger
event handler calling the dialogus.open('menu', {canClose: true})
method.
listenForNodusEvents()
Function should add all the event listeners for the nodus
module custom events. It has to be called only once on each page load, for side effects only.
Add gotonodetrigger
event handler calling the changeCurrentNodeBy({id})
function for the given event.detail.id
.
Add editnodetrigger
event handler calling the dialogus.open('edit node', {node, canClose: true})
method. It should get the node information with the graphus.getNodeById(event.detail.id)
method and pass it as node
property in the data argument.
Add deletenodetrigger
event handler getting the node information with the graphus.getNodeById(event.detail.id)
method and then calling the dialogus.open('delete node', {node, canClose: true})
method.
Add addlinktrigger
event handler calling the graphus.getNameById(event.detail.id)
method, and then calling the dialogus.open('add link', {from: name}, canClose: true})
method.
Add nodeselectedtrigger
event handler, that accepts the event.detail.id
and then calls the graphus.getLinksById(id.current, id.selected)
method. Next it should call the linkus.showTwin(links, id.current)
method.
listenForLinkusEvents()
Function should add all the event listeners for the linkus
module custom events. It has to be called only once on each page load, for side effects only.
Add gotolinktrigger
event handler calling the changeCurrentNodeBy({id: id.from, select: {id: id.to}})
function for the given event.detail.id
.
Add editlinktrigger
event handler accepting the event.detail.id
, then getting the node names with the graphus.getNameById(id.from)
and graphus.getNameById(id.to)
methods, and the link description with the graphus.getLinkDescription(id.from, id.to)
method, and then calling the dialogus.open('edit link', {link: {id: {from: id.from, to: id.to}, from: fromName, to: toName}, description, canClose: true})
method.
Add deletelinktrigger
event handler accepting the event.detail.id
, then getting the link information with the graphus.getLinksById(id.from, id.to)
calling the dialogus.open('delete link', {link, canClose: true})
method.
listenForDialogusEvents()
Function should add all the event listeners for the dialogus
module custom events. It has to be called only once on each page load, for side effects only.
Add splashtrigger
event handler calling the dialogus.open('splash', {version, canClose: true})
method.
Add addnodetrigger
event handler, that should check if event.detail.name
is empty, and if is is, should call the dialogus.open('inform', {title: 'Name required', text: 'Node name cannot be empty or empty-like.', canClose: true})
method. Otherwise, it should call the graphus.isNameTaken(event.detail.name)
method, and if it returns true
, should call the dialogus.open('inform', {title: 'Name taken', text: 'There\'s already a node named ' + event.detail.name + '. Try another name.', canClose: true})
method. Otherwise, should call the dialogus.close('add node')
method, and then call the graphus.addNode(event.detail.name, ?event.detail.description)
method. Second argument is optional, it is passed if not empty.
Add savenodetrigger
event handler, that should accept the event.detail.node
object, then it should check if node.name
is empty, and if it is, should call the dialogus.open('inform', {title: 'Name required', text: 'Node name cannot be empty or empty-like.', canClose: true})
method. Otherwise, it should call the graphus.isNameTaken(node.name)
method, and if it returns true
, it should call the graphus.getIdByName(node.name)
method, and if it does not return the node.id
, then call the dialogus.open('inform', {title: 'Name taken', text: 'There\'s already a node named ' + node.name + '. Try another name.', canClose: true})
method. Otherwise, should call the dialogus.close('edit node')
method, and then call the graphus.updateNode(node.id, {name: node.name, description: node.description})
method.
Add deletenodetrigger
event handler calling the graphus.deleteNode(event.detail.id)
method.
Add addlinktrigger
event handler that should accept the event.detail.link
object, then check if both link.from
and link.to
are empty, and if they are call the dialogus.open('inform', {title: 'Node names required', text: 'Links are linking pairs of nodes, so node names have to be specified.', canClose: true})
method. Otherwise, it should check if either link.from
or link.to
is empty, or they are equal, or graphus.getIdByName(link.from)
or graphus.getIdByName(link.to)
returns null
, and if so call the dialogus.open('inform', {title: 'Two exising nodes required', text: 'Links can be created only between two distinct existing nodes'})
method. Otherwise, it should call graphus.doesLinkExist(link.from, link.to)
and if it returns true
, call the dialogus.open('inform', {title: 'Link exists', text: 'There\'s already a link between those two nodes in that direction.', canClose: true})
method. Otherwise, it should call the dialogus.close('add link')
method, and then call the graphus.addLink(fromId, toId, link.description)
method.
Add savelinktrigger
event handler accepting the event.detail.link
object, then check if both link.from
and link.to
are empty, and if they are call the dialogus.open('inform', {title: 'Node names required', text: 'Links are linking pairs of nodes, so node names have to be specified.', canClose: true})
method. Otherwise, it should check if either link.from
or link.to
is empty, or they are equal, or graphus.getIdByName(link.from)
or graphus.getIdByName(link.to)
returns null
, and if so call the dialogus.open('inform', {title: 'Two exising nodes required', text: 'Links can be created only between two distinct existing nodes'})
method. Otherwise, it should call graphus.getIdByName(link.from)
and graphus.getIdByName(link.to)
, and if not both are equal to link.id.from
and link.id.to
respectively, call the graphus.doesLinkExist(fromId, toId)
and if it returns true
, call the dialogus.open('inform', {title: 'Link exists', text: 'There\'s already a link between those two nodes in that direction.', canClose: true})
method. Otherwise, it should call the dialogus.close('edit link')
method, and then call the graphus.updateLink(link.id.from, link.id.to, {?from: fromId, ?to: toId, description: link.description})
method with from
and to
properties present only if they are not equal to link.id.from
and link.id.to
respectively.
Add deletelinktrigger
event handler accepting the event.detail.id
, then calling the graphus.deleteLink(id.from, id.to)
method.
listenForGraphusEvents()
Function should add all the event listeners for the graphus
module custom events. It has to be called only once on each page load, for side effects only.
Add graphloaded
event handler calling the following functions in that order:
Add graphupdated
event handler that acts based on the event.detail.change
object.
If the change.type
is node
and change.action
is add
, call the headus.enlistNode(name)
method. Also call the nodus.showOne(node)
and linkus.showTwin(links, id)
methods for the new node, using data from the change
object and passing an empty array as links
argument.
If the change.type
is node
and change.action
is update
, check if change.name.old
equals change.name.new
. If it does not, call the headus.unlistNode(change.name.old)
and headus.enlistNode(change.name.new)
methods. Also call the nodus.getCurrentId()
method and if it returns change.id
, call the nodus.updateOne({name: change.name.new, description: change.description})
method.
If the change.type
is node
and change.action
is delete
, call the headus.unlistNode(change.id)
method. Also if the headus.getQuery()
method returns change.name
, call the headus.clearQuery()
method. Also if the nodus.getCurrentId()
method returns change.id
, call the showMany()
function. Also if the nodus.getListedNodes()
method returns an array of ids that includes change.id
, call the nodus.removeNode(change.id)
method. Also if the linkus.geListedLinks()
method returns an array of links that includes any of the id pairs of either change.id
or change.links
, call the linkus.removeLink(from, to)
method for each such link id pair.
If the change.type
is link
and change.action
is add
, call the nodus.getCurrentId()
method and if it returns the change.id.from
, call the changeCurrentNodeBy({id: change.id.from, select: {id: change.id.to})
function. Otherwise, if it returns the change.id.to
, call the changeCurrentNodeBy({id: change.id.to, select: {id: change.id.from})
function.
If the change.type
is link
and change.action
is update
, check if change.id.update
is provided, and if it is, call the changeCurrentNodeBy({id: change.id.update.from, select: {id: change.id.update.to})
function. Otherwise, call the linkus.getListedLinks()
method and if it returns an array of links that includes the change.id
, call the linkus.updateLink(change.id.from, change.id.to, change.description)
method.
If the change.type
is link
and change.action
is delete
, call the nodus.getCurrentId()
method, and if it returns the change.id.from
, call the changeCurrentNodeBy({id: change.id.from})
function. Otherwise, if it returns the change.id.to
, call the changeCurrentNodeBy({id: change.id.to})
function.
showBody()
Function should remove the hidden
attribute from the <body>
element. It is to be used for side effects only.
changeCurrentNodeBy({id|name, ?select: {id|name}}, ?silent)
Function should try to change the current node to the node with the given id
or name
. It should be called for side effects only.
If id
is given or acquired from the graphus.getIdByName(name)
method, should call the graphus.getNodeById(id)
method. If the node is not found and silent
is not true
, should call the dialogus.open('inform', {message: 'Node not found', canClose: true})
method. If node is found, should call the graphus.getLinksById(id)
method for the same node id. Next nodus.showOne(node)
and linkus.showTwin(links, id)
methods should be called for the node and links respectively.
If second argument is a boolean it is to be assigned to silent
, and select
should become undefined
.
If select
is given, function should call the graphus.getLinksById(id, select.id)
method. Also should pass select.id
to nodus.showOne(node, select.id)
.
If name
is given instead of id
, function should call the graphus.getIdByName(name)
method first. Same for select
, if select.name
is given instead of select.id
.
showMany()
Function should call the following functions in that order: