<commit_before>/**
 * Manager
 */
( function ( window, factory ) {

    if ( typeof define === 'function' && define.amd ) {

        define( [
            'jquery',
            './Util',
            './Config',
            './Inventory'
        ], function ( $, Util, Config, Inventory ) {
            return factory( window, $, Util, Config, Inventory );
        } );

    } else if ( typeof exports === 'object' ) {

        module.exports = factory(
            window,
            require( 'jquery' ),
            require( './Util' ),
            require( './Config' ),
            require( './Inventory' )
        );

    } else {

        window.AdManager = window.AdManager || {};

        window.AdManager.Manager = factory(
            window,
            window.jQuery,
            window.AdManager.Util,
            window.AdManager.Config,
            window.AdManager.Inventory
        );

    }

} ( window, function ( window, $, Util, Config, Inventory ) {

    var name = 'Manager',
        debugEnabled = true,
        debug = debugEnabled ? Util.debug : function () {},
        libraryLoaded = false,
        definedSlots = [],
        pagePositions = [],
        inventory = [],
        account = null,
        adSelector = '';

    //////////////////////////////////////////////////////////////////////////////////////

    function init() {

        if ( ! isEnabled() ) {
            return;
        }

        inventory = Inventory.getInventory();
        account = Config.get( 'account' );
        adSelector = Config.get( 'adSelector' );

        bindHandlers();
        loadLibrary();

    }

    /**
     * Bind to custom jQuery events
     */
    function bindHandlers() {

        $( document )
            .on( 'GPT:unitsInserted', function ( event ) {} )
            .on( 'GPT:libraryLoaded', function ( event ) {
                libraryLoaded = true;
                initSequence();
            } )
            .on( 'GPT:slotsDefined', function ( event ) {
                displayPageAds();
            } );

    }

    function initSequence() {

        $.event.trigger( 'GPT:initSequence' );

        listenForDfpEvents();
        enableSingleRequest();
        setTargeting();
        setPagePositions();
        defineSlotsForPagePositions();

    }

    /**
     * Request GPT library
     */
    function loadLibrary() {

        if ( libraryLoaded ) {
            return onLibraryLoaded();
        }

        var googletag,
            gads,
            useSSL,
            node,
            readyStateLoaded = false
        ;

        window.googletag = window.googletag || {};
        window.googletag.cmd = window.googletag.cmd || [];

        gads = document.createElement( 'script' );
        gads.async = true;
        gads.type = 'text/javascript';
        useSSL = 'https:' == document.location.protocol;
        gads.src = ( useSSL ? 'https:' : 'http:' ) + '//www.googletagservices.com/tag/js/gpt.js';
        if ( gads.addEventListener ) {
            gads.addEventListener( 'load', onLibraryLoaded, false );
        } else if ( gads.readyState ) {
            gads.onreadystatechange = function () { // Legacy IE
                if ( ! readyStateLoaded ) {
                    readyStateLoaded = true;
                    onLibraryLoaded();
                }
            };
        }
        node = document.getElementsByTagName( 'script' )[0];
        node.parentNode.insertBefore( gads, node );

    }

    /**
     * Callback when GPT library is loaded
     */
    function onLibraryLoaded() {

        googletag.cmd.push( function () {
            $.event.trigger( 'GPT:libraryLoaded' );
        } );

    }

    /**
     * Bind to GPT events
     */
    function listenForDfpEvents() {

        googletag.cmd.push( function () {
            googletag.pubads()
                .addEventListener( 'slotRenderEnded', function ( event ) {
                    slotRenderEnded( event );
                } )
            ;
        } );

    }

    /**
     * Enable Batched SRA
     *
     * @uses collapseEmptyDivs()
     * @uses enableSingleRequest()
     * @uses disableInitialLoad()
     */
    function enableSingleRequest() {

        googletag.cmd.push( function () {
            // https://developers.google.com/doubleclick-gpt/reference#googletag.PubAdsService_collapseEmptyDivs
            googletag.pubads().collapseEmptyDivs();

            // https://developers.google.com/doubleclick-gpt/reference#googletag.PubAdsService_enableSingleRequest
            googletag.pubads().enableSingleRequest();

            // https://developers.google.com/doubleclick-gpt/reference#googletag.PubAdsService_disableInitialLoad
            googletag.pubads().disableInitialLoad();
        } );

    }

    /**
     * Send Targeting
     * Defined in Page Config
     */
    function setTargeting() {

        googletag.cmd.push( function () {

            var targeting = Config.get( 'targeting' );

            if ( ! targeting.length ) {
                return;
            }

            $.each( targeting, function ( key, value ) {
                googletag.pubads().setTargeting( key, value );
            } );

        } );

    }

    /**
     * Set Page Positions
     */
    function setPagePositions() {

        var clientType = Config.get( 'clientType' ),
            $context = $( Config.get( 'context' ) ),
            $units = null,
            selector = adSelector
        ;

        if ( clientType !== false ) {
            selector += '[data-client-type="' + clientType + '"]';
        }

        $units = $context.find( selector );

        $units.each( function () {
            var id = $( this ).data( 'id' );
            pagePositions.push( id );
        } );

    }

    /**
     * Define Slots for Page Positions
     */
    function defineSlotsForPagePositions() {

        googletag.cmd.push( function () {

            var currentPosition = null,
                $context = $( Config.get( 'context' ) ),
                $unit,
                $unitTarget;

            for ( var i = 0; i < pagePositions.length; i++ ) {

                Inventory.incrementAdSlot( pagePositions[ i ] );
                currentPosition = Inventory.getAdInfo( pagePositions[ i ] );

                if ( typeof currentPosition.id == 'undefined' ) {
                    continue;
                }

                // find the empty container div on the page. we
                // will dynamically instantiate the unique ad unit.
                $unit = $context.find( adSelector + '[data-id="' + currentPosition.id + '"]' );
                $unitTarget = $( '<div />' );

                if ( $unit.length < 1 ) {
                    continue;
                }

                // generate new div
                $unitTarget.addClass( Config.get( 'adUnitTargetClass' ) );
                $unitTarget.attr( 'id', currentPosition.idName );
                $unit.append( $unitTarget );

                // activate
                $unit.addClass( 'active' );

                definedSlots[ i ] = googletag
                    .defineSlot(
                        '/' + account + '/' + currentPosition.slot,
                        currentPosition.sizes,
                        currentPosition.idName
                     )
                    .addService( googletag.pubads() )
                ;

            }

            // Enables GPT services for defined slots
            googletag.enableServices();

            $.event.trigger( 'GPT:slotsDefined' );

        } );

    }

    function displayPageAds() {

        googletag.cmd.push( function () {

            // Fetch and display ads for definedSlots
            googletag.pubads().refresh( definedSlots );

            // lastly, run display code
            for ( var i = 0; i < pagePositions.length; i++ ) {

                currentPosition = Inventory.getAdInfo( pagePositions[ i ] );

                if ( $( '#' + currentPosition.idName ).length ) {
                    googletag.display( currentPosition.idName );
                }

            }

        } );

    }

    /**
     * slotRenderEnded - callback after unit is rendered
     *
     * @see https://developers.google.com/doubleclick-gpt/reference
     * @param object unit
     */
    function slotRenderEnded( unit ) {

        var unitName = unit.slot.getAdUnitPath().replace( '/' + account + '/', '' ),
            adInfo = Inventory.getAdInfo( unitName )
        ;

        $.event.trigger( 'GPT:adUnitRendered', {
            name: unitName,
            id: adInfo.id,
            size: unit.size,
            isEmpty: unit.isEmpty,
            creativeId: unit.creativeId,
            lineItemId: unit.lineItemId,
            serviceName: unit.serviceName
        } );

    }

    /**
     * getDefinedSlot
     *
     * @param string name
     * @return object definedSlot
     */
    function getDefinedSlot( name ) {

        var definedSlot = null;

        $.each( definedSlots, function ( i, slot ) {
            var unitName = slot.getAdUnitPath().replace( '/' + account + '/', '' );
            if ( unitName === name ) {
                definedSlot = slot;
                return false;
            }
        } );

        return definedSlot;

    }

    /**
     * displaySlot
     *
     * @param string unit [type or slot]
     */
    function displaySlot( unit ) {

        googletag.cmd.push( function () {
            var position = Inventory.getAdInfo( unit ),
                slot = getDefinedSlot( position.slot )
            ;
            googletag.pubads().refresh( [ slot ] );
            googletag.display( position.idName );
            definedSlots = Inventory.removeDefinedSlot( definedSlots, position.slot );
        } );

    }

    /**
     * Check if the Ad Manager is enabled for page
     *
     * @return bool
     */
    function isEnabled() {

        return Config.get( 'enabled' );

    }

    /**
     * Empties all ads in a given context
     *
     * @param object options.$context
     * @param bool   options.removeContainer
     */
    function emptyAds( options ) {

        var $context = options.$context,
            removeContainer = options.removeContainer || false;

        $context.find( adSelector ).empty();

        if ( removeContainer ) {
            $context.find( adSelector ).remove();
        }

    }

    //////////////////////////////////////////////////////////////////////////////////////

    return {
        init:         init,
        isEnabled:    isEnabled,
        displaySlot:  displaySlot,
        initSequence: initSequence,
        emptyAds:     emptyAds
    };

} ) );<commit_msg>Align object for readability<commit_after>/**
 * Manager
 */
( function ( window, factory ) {

    if ( typeof define === 'function' && define.amd ) {

        define( [
            'jquery',
            './Util',
            './Config',
            './Inventory'
        ], function ( $, Util, Config, Inventory ) {
            return factory( window, $, Util, Config, Inventory );
        } );

    } else if ( typeof exports === 'object' ) {

        module.exports = factory(
            window,
            require( 'jquery' ),
            require( './Util' ),
            require( './Config' ),
            require( './Inventory' )
        );

    } else {

        window.AdManager = window.AdManager || {};

        window.AdManager.Manager = factory(
            window,
            window.jQuery,
            window.AdManager.Util,
            window.AdManager.Config,
            window.AdManager.Inventory
        );

    }

} ( window, function ( window, $, Util, Config, Inventory ) {

    var name = 'Manager',
        debugEnabled = true,
        debug = debugEnabled ? Util.debug : function () {},
        libraryLoaded = false,
        definedSlots = [],
        pagePositions = [],
        inventory = [],
        account = null,
        adSelector = '';

    //////////////////////////////////////////////////////////////////////////////////////

    function init() {

        if ( ! isEnabled() ) {
            return;
        }

        inventory = Inventory.getInventory();
        account = Config.get( 'account' );
        adSelector = Config.get( 'adSelector' );

        bindHandlers();
        loadLibrary();

    }

    /**
     * Bind to custom jQuery events
     */
    function bindHandlers() {

        $( document )
            .on( 'GPT:unitsInserted', function ( event ) {} )
            .on( 'GPT:libraryLoaded', function ( event ) {
                libraryLoaded = true;
                initSequence();
            } )
            .on( 'GPT:slotsDefined', function ( event ) {
                displayPageAds();
            } );

    }

    function initSequence() {

        $.event.trigger( 'GPT:initSequence' );

        listenForDfpEvents();
        enableSingleRequest();
        setTargeting();
        setPagePositions();
        defineSlotsForPagePositions();

    }

    /**
     * Request GPT library
     */
    function loadLibrary() {

        if ( libraryLoaded ) {
            return onLibraryLoaded();
        }

        var googletag,
            gads,
            useSSL,
            node,
            readyStateLoaded = false
        ;

        window.googletag = window.googletag || {};
        window.googletag.cmd = window.googletag.cmd || [];

        gads = document.createElement( 'script' );
        gads.async = true;
        gads.type = 'text/javascript';
        useSSL = 'https:' == document.location.protocol;
        gads.src = ( useSSL ? 'https:' : 'http:' ) + '//www.googletagservices.com/tag/js/gpt.js';
        if ( gads.addEventListener ) {
            gads.addEventListener( 'load', onLibraryLoaded, false );
        } else if ( gads.readyState ) {
            gads.onreadystatechange = function () { // Legacy IE
                if ( ! readyStateLoaded ) {
                    readyStateLoaded = true;
                    onLibraryLoaded();
                }
            };
        }
        node = document.getElementsByTagName( 'script' )[0];
        node.parentNode.insertBefore( gads, node );

    }

    /**
     * Callback when GPT library is loaded
     */
    function onLibraryLoaded() {

        googletag.cmd.push( function () {
            $.event.trigger( 'GPT:libraryLoaded' );
        } );

    }

    /**
     * Bind to GPT events
     */
    function listenForDfpEvents() {

        googletag.cmd.push( function () {
            googletag.pubads()
                .addEventListener( 'slotRenderEnded', function ( event ) {
                    slotRenderEnded( event );
                } )
            ;
        } );

    }

    /**
     * Enable Batched SRA
     *
     * @uses collapseEmptyDivs()
     * @uses enableSingleRequest()
     * @uses disableInitialLoad()
     */
    function enableSingleRequest() {

        googletag.cmd.push( function () {
            // https://developers.google.com/doubleclick-gpt/reference#googletag.PubAdsService_collapseEmptyDivs
            googletag.pubads().collapseEmptyDivs();

            // https://developers.google.com/doubleclick-gpt/reference#googletag.PubAdsService_enableSingleRequest
            googletag.pubads().enableSingleRequest();

            // https://developers.google.com/doubleclick-gpt/reference#googletag.PubAdsService_disableInitialLoad
            googletag.pubads().disableInitialLoad();
        } );

    }

    /**
     * Send Targeting
     * Defined in Page Config
     */
    function setTargeting() {

        googletag.cmd.push( function () {

            var targeting = Config.get( 'targeting' );

            if ( ! targeting.length ) {
                return;
            }

            $.each( targeting, function ( key, value ) {
                googletag.pubads().setTargeting( key, value );
            } );

        } );

    }

    /**
     * Set Page Positions
     */
    function setPagePositions() {

        var clientType = Config.get( 'clientType' ),
            $context = $( Config.get( 'context' ) ),
            $units = null,
            selector = adSelector
        ;

        if ( clientType !== false ) {
            selector += '[data-client-type="' + clientType + '"]';
        }

        $units = $context.find( selector );

        $units.each( function () {
            var id = $( this ).data( 'id' );
            pagePositions.push( id );
        } );

    }

    /**
     * Define Slots for Page Positions
     */
    function defineSlotsForPagePositions() {

        googletag.cmd.push( function () {

            var currentPosition = null,
                $context = $( Config.get( 'context' ) ),
                $unit,
                $unitTarget;

            for ( var i = 0; i < pagePositions.length; i++ ) {

                Inventory.incrementAdSlot( pagePositions[ i ] );
                currentPosition = Inventory.getAdInfo( pagePositions[ i ] );

                if ( typeof currentPosition.id == 'undefined' ) {
                    continue;
                }

                // find the empty container div on the page. we
                // will dynamically instantiate the unique ad unit.
                $unit = $context.find( adSelector + '[data-id="' + currentPosition.id + '"]' );
                $unitTarget = $( '<div />' );

                if ( $unit.length < 1 ) {
                    continue;
                }

                // generate new div
                $unitTarget.addClass( Config.get( 'adUnitTargetClass' ) );
                $unitTarget.attr( 'id', currentPosition.idName );
                $unit.append( $unitTarget );

                // activate
                $unit.addClass( 'active' );

                definedSlots[ i ] = googletag
                    .defineSlot(
                        '/' + account + '/' + currentPosition.slot,
                        currentPosition.sizes,
                        currentPosition.idName
                     )
                    .addService( googletag.pubads() )
                ;

            }

            // Enables GPT services for defined slots
            googletag.enableServices();

            $.event.trigger( 'GPT:slotsDefined' );

        } );

    }

    function displayPageAds() {

        googletag.cmd.push( function () {

            // Fetch and display ads for definedSlots
            googletag.pubads().refresh( definedSlots );

            // lastly, run display code
            for ( var i = 0; i < pagePositions.length; i++ ) {

                currentPosition = Inventory.getAdInfo( pagePositions[ i ] );

                if ( $( '#' + currentPosition.idName ).length ) {
                    googletag.display( currentPosition.idName );
                }

            }

        } );

    }

    /**
     * slotRenderEnded - callback after unit is rendered
     *
     * @see https://developers.google.com/doubleclick-gpt/reference
     * @param object unit
     */
    function slotRenderEnded( unit ) {

        var unitName = unit.slot.getAdUnitPath().replace( '/' + account + '/', '' ),
            adInfo = Inventory.getAdInfo( unitName )
        ;

        $.event.trigger( 'GPT:adUnitRendered', {
            name:        unitName,
            id:          adInfo.id,
            size:        unit.size,
            isEmpty:     unit.isEmpty,
            creativeId:  unit.creativeId,
            lineItemId:  unit.lineItemId,
            serviceName: unit.serviceName
        } );

    }

    /**
     * getDefinedSlot
     *
     * @param string name
     * @return object definedSlot
     */
    function getDefinedSlot( name ) {

        var definedSlot = null;

        $.each( definedSlots, function ( i, slot ) {
            var unitName = slot.getAdUnitPath().replace( '/' + account + '/', '' );
            if ( unitName === name ) {
                definedSlot = slot;
                return false;
            }
        } );

        return definedSlot;

    }

    /**
     * displaySlot
     *
     * @param string unit [type or slot]
     */
    function displaySlot( unit ) {

        googletag.cmd.push( function () {
            var position = Inventory.getAdInfo( unit ),
                slot = getDefinedSlot( position.slot )
            ;
            googletag.pubads().refresh( [ slot ] );
            googletag.display( position.idName );
            definedSlots = Inventory.removeDefinedSlot( definedSlots, position.slot );
        } );

    }

    /**
     * Check if the Ad Manager is enabled for page
     *
     * @return bool
     */
    function isEnabled() {

        return Config.get( 'enabled' );

    }

    /**
     * Empties all ads in a given context
     *
     * @param object options.$context
     * @param bool   options.removeContainer
     */
    function emptyAds( options ) {

        var $context = options.$context,
            removeContainer = options.removeContainer || false;

        $context.find( adSelector ).empty();

        if ( removeContainer ) {
            $context.find( adSelector ).remove();
        }

    }

    //////////////////////////////////////////////////////////////////////////////////////

    return {
        init:         init,
        isEnabled:    isEnabled,
        displaySlot:  displaySlot,
        initSequence: initSequence,
        emptyAds:     emptyAds
    };

} ) );<|endoftext|>


<commit_before>import InfiniteLoader, { forceUpdateReactVirtualizedComponent, isRangeVisible, scanForUnloadedRanges } from './InfiniteLoader'
import React, { Component } from 'react'
import List from '../List'
import { render } from '../TestUtils'

describe('InfiniteLoader', () => {
  let innerOnRowsRendered
  let isRowLoadedCalls = []
  let isRowLoadedMap = {}
  let loadMoreRowsCalls = []
  let rowRendererCalls = []

  beforeEach(() => {
    isRowLoadedCalls = []
    isRowLoadedMap = {}
    loadMoreRowsCalls = []
    rowRendererCalls = []
  })

  function defaultIsRowLoaded ({ index }) {
    isRowLoadedCalls.push(index)
    return !!isRowLoadedMap[index]
  }

  function defaultLoadMoreRows ({ startIndex, stopIndex }) {
    loadMoreRowsCalls.push({ startIndex, stopIndex })
  }

  function rowRenderer ({ index, key, style }) {
    rowRendererCalls.push(index)
    return (

    )
  }

  function getMarkup ({
    height = 100,
    isRowLoaded = defaultIsRowLoaded,
    loadMoreRows = defaultLoadMoreRows,
    minimumBatchSize = 1,
    rowHeight = 20,
    rowCount = 100,
    scrollToIndex,
    threshold = 10,
    width = 200
  } = {}) {
    return (
      <InfiniteLoader
        isRowLoaded={isRowLoaded}
        loadMoreRows={loadMoreRows}
        minimumBatchSize={minimumBatchSize}
        rowCount={rowCount}
        threshold={threshold}
      >
        {({ onRowsRendered, registerChild }) => {
          innerOnRowsRendered = onRowsRendered

          return (
            <List
              ref={registerChild}
              height={height}
              onRowsRendered={onRowsRendered}
              rowHeight={rowHeight}
              rowRenderer={rowRenderer}
              rowCount={rowCount}
              scrollToIndex={scrollToIndex}
              width={width}
            />
          )
        }}
      </InfiniteLoader>
    )
  }

  it('should call :isRowLoaded for all rows within the threshold each time a range of rows are rendered', () => {
    render(getMarkup())
    expect(isRowLoadedCalls).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
  })

  it('should call :isRowLoaded for all rows within the rowCount each time a range of rows are rendered', () => {
    render(getMarkup({ rowCount: 10 }))
    expect(isRowLoadedCalls).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
  })

  it('should call :loadMoreRows for unloaded rows within the threshold', () => {
    render(getMarkup())
    expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 14 }])
  })

  it('should call :loadMoreRows for unloaded rows within the rowCount', () => {
    render(getMarkup({ rowCount: 10 }))
    expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 9 }])
  })

  it('should :forceUpdate once rows have loaded if :loadMoreRows returns a Promise', async (done) => {
    let savedResolve
    function loadMoreRows () {
      return new Promise((resolve) => {
        savedResolve = resolve
      })
    }
    render(getMarkup({ loadMoreRows }))
    rowRendererCalls.splice(0)
    await savedResolve()
    expect(rowRendererCalls.length > 0).toEqual(true)
    done()
  })

  it('should not :forceUpdate once rows have loaded rows are no longer visible', async (done) => {
    let resolves = []
    function loadMoreRows () {
      return new Promise((resolve) => {
        resolves.push(resolve)
      })
    }
    render(getMarkup({ loadMoreRows }))
    // Simulate a new range of rows being loaded
    innerOnRowsRendered({ startIndex: 100, stopIndex: 101 })
    rowRendererCalls.splice(0)
    await resolves[0]() // Resolve the first request only, not the simulated row-change
    expect(rowRendererCalls.length).toEqual(0)
    done()
  })

  describe('minimumBatchSize', () => {
    it('should respect the specified :minimumBatchSize when scrolling down', () => {
      render(getMarkup({
        minimumBatchSize: 10,
        threshold: 0
      }))
      expect(loadMoreRowsCalls.length).toEqual(1)
      expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 9 }])
    })

    it('should respect the specified :minimumBatchSize when scrolling up', () => {
      render(getMarkup({
        minimumBatchSize: 10,
        scrollToIndex: 20,
        threshold: 0
      }))
      loadMoreRowsCalls.splice(0)
      render(getMarkup({
        isRowLoaded: ({ index }) => index >= 20,
        minimumBatchSize: 10,
        scrollToIndex: 15,
        threshold: 0
      }))
      expect(loadMoreRowsCalls.length).toEqual(1)
      expect(loadMoreRowsCalls).toEqual([{ startIndex: 10, stopIndex: 19 }])
    })

    it('should not interfere with :threshold', () => {
      render(getMarkup({
        minimumBatchSize: 10,
        threshold: 10
      }))
      expect(loadMoreRowsCalls.length).toEqual(1)
      expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 14 }])
    })

    it('should respect the specified :minimumBatchSize if a user scrolls past the previous range', () => {
      const isRowLoadedIndices = {}

      function isRowLoaded ({ index }) {
        if (!isRowLoadedIndices[index]) {
          isRowLoadedIndices[index] = true

          return false
        } else {
          return true
        }
      }

      render(getMarkup({
        isRowLoaded,
        minimumBatchSize: 10,
        threshold: 0
      }))
      // Simulate a new range of rows being loaded
      innerOnRowsRendered({ startIndex: 5, stopIndex: 10 })
      expect(loadMoreRowsCalls).toEqual([
        { startIndex: 0, stopIndex: 9 },
        { startIndex: 10, stopIndex: 19 }
      ])
    })

    it('should not exceed ending boundaries if :minimumBatchSize is larger than needed', () => {
      render(getMarkup({
        minimumBatchSize: 10,
        rowCount: 25,
        threshold: 0
      }))
      // Simulate a new range of rows being loaded
      innerOnRowsRendered({ startIndex: 18, stopIndex: 22 })
      expect(loadMoreRowsCalls).toEqual([
        { startIndex: 0, stopIndex: 9 },
        { startIndex: 15, stopIndex: 24 }
      ])
    })

    it('should not exceed beginning boundaries if :minimumBatchSize is larger than needed', () => {
      render(getMarkup({
        minimumBatchSize: 10,
        scrollToIndex: 15,
        threshold: 0
      }))
      loadMoreRowsCalls.splice(0)
      render(getMarkup({
        isRowLoaded: ({ index }) => index >= 6,
        minimumBatchSize: 10,
        scrollToIndex: 2,
        threshold: 0
      }))
      expect(loadMoreRowsCalls.length).toEqual(1)
      expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 5 }])
    })
  })

  // Verifies improved memoization; see bvaughn/react-virtualized/issues/345
  it('should memoize calls to :loadMoreRows (not calling unless unloaded ranges have changed)', () => {
    render(getMarkup({
      isRowLoaded: () => false,
      minimumBatchSize: 20,
      threshold: 0
    }))
    expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 19 }])
    innerOnRowsRendered({ startIndex: 0, stopIndex: 15 })
    expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 19 }])
    loadMoreRowsCalls.splice(0)
    innerOnRowsRendered({ startIndex: 0, stopIndex: 20 })
    expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 20 }])
  })

  it('resetLoadMoreRowsCache should reset memoized state', () => {
    const component = render(getMarkup({
      isRowLoaded: () => false,
      minimumBatchSize: 20,
      threshold: 0
    }))
    expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 19 }])
    innerOnRowsRendered({ startIndex: 0, stopIndex: 15 })
    loadMoreRowsCalls.splice(0)
    expect(loadMoreRowsCalls).toEqual([])
    component.resetLoadMoreRowsCache()
    innerOnRowsRendered({ startIndex: 0, stopIndex: 15 })
    expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 19 }])
  })
})

describe('scanForUnloadedRanges', () => {
  function createIsRowLoaded (rows) {
    return ({ index }) => rows[index]
  }

  it('should return an empty array for a range of rows that have all been loaded', () => {
    expect(scanForUnloadedRanges({
      isRowLoaded: createIsRowLoaded([true, true, true]),
      startIndex: 0,
      stopIndex: 2
    })).toEqual([])
  })

  it('return a range of only 1 unloaded row', () => {
    expect(scanForUnloadedRanges({
      isRowLoaded: createIsRowLoaded([true, false, true]),
      startIndex: 0,
      stopIndex: 2
    })).toEqual([
      { startIndex: 1, stopIndex: 1 }
    ])
  })

  it('return a range of multiple unloaded rows', () => {
    expect(scanForUnloadedRanges({
      isRowLoaded: createIsRowLoaded([false, false, true]),
      startIndex: 0,
      stopIndex: 2
    })).toEqual([
      { startIndex: 0, stopIndex: 1 }
    ])
  })

  it('return multiple ranges of unloaded rows', () => {
    expect(scanForUnloadedRanges({
      isRowLoaded: createIsRowLoaded([true, false, false, true, false, true, false]),
      startIndex: 0,
      stopIndex: 6
    })).toEqual([
      { startIndex: 1, stopIndex: 2 },
      { startIndex: 4, stopIndex: 4 },
      { startIndex: 6, stopIndex: 6 }
    ])
  })
})

describe('isRangeVisible', () => {
  it('first row(s) are visible', () => {
    expect(isRangeVisible({
      lastRenderedStartIndex: 10,
      lastRenderedStopIndex: 20,
      startIndex: 20,
      stopIndex: 30
    })).toEqual(true)
  })

  it('last row(s) are visible', () => {
    expect(isRangeVisible({
      lastRenderedStartIndex: 10,
      lastRenderedStopIndex: 20,
      startIndex: 0,
      stopIndex: 10
    })).toEqual(true)
  })

  it('all row(s) are visible', () => {
    expect(isRangeVisible({
      lastRenderedStartIndex: 10,
      lastRenderedStopIndex: 20,
      startIndex: 12,
      stopIndex: 14
    })).toEqual(true)
  })

  it('no row(s) are visible', () => {
    expect(isRangeVisible({
      lastRenderedStartIndex: 10,
      lastRenderedStopIndex: 20,
      startIndex: 0,
      stopIndex: 9
    })).toEqual(false)

    expect(isRangeVisible({
      lastRenderedStartIndex: 10,
      lastRenderedStopIndex: 20,
      startIndex: 21,
      stopIndex: 30
    })).toEqual(false)
  })
})

describe('forceUpdateReactVirtualizedComponent', () => {
  it('should call :recomputeGridSize if defined', () => {
    let recomputedGridSize = false
    class TestComponent extends Component {
      recomputeGridSize () {
        recomputedGridSize = true
      }
      render () {
        return

      }
    }
    forceUpdateReactVirtualizedComponent(
      render(<TestComponent />)
    )
    expect(recomputedGridSize).toEqual(true)
  })

  it('should called :recomputeRowHeights if defined', () => {
    let recomputedRowHeights = false
    class TestComponent extends Component {
      recomputeRowHeights () {
        recomputedRowHeights = true
      }
      render () {
        return

      }
    }
    forceUpdateReactVirtualizedComponent(
      render(<TestComponent />)
    )
    expect(recomputedRowHeights).toEqual(true)
  })

  it('should call :forceUpdate otherwise', () => {
    let forceUpdated = false
    class TestComponent extends Component {
      forceUpdate () {
        forceUpdated = true
      }
      render () {
        return

      }
    }
    forceUpdateReactVirtualizedComponent(
      render(<TestComponent />)
    )
    expect(forceUpdated).toEqual(true)
  })
})
<commit_msg>Updated InfiniteLoader tests slightly for better coverage<commit_after>import InfiniteLoader, { forceUpdateReactVirtualizedComponent, isRangeVisible, scanForUnloadedRanges } from './InfiniteLoader'
import React, { Component } from 'react'
import List from '../List'
import { render } from '../TestUtils'

describe('InfiniteLoader', () => {
  let innerOnRowsRendered
  let isRowLoadedCalls = []
  let isRowLoadedMap = {}
  let loadMoreRowsCalls = []
  let rowRendererCalls = []

  beforeEach(() => {
    isRowLoadedCalls = []
    isRowLoadedMap = {}
    loadMoreRowsCalls = []
    rowRendererCalls = []
  })

  function defaultIsRowLoaded ({ index }) {
    isRowLoadedCalls.push(index)
    return !!isRowLoadedMap[index]
  }

  function defaultLoadMoreRows ({ startIndex, stopIndex }) {
    loadMoreRowsCalls.push({ startIndex, stopIndex })
  }

  function rowRenderer ({ index, key, style }) {
    rowRendererCalls.push(index)
    return (

    )
  }

  function getMarkup ({
    height = 100,
    isRowLoaded = defaultIsRowLoaded,
    loadMoreRows = defaultLoadMoreRows,
    minimumBatchSize = 1,
    rowHeight = 20,
    rowCount = 100,
    scrollToIndex,
    threshold = 10,
    width = 200
  } = {}) {
    return (
      <InfiniteLoader
        isRowLoaded={isRowLoaded}
        loadMoreRows={loadMoreRows}
        minimumBatchSize={minimumBatchSize}
        rowCount={rowCount}
        threshold={threshold}
      >
        {({ onRowsRendered, registerChild }) => {
          innerOnRowsRendered = onRowsRendered

          return (
            <List
              ref={registerChild}
              height={height}
              onRowsRendered={onRowsRendered}
              rowHeight={rowHeight}
              rowRenderer={rowRenderer}
              rowCount={rowCount}
              scrollToIndex={scrollToIndex}
              width={width}
            />
          )
        }}
      </InfiniteLoader>
    )
  }

  it('should call :isRowLoaded for all rows within the threshold each time a range of rows are rendered', () => {
    render(getMarkup())
    expect(isRowLoadedCalls).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
  })

  it('should call :isRowLoaded for all rows within the rowCount each time a range of rows are rendered', () => {
    render(getMarkup({ rowCount: 10 }))
    expect(isRowLoadedCalls).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
  })

  it('should call :loadMoreRows for unloaded rows within the threshold', () => {
    render(getMarkup())
    expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 14 }])
  })

  it('should call :loadMoreRows for unloaded rows within the rowCount', () => {
    render(getMarkup({ rowCount: 10 }))
    expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 9 }])
  })

  it('should :forceUpdate once rows have loaded if :loadMoreRows returns a Promise', async (done) => {
    let savedResolve
    function loadMoreRows () {
      return new Promise((resolve) => {
        savedResolve = resolve
      })
    }
    render(getMarkup({ loadMoreRows }))
    rowRendererCalls.splice(0)
    await savedResolve()
    expect(rowRendererCalls.length > 0).toEqual(true)
    done()
  })

  it('should not :forceUpdate once rows have loaded rows are no longer visible', async (done) => {
    let resolves = []
    function loadMoreRows () {
      return new Promise((resolve) => {
        resolves.push(resolve)
      })
    }
    render(getMarkup({ loadMoreRows }))
    // Simulate a new range of rows being loaded
    innerOnRowsRendered({ startIndex: 100, stopIndex: 101 })
    rowRendererCalls.splice(0)
    await resolves[0]() // Resolve the first request only, not the simulated row-change
    expect(rowRendererCalls.length).toEqual(0)
    done()
  })

  describe('minimumBatchSize', () => {
    it('should respect the specified :minimumBatchSize when scrolling down', () => {
      render(getMarkup({
        minimumBatchSize: 10,
        threshold: 0
      }))
      expect(loadMoreRowsCalls.length).toEqual(1)
      expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 9 }])
    })

    it('should respect the specified :minimumBatchSize when scrolling up', () => {
      render(getMarkup({
        minimumBatchSize: 10,
        scrollToIndex: 20,
        threshold: 0
      }))
      loadMoreRowsCalls.splice(0)
      render(getMarkup({
        isRowLoaded: ({ index }) => index >= 20,
        minimumBatchSize: 10,
        scrollToIndex: 15,
        threshold: 0
      }))
      expect(loadMoreRowsCalls.length).toEqual(1)
      expect(loadMoreRowsCalls).toEqual([{ startIndex: 10, stopIndex: 19 }])
    })

    it('should not interfere with :threshold', () => {
      render(getMarkup({
        minimumBatchSize: 10,
        threshold: 10
      }))
      expect(loadMoreRowsCalls.length).toEqual(1)
      expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 14 }])
    })

    it('should respect the specified :minimumBatchSize if a user scrolls past the previous range', () => {
      const isRowLoadedIndices = {}

      function isRowLoaded ({ index }) {
        if (!isRowLoadedIndices[index]) {
          isRowLoadedIndices[index] = true

          return false
        } else {
          return true
        }
      }

      render(getMarkup({
        isRowLoaded,
        minimumBatchSize: 10,
        threshold: 0
      }))
      // Simulate a new range of rows being loaded
      innerOnRowsRendered({ startIndex: 5, stopIndex: 10 })
      expect(loadMoreRowsCalls).toEqual([
        { startIndex: 0, stopIndex: 9 },
        { startIndex: 10, stopIndex: 19 }
      ])
    })

    it('should not exceed ending boundaries if :minimumBatchSize is larger than needed', () => {
      render(getMarkup({
        minimumBatchSize: 10,
        rowCount: 25,
        threshold: 0
      }))
      // Simulate a new range of rows being loaded
      innerOnRowsRendered({ startIndex: 18, stopIndex: 22 })
      expect(loadMoreRowsCalls).toEqual([
        { startIndex: 0, stopIndex: 9 },
        { startIndex: 15, stopIndex: 24 }
      ])
    })

    it('should not exceed beginning boundaries if :minimumBatchSize is larger than needed', () => {
      render(getMarkup({
        minimumBatchSize: 10,
        scrollToIndex: 15,
        threshold: 0
      }))
      loadMoreRowsCalls.splice(0)
      render(getMarkup({
        isRowLoaded: ({ index }) => index >= 6,
        minimumBatchSize: 10,
        scrollToIndex: 2,
        threshold: 0
      }))
      expect(loadMoreRowsCalls.length).toEqual(1)
      expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 5 }])
    })
  })

  // Verifies improved memoization; see bvaughn/react-virtualized/issues/345
  it('should memoize calls to :loadMoreRows (not calling unless unloaded ranges have changed)', () => {
    render(getMarkup({
      isRowLoaded: () => false,
      minimumBatchSize: 20,
      threshold: 0
    }))
    expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 19 }])
    innerOnRowsRendered({ startIndex: 0, stopIndex: 15 })
    expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 19 }])
    loadMoreRowsCalls.splice(0)
    innerOnRowsRendered({ startIndex: 0, stopIndex: 20 })
    expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 20 }])
  })

  it('resetLoadMoreRowsCache should reset memoized state', () => {
    const component = render(getMarkup({
      isRowLoaded: () => false,
      minimumBatchSize: 20,
      threshold: 0
    }))
    expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 19 }])
    innerOnRowsRendered({ startIndex: 0, stopIndex: 15 })
    loadMoreRowsCalls.splice(0)
    expect(loadMoreRowsCalls).toEqual([])
    component.resetLoadMoreRowsCache()
    innerOnRowsRendered({ startIndex: 0, stopIndex: 15 })
    expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 19 }])
  })
})

describe('scanForUnloadedRanges', () => {
  function createIsRowLoaded (rows) {
    return ({ index }) => rows[index]
  }

  it('should return an empty array for a range of rows that have all been loaded', () => {
    expect(scanForUnloadedRanges({
      isRowLoaded: createIsRowLoaded([true, true, true]),
      startIndex: 0,
      stopIndex: 2
    })).toEqual([])
  })

  it('return a range of only 1 unloaded row', () => {
    expect(scanForUnloadedRanges({
      isRowLoaded: createIsRowLoaded([true, false, true]),
      startIndex: 0,
      stopIndex: 2
    })).toEqual([
      { startIndex: 1, stopIndex: 1 }
    ])
  })

  it('return a range of multiple unloaded rows', () => {
    expect(scanForUnloadedRanges({
      isRowLoaded: createIsRowLoaded([false, false, true]),
      startIndex: 0,
      stopIndex: 2
    })).toEqual([
      { startIndex: 0, stopIndex: 1 }
    ])
  })

  it('return multiple ranges of unloaded rows', () => {
    expect(scanForUnloadedRanges({
      isRowLoaded: createIsRowLoaded([true, false, false, true, false, true, false]),
      startIndex: 0,
      stopIndex: 6
    })).toEqual([
      { startIndex: 1, stopIndex: 2 },
      { startIndex: 4, stopIndex: 4 },
      { startIndex: 6, stopIndex: 6 }
    ])
  })
})

describe('isRangeVisible', () => {
  it('first row(s) are visible', () => {
    expect(isRangeVisible({
      lastRenderedStartIndex: 10,
      lastRenderedStopIndex: 20,
      startIndex: 20,
      stopIndex: 30
    })).toEqual(true)
  })

  it('last row(s) are visible', () => {
    expect(isRangeVisible({
      lastRenderedStartIndex: 10,
      lastRenderedStopIndex: 20,
      startIndex: 0,
      stopIndex: 10
    })).toEqual(true)
  })

  it('all row(s) are visible', () => {
    expect(isRangeVisible({
      lastRenderedStartIndex: 10,
      lastRenderedStopIndex: 20,
      startIndex: 12,
      stopIndex: 14
    })).toEqual(true)
  })

  it('no row(s) are visible', () => {
    expect(isRangeVisible({
      lastRenderedStartIndex: 10,
      lastRenderedStopIndex: 20,
      startIndex: 0,
      stopIndex: 9
    })).toEqual(false)

    expect(isRangeVisible({
      lastRenderedStartIndex: 10,
      lastRenderedStopIndex: 20,
      startIndex: 21,
      stopIndex: 30
    })).toEqual(false)
  })
})

describe('forceUpdateReactVirtualizedComponent', () => {
  it('should call :recomputeGridSize if defined', () => {
    const recomputeGridSize = jest.fn()
    class TestComponent extends Component {
      recomputeGridSize = recomputeGridSize
      render () {
        return

      }
    }
    forceUpdateReactVirtualizedComponent(
      render(<TestComponent />),
      10
    )
    expect(recomputeGridSize).toHaveBeenCalledTimes(1)
    expect(recomputeGridSize).toHaveBeenCalledWith(10)
  })

  it('should called :recomputeRowHeights if defined', () => {
    const recomputeRowHeights = jest.fn()
    class TestComponent extends Component {
      recomputeRowHeights = recomputeRowHeights
      render () {
        return

      }
    }
    forceUpdateReactVirtualizedComponent(
      render(<TestComponent />),
      10
    )
    expect(recomputeRowHeights).toHaveBeenCalledTimes(1)
    expect(recomputeRowHeights).toHaveBeenCalledWith(10)
  })

  it('should call :forceUpdate otherwise', () => {
    const forceUpdate = jest.fn()
    class TestComponent extends Component {
      forceUpdate = forceUpdate
      render () {
        return

      }
    }
    forceUpdateReactVirtualizedComponent(
      render(<TestComponent />),
      10
    )
    expect(forceUpdate).toHaveBeenCalledTimes(1)
  })
})
<|endoftext|>


<commit_before>/*
	Name: autoComplete
	Author: Raymond Camden & Andy Matthews
	Contributors: Jim Pease (@jmpease)
	Website: http://raymondcamden.com/
			http://andyMatthews.net
	Packed With: http://jsutility.pjoneil.net/
	Version: 1.4.2
*/
(function($) {

	"use strict";

	var defaults = {
		method: 'GET',
		icon: 'arrow-r',
		cancelRequests: false,
		target: $(),
		source: null,
		callback: null,
		link: null,
		minLength: 0,
		transition: 'fade',
		matchFromStart: true,
        termParam : 'term',
        loadingHtml : '
Searching...
',
        interval : 0,
        builder : null
	},
	openXHR = {},
	buildItems = function($this, data, settings) {
		var str;
        if (settings.builder) {
            str = settings.builder.apply($this.eq(0), [data, settings]);
        } else {
            str = [];
            if (data) {
                $.each(data, function(index, value) {
                    // are we working with objects or strings?
                    if ($.isPlainObject(value)) {
                        str.push('
' + value.label + '
');
                    } else {
                        str.push('
' + value + '
');
                    }
                });
            }
        }
        if ($.isArray(str)) str = str.join('');
		$(settings.target).html(str).listview("refresh");

		// is there a callback?
		if (settings.callback !== null && $.isFunction(settings.callback)) {
			attachCallback(settings);
		}

		if (str.length > 0) {
			$this.trigger("targetUpdated.autocomplete");
		} else {
			$this.trigger("targetCleared.autocomplete");
		}
	},
	attachCallback = function(settings) {
		$('li a', $(settings.target)).bind('click.autocomplete',function(e){
			e.stopPropagation();
			e.preventDefault();
			settings.callback(e);
		});
	},
	clearTarget = function($this, $target) {
		$target.html('').listview('refresh').closest("fieldset").removeClass("ui-search-active");
		$this.trigger("targetCleared.autocomplete");
	},
	handleInput = function(e) {
		var $this = $(this),
			id = $this.attr("id"),
			text,
			data,
			settings = $this.jqmData("autocomplete"),
			element_text,
			re;
        if (e) {
            if (e.keyCode == 38) { // up
                $('.ui-btn-active', $(settings.target)).removeClass('ui-btn-active').prev('li.ui-btn').addClass('ui-btn-active').length
                    || $('.ui-btn:last', $(settings.target)).addClass('ui-btn-active');
            } else if (e.keyCode == 40) {
                $('.ui-btn-active', $(settings.target)).removeClass('ui-btn-active').next('li.ui-btn').addClass('ui-btn-active').length
                    || $('.ui-btn:first', $(settings.target)).addClass('ui-btn-active');
            } else if (e.keyCode == 13) {
                $('.ui-btn-active a', $(settings.target)).click();
            }
        }
		if (settings) {
			// get the current text of the input field
			text = $this.val();
            // check if it's the same as the last one
            if (settings._lastText === text) return;
            // reset the timeout...
            if (settings._retryTimeout) {
                window.clearTimeout(settings._retryTimeout);
                settings._retryTimeout = null;
            }
            // dont change the result the user is browsing...
            if (e && (e.keyCode == 13 || e.keyCode == 38 || e.keyCode == 40)) return;
			// if we don't have enough text zero out the target
			if (text.length < settings.minLength) {
				clearTarget($this, $(settings.target));
			} else {
                if (settings.interval && Date.now() - settings._lastRequest < settings.interval) {
                    settings._retryTimeout = window.setTimeout($.proxy(handleInput, this), settings.interval - Date.now() + settings._lastRequest );
                    return;
                }
                settings._lastRequest = Date.now();
                // store last text
                settings._lastText = text;

				// are we looking at a source array or remote data?
				if ($.isArray(settings.source)) {
					data = settings.source.sort().filter(function(element) {
						// matching from start, or anywhere in the string?
						if (settings.matchFromStart) {
							// from start
							element_text, re = new RegExp('^' + text, 'i');
						} else {
							// anywhere
							element_text, re = new RegExp(text, 'i');
						}
						if ($.isPlainObject(element)) {
							element_text = element.label;
						} else {
							element_text = element;
						}
						return re.test(element_text);
					});
					buildItems($this, data, settings);
				}
				// Accept a function as source.
				// Function needs to call the callback, which is the first parameter.
				// source:function(text,callback) { mydata = [1,2]; callback(mydata); }
				else if (typeof settings.source === 'function') {

					settings.source(text,function(data){
						buildItems($this, data, settings);
					});

				} else {
                    var ajax = {
						type: settings.method,
                        data: {},
                        dataType: 'json',
						beforeSend: function(jqXHR) {
							if (settings.cancelRequests) {
								if (openXHR[id]) {
									// If we have an open XML HTTP Request for this autoComplete ID, abort it
									openXHR[id].abort();
								} else {
								}
								// Set this request to the open XML HTTP Request list for this ID
								openXHR[id] = jqXHR;
							}
                            if (settings.loadingHtml) {
                                // Set a loading indicator as a temporary stop-gap to the response time issue
                                settings.target.html(settings.loadingHtml).listview('refresh');
                                settings.target.closest("fieldset").addClass("ui-search-active");
                            }
						},
						success: function(data) {
							buildItems($this, data, settings);
						},
						complete: function (jqXHR, textStatus) {
							// Clear this ID's open XML HTTP Request from the list
							if (settings.cancelRequests) {
								openXHR[id] = null;
							}
						}
                    };
                    if ($.isPlainObject(settings.source)) {
                        if (settings.source.callback) {
                            settings.source.callback(text, ajax);
                        }
                        for (var k in settings.source) {
                            if (k != 'callback') ajax[k] = settings.source[k];
                        }
                    } else {
                        ajax.url = settings.source;
                    }
					if (settings.termParam) ajax.data[settings.termParam] = text;
					$.ajax(ajax);
				}
			}
		}
	},
	methods = {
		init: function(options) {
			var el = this;
			el.jqmData("autocomplete", $.extend({}, defaults, options));
			var settings = el.jqmData("autocomplete");
			return el.unbind("keyup.autocomplete")
						.bind("keyup.autocomplete", handleInput)
						.next('.ui-input-clear')
						.bind('click', function(e){
							clearTarget(el, $(settings.target));
						});
		},
		// Allow dynamic update of source and link
		update: function(options) {
			var settings = this.jqmData("autocomplete");
			if (settings) {
				this.jqmData("autocomplete", $.extend(settings, options));
			}
			return this;
		},
		// Method to forcibly clear our target
		clear: function() {
			var settings = this.jqmData("autocomplete");
			if (settings) {
				clearTarget(this, $(settings.target));
			}
			return this;
		},
		// Method to destroy (cleanup) plugin
		destroy: function() {
			var settings = this.jqmData("autocomplete");
			if (settings) {
				clearTarget(this, $(settings.target));
				this.jqmRemoveData("autocomplete");
				this.unbind(".autocomplete");
			}
			return this;
		}
	};

	$.fn.autocomplete = function(method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		} else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		}
	};

})(jQuery);
<commit_msg>up/down fix<commit_after>/*
	Name: autoComplete
	Author: Raymond Camden & Andy Matthews
	Contributors: Jim Pease (@jmpease)
	Website: http://raymondcamden.com/
			http://andyMatthews.net
	Packed With: http://jsutility.pjoneil.net/
	Version: 1.4.2
*/
(function($) {

	"use strict";

	var defaults = {
		method: 'GET',
		icon: 'arrow-r',
		cancelRequests: false,
		target: $(),
		source: null,
		callback: null,
		link: null,
		minLength: 0,
		transition: 'fade',
		matchFromStart: true,
        termParam : 'term',
        loadingHtml : '
Searching...
',
        interval : 0,
        builder : null
	},
	openXHR = {},
	buildItems = function($this, data, settings) {
		var str;
        if (settings.builder) {
            str = settings.builder.apply($this.eq(0), [data, settings]);
        } else {
            str = [];
            if (data) {
                $.each(data, function(index, value) {
                    // are we working with objects or strings?
                    if ($.isPlainObject(value)) {
                        str.push('
' + value.label + '
');
                    } else {
                        str.push('
' + value + '
');
                    }
                });
            }
        }
        if ($.isArray(str)) str = str.join('');
		$(settings.target).html(str).listview("refresh");

		// is there a callback?
		if (settings.callback !== null && $.isFunction(settings.callback)) {
			attachCallback(settings);
		}

		if (str.length > 0) {
			$this.trigger("targetUpdated.autocomplete");
		} else {
			$this.trigger("targetCleared.autocomplete");
		}
	},
	attachCallback = function(settings) {
		$('li a', $(settings.target)).bind('click.autocomplete',function(e){
			e.stopPropagation();
			e.preventDefault();
			settings.callback(e);
		});
	},
	clearTarget = function($this, $target) {
		$target.html('').listview('refresh').closest("fieldset").removeClass("ui-search-active");
		$this.trigger("targetCleared.autocomplete");
	},
	handleInput = function(e) {
		var $this = $(this),
			id = $this.attr("id"),
			text,
			data,
			settings = $this.jqmData("autocomplete"),
			element_text,
			re;
        if (e) {
            if (e.keyCode == 38) { // up
                $('.ui-btn-active', $(settings.target)).removeClass('ui-btn-active').prevAll('li.ui-btn:eq(0)').addClass('ui-btn-active').length
                    || $('.ui-btn:last', $(settings.target)).addClass('ui-btn-active');
            } else if (e.keyCode == 40) {
                $('.ui-btn-active', $(settings.target)).removeClass('ui-btn-active').nextAll('li.ui-btn:eq(0)').addClass('ui-btn-active').length
                    || $('.ui-btn:first', $(settings.target)).addClass('ui-btn-active');
            } else if (e.keyCode == 13) {
                $('.ui-btn-active a', $(settings.target)).click();
            }
        }
		if (settings) {
			// get the current text of the input field
			text = $this.val();
            // check if it's the same as the last one
            if (settings._lastText === text) return;
            // reset the timeout...
            if (settings._retryTimeout) {
                window.clearTimeout(settings._retryTimeout);
                settings._retryTimeout = null;
            }
            // dont change the result the user is browsing...
            if (e && (e.keyCode == 13 || e.keyCode == 38 || e.keyCode == 40)) return;
			// if we don't have enough text zero out the target
			if (text.length < settings.minLength) {
				clearTarget($this, $(settings.target));
			} else {
                if (settings.interval && Date.now() - settings._lastRequest < settings.interval) {
                    settings._retryTimeout = window.setTimeout($.proxy(handleInput, this), settings.interval - Date.now() + settings._lastRequest );
                    return;
                }
                settings._lastRequest = Date.now();
                // store last text
                settings._lastText = text;

				// are we looking at a source array or remote data?
				if ($.isArray(settings.source)) {
					data = settings.source.sort().filter(function(element) {
						// matching from start, or anywhere in the string?
						if (settings.matchFromStart) {
							// from start
							element_text, re = new RegExp('^' + text, 'i');
						} else {
							// anywhere
							element_text, re = new RegExp(text, 'i');
						}
						if ($.isPlainObject(element)) {
							element_text = element.label;
						} else {
							element_text = element;
						}
						return re.test(element_text);
					});
					buildItems($this, data, settings);
				}
				// Accept a function as source.
				// Function needs to call the callback, which is the first parameter.
				// source:function(text,callback) { mydata = [1,2]; callback(mydata); }
				else if (typeof settings.source === 'function') {

					settings.source(text,function(data){
						buildItems($this, data, settings);
					});

				} else {
                    var ajax = {
						type: settings.method,
                        data: {},
                        dataType: 'json',
						beforeSend: function(jqXHR) {
							if (settings.cancelRequests) {
								if (openXHR[id]) {
									// If we have an open XML HTTP Request for this autoComplete ID, abort it
									openXHR[id].abort();
								} else {
								}
								// Set this request to the open XML HTTP Request list for this ID
								openXHR[id] = jqXHR;
							}
                            if (settings.loadingHtml) {
                                // Set a loading indicator as a temporary stop-gap to the response time issue
                                settings.target.html(settings.loadingHtml).listview('refresh');
                                settings.target.closest("fieldset").addClass("ui-search-active");
                            }
						},
						success: function(data) {
							buildItems($this, data, settings);
						},
						complete: function (jqXHR, textStatus) {
							// Clear this ID's open XML HTTP Request from the list
							if (settings.cancelRequests) {
								openXHR[id] = null;
							}
						}
                    };
                    if ($.isPlainObject(settings.source)) {
                        if (settings.source.callback) {
                            settings.source.callback(text, ajax);
                        }
                        for (var k in settings.source) {
                            if (k != 'callback') ajax[k] = settings.source[k];
                        }
                    } else {
                        ajax.url = settings.source;
                    }
					if (settings.termParam) ajax.data[settings.termParam] = text;
					$.ajax(ajax);
				}
			}
		}
	},
	methods = {
		init: function(options) {
			var el = this;
			el.jqmData("autocomplete", $.extend({}, defaults, options));
			var settings = el.jqmData("autocomplete");
			return el.unbind("keyup.autocomplete")
						.bind("keyup.autocomplete", handleInput)
						.next('.ui-input-clear')
						.bind('click', function(e){
							clearTarget(el, $(settings.target));
						});
		},
		// Allow dynamic update of source and link
		update: function(options) {
			var settings = this.jqmData("autocomplete");
			if (settings) {
				this.jqmData("autocomplete", $.extend(settings, options));
			}
			return this;
		},
		// Method to forcibly clear our target
		clear: function() {
			var settings = this.jqmData("autocomplete");
			if (settings) {
				clearTarget(this, $(settings.target));
			}
			return this;
		},
		// Method to destroy (cleanup) plugin
		destroy: function() {
			var settings = this.jqmData("autocomplete");
			if (settings) {
				clearTarget(this, $(settings.target));
				this.jqmRemoveData("autocomplete");
				this.unbind(".autocomplete");
			}
			return this;
		}
	};

	$.fn.autocomplete = function(method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		} else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		}
	};

})(jQuery);
<|endoftext|>


<commit_before>(function ($) {
  var queryParser = function (a) {
      var i, p, b = {};
      if (a === "") {
        return {};
      }
      for (i = 0; i < a.length; i += 1) {
        p = a[i].split('=');
        if (p.length === 2) {
          b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
        }
      }
      return b;
    };
  $.queryParams = function () {
    return queryParser(window.location.search.substr(1).split('&'));
  };
  $.hashParams = function () {
    return queryParser(window.location.hash.substr(1).split('&'));
  };

  window.Swiftype = window.Swiftype || {};
  Swiftype.root_url = Swiftype.root_url || 'https://api.swiftype.com';
  Swiftype.pingUrl = function(endpoint, callback) {
    var to = setTimeout(callback, 350);
    var img = new Image();
    img.onload = img.onerror = function() {
      clearTimeout(to);
      callback();
    };
    img.src = endpoint;
    return false;
  };
  Swiftype.pingSearchResultClick = function (engineKey, docId, callback) {
    var params = {
      t: new Date().getTime(),
      engine_key: engineKey,
      doc_id: docId,
      q: Swiftype.currentQuery
    };
    var url = Swiftype.root_url + '/api/v1/public/analytics/pc?' + $.param(params);
    Swiftype.pingUrl(url, callback);
  };

  $.fn.swiftypeSearch = function (options) {
    var options = $.extend({}, $.fn.swiftypeSearch.defaults, options);

    return this.each(function () {
      var $this = $(this);
      var config = $.meta ? $.extend({}, options, $this.data()) : options;
      $this.data('swiftype-config-search', config);

      $this.selectedCallback = function (data) {
        return function (e) {
          var $el = $(this);
          e.preventDefault();
          Swiftype.pingSearchResultClick(config.engineKey, data['id'], function() {
            config.onComplete($el);
          });
        };
      };

      $this.registerResult = function ($element, data) {
        $element.data('swiftype-item', data);
        $('a', $element).click($this.selectedCallback(data));
      };

      $this.getContentCache = function () {
        return $('#' + contentCacheId);
      };

      var $resultContainer = $(config.resultContainingElement),
        initialContentOfResultContainer = $resultContainer.html(),
        contentCacheId = 'st-content-cache',
        $contentCache = $this.getContentCache();

      var setSearchHash = function (query, page) {
          location.hash = "stq=" + encodeURIComponent(query) + "&stp=" + page;
        };

      var submitSearch = function (query, options) {
          options = $.extend({
            page: 1
          }, options);
          var params = {};

          if (!$contentCache.length) {
            $resultContainer.after("

");
            $contentCache.html(initialContentOfResultContainer).hide();
          }
          config.loadingFunction(query, $resultContainer);

          Swiftype.currentQuery = query;
          params['q'] = query;
          params['engine_key'] = config.engineKey;
          params['page'] = options.page;

          function handleFunctionParam(field) {
            if (field !== undefined) {
              var evald = field;
              if (typeof evald === 'function') {
                evald = evald.call();
              }
              return evald;
            }
            return undefined;
          }

          params['per_page'] = handleFunctionParam(config.perPage);
          params['search_fields'] = handleFunctionParam(config.searchFields);
          params['fetch_fields'] = handleFunctionParam(config.fetchFields);
          params['facets'] = handleFunctionParam(config.facets);
          params['filters'] = handleFunctionParam(config.filters);
          params['document_types'] = handleFunctionParam(config.documentTypes);
          params['functional_boosts'] = handleFunctionParam(config.functionalBoosts);
          params['sort_field'] = handleFunctionParam(config.sortField);
          params['sort_direction'] = handleFunctionParam(config.sortDirection);
          params['spelling'] = handleFunctionParam(config.spelling);

          $.ajax({
            dataType: "json",
            url: Swiftype.root_url + "/api/v1/public/engines/search.json?callback=?",
            data: params,
            xhrFields: { withCredentials: true },
            success: renderSearchResults
          });
        };

      $(window).hashchange(function () {
        var params = $.hashParams();
        if (params.stq) {
          submitSearch(params.stq, {
            page: params.stp
          });
        } else {
          var $contentCache = $this.getContentCache();
          if ($contentCache.length) {
            $resultContainer.html($contentCache.html());
            $contentCache.remove();
          }
        }
      });

      var $containingForm = $this.parents('form');
      if ($containingForm) {
        $containingForm.bind('submit', function (e) {
          e.preventDefault();
          var searchQuery = $this.val();
          setSearchHash(searchQuery, 1);
        });
      }

      $(document).on('click', '[data-hash][data-page]', function (e) {
        e.preventDefault();
        var $this = $(this);
        setSearchHash($.hashParams().stq, $this.data('page'));
      });

      $(document).on('click', '[data-hash][data-spelling-suggestion]', function (e) {
        e.preventDefault();
        var $this = $(this);
        setSearchHash($this.data('spelling-suggestion'), 1);
      });

      var renderSearchResults = function (data) {
        if (typeof config.preRenderFunction === 'function') {
          config.preRenderFunction.call($this, data);
        }

        config.renderResultsFunction($this.getContext(), data);

        if (typeof config.postRenderFunction === 'function') {
          config.postRenderFunction.call($this, data);
        }
      };

      $this.getContext = function () {
        return {
          config: config,
          resultContainer: $resultContainer,
          registerResult: $this.registerResult
        };
      };

      $(window).hashchange(); // if the swiftype query hash is present onload (maybe the user is pressing the back button), submit a query onload
    });
  };

  var renderPagination = function (ctx, resultInfo) {
    var maxPagesType, maxPages = -1,
      config = ctx.config;
    $.each(resultInfo, function(documentType, typeInfo) {
      if (typeInfo.num_pages > maxPages) {
        maxPagesType = documentType;
        maxPages = typeInfo.num_pages;
      }
    });
    var currentPage = resultInfo[maxPagesType].current_page,
      totalPages = resultInfo[maxPagesType].num_pages;

    $(config.renderPaginationForType(maxPagesType, currentPage, totalPages)).appendTo(ctx.resultContainer);
  };

  var normalize = function (str) {
      return $.trim(str).toLowerCase();
    };

  function htmlEscape(str) {
    return String(str).replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>');
  }

  var defaultRenderResultsFunction = function (ctx, data) {
    var $resultContainer = ctx.resultContainer,
      config = ctx.config;

    $resultContainer.html('');

    $.each(data.records, function (documentType, items) {
      $.each(items, function (idx, item) {
        ctx.registerResult($(config.renderFunction(documentType, item)).appendTo($resultContainer), item);
      });
    });

    renderPagination(ctx, data.info);
  };

  var defaultRenderFunction = function (document_type, item) {
      return '

' + htmlEscape(item['title']) + '

';
    };

  var defaultLoadingFunction = function(query, $resultContainer) {
      $resultContainer.html('
loading...
');
    };

  var defaultOnComplete = function(elem) {
    window.location = elem.attr('href');
  };

  var defaultPostRenderFunction = function(data) {
    var totalResultCount = 0;
    var $resultContainer = this.getContext().resultContainer;
    var spellingSuggestion = null;

    if (data['info']) {
      $.each(data['info'], function(index, value) {
        totalResultCount += value['total_result_count'];
        if ( value['spelling_suggestion'] ) {
          spellingSuggestion = value['spelling_suggestion']['text'];
        }

      });
    }

    if (totalResultCount === 0) {
      $resultContainer.html("
No results found.
");
    }

    if (spellingSuggestion !== null) {
      $resultContainer.append('
Did you mean ' + spellingSuggestion + '?
');
    }
  };

  var defaultRenderPaginationForType = function (type, currentPage, totalPages) {
      var pages = '
',
        previousPage, nextPage;
      if (currentPage != 1) {
        previousPage = currentPage - 1;
        pages = pages + '« previous';
      }
      if (currentPage < totalPages) {
        nextPage = currentPage + 1;
        pages = pages + 'next »';
      }
      pages += '
';
      return pages;
    };

  $.fn.swiftypeSearch.defaults = {
    attachTo: undefined,
    documentTypes: undefined,
    facets: undefined,
    filters: undefined,
    engineKey: undefined,
    searchFields: undefined,
    functionalBoosts: undefined,
    sortField: undefined,
    sortDirection: undefined,
    fetchFields: undefined,
    preRenderFunction: undefined,
    postRenderFunction: defaultPostRenderFunction,
    loadingFunction: defaultLoadingFunction,
    renderResultsFunction: defaultRenderResultsFunction,
    renderFunction: defaultRenderFunction,
    renderPaginationForType: defaultRenderPaginationForType,
    onComplete: defaultOnComplete,
    perPage: 10,
    spelling: 'strict'
  };
})(jQuery);
<commit_msg>Added highlightFields search option<commit_after>(function ($) {
  var queryParser = function (a) {
      var i, p, b = {};
      if (a === "") {
        return {};
      }
      for (i = 0; i < a.length; i += 1) {
        p = a[i].split('=');
        if (p.length === 2) {
          b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
        }
      }
      return b;
    };
  $.queryParams = function () {
    return queryParser(window.location.search.substr(1).split('&'));
  };
  $.hashParams = function () {
    return queryParser(window.location.hash.substr(1).split('&'));
  };

  window.Swiftype = window.Swiftype || {};
  Swiftype.root_url = Swiftype.root_url || 'https://api.swiftype.com';
  Swiftype.pingUrl = function(endpoint, callback) {
    var to = setTimeout(callback, 350);
    var img = new Image();
    img.onload = img.onerror = function() {
      clearTimeout(to);
      callback();
    };
    img.src = endpoint;
    return false;
  };
  Swiftype.pingSearchResultClick = function (engineKey, docId, callback) {
    var params = {
      t: new Date().getTime(),
      engine_key: engineKey,
      doc_id: docId,
      q: Swiftype.currentQuery
    };
    var url = Swiftype.root_url + '/api/v1/public/analytics/pc?' + $.param(params);
    Swiftype.pingUrl(url, callback);
  };

  $.fn.swiftypeSearch = function (options) {
    var options = $.extend({}, $.fn.swiftypeSearch.defaults, options);

    return this.each(function () {
      var $this = $(this);
      var config = $.meta ? $.extend({}, options, $this.data()) : options;
      $this.data('swiftype-config-search', config);

      $this.selectedCallback = function (data) {
        return function (e) {
          var $el = $(this);
          e.preventDefault();
          Swiftype.pingSearchResultClick(config.engineKey, data['id'], function() {
            config.onComplete($el);
          });
        };
      };

      $this.registerResult = function ($element, data) {
        $element.data('swiftype-item', data);
        $('a', $element).click($this.selectedCallback(data));
      };

      $this.getContentCache = function () {
        return $('#' + contentCacheId);
      };

      var $resultContainer = $(config.resultContainingElement),
        initialContentOfResultContainer = $resultContainer.html(),
        contentCacheId = 'st-content-cache',
        $contentCache = $this.getContentCache();

      var setSearchHash = function (query, page) {
          location.hash = "stq=" + encodeURIComponent(query) + "&stp=" + page;
        };

      var submitSearch = function (query, options) {
          options = $.extend({
            page: 1
          }, options);
          var params = {};

          if (!$contentCache.length) {
            $resultContainer.after("

");
            $contentCache.html(initialContentOfResultContainer).hide();
          }
          config.loadingFunction(query, $resultContainer);

          Swiftype.currentQuery = query;
          params['q'] = query;
          params['engine_key'] = config.engineKey;
          params['page'] = options.page;

          function handleFunctionParam(field) {
            if (field !== undefined) {
              var evald = field;
              if (typeof evald === 'function') {
                evald = evald.call();
              }
              return evald;
            }
            return undefined;
          }

          params['per_page'] = handleFunctionParam(config.perPage);
          params['search_fields'] = handleFunctionParam(config.searchFields);
          params['fetch_fields'] = handleFunctionParam(config.fetchFields);
          params['facets'] = handleFunctionParam(config.facets);
          params['filters'] = handleFunctionParam(config.filters);
          params['document_types'] = handleFunctionParam(config.documentTypes);
          params['functional_boosts'] = handleFunctionParam(config.functionalBoosts);
          params['sort_field'] = handleFunctionParam(config.sortField);
          params['sort_direction'] = handleFunctionParam(config.sortDirection);
          params['spelling'] = handleFunctionParam(config.spelling);
          params['highlight_fields'] = handleFunctionParam(config.highlightFields);

          $.ajax({
            dataType: "json",
            url: Swiftype.root_url + "/api/v1/public/engines/search.json?callback=?",
            data: params,
            xhrFields: { withCredentials: true },
            success: renderSearchResults
          });
        };

      $(window).hashchange(function () {
        var params = $.hashParams();
        if (params.stq) {
          submitSearch(params.stq, {
            page: params.stp
          });
        } else {
          var $contentCache = $this.getContentCache();
          if ($contentCache.length) {
            $resultContainer.html($contentCache.html());
            $contentCache.remove();
          }
        }
      });

      var $containingForm = $this.parents('form');
      if ($containingForm) {
        $containingForm.bind('submit', function (e) {
          e.preventDefault();
          var searchQuery = $this.val();
          setSearchHash(searchQuery, 1);
        });
      }

      $(document).on('click', '[data-hash][data-page]', function (e) {
        e.preventDefault();
        var $this = $(this);
        setSearchHash($.hashParams().stq, $this.data('page'));
      });

      $(document).on('click', '[data-hash][data-spelling-suggestion]', function (e) {
        e.preventDefault();
        var $this = $(this);
        setSearchHash($this.data('spelling-suggestion'), 1);
      });

      var renderSearchResults = function (data) {
        if (typeof config.preRenderFunction === 'function') {
          config.preRenderFunction.call($this, data);
        }

        config.renderResultsFunction($this.getContext(), data);

        if (typeof config.postRenderFunction === 'function') {
          config.postRenderFunction.call($this, data);
        }
      };

      $this.getContext = function () {
        return {
          config: config,
          resultContainer: $resultContainer,
          registerResult: $this.registerResult
        };
      };

      $(window).hashchange(); // if the swiftype query hash is present onload (maybe the user is pressing the back button), submit a query onload
    });
  };

  var renderPagination = function (ctx, resultInfo) {
    var maxPagesType, maxPages = -1,
      config = ctx.config;
    $.each(resultInfo, function(documentType, typeInfo) {
      if (typeInfo.num_pages > maxPages) {
        maxPagesType = documentType;
        maxPages = typeInfo.num_pages;
      }
    });
    var currentPage = resultInfo[maxPagesType].current_page,
      totalPages = resultInfo[maxPagesType].num_pages;

    $(config.renderPaginationForType(maxPagesType, currentPage, totalPages)).appendTo(ctx.resultContainer);
  };

  var normalize = function (str) {
      return $.trim(str).toLowerCase();
    };

  function htmlEscape(str) {
    return String(str).replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>');
  }

  var defaultRenderResultsFunction = function (ctx, data) {
    var $resultContainer = ctx.resultContainer,
      config = ctx.config;

    $resultContainer.html('');

    $.each(data.records, function (documentType, items) {
      $.each(items, function (idx, item) {
        ctx.registerResult($(config.renderFunction(documentType, item)).appendTo($resultContainer), item);
      });
    });

    renderPagination(ctx, data.info);
  };

  var defaultRenderFunction = function (document_type, item) {
      return '

' + htmlEscape(item['title']) + '

';
    };

  var defaultLoadingFunction = function(query, $resultContainer) {
      $resultContainer.html('
loading...
');
    };

  var defaultOnComplete = function(elem) {
    window.location = elem.attr('href');
  };

  var defaultPostRenderFunction = function(data) {
    var totalResultCount = 0;
    var $resultContainer = this.getContext().resultContainer;
    var spellingSuggestion = null;

    if (data['info']) {
      $.each(data['info'], function(index, value) {
        totalResultCount += value['total_result_count'];
        if ( value['spelling_suggestion'] ) {
          spellingSuggestion = value['spelling_suggestion']['text'];
        }

      });
    }

    if (totalResultCount === 0) {
      $resultContainer.html("
No results found.
");
    }

    if (spellingSuggestion !== null) {
      $resultContainer.append('
Did you mean ' + spellingSuggestion + '?
');
    }
  };

  var defaultRenderPaginationForType = function (type, currentPage, totalPages) {
      var pages = '
',
        previousPage, nextPage;
      if (currentPage != 1) {
        previousPage = currentPage - 1;
        pages = pages + '« previous';
      }
      if (currentPage < totalPages) {
        nextPage = currentPage + 1;
        pages = pages + 'next »';
      }
      pages += '
';
      return pages;
    };

  $.fn.swiftypeSearch.defaults = {
    attachTo: undefined,
    documentTypes: undefined,
    facets: undefined,
    filters: undefined,
    engineKey: undefined,
    searchFields: undefined,
    functionalBoosts: undefined,
    sortField: undefined,
    sortDirection: undefined,
    fetchFields: undefined,
    highlightFields: undefined,
    preRenderFunction: undefined,
    postRenderFunction: defaultPostRenderFunction,
    loadingFunction: defaultLoadingFunction,
    renderResultsFunction: defaultRenderResultsFunction,
    renderFunction: defaultRenderFunction,
    renderPaginationForType: defaultRenderPaginationForType,
    onComplete: defaultOnComplete,
    perPage: 10,
    spelling: 'strict'
  };
})(jQuery);
<|endoftext|>


<commit_before>var locastyle = locastyle || {};

locastyle.form = (function() {
  'use strict';

  var config = {
    selectors: {
      disable: '.ls-form-disable',
      text: '.ls-form-text'
    }
  };

  function init() {
    formDisable();
    formText();
    masks();
    textareaAutoresize();
    prefixSufix();
    togglePasswordField();
    textareaHeight();
  }

  function prefixSufix () {
    if(locastyle.breakpointClass == 'ls-screen-xs'){
      $('.ls-label-text-sufix').parents('.ls-label').addClass('ls-label-text-has-sufix');
      $('.ls-label-text-prefix').parents('.ls-label').addClass('ls-label-text-has-prefix');
    } else{
      $('.ls-label-text-sufix').prev('[class*="col-"]')
        .addClass('ls-no-padding-right')
        .find(':input').addClass('ls-no-right-radius ls-border');
    }
  }

  function textareaAutoresize (argument) {
    $('textarea.ls-textarea-autoresize', '.ls-label').each(function (index, textarea) {
      var $textarea = $(textarea);
      var height = $textarea.height();
      $textarea.keyup(function (e) {
        if (!$textarea.prop('scrollTop')) {
          var scrollHeight;
          do {
            scrollHeight = $textarea.prop('scrollHeight');
            var height = $textarea.height();
            $textarea.height(height - 5);
          } while (scrollHeight && (scrollHeight != $textarea.prop('scrollHeight')));
        }
        $textarea.height($textarea.prop('scrollHeight') );
      });
    });
  }

  function textareaHeight (){
    $('textarea').each(function (index, textarea) {
      var text = $(textarea).val();
      var lines = text.split(/\r|\r\n|\n/);
      var count = lines.length;
      var total = count * 18;
      $(textarea).height(total + 'px');
    });
  }

  function masks() {
    $('.ls-mask-date').mask('11/11/1111');
    $('.ls-mask-time').mask('00:00:00');
    $('.ls-mask-date_time').mask('00/00/0000 00:00:00');
    $('.ls-mask-cep').mask('00000-000');
    $('.ls-mask-phone8').mask('0000-0000');
    $('.ls-mask-phone9').mask('00009-0000');
    $('.ls-mask-phone8_with_ddd').mask('(00) 0000-0000');
    $('.ls-mask-phone9_with_ddd').mask('(00) 00009-0000');
    $('.ls-mask-cpf').mask('000.000.000-00', {reverse: true});
    $('.ls-mask-cnpj').mask('00.000.000/0000-00', {reverse: true});
    $('.ls-mask-money').mask("#.##0,00", {reverse: true, maxlength: false});
    $('.ls-mask-number').mask("#.##0", {reverse: true, maxlength: false});
    $('.ls-mask-ip_address').mask('0ZZ.0ZZ.0ZZ.0ZZ', {translation: {'Z': {pattern: /[0-9]/, optional: true}}});
    $('.ls-mask-percent').mask('##0,00%', {reverse: true});
  }

  function formDisable() {
    $( config.selectors.disable ).each(function(indexContainer, container){
      var $container = $(container);
      $container.find(':input').each(function(indexField, field){
        var $field = $(field);
        $field.attr('disabled', 'disabled');
        $field.data('original-value', $field.val() );
      });
    });

  }

  function formText() {
    $( config.selectors.text ).each(function(indexContainer, container){
      $(container).find(':input').each(function(indexField, field){
        $(field).addClass('ls-form-text');
      });
      $(container).data('form-text', true);
    });
  }

  function dataToggleClass($element){
    if($($element).data('toggle-class') !== undefined){
      var getClass = $($element).data('toggle-class').split(',');
      $($element).toggleClass(getClass[0]).toggleClass(getClass[1]);
    }
  }

  function togglePasswordField(){
    $('.ls-toggle-pass').on("click", function(e){
      e.preventDefault();
      var target = $(this).data('target');
      dataToggleClass($(this));
      if ($(target).attr('type') == 'password'){
        $(target).removeAttr('attr').prop('type','text');
      } else {
        $(target).removeAttr('attr').prop('type','password');
      }
    });
  }

  return {
    init: init,
    togglePasswordField: togglePasswordField
  };

}());
<commit_msg>All this was declared, but not used.<commit_after>var locastyle = locastyle || {};

locastyle.form = (function() {
  'use strict';

  var config = {
    selectors: {
      disable: '.ls-form-disable',
      text: '.ls-form-text'
    }
  };

  function init() {
    formDisable();
    formText();
    masks();
    textareaAutoresize();
    prefixSufix();
    togglePasswordField();
    textareaHeight();
  }

  function prefixSufix () {
    if(locastyle.breakpointClass == 'ls-screen-xs'){
      $('.ls-label-text-sufix').parents('.ls-label').addClass('ls-label-text-has-sufix');
      $('.ls-label-text-prefix').parents('.ls-label').addClass('ls-label-text-has-prefix');
    } else{
      $('.ls-label-text-sufix').prev('[class*="col-"]')
        .addClass('ls-no-padding-right')
        .find(':input').addClass('ls-no-right-radius ls-border');
    }
  }

  function textareaAutoresize () {
    $('textarea.ls-textarea-autoresize', '.ls-label').each(function (index, textarea) {
      var $textarea = $(textarea);
      $textarea.keyup(function () {
        if (!$textarea.prop('scrollTop')) {
          var scrollHeight;
          do {
            scrollHeight = $textarea.prop('scrollHeight');
            var height = $textarea.height();
            $textarea.height(height - 5);
          } while (scrollHeight && (scrollHeight != $textarea.prop('scrollHeight')));
        }
        $textarea.height($textarea.prop('scrollHeight') );
      });
    });
  }

  function textareaHeight (){
    $('textarea').each(function (index, textarea) {
      var text = $(textarea).val();
      var lines = text.split(/\r|\r\n|\n/);
      var count = lines.length;
      var total = count * 18;
      $(textarea).height(total + 'px');
    });
  }

  function masks() {
    $('.ls-mask-date').mask('11/11/1111');
    $('.ls-mask-time').mask('00:00:00');
    $('.ls-mask-date_time').mask('00/00/0000 00:00:00');
    $('.ls-mask-cep').mask('00000-000');
    $('.ls-mask-phone8').mask('0000-0000');
    $('.ls-mask-phone9').mask('00009-0000');
    $('.ls-mask-phone8_with_ddd').mask('(00) 0000-0000');
    $('.ls-mask-phone9_with_ddd').mask('(00) 00009-0000');
    $('.ls-mask-cpf').mask('000.000.000-00', {reverse: true});
    $('.ls-mask-cnpj').mask('00.000.000/0000-00', {reverse: true});
    $('.ls-mask-money').mask("#.##0,00", {reverse: true, maxlength: false});
    $('.ls-mask-number').mask("#.##0", {reverse: true, maxlength: false});
    $('.ls-mask-ip_address').mask('0ZZ.0ZZ.0ZZ.0ZZ', {translation: {'Z': {pattern: /[0-9]/, optional: true}}});
    $('.ls-mask-percent').mask('##0,00%', {reverse: true});
  }

  function formDisable() {
    $( config.selectors.disable ).each(function(indexContainer, container){
      var $container = $(container);
      $container.find(':input').each(function(indexField, field){
        var $field = $(field);
        $field.attr('disabled', 'disabled');
        $field.data('original-value', $field.val() );
      });
    });

  }

  function formText() {
    $( config.selectors.text ).each(function(indexContainer, container){
      $(container).find(':input').each(function(indexField, field){
        $(field).addClass('ls-form-text');
      });
      $(container).data('form-text', true);
    });
  }

  function dataToggleClass($element){
    if($($element).data('toggle-class') !== undefined){
      var getClass = $($element).data('toggle-class').split(',');
      $($element).toggleClass(getClass[0]).toggleClass(getClass[1]);
    }
  }

  function togglePasswordField(){
    $('.ls-toggle-pass').on("click", function(e){
      e.preventDefault();
      var target = $(this).data('target');
      dataToggleClass($(this));
      if ($(target).attr('type') == 'password'){
        $(target).removeAttr('attr').prop('type','text');
      } else {
        $(target).removeAttr('attr').prop('type','password');
      }
    });
  }

  return {
    init: init,
    togglePasswordField: togglePasswordField
  };

}());
<|endoftext|>