-
Notifications
You must be signed in to change notification settings - Fork 0
Overview
A level is represented by rows of inline <div>
s (cells) inside the #view
node,
it is created through the renderView
function. Each cell has an onclick handler,
which passes the event object to the tool
property of the mouseTool
object.
The mouseTool
holds two properties: tool
and char
. The tool
is an event
handler which first determines whether its associated action can be performed,
and if so, performs the action. The char
is a single character string
that determines how the targeted cell's textContent
and style.backgroundColor
attributes should be altered.
The lowest-level function responsible for editing a cell is applyEdit
, which
takes a cell and a character. Whenever a user edits a cell, the tool will
call applyEdit
and pass the mouseTool
's char
value to it. The cell's new
text content will be set to the char, and its corresponding color will be
looked up in the PALETTE
constant and set as the cell's background color.
The PALETTE
object holds every available character and associates a color
and a short description with it, for example:
'+': {color: 'rgb(255, 100, 100)', help: 'lava'}
Similarly, the TOOLS
array holds all of the available tool functions. It also
stores an icon name which will be assigned to the tool's selection button in the
UI.
Two tools are implemented: a brush
which simply edits a cell through applyEdit
and a fill
tool which is powered by the scanline flood fill algorithm.
A simple array-wrapping HistoryStack
class is used to store information about
edited cells. Each record in the stack
property of HistoryStack
is an object
which holds an array of cells altered by an action and a character value to which
they should be restored: {cells: [cell0, cell1, ...], char: char}
.
The reason for introducing a custom class was the need to be able to allow
interaction between a stack and a UI element through event handling.
Every time a method which alters the stack, such as pop
is called, a custom
historyupdate
event is dispatched, and its handler alters the state of undo
and redo UI buttons accordingly.
To simplify moving through history states there are two instances of HistoryStack
:
past
and future
. Every time a user makes an alteration to the level,
the future
stack is emptied and a history record is pushed to the past
stack.
When undo
or redo
is called, a record is popped from a corresponding stack,
and the cells in it are looped through and restored to the previous state by
applyEdit
using the character in the record. The opposite stack receives a
new record that holds the same list of cells but with the char
value switched.
When the past
stack grows too large, older records are removed by calling
the drop
method.
Storing palette and tool values in objects, allows looping through them to easily
create as many corresponding UI buttons on the fly as needed. Creation of buttons
and dialog boxes is abstracted, while other UI elements are either present in the
initial HTML template, or created dynamically through simple DOM manipulations.
As there are only two mouse tools in this editor, and it's not likely that more
will be implemented, not to clutter the layout, undo and redo buttons are
attached to the same node, though their function is different from that of the
mouse tools.
Undo and redo buttons get enabled and disabled as the document
tracks the historyupdate
events.
Clicking this will append a span.dialog
element to the menu,
containing two <input>
fields and two buttons, dialogYes
and dialogNo
.
The former will attempt to call renderView
with the input as parameters, while
the latter will remove the <span>
from the document.
This will similarly create a <span>
element with the dialog class, but with
a single <textarea>
for user input. Clicking dialogYes
will first determine if
the string can be interpreted as a grid (i.e. consists of lines of equal length),
then call renderView
, with the calculated grid measurements and will loop through
the characters in the string, trying to call applyEdit
with the current character
and the corresponding cell. If a character is not present in PALETTE
, and an error
message will be displayed in the info
<textarea>
at the bottom of the toolbar.
Clicking the copy button will call a copyLevel
function, which will loop through
the cell divs and append their textContent
to an output string, additionally appending
a newline after every row. The following trick is used to hand out the string to
the user.
The info
's textContent
is set to the output string, then selected,
copied to clipboard and finally removed from the info
. Like this:
info.textContent = level;
info.select();
document.execCommand('Copy');
info.textContent = 'level copied to clipboard';
The user can then paste the string into their text editor to use with their program.
A <textarea>
where information about actions and UI elements appears. It was
initially used to display the level as a string, but as seen above it no longer
does that directly. The following css rules are used to hide the cursor, preserve
newlines for copying and prevent resizing:
.info {
resize: none;
white-space: pre-line;
color: transparent; /* the cursor (and the text as well) is now invisible */
text-shadow: 0 0 0 white; /* but a shadow with no offset is drawn for the text*/
}
The icons are Google's material icons, currently displayed as an icon font and are
linked in the <head>
of index.html.