Guest User

Untitled

a guest
Aug 11th, 2010
178
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // JumNav.js
  2. // Version 0.3
  3. //
  4. // Autor: Bernd Pol <[email protected]>
  5. // License: GPL
  6. //
  7. // Navigation script for use with jumanji
  8. // (jumanji-git >= 20100809)
  9. //
  10. // Note: Needs some patches to jumanji.c from umanji-git 20100809,
  11. //       see: http://pastebin.com/6qPGTk6P
  12.  
  13. /*****************
  14.  *** Changelog ***
  15.  *****************/
  16. /*
  17.  * Version 0.3
  18.  * - Bug fixed: Handles most links properly now, esp. javascript calls
  19.  *              on the page
  20.  * - Navigation mode implemented: allows to navigate back and forth the
  21.  *              clickable elements hierarchy on the page instead of
  22.  *              following hinting labels.
  23.  *              Also allows for some interaction (display mode changes).
  24.  *              Defaults:
  25.  *              #   toggle navigation/hinting mode
  26.  *              n   go to next node in the hierarchy
  27.  *              p   go to previous node in the hierarchy
  28.  *              s   select ("click") the current node
  29.  *              +   toggle display colors
  30.  *              -   toggle a "raw" position mode in order to properly
  31.  *                  align the hinting labels/node rectangles on some pages
  32.  *
  33.  * Version 0.2
  34.  * - If an action was performed by this script on a selection,
  35.  *   i.e. a mouse click simulation, jumanji could not be informed
  36.  *   about this and would thus not get out of the follow mode:
  37.  *   --> JumNav now returns a special notification to Jumanji
  38.  *       Note: This requires Jumanji being able to process this value upon
  39.  *             return from the update_hints() call.
  40.  *             Otherwise the "clickedNotification" configuration variable
  41.  *             must be set to an empty string to not have Jumanji this value
  42.  *             interpreted as an URL.
  43.  *
  44.  * - Bug fixed: hints with 1 label only were not processed.
  45.  */
  46.  
  47. /*********************
  48.  *** Configuration ***
  49.  *********************/
  50. // -----------------------------------------------------------------------------
  51. //                        start of user configuration section
  52. // -----------------------------------------------------------------------------
  53. /*
  54.  * ====================
  55.  * Collateral Sequences
  56.  * ====================
  57.  *
  58.  * There are several label number representations possible. Just uncomment the
  59.  * one you want.
  60.  *
  61.  * Note that the first symbol in sequence will be treated as zero equivalent
  62.  * and the labels will be get those zero equivalents prepended if necessary,
  63.  * e.g. the number 1 in a three-digit "alpha" sequence will show as "aab".
  64.  */
  65. var collSequence = "optimal";   // automatic: find shortest to type sequence
  66.  
  67. // var collSequence = "numeric";   // decimal numbers
  68. // var collSequence = "alpha";     // lower case letter sequences
  69. // var collSequence = "longalpha"; // lower followed by upper case letters
  70.  
  71. /*
  72.  * Alternatively this can be any unique sequence of alphanumeric symbols, e.g.:
  73.  */
  74. // var collSequence = "asdfghjkl"; // home row keys (for touch typers)
  75. // var collSequence = "uiophjklnm"; // right hand only
  76. /*
  77.  * ==================
  78.  * Node label display
  79.  * ==================
  80.  */
  81. /*
  82.  * If true show matching labels and selectable digits only.
  83.  * If false keep the labels intact and mark the selectable group in an
  84.  * alternative color (see below: partialLabelColor[displayMode], partialLabelBackground[displayMode]).
  85.  */
  86. var shortenLabels = true;
  87. // var shortenLabels = false;
  88. /*
  89.  * This defines the font size shown in the labels. It may be an absolute number
  90.  * with a trailing "px" giving the font height in pixels, a number with trailing
  91.  * "%" giving the height relative to the parents font size, or one of the
  92.  * predefined font size property values (ranging from smallest to largest):
  93.  * "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large"
  94.  * or defining a relative value to the parent font size:
  95.  * "smaller", "larger"
  96.  * ( see also: http://www.w3schools.com/jsref/prop_style_fontsize.asp )
  97.  */
  98. var nodeLabelSize = "12px";
  99. // var nodeLabelSize = "85%";
  100. // var nodeLabelSize = "small";
  101. /*
  102.  * How to display the hinting labels
  103.  * ---------------------------------
  104.  * There are two sets of colors so you can switch displays if they become
  105.  * unreadable on a certain side.
  106.  * Use color names or "#rrggbb" hexadecimal values for red, green, and blue
  107.  * channels respectively.
  108.  */
  109. var nodeLabelBorder = ["black", "red"];
  110. var nodeLabelBorderWidth = [1, 1];
  111.  
  112. var nodeLabelColor = ["red", "black"];
  113. //var nodeLabelBackground = "";         // empty string for no extra color
  114. var nodeLabelBackground = ["lightyellow","white"];
  115. /*
  116.  * How to display the partial selection group if shortenLabels was set to false.
  117.  */
  118. var partialLabelColor = ["blue", "yellow"];
  119. var partialLabelBackground = ["lightgreen", "lightblue"];
  120. /*
  121.  * How to display the finally selectable label
  122.  * (in case the user needs to confirm the selection).
  123.  */
  124. var foundLabelColor = ["yellow", "red"];
  125. var foundLabelBackground = ["red", "yellow"];
  126. /*
  127.  * Define the transparence of the labels.
  128.  */
  129. var nodeOpacity = 0.6;
  130. /*
  131.  * ============================
  132.  * Navigatable Elements Display
  133.  * ============================
  134.  */
  135. /*
  136.  * How to display the clickable nodes in navigation mode
  137.  * -----------------------------------------------------
  138.  * Use color names or "#rrggbb" hexadecimal values for red, green, and blue
  139.  * channels respectively.
  140.  */
  141. var navElementBorderWidth = [1, 1];
  142. var navElementBorder = ["blue", "yellow"];
  143. //var navElementBackground = "";                  // empty string for no extra color
  144. var navElementBackground = ["lightyellow", "lightblue"];
  145. /*
  146.  * How to display the currently selected element.
  147.  */
  148. var curNavElementBorderWidth = [2, 2];
  149. var curNavElementBorder = ["red", "green"];
  150. //var curNavElementBackground = "";               // empty string for no extra color
  151. var curNavElementBackground = ["lightgreen", "lightred"];
  152. /*
  153.  * Define the transparence of the selectable elements display.
  154.  */
  155. var navElementOpacity = 0.5;
  156. /*
  157.  * ========================
  158.  * Navigation Mode Commands
  159.  * ========================
  160.  */
  161. /*
  162.  * Navigation mode switch
  163.  * ----------------------
  164.  * If JumNav finds this mode switch command character as most recent input in
  165.  * the label head string when update_hints was called while running in hinted
  166.  * mode, it will switch to navigation mode. When in navigation mode it will
  167.  * switch back to hinted mode.
  168.  */
  169. var modeSwitchChar = '#';
  170. var displaySwitchChar = "+";
  171. /*
  172.  * This switches the positions of the elements to adjust to for some
  173.  * (presumedly) webkit element position reporting problem.
  174.  * Use this if the hints appear misaligned on the current page (switch to
  175.  * navigation mode, then switch to raw mode, and then back again to hinting
  176.  * mode if wanted).
  177.  * This is a toggle. It effectively re-creates the hints and labels to be
  178.  * displayed on the page.
  179.  */
  180. var switchRawChar = "-";
  181. /*
  182.  * Select the current node element
  183.  */
  184. var navSelectNode = "s";
  185. /*
  186.  * Go to next/previous node
  187.  */
  188. var navNextNode = "n";
  189. var navPreviousNode = "p";
  190.  
  191. // -----------------------------------------------------------------------------
  192. //                        end of user configuration section
  193. // -----------------------------------------------------------------------------
  194. /*
  195.  * The overlay identification
  196.  * --------------------------
  197.  * This will be prepended to every label overlay element. Redefine if there are
  198.  * name conflicts.
  199.  */
  200. var overlayId = "JumNavLabel";
  201. /*
  202.  * Click processing notification
  203.  * -----------------------------
  204.  * These are values JumNav returns to Jumanji when it has processed a link by
  205.  * its own, i.e. usually some mouseclick emulation, or simply wants Jumanji to
  206.  * clear its global buffer.
  207.  * It requires that Jumaji can process these values. Otherwise set these
  208.  * notifications to empty strings.
  209.  */
  210. var clearBufferNotification = "\\";
  211. var clickedNotification = "\\\\";
  212.  
  213. /**********************
  214.  *** Link Following ***
  215.  **********************/
  216. /*
  217.  * Common variables
  218.  * ----------------
  219.  */
  220. var clickableNodes;
  221. var elementsOverlays;
  222. var labelsOverlays;
  223. var nodeLabels;
  224.  
  225. var hasLinkNodes;
  226. var curLabelHead;
  227. var curLabelNum;
  228. var matchingLabelNum;
  229. var labelDigits;
  230.  
  231. var useSequence;
  232. var useBase;
  233.  
  234. var navigationMode = false;
  235. var curNavCommand;
  236. var displayMode;
  237. var rawPosition;
  238. /*
  239.  * Clear All Link Information
  240.  * --------------------------
  241.  */
  242. function clearLinkInfo() {
  243.     removeOverlays();
  244.     elementsOverlays = null;
  245.     labelsOverlays = null;
  246.     nodeLabels = null;
  247.     clickableNodes = null;
  248.     hasLinkNodes = false;
  249.     curLabelHead = "";
  250.     curLabelNum = -1;   // marks number as invalid
  251.     matchingLabelNum = -1;
  252.     labelDigits = 0;
  253.     navigationMode = false;
  254.     curNavCommand = "";
  255.     displayMode = 0;
  256.     rawPosition = false;
  257. }
  258. /*
  259.  * Initialize labels collation sequence
  260.  * ------------------------------------
  261.  */
  262. function initCollSequence() {
  263.     if (collSequence == "numeric") {
  264.         useSequence = "0123456789";
  265.         useBase = 10;
  266.     }
  267.     else if (collSequence == "alpha") {
  268.         useSequence = "abcdefghijklmnopqrstuvxyz";
  269.         useBase = 25;
  270.     }
  271.     else if (collSequence == "longalpha") {
  272.         // We use lower key characters first, then upper key ones
  273.         // to ease the typing.
  274.         useSequence = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ";
  275.         useBase = 50;
  276.     }
  277.     else if (collSequence != "optimal") {
  278.         useSequence = collSequence;
  279.         useBase = collSequence.length;
  280.     }
  281. }
  282. /*
  283.  * Label Handling
  284.  * ==============
  285.  */
  286. /*
  287.  * Construct the Label Text For a Given Position Number
  288.  * ----------------------------------------------------
  289.  * @param posNumber decimal position number
  290.  *                  (must be >= 0)
  291.  * @return          string representation of this number according to the
  292.  *                  predefined collateral sequence.
  293.  *                  Leading filled with the zero equivalence of the predefined
  294.  *                  collateral sequence up to labelDigits length.
  295.  */
  296. function labelText( posNumber ) {
  297.     var head = posNumber;
  298.     var remainder = 0;
  299.     var labelString = "";
  300.     /*
  301.      * Numeric sequences should count from 1 instead from 0.
  302.      */
  303.     if (collSequence == "numeric" ||
  304.         (collSequence == "optimal" && useSequence.charAt(0) == "0"))
  305.         head++;
  306.     /*
  307.      * Compute the symbolic digits.
  308.      */
  309.     if (head == 0) {
  310.         labelString = useSequence.charAt(0);
  311.     }
  312.     while (head > 0) {
  313.         remainder = head % useBase;
  314.         labelString = useSequence.charAt(remainder) + labelString;
  315.         head = (head - remainder) / useBase;
  316.     }
  317.     // Fill with the zero equivalent of this collateral sequence.
  318.     while (labelString.length < labelDigits) {
  319.         labelString = useSequence.charAt(0) + labelString;
  320.     }
  321.     return labelString;
  322. }
  323. /*
  324.  * Construct the Label Lumber For a Given Label String
  325.  * ---------------------------------------------------
  326.  * @param labelString   string representation of the label
  327.  * @return              decimal equivalent according to the prdefined
  328.  *                      collataration sequence.
  329.  */
  330. //function labelNumber( labelString ) {
  331. //    var posNumber = 0;
  332. //    var curBase = useBase;
  333. //    var curDigit;
  334. //
  335. //    for (var i=labelString.length-1; i >= 0; i--) {
  336. //        curDigit = labelString.charAt(i);
  337. //        posNumber += useBase * useSequence.indexOf(curDigit);
  338. //        curBase *= useBase;
  339. //    }
  340. //    /*
  341. //     * Adjust for numeric counting from 1 instead of 0.
  342. //     */
  343. //    if (collSequence == "numeric" ||
  344. //        (collSequence == "optimal" && useSequence.charAt(0) == "0"))
  345. //        posNumber--;
  346. //
  347. //    return posNumber;
  348. //}
  349. /*
  350.  * Evaluate the position of an element
  351.  * -----------------------------------
  352.  *
  353.  *  @param  thisElement element to inspect
  354.  *          horOffset   number of pixels to shift the element left
  355.  *          vertOffset  number of pixels to shift the element up
  356.  *          horCorr     number of pixels to add if at left border
  357.  *          vertCorr    number of pixels to add if at top border
  358.  *
  359.  *  @return array [up, left, width, height] of position and size
  360.  */
  361. function positionOf( thisElement, horOffset, vertOffset, horCorr, vertCorr ) {
  362.     var rect = thisElement.getBoundingClientRect();
  363.  
  364.     var up = rect.top;
  365.     var left = rect.left;
  366.     var width = rect.width;
  367.     var height = rect.height;
  368.  
  369.     left = Math.max((left + document.defaultView.scrollX), document.defaultView.scrollX) - horOffset;
  370.     up = Math.max((up + document.defaultView.scrollY), document.defaultView.scrollY) - vertOffset;
  371.  
  372.     if (horCorr != 0)
  373.         if (left < horCorr)
  374.             left = horCorr;
  375.  
  376.     if (vertCorr != 0)
  377.         if (up < vertCorr)
  378.             up = vertCorr;
  379.  
  380.     return [up, left, width, height];
  381. }
  382. /*
  383.  * Maintaining Navigation Information
  384.  * ==================================
  385.  */
  386. /*
  387.  * Check visibility of an element.
  388.  * -------------------------------
  389.  * Recursively checks the given Element wether it is not hidden.
  390.  *
  391.  * @param   thisElement  node to be checked.
  392.  * @return  true if this Element is not hidden and not behind a hidden parent in
  393.  *          the DOM tree.
  394.  */
  395. function isVisible( thisElement ) {
  396.     if ( thisElement == document ) {
  397.         return true;
  398.     }
  399.     if ( ! thisElement ) {
  400.         return false;
  401.     }
  402.     if ( ! thisElement.parentNode ) {
  403.         return false;
  404.     }
  405.     if ( thisElement.style ) {
  406.         if ( thisElement.style.display == 'none' ) {
  407.             return false;
  408.         }
  409.         if ( thisElement.style.visibility == 'hidden' ) {
  410.             return false;
  411.         }
  412.     }
  413.     return isVisible( thisElement.parentNode );
  414. }
  415. /*
  416.  * Check if the element is displayable at all.
  417.  * -------------------------------------------
  418.  *
  419.  * @param   thisElement element to check
  420.  * @return  false if the element width or height are equal or below zero
  421.  */
  422. function isDisplayable( thisElement ) {
  423.     var width = thisElement.offsetWidth;
  424.     var height = thisElement.offsetHeight;
  425.  
  426.     if (width <= 0 || height <= 0)
  427.         return false;
  428.     else
  429.         return true;
  430. }
  431. /*
  432.  * Find Clickable Elements in the Document
  433.  * ---------------------------------------
  434.  * Scans the child nodes of the current ducument for those being clickable.
  435.  *
  436.  * @return  clickableNodes: array of clickable child nodes collected
  437.  *          labelDigits:    number of label digits required by this
  438.  *                          collateral sequence
  439.  */
  440. function findClickableNodes() {
  441.     /*
  442.      * Make sure to always start in a clear state.
  443.      */
  444.     clearLinkInfo();
  445.     clickableNodes = new Array();
  446.     /*
  447.      * Recursively scan the DOM-provided document links array.
  448.      */
  449.     function addClickableNodesIn( thisParent ) {
  450.         for (var i = 0; i < thisParent.childNodes.length; i++) {
  451.             var curNode = thisParent.childNodes[i];
  452.             /*
  453.              * Look at available and visible type 1 nodes only.
  454.              */
  455.             if (curNode.nodeType == 1 &&
  456.                 isDisplayable( curNode ) &&
  457.                 isVisible( curNode )) {
  458.                 /*
  459.                  * Check if this is a clickable element and
  460.                  * add it to the clickableNodes array if so.
  461.                  */
  462.                 var isClickable =
  463.                     curNode.nodeName.toLowerCase()=="a" |
  464.                     curNode.nodeName.toLowerCase()=="input" |
  465.                     curNode.nodeName.toLowerCase()=="select" |
  466.                     curNode.nodeName.toLowerCase()=="textarea" |
  467.                     curNode.hasAttribute( "tabindex" ) |
  468.                     curNode.hasAttribute( "href" ) |
  469.                     curNode.hasAttribute( "onclick" );
  470.                 if (isClickable) {
  471.                     clickableNodes.push( curNode );
  472.                 }
  473.             }
  474.             /*
  475.              * Recursively check for clickable nodes in the childs of this one.
  476.              */
  477.             addClickableNodesIn( curNode );
  478.         }
  479.     }
  480.     /*
  481.      * Now start this scan at the document root.
  482.      */
  483.     addClickableNodesIn( document );
  484.     /*
  485.      * If wanted now find an optimal collateral sequence for labels display.
  486.      */
  487.     var curLength = clickableNodes.length;
  488.     /*
  489.      * Do so only if there are any clickable nodes at all.
  490.      */
  491.     if (curLength > 0) {
  492.         hasLinkNodes = true;
  493.     } else {
  494.         hasLinkNodes = false;
  495.         return;
  496.     }
  497.  
  498.     if (collSequence == "optimal") {
  499.         if (curLength < 10) {
  500.             // Labels need one number digit only.
  501.             useSequence = "0123456789";
  502.             useBase = 10;
  503.         }
  504.         else if (curLength < 50) {
  505.             // Labels displayable with one longalpha digit.
  506.             useSequence = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ";
  507.             useBase = 50;
  508.         }
  509.         else if (curLength > 99) {
  510.             // Labels would need more than three number digits.
  511.             // Note: This could as well be a lower + upper case sequence but
  512.             //       using lower case only appears to be more practical.
  513.             useSequence = "abcdefghijklmnopqrstuvxyz";
  514.             useBase = 25;
  515.         }
  516.         else {
  517.             // Labels displayable with two number digits.
  518.             useSequence = "0123456789";
  519.             useBase = 10;
  520.         }
  521.     }
  522.     /*
  523.      * Finally compute the number of digits the labels need to show.
  524.      */
  525.     labelDigits = 0;
  526.     if (curLength == 1)
  527.         labelDigits = 1;
  528.     else
  529.         while (curLength > 1) {
  530.             labelDigits++;
  531.             curLength /= useBase;
  532.         }
  533. }
  534. /*
  535.  * Hinting Labels
  536.  * ==============
  537.  */
  538. /*
  539.  * Create Labels Overlays
  540.  * ----------------------
  541.  * Requires the clickableNodes being evaluated already and no overlays being
  542.  * created yet.
  543.  *
  544.  * @return  labelsOverlays: array of label elements
  545.  *          nodeLabels:     array of label texts
  546.  */
  547. function createLabelOverlays() {
  548.     var curElement;
  549.     var curLabel;
  550.     var curOverlay;
  551.     var curPosition;
  552.     /*
  553.      * Do nothing if there are no clickable nodes at all.
  554.      */
  555.     if (! hasLinkNodes)
  556.         return;
  557.     /*
  558.      * Scan the clickableNodes and construct a labels overlay for each.
  559.      */
  560.     labelsOverlays = new Array();
  561.     nodeLabels = new Array();
  562.  
  563.     for (var i = 0; i < clickableNodes.length; i++) {
  564.         curLabel = labelText( i );
  565.         curElement = clickableNodes[i];
  566.         curPosition = positionOf( curElement, 6, 6, 1, 1 );
  567.         /*
  568.          * Create a hidden overlay for this element.
  569.          */
  570.         curOverlay    = document.createElement( "span" );
  571.         curOverlay.id = overlayId;
  572.         //
  573.         curOverlay.style.position   = "absolute";
  574.         if (! rawPosition) {
  575.             curOverlay.style.top    = curPosition[0] + "px";
  576.             curOverlay.style.left   = curPosition[1] + "px";
  577.         }
  578.         curOverlay.style.width      = "auto";
  579.         curOverlay.style.height     = "auto";
  580.         curOverlay.style.padding    = "1px";
  581.         if (nodeLabelBackground)
  582.             curOverlay.style.background = nodeLabelBackground[displayMode];
  583.         else
  584.             curOverlay.style.background = "transparent";
  585.         curOverlay.style.fontSize   = nodeLabelSize;
  586.         curOverlay.style.fontWeight = 'bold';
  587.         curOverlay.style.fontColor  = nodeLabelColor[displayMode];
  588.         curOverlay.style.textTransform = "none";
  589.         //
  590.         curOverlay.style.zorder = 10000;    // always on top
  591.         curOverlay.style.opacity = nodeOpacity;
  592.         //
  593.         curOverlay.style.border     = nodeLabelBorderWidth[displayMode] + "px dashed "
  594.                                         + nodeLabelBorder[displayMode];
  595.         //
  596.         curOverlay.style.visibility = "hidden";
  597.         // This will be displayed:
  598.         curOverlay.innerHTML =
  599.             "<font color=\"" +
  600.             nodeLabelColor[displayMode] + "\">" +
  601.             curLabel +
  602.             "</font>";
  603.         //
  604.         labelsOverlays.push( curOverlay );
  605.         nodeLabels.push( curLabel );
  606.         /*
  607.          * Insert this into the document as sibling of the current element.
  608.          */
  609.         curElement.parentNode.insertBefore( curOverlay, curElement );
  610.     }
  611. }
  612. /*
  613.  * Switch labels display
  614.  * -----------------------
  615.  */
  616. function switchLabelsDisplay() {
  617.     var curLabel;
  618.     /*
  619.      * Do nothing if there are no clickable nodes at all.
  620.      */
  621.     if (! hasLinkNodes)
  622.         return;
  623.  
  624.     for (var i = 0; i < labelsOverlays.length; i++) {
  625.         curLabel = labelsOverlays[i];
  626.  
  627.         if (nodeLabelBackground)
  628.             curLabel.style.background = nodeLabelBackground[displayMode];
  629.  
  630.         curLabel.style.border = nodeLabelBorderWidth[displayMode] + "px dashed "
  631.                                         + nodeLabelBorder[displayMode];
  632.     }
  633. }
  634. /*
  635.  * Show Labels Overlays
  636.  * --------------------
  637.  * Shows overlays starting with labelHead, hides all others.
  638.  * If no direct match yet show the label tails only. Else show the complete
  639.  * label.
  640.  *
  641.  * @param   labelHead   initial character sequence of the labels to be shown
  642.  *                      where only the remaining tail will be displayed
  643.  *                      if "*":   show all labels without change
  644.  *                      if "":    reset and show all labels
  645.  *
  646.  */
  647. function showLabelOverlays( labelHead ) {
  648.     var curLabel;
  649.     var curOverlay;
  650.     var headLength = labelHead.length;
  651.     /*
  652.      * Do nothing if there are no clickable nodes at all or if the labelHead
  653.      * does not denote a valid labels group.
  654.      */
  655.     if (! hasLinkNodes)
  656.         return;
  657.     if (! isValidLabel( labelHead )) // (sets curlabelNum to element group top)
  658.         return;
  659.     /*
  660.      * Scan the labels overlays array.
  661.      */
  662.     for (var i = 0; i < labelsOverlays.length; i++) {
  663.         labelsOverlays[i].style.visibility = "hidden";
  664.         curLabel = nodeLabels[i];
  665.  
  666.         if (labelHead == "") {
  667.             // Restore the label text to all digits and show the label.
  668.             labelsOverlays[i].innerHTML =
  669.                 "<font color=\"" +
  670.                 nodeLabelColor[displayMode] + "\">" +
  671.                 curLabel +
  672.                 "</font>";
  673.             labelsOverlays[i].style.visibility = "visible";
  674.         }
  675.         else if (labelHead == "*") {
  676.             if (matchingLabelNum >= 0)
  677.                 labelsOverlays[matchingLabelNum].style.visibility = "visible";
  678.             else
  679.                 labelsOverlays[i].style.visibility = "visible";
  680.         }
  681.         else {
  682.             if (curLabel.substring( 0, headLength) == labelHead) {
  683.                 if (headLength != labelDigits) {
  684.                     /*
  685.                      * This is a partial label.
  686.                      */
  687.                     if (shortenLabels) {
  688.                         // Show relevant digits only.
  689.                         labelsOverlays[i].innerHTML =
  690.                             "<font color=\"" +
  691.                             nodeLabelColor[displayMode] + "\">" +
  692.                             curLabel.substring( headLength, labelDigits ) +
  693.                             "</font>";
  694.                     } else {
  695.                         // Mark matching labels differently.
  696.                         labelsOverlays[i].innerHTML =
  697.                             "<font style=\"background: " +
  698.                             partialLabelBackground[displayMode] + "\" color=\"" +
  699.                             partialLabelColor[displayMode] + "\">" +
  700.                             curLabel +
  701.                             "</font>";
  702.                     }
  703.                 }
  704.                 else {
  705.                     // This is a full match, remember and show it.
  706.                     matchingLabelNum = i;
  707.                     /*
  708.                      * Note: This is not visible actually. But it does not hurt.
  709.                      *       It is here in case the user has to confirm the
  710.                      *       selection.
  711.                      */
  712.                     labelsOverlays[i].innerHTML =
  713.                         "<font color=\"" +
  714.                         foundLabelColor[displayMode] +
  715.                         "\" style=\"background: " +
  716.                         foundLabelBackground[displayMode] + "\">" +
  717.                         curLabel +
  718.                         "</font>";
  719.                 }
  720.                 /*
  721.                  * Show this label
  722.                  */
  723.                 labelsOverlays[i].style.visibility = "visible";
  724.             }
  725.             /*
  726.              * Treat nonmatching labels here.
  727.              */
  728.             else {
  729.                 // Restore to full label representation.
  730.                 labelsOverlays[i].innerHTML =
  731.                     "<font color=\"" +
  732.                     nodeLabelColor[displayMode] + "\">" +
  733.                     curLabel +
  734.                     "</font>";
  735.  
  736.                 if (shortenLabels)
  737.                     labelsOverlays[i].style.visibility = "hidden";
  738.                 else
  739.                     labelsOverlays[i].style.visibility = "visible";
  740.             }
  741.         }
  742.     }
  743. }
  744. /*
  745.  * Hide overlays
  746.  * -------------
  747.  * Hides every label overlay.
  748.  */
  749. function hideLabelOverlays() {
  750.     /*
  751.      * Do nothing if there are no clickable nodes at all.
  752.      */
  753.     if (! hasLinkNodes)
  754.         return;
  755.     /*
  756.      * Scan the labels overlays array and hide the nodes displays.
  757.      */
  758.     for (var i = 0; i < labelsOverlays.length; i++) {
  759.         labelsOverlays[i].style.visibility = "hidden";
  760.     }
  761. }
  762. /*
  763.  * Display overlays again
  764.  * ----------------------
  765.  */
  766. // function redisplayOverlays() {
  767. //     showLabelOverlays( curLabelHead );
  768. // }
  769. /*
  770.  * Remove label overlays
  771.  * ---------------------
  772.  * Removes all labels overlays.
  773.  *
  774.  * NOTE: This invalidates the overlays and should not be called out of context.
  775.  */
  776. function removeOverlays() {
  777.     /*
  778.      * Do nothing if there are no overlays at all.
  779.      */
  780.     if (! hasLinkNodes) {
  781.         return;
  782.     }
  783.     /*
  784.      * Track the labels overlays array and remove the node elements kept from
  785.      * their parents.
  786.      */
  787.     for (var i = 0; i < labelsOverlays.length; i++) {
  788.         curNode = labelsOverlays[i];
  789.         curNode.parentNode.removeChild(curNode);
  790.     }
  791.     for (var i = 0; i < elementsOverlays.length; i++) {
  792.         curNode = elementsOverlays[i];
  793.         curNode.parentNode.removeChild(curNode);
  794.     }
  795. }
  796. /*
  797.  * Navigation Elements
  798.  * ===================
  799.  */
  800. /*
  801.  * Create clickable elements overlays
  802.  * ----------------------------------
  803.  */
  804. /*
  805.  * Create Element Overlays
  806.  * -----------------------
  807.  * Requires the clickableNodes being evaluated already and no overlays being
  808.  * created yet.
  809.  *
  810.  * @return  elementsOverlays: array of label elements
  811.  */
  812. function createElementOverlays() {
  813.     var curElement;
  814.     var curOverlay;
  815.     var curPosition;
  816.     /*
  817.      * Do nothing if there are no clickable nodes at all.
  818.      */
  819.     if (! hasLinkNodes)
  820.         return;
  821.     /*
  822.      * Scan the clickableNodes and construct a element overlay for each.
  823.      */
  824.     elementsOverlays = new Array();
  825.  
  826.     for (var i = 0; i < clickableNodes.length; i++) {
  827.         curElement = clickableNodes[i];
  828.         curPosition = positionOf( curElement, 0, 0, 0, 0 );
  829.         /*
  830.          * Create a hidden overlay for this element.
  831.          */
  832.         curOverlay    = document.createElement( "span" );
  833.         curOverlay.id = overlayId + "Element";
  834.         //
  835.         curOverlay.style.position   = "absolute";
  836.         if (! rawPosition) {
  837.             curOverlay.style.top    = curPosition[0] + "px";
  838.             curOverlay.style.left   = curPosition[1] + "px";
  839.         }
  840.         curOverlay.style.width      = curPosition[2] + "px";
  841.         curOverlay.style.height     = curPosition[3] + "px";
  842.         if (navElementBackground[displayMode])
  843.             curOverlay.style.background = navElementBackground[displayMode];
  844.         //
  845.         curOverlay.style.zorder     = 10000;    // always on top
  846.         curOverlay.style.opacity    = navElementOpacity;
  847.         //
  848.         curOverlay.style.border     = navElementBorderWidth[displayMode]
  849.                                         + "px dashed " + navElementBorder[displayMode];
  850.         //
  851.         curOverlay.style.visibility = "hidden";
  852.         //
  853.         elementsOverlays.push( curOverlay );
  854.         /*
  855.          * Insert this into the document as sibling of the current element.
  856.          */
  857.         curElement.parentNode.insertBefore( curOverlay, curElement );
  858.     }
  859. }
  860. /*
  861.  * Switch elements display
  862.  * -----------------------
  863.  */
  864. function switchElementsDisplay() {
  865.     var curElement;
  866.     /*
  867.      * Do nothing if there are no clickable nodes at all.
  868.      */
  869.     if (! hasLinkNodes)
  870.         return;
  871.  
  872.     for (var i = 0; i < elementsOverlays.length; i++) {
  873.         curElement = elementsOverlays[i];
  874.  
  875.         if (navElementBackground)
  876.             curElement.style.background = navElementBackground[displayMode];
  877.  
  878.         curElement.style.border =
  879.             navElementBorderWidth[displayMode] + "px dashed " + navElementBorder[displayMode];
  880.     }
  881.     enhanceElementDisplay( curLabelNum );
  882. }
  883. /*
  884.  * Show clickable element overlays
  885.  * -------------------------------
  886.  */
  887. function showElementOverlays() {
  888.     /*
  889.      * Do nothing if there are no clickable nodes at all.
  890.      */
  891.     if (! hasLinkNodes)
  892.         return;
  893.     /*
  894.      * Reenable the display of the crrent selection.
  895.      */
  896.     enhanceElementDisplay( curLabelNum );
  897.     /*
  898.      * Scan the labels overlays array.
  899.      */
  900.     for (var i = 0; i < elementsOverlays.length; i++)
  901.         elementsOverlays[i].style.visibility = "visible";
  902. }
  903. /*
  904.  * Hide clickable element overlays
  905.  * -------------------------------
  906.  */
  907. function hideElementOverlays() {
  908.     /*
  909.      * Do nothing if there are no clickable nodes at all.
  910.      */
  911.     if (! hasLinkNodes)
  912.         return;
  913.     /*
  914.      * Remove the enhanced display of the selected label so that the proper
  915.      * element will be enhanced on later visibility change.
  916.      */
  917.     normalElementDisplay( curLabelNum );
  918.     /*
  919.      * Scan the labels overlays array.
  920.      */
  921.     for (var i = 0; i < elementsOverlays.length; i++)
  922.         elementsOverlays[i].style.visibility = "hidden";
  923. }
  924. /*
  925.  * Enhance the display of an element
  926.  * ---------------------------------
  927.  * @param   elementNum  number of element to change
  928.  */
  929. function enhanceElementDisplay( elementNum ) {
  930.     if (curNavElementBackground)
  931.         elementsOverlays[elementNum].style.background = curNavElementBackground[displayMode];
  932.     else
  933.         elementsOverlays[elementNum].style.background = "transparent";
  934.  
  935.     elementsOverlays[elementNum].style.border =
  936.         curNavElementBorderWidth[displayMode] + "px dashed " + curNavElementBorder[displayMode];
  937. }
  938. /*
  939.  * Set the display of an element back to normal
  940.  * --------------------------------------------
  941.  * @param   elementNum  number of element to change
  942.  */
  943. function normalElementDisplay( elementNum ) {
  944.     if (navElementBackground)
  945.         elementsOverlays[elementNum].style.background = navElementBackground[displayMode];
  946.     else
  947.         elementsOverlays[elementNum].style.background = "transparent";
  948.  
  949.     elementsOverlays[elementNum].style.border =
  950.         navElementBorderWidth[displayMode] + "px dashed " + navElementBorder[displayMode];
  951. }
  952. /*
  953.  * Navigating Commons
  954.  * ==================
  955.  */
  956. /*
  957.  * Check if the label is valid
  958.  * ---------------------------
  959.  * Checks if a label starting with the given digits sequence is known in the
  960.  * nodeLabels array.
  961.  *
  962.  * @param   thisHead    head sequence of the label to check
  963.  * @return  true        there are labels starting with this sequence
  964.  *                      curLabelNum: number of first occurence found
  965.  *          false       there are no such labels known
  966.  *                      curLabelNum: -1
  967.  */
  968. function isValidLabel( thisHead ) {
  969.     /*
  970.      * If the whole tree is to be matched just set curLabelNum to its top,
  971.      */
  972.     if (thisHead == "") {
  973.         curLabelNum = 0;
  974.         return true;
  975.     }
  976.     /*
  977.      * Try to find a matching labels group and set curLabelNum to its top.
  978.      */
  979.     var headLength = thisHead.length;
  980.     for( var i = 0; i < nodeLabels.length; i++ ) {
  981.         if (thisHead == nodeLabels[i].substring( 0, headLength )) {
  982.             curLabelNum = i;
  983.             return true;
  984.         }
  985.     }
  986.     curLabelNum = -1;
  987.     return false;
  988. }
  989. /*
  990.  * Emulate a mouse click
  991.  * ---------------------
  992.  * @param   curElement  element to click on
  993.  */
  994. function emulateClickOn( curElement ) {
  995.     /*
  996.      * This requires some more effort in order to trigger the attached
  997.      * actions.
  998.      * At first we need a special event to track mouse clicks.
  999.      */
  1000.     var thisEvent = document.createEvent("MouseEvents");
  1001.     /*
  1002.      * Then the mouse click action needs to be defined.
  1003.      */
  1004.     thisEvent.initMouseEvent(
  1005.         "click",        // the event type
  1006.         true, true,     // allow bubbles and default action cancels
  1007.         window,         // this view's base
  1008.         0,              // mouse click count
  1009.         0, 0, 0, 0,     // screen and client coordinates
  1010.         false, false,   // no control or alt key depressed simultaneously
  1011.         false, false,   // ditto, shift or meta key
  1012.         0,              // mouse button
  1013.         null);          // no other related target
  1014.     /*
  1015.      * Finally get this known to the system.
  1016.      */
  1017.     curElement.dispatchEvent(thisEvent);
  1018. }
  1019. /*
  1020.  * Simulate a Mouseclick
  1021.  * ---------------------
  1022.  *
  1023.  *  @param  labelPos    number of the label to be clicked on
  1024.  */
  1025. function clickLabel( labelPos ) {
  1026.     var curElement = clickableNodes[ labelPos ];
  1027.     var curLabel = nodeLabels[ labelPos ];
  1028.     var curName = curElement.nodeName.toLowerCase();
  1029.  
  1030.     clear();
  1031.     /*
  1032.      * Anchor
  1033.      */
  1034.     if (curName == "a") {
  1035.         /*
  1036.          * It is a link. Go there if possible.
  1037.          */
  1038.         var thisReference = curElement.href;    // get full address (javascript!)
  1039.         /*
  1040.          * Consider the case where the anchor does not reference an absolute URL.
  1041.          */
  1042.         if (thisReference.match(/javascript:/)) {
  1043.             emulateClickOn( curElement );
  1044.         }
  1045.         else
  1046.             return thisReference;
  1047.     }
  1048.     /*
  1049.      * OnClick
  1050.      */
  1051.     else if (curElement.hasAttribute("onclick")) {
  1052.         emulateClickOn( curElement );
  1053.     }
  1054.     /*
  1055.      * Input
  1056.      */
  1057.     else if (curName == "input") {
  1058.         /*
  1059.          * There are several types of input elements which need be handled
  1060.          * differently.
  1061.          */
  1062.         var curType = curElement.getAttribute('type').toLowerCase();
  1063.  
  1064.         if (curType == 'text' || curType == 'file' || curType == 'password') {
  1065.             /*
  1066.              * These need be explicitely selected.
  1067.              */
  1068.             curElement.focus();
  1069.             curElement.select();
  1070.             curElement.click();
  1071.         } else {
  1072.             /*
  1073.              * It is a genuine input element.
  1074.              * This allows us to use the click() method.
  1075.              */
  1076.             curElement.click();
  1077.         }
  1078.     }
  1079.     /*
  1080.      * Special Input Elements
  1081.      */
  1082.     else if (curName == 'textarea' || curName == 'select') {
  1083.         /*
  1084.          * Handle these like the special input element types.
  1085.          */
  1086.         curElement.focus();
  1087.         curElement.select();
  1088.     }
  1089.     /*
  1090.      * Undetected Links
  1091.      */
  1092.     else if (curElement.hasAttribute( "href" )) {
  1093.         /*
  1094.          * Handle a possible not detected link.
  1095.          */
  1096.         return curElement.getAttribute("href");
  1097.     }
  1098.     /*
  1099.      * Last Resort
  1100.      */
  1101.     else {
  1102.         /*
  1103.          * This is none of the elements we know (as yet) but we can try to
  1104.          * click there anyway.
  1105.          */
  1106.         emulateClickOn( curElement );
  1107.     }
  1108.     /*
  1109.      * Return a notification value if there was no link detected so that
  1110.      * Jumanji may get out of its "follow" mode.
  1111.      */
  1112.     if (clickedNotification != "")
  1113.         return clickedNotification;
  1114.     else
  1115.         return;
  1116. }
  1117. /*
  1118.  * Navigate Element Links
  1119.  * ======================
  1120.  */
  1121. /*
  1122.  * Switch raw position mode
  1123.  * ------------------------
  1124.  * This is meant to adjust a position reporting problem in webkit(?, not really sure).
  1125.  */
  1126. function switchRawPosition() {
  1127.     rawPosition = ! rawPosition;
  1128.  
  1129.     removeOverlays();
  1130.     createElementOverlays();
  1131.     createLabelOverlays();
  1132.  
  1133.     if (navigationMode)
  1134.         showElementOverlays();
  1135.     else
  1136.         showLabelOverlays( curLabelHead );
  1137. }
  1138. /*
  1139.  * Switch display mode
  1140.  * -------------------
  1141.  */
  1142. function switchDisplayMode() {
  1143.     if (displayMode == 0)
  1144.         displayMode = 1;
  1145.     else
  1146.         displayMode = 0;
  1147.  
  1148.     switchElementsDisplay();
  1149.     switchLabelsDisplay();
  1150.  
  1151.     if (navigationMode)
  1152.         showElementOverlays();
  1153.     else
  1154.         showLabelOverlays( curLabelHead );
  1155. }
  1156. /*
  1157.  * Go to next node
  1158.  * ---------------
  1159.  */
  1160. function goNextNode() {
  1161.     normalElementDisplay( curLabelNum );
  1162.     curLabelNum++;
  1163.     if (curLabelNum >= elementsOverlays.length)
  1164.         curLabelNum = 0;
  1165.     enhanceElementDisplay( curLabelNum );
  1166. }
  1167. /*
  1168.  * Go to previous node
  1169.  * ---------------
  1170.  */
  1171. function goPreviousNode() {
  1172.     normalElementDisplay( curLabelNum );
  1173.     curLabelNum--;
  1174.     if (curLabelNum < 0)
  1175.         curLabelNum = elementsOverlays.length - 1;
  1176.     enhanceElementDisplay( curLabelNum );
  1177. }
  1178. /*
  1179.  ******************************
  1180.  *** Jumanji Main Interface ***
  1181.  ******************************
  1182.  */
  1183. /*
  1184.  * Start Navigating
  1185.  * ================
  1186.  */
  1187. function show_hints() {
  1188.     initCollSequence();
  1189.  
  1190.     if (hasLinkNodes) {
  1191.         clearLinkInfo();
  1192.     }
  1193.     findClickableNodes();
  1194.     if (hasLinkNodes) {
  1195.         createElementOverlays();
  1196.         createLabelOverlays();
  1197.         showLabelOverlays("");
  1198.     } else {
  1199.         alert( "No clickable node found on this page." );
  1200.     }
  1201. }
  1202. /*
  1203.  * Navigate the hinted elements
  1204.  * ============================
  1205.  * @param   labelHead   head string of the label group to display
  1206.  * @return  URI of page to select if no action taken here
  1207.  *          else nothing
  1208.  */
  1209. function update_hints( labelHead ) {
  1210.     /*
  1211.      * Check if modes should be switched.
  1212.      */
  1213.     if (labelHead.length > 0) {
  1214.         if (! navigationMode) {
  1215.             if (labelHead == "00" ||
  1216.                 labelHead.charAt(labelHead.length-1) == modeSwitchChar) {
  1217.                 navigationMode = true;
  1218.                 hideLabelOverlays();
  1219.                 /*
  1220.                  * Make sure a selectable label will be displayed.
  1221.                  */
  1222.                 if (curLabelNum < 0)
  1223.                     curLabelNum = 0;
  1224.             }
  1225.         }
  1226.         else {
  1227.             curNavCommand = labelHead.charAt(labelHead.length-1);
  1228.             if (curNavCommand == modeSwitchChar) {
  1229.                 navigationMode = false;
  1230.                 hideElementOverlays();
  1231.                 showLabelOverlays( curLabelHead );
  1232.                 return clearBufferNotification;
  1233.             }
  1234.         }
  1235.     }
  1236.     else {
  1237.         curNavCommand = "";
  1238.     }
  1239.     /*
  1240.      * Continue navigation in the selected mode.
  1241.      */
  1242.     if (! navigationMode) {
  1243.         curLabelHead = labelHead;
  1244.         showLabelOverlays( labelHead );
  1245.     } else {
  1246.         showElementOverlays();
  1247.         /*
  1248.          * Evaluate the navigation commands.
  1249.          */
  1250.         switch (curNavCommand) {
  1251.         case navNextNode:
  1252.             goNextNode();
  1253.             break;
  1254.         case navPreviousNode:
  1255.             goPreviousNode();
  1256.             break;
  1257.         case displaySwitchChar:
  1258.             switchDisplayMode();
  1259.             break;
  1260.         case switchRawChar:
  1261.             switchRawPosition();
  1262.             break;
  1263.         case navSelectNode:
  1264.             matchingLabelNum = curLabelNum;
  1265.             break;
  1266.         }
  1267. //        if (curNavCommand == navNextNode)
  1268. //            goNextNode();
  1269. //        else if (curNavCommand == navPreviousNode)
  1270. //            goPreviousNode();
  1271. //        else if (curNavCommand == displaySwitchChar)
  1272. //            switchDisplayMode();
  1273. //        else if (curNavCommand == switchRawChar)
  1274. //            switchRawPosition();
  1275. //        else if (curNavCommand == navSelectNode)
  1276. //            matchingLabelNum = curLabelNum;
  1277.     }
  1278.     /*
  1279.      * Emulate the mouse action if we found a matching label.
  1280.      */
  1281.     if (matchingLabelNum != -1) {
  1282.         var result=clickLabel( matchingLabelNum );
  1283.         if (result) {
  1284.             return result;
  1285.         }
  1286.     }
  1287.     else if (navigationMode) {
  1288.         return clearBufferNotification;
  1289.     }
  1290. }
  1291. /*
  1292.  * Stop navigating and clear everything
  1293.  * ====================================
  1294.  */
  1295. function clear() {
  1296.     if (navigationMode) {
  1297.         hideElementOverlays();
  1298.     } else {
  1299.         hideLabelOverlays();
  1300.     }
  1301.     clearLinkInfo();
  1302. }
Advertisement
Add Comment
Please, Sign In to add comment