Complete this assignment with the same team you worked with for Type Inference (Written). You and your partner must each understand the answers to all the problems, so don't just split up the work.
Functional Reactive Programming with Flapjax
Drag and drop is an effective technique for various interactions. It is so common on the desktop that it is packaged into standard APIs. In this assignment, you will do the same for web applications. You will build a generic, reusable drag and drop API. You will use your API to implement two actions based on drag and drop—moving and resizing windows.
Using Flapjax
For this assignment, you will write your applications in Flapjax. Flapjax is a JavaScript framework for writing functional reactive applications. The Flapjax API reference lists the functions you have at your disposal. There are variety of demos for you to try as well.
This assignment is an exercise in programming with event streams. You will not need behaviours, nor will you need to communicate with the server.
Since Flapjax is a framework, you will be writing JavaScript code. If you aren't familiar with JavaScript, reading the Flapjax examples should help you get started. If you want more information, search the web. The Mozilla Developer Center (MDC) is a good place to start. As always, don't hesitate to ask the TA's.
Two important details:
Use Firefox 3 with Firebug for this assignment. Flapjax is designed for all major browsers. However, an application can use a vendor-specific feature and break compatibility. This is not a web programming assignment, so you need not target multiple browsers.
You may not use the
sendEvent
andsendBehavior
(orsendBehaviour
) functions in Flapjax. They are intended for building primitive events and for unprincipled hacks in large systems.
DOM Background
We will give you directions and hints on using the DOM throughout this assignment.
Positioning Elements
Usually, the position of an element on a web page is computed
dynamically. This calculation is determined by factors such at the
position of neighboring elements and the size of the window. When
dragging an element, we will often abandon the normal
positioning routine to set the position beneath the mouse. In CSS
terminology, we will use absolute
positions.
The following code positions a black box 80 pixels off the left edge and 50 pixels off the top edge of the window:
<div id="dragTarget0" style="background-color: #000000; color: #ffffff; padding: 10px; font-family: sans-serif; cursor: move; position: absolute; left: 80px; top: 50px"> Drag Me! </div>
Note that the element has the name dragTarget0
. In
JavaScript, you can reference named DOM elements by:
document.getElementById('dragTarget0')Alternatively, you can use Flapjax's shorthand,
$('dragTarget0')
.
This is your template for draggable elements. You're free to modify
it as you wish. Remember to include position: absolute
. If
you have multiple drag-targets on screen, you may wish to assign them
distinct initial positions with left: intial-x; top:
initial-y
.
More Information
For more information on the DOM, CSS, etc. the web is your best resource. In particular, w3schools.com has a comprehensive reference.
Dragging Without Dropping
The mousedown
event fires whenever a mouse button is
pressed over a specified element. In Flapjax, you can retrieve an
event stream of mousedown
events with:
extractEvent_e(element,'mousedown')
Similarly, the mousemove
event fires whenever the mouse
is moved over an element (or the entire document):
extractEvent_e(element,'mousemove')
Use
mousedown
andmousemove
as your primitive event sources to write the function:drag_e :: Element -> EventStream Dragging Dragging = { dragging: true, left: integer, top: integer }
After the mouse button is pressed, the returned event stream should fire aDragging
event for eachmousemove
event. Since we haven't handled themouseup
event, theDragging
events will continue firing after the mouse button is released. We will tackle this later.Include a drag-target on your page, such as the Drag Me! element. Write the coordinates from each
Dragging
event back to the element so that it moves when dragged.Is anything wrong with your implementation of dragging? Does it behave like drag and drop on a desktop operating system? (Does it?)
DOM Hints
To compute the position of an element, use the
cumulativeOffset
function.
The mousemove
event has the
clientX
and
clientY
properties. You may use them to compute the
left
and top
properties of
Dragging
.
To set the position of an element, write to the
style.left
and style.top
properties. For
example:
$('dragTarget0').style.left = 50; $('dragTarget0').style.top = 100;
Basic Drag and Drop
We will now handle mouseup
events so that drop works
correctly.
Extend
drag_e
to fire adrop
event when amouseup
event fires:drag_e :: Element -> EventStream Dragging Dragging = { dragging: true, left: integer, top: integer } | { drop: true, left: integer, top: integer }
You may wish to useone_e
to fire a single event.Try to drag the element multiple times. Draw multiple elements and ensure that they can be dragged independently.
Extend
drag_e
to fire astartDrag
event:drag_e :: Element -> EventStream Dragging Dragging = { startDrag: true, left: integer, top: integer } | { dragging: true, left: integer, top: integer } | { drop: true, left: integer, top: integer }
Window Manager
You will use drag_e
to implement two basic operations
that window managers provide: moving windows and resizing windows. A
window has a title bar which can be dragged to move the window and a
resize handle on the bottom-right corner which can be dragged to resize
the window.
The rest of the window contains an application. For simplicity, all your windows will contain the same application, a stripped-down version of Notepad. We can define the Notepad window as:
<div style="position: absolute; border: 1px solid black"> <div style="background-color: #003333; color: #ffffff; cursor: move; padding: 5px"> <!-- This DIV is the title bar --> Notepad </div> <div style="padding: 10px"> <!-- This DIV is the container --> <textarea style="height: 100%; width: 100%"> It's all about Windows 3.1 </textarea> </div> <div style="position: absolute; cursor: move; background-color: black; width: 10px; height: 10px; bottom: 0px; right: 0px"> <!-- This DIV is the resize handle --> </div> </div>
Create a document that displays a link/button entitled "New Window." When the link is clicked, create and display a new Notepad window.
Don't stack your windows at a single location. You should either cascade or tile your new windows.
Use
drag_e
to allow the user to drag the title bar to move the window. In addition, when thedragStart
event fires, bring the selected window to the front. (Usestyle.zIndex
.)Ensure that dragging works correctly when you have multiple windows open. In addition, ensure that you can still enter text into each window.
Use
drag_e
to allow the user to resize the window. You will need to set thestyle.height
andstyle.width
properties of the container, not the window.Don't let the window get too small. Choose and enforce a reasonable minimum size.
DOM Hints
Flapjax defines functions that allow you to construct HTML elements compositionally. These functions are defined for most HTML elements. You may find them useful. For example, the HTML above may be written using Flapjax's combinators as:
DIV({ style: { position: 'absolute', border: '1px solid black'} }, DIV({ style: { backgroundColor: '#003333', color: '#ffffff', cursor: 'move', padding: '5px' } }, // This is the title bar 'Notepad'), DIV({ style: { padding: '10px' }}, // This is the container TEXTAREA({ style: { height: '100%', width: '100%' } }, "It's all about Windows 3.1")), DIV({ style: { position: 'absolute', cursor: 'move', backgroundColor: 'black', width: '10px', height: '10px', bottom: '0px', right: '0px' } } // This is the resize handle ))
Handin
Turn in all files needed to run your program.