Permalink
Browse files

Easier way to attach event handlers to data structures (#54)

  • Loading branch information...
vkaravir committed Mar 28, 2012
1 parent c8f96c7 commit 5756befa3f50ff8b46c37c3430aa4870f6af9d85
Showing with 134 additions and 44 deletions.
  1. +33 −0 doc/api.html
  2. +6 −1 doc/apidoc.css
  3. +9 −19 doc/exercise.html
  4. +7 −11 examples/simple-exercise.html
  5. +9 −13 examples/sudoku.html
  6. +22 −0 src/datastructures.js
  7. +48 −0 src/tree.js
View
@@ -361,6 +361,23 @@ <h3 class="apimethod">.toggleLine(index, [options])</h3>
<li><code>endIndex</code>: Index in the array where the line will end, inclusive. Defaults to last index of the array.</li>
</ul>
<h3 class="apitopic">Events</h3>
<p>There are functions to attach event handlers for the array elements. The events that can be
listened for are: click, dblclick, mousedown, mousemove, mouseup, mouseenter, and mouseleave.
See <a href="http://api.jquery.com/category/events/">jQuery documentation</a> for details on
the events. Every event handler gets as a first parameter the index of the array element that
the event was triggered for. The second parameter is the jQuery event object. Inside the event
handler function, <code>this</code> will refer to the JSAV array object. </p>
<p><b>Returns:</b> a JSAV array object. Thus, this method can be chained.</p>
<p>Example to toggle arrow on click of an element:</p>
<pre>arr.click(function(index) {
this.toggleArrow(index);
});</pre>
<p>Since many of the JSAV array functions take the index as the first parameter, they can be used
as event handlers. For example, to highlight an index on mouseenter and unhighlight on mouseleave,
you can use:</p>
<pre>arr.mouseenter(arr.highlight).mouseleave(arr.unhighlight);</pre>
<h3 class="apitopic">CSS control</h3>
There are various options and style effects that can be controlled via
@@ -432,6 +449,22 @@ <h3 class="apimethod">.layout()</h3>
<p>This function (re)calculates the layout for the tree. Note, that the library does not do this automatically. That means that after changing the tree, you should call this manually at the end of each animation step. This function exists for all trees.</p>
<h3 class="apitopic">Events</h3>
<p>There are functions to attach event handlers for the nodes and edges in the tree. The events
that can be
listened for are: click, dblclick, mousedown, mousemove, mouseup, mouseenter, and mouseleave.
See <a href="http://api.jquery.com/category/events/">jQuery documentation</a> for details on
the events. Every event handler gets as a parameter the jQuery event object. Inside the event
handler function, <code>this</code> will refer to the JSAV node or edge object. </p>
<p>The function takes another, optional, parameter options that should be an object. It can be
used to specify whether
the event handler is for nodes or edges. By default, it is attached to only nodes.
<p><b>Returns:</b> a JSAV tree object. Thus, this method can be chained.</p>
<p>For example, to highlight an node on mouseenter and unhighlight on mouseleave,
you can use:</p>
<pre>tree.mouseenter(function() { this.highlight(); })
.mouseleave(function() { this.unhighlight(); });</pre>
<h2>Tree Node API</h2>
<p>The following functions exist for both tree nodes and binary tree nodes.</p>
<h3 class="apimethod">.value([newValue])</h3>
View
@@ -17,6 +17,9 @@
content: "NOTE: ";
color: green;
}
h1, h2, h3, h4, h5, h6 {
font-family: Arial, sans-serif;
}
h2 {
border-bottom: 1px dashed #bbb;
}
@@ -28,7 +31,9 @@ h3.apimethod {
font-family: monospace;
}
body {
margin: 20px;
margin: 20px 80px;
font-family: Georgia, Times, Helvetica, serif;
line-height: 1.5;
}
p, ul, ol, dl {
margin-left: 10px;
View
@@ -315,47 +315,37 @@ <h2>Creating the exercise</h2>
<h2>Adding Student Interaction</h2>
<p>
The JSAV library design allows developers to represent all
data structures using HTML elements.
It is simple to attach listeners for user actions (like mouse clicks)
to these elements using standard HTML.
Looking at the initial HTML and the HTML generated to initialize the array (inside the loop in the <code>initialize</code> function),
we see that it is an <code>ol</code> element with several
<code>li</code> elements inside.
And since the exercise was to click the indices, we will
register a click event handler to these <code>li</code> elements.
to JSAV data structures. We will
register a click event handler to the array.
Inside the handler, we need to first decide if we are in "swap mode" or not. This
is done based on whether the last index is highlighted or not.
</p>
<pre>
// we already have reference to the ol element, find its li children
// and bind a function to handle all click events
$array.find("li").live("click", function() {
// find the index of the clicked (variable this) element within its' parent
var index = $array.find("li").index(this);
// bind a function to handle all click events on the array
jsavArray.click(function(index) {
// if last index is highlighted, we are in "swap mode"
if (jsavArray.isHighlight(arraySize - 1)) {
if (this.isHighlight(arraySize - 1)) {
// when in swap mode, first click on index will store that index
// and change the font size on the value
if (swapIndex.value() == -1) {
swapIndex.value(index);
// apply the CSS property change to index
jsavArray.css(index, {"font-size": "130%"});
this.css(index, {"font-size": "130%"});
av.step(); // add a step to the animation
} else {
// the second click (swapIndex has some value) will cause
// the swap of indices index and stored swapIndex
jsavArray.swap(index, swapIndex.value());
this.swap(index, swapIndex.value());
// change the font-size back to normal
jsavArray.css([swapIndex.value(), index], {"font-size": "100%"});
this.css([swapIndex.value(), index], {"font-size": "100%"});
swapIndex.value(-1);
exercise.gradeableStep(); // this step will be graded
}
} else { // we are in highlight mode
// highlight the index
jsavArray.highlight(index);
this.highlight(index);
if (index == (arraySize - 1)) {
av.umsg("Good, now swap the first and last index");
}
@@ -77,33 +77,29 @@
var exercise = av.exercise(modelSolution, initialize, {css: "background-color"});
exercise.reset();
// we already have reference to the ol element, find its li children
// and bind a function to handle all click events
$array.find("li").live("click", function() {
// find the index of the clicked (variable this) element within its' parent
var index = $array.find("li").index(this);
// bind a function to handle all click events on the array
jsavArray.click(function(index) {
// if last index is highlighted, we are in "swap mode"
if (jsavArray.isHighlight(arraySize - 1)) {
if (this.isHighlight(arraySize - 1)) {
// when in swap mode, first click on index will store that index
// and change the font size on the value
if (swapIndex.value() == -1) {
swapIndex.value(index);
// apply the CSS property change to index
jsavArray.css(index, {"font-size": "130%"});
this.css(index, {"font-size": "130%"});
av.step(); // add a step to the animation
} else {
// the second click (swapIndex has some value) will cause
// the swap of indices index and stored swapIndex
jsavArray.swap(index, swapIndex.value());
this.swap(index, swapIndex.value());
// change the font-size back to normal
jsavArray.css([swapIndex.value(), index], {"font-size": "100%"});
this.css([swapIndex.value(), index], {"font-size": "100%"});
swapIndex.value(-1);
exercise.gradeableStep(); // this step will be graded
}
} else { // we are in highlight mode
// highlight the index
jsavArray.highlight(index);
this.highlight(index);
if (index == (arraySize - 1)) {
av.umsg("Good, now swap the first and last index");
}
View
@@ -39,23 +39,19 @@ <h1>JSAV Sudoku Grid</h1>
var c1;
var av = new JSAV("container");
var arrays = [];
// function to handle a click event on an array
var clickHandler = function(index, e) {
this.css(index, {"background-color": "yellow", "transform": "scale(1.2)", "zIndex": 700});
av.step();
};
// initialize nine arrays
for (var i = 0; i < 9; i++) {
arrays[i] = av.ds.array([1, 2, 3, 4, 5, 6, 7, 8, 9]);
// bind the clickHandler to handle click events on the array
arrays[i].click(clickHandler);
}
$(function() {
// register a click event handler to all JSAV array indices
$(".jsavindex").click(function() {
//find the index of the clicked item
var array = $(this).parent(),
pos = array.find(".jsavindex").index(this),
arraypos = array.parent().find(".jsavarray").index(array);
// change styling of the clicked element
arrays[arraypos].css(pos, {"background-color": "yellow", "transform": "scale(1.2)", "zIndex": 700});
av.step();
});
});
av.recorded();
})();
</script>
View
@@ -373,6 +373,28 @@
arrproto.clear = function() {
this.element.remove();
};
// events to register as functions on array
var events = ["click", "dblclick", "mousedown", "mousemove", "mouseup",
"mouseenter", "mouseleave"];
// returns a function for the passed eventType that binds a passed
// function to that eventType for indices in the array
var eventhandler = function(eventType) {
return function(handler) {
var self = this;
this.element.on(eventType, ".jsavindex", function(e) {
var index = self.element.find(".jsavindex").index(this);
// bind this to the array and call handler
// with params array index and the event
handler.call(self, index, e);
});
return this;
}
};
// create the event binding functions and add to array prototype
for (var i = events.length; i--; ) {
arrproto[events[i]] = eventhandler(events[i]);
}
arrproto.toggleArrow = JSAV.anim(function(indices) {
var $elems = getIndices($(this.element).find("li"), indices);
View
@@ -122,6 +122,40 @@
treeproto.state = function(newState) {
// TODO: Should tree.state be implemented??? Probably..
};
// events to register as functions on tree
var events = ["click", "dblclick", "mousedown", "mousemove", "mouseup",
"mouseenter", "mouseleave"];
// returns a function for the passed eventType that binds a passed
// function to that eventType nodes/edges in the tree
var eventhandler = function(eventType) {
return function(handler, options) {
// default options; not enabled for edges by default
var opts = jQuery.extend({node: true, edge: false}, options);
if (opts.node) {
this.element.on(eventType, ".jsavnode", function(e) {
var node = $(this).data("node");
// bind this to the node and call handler
// with the event as parameter
handler.call(node, e);
});
}
if (opts.edge) {
this.jsav.canvas.on(eventType, '.jsavedge[data-container="' + this.id() + '"]', function(e) {
var edge = $(this).data("edge");
// bind this to the edge and call handler
// with the event as parameter
handler.call(edge, e);
});
}
return this;
}
};
// create the event binding functions and add to array prototype
for (var i = events.length; i--; ) {
treeproto[events[i]] = eventhandler(events[i]);
}
var TreeNode = function(container, value, parent, options) {
this.init(container, value, parent, options);
@@ -309,6 +343,8 @@
nodeproto.state = function() {
// TODO: Should this be implemented??? Probably..
};
// TODO: these are so ugly, do something! soon!
nodeproto.highlight = function() {
var testDiv = $('<div class="' + this.container.element[0].className +
'" style="position:absolute;left:-10000px">' +
@@ -319,6 +355,16 @@
this._setcss({color: styleDiv.css("color"), "background-color": styleDiv.css("background-color")});
testDiv.remove();
};
nodeproto.unhighlight = function() {
var testDiv = $('<div class="' + this.container.element[0].className +
'" style="position:absolute;left:-10000px">' +
'<div class="' + this.element[0].className + ' jsavhighlight"></div><div class="' + this.element[0].className + '" ></div></div>'),
styleDiv = testDiv.find(".jsavnode").not(".jsavhighlight");
// TODO: general way to get styles for the whole av system
$("body").append(testDiv);
this._setcss({color: styleDiv.css("color"), "background-color": styleDiv.css("background-color")});
testDiv.remove();
};
nodeproto.isHighlight = function() {
var testDiv = $('<div class="' + this.container.element[0].className +
@@ -364,6 +410,8 @@
this.g.rObj.node.setAttribute("class", "jsavedge");
this.g.rObj.node.setAttribute("data-startnode", this.startnode.id());
this.g.rObj.node.setAttribute("data-endnode", this.endnode.id());
this.g.rObj.node.setAttribute("data-container", this.container.id());
$(this.g.rObj.node).data("edge", this);
if (visible) {
if (this.jsav.currentStep() === 0) { // at beginning, just make it visible
this.g.rObj.attr({"opacity": 1});

0 comments on commit 5756bef

Please sign in to comment.