Merge "Avoid theoretical division by zero"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sun, 17 Aug 2014 19:51:07 +0000 (19:51 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sun, 17 Aug 2014 19:51:07 +0000 (19:51 +0000)
1  2 
resources/src/jquery/jquery.suggestions.js

   *            Type: Number, Range: 1 - 100, Default: 7
   * delay: Number of ms to wait for the user to stop typing
   *            Type: Number, Range: 0 - 1200, Default: 120
 + * cache: Whether to cache results from a fetch
 + *            Type: Boolean, Default: false
 + * cacheMaxAge: Number of ms to cache results from a fetch
 + *            Type: Number, Range: 1 - Infinity, Default: 60000 (1 minute)
   * submitOnClick: Whether to submit the form containing the textbox when a suggestion is clicked
   *            Type: Boolean, Default: false
   * maxExpandFactor: Maximum suggestions box width relative to the textbox width. If set
@@@ -51,8 -47,6 +51,8 @@@
   */
  ( function ( $ ) {
  
 +var hasOwn = Object.hasOwnProperty;
 +
  $.suggestions = {
        /**
         * Cancel any delayed maybeFetch() call and callback the context so
         */
        update: function ( context, delayed ) {
                function maybeFetch() {
 +                      var val = context.data.$textbox.val(),
 +                              cache = context.data.cache,
 +                              cacheHit;
 +
                        // Only fetch if the value in the textbox changed and is not empty, or if the results were hidden
                        // if the textbox is empty then clear the result div, but leave other settings intouched
 -                      if ( context.data.$textbox.val().length === 0 ) {
 +                      if ( val.length === 0 ) {
                                $.suggestions.hide( context );
                                context.data.prevText = '';
                        } else if (
 -                              context.data.$textbox.val() !== context.data.prevText ||
 +                              val !== context.data.prevText ||
                                !context.data.$container.is( ':visible' )
                        ) {
 -                              if ( typeof context.config.fetch === 'function' ) {
 -                                      context.data.prevText = context.data.$textbox.val();
 -                                      context.config.fetch.call( context.data.$textbox, context.data.$textbox.val() );
 +                              context.data.prevText = val;
 +                              // Try cache first
 +                              if ( context.config.cache && hasOwn.call( cache, val ) ) {
 +                                      if ( +new Date() - cache[ val ].timestamp < context.config.cacheMaxAge ) {
 +                                              context.data.$textbox.suggestions( 'suggestions', cache[ val ].suggestions );
 +                                              cacheHit = true;
 +                                      } else {
 +                                              // Cache expired
 +                                              delete cache[ val ];
 +                                      }
 +                              }
 +                              if ( !cacheHit && typeof context.config.fetch === 'function' ) {
 +                                      context.config.fetch.call(
 +                                              context.data.$textbox,
 +                                              val,
 +                                              function ( suggestions ) {
 +                                                      context.data.$textbox.suggestions( 'suggestions', suggestions );
 +                                                      if ( context.config.cache ) {
 +                                                              cache[ val ] = {
 +                                                                      suggestions: suggestions,
 +                                                                      timestamp: +new Date()
 +                                                              };
 +                                                      }
 +                                              }
 +                                      );
                                }
                        }
  
                                                                } else {
                                                                        regionWidth = $region.outerWidth();
                                                                        docWidth = $( document ).width();
-                                                                       if ( ( regionWidth / docWidth  ) > 0.85 ) {
+                                                                       if ( regionWidth > ( 0.85 * docWidth ) ) {
                                                                                // If the input size takes up more than 85% of the document horizontally
                                                                                // expand the suggestions to the writing direction's native end.
                                                                                expandFrom = 'start';
                                                                                // Calculate the center points of the input and document
                                                                                regionCenter = $region.offset().left + regionWidth / 2;
                                                                                docCenter = docWidth / 2;
-                                                                               if ( Math.abs( regionCenter - docCenter ) / docCenter < 0.10 ) {
+                                                                               if ( Math.abs( regionCenter - docCenter ) < ( 0.10 * docCenter ) ) {
                                                                                        // If the input's center is within 10% of the document center
                                                                                        // use the writing direction's native end.
                                                                                        expandFrom = 'start';
                                                        }
  
                                                        // Widen results box if needed (new width is only calculated here, applied later).
 -                                                      // We need this awful hack to calculate the actual pre-ellipsis width.
 +
 +                                                      // The monstrosity below accomplishes two things:
 +                                                      // * Wraps the text contents in a DOM element, so that we can know its width. There is
 +                                                      //   no way to directly access the width of a text node, and we can't use the parent
 +                                                      //   node width as it has text-overflow: ellipsis; and overflow: hidden; applied to
 +                                                      //   it, which trims it to a smaller width.
 +                                                      // * Temporarily applies position: absolute; to the wrapper to pull it out of normal
 +                                                      //   document flow. Otherwise the CSS text-overflow: ellipsis; and overflow: hidden;
 +                                                      //   rules would cause some browsers (at least all versions of IE from 6 to 11) to
 +                                                      //   still report the "trimmed" width. This should not be done in regular CSS
 +                                                      //   stylesheets as we don't want this rule to apply to other <span> elements, like
 +                                                      //   the ones generated by jquery.highlightText.
                                                        $spanForWidth = $result.wrapInner( '<span>' ).children();
 -                                                      childrenWidth = $spanForWidth.outerWidth();
 +                                                      childrenWidth = $spanForWidth.css( 'position', 'absolute' ).outerWidth();
                                                        $spanForWidth.contents().unwrap();
 +
                                                        if ( childrenWidth > $result.width() && childrenWidth > expWidth ) {
                                                                // factor in any padding, margin, or border space on the parent
                                                                expWidth = childrenWidth + ( context.data.$container.width() - $result.width() );
                        case 'delay':
                                context.config[property] = Math.max( 0, Math.min( 1200, value ) );
                                break;
 +                      case 'cacheMaxAge':
 +                              context.config[property] = Math.max( 1, value );
 +                              break;
                        case 'maxExpandFactor':
                                context.config[property] = Math.max( 1, value );
                                break;
 +                      case 'cache':
                        case 'submitOnClick':
                        case 'positionFromLeft':
                        case 'highlightInput':
 -                              context.config[property] = value ? true : false;
 +                              context.config[property] = !!value;
                                break;
                }
        },
@@@ -517,8 -469,6 +517,8 @@@ $.fn.suggestions = function () 
                                        suggestions: [],
                                        maxRows: 7,
                                        delay: 120,
 +                                      cache: false,
 +                                      cacheMaxAge: 60000,
                                        submitOnClick: false,
                                        maxExpandFactor: 3,
                                        expandFrom: 'auto',
                                // Text in textbox when suggestions were last fetched
                                prevText: null,
  
 +                              // Cache of fetched suggestions
 +                              cache: {},
 +
                                // Number of results visible without scrolling
                                visibleResults: 0,