summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--javascripts/Nfa.js3
-rw-r--r--javascripts/Nfa2Dfa.js4
-rw-r--r--javascripts/NfaSimulator.js3
-rw-r--r--javascripts/NfaState.js3
-rw-r--r--javascripts/RegexParser.js5
-rw-r--r--javascripts/Stack.js3
-rw-r--r--javascripts/globals.js23
-rw-r--r--javascripts/graph.js21
-rw-r--r--javascripts/lib/jquery.bgiframe.js104
-rw-r--r--javascripts/lib/jquery.dimensions.js504
-rw-r--r--javascripts/lib/jquery.tooltip.js294
-rw-r--r--javascripts/ui.js (renamed from javascripts/uifunc.js)67
-rw-r--r--regexvis.html180
-rw-r--r--stylesheets/jquery.tooltip.css13
-rw-r--r--stylesheets/styles.css56
15 files changed, 1180 insertions, 103 deletions
diff --git a/javascripts/Nfa.js b/javascripts/Nfa.js
index a214700..eab1d6c 100644
--- a/javascripts/Nfa.js
+++ b/javascripts/Nfa.js
@@ -2,6 +2,8 @@
* Nfa
* NFA consisting of several NfaStates, following Thompson's algorithm.
*/
+
+
function Nfa(symbol) {
this.startState = null;
this.finalState = null;
@@ -60,3 +62,4 @@ Nfa.prototype.kleene = function() {
return this;
};
+
diff --git a/javascripts/Nfa2Dfa.js b/javascripts/Nfa2Dfa.js
index f38b2cf..a29683e 100644
--- a/javascripts/Nfa2Dfa.js
+++ b/javascripts/Nfa2Dfa.js
@@ -3,6 +3,7 @@
* Convert a NFA to a DFA utilizing epsilon closure.
*/
+
// Compare two NfaStates for equality.
function StateCmp(a, b) {
if (a.id == b.id) {
@@ -11,7 +12,7 @@ function StateCmp(a, b) {
return false
};
-// Compare two stacks filled with NfaStates for euqality.
+// Compare two stacks filled with NfaStates for equality.
function NfaStackCmp(x, y) {
var b = true;
if (x.length != y.length) { return false }
@@ -114,3 +115,4 @@ Nfa2Dfa.prototype.move = function(str, p) {
};
return q;
};
+
diff --git a/javascripts/NfaSimulator.js b/javascripts/NfaSimulator.js
index c5a035f..d8ad9a2 100644
--- a/javascripts/NfaSimulator.js
+++ b/javascripts/NfaSimulator.js
@@ -2,6 +2,8 @@
* NfaSimulator
* Simulate a NFA with a word. Check if regular expression produces word.
*/
+
+
function NfaSimulator(nfa) {
this.startState = nfa.getStartState();
this.finalState = nfa.getFinalState();
@@ -64,3 +66,4 @@ NfaSimulator.prototype.move = function(symbol) {
};
};
};
+
diff --git a/javascripts/NfaState.js b/javascripts/NfaState.js
index 4291295..3453466 100644
--- a/javascripts/NfaState.js
+++ b/javascripts/NfaState.js
@@ -2,6 +2,8 @@
* NfaState
* Represents a state of a NFA (following Thompson's algorithm).
*/
+
+
function NfaState(symbol) {
if(!symbol) {
this.symbol = EPSILON;
@@ -20,3 +22,4 @@ NfaState.prototype.setFollowUp = function(index, state) {
if (! ((index == 0) || (index==1)) ) return;
this.followUps[index] = state;
};
+
diff --git a/javascripts/RegexParser.js b/javascripts/RegexParser.js
index 42251af..63b8558 100644
--- a/javascripts/RegexParser.js
+++ b/javascripts/RegexParser.js
@@ -3,6 +3,8 @@
* Parsing a regular expression using recursive descent method.
* parse() returns a NFA (constructed following Thompson's algrotihm).
*/
+
+
function RegexParser() {};
// Accessor functions
@@ -123,4 +125,5 @@ RegexParser.prototype.parse = function(regex) {
nfa = null;
};
return nfa;
-}
+};
+
diff --git a/javascripts/Stack.js b/javascripts/Stack.js
index 4ccd74e..3349ffb 100644
--- a/javascripts/Stack.js
+++ b/javascripts/Stack.js
@@ -2,6 +2,8 @@
* Stack
* Simple implementation of a stack using Array().
*/
+
+
function Stack() {
this.a = new Array();
this.length = 0;
@@ -53,3 +55,4 @@ Stack.prototype.str = function(separator) {
};
return a.join(separator);
};
+
diff --git a/javascripts/globals.js b/javascripts/globals.js
index e48018d..3a5a495 100644
--- a/javascripts/globals.js
+++ b/javascripts/globals.js
@@ -1,13 +1,16 @@
// globals
-var EPSILON = '~';
-var NEXTSTATE = 0;
-var EMPTYSYMBOL = '%';
-var ALPHABET = 'abcd'+EMPTYSYMBOL;
-var ALPHABETS = ALPHABET+'()|*';
-var REDELIMITER = '$';
+var EPSILON = '~'; // internal use, symbol for 'non-symbol'
+var NEXTSTATE = 0; // internal use, state indices, begin at 0
+var STOPSYMBOL = '%'; // internal use, stop symbol
+var ALPHABET = 'abcd'+STOPSYMBOL; // used alphabet, need to include stop symbol
+var SPECIALS = '()|*'; // symbol with special meaning in a regex
+var ALPHABETS = ALPHABET+SPECIALS; // include special symbols
+var REDELIMITER = '$'; // internal use, delimiter for regex
-var ttable = new Object();
-var g;
-var graphit = true;
-var lock = false;
+var ttable = new Object(); // transition table, internal use
+var g; // graph object (used by Raphael)
+var graphit = true; // draw a graph? value set by checkbox
+var lock = false; // lock for graph animation
+var alphabetEdit = false; // are we currently editing the alphabet?
+var alphabetEditable = true; // is the alphabet still editable?
diff --git a/javascripts/graph.js b/javascripts/graph.js
index 70b67d8..17dcaaf 100644
--- a/javascripts/graph.js
+++ b/javascripts/graph.js
@@ -1,4 +1,4 @@
-// Source: http://raphaeljs.com/graffle.html (extended with labels and arrow heads)
+// source: http://raphaeljs.com/graffle.html (extended with labels and arrow heads)
Raphael.fn.connection = function (obj1, obj2, line, bg, strokeColor, symbol, labelFontSize, name1, name2) {
if (obj1.line && obj1.from && obj1.to) {
line = obj1;
@@ -79,11 +79,15 @@ Raphael.fn.connection = function (obj1, obj2, line, bg, strokeColor, symbol, lab
return ret;
};
};
-// Source: http://stackoverflow.com/questions/2627436/svg-animation-along-path-with-raphael
+
+
+// source: http://stackoverflow.com/questions/2627436/svg-animation-along-path-with-raphael
Raphael.fn.circlePath = function(x , y, r) {
return "M"+x+","+(y-r)+"A"+r+","+r+",0,1,1,"+(x-0.1)+","+(y-r)+" z";
};
-// The nodes
+
+
+// nodes
Raphael.fn.aNode = function(x, y, r, isFinal, hasSelfConn,
strokeWidth, strokeColor,labelText, labelFontSize, name) {
var res = this.set();
@@ -143,15 +147,9 @@ Raphael.fn.aNode = function(x, y, r, isFinal, hasSelfConn,
name: name
};
};
-// Source: http://www.davidcramer.net/code/63/dir-in-javascript.html
-function dir(object) {
- methods = [];
- for (z in object) if (typeof(z) != 'number') methods.push(z);
- return methods.join(', ');
-};
-// Drawing the graph.
+// drawing a graph
function graph() {
var nodeRadius = 30;
var nodeRadiusHi = nodeRadius + 10;
@@ -180,6 +178,7 @@ function graph() {
up = function () {
this.animate({r:nodeRadius, opacity:nodeOpacity}, 500, ">");
};
+
// nodes
var nodes = [];
var nodeById = [];
@@ -221,6 +220,7 @@ function graph() {
nx = nx + nxOffset;
i++;
};
+
// connections
var connections = [];
var k = l = 0;
@@ -252,3 +252,4 @@ function graph() {
graphNodeByName: graphNodeByName
};
};
+
diff --git a/javascripts/lib/jquery.bgiframe.js b/javascripts/lib/jquery.bgiframe.js
new file mode 100644
index 0000000..5d270f3
--- /dev/null
+++ b/javascripts/lib/jquery.bgiframe.js
@@ -0,0 +1,104 @@
+/* Copyright (c) 2006 Brandon Aaron (http://brandonaaron.net)
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
+ *
+ * $LastChangedDate: 2007-06-20 03:23:36 +0200 (Mi, 20 Jun 2007) $
+ * $Rev: 2110 $
+ *
+ * Version 2.1
+ */
+
+(function($){
+
+/**
+ * The bgiframe is chainable and applies the iframe hack to get
+ * around zIndex issues in IE6. It will only apply itself in IE
+ * and adds a class to the iframe called 'bgiframe'. The iframe
+ * is appeneded as the first child of the matched element(s)
+ * with a tabIndex and zIndex of -1.
+ *
+ * By default the plugin will take borders, sized with pixel units,
+ * into account. If a different unit is used for the border's width,
+ * then you will need to use the top and left settings as explained below.
+ *
+ * NOTICE: This plugin has been reported to cause perfromance problems
+ * when used on elements that change properties (like width, height and
+ * opacity) a lot in IE6. Most of these problems have been caused by
+ * the expressions used to calculate the elements width, height and
+ * borders. Some have reported it is due to the opacity filter. All
+ * these settings can be changed if needed as explained below.
+ *
+ * @example $('div').bgiframe();
+ * @before <div><p>Paragraph</p></div>
+ * @result <div><iframe class="bgiframe".../><p>Paragraph</p></div>
+ *
+ * @param Map settings Optional settings to configure the iframe.
+ * @option String|Number top The iframe must be offset to the top
+ * by the width of the top border. This should be a negative
+ * number representing the border-top-width. If a number is
+ * is used here, pixels will be assumed. Otherwise, be sure
+ * to specify a unit. An expression could also be used.
+ * By default the value is "auto" which will use an expression
+ * to get the border-top-width if it is in pixels.
+ * @option String|Number left The iframe must be offset to the left
+ * by the width of the left border. This should be a negative
+ * number representing the border-left-width. If a number is
+ * is used here, pixels will be assumed. Otherwise, be sure
+ * to specify a unit. An expression could also be used.
+ * By default the value is "auto" which will use an expression
+ * to get the border-left-width if it is in pixels.
+ * @option String|Number width This is the width of the iframe. If
+ * a number is used here, pixels will be assume. Otherwise, be sure
+ * to specify a unit. An experssion could also be used.
+ * By default the value is "auto" which will use an experssion
+ * to get the offsetWidth.
+ * @option String|Number height This is the height of the iframe. If
+ * a number is used here, pixels will be assume. Otherwise, be sure
+ * to specify a unit. An experssion could also be used.
+ * By default the value is "auto" which will use an experssion
+ * to get the offsetHeight.
+ * @option Boolean opacity This is a boolean representing whether or not
+ * to use opacity. If set to true, the opacity of 0 is applied. If
+ * set to false, the opacity filter is not applied. Default: true.
+ * @option String src This setting is provided so that one could change
+ * the src of the iframe to whatever they need.
+ * Default: "javascript:false;"
+ *
+ * @name bgiframe
+ * @type jQuery
+ * @cat Plugins/bgiframe
+ * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
+ */
+$.fn.bgIframe = $.fn.bgiframe = function(s) {
+ // This is only for IE6
+ if ( $.browser.msie && parseInt($.browser.version) <= 6 ) {
+ s = $.extend({
+ top : 'auto', // auto == .currentStyle.borderTopWidth
+ left : 'auto', // auto == .currentStyle.borderLeftWidth
+ width : 'auto', // auto == offsetWidth
+ height : 'auto', // auto == offsetHeight
+ opacity : true,
+ src : 'javascript:false;'
+ }, s || {});
+ var prop = function(n){return n&&n.constructor==Number?n+'px':n;},
+ html = '<iframe class="bgiframe"frameborder="0"tabindex="-1"src="'+s.src+'"'+
+ 'style="display:block;position:absolute;z-index:-1;'+
+ (s.opacity !== false?'filter:Alpha(Opacity=\'0\');':'')+
+ 'top:'+(s.top=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+\'px\')':prop(s.top))+';'+
+ 'left:'+(s.left=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+\'px\')':prop(s.left))+';'+
+ 'width:'+(s.width=='auto'?'expression(this.parentNode.offsetWidth+\'px\')':prop(s.width))+';'+
+ 'height:'+(s.height=='auto'?'expression(this.parentNode.offsetHeight+\'px\')':prop(s.height))+';'+
+ '"/>';
+ return this.each(function() {
+ if ( $('> iframe.bgiframe', this).length == 0 )
+ this.insertBefore( document.createElement(html), this.firstChild );
+ });
+ }
+ return this;
+};
+
+// Add browser.version if it doesn't exist
+if (!$.browser.version)
+ $.browser.version = navigator.userAgent.toLowerCase().match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)[1];
+
+})(jQuery); \ No newline at end of file
diff --git a/javascripts/lib/jquery.dimensions.js b/javascripts/lib/jquery.dimensions.js
new file mode 100644
index 0000000..2934714
--- /dev/null
+++ b/javascripts/lib/jquery.dimensions.js
@@ -0,0 +1,504 @@
+/* Copyright (c) 2007 Paul Bakaus (paul.bakaus@googlemail.com) and Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
+ *
+ * $LastChangedDate: 2007-06-22 04:38:37 +0200 (Fr, 22 Jun 2007) $
+ * $Rev: 2141 $
+ *
+ * Version: 1.0b2
+ */
+
+(function($){
+
+// store a copy of the core height and width methods
+var height = $.fn.height,
+ width = $.fn.width;
+
+$.fn.extend({
+ /**
+ * If used on document, returns the document's height (innerHeight)
+ * If used on window, returns the viewport's (window) height
+ * See core docs on height() to see what happens when used on an element.
+ *
+ * @example $("#testdiv").height()
+ * @result 200
+ *
+ * @example $(document).height()
+ * @result 800
+ *
+ * @example $(window).height()
+ * @result 400
+ *
+ * @name height
+ * @type Object
+ * @cat Plugins/Dimensions
+ */
+ height: function() {
+ if ( this[0] == window )
+ return self.innerHeight ||
+ $.boxModel && document.documentElement.clientHeight ||
+ document.body.clientHeight;
+
+ if ( this[0] == document )
+ return Math.max( document.body.scrollHeight, document.body.offsetHeight );
+
+ return height.apply(this, arguments);
+ },
+
+ /**
+ * If used on document, returns the document's width (innerWidth)
+ * If used on window, returns the viewport's (window) width
+ * See core docs on height() to see what happens when used on an element.
+ *
+ * @example $("#testdiv").width()
+ * @result 200
+ *
+ * @example $(document).width()
+ * @result 800
+ *
+ * @example $(window).width()
+ * @result 400
+ *
+ * @name width
+ * @type Object
+ * @cat Plugins/Dimensions
+ */
+ width: function() {
+ if ( this[0] == window )
+ return self.innerWidth ||
+ $.boxModel && document.documentElement.clientWidth ||
+ document.body.clientWidth;
+
+ if ( this[0] == document )
+ return Math.max( document.body.scrollWidth, document.body.offsetWidth );
+
+ return width.apply(this, arguments);
+ },
+
+ /**
+ * Returns the inner height value (without border) for the first matched element.
+ * If used on document, returns the document's height (innerHeight)
+ * If used on window, returns the viewport's (window) height
+ *
+ * @example $("#testdiv").innerHeight()
+ * @result 800
+ *
+ * @name innerHeight
+ * @type Number
+ * @cat Plugins/Dimensions
+ */
+ innerHeight: function() {
+ return this[0] == window || this[0] == document ?
+ this.height() :
+ this.is(':visible') ?
+ this[0].offsetHeight - num(this, 'borderTopWidth') - num(this, 'borderBottomWidth') :
+ this.height() + num(this, 'paddingTop') + num(this, 'paddingBottom');
+ },
+
+ /**
+ * Returns the inner width value (without border) for the first matched element.
+ * If used on document, returns the document's Width (innerWidth)
+ * If used on window, returns the viewport's (window) width
+ *
+ * @example $("#testdiv").innerWidth()
+ * @result 1000
+ *
+ * @name innerWidth
+ * @type Number
+ * @cat Plugins/Dimensions
+ */
+ innerWidth: function() {
+ return this[0] == window || this[0] == document ?
+ this.width() :
+ this.is(':visible') ?
+ this[0].offsetWidth - num(this, 'borderLeftWidth') - num(this, 'borderRightWidth') :
+ this.width() + num(this, 'paddingLeft') + num(this, 'paddingRight');
+ },
+
+ /**
+ * Returns the outer height value (including border) for the first matched element.
+ * Cannot be used on document or window.
+ *
+ * @example $("#testdiv").outerHeight()
+ * @result 1000
+ *
+ * @name outerHeight
+ * @type Number
+ * @cat Plugins/Dimensions
+ */
+ outerHeight: function() {
+ return this[0] == window || this[0] == document ?
+ this.height() :
+ this.is(':visible') ?
+ this[0].offsetHeight :
+ this.height() + num(this,'borderTopWidth') + num(this, 'borderBottomWidth') + num(this, 'paddingTop') + num(this, 'paddingBottom');
+ },
+
+ /**
+ * Returns the outer width value (including border) for the first matched element.
+ * Cannot be used on document or window.
+ *
+ * @example $("#testdiv").outerHeight()
+ * @result 1000
+ *
+ * @name outerHeight
+ * @type Number
+ * @cat Plugins/Dimensions
+ */
+ outerWidth: function() {
+ return this[0] == window || this[0] == document ?
+ this.width() :
+ this.is(':visible') ?
+ this[0].offsetWidth :
+ this.width() + num(this, 'borderLeftWidth') + num(this, 'borderRightWidth') + num(this, 'paddingLeft') + num(this, 'paddingRight');
+ },
+
+ /**
+ * Returns how many pixels the user has scrolled to the right (scrollLeft).
+ * Works on containers with overflow: auto and window/document.
+ *
+ * @example $("#testdiv").scrollLeft()
+ * @result 100
+ *
+ * @name scrollLeft
+ * @type Number
+ * @cat Plugins/Dimensions
+ */
+ /**
+ * Sets the scrollLeft property and continues the chain.
+ * Works on containers with overflow: auto and window/document.
+ *
+ * @example $("#testdiv").scrollLeft(10).scrollLeft()
+ * @result 10
+ *
+ * @name scrollLeft
+ * @param Number value A positive number representing the desired scrollLeft.
+ * @type jQuery
+ * @cat Plugins/Dimensions
+ */
+ scrollLeft: function(val) {
+ if ( val != undefined )
+ // set the scroll left
+ return this.each(function() {
+ if (this == window || this == document)
+ window.scrollTo( val, $(window).scrollTop() );
+ else
+ this.scrollLeft = val;
+ });
+
+ // return the scroll left offest in pixels
+ if ( this[0] == window || this[0] == document )
+ return self.pageXOffset ||
+ $.boxModel && document.documentElement.scrollLeft ||
+ document.body.scrollLeft;
+
+ return this[0].scrollLeft;
+ },
+
+ /**
+ * Returns how many pixels the user has scrolled to the bottom (scrollTop).
+ * Works on containers with overflow: auto and window/document.
+ *
+ * @example $("#testdiv").scrollTop()
+ * @result 100
+ *
+ * @name scrollTop
+ * @type Number
+ * @cat Plugins/Dimensions
+ */
+ /**
+ * Sets the scrollTop property and continues the chain.
+ * Works on containers with overflow: auto and window/document.
+ *
+ * @example $("#testdiv").scrollTop(10).scrollTop()
+ * @result 10
+ *
+ * @name scrollTop
+ * @param Number value A positive number representing the desired scrollTop.
+ * @type jQuery
+ * @cat Plugins/Dimensions
+ */
+ scrollTop: function(val) {
+ if ( val != undefined )
+ // set the scroll top
+ return this.each(function() {
+ if (this == window || this == document)
+ window.scrollTo( $(window).scrollLeft(), val );
+ else
+ this.scrollTop = val;
+ });
+
+ // return the scroll top offset in pixels
+ if ( this[0] == window || this[0] == document )
+ return self.pageYOffset ||
+ $.boxModel && document.documentElement.scrollTop ||
+ document.body.scrollTop;
+
+ return this[0].scrollTop;
+ },
+
+ /**
+ * Returns the top and left positioned offset in pixels.
+ * The positioned offset is the offset between a positioned
+ * parent and the element itself.
+ *
+ * @example $("#testdiv").position()
+ * @result { top: 100, left: 100 }
+ *
+ * @name position
+ * @param Map options Optional settings to configure the way the offset is calculated.
+ * @option Boolean margin Should the margin of the element be included in the calculations? False by default.
+ * @option Boolean border Should the border of the element be included in the calculations? False by default.
+ * @option Boolean padding Should the padding of the element be included in the calculations? False by default.
+ * @param Object returnObject An object to store the return value in, so as not to break the chain. If passed in the
+ * chain will not be broken and the result will be assigned to this object.
+ * @type Object
+ * @cat Plugins/Dimensions
+ */
+ position: function(options, returnObject) {
+ var elem = this[0], parent = elem.parentNode, op = elem.offsetParent,
+ options = $.extend({ margin: false, border: false, padding: false, scroll: false }, options || {}),
+ x = elem.offsetLeft,
+ y = elem.offsetTop,
+ sl = elem.scrollLeft,
+ st = elem.scrollTop;
+
+ // Mozilla and IE do not add the border
+ if ($.browser.mozilla || $.browser.msie) {
+ // add borders to offset
+ x += num(elem, 'borderLeftWidth');
+ y += num(elem, 'borderTopWidth');
+ }
+
+ if ($.browser.mozilla) {
+ do {
+ // Mozilla does not add the border for a parent that has overflow set to anything but visible
+ if ($.browser.mozilla && parent != elem && $.css(parent, 'overflow') != 'visible') {
+ x += num(parent, 'borderLeftWidth');
+ y += num(parent, 'borderTopWidth');
+ }
+
+ if (parent == op) break; // break if we are already at the offestParent
+ } while ((parent = parent.parentNode) && (parent.tagName.toLowerCase() != 'body' || parent.tagName.toLowerCase() != 'html'));
+ }
+
+ var returnValue = handleOffsetReturn(elem, options, x, y, sl, st);
+
+ if (returnObject) { $.extend(returnObject, returnValue); return this; }
+ else { return returnValue; }
+ },
+
+ /**
+ * Returns the location of the element in pixels from the top left corner of the viewport.
+ *
+ * For accurate readings make sure to use pixel values for margins, borders and padding.
+ *
+ * Known issues:
+ * - Issue: A div positioned relative or static without any content before it and its parent will report an offsetTop of 0 in Safari
+ * Workaround: Place content before the relative div ... and set height and width to 0 and overflow to hidden
+ *
+ * @example $("#testdiv").offset()
+ * @result { top: 100, left: 100, scrollTop: 10, scrollLeft: 10 }
+ *
+ * @example $("#testdiv").offset({ scroll: false })
+ * @result { top: 90, left: 90 }
+ *
+ * @example var offset = {}
+ * $("#testdiv").offset({ scroll: false }, offset)
+ * @result offset = { top: 90, left: 90 }
+ *
+ * @name offset
+ * @param Map options Optional settings to configure the way the offset is calculated.
+ * @option Boolean margin Should the margin of the element be included in the calculations? True by default.
+ * @option Boolean border Should the border of the element be included in the calculations? False by default.
+ * @option Boolean padding Should the padding of the element be included in the calculations? False by default.
+ * @option Boolean scroll Should the scroll offsets of the parent elements be included in the calculations? True by default.
+ * When true it adds the totla scroll offets of all parents to the total offset and also adds two properties
+ * to the returned object, scrollTop and scrollLeft.
+ * @options Boolean lite Will use offsetLite instead of offset when set to true. False by default.
+ * @param Object returnObject An object to store the return value in, so as not to break the chain. If passed in the
+ * chain will not be broken and the result will be assigned to this object.
+ * @type Object
+ * @cat Plugins/Dimensions
+ */
+ offset: function(options, returnObject) {
+ var x = 0, y = 0, sl = 0, st = 0,
+ elem = this[0], parent = this[0], op, parPos, elemPos = $.css(elem, 'position'),
+ mo = $.browser.mozilla, ie = $.browser.msie, sf = $.browser.safari, oa = $.browser.opera,
+ absparent = false, relparent = false,
+ options = $.extend({ margin: true, border: false, padding: false, scroll: true, lite: false }, options || {});
+
+ // Use offsetLite if lite option is true
+ if (options.lite) return this.offsetLite(options, returnObject);
+
+ if (elem.tagName.toLowerCase() == 'body') {
+ // Safari is the only one to get offsetLeft and offsetTop properties of the body "correct"
+ // Except they all mess up when the body is positioned absolute or relative
+ x = elem.offsetLeft;
+ y = elem.offsetTop;
+ // Mozilla ignores margin and subtracts border from body element
+ if (mo) {
+ x += num(elem, 'marginLeft') + (num(elem, 'borderLeftWidth')*2);
+ y += num(elem, 'marginTop') + (num(elem, 'borderTopWidth') *2);
+ } else
+ // Opera ignores margin
+ if (oa) {
+ x += num(elem, 'marginLeft');
+ y += num(elem, 'marginTop');
+ } else
+ // IE does not add the border in Standards Mode
+ if (ie && jQuery.boxModel) {
+ x += num(elem, 'borderLeftWidth');
+ y += num(elem, 'borderTopWidth');
+ }
+ } else {
+ do {
+ parPos = $.css(parent, 'position');
+
+ x += parent.offsetLeft;
+ y += parent.offsetTop;
+
+ // Mozilla and IE do not add the border
+ if (mo || ie) {
+ // add borders to offset
+ x += num(parent, 'borderLeftWidth');
+ y += num(parent, 'borderTopWidth');
+
+ // Mozilla does not include the border on body if an element isn't positioned absolute and is without an absolute parent
+ if (mo && parPos == 'absolute') absparent = true;
+ // IE does not include the border on the body if an element is position static and without an absolute or relative parent
+ if (ie && parPos == 'relative') relparent = true;
+ }
+
+ op = parent.offsetParent;
+ if (options.scroll || mo) {
+ do {
+ if (options.scroll) {
+ // get scroll offsets
+ sl += parent.scrollLeft;
+ st += parent.scrollTop;
+ }
+
+ // Mozilla does not add the border for a parent that has overflow set to anything but visible
+ if (mo && parent != elem && $.css(parent, 'overflow') != 'visible') {
+ x += num(parent, 'borderLeftWidth');
+ y += num(parent, 'borderTopWidth');
+ }
+
+ parent = parent.parentNode;
+ } while (parent != op);
+ }
+ parent = op;
+
+ if (parent.tagName.toLowerCase() == 'body' || parent.tagName.toLowerCase() == 'html') {
+ // Safari and IE Standards Mode doesn't add the body margin for elments positioned with static or relative
+ if ((sf || (ie && $.boxModel)) && elemPos != 'absolute' && elemPos != 'fixed') {
+ x += num(parent, 'marginLeft');
+ y += num(parent, 'marginTop');
+ }
+ // Mozilla does not include the border on body if an element isn't positioned absolute and is without an absolute parent
+ // IE does not include the border on the body if an element is positioned static and without an absolute or relative parent
+ if ( (mo && !absparent && elemPos != 'fixed') ||
+ (ie && elemPos == 'static' && !relparent) ) {
+ x += num(parent, 'borderLeftWidth');
+ y += num(parent, 'borderTopWidth');
+ }
+ break; // Exit the loop
+ }
+ } while (parent);
+ }
+
+ var returnValue = handleOffsetReturn(elem, options, x, y, sl, st);
+
+ if (returnObject) { $.extend(returnObject, returnValue); return this; }
+ else { return returnValue; }
+ },
+
+ /**
+ * Returns the location of the element in pixels from the top left corner of the viewport.
+ * This method is much faster than offset but not as accurate. This method can be invoked
+ * by setting the lite option to true in the offset method.
+ *
+ * @name offsetLite
+ * @param Map options Optional settings to configure the way the offset is calculated.
+ * @option Boolean margin Should the margin of the element be included in the calculations? True by default.
+ * @option Boolean border Should the border of the element be included in the calculations? False by default.
+ * @option Boolean padding Should the padding of the element be included in the calculations? False by default.
+ * @option Boolean scroll Should the scroll offsets of the parent elements be included in the calculations? True by default.
+ * When true it adds the totla scroll offets of all parents to the total offset and also adds two properties
+ * to the returned object, scrollTop and scrollLeft.
+ * @param Object returnObject An object to store the return value in, so as not to break the chain. If passed in the
+ * chain will not be broken and the result will be assigned to this object.
+ * @type Object
+ * @cat Plugins/Dimensions
+ */
+ offsetLite: function(options, returnObject) {
+ var x = 0, y = 0, sl = 0, st = 0, parent = this[0], op,
+ options = $.extend({ margin: true, border: false, padding: false, scroll: true }, options || {});
+
+ do {
+ x += parent.offsetLeft;
+ y += parent.offsetTop;
+
+ op = parent.offsetParent;
+ if (options.scroll) {
+ // get scroll offsets
+ do {
+ sl += parent.scrollLeft;
+ st += parent.scrollTop;
+ parent = parent.parentNode;
+ } while(parent != op);
+ }
+ parent = op;
+ } while (parent && parent.tagName.toLowerCase() != 'body' && parent.tagName.toLowerCase() != 'html');
+
+ var returnValue = handleOffsetReturn(this[0], options, x, y, sl, st);
+
+ if (returnObject) { $.extend(returnObject, returnValue); return this; }
+ else { return returnValue; }
+ }
+});
+
+/**
+ * Handles converting a CSS Style into an Integer.
+ * @private
+ */
+var num = function(el, prop) {
+ return parseInt($.css(el.jquery?el[0]:el,prop))||0;
+};
+
+/**
+ * Handles the return value of the offset and offsetLite methods.
+ * @private
+ */
+var handleOffsetReturn = function(elem, options, x, y, sl, st) {
+ if ( !options.margin ) {
+ x -= num(elem, 'marginLeft');
+ y -= num(elem, 'marginTop');
+ }
+
+ // Safari and Opera do not add the border for the element
+ if ( options.border && ($.browser.safari || $.browser.opera) ) {
+ x += num(elem, 'borderLeftWidth');
+ y += num(elem, 'borderTopWidth');
+ } else if ( !options.border && !($.browser.safari || $.browser.opera) ) {
+ x -= num(elem, 'borderLeftWidth');
+ y -= num(elem, 'borderTopWidth');
+ }
+
+ if ( options.padding ) {
+ x += num(elem, 'paddingLeft');
+ y += num(elem, 'paddingTop');
+ }
+
+ // do not include scroll offset on the element
+ if ( options.scroll ) {
+ sl -= elem.scrollLeft;
+ st -= elem.scrollTop;
+ }
+
+ return options.scroll ? { top: y - st, left: x - sl, scrollTop: st, scrollLeft: sl }
+ : { top: y, left: x };
+};
+
+})(jQuery); \ No newline at end of file
diff --git a/javascripts/lib/jquery.tooltip.js b/javascripts/lib/jquery.tooltip.js
new file mode 100644
index 0000000..63b715f
--- /dev/null
+++ b/javascripts/lib/jquery.tooltip.js
@@ -0,0 +1,294 @@
+/*
+ * jQuery Tooltip plugin 1.3
+ *
+ * http://bassistance.de/jquery-plugins/jquery-plugin-tooltip/
+ * http://docs.jquery.com/Plugins/Tooltip
+ *
+ * Copyright (c) 2006 - 2008 Jörn Zaefferer
+ *
+ * $Id: jquery.tooltip.js 5741 2008-06-21 15:22:16Z joern.zaefferer $
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ */
+
+;(function($) {
+
+ // the tooltip element
+ var helper = {},
+ // the current tooltipped element
+ current,
+ // the title of the current element, used for restoring
+ title,
+ // timeout id for delayed tooltips
+ tID,
+ // IE 5.5 or 6
+ IE = $.browser.msie && /MSIE\s(5\.5|6\.)/.test(navigator.userAgent),
+ // flag for mouse tracking
+ track = false;
+
+ $.tooltip = {
+ blocked: false,
+ defaults: {
+ delay: 200,
+ fade: false,
+ showURL: true,
+ extraClass: "",
+ top: 15,
+ left: 15,
+ id: "tooltip"
+ },
+ block: function() {
+ $.tooltip.blocked = !$.tooltip.blocked;
+ }
+ };
+
+ $.fn.extend({
+ tooltip: function(settings) {
+ settings = $.extend({}, $.tooltip.defaults, settings);
+ createHelper(settings);
+ return this.each(function() {
+ $.data(this, "tooltip", settings);
+ this.tOpacity = helper.parent.css("opacity");
+ // copy tooltip into its own expando and remove the title
+ this.tooltipText = this.title;
+ $(this).removeAttr("title");
+ // also remove alt attribute to prevent default tooltip in IE
+ this.alt = "";
+ })
+ .mouseover(save)
+ .mouseout(hide)
+ .click(hide);
+ },
+ fixPNG: IE ? function() {
+ return this.each(function () {
+ var image = $(this).css('backgroundImage');
+ if (image.match(/^url\(["']?(.*\.png)["']?\)$/i)) {
+ image = RegExp.$1;
+ $(this).css({
+ 'backgroundImage': 'none',
+ 'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')"
+ }).each(function () {
+ var position = $(this).css('position');
+ if (position != 'absolute' && position != 'relative')
+ $(this).css('position', 'relative');
+ });
+ }
+ });
+ } : function() { return this; },
+ unfixPNG: IE ? function() {
+ return this.each(function () {
+ $(this).css({'filter': '', backgroundImage: ''});
+ });
+ } : function() { return this; },
+ hideWhenEmpty: function() {
+ return this.each(function() {
+ $(this)[ $(this).html() ? "show" : "hide" ]();
+ });
+ },
+ url: function() {
+ return this.attr('href') || this.attr('src');
+ }
+ });
+
+ function createHelper(settings) {
+ // there can be only one tooltip helper
+ if( helper.parent )
+ return;
+ // create the helper, h3 for title, div for url
+ helper.parent = $('<div id="' + settings.id + '"><h3></h3><div class="body"></div><div class="url"></div></div>')
+ // add to document
+ .appendTo(document.body)
+ // hide it at first
+ .hide();
+
+ // apply bgiframe if available
+ if ( $.fn.bgiframe )
+ helper.parent.bgiframe();
+
+ // save references to title and url elements
+ helper.title = $('h3', helper.parent);
+ helper.body = $('div.body', helper.parent);
+ helper.url = $('div.url', helper.parent);
+ }
+
+ function settings(element) {
+ return $.data(element, "tooltip");
+ }
+
+ // main event handler to start showing tooltips
+ function handle(event) {
+ // show helper, either with timeout or on instant
+ if( settings(this).delay )
+ tID = setTimeout(show, settings(this).delay);
+ else
+ show();
+
+ // if selected, update the helper position when the mouse moves
+ track = !!settings(this).track;
+ $(document.body).bind('mousemove', update);
+
+ // update at least once
+ update(event);
+ }
+
+ // save elements title before the tooltip is displayed
+ function save() {
+ // if this is the current source, or it has no title (occurs with click event), stop
+ if ( $.tooltip.blocked || this == current || (!this.tooltipText && !settings(this).bodyHandler) )
+ return;
+
+ // save current
+ current = this;
+ title = this.tooltipText;
+
+ if ( settings(this).bodyHandler ) {
+ helper.title.hide();
+ var bodyContent = settings(this).bodyHandler.call(this);
+ if (bodyContent.nodeType || bodyContent.jquery) {
+ helper.body.empty().append(bodyContent)
+ } else {
+ helper.body.html( bodyContent );
+ }
+ helper.body.show();
+ } else if ( settings(this).showBody ) {
+ var parts = title.split(settings(this).showBody);
+ helper.title.html(parts.shift()).show();
+ helper.body.empty();
+ for(var i = 0, part; (part = parts[i]); i++) {
+ if(i > 0)
+ helper.body.append("<br/>");
+ helper.body.append(part);
+ }
+ helper.body.hideWhenEmpty();
+ } else {
+ helper.title.html(title).show();
+ helper.body.hide();
+ }
+
+ // if element has href or src, add and show it, otherwise hide it
+ if( settings(this).showURL && $(this).url() )
+ helper.url.html( $(this).url().replace('http://', '') ).show();
+ else
+ helper.url.hide();
+
+ // add an optional class for this tip
+ helper.parent.addClass(settings(this).extraClass);
+
+ // fix PNG background for IE
+ if (settings(this).fixPNG )
+ helper.parent.fixPNG();
+
+ handle.apply(this, arguments);
+ }
+
+ // delete timeout and show helper
+ function show() {
+ tID = null;
+ if ((!IE || !$.fn.bgiframe) && settings(current).fade) {
+ if (helper.parent.is(":animated"))
+ helper.parent.stop().show().fadeTo(settings(current).fade, current.tOpacity);
+ else
+ helper.parent.is(':visible') ? helper.parent.fadeTo(settings(current).fade, current.tOpacity) : helper.parent.fadeIn(settings(current).fade);
+ } else {
+ helper.parent.show();
+ }
+ update();
+ }
+
+ /**
+ * callback for mousemove
+ * updates the helper position
+ * removes itself when no current element
+ */
+ function update(event) {
+ if($.tooltip.blocked)
+ return;
+
+ if (event && event.target.tagName == "OPTION") {
+ return;
+ }
+
+ // stop updating when tracking is disabled and the tooltip is visible
+ if ( !track && helper.parent.is(":visible")) {
+ $(document.body).unbind('mousemove', update)
+ }
+
+ // if no current element is available, remove this listener
+ if( current == null ) {
+ $(document.body).unbind('mousemove', update);
+ return;
+ }
+
+ // remove position helper classes
+ helper.parent.removeClass("viewport-right").removeClass("viewport-bottom");
+
+ var left = helper.parent[0].offsetLeft;
+ var top = helper.parent[0].offsetTop;
+ if (event) {
+ // position the helper 15 pixel to bottom right, starting from mouse position
+ left = event.pageX + settings(current).left;
+ top = event.pageY + settings(current).top;
+ var right='auto';
+ if (settings(current).positionLeft) {
+ right = $(window).width() - left;
+ left = 'auto';
+ }
+ helper.parent.css({
+ left: left,
+ right: right,
+ top: top
+ });
+ }
+
+ var v = viewport(),
+ h = helper.parent[0];
+ // check horizontal position
+ if (v.x + v.cx < h.offsetLeft + h.offsetWidth) {
+ left -= h.offsetWidth + 20 + settings(current).left;
+ helper.parent.css({left: left + 'px'}).addClass("viewport-right");
+ }
+ // check vertical position
+ if (v.y + v.cy < h.offsetTop + h.offsetHeight) {
+ top -= h.offsetHeight + 20 + settings(current).top;
+ helper.parent.css({top: top + 'px'}).addClass("viewport-bottom");
+ }
+ }
+
+ function viewport() {
+ return {
+ x: $(window).scrollLeft(),
+ y: $(window).scrollTop(),
+ cx: $(window).width(),
+ cy: $(window).height()
+ };
+ }
+
+ // hide helper and restore added classes and the title
+ function hide(event) {
+ if($.tooltip.blocked)
+ return;
+ // clear timeout if possible
+ if(tID)
+ clearTimeout(tID);
+ // no more current element
+ current = null;
+
+ var tsettings = settings(this);
+ function complete() {
+ helper.parent.removeClass( tsettings.extraClass ).hide().css("opacity", "");
+ }
+ if ((!IE || !$.fn.bgiframe) && tsettings.fade) {
+ if (helper.parent.is(':animated'))
+ helper.parent.stop().fadeTo(tsettings.fade, 0, complete);
+ else
+ helper.parent.stop().fadeOut(tsettings.fade, complete);
+ } else
+ complete();
+
+ if( settings(this).fixPNG )
+ helper.parent.unfixPNG();
+ }
+
+})(jQuery);
diff --git a/javascripts/uifunc.js b/javascripts/ui.js
index f14b6b2..31a3cfa 100644
--- a/javascripts/uifunc.js
+++ b/javascripts/ui.js
@@ -1,3 +1,58 @@
+/*
+ * UI
+ * functions, intialization
+ */
+
+
+// initialization of ui
+$(function() {
+ // #alphabet
+ $('#alphabet').html(ALPHABET.substr(0,ALPHABET.length-1));
+ // #regex
+ $('#regex').change(function() { checkLength(this, "#parseButton"); });
+ $('#regex').keypress(function(event) { return getKey(event, ALPHABETS); });
+ $('#regex').mouseout(function() { checkLength(this, "#parseButton"); });
+ // #parseButton
+ $('#parseButton').click(function() {
+ uiParse();
+ alphabetEditable=false;
+ });
+ // #word
+ $('#word').change(function() { checkLength(this, "#checkButton"); });
+ $('#word').keypress(function(event) { return graphMoveByInput(event, ALPHABET); });
+ $('#word').mouseout(function() { checkLength(this, "#checkButton"); });
+ // #checkButton
+ $('#checkButton').click(function() { uiSimulate(); });
+ // #graphit
+ $('#graphit').change(function() { graphit = this.checked; });
+ // #reloadButton
+ $('#reloadButton').click(function() { window.location.reload(); });
+ // #descButton
+ $('#descButton').click(function() { $('#desc').toggle(); });
+ // in place edit of alphabet
+ $('body').click(function() {
+ if (alphabetEdit) {
+ ALPHABET = $('#alphabetInput').attr('value').replace(/\s/g,'').replace(/%/g, '').replace(/\*/g, '');
+ ALPHABET = ALPHABET.replace(/\(/g, '').replace(/\)/g, '').replace(/\|/g, '')+STOPSYMBOL;
+ ALPHABETS = ALPHABET+SPECIALS;
+ $('#alphabetInput').remove();
+ $('#alphabet').html('<strong class="grayc">'+ALPHABET.substr(0, ALPHABET.length-1)+'</strong>');
+ alphabetEdit = false;
+ }
+ });
+ $('#alphabet').dblclick(function() {
+ if (alphabetEditable) {
+ alphabetEdit = true;
+ $(this).html('<input id="alphabetInput" type="text" value="'+ALPHABET.substr(0,ALPHABET.length-1)+'" />');
+ $('#alphabetInput').click(function(event) {
+ event.stopPropagation();
+ });
+ }
+ });
+ // tooltips
+ $('*.help').tooltip({showBody: " - "});
+});
+
// Enable/disable if input length is > 0.
function checkLength(el, bId) {
if (graphit && (el.id == 'word')) return;
@@ -30,7 +85,9 @@ function graph_success() {
$('#word').removeClass('inprogress');
$('#word').addClass('success');
$('#checkMessage').effect("highlight", {}, 1000);
- window.g.mover.attr({fill: 'green'});//'#cdeb8b'});
+ if(graphit) {
+ window.g.mover.attr({fill: 'green'});//'#cdeb8b'});
+ }
};
function graph_failure() {
$('#checkMessage').html('Word not accepted');
@@ -45,6 +102,11 @@ function graph_failure() {
// Call of RegexParser.parse() from UI.
function uiParse() {
+ var regex = $('#regex').attr('value');
+ if (regex.match(/\*(\(|\))*\*/)) {
+ alert("Supernumerous * symbol(s), please correct regular expression.");
+ return;
+ }
disable('#graphit');
var parser = new RegexParser();
nfa = parser.parse($('#regex').attr('value'));
@@ -106,7 +168,6 @@ function getKey(e, set) {
};
// Moving inside the graph by input symbols.
-// setTimeout ?
function graphMoveByInput(e) {
if (lock) {
if (failedInputs) {
@@ -163,7 +224,9 @@ function graphMoveByInput(e) {
// go back one state
window.gPrevState = gPrevStates.pop();
+ if (!window.gPrevState) return; // none of word left
window.setTimeout(function() {
+ if (!window.gPrevState) return; // none of word left
g.mover.animate({cx:gPrevState.node[1][0].cx.baseVal.value, cy:gPrevState.node[1][0].cy.baseVal.value}, 250);
lock = false;
}, 250);
diff --git a/regexvis.html b/regexvis.html
index c2c765c..22c0a95 100644
--- a/regexvis.html
+++ b/regexvis.html
@@ -1,90 +1,150 @@
<!DOCTYPE html>
<html>
<head>
- <!-- 2010-05-07 -->
- <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
- <meta http-equiv='Content-Language' content='en_EN' />
- <meta name='author' content='Patrick Simianer' />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta http-equiv="Content-Language" content="en_EN" />
+ <meta name="author" content="Patrick Simianer" />
- <title>Visualizing Regular Expressions (Patrick Simianer, 'Endliche Automaten', SS2010)</title>
+ <title>Visualizing Regular Expressions | regexvis</title>
- <link rel='stylesheet' type='text/css' href='stylesheets/styles.css' />
-
- <script type='text/javascript' src='javascripts/lib/raphael.js'></script>
- <script type='text/javascript' src='javascripts/lib/jquery.js'></script>
- <script type='text/javascript' src='javascripts/lib/jquery-ui.js'></script>
-
- <script type='text/javascript' src='javascripts/globals.js'></script>
- <script type='text/javascript' src='javascripts/NfaState.js'></script>
- <script type='text/javascript' src='javascripts/Nfa.js'></script>
- <script type='text/javascript' src='javascripts/RegexParser.js'></script>
- <script type='text/javascript' src='javascripts/Stack.js'></script>
- <script type='text/javascript' src='javascripts/NfaSimulator.js'></script>
- <script type='text/javascript' src='javascripts/Nfa2Dfa.js'></script>
- <script type='text/javascript' src='javascripts/graph.js'></script>
- <script type="text/javascript" src='javascripts/uifunc.js'></script>
+ <link rel="stylesheet" type="text/css" href="stylesheets/styles.css" />
+
+ <!-- http://raphaeljs.com -->
+ <script type="text/javascript" src="javascripts/lib/raphael.js"></script>
+ <!-- http://jquery.com -->
+ <script type="text/javascript" src="javascripts/lib/jquery.js"></script>
+ <!-- http://jqueryui.com -->
+ <script type="text/javascript" src="javascripts/lib/jquery-ui.js"></script>
+ <!-- http://bassistance.de/jquery-plugins/jquery-plugin-tooltip/ -->
+ <script type="text/javascript" src="javascripts/lib/jquery.bgiframe.js"></script>
+ <script type="text/javascript" src="javascripts/lib/jquery.dimensions.js"></script>
+ <script type="text/javascript" src="javascripts/lib/jquery.tooltip.js"></script>
+ <link rel="stylesheet" type="text/css" href="stylesheets/jquery.tooltip.css" />
+
+ <script type="text/javascript" src="javascripts/globals.js"></script>
+ <script type="text/javascript" src="javascripts/NfaState.js"></script>
+ <script type="text/javascript" src="javascripts/Nfa.js"></script>
+ <script type="text/javascript" src="javascripts/RegexParser.js"></script>
+ <script type="text/javascript" src="javascripts/Stack.js"></script>
+ <script type="text/javascript" src="javascripts/NfaSimulator.js"></script>
+ <script type="text/javascript" src="javascripts/Nfa2Dfa.js"></script>
+ <script type="text/javascript" src="javascripts/graph.js"></script>
+ <script type="text/javascript" src="javascripts/ui.js"></script>
</head>
<body>
<div id="wrapper">
-
- <p style="line-height:1.3em;text-align:justify;width:52%">
- <a class="message gray" href="#" onclick="$('#desc').toggle();">
- <strong>Description</strong></a>
- <span id="desc">&nbsp;Enter a regular expression (<span style="font-family:Courier">'*'</span>, <span style="font-family:Courier">'|'</span> and <span style="font-family:Courier">'()'</span> supported). The usable <strong>alphabet</strong> is shown above the input box. Tick <strong>'Make a Graph'</strong> to enable or disable graph generation. The generated graph is <strong>deterministic finite state automata</strong>. Once the (correct) expression is parsed, it is possible to simulate the automata by entering a accepted or non-accepted <strong>word</strong> in the lower input box.<br /></span>
- </p>
-
+
+
+ <!-- meta -->
+ <div id="top">
+ <a id="reloadButton" class="message gray grayc" href='#' />Reload</a>
+ <a id="descButton" class="message gray grayc" href="#">Description</a>
+
+ <span id="desc">This is a little tool to visualize <a href="http://en.wikipedia.org/wiki/Regular_expression">regular expressions</a>
+ using a <a href="http://en.wikipedia.org/wiki/Finite_state_automata">deterministic finite state automaton</a>.
+ It was developed for a seminar by Karin Haenelt --
+ "<a href="http://kontext.fraunhofer.de/haenelt/kurs/FSA/index.html">Endliche Automaten für die Sprachverarbeitung</a>"
+ in summer term 2010. It is written in pure JavaScript (plus some SVG and underlying HTML) using the
+ <a href="http://raphaeljs.com/">Raphaël</a> and <a href="http://jquery.com/">jQuery</a> libraries. The corresponding
+ presentation can be downloaded <a href="">here</a> (in German).<br />
+ The project includes a parser for basic regular expressions, a simulator for testing if a given word is included in the
+ language defined by the regular expression and another relatively fancy simulator for checking includance graphically step
+ by step. These parts are based on several standard algorithms which can be found in the following literature:
+ <ul>
+ <li><strong>Recursive descent parser, Thompson's algorithm:</strong>
+ <em>Algorithmen in Java</em> (Hans Werner Lang);
+ <em>Regular expression search algorithm</em> (K. Thompson);
+ <em>Compilers. Principles, Techniques & Tools.</em> (Aho et al)</li>
+ <li><strong>Epsilon closure:</strong> <em>The treatment of epsilon moves in subset construction.</em> (Gertjan van Noord)</li>
+ </ul>
+ The user interface should be self explanatory and fail safe but you can obtain help for each step by hovering over The
+ corresponding <span style="font-family:Courier">?</span> symbol. If something goes wrong just click <em>Reload</em>.
+ <p class="small r">Click <em>Description</em> again to hide this text.</p>
+ </span>
+ </div>
+ <!-- /meta -->
+
+
+ <!-- form -->
<table>
- <tr style="width:100%">
- <td class="r"><strong class="grayc">Alphabet:</strong></td>
- <td style="width:400px"><strong class="grayc">
- <script type="text/javascript" charset="utf-8">
- document.write(ALPHABET.substr(0,ALPHABET.length-1));
- </script></strong>
- </td>
+ <tr>
+ <td><div class="message gray grayc">-1</div></td>
+ <td style="width:200px" class="r grayc"><strong>Alphabet:</strong></td>
+ <td style="width:500px">
+ <strong class="grayc"><div id="alphabet"></div></strong>
+ </td>
+ <td>
+ <div title="Alphabet: - The alphabet that is usable in the regular expression. The symbol
+ <span style='font-family:Courier'>%</span> and all special characters (see help for <span style='font-family:Courier'>Regular Expression</span>) are forbidden and will be removed, any whitespace will be omitted.
+ Input will be treated case sensitive. Double click to change the usable symbols (click outside the
+ input box to save)." id="helpAlphabet" class="message green greenc help">?</div>
+ </td>
</tr>
+
+ <tr>
+ <td><div class="message gray grayc">&nbsp;0</div></td>
+ <td class="r grayc"><strong>Draw Graph:</strong></td>
+ <td>
+ <input id="graphit" type="checkbox" name="graphit" value="" autocomplete="off" checked="checked" />
+ </td>
+ <td>
+ <div title="Draw Graph: - Tick if you want a graphical simulation (default), untick for simple check."
+ id="helpGraph" class="message green greenc help">?</div>
+ </td>
+ </tr>
+
<tr>
+ <td><div class="message gray grayc">&nbsp;1</div></td>
<td class="r"><strong>Regular Expression:</strong></td>
- <td><input type="text" name="regex" id="regex" value="" autocomplete="off"
- onchange='checkLength(this, "#parseButton");'
- onkeypress="return getKey(event, ALPHABETS);"
- onmouseout='checkLength(this, "#parseButton");' />
- <input type="button" name="parseButton" id="parseButton"
- value="Parse" disabled='disabled' autocomplete="off" onclick="uiParse();" />
+ <td>
+ <input id="regex" type="text" name="regex" value="" autocomplete="off" />
+ <input type="button" name="parseButton" id="parseButton" value="Parse" disabled='disabled' autocomplete="off" />
</td>
+ <td>
+ <div title="Regular Expression: - Enter a regular expression here, the special symbols
+ <span style='font-family:Courier'>*</span>, <span style='font-family:Courier'>|</span> and
+ <span style='font-family:Courier'>()</span> are currently supported. Click on <em>Parse</em> to parse
+ the string. If <em>Draw Graph</em> is ticked the corresponding graph will be drawn below."
+ id="helpRe" class="message green greenc help">?</div>
+ </td>
</tr>
- <tr>
+
+ <tr>
+ <td><div class="message gray grayc">&nbsp;2</div></td>
<td class="r"><strong>Word:</strong></td>
- <td><input type="text" name="word" id="word" value="" disabled="disabled" autocomplete="off"
- onchange='checkLength(this, "#checkButton");'
- onkeypress="return graphMoveByInput(event, ALPHABET);"
- onmouseout='checkLength(this, "#checkButton");' />
- <input type="button" name="checkButton" id="checkButton"
- value="Check" disabled='disabled' autocomplete="off" onclick="uiSimulate();" />
- </td>
- </tr>
- <tr>
- <td class="r"><strong>Make a Graph:</strong></td>
- <td>
- <input type="checkbox" name="graphit" value="" id="graphit" autocomplete="off"
- checked="checked" onchange="graphit = this.checked;" />
- </td>
- </tr>
+ <td>
+ <input id="word" type="text" name="word" value="" disabled="disabled" autocomplete="off" />
+ <input id="checkButton" type="button" name="checkButton" value="Check" disabled='disabled' autocomplete="off" />
+ </td>
+ <td>
+ <div title="Word: - Enter the word to be simulated here. The simulation process will be shown step by step
+ if <em>Draw Graph</em> was ticked before parsing, otherwise click on check to simulate the word.
+ Backspace works as one would expect. <span class='small'>Do not type too fast while graphical simulation,
+ because synchronization is not perfect, you can press backspace several times to overcome occurred problems.</span>"
+ id="helpWord" class="message green greenc help">?</div>
+ </td>
+ </tr>
</table>
-
+ <!-- /form -->
+
+
+ <!-- messages -->
<p>
- <a class='message gray' href='#' onclick="window.location.reload();"/>Reload</a>
<span id="parseMessage" class="message" style="display:none"></span>
<span id="checkMessage" class="message" style="display:none"></span>
</p>
+ <!-- /messages -->
+ <!-- svg drawing area -->
+ <div id="holder"></div>
+ <!-- /svg drawing area -->
- <div id="holder"></div>
-
</div>
</body>
</html>
+
diff --git a/stylesheets/jquery.tooltip.css b/stylesheets/jquery.tooltip.css
new file mode 100644
index 0000000..ad34a27
--- /dev/null
+++ b/stylesheets/jquery.tooltip.css
@@ -0,0 +1,13 @@
+#tooltip {
+ position: absolute;
+ z-index: 3000;
+ border: 1px solid #111;
+ background-color: #eee;
+ padding: 5px;
+ opacity: 0.85;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px
+}
+#tooltip h3, #tooltip div { margin: 0; }
+
diff --git a/stylesheets/styles.css b/stylesheets/styles.css
index de0841a..c728105 100644
--- a/stylesheets/styles.css
+++ b/stylesheets/styles.css
@@ -1,45 +1,63 @@
+/* by tag */
body {
background-color: #fff;
font-family: Helvetica;
- color: #000;
+ color: #000
}
a { color: #000 }
input {
vertical-align: bottom;
- font-size: 1.1em;
+ font-size: 1.1em
}
input[type='button'] {
vertical-align: middle;
margin-bottom: 2px;
- font-size: 1.25em;
+ font-size: 1.25em
}
input[type='text'] { padding: 4px }
-
+/* by id */
div#wrapper {
margin-left: auto;
margin-right: auto;
- width: 1000px;
+ width: 1000px
+}
+div#top {
+ line-height: 1.3em;
+ text-align: justify;
+ width: 82%;
+ margin-bottom: 1em
}
div#holder {
margin-left: auto;
- margin-right: auto;
+ margin-right: auto
}
span#desc {
font-family: Georgia;
- display: none;
+ line-height: 1.5em;
+ display: none
}
-
+/* by class */
.message {
- font-weight: bold;
- -moz-border-radius: 5px;
- padding: 4px;
- text-decoration: none;
-}
-.success { background-color: #cdeb8b }
-.failure { background-color: #b02b2c }
-.inprogress { background-color: #ffff88 }
-.gray { background-color: #ccc }
-.grayc { color: #ccc }
-.r { text-align: right }
+ font-weight: bold;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ padding: 4px;
+ padding-left: 8px;
+ padding-right: 8px;
+ text-decoration: none
+}
+.message:hover { color: #000 }
+.help:hover { cursor: pointer }
+.success { background-color: #cdeb8b }
+.failure { background-color: #b02b2c }
+.inprogress { background-color: #ff8 }
+.gray { background-color: #eee }
+.grayc { color: #ccc }
+.green { background-color: #00ff7f }
+.greenc { color: #008000 }
+.r { text-align: right }
+.small { font-size: 80% }
+