Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // JumNav.js
- // Version 0.3
- //
- // Autor: Bernd Pol <[email protected]>
- // License: GPL
- //
- // Navigation script for use with jumanji
- // (jumanji-git >= 20100809)
- //
- // Note: Needs some patches to jumanji.c from umanji-git 20100809,
- // see: http://pastebin.com/6qPGTk6P
- /*****************
- *** Changelog ***
- *****************/
- /*
- * Version 0.3
- * - Bug fixed: Handles most links properly now, esp. javascript calls
- * on the page
- * - Navigation mode implemented: allows to navigate back and forth the
- * clickable elements hierarchy on the page instead of
- * following hinting labels.
- * Also allows for some interaction (display mode changes).
- * Defaults:
- * # toggle navigation/hinting mode
- * n go to next node in the hierarchy
- * p go to previous node in the hierarchy
- * s select ("click") the current node
- * + toggle display colors
- * - toggle a "raw" position mode in order to properly
- * align the hinting labels/node rectangles on some pages
- *
- * Version 0.2
- * - If an action was performed by this script on a selection,
- * i.e. a mouse click simulation, jumanji could not be informed
- * about this and would thus not get out of the follow mode:
- * --> JumNav now returns a special notification to Jumanji
- * Note: This requires Jumanji being able to process this value upon
- * return from the update_hints() call.
- * Otherwise the "clickedNotification" configuration variable
- * must be set to an empty string to not have Jumanji this value
- * interpreted as an URL.
- *
- * - Bug fixed: hints with 1 label only were not processed.
- */
- /*********************
- *** Configuration ***
- *********************/
- // -----------------------------------------------------------------------------
- // start of user configuration section
- // -----------------------------------------------------------------------------
- /*
- * ====================
- * Collateral Sequences
- * ====================
- *
- * There are several label number representations possible. Just uncomment the
- * one you want.
- *
- * Note that the first symbol in sequence will be treated as zero equivalent
- * and the labels will be get those zero equivalents prepended if necessary,
- * e.g. the number 1 in a three-digit "alpha" sequence will show as "aab".
- */
- var collSequence = "optimal"; // automatic: find shortest to type sequence
- // var collSequence = "numeric"; // decimal numbers
- // var collSequence = "alpha"; // lower case letter sequences
- // var collSequence = "longalpha"; // lower followed by upper case letters
- /*
- * Alternatively this can be any unique sequence of alphanumeric symbols, e.g.:
- */
- // var collSequence = "asdfghjkl"; // home row keys (for touch typers)
- // var collSequence = "uiophjklnm"; // right hand only
- /*
- * ==================
- * Node label display
- * ==================
- */
- /*
- * If true show matching labels and selectable digits only.
- * If false keep the labels intact and mark the selectable group in an
- * alternative color (see below: partialLabelColor[displayMode], partialLabelBackground[displayMode]).
- */
- var shortenLabels = true;
- // var shortenLabels = false;
- /*
- * This defines the font size shown in the labels. It may be an absolute number
- * with a trailing "px" giving the font height in pixels, a number with trailing
- * "%" giving the height relative to the parents font size, or one of the
- * predefined font size property values (ranging from smallest to largest):
- * "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large"
- * or defining a relative value to the parent font size:
- * "smaller", "larger"
- * ( see also: http://www.w3schools.com/jsref/prop_style_fontsize.asp )
- */
- var nodeLabelSize = "12px";
- // var nodeLabelSize = "85%";
- // var nodeLabelSize = "small";
- /*
- * How to display the hinting labels
- * ---------------------------------
- * There are two sets of colors so you can switch displays if they become
- * unreadable on a certain side.
- * Use color names or "#rrggbb" hexadecimal values for red, green, and blue
- * channels respectively.
- */
- var nodeLabelBorder = ["black", "red"];
- var nodeLabelBorderWidth = [1, 1];
- var nodeLabelColor = ["red", "black"];
- //var nodeLabelBackground = ""; // empty string for no extra color
- var nodeLabelBackground = ["lightyellow","white"];
- /*
- * How to display the partial selection group if shortenLabels was set to false.
- */
- var partialLabelColor = ["blue", "yellow"];
- var partialLabelBackground = ["lightgreen", "lightblue"];
- /*
- * How to display the finally selectable label
- * (in case the user needs to confirm the selection).
- */
- var foundLabelColor = ["yellow", "red"];
- var foundLabelBackground = ["red", "yellow"];
- /*
- * Define the transparence of the labels.
- */
- var nodeOpacity = 0.6;
- /*
- * ============================
- * Navigatable Elements Display
- * ============================
- */
- /*
- * How to display the clickable nodes in navigation mode
- * -----------------------------------------------------
- * Use color names or "#rrggbb" hexadecimal values for red, green, and blue
- * channels respectively.
- */
- var navElementBorderWidth = [1, 1];
- var navElementBorder = ["blue", "yellow"];
- //var navElementBackground = ""; // empty string for no extra color
- var navElementBackground = ["lightyellow", "lightblue"];
- /*
- * How to display the currently selected element.
- */
- var curNavElementBorderWidth = [2, 2];
- var curNavElementBorder = ["red", "green"];
- //var curNavElementBackground = ""; // empty string for no extra color
- var curNavElementBackground = ["lightgreen", "lightred"];
- /*
- * Define the transparence of the selectable elements display.
- */
- var navElementOpacity = 0.5;
- /*
- * ========================
- * Navigation Mode Commands
- * ========================
- */
- /*
- * Navigation mode switch
- * ----------------------
- * If JumNav finds this mode switch command character as most recent input in
- * the label head string when update_hints was called while running in hinted
- * mode, it will switch to navigation mode. When in navigation mode it will
- * switch back to hinted mode.
- */
- var modeSwitchChar = '#';
- var displaySwitchChar = "+";
- /*
- * This switches the positions of the elements to adjust to for some
- * (presumedly) webkit element position reporting problem.
- * Use this if the hints appear misaligned on the current page (switch to
- * navigation mode, then switch to raw mode, and then back again to hinting
- * mode if wanted).
- * This is a toggle. It effectively re-creates the hints and labels to be
- * displayed on the page.
- */
- var switchRawChar = "-";
- /*
- * Select the current node element
- */
- var navSelectNode = "s";
- /*
- * Go to next/previous node
- */
- var navNextNode = "n";
- var navPreviousNode = "p";
- // -----------------------------------------------------------------------------
- // end of user configuration section
- // -----------------------------------------------------------------------------
- /*
- * The overlay identification
- * --------------------------
- * This will be prepended to every label overlay element. Redefine if there are
- * name conflicts.
- */
- var overlayId = "JumNavLabel";
- /*
- * Click processing notification
- * -----------------------------
- * These are values JumNav returns to Jumanji when it has processed a link by
- * its own, i.e. usually some mouseclick emulation, or simply wants Jumanji to
- * clear its global buffer.
- * It requires that Jumaji can process these values. Otherwise set these
- * notifications to empty strings.
- */
- var clearBufferNotification = "\\";
- var clickedNotification = "\\\\";
- /**********************
- *** Link Following ***
- **********************/
- /*
- * Common variables
- * ----------------
- */
- var clickableNodes;
- var elementsOverlays;
- var labelsOverlays;
- var nodeLabels;
- var hasLinkNodes;
- var curLabelHead;
- var curLabelNum;
- var matchingLabelNum;
- var labelDigits;
- var useSequence;
- var useBase;
- var navigationMode = false;
- var curNavCommand;
- var displayMode;
- var rawPosition;
- /*
- * Clear All Link Information
- * --------------------------
- */
- function clearLinkInfo() {
- removeOverlays();
- elementsOverlays = null;
- labelsOverlays = null;
- nodeLabels = null;
- clickableNodes = null;
- hasLinkNodes = false;
- curLabelHead = "";
- curLabelNum = -1; // marks number as invalid
- matchingLabelNum = -1;
- labelDigits = 0;
- navigationMode = false;
- curNavCommand = "";
- displayMode = 0;
- rawPosition = false;
- }
- /*
- * Initialize labels collation sequence
- * ------------------------------------
- */
- function initCollSequence() {
- if (collSequence == "numeric") {
- useSequence = "0123456789";
- useBase = 10;
- }
- else if (collSequence == "alpha") {
- useSequence = "abcdefghijklmnopqrstuvxyz";
- useBase = 25;
- }
- else if (collSequence == "longalpha") {
- // We use lower key characters first, then upper key ones
- // to ease the typing.
- useSequence = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ";
- useBase = 50;
- }
- else if (collSequence != "optimal") {
- useSequence = collSequence;
- useBase = collSequence.length;
- }
- }
- /*
- * Label Handling
- * ==============
- */
- /*
- * Construct the Label Text For a Given Position Number
- * ----------------------------------------------------
- * @param posNumber decimal position number
- * (must be >= 0)
- * @return string representation of this number according to the
- * predefined collateral sequence.
- * Leading filled with the zero equivalence of the predefined
- * collateral sequence up to labelDigits length.
- */
- function labelText( posNumber ) {
- var head = posNumber;
- var remainder = 0;
- var labelString = "";
- /*
- * Numeric sequences should count from 1 instead from 0.
- */
- if (collSequence == "numeric" ||
- (collSequence == "optimal" && useSequence.charAt(0) == "0"))
- head++;
- /*
- * Compute the symbolic digits.
- */
- if (head == 0) {
- labelString = useSequence.charAt(0);
- }
- while (head > 0) {
- remainder = head % useBase;
- labelString = useSequence.charAt(remainder) + labelString;
- head = (head - remainder) / useBase;
- }
- // Fill with the zero equivalent of this collateral sequence.
- while (labelString.length < labelDigits) {
- labelString = useSequence.charAt(0) + labelString;
- }
- return labelString;
- }
- /*
- * Construct the Label Lumber For a Given Label String
- * ---------------------------------------------------
- * @param labelString string representation of the label
- * @return decimal equivalent according to the prdefined
- * collataration sequence.
- */
- //function labelNumber( labelString ) {
- // var posNumber = 0;
- // var curBase = useBase;
- // var curDigit;
- //
- // for (var i=labelString.length-1; i >= 0; i--) {
- // curDigit = labelString.charAt(i);
- // posNumber += useBase * useSequence.indexOf(curDigit);
- // curBase *= useBase;
- // }
- // /*
- // * Adjust for numeric counting from 1 instead of 0.
- // */
- // if (collSequence == "numeric" ||
- // (collSequence == "optimal" && useSequence.charAt(0) == "0"))
- // posNumber--;
- //
- // return posNumber;
- //}
- /*
- * Evaluate the position of an element
- * -----------------------------------
- *
- * @param thisElement element to inspect
- * horOffset number of pixels to shift the element left
- * vertOffset number of pixels to shift the element up
- * horCorr number of pixels to add if at left border
- * vertCorr number of pixels to add if at top border
- *
- * @return array [up, left, width, height] of position and size
- */
- function positionOf( thisElement, horOffset, vertOffset, horCorr, vertCorr ) {
- var rect = thisElement.getBoundingClientRect();
- var up = rect.top;
- var left = rect.left;
- var width = rect.width;
- var height = rect.height;
- left = Math.max((left + document.defaultView.scrollX), document.defaultView.scrollX) - horOffset;
- up = Math.max((up + document.defaultView.scrollY), document.defaultView.scrollY) - vertOffset;
- if (horCorr != 0)
- if (left < horCorr)
- left = horCorr;
- if (vertCorr != 0)
- if (up < vertCorr)
- up = vertCorr;
- return [up, left, width, height];
- }
- /*
- * Maintaining Navigation Information
- * ==================================
- */
- /*
- * Check visibility of an element.
- * -------------------------------
- * Recursively checks the given Element wether it is not hidden.
- *
- * @param thisElement node to be checked.
- * @return true if this Element is not hidden and not behind a hidden parent in
- * the DOM tree.
- */
- function isVisible( thisElement ) {
- if ( thisElement == document ) {
- return true;
- }
- if ( ! thisElement ) {
- return false;
- }
- if ( ! thisElement.parentNode ) {
- return false;
- }
- if ( thisElement.style ) {
- if ( thisElement.style.display == 'none' ) {
- return false;
- }
- if ( thisElement.style.visibility == 'hidden' ) {
- return false;
- }
- }
- return isVisible( thisElement.parentNode );
- }
- /*
- * Check if the element is displayable at all.
- * -------------------------------------------
- *
- * @param thisElement element to check
- * @return false if the element width or height are equal or below zero
- */
- function isDisplayable( thisElement ) {
- var width = thisElement.offsetWidth;
- var height = thisElement.offsetHeight;
- if (width <= 0 || height <= 0)
- return false;
- else
- return true;
- }
- /*
- * Find Clickable Elements in the Document
- * ---------------------------------------
- * Scans the child nodes of the current ducument for those being clickable.
- *
- * @return clickableNodes: array of clickable child nodes collected
- * labelDigits: number of label digits required by this
- * collateral sequence
- */
- function findClickableNodes() {
- /*
- * Make sure to always start in a clear state.
- */
- clearLinkInfo();
- clickableNodes = new Array();
- /*
- * Recursively scan the DOM-provided document links array.
- */
- function addClickableNodesIn( thisParent ) {
- for (var i = 0; i < thisParent.childNodes.length; i++) {
- var curNode = thisParent.childNodes[i];
- /*
- * Look at available and visible type 1 nodes only.
- */
- if (curNode.nodeType == 1 &&
- isDisplayable( curNode ) &&
- isVisible( curNode )) {
- /*
- * Check if this is a clickable element and
- * add it to the clickableNodes array if so.
- */
- var isClickable =
- curNode.nodeName.toLowerCase()=="a" |
- curNode.nodeName.toLowerCase()=="input" |
- curNode.nodeName.toLowerCase()=="select" |
- curNode.nodeName.toLowerCase()=="textarea" |
- curNode.hasAttribute( "tabindex" ) |
- curNode.hasAttribute( "href" ) |
- curNode.hasAttribute( "onclick" );
- if (isClickable) {
- clickableNodes.push( curNode );
- }
- }
- /*
- * Recursively check for clickable nodes in the childs of this one.
- */
- addClickableNodesIn( curNode );
- }
- }
- /*
- * Now start this scan at the document root.
- */
- addClickableNodesIn( document );
- /*
- * If wanted now find an optimal collateral sequence for labels display.
- */
- var curLength = clickableNodes.length;
- /*
- * Do so only if there are any clickable nodes at all.
- */
- if (curLength > 0) {
- hasLinkNodes = true;
- } else {
- hasLinkNodes = false;
- return;
- }
- if (collSequence == "optimal") {
- if (curLength < 10) {
- // Labels need one number digit only.
- useSequence = "0123456789";
- useBase = 10;
- }
- else if (curLength < 50) {
- // Labels displayable with one longalpha digit.
- useSequence = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ";
- useBase = 50;
- }
- else if (curLength > 99) {
- // Labels would need more than three number digits.
- // Note: This could as well be a lower + upper case sequence but
- // using lower case only appears to be more practical.
- useSequence = "abcdefghijklmnopqrstuvxyz";
- useBase = 25;
- }
- else {
- // Labels displayable with two number digits.
- useSequence = "0123456789";
- useBase = 10;
- }
- }
- /*
- * Finally compute the number of digits the labels need to show.
- */
- labelDigits = 0;
- if (curLength == 1)
- labelDigits = 1;
- else
- while (curLength > 1) {
- labelDigits++;
- curLength /= useBase;
- }
- }
- /*
- * Hinting Labels
- * ==============
- */
- /*
- * Create Labels Overlays
- * ----------------------
- * Requires the clickableNodes being evaluated already and no overlays being
- * created yet.
- *
- * @return labelsOverlays: array of label elements
- * nodeLabels: array of label texts
- */
- function createLabelOverlays() {
- var curElement;
- var curLabel;
- var curOverlay;
- var curPosition;
- /*
- * Do nothing if there are no clickable nodes at all.
- */
- if (! hasLinkNodes)
- return;
- /*
- * Scan the clickableNodes and construct a labels overlay for each.
- */
- labelsOverlays = new Array();
- nodeLabels = new Array();
- for (var i = 0; i < clickableNodes.length; i++) {
- curLabel = labelText( i );
- curElement = clickableNodes[i];
- curPosition = positionOf( curElement, 6, 6, 1, 1 );
- /*
- * Create a hidden overlay for this element.
- */
- curOverlay = document.createElement( "span" );
- curOverlay.id = overlayId;
- //
- curOverlay.style.position = "absolute";
- if (! rawPosition) {
- curOverlay.style.top = curPosition[0] + "px";
- curOverlay.style.left = curPosition[1] + "px";
- }
- curOverlay.style.width = "auto";
- curOverlay.style.height = "auto";
- curOverlay.style.padding = "1px";
- if (nodeLabelBackground)
- curOverlay.style.background = nodeLabelBackground[displayMode];
- else
- curOverlay.style.background = "transparent";
- curOverlay.style.fontSize = nodeLabelSize;
- curOverlay.style.fontWeight = 'bold';
- curOverlay.style.fontColor = nodeLabelColor[displayMode];
- curOverlay.style.textTransform = "none";
- //
- curOverlay.style.zorder = 10000; // always on top
- curOverlay.style.opacity = nodeOpacity;
- //
- curOverlay.style.border = nodeLabelBorderWidth[displayMode] + "px dashed "
- + nodeLabelBorder[displayMode];
- //
- curOverlay.style.visibility = "hidden";
- // This will be displayed:
- curOverlay.innerHTML =
- "<font color=\"" +
- nodeLabelColor[displayMode] + "\">" +
- curLabel +
- "</font>";
- //
- labelsOverlays.push( curOverlay );
- nodeLabels.push( curLabel );
- /*
- * Insert this into the document as sibling of the current element.
- */
- curElement.parentNode.insertBefore( curOverlay, curElement );
- }
- }
- /*
- * Switch labels display
- * -----------------------
- */
- function switchLabelsDisplay() {
- var curLabel;
- /*
- * Do nothing if there are no clickable nodes at all.
- */
- if (! hasLinkNodes)
- return;
- for (var i = 0; i < labelsOverlays.length; i++) {
- curLabel = labelsOverlays[i];
- if (nodeLabelBackground)
- curLabel.style.background = nodeLabelBackground[displayMode];
- curLabel.style.border = nodeLabelBorderWidth[displayMode] + "px dashed "
- + nodeLabelBorder[displayMode];
- }
- }
- /*
- * Show Labels Overlays
- * --------------------
- * Shows overlays starting with labelHead, hides all others.
- * If no direct match yet show the label tails only. Else show the complete
- * label.
- *
- * @param labelHead initial character sequence of the labels to be shown
- * where only the remaining tail will be displayed
- * if "*": show all labels without change
- * if "": reset and show all labels
- *
- */
- function showLabelOverlays( labelHead ) {
- var curLabel;
- var curOverlay;
- var headLength = labelHead.length;
- /*
- * Do nothing if there are no clickable nodes at all or if the labelHead
- * does not denote a valid labels group.
- */
- if (! hasLinkNodes)
- return;
- if (! isValidLabel( labelHead )) // (sets curlabelNum to element group top)
- return;
- /*
- * Scan the labels overlays array.
- */
- for (var i = 0; i < labelsOverlays.length; i++) {
- labelsOverlays[i].style.visibility = "hidden";
- curLabel = nodeLabels[i];
- if (labelHead == "") {
- // Restore the label text to all digits and show the label.
- labelsOverlays[i].innerHTML =
- "<font color=\"" +
- nodeLabelColor[displayMode] + "\">" +
- curLabel +
- "</font>";
- labelsOverlays[i].style.visibility = "visible";
- }
- else if (labelHead == "*") {
- if (matchingLabelNum >= 0)
- labelsOverlays[matchingLabelNum].style.visibility = "visible";
- else
- labelsOverlays[i].style.visibility = "visible";
- }
- else {
- if (curLabel.substring( 0, headLength) == labelHead) {
- if (headLength != labelDigits) {
- /*
- * This is a partial label.
- */
- if (shortenLabels) {
- // Show relevant digits only.
- labelsOverlays[i].innerHTML =
- "<font color=\"" +
- nodeLabelColor[displayMode] + "\">" +
- curLabel.substring( headLength, labelDigits ) +
- "</font>";
- } else {
- // Mark matching labels differently.
- labelsOverlays[i].innerHTML =
- "<font style=\"background: " +
- partialLabelBackground[displayMode] + "\" color=\"" +
- partialLabelColor[displayMode] + "\">" +
- curLabel +
- "</font>";
- }
- }
- else {
- // This is a full match, remember and show it.
- matchingLabelNum = i;
- /*
- * Note: This is not visible actually. But it does not hurt.
- * It is here in case the user has to confirm the
- * selection.
- */
- labelsOverlays[i].innerHTML =
- "<font color=\"" +
- foundLabelColor[displayMode] +
- "\" style=\"background: " +
- foundLabelBackground[displayMode] + "\">" +
- curLabel +
- "</font>";
- }
- /*
- * Show this label
- */
- labelsOverlays[i].style.visibility = "visible";
- }
- /*
- * Treat nonmatching labels here.
- */
- else {
- // Restore to full label representation.
- labelsOverlays[i].innerHTML =
- "<font color=\"" +
- nodeLabelColor[displayMode] + "\">" +
- curLabel +
- "</font>";
- if (shortenLabels)
- labelsOverlays[i].style.visibility = "hidden";
- else
- labelsOverlays[i].style.visibility = "visible";
- }
- }
- }
- }
- /*
- * Hide overlays
- * -------------
- * Hides every label overlay.
- */
- function hideLabelOverlays() {
- /*
- * Do nothing if there are no clickable nodes at all.
- */
- if (! hasLinkNodes)
- return;
- /*
- * Scan the labels overlays array and hide the nodes displays.
- */
- for (var i = 0; i < labelsOverlays.length; i++) {
- labelsOverlays[i].style.visibility = "hidden";
- }
- }
- /*
- * Display overlays again
- * ----------------------
- */
- // function redisplayOverlays() {
- // showLabelOverlays( curLabelHead );
- // }
- /*
- * Remove label overlays
- * ---------------------
- * Removes all labels overlays.
- *
- * NOTE: This invalidates the overlays and should not be called out of context.
- */
- function removeOverlays() {
- /*
- * Do nothing if there are no overlays at all.
- */
- if (! hasLinkNodes) {
- return;
- }
- /*
- * Track the labels overlays array and remove the node elements kept from
- * their parents.
- */
- for (var i = 0; i < labelsOverlays.length; i++) {
- curNode = labelsOverlays[i];
- curNode.parentNode.removeChild(curNode);
- }
- for (var i = 0; i < elementsOverlays.length; i++) {
- curNode = elementsOverlays[i];
- curNode.parentNode.removeChild(curNode);
- }
- }
- /*
- * Navigation Elements
- * ===================
- */
- /*
- * Create clickable elements overlays
- * ----------------------------------
- */
- /*
- * Create Element Overlays
- * -----------------------
- * Requires the clickableNodes being evaluated already and no overlays being
- * created yet.
- *
- * @return elementsOverlays: array of label elements
- */
- function createElementOverlays() {
- var curElement;
- var curOverlay;
- var curPosition;
- /*
- * Do nothing if there are no clickable nodes at all.
- */
- if (! hasLinkNodes)
- return;
- /*
- * Scan the clickableNodes and construct a element overlay for each.
- */
- elementsOverlays = new Array();
- for (var i = 0; i < clickableNodes.length; i++) {
- curElement = clickableNodes[i];
- curPosition = positionOf( curElement, 0, 0, 0, 0 );
- /*
- * Create a hidden overlay for this element.
- */
- curOverlay = document.createElement( "span" );
- curOverlay.id = overlayId + "Element";
- //
- curOverlay.style.position = "absolute";
- if (! rawPosition) {
- curOverlay.style.top = curPosition[0] + "px";
- curOverlay.style.left = curPosition[1] + "px";
- }
- curOverlay.style.width = curPosition[2] + "px";
- curOverlay.style.height = curPosition[3] + "px";
- if (navElementBackground[displayMode])
- curOverlay.style.background = navElementBackground[displayMode];
- //
- curOverlay.style.zorder = 10000; // always on top
- curOverlay.style.opacity = navElementOpacity;
- //
- curOverlay.style.border = navElementBorderWidth[displayMode]
- + "px dashed " + navElementBorder[displayMode];
- //
- curOverlay.style.visibility = "hidden";
- //
- elementsOverlays.push( curOverlay );
- /*
- * Insert this into the document as sibling of the current element.
- */
- curElement.parentNode.insertBefore( curOverlay, curElement );
- }
- }
- /*
- * Switch elements display
- * -----------------------
- */
- function switchElementsDisplay() {
- var curElement;
- /*
- * Do nothing if there are no clickable nodes at all.
- */
- if (! hasLinkNodes)
- return;
- for (var i = 0; i < elementsOverlays.length; i++) {
- curElement = elementsOverlays[i];
- if (navElementBackground)
- curElement.style.background = navElementBackground[displayMode];
- curElement.style.border =
- navElementBorderWidth[displayMode] + "px dashed " + navElementBorder[displayMode];
- }
- enhanceElementDisplay( curLabelNum );
- }
- /*
- * Show clickable element overlays
- * -------------------------------
- */
- function showElementOverlays() {
- /*
- * Do nothing if there are no clickable nodes at all.
- */
- if (! hasLinkNodes)
- return;
- /*
- * Reenable the display of the crrent selection.
- */
- enhanceElementDisplay( curLabelNum );
- /*
- * Scan the labels overlays array.
- */
- for (var i = 0; i < elementsOverlays.length; i++)
- elementsOverlays[i].style.visibility = "visible";
- }
- /*
- * Hide clickable element overlays
- * -------------------------------
- */
- function hideElementOverlays() {
- /*
- * Do nothing if there are no clickable nodes at all.
- */
- if (! hasLinkNodes)
- return;
- /*
- * Remove the enhanced display of the selected label so that the proper
- * element will be enhanced on later visibility change.
- */
- normalElementDisplay( curLabelNum );
- /*
- * Scan the labels overlays array.
- */
- for (var i = 0; i < elementsOverlays.length; i++)
- elementsOverlays[i].style.visibility = "hidden";
- }
- /*
- * Enhance the display of an element
- * ---------------------------------
- * @param elementNum number of element to change
- */
- function enhanceElementDisplay( elementNum ) {
- if (curNavElementBackground)
- elementsOverlays[elementNum].style.background = curNavElementBackground[displayMode];
- else
- elementsOverlays[elementNum].style.background = "transparent";
- elementsOverlays[elementNum].style.border =
- curNavElementBorderWidth[displayMode] + "px dashed " + curNavElementBorder[displayMode];
- }
- /*
- * Set the display of an element back to normal
- * --------------------------------------------
- * @param elementNum number of element to change
- */
- function normalElementDisplay( elementNum ) {
- if (navElementBackground)
- elementsOverlays[elementNum].style.background = navElementBackground[displayMode];
- else
- elementsOverlays[elementNum].style.background = "transparent";
- elementsOverlays[elementNum].style.border =
- navElementBorderWidth[displayMode] + "px dashed " + navElementBorder[displayMode];
- }
- /*
- * Navigating Commons
- * ==================
- */
- /*
- * Check if the label is valid
- * ---------------------------
- * Checks if a label starting with the given digits sequence is known in the
- * nodeLabels array.
- *
- * @param thisHead head sequence of the label to check
- * @return true there are labels starting with this sequence
- * curLabelNum: number of first occurence found
- * false there are no such labels known
- * curLabelNum: -1
- */
- function isValidLabel( thisHead ) {
- /*
- * If the whole tree is to be matched just set curLabelNum to its top,
- */
- if (thisHead == "") {
- curLabelNum = 0;
- return true;
- }
- /*
- * Try to find a matching labels group and set curLabelNum to its top.
- */
- var headLength = thisHead.length;
- for( var i = 0; i < nodeLabels.length; i++ ) {
- if (thisHead == nodeLabels[i].substring( 0, headLength )) {
- curLabelNum = i;
- return true;
- }
- }
- curLabelNum = -1;
- return false;
- }
- /*
- * Emulate a mouse click
- * ---------------------
- * @param curElement element to click on
- */
- function emulateClickOn( curElement ) {
- /*
- * This requires some more effort in order to trigger the attached
- * actions.
- * At first we need a special event to track mouse clicks.
- */
- var thisEvent = document.createEvent("MouseEvents");
- /*
- * Then the mouse click action needs to be defined.
- */
- thisEvent.initMouseEvent(
- "click", // the event type
- true, true, // allow bubbles and default action cancels
- window, // this view's base
- 0, // mouse click count
- 0, 0, 0, 0, // screen and client coordinates
- false, false, // no control or alt key depressed simultaneously
- false, false, // ditto, shift or meta key
- 0, // mouse button
- null); // no other related target
- /*
- * Finally get this known to the system.
- */
- curElement.dispatchEvent(thisEvent);
- }
- /*
- * Simulate a Mouseclick
- * ---------------------
- *
- * @param labelPos number of the label to be clicked on
- */
- function clickLabel( labelPos ) {
- var curElement = clickableNodes[ labelPos ];
- var curLabel = nodeLabels[ labelPos ];
- var curName = curElement.nodeName.toLowerCase();
- clear();
- /*
- * Anchor
- */
- if (curName == "a") {
- /*
- * It is a link. Go there if possible.
- */
- var thisReference = curElement.href; // get full address (javascript!)
- /*
- * Consider the case where the anchor does not reference an absolute URL.
- */
- if (thisReference.match(/javascript:/)) {
- emulateClickOn( curElement );
- }
- else
- return thisReference;
- }
- /*
- * OnClick
- */
- else if (curElement.hasAttribute("onclick")) {
- emulateClickOn( curElement );
- }
- /*
- * Input
- */
- else if (curName == "input") {
- /*
- * There are several types of input elements which need be handled
- * differently.
- */
- var curType = curElement.getAttribute('type').toLowerCase();
- if (curType == 'text' || curType == 'file' || curType == 'password') {
- /*
- * These need be explicitely selected.
- */
- curElement.focus();
- curElement.select();
- curElement.click();
- } else {
- /*
- * It is a genuine input element.
- * This allows us to use the click() method.
- */
- curElement.click();
- }
- }
- /*
- * Special Input Elements
- */
- else if (curName == 'textarea' || curName == 'select') {
- /*
- * Handle these like the special input element types.
- */
- curElement.focus();
- curElement.select();
- }
- /*
- * Undetected Links
- */
- else if (curElement.hasAttribute( "href" )) {
- /*
- * Handle a possible not detected link.
- */
- return curElement.getAttribute("href");
- }
- /*
- * Last Resort
- */
- else {
- /*
- * This is none of the elements we know (as yet) but we can try to
- * click there anyway.
- */
- emulateClickOn( curElement );
- }
- /*
- * Return a notification value if there was no link detected so that
- * Jumanji may get out of its "follow" mode.
- */
- if (clickedNotification != "")
- return clickedNotification;
- else
- return;
- }
- /*
- * Navigate Element Links
- * ======================
- */
- /*
- * Switch raw position mode
- * ------------------------
- * This is meant to adjust a position reporting problem in webkit(?, not really sure).
- */
- function switchRawPosition() {
- rawPosition = ! rawPosition;
- removeOverlays();
- createElementOverlays();
- createLabelOverlays();
- if (navigationMode)
- showElementOverlays();
- else
- showLabelOverlays( curLabelHead );
- }
- /*
- * Switch display mode
- * -------------------
- */
- function switchDisplayMode() {
- if (displayMode == 0)
- displayMode = 1;
- else
- displayMode = 0;
- switchElementsDisplay();
- switchLabelsDisplay();
- if (navigationMode)
- showElementOverlays();
- else
- showLabelOverlays( curLabelHead );
- }
- /*
- * Go to next node
- * ---------------
- */
- function goNextNode() {
- normalElementDisplay( curLabelNum );
- curLabelNum++;
- if (curLabelNum >= elementsOverlays.length)
- curLabelNum = 0;
- enhanceElementDisplay( curLabelNum );
- }
- /*
- * Go to previous node
- * ---------------
- */
- function goPreviousNode() {
- normalElementDisplay( curLabelNum );
- curLabelNum--;
- if (curLabelNum < 0)
- curLabelNum = elementsOverlays.length - 1;
- enhanceElementDisplay( curLabelNum );
- }
- /*
- ******************************
- *** Jumanji Main Interface ***
- ******************************
- */
- /*
- * Start Navigating
- * ================
- */
- function show_hints() {
- initCollSequence();
- if (hasLinkNodes) {
- clearLinkInfo();
- }
- findClickableNodes();
- if (hasLinkNodes) {
- createElementOverlays();
- createLabelOverlays();
- showLabelOverlays("");
- } else {
- alert( "No clickable node found on this page." );
- }
- }
- /*
- * Navigate the hinted elements
- * ============================
- * @param labelHead head string of the label group to display
- * @return URI of page to select if no action taken here
- * else nothing
- */
- function update_hints( labelHead ) {
- /*
- * Check if modes should be switched.
- */
- if (labelHead.length > 0) {
- if (! navigationMode) {
- if (labelHead == "00" ||
- labelHead.charAt(labelHead.length-1) == modeSwitchChar) {
- navigationMode = true;
- hideLabelOverlays();
- /*
- * Make sure a selectable label will be displayed.
- */
- if (curLabelNum < 0)
- curLabelNum = 0;
- }
- }
- else {
- curNavCommand = labelHead.charAt(labelHead.length-1);
- if (curNavCommand == modeSwitchChar) {
- navigationMode = false;
- hideElementOverlays();
- showLabelOverlays( curLabelHead );
- return clearBufferNotification;
- }
- }
- }
- else {
- curNavCommand = "";
- }
- /*
- * Continue navigation in the selected mode.
- */
- if (! navigationMode) {
- curLabelHead = labelHead;
- showLabelOverlays( labelHead );
- } else {
- showElementOverlays();
- /*
- * Evaluate the navigation commands.
- */
- switch (curNavCommand) {
- case navNextNode:
- goNextNode();
- break;
- case navPreviousNode:
- goPreviousNode();
- break;
- case displaySwitchChar:
- switchDisplayMode();
- break;
- case switchRawChar:
- switchRawPosition();
- break;
- case navSelectNode:
- matchingLabelNum = curLabelNum;
- break;
- }
- // if (curNavCommand == navNextNode)
- // goNextNode();
- // else if (curNavCommand == navPreviousNode)
- // goPreviousNode();
- // else if (curNavCommand == displaySwitchChar)
- // switchDisplayMode();
- // else if (curNavCommand == switchRawChar)
- // switchRawPosition();
- // else if (curNavCommand == navSelectNode)
- // matchingLabelNum = curLabelNum;
- }
- /*
- * Emulate the mouse action if we found a matching label.
- */
- if (matchingLabelNum != -1) {
- var result=clickLabel( matchingLabelNum );
- if (result) {
- return result;
- }
- }
- else if (navigationMode) {
- return clearBufferNotification;
- }
- }
- /*
- * Stop navigating and clear everything
- * ====================================
- */
- function clear() {
- if (navigationMode) {
- hideElementOverlays();
- } else {
- hideLabelOverlays();
- }
- clearLinkInfo();
- }
Advertisement
Add Comment
Please, Sign In to add comment