<commit_before>// Copyright 2021, University of Colorado Boulder

/**
 * CarouselComboBox behaves like a combo box, but its listbox is a carousel. This allows you to scroll through a
 * long list of items, a feature that ComboBoxListBox does not support. ComboBoxItem, ComboBoxButton, and
 * Carousel are reused.
 *
 * NOTE! This was created as a quick way to address situations where ComboBox's listbox gets too long, for example
 * https://github.com/phetsims/sun/issues/673. This tends to happen in internal 'demo' applications
 * (sun, scenery-phet,... ) that have long lists of demos. And as a design best-practice, we tend to avoid
 * longs lists of things in sims. So that's why CarouselComboBox currently lives in sun/demo/.
 * It was written to be fairly general, so should be relatively easy to relocate if it's needed for wider use.
 *
 * Possible future work:
 * - Modify ComboBox so that it can use different types of popups (ComboBoxListBox, Carousel,...), or
 * - Make CarouselComboBox pop up the Carousel in front of everything else
 * - a11y support in CarouselComboBox
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */

import Multilink from '../../../axon/js/Multilink.js';
import Property from '../../../axon/js/Property.js';
import Dimension2 from '../../../dot/js/Dimension2.js';
import dotRandom from '../../../dot/js/dotRandom.js';
import merge from '../../../phet-core/js/merge.js';
import AssertUtils from '../../../phetcommon/js/AssertUtils.js';
import PressListener from '../../../scenery/js/listeners/PressListener.js';
import AlignBox from '../../../scenery/js/nodes/AlignBox.js';
import AlignGroup from '../../../scenery/js/nodes/AlignGroup.js';
import HBox from '../../../scenery/js/nodes/HBox.js';
import Node from '../../../scenery/js/nodes/Node.js';
import Rectangle from '../../../scenery/js/nodes/Rectangle.js';
import VBox from '../../../scenery/js/nodes/VBox.js';
import Color from '../../../scenery/js/util/Color.js';
import Tandem from '../../../tandem/js/Tandem.js';
import Carousel from '../Carousel.js';
import ComboBoxButton from '../ComboBoxButton.js';
import ComboBoxItem from '../ComboBoxItem.js';
import PageControl from '../PageControl.js';
import sun from '../sun.js';

class CarouselComboBox extends Node {

  /**
   * @param {Property.<*>} property - the Property that is set when an item is selected
   * @param {ComboBoxItem[]} comboBoxItems - the items that appear in the carousel
   * @param {Object} [options]
   */
  constructor( property, comboBoxItems, options ) {

    assert && assert( property instanceof Property, 'invalid property' );
    assert && AssertUtils.assertArrayOf( comboBoxItems, ComboBoxItem );

    options = merge( {

      // Options specific to CarouselComboBox
      align: 'left', // {string} alignment of items in the carousel, 'left'|'right'|'center'
      overColor: Color.grayColor( 245 ), // {ColorDef} background color of the item that the pointer is over
      selectedColor: 'yellow', // {ColorDef} background color of the selected item
      itemXMargin: 6, // {number} x margin for backgrounds on the items in the carousel
      itemYMargin: 2, // {number} y margin for backgrounds on the items in the carousel

      // Options passed to Carousel
      carouselOptions: {
        arrowSize: new Dimension2( 20, 4 ),

        // Like ComboBox, 'vertical' is the only orientation supported (verified below).
        orientation: 'vertical',

        // Spacing and margins will be built into the backgrounds that are add to the item Nodes,
        // so set them to zero for the carousel.
        spacing: 0,
        margin: 0,

        // {number} If there are fewer items than this in the carousel, the actual number of items will be used.
        itemsPerPage: 15
      },

      pageControlOptions: {
        interactive: true
      },

      // Options passed to ComboBoxButton
      buttonOptions: {
        arrowDirection: 'down', // 'up' is not currently supported (verified below)
        baseColor: 'rgb( 218, 236, 255 )',
        xMargin: 6, // You'll typically want this to be the same as itemXMargin, but we're not going to force you :)
        yMargin: 4
      },

      // phet-io
      tandem: Tandem.OPTIONAL
    }, options );

    // Validate options
    assert && assert( options.carouselOptions.orientation === 'vertical', 'orientation must be vertical' );
    assert && assert( options.buttonOptions.arrowDirection === 'down', 'arrowDirection must be down' );

    // Check for options that are not settable by the client
    assert && assert( !options.children, 'CarouselComboBox sets children' );
    assert && assert( !options.buttonOptions.content, 'CarouselComboBox sets buttonOptions.content' );
    assert && assert( !options.buttonOptions.listener, 'CarouselComboBox sets buttonOptions.listener' );

    // Don't create pages that are longer than the number of items
    options.carouselOptions.itemsPerPage = Math.min( options.carouselOptions.itemsPerPage, comboBoxItems.length );

    // Orientations must be the same.
    assert && assert( !options.pageControlOptions.orientation, 'use carouselOptions.orientation' );
    options.pageControlOptions.orientation = options.carouselOptions.orientation;

    // Create tandems for subcomponents, if they were not provided
    options.carouselOptions.tandem = options.carouselOptions.tandem || options.tandem.createTandem( 'carousel' );
    options.buttonOptions.tandem = options.buttonOptions.tandem || options.tandem.createTandem( 'button' );

    // Make all Nodes for the comboBoxItems have the same width and height, left justified.
    const alignGroup = new AlignGroup();
    const itemNodes = _.map( comboBoxItems, item => {
      const node = new AlignBox( item.node, {
        group: alignGroup,
        xAlign: options.align
      } );
      node.itemValue = item.value; // attach the item's value to the Node, we'll need it later
      return node;
    } );

    // Size of the background that we'll put behind all item Nodes. Since we made all of the Nodes have the same
    // dimensions using AlignBox above, we can simply inspect the first Node.
    const backgroundSize = new Dimension2(
      itemNodes[ 0 ].width + 2 * options.itemXMargin,
      itemNodes[ 0 ].height + 2 * options.itemYMargin
    );

    // Create items for the carousel, whose API for 'items' is different than ComboBox. The Node for each item is a
    // label on a background. The background changes color when the item is selected or the pointer is over the item.
    const carouselItemNodes = [];
    const multilinks = [];
    for ( let i = 0; i < itemNodes.length; i++ ) {

      const itemNode = itemNodes[ i ];

      const backgroundNode = new Rectangle( 0, 0, backgroundSize.width, backgroundSize.height, {
        // Actual alignment of an item on the background is determined by AlignBox xAlign, above.
        center: itemNode.center
      } );

      const carouselItemNode = new Node( {
        children: [ backgroundNode, itemNode ]
      } );

      // Press on an item to select its associated value.
      const pressListener = new PressListener( {
        press: () => {
          property.value = itemNode.itemValue;
        }
      } );
      carouselItemNode.addInputListener( pressListener );

      // Selecting an item sets its background to the selected color.
      // Pointer over an item sets its background to the highlighted color.
      // Must be disposed because we do not own property.
      const multilink = new Multilink(
        [ property, pressListener.isOverProperty ],
        ( propertyValue, isOver ) => {
          if ( propertyValue === itemNode.itemValue ) {
            backgroundNode.fill = options.selectedColor;
          }
          else if ( isOver ) {
            backgroundNode.fill = options.overColor;
          }
          else {
            backgroundNode.fill = 'transparent';
          }
        } );
      multilinks.push( multilink );

      carouselItemNodes.push( carouselItemNode );
    }
    assert && assert( carouselItemNodes.length === comboBoxItems.length, 'expected a carouselItem for each comboBoxItem' );

    // Create the carousel.
    const carousel = new Carousel( carouselItemNodes, options.carouselOptions );

    // page control
    const pageControl = new PageControl( carousel.numberOfPages, carousel.pageNumberProperty, options.pageControlOptions );

    // Page control to the left of carousel
    const carouselAndPageControl = new HBox( {
      spacing: 4,
      children: [ carousel, pageControl ]
    } );

    // Pressing this button pops the carousel up and down
    const button = new ComboBoxButton( property, comboBoxItems, merge( {}, {
      listener: () => {
        carouselAndPageControl.visible = !carouselAndPageControl.visible;
      }
    }, options.buttonOptions ) );

    // Put the button above the carousel, left aligned.
    const vBox = new VBox( {
      spacing: 0,
      align: 'left',
      children: [ button, carouselAndPageControl ]
    } );

    // Wrap everything with Node, to hide VBox's API.
    options.children = [ vBox ];

    super( options );

    // If the Property changes, hide the carousel. unlink is needed on disposed.
    const propertyListener = () => { carouselAndPageControl.visible = false; };
    property.link( propertyListener );

    // Clicking outside this UI component will hide the carousel and page control.
    // NOTE: adapted from ComboBox.
    const clickToDismissListener = {
      down: event => {
        assert && assert( carousel.visible, 'this listener should be registered only when carousel is visible' );

        // If fuzzing is enabled, exercise this listener some percentage of the time, so that this listener is tested.
        // The rest of the time, ignore this listener, so that the carousel remains popped up, and we test making
        // choices from the carousel. See https://github.com/phetsims/sun/issues/677
        if ( !phet.chipper.isFuzzEnabled() || dotRandom.nextDouble() < 0.25 ) {
          const trail = event.trail;
          if ( !trail.containsNode( button ) && !trail.containsNode( carousel ) && !trail.containsNode( pageControl ) ) {
            carouselAndPageControl.visible = false;
          }
        }
      }
    };

    // Add clickToDismissListener only when the carousel & page control are visible. unlink is not needed.
    // NOTE: adapted from ComboBox.
    let display = null; // {Display}
    carouselAndPageControl.visibleProperty.link( visible => {
      if ( visible ) {
        assert && assert( !display, 'unexpected display' );
        display = this.getUniqueTrail().rootNode().getRootedDisplays()[ 0 ];
        display.addInputListener( clickToDismissListener );
      }
      else {
        if ( display && display.hasInputListener( clickToDismissListener ) ) {
          display.removeInputListener( clickToDismissListener );
          display = null;
        }
      }
    } );

    // @private
    this.disposeCarouselComboBox = () => {

      // Deregister observers
      property.unlink( propertyListener );
      multilinks.forEach( multilink => multilink.dispose() );

      // Dispose of subcomponents
      carousel.dispose();
      pageControl.dispose();
      button.dispose();
    };
  }

  /**
   * @public
   * @override
   */
  dispose() {
    this.disposeCarouselComboBox();
    super.dispose();
  }
}

sun.register( 'CarouselComboBox', CarouselComboBox );
export default CarouselComboBox;<commit_msg>dispose subcomponents in reverse order of creation, https://github.com/phetsims/sun/issues/673<commit_after>// Copyright 2021, University of Colorado Boulder

/**
 * CarouselComboBox behaves like a combo box, but its listbox is a carousel. This allows you to scroll through a
 * long list of items, a feature that ComboBoxListBox does not support. ComboBoxItem, ComboBoxButton, and
 * Carousel are reused.
 *
 * NOTE! This was created as a quick way to address situations where ComboBox's listbox gets too long, for example
 * https://github.com/phetsims/sun/issues/673. This tends to happen in internal 'demo' applications
 * (sun, scenery-phet,... ) that have long lists of demos. And as a design best-practice, we tend to avoid
 * longs lists of things in sims. So that's why CarouselComboBox currently lives in sun/demo/.
 * It was written to be fairly general, so should be relatively easy to relocate if it's needed for wider use.
 *
 * Possible future work:
 * - Modify ComboBox so that it can use different types of popups (ComboBoxListBox, Carousel,...), or
 * - Make CarouselComboBox pop up the Carousel in front of everything else
 * - a11y support in CarouselComboBox
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */

import Multilink from '../../../axon/js/Multilink.js';
import Property from '../../../axon/js/Property.js';
import Dimension2 from '../../../dot/js/Dimension2.js';
import dotRandom from '../../../dot/js/dotRandom.js';
import merge from '../../../phet-core/js/merge.js';
import AssertUtils from '../../../phetcommon/js/AssertUtils.js';
import PressListener from '../../../scenery/js/listeners/PressListener.js';
import AlignBox from '../../../scenery/js/nodes/AlignBox.js';
import AlignGroup from '../../../scenery/js/nodes/AlignGroup.js';
import HBox from '../../../scenery/js/nodes/HBox.js';
import Node from '../../../scenery/js/nodes/Node.js';
import Rectangle from '../../../scenery/js/nodes/Rectangle.js';
import VBox from '../../../scenery/js/nodes/VBox.js';
import Color from '../../../scenery/js/util/Color.js';
import Tandem from '../../../tandem/js/Tandem.js';
import Carousel from '../Carousel.js';
import ComboBoxButton from '../ComboBoxButton.js';
import ComboBoxItem from '../ComboBoxItem.js';
import PageControl from '../PageControl.js';
import sun from '../sun.js';

class CarouselComboBox extends Node {

  /**
   * @param {Property.<*>} property - the Property that is set when an item is selected
   * @param {ComboBoxItem[]} comboBoxItems - the items that appear in the carousel
   * @param {Object} [options]
   */
  constructor( property, comboBoxItems, options ) {

    assert && assert( property instanceof Property, 'invalid property' );
    assert && AssertUtils.assertArrayOf( comboBoxItems, ComboBoxItem );

    options = merge( {

      // Options specific to CarouselComboBox
      align: 'left', // {string} alignment of items in the carousel, 'left'|'right'|'center'
      overColor: Color.grayColor( 245 ), // {ColorDef} background color of the item that the pointer is over
      selectedColor: 'yellow', // {ColorDef} background color of the selected item
      itemXMargin: 6, // {number} x margin for backgrounds on the items in the carousel
      itemYMargin: 2, // {number} y margin for backgrounds on the items in the carousel

      // Options passed to Carousel
      carouselOptions: {
        arrowSize: new Dimension2( 20, 4 ),

        // Like ComboBox, 'vertical' is the only orientation supported (verified below).
        orientation: 'vertical',

        // Spacing and margins will be built into the backgrounds that are add to the item Nodes,
        // so set them to zero for the carousel.
        spacing: 0,
        margin: 0,

        // {number} If there are fewer items than this in the carousel, the actual number of items will be used.
        itemsPerPage: 15
      },

      pageControlOptions: {
        interactive: true
      },

      // Options passed to ComboBoxButton
      buttonOptions: {
        arrowDirection: 'down', // 'up' is not currently supported (verified below)
        baseColor: 'rgb( 218, 236, 255 )',
        xMargin: 6, // You'll typically want this to be the same as itemXMargin, but we're not going to force you :)
        yMargin: 4
      },

      // phet-io
      tandem: Tandem.OPTIONAL
    }, options );

    // Validate options
    assert && assert( options.carouselOptions.orientation === 'vertical', 'orientation must be vertical' );
    assert && assert( options.buttonOptions.arrowDirection === 'down', 'arrowDirection must be down' );

    // Check for options that are not settable by the client
    assert && assert( !options.children, 'CarouselComboBox sets children' );
    assert && assert( !options.buttonOptions.content, 'CarouselComboBox sets buttonOptions.content' );
    assert && assert( !options.buttonOptions.listener, 'CarouselComboBox sets buttonOptions.listener' );

    // Don't create pages that are longer than the number of items
    options.carouselOptions.itemsPerPage = Math.min( options.carouselOptions.itemsPerPage, comboBoxItems.length );

    // Orientations must be the same.
    assert && assert( !options.pageControlOptions.orientation, 'use carouselOptions.orientation' );
    options.pageControlOptions.orientation = options.carouselOptions.orientation;

    // Create tandems for subcomponents, if they were not provided
    options.carouselOptions.tandem = options.carouselOptions.tandem || options.tandem.createTandem( 'carousel' );
    options.buttonOptions.tandem = options.buttonOptions.tandem || options.tandem.createTandem( 'button' );

    // Make all Nodes for the comboBoxItems have the same width and height, left justified.
    const alignGroup = new AlignGroup();
    const itemNodes = _.map( comboBoxItems, item => {
      const node = new AlignBox( item.node, {
        group: alignGroup,
        xAlign: options.align
      } );
      node.itemValue = item.value; // attach the item's value to the Node, we'll need it later
      return node;
    } );

    // Size of the background that we'll put behind all item Nodes. Since we made all of the Nodes have the same
    // dimensions using AlignBox above, we can simply inspect the first Node.
    const backgroundSize = new Dimension2(
      itemNodes[ 0 ].width + 2 * options.itemXMargin,
      itemNodes[ 0 ].height + 2 * options.itemYMargin
    );

    // Create items for the carousel, whose API for 'items' is different than ComboBox. The Node for each item is a
    // label on a background. The background changes color when the item is selected or the pointer is over the item.
    const carouselItemNodes = [];
    const multilinks = [];
    for ( let i = 0; i < itemNodes.length; i++ ) {

      const itemNode = itemNodes[ i ];

      const backgroundNode = new Rectangle( 0, 0, backgroundSize.width, backgroundSize.height, {
        // Actual alignment of an item on the background is determined by AlignBox xAlign, above.
        center: itemNode.center
      } );

      const carouselItemNode = new Node( {
        children: [ backgroundNode, itemNode ]
      } );

      // Press on an item to select its associated value.
      const pressListener = new PressListener( {
        press: () => {
          property.value = itemNode.itemValue;
        }
      } );
      carouselItemNode.addInputListener( pressListener );

      // Selecting an item sets its background to the selected color.
      // Pointer over an item sets its background to the highlighted color.
      // Must be disposed because we do not own property.
      const multilink = new Multilink(
        [ property, pressListener.isOverProperty ],
        ( propertyValue, isOver ) => {
          if ( propertyValue === itemNode.itemValue ) {
            backgroundNode.fill = options.selectedColor;
          }
          else if ( isOver ) {
            backgroundNode.fill = options.overColor;
          }
          else {
            backgroundNode.fill = 'transparent';
          }
        } );
      multilinks.push( multilink );

      carouselItemNodes.push( carouselItemNode );
    }
    assert && assert( carouselItemNodes.length === comboBoxItems.length, 'expected a carouselItem for each comboBoxItem' );

    // Create the carousel.
    const carousel = new Carousel( carouselItemNodes, options.carouselOptions );

    // page control
    const pageControl = new PageControl( carousel.numberOfPages, carousel.pageNumberProperty, options.pageControlOptions );

    // Page control to the left of carousel
    const carouselAndPageControl = new HBox( {
      spacing: 4,
      children: [ carousel, pageControl ]
    } );

    // Pressing this button pops the carousel up and down
    const button = new ComboBoxButton( property, comboBoxItems, merge( {}, {
      listener: () => {
        carouselAndPageControl.visible = !carouselAndPageControl.visible;
      }
    }, options.buttonOptions ) );

    // Put the button above the carousel, left aligned.
    const vBox = new VBox( {
      spacing: 0,
      align: 'left',
      children: [ button, carouselAndPageControl ]
    } );

    // Wrap everything with Node, to hide VBox's API.
    options.children = [ vBox ];

    super( options );

    // If the Property changes, hide the carousel. unlink is needed on disposed.
    const propertyListener = () => { carouselAndPageControl.visible = false; };
    property.link( propertyListener );

    // Clicking outside this UI component will hide the carousel and page control.
    // NOTE: adapted from ComboBox.
    const clickToDismissListener = {
      down: event => {
        assert && assert( carousel.visible, 'this listener should be registered only when carousel is visible' );

        // If fuzzing is enabled, exercise this listener some percentage of the time, so that this listener is tested.
        // The rest of the time, ignore this listener, so that the carousel remains popped up, and we test making
        // choices from the carousel. See https://github.com/phetsims/sun/issues/677
        if ( !phet.chipper.isFuzzEnabled() || dotRandom.nextDouble() < 0.25 ) {
          const trail = event.trail;
          if ( !trail.containsNode( button ) && !trail.containsNode( carousel ) && !trail.containsNode( pageControl ) ) {
            carouselAndPageControl.visible = false;
          }
        }
      }
    };

    // Add clickToDismissListener only when the carousel & page control are visible. unlink is not needed.
    // NOTE: adapted from ComboBox.
    let display = null; // {Display}
    carouselAndPageControl.visibleProperty.link( visible => {
      if ( visible ) {
        assert && assert( !display, 'unexpected display' );
        display = this.getUniqueTrail().rootNode().getRootedDisplays()[ 0 ];
        display.addInputListener( clickToDismissListener );
      }
      else {
        if ( display && display.hasInputListener( clickToDismissListener ) ) {
          display.removeInputListener( clickToDismissListener );
          display = null;
        }
      }
    } );

    // @private
    this.disposeCarouselComboBox = () => {

      // Deregister observers
      property.unlink( propertyListener );
      multilinks.forEach( multilink => multilink.dispose() );

      // Dispose of subcomponents
      button.dispose();
      pageControl.dispose();
      carousel.dispose();
    };
  }

  /**
   * @public
   * @override
   */
  dispose() {
    this.disposeCarouselComboBox();
    super.dispose();
  }
}

sun.register( 'CarouselComboBox', CarouselComboBox );
export default CarouselComboBox;<|endoftext|>


<commit_before>// Copyright 2002-2014, University of Colorado Boulder

/**
 * Main ScreenView container for Buttons portion of the UI component demo.
 *
 * @author John Blanco
 */
define( function( require ) {
  'use strict';

  var Color = require( 'SCENERY/util/Color' );
  var inherit = require( 'PHET_CORE/inherit' );
  var OutsideBackgroundNode = require( 'SCENERY_PHET/OutsideBackgroundNode' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Property = require( 'AXON/Property' );
  var RectangularPushButton = require( 'SUN/experimental/buttons/RectangularPushButton' );
  var BooleanRectangularStickyToggleButton = require( 'SUN/experimental/buttons/BooleanRectangularStickyToggleButton' );
  var RefreshButton = require( 'SUN/experimental/buttons/RefreshButton' );
  var ResetAllButton = require( 'SCENERY_PHET/ResetAllButton' );
  var ResetAllButton2 = require( 'SUN/experimental/buttons/ResetAllButton2' );
  var ReturnToLevelSelectButton = require( 'SUN/experimental/buttons/ReturnToLevelSelectButton' );
  var RoundPushButton = require( 'SUN/experimental/buttons/RoundPushButton' );
  var RoundStickyToggleButton = require( 'SUN/experimental/buttons/RoundStickyToggleButton' );
  var InOutRadioButton = require( 'SUN/experimental/buttons/InOutRadioButton' );
  var ScreenView = require( 'JOIST/ScreenView' );
  var SoundToggleButton2 = require( 'SUN/experimental/buttons/SoundToggleButton2' );
  var Text = require( 'SCENERY/nodes/Text' );
  var TimerToggleButton2 = require( 'SUN/experimental/buttons/TimerToggleButton2' );
  var BooleanRectangularToggleButtonWithContent = require( 'SUN/experimental/buttons/BooleanRectangularToggleButton' );

  // Constants
  var BUTTON_FONT = new PhetFont( { size: 20 } );
  var BUTTON_CAPTION_FONT = new PhetFont( { size: 16 } );

  function ButtonsView() {
    ScreenView.call( this, { renderer: 'svg' } );

    // background
    this.addChild( new OutsideBackgroundNode( this.layoutBounds.centerX, this.layoutBounds.centerY + 20, this.layoutBounds.width * 3, this.layoutBounds.height, this.layoutBounds.height ) );
    // Set up a property for testing button enable/disable.
    var buttonsEnabled = new Property( true );

    // convenience vars for layout
    var rightEdge = this.layoutBounds.width * 0.6;
    var buttonSpacing = 10;

    // Text area for outputting test information
    var outputText = new Text( '(output text)', { font: new PhetFont( 16 ), bottom: this.layoutBounds.height - 5, left: this.layoutBounds.minX + 10  } );
    this.addChild( outputText );

    // add refresh button and caption
    var refreshButton = new RefreshButton(
      {
        listener: function() { outputText.text = 'Refresh pressed'; },
        right: rightEdge,
        top: 10
      } );
    this.addChild( refreshButton );
    var refreshButtonLabel = new Text( 'Refresh Button: ', { font: BUTTON_CAPTION_FONT, right: refreshButton.left - 5, centerY: refreshButton.centerY } );
    this.addChild( refreshButtonLabel );

    // add return to level select button and caption
    var returnToLevelSelectButton = new ReturnToLevelSelectButton(
      {
        listener: function() { outputText.text = 'Return to level select pressed'; },
        centerX: refreshButton.centerX,
        top: refreshButton.bottom + buttonSpacing
      } );
    this.addChild( returnToLevelSelectButton );
    var returnToLevelSelectButtonLabel = new Text( 'Return to Level Selection Button: ', { font: BUTTON_CAPTION_FONT, right: returnToLevelSelectButton.left - 5, centerY: returnToLevelSelectButton.centerY } );
    this.addChild( returnToLevelSelectButtonLabel );

    // add sound toggle button
    var soundEnabled = new Property( true );
    var soundToggleButton = new SoundToggleButton2( soundEnabled, { centerX: refreshButton.centerX, top: returnToLevelSelectButton.bottom + buttonSpacing } );
    this.addChild( soundToggleButton );
    var soundToggleButtonLabel = new Text( 'Sound Toggle Button: ', { font: BUTTON_CAPTION_FONT, right: soundToggleButton.left - 5, centerY: soundToggleButton.centerY } );
    this.addChild( soundToggleButtonLabel );

    // add timer toggle button
    var timerEnabled = new Property( true );
    var timerToggleButton = new TimerToggleButton2( timerEnabled, { centerX: refreshButton.centerX, y: soundToggleButton.bottom + 5 } );
    this.addChild( timerToggleButton );
    var timerToggleButtonLabel = new Text( 'Timer Toggle Button: ', { font: BUTTON_CAPTION_FONT, right: timerToggleButton.left - 5, centerY: timerToggleButton.centerY } );
    this.addChild( timerToggleButtonLabel );

    // Count variables, declared here so that they can be reset.
    var buttonCFireCount = 0;
    var fireOnDownCount = 0;

    // Reset function
    function resetAll() {
      outputText.text = 'Reset All pressed';
      buttonsEnabled.reset();
      soundEnabled.reset();
      timerEnabled.reset();
      buttonCFireCount = 0;
      fireOnDownCount = 0;
      roundToggleButtonProperty.reset();
      rectangularToggleButtonProperty.reset();
    }

    // add reset all button and caption
    var resetAllButton = new ResetAllButton( resetAll, { radius: 22, centerX: refreshButton.centerX, top: timerToggleButton.bottom + buttonSpacing } );
    this.addChild( resetAllButton );
    var resetAllButtonLabel = new Text( 'Reset All Button: ', { font: BUTTON_CAPTION_FONT, right: resetAllButton.left - 5, centerY: resetAllButton.centerY } );
    this.addChild( resetAllButtonLabel );

    // Test button behavior.
    var buttonA = new RectangularPushButton(
      {
        content: new Text( '--- A ---', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Button A pressed'; },
        left: 100,
        top: 300
      } );
    this.addChild( buttonA );

    var buttonB = new RectangularPushButton(
      {
        content: new Text( '--- B ---', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Button B pressed'; },
        left: buttonA.right + 10,
        centerY: buttonA.centerY,
        baseColor: new Color( 250, 0, 0 )
      } );
    this.addChild( buttonB );

    var buttonC = new RectangularPushButton(
      {
        content: new Text( '--- C ---', { font: BUTTON_FONT } ),
        listener: function() {
          outputText.text = 'Button C pressed ' + ( ++buttonCFireCount ) + 'x';
        },
        left: buttonB.right + 10,
        centerY: buttonB.centerY,
        baseColor: new Color( 204, 102, 204 )
      } );
    this.addChild( buttonC );

    var fireOnDownButton = new RectangularPushButton(
      {
        content: new Text( 'Fire on Down Button', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Fire on down button pressed ' + ( ++fireOnDownCount ) + 'x'; },
        left: buttonC.right + 30,
        centerY: buttonC.centerY,
        baseColor: new Color( 255, 255, 61 ),
        fireOnDown: true,
        stroke: 'black'
      } );
    this.addChild( fireOnDownButton );

    var buttonEnableButton = new BooleanRectangularToggleButtonWithContent(
      new Text( 'Disable Buttons', { font: BUTTON_CAPTION_FONT } ),
      new Text( 'Enable Buttons', { font: BUTTON_CAPTION_FONT } ),
      buttonsEnabled,
      { baseColor: new Color( 204, 255, 51 ), left: buttonA.left, top: buttonA.bottom + 30 }
    );
    this.addChild( buttonEnableButton );

    // add alternative reset all button
    var resetAllButton2 = new ResetAllButton2( { listener: resetAll, radius: 22, centerX: buttonC.centerX, centerY: buttonEnableButton.centerY } );
    this.addChild( resetAllButton2 );

    var buttonD = new RoundPushButton(
      {
        content: new Text( '--- D ---', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Button D pressed'; },
        left: resetAllButton2.right + buttonSpacing,
        centerY: resetAllButton2.centerY
      } );
    this.addChild( buttonD );

    var buttonE = new RoundPushButton(
      {
        content: new Text( '--- E ---', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Button E pressed'; },
        baseColor: new Color( 245, 184, 0 ),
        left: buttonD.right + buttonSpacing,
        centerY: buttonD.centerY
      } );
    this.addChild( buttonE );

    var fireButton = new RoundPushButton(
      {
        content: new Text( 'Fire!', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Fire button pressed'; },
        baseColor: new Color( 255, 100, 51 ),
        left: buttonE.right + buttonSpacing,
        centerY: buttonE.centerY,
        stroke: 'black',
        lineWidth: 0.5
      } );
    this.addChild( fireButton );

    var goButton = new RoundPushButton(
      {
        content: new Text( 'Go!', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Go button pressed'; },
        baseColor: new Color( 0, 163, 0 ),
        minXPadding: 10,
        centerX: resetAllButton2.centerX,
        top: buttonE.bottom + 5
      } );
    this.addChild( goButton );

    var helpButtonBaseColor = new Color( 244, 154, 194 );
    var helpButton = new RoundPushButton(
      {
        content: new Text( 'Help', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Help button pressed'; },
        baseColor: new Color( 244, 154, 194 ),
        minXPadding: 10,
        left: goButton.right + 5,
        centerY: goButton.centerY,
        stroke: helpButtonBaseColor.colorUtilsDarker( 0.4 ),
        lineWidth: 0.5
      } );
    this.addChild( helpButton );

    //Demonstrate using arbitrary values for toggle button.  Wrap in extra quotes so it is clear that it is a string in the debugging UI.
    var roundToggleButtonProperty = new Property( '"off"' );
    roundToggleButtonProperty.lazyLink( function( toggleButton ) {
      outputText.text = "Round sticky toggle button state changed to: " + toggleButton;
    } );
    var roundStickyToggleButton = new RoundStickyToggleButton( '"off"', '"on"', roundToggleButtonProperty, {
      baseColor: new Color( 255, 0, 0 ),
      left: helpButton.right + 5,
      centerY: goButton.centerY
    } );
    this.addChild( roundStickyToggleButton );

    var rectangularToggleButtonProperty = new Property( false );
    rectangularToggleButtonProperty.lazyLink( function( toggleButton ) {
      outputText.text = "Rectangular sticky toggle button state changed to: " + toggleButton;
    } );
    var booleanRectangularStickyToggleButton = new BooleanRectangularStickyToggleButton( rectangularToggleButtonProperty, {
      baseColor: new Color( 0, 200, 200 ),
      left: roundStickyToggleButton.right + 10,
      centerY: goButton.centerY,
      minWidth: 50,
      minHeight: 35
    } );
    this.addChild( booleanRectangularStickyToggleButton );

    var inOutRadioButton = new InOutRadioButton( new Property( true ), true, new Text( 'In/Out' ) );
    this.addChild( inOutRadioButton );

    var transparentButton = new RectangularPushButton(
      {
        content: new Text( 'Transparent Button', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Transparent button pressed'; },
        left: helpButton.centerX,
        top: roundStickyToggleButton.bottom - 10,
        baseColor: new Color( 255, 255, 0, 0.7 )
      } );
    this.addChild( transparentButton );

    // Hook up button enable property
    buttonsEnabled.link( function( enabled ) {
      buttonA.enabled = enabled;
      buttonB.enabled = enabled;
      buttonC.enabled = enabled;
      buttonD.enabled = enabled;
      buttonE.enabled = enabled;
      fireOnDownButton.enabled = enabled;
      fireButton.enabled = enabled;
      goButton.enabled = enabled;
      helpButton.enabled = enabled;
      refreshButton.enabled = enabled;
      returnToLevelSelectButton.enabled = enabled;
      soundToggleButton.enabled = enabled;
      timerToggleButton.enabled = enabled;
      roundStickyToggleButton.enabled = enabled;
      booleanRectangularStickyToggleButton.enabled = enabled;
    } );

    // TODO: For debug, don't leave this here long term.
    var debugText = new Text( '(debug text)', { font: new PhetFont( 16 ), bottom: outputText.top - 5, left: this.layoutBounds.minX + 10  } );
    this.addChild( debugText );
    window.debugText = debugText;
  }

  return inherit( ScreenView, ButtonsView, {
    step: function( timeElapsed ) {
      // Does nothing for now.
    }
  } );
} );
<commit_msg>Removed an unused option - "toggleOnDown".<commit_after>// Copyright 2002-2014, University of Colorado Boulder

/**
 * Main ScreenView container for Buttons portion of the UI component demo.
 *
 * @author John Blanco
 */
define( function( require ) {
  'use strict';

  var Color = require( 'SCENERY/util/Color' );
  var inherit = require( 'PHET_CORE/inherit' );
  var OutsideBackgroundNode = require( 'SCENERY_PHET/OutsideBackgroundNode' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Property = require( 'AXON/Property' );
  var RectangularPushButton = require( 'SUN/experimental/buttons/RectangularPushButton' );
  var BooleanRectangularStickyToggleButton = require( 'SUN/experimental/buttons/BooleanRectangularStickyToggleButton' );
  var RefreshButton = require( 'SUN/experimental/buttons/RefreshButton' );
  var ResetAllButton = require( 'SCENERY_PHET/ResetAllButton' );
  var ResetAllButton2 = require( 'SUN/experimental/buttons/ResetAllButton2' );
  var ReturnToLevelSelectButton = require( 'SUN/experimental/buttons/ReturnToLevelSelectButton' );
  var RoundPushButton = require( 'SUN/experimental/buttons/RoundPushButton' );
  var RoundStickyToggleButton = require( 'SUN/experimental/buttons/RoundStickyToggleButton' );
  var InOutRadioButton = require( 'SUN/experimental/buttons/InOutRadioButton' );
  var ScreenView = require( 'JOIST/ScreenView' );
  var SoundToggleButton2 = require( 'SUN/experimental/buttons/SoundToggleButton2' );
  var Text = require( 'SCENERY/nodes/Text' );
  var TimerToggleButton2 = require( 'SUN/experimental/buttons/TimerToggleButton2' );
  var BooleanRectangularToggleButtonWithContent = require( 'SUN/experimental/buttons/BooleanRectangularToggleButton' );

  // Constants
  var BUTTON_FONT = new PhetFont( { size: 20 } );
  var BUTTON_CAPTION_FONT = new PhetFont( { size: 16 } );

  function ButtonsView() {
    ScreenView.call( this, { renderer: 'svg' } );

    // background
    this.addChild( new OutsideBackgroundNode( this.layoutBounds.centerX, this.layoutBounds.centerY + 20, this.layoutBounds.width * 3, this.layoutBounds.height, this.layoutBounds.height ) );
    // Set up a property for testing button enable/disable.
    var buttonsEnabled = new Property( true );

    // convenience vars for layout
    var rightEdge = this.layoutBounds.width * 0.6;
    var buttonSpacing = 10;

    // Text area for outputting test information
    var outputText = new Text( '(output text)', { font: new PhetFont( 16 ), bottom: this.layoutBounds.height - 5, left: this.layoutBounds.minX + 10  } );
    this.addChild( outputText );

    // add refresh button and caption
    var refreshButton = new RefreshButton(
      {
        listener: function() { outputText.text = 'Refresh pressed'; },
        right: rightEdge,
        top: 10
      } );
    this.addChild( refreshButton );
    var refreshButtonLabel = new Text( 'Refresh Button: ', { font: BUTTON_CAPTION_FONT, right: refreshButton.left - 5, centerY: refreshButton.centerY } );
    this.addChild( refreshButtonLabel );

    // add return to level select button and caption
    var returnToLevelSelectButton = new ReturnToLevelSelectButton(
      {
        listener: function() { outputText.text = 'Return to level select pressed'; },
        centerX: refreshButton.centerX,
        top: refreshButton.bottom + buttonSpacing
      } );
    this.addChild( returnToLevelSelectButton );
    var returnToLevelSelectButtonLabel = new Text( 'Return to Level Selection Button: ', { font: BUTTON_CAPTION_FONT, right: returnToLevelSelectButton.left - 5, centerY: returnToLevelSelectButton.centerY } );
    this.addChild( returnToLevelSelectButtonLabel );

    // add sound toggle button
    var soundEnabled = new Property( true );
    var soundToggleButton = new SoundToggleButton2( soundEnabled, { centerX: refreshButton.centerX, top: returnToLevelSelectButton.bottom + buttonSpacing } );
    this.addChild( soundToggleButton );
    var soundToggleButtonLabel = new Text( 'Sound Toggle Button: ', { font: BUTTON_CAPTION_FONT, right: soundToggleButton.left - 5, centerY: soundToggleButton.centerY } );
    this.addChild( soundToggleButtonLabel );

    // add timer toggle button
    var timerEnabled = new Property( true );
    var timerToggleButton = new TimerToggleButton2( timerEnabled, { centerX: refreshButton.centerX, y: soundToggleButton.bottom + 5 } );
    this.addChild( timerToggleButton );
    var timerToggleButtonLabel = new Text( 'Timer Toggle Button: ', { font: BUTTON_CAPTION_FONT, right: timerToggleButton.left - 5, centerY: timerToggleButton.centerY } );
    this.addChild( timerToggleButtonLabel );

    // Count variables, declared here so that they can be reset.
    var buttonCFireCount = 0;
    var fireOnDownCount = 0;

    // Reset function
    function resetAll() {
      outputText.text = 'Reset All pressed';
      buttonsEnabled.reset();
      soundEnabled.reset();
      timerEnabled.reset();
      buttonCFireCount = 0;
      fireOnDownCount = 0;
      roundToggleButtonProperty.reset();
      rectangularToggleButtonProperty.reset();
    }

    // add reset all button and caption
    var resetAllButton = new ResetAllButton( resetAll, { radius: 22, centerX: refreshButton.centerX, top: timerToggleButton.bottom + buttonSpacing } );
    this.addChild( resetAllButton );
    var resetAllButtonLabel = new Text( 'Reset All Button: ', { font: BUTTON_CAPTION_FONT, right: resetAllButton.left - 5, centerY: resetAllButton.centerY } );
    this.addChild( resetAllButtonLabel );

    // Test button behavior.
    var buttonA = new RectangularPushButton(
      {
        content: new Text( '--- A ---', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Button A pressed'; },
        left: 100,
        top: 300
      } );
    this.addChild( buttonA );

    var buttonB = new RectangularPushButton(
      {
        content: new Text( '--- B ---', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Button B pressed'; },
        left: buttonA.right + 10,
        centerY: buttonA.centerY,
        baseColor: new Color( 250, 0, 0 )
      } );
    this.addChild( buttonB );

    var buttonC = new RectangularPushButton(
      {
        content: new Text( '--- C ---', { font: BUTTON_FONT } ),
        listener: function() {
          outputText.text = 'Button C pressed ' + ( ++buttonCFireCount ) + 'x';
        },
        left: buttonB.right + 10,
        centerY: buttonB.centerY,
        baseColor: new Color( 204, 102, 204 )
      } );
    this.addChild( buttonC );

    var fireOnDownButton = new RectangularPushButton(
      {
        content: new Text( 'Fire on Down Button', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Fire on down button pressed ' + ( ++fireOnDownCount ) + 'x'; },
        left: buttonC.right + 30,
        centerY: buttonC.centerY,
        baseColor: new Color( 255, 255, 61 ),
        fireOnDown: true,
        stroke: 'black'
      } );
    this.addChild( fireOnDownButton );

    var buttonEnableButton = new BooleanRectangularToggleButtonWithContent(
      new Text( 'Disable Buttons', { font: BUTTON_CAPTION_FONT } ),
      new Text( 'Enable Buttons', { font: BUTTON_CAPTION_FONT } ),
      buttonsEnabled,
      { baseColor: new Color( 204, 255, 51 ), left: buttonA.left, top: buttonA.bottom + 30 }
    );
    this.addChild( buttonEnableButton );

    // add alternative reset all button
    var resetAllButton2 = new ResetAllButton2( { listener: resetAll, radius: 22, centerX: buttonC.centerX, centerY: buttonEnableButton.centerY } );
    this.addChild( resetAllButton2 );

    var buttonD = new RoundPushButton(
      {
        content: new Text( '--- D ---', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Button D pressed'; },
        left: resetAllButton2.right + buttonSpacing,
        centerY: resetAllButton2.centerY
      } );
    this.addChild( buttonD );

    var buttonE = new RoundPushButton(
      {
        content: new Text( '--- E ---', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Button E pressed'; },
        baseColor: new Color( 245, 184, 0 ),
        left: buttonD.right + buttonSpacing,
        centerY: buttonD.centerY
      } );
    this.addChild( buttonE );

    var fireButton = new RoundPushButton(
      {
        content: new Text( 'Fire!', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Fire button pressed'; },
        baseColor: new Color( 255, 100, 51 ),
        left: buttonE.right + buttonSpacing,
        centerY: buttonE.centerY,
        stroke: 'black',
        lineWidth: 0.5
      } );
    this.addChild( fireButton );

    var goButton = new RoundPushButton(
      {
        content: new Text( 'Go!', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Go button pressed'; },
        baseColor: new Color( 0, 163, 0 ),
        minXPadding: 10,
        centerX: resetAllButton2.centerX,
        top: buttonE.bottom + 5
      } );
    this.addChild( goButton );

    var helpButtonBaseColor = new Color( 244, 154, 194 );
    var helpButton = new RoundPushButton(
      {
        content: new Text( 'Help', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Help button pressed'; },
        baseColor: new Color( 244, 154, 194 ),
        minXPadding: 10,
        left: goButton.right + 5,
        centerY: goButton.centerY,
        stroke: helpButtonBaseColor.colorUtilsDarker( 0.4 ),
        lineWidth: 0.5
      } );
    this.addChild( helpButton );

    // Demonstrate using arbitrary values for toggle button.  Wrap in extra
    // quotes so it is clear that it is a string in the debugging UI.
    var roundToggleButtonProperty = new Property( '"off"' );
    roundToggleButtonProperty.lazyLink( function( toggleButton ) {
      outputText.text = "Round sticky toggle button state changed to: " + toggleButton;
    } );
    var roundStickyToggleButton = new RoundStickyToggleButton( '"off"', '"on"', roundToggleButtonProperty, {
      baseColor: new Color( 255, 0, 0 ),
      left: helpButton.right + 5,
      centerY: goButton.centerY
    } );
    this.addChild( roundStickyToggleButton );

    var rectangularToggleButtonProperty = new Property( false );
    rectangularToggleButtonProperty.lazyLink( function( toggleButton ) {
      outputText.text = 'Rectangular sticky toggle button state changed to: ' + toggleButton;
    } );
    var booleanRectangularStickyToggleButton = new BooleanRectangularStickyToggleButton( rectangularToggleButtonProperty, {
      baseColor: new Color( 0, 200, 200 ),
      left: roundStickyToggleButton.right + 10,
      centerY: goButton.centerY,
      minWidth: 50,
      minHeight: 35
    } );
    this.addChild( booleanRectangularStickyToggleButton );

    var inOutRadioButton = new InOutRadioButton( new Property( true ), true, new Text( 'In/Out' ) );
    this.addChild( inOutRadioButton );

    var transparentButton = new RectangularPushButton(
      {
        content: new Text( 'Transparent Button', { font: BUTTON_FONT } ),
        listener: function() { outputText.text = 'Transparent button pressed'; },
        left: helpButton.centerX,
        top: roundStickyToggleButton.bottom - 10,
        baseColor: new Color( 255, 255, 0, 0.7 )
      } );
    this.addChild( transparentButton );

    // Hook up button enable property
    buttonsEnabled.link( function( enabled ) {
      buttonA.enabled = enabled;
      buttonB.enabled = enabled;
      buttonC.enabled = enabled;
      buttonD.enabled = enabled;
      buttonE.enabled = enabled;
      fireOnDownButton.enabled = enabled;
      fireButton.enabled = enabled;
      goButton.enabled = enabled;
      helpButton.enabled = enabled;
      refreshButton.enabled = enabled;
      returnToLevelSelectButton.enabled = enabled;
      soundToggleButton.enabled = enabled;
      timerToggleButton.enabled = enabled;
      roundStickyToggleButton.enabled = enabled;
      booleanRectangularStickyToggleButton.enabled = enabled;
    } );

    // TODO: For debug, don't leave this here long term.
    var debugText = new Text( '(debug text)', { font: new PhetFont( 16 ), bottom: outputText.top - 5, left: this.layoutBounds.minX + 10  } );
    this.addChild( debugText );
    window.debugText = debugText;
  }

  return inherit( ScreenView, ButtonsView, {
    step: function( timeElapsed ) {
      // Does nothing for now.
    }
  } );
} );
<|endoftext|>


<commit_before>

function Documents(renderer) {
	var me = this;

	var documents = [];
	documentsIndex = {};
	for (var i = 0; i < $documents.length; i++) {
		documents[i] = new Document($documents[i], i, renderer);
		documentsIndex[$documents[i].title] = i;
	}

	me.newLayout = function (layout, duration) {
		var f = layout.projection();
		var delayFactor = (duration > 0) ? 1 : 0;
		for (var i = 0; i < documents.length; i++) {
			documents[i].newPosition(f, duration, delayFactor*(documents.length-i));
		}
		$('#canvas').animate({height:layout.maxY+50}, duration);
		$('#main').animate({height:layout.maxY+100}, duration);
	}

	me.updateResultMarkers = function (showResult) {
		if (showResult) {
			var max = 0.0001;
			for (var i = 0; i < $documents.length; i++) {
				if (max < $documents[i].resultCount) max = $documents[i].resultCount
			}
			for (var i = 0; i < documents.length; i++) documents[i].updateResultMarker(showResult, max);
		} else {
			for (var i = 0; i < documents.length; i++) documents[i].updateResultMarker(showResult);
		}
	}

	me.docs = documents;

	return me;
}

var documentsReverse = [];

function Document(data, index, renderer) {
	var me = this;
	me.data = data;
	var thumbId = data.t.charAt(0);
	var imageUrl = 'style/thumb'+thumbId+'-transparent.png';
	var color = qualityToColor(data.qualitySum);

	function startReader(index, data, viewObject) {
	}

	var viewObject = renderer.drawImage(imageUrl, 'thumb', color, 'Unterrichtung des Parlaments '+data.title_);

	$(viewObject).click(function(){
		Lightbox(data.title+"-01");
	});

	for (var z=0;z<data.c;z++) {
		z=(z<10)?'0'+z:z;
		documentsReverse.push(data.title+"-"+z);
	}

	viewObject.popover({
		html:true,
		content:function () {
			var thumbs = [];
			var maxY = 0;
			for (var i = 0; i < data.c; i++) {
				var resultCount = $pages[data.pageIds[i]].resultCount;
				var opacity = (resultCount < 1) ? 0.1 : 1;
				var t = data.t.charAt(i);
				var color = qualityToColor(data.quality[i]);
				var x = (i % 6)*37;
				var y = Math.floor(i / 6)*46;
				thumbs.push('

');
				if (maxY < y) maxY = y;
			}
			return '
'+thumbs.join('')+'
';
		},
		trigger:'hover',
		placement:'bottom'
	});

	me.newPosition = function (f, duration, delay) {
		var pos = f(index, data);
		data.pos = pos;
		if (duration <= 0) {
			renderer.setPosition(viewObject, pos.x, pos.y);
		} else {
			renderer.moveToPosition(viewObject, pos.x, pos.y, duration, delay);
		}
	}

	me.updateResultMarker = function (showResult, max) {
		var duration = 250;
		var opacity = 1;
		var scale = 1;
		if (showResult) {
			if (data.resultCount <= 0) {
				opacity = 0.1;
			} else {
				opacity = Math.min(0.3 + data.resultCount/max, 1);
			}
		}
		viewObject.transition({opacity:opacity, duration:0 });
	}

	return me;
}

function qualityToColor(v) {
	// rot, gelb, grün
	var r = Math.round((v < 1 ? 1 : 2-v)*255);
	var g = Math.round((v < 1 ? v : 1  )*255);
	var b = 0;
	return 'rgb('+r+','+g+','+b+')';
}

function Renderer(target) {
	var me = this;

	me.drawImage = function (url, className, backgroundColor, title) {
		var div = $('

');
		//var img = $('');
		//div.append(img);
		target.append(div);
		return div;
	}

	me.setPosition = function (obj, x, y) {
		obj.css({left:x, top:y})
	}

	me.moveToPosition = function (obj, x, y, duration, delay) {
		if (Modernizr.csstransforms) {
			obj.transition({left:x+'px', top:y+'px', duration:duration, delay:delay, easing:'in-out'})
		} else {
			obj.animate({left:x, top:y}, duration, 'smooth')
		}
	}

	me.click = function (obj, f) {
		obj.click(f);
	}

	return me;
}

$.easing.smooth = function(p) {
	return p < 0.5 ?
		Math.pow( p * 2, 3) / 2 :
		1 - Math.pow( p * -2 + 2, 3 ) / 2;
}

function Lightbox(h) {

	var issue = h.substr(0,7);
	var page = h.substr(8,2);

	var data = documents.docs[documentsIndex[issue]].data;

	var $lb = $('#lightbox');

	if ($lb.data('issue') !== issue) {

		$lb.fadeOut('fast');
		$lb.data({
			year: data.j,
			week: data.w,
			page: page,
			pages: data.c,
			issue: issue
		});

		var $lbnav = $('#lightbox-navigation-items');

		$lbnav.html('');

		var $lbel;

		for (var i=1; i <= data.c; i++) {

			d = (i<10)?'0'+i:i;

			$lbel = $('irks');
			$lbel.data({issue:issue,page:d});
			$lbel.click(function(){
				$(this).blur();
				location.hash='!/'+$(this).data('issue')+'-'+$(this).data('page');
				LightboxPage($(this).data('issue'), $(this).data('page'));
			});
			$lbel.tooltip({
				title: "Seite "+i,
				placement: "top"
			});
			$lbel.append('');
			$lbnav.append($lbel);

		}

		$lbnav.css({width: (data.c*40 + 60)});

	}

	$lb.fadeIn('fast');

	LightboxPage(issue, page);

}

function LightboxPage(issue,page) {

	if (currentDoc === issue+'-'+page) {
		return;
	} else {
		currentDoc = issue+'-'+page;
		location.hash = '!/'+issue+'-'+page;
	}

	var conf = $('#lightbox').data();

	$('#lightbox-header').html('
'+conf.week+'/'+conf.year+'

Seite '+page+'/'+conf.pages+'
');
	$('#lightbox-viewport-doc').html('');

	$('#lightbox-viewport-doc').html('');

	$('#lightbox-viewport-trans').addClass('loading');

	$.ajax({
		url: '/data/t/'+issue+'-'+page+'.json',
		method: 'GET',
		contentType: 'json',
		success: function(data) {
			$('#lightbox-viewport-trans').html('
'+data.content+'
');
			$('#lightbox-viewport-trans').removeClass('loading');
			setTimeout(function(){
				$('#lightbox-viewport-trans').removeClass('loading');
			},1000);
		}
	});

	$('#lightbox-viewport-improve-button').attr('href','http://wiki.derwesten-recherche.org/index.php?title='+issue+'-'+page+'&action=edit');

	$('#share-twitter').attr('href','https://twitter.com/intent/tweet?url=http%3A%2F%2Fafghanistan.derwesten-recherche.de%2F%23%21%2F'+issue+'-'+page+'&text=Die+Afghanistan+Papiere%2C+Ausgabe+'+conf.week+'%2F'+conf.year+'+Seite+'+page+'+%23afghanistan+%23leak&via=wazrechereche&related=wazrechereche');
	$('#share-facebook').attr('href','http://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fafghanistan.derwesten-recherche.de%2F%23%21%2F'+issue+'-'+page+'&t=Die+Afghanistan+Papiere%2C+Ausgabe+'+conf.week+'%2F'+conf.year+'+Seite+'+page);
	$('#share-google').attr('href','https://plus.google.com/share?url=http%3A%2F%2Fafghanistan.derwesten-recherche.de%2F%23%21%2F'+issue+'-'+page);

 	disqus_identifier = currentDoc;
 	disqus_url = location.href;
	disqus_title = "Die Afghanistan Papiere "+conf.week+'/'+conf.year+' Seite '+page;

	DISQUS.reset({
		reload: true,
		config: function () {
		    this.page.identifier = disqus_identifier;
		    this.page.url = location.href;
			this.page.title = disqus_title;
		  }
	});

	$('.lightbox-item').removeClass('active');
	$('#lightbox-item-'+issue+'-'+page).addClass('active');

}

function hashCheck() {

	if (location.hash && location.hash.substr(3,10).match(/^20[0-9]{2}-[0-9]{2}-[0-9]{2}/) && documentsIndex.hasOwnProperty(location.hash.substr(3,7))) {

		Lightbox(location.hash.substr(3));

	}

}

function randomDoc() {
	return documentsReverse[(parseInt(Math.random()*10000000)%documentsReverse.length)];
}
<commit_msg>workaround for disqus initiialization error<commit_after>

function Documents(renderer) {
	var me = this;

	var documents = [];
	documentsIndex = {};
	for (var i = 0; i < $documents.length; i++) {
		documents[i] = new Document($documents[i], i, renderer);
		documentsIndex[$documents[i].title] = i;
	}

	me.newLayout = function (layout, duration) {
		var f = layout.projection();
		var delayFactor = (duration > 0) ? 1 : 0;
		for (var i = 0; i < documents.length; i++) {
			documents[i].newPosition(f, duration, delayFactor*(documents.length-i));
		}
		$('#canvas').animate({height:layout.maxY+50}, duration);
		$('#main').animate({height:layout.maxY+100}, duration);
	}

	me.updateResultMarkers = function (showResult) {
		if (showResult) {
			var max = 0.0001;
			for (var i = 0; i < $documents.length; i++) {
				if (max < $documents[i].resultCount) max = $documents[i].resultCount
			}
			for (var i = 0; i < documents.length; i++) documents[i].updateResultMarker(showResult, max);
		} else {
			for (var i = 0; i < documents.length; i++) documents[i].updateResultMarker(showResult);
		}
	}

	me.docs = documents;

	return me;
}

var documentsReverse = [];

function Document(data, index, renderer) {
	var me = this;
	me.data = data;
	var thumbId = data.t.charAt(0);
	var imageUrl = 'style/thumb'+thumbId+'-transparent.png';
	var color = qualityToColor(data.qualitySum);

	function startReader(index, data, viewObject) {
	}

	var viewObject = renderer.drawImage(imageUrl, 'thumb', color, 'Unterrichtung des Parlaments '+data.title_);

	$(viewObject).click(function(){
		Lightbox(data.title+"-01");
	});

	for (var z=0;z<data.c;z++) {
		z=(z<10)?'0'+z:z;
		documentsReverse.push(data.title+"-"+z);
	}

	viewObject.popover({
		html:true,
		content:function () {
			var thumbs = [];
			var maxY = 0;
			for (var i = 0; i < data.c; i++) {
				var resultCount = $pages[data.pageIds[i]].resultCount;
				var opacity = (resultCount < 1) ? 0.1 : 1;
				var t = data.t.charAt(i);
				var color = qualityToColor(data.quality[i]);
				var x = (i % 6)*37;
				var y = Math.floor(i / 6)*46;
				thumbs.push('

');
				if (maxY < y) maxY = y;
			}
			return '
'+thumbs.join('')+'
';
		},
		trigger:'hover',
		placement:'bottom'
	});

	me.newPosition = function (f, duration, delay) {
		var pos = f(index, data);
		data.pos = pos;
		if (duration <= 0) {
			renderer.setPosition(viewObject, pos.x, pos.y);
		} else {
			renderer.moveToPosition(viewObject, pos.x, pos.y, duration, delay);
		}
	}

	me.updateResultMarker = function (showResult, max) {
		var duration = 250;
		var opacity = 1;
		var scale = 1;
		if (showResult) {
			if (data.resultCount <= 0) {
				opacity = 0.1;
			} else {
				opacity = Math.min(0.3 + data.resultCount/max, 1);
			}
		}
		viewObject.transition({opacity:opacity, duration:0 });
	}

	return me;
}

function qualityToColor(v) {
	// rot, gelb, grün
	var r = Math.round((v < 1 ? 1 : 2-v)*255);
	var g = Math.round((v < 1 ? v : 1  )*255);
	var b = 0;
	return 'rgb('+r+','+g+','+b+')';
}

function Renderer(target) {
	var me = this;

	me.drawImage = function (url, className, backgroundColor, title) {
		var div = $('

');
		//var img = $('');
		//div.append(img);
		target.append(div);
		return div;
	}

	me.setPosition = function (obj, x, y) {
		obj.css({left:x, top:y})
	}

	me.moveToPosition = function (obj, x, y, duration, delay) {
		if (Modernizr.csstransforms) {
			obj.transition({left:x+'px', top:y+'px', duration:duration, delay:delay, easing:'in-out'})
		} else {
			obj.animate({left:x, top:y}, duration, 'smooth')
		}
	}

	me.click = function (obj, f) {
		obj.click(f);
	}

	return me;
}

$.easing.smooth = function(p) {
	return p < 0.5 ?
		Math.pow( p * 2, 3) / 2 :
		1 - Math.pow( p * -2 + 2, 3 ) / 2;
}

function Lightbox(h) {

	var issue = h.substr(0,7);
	var page = h.substr(8,2);

	var data = documents.docs[documentsIndex[issue]].data;

	var $lb = $('#lightbox');

	if ($lb.data('issue') !== issue) {

		$lb.fadeOut('fast');
		$lb.data({
			year: data.j,
			week: data.w,
			page: page,
			pages: data.c,
			issue: issue
		});

		var $lbnav = $('#lightbox-navigation-items');

		$lbnav.html('');

		var $lbel;

		for (var i=1; i <= data.c; i++) {

			d = (i<10)?'0'+i:i;

			$lbel = $('irks');
			$lbel.data({issue:issue,page:d});
			$lbel.click(function(){
				$(this).blur();
				location.hash='!/'+$(this).data('issue')+'-'+$(this).data('page');
				LightboxPage($(this).data('issue'), $(this).data('page'));
			});
			$lbel.tooltip({
				title: "Seite "+i,
				placement: "top"
			});
			$lbel.append('');
			$lbnav.append($lbel);

		}

		$lbnav.css({width: (data.c*40 + 60)});

	}

	$lb.fadeIn('fast');

	LightboxPage(issue, page);

}

function LightboxPage(issue,page) {

	if (currentDoc === issue+'-'+page) {
		return;
	} else {
		currentDoc = issue+'-'+page;
		location.hash = '!/'+issue+'-'+page;
	}

	var conf = $('#lightbox').data();

	$('#lightbox-header').html('
'+conf.week+'/'+conf.year+'

Seite '+page+'/'+conf.pages+'
');
	$('#lightbox-viewport-doc').html('');

	$('#lightbox-viewport-doc').html('');

	$('#lightbox-viewport-trans').addClass('loading');

	$.ajax({
		url: '/data/t/'+issue+'-'+page+'.json',
		method: 'GET',
		contentType: 'json',
		success: function(data) {
			$('#lightbox-viewport-trans').html('
'+data.content+'
');
			$('#lightbox-viewport-trans').removeClass('loading');
			setTimeout(function(){
				$('#lightbox-viewport-trans').removeClass('loading');
			},1000);
		}
	});

	$('#lightbox-viewport-improve-button').attr('href','http://wiki.derwesten-recherche.org/index.php?title='+issue+'-'+page+'&action=edit');

	$('#share-twitter').attr('href','https://twitter.com/intent/tweet?url=http%3A%2F%2Fafghanistan.derwesten-recherche.de%2F%23%21%2F'+issue+'-'+page+'&text=Die+Afghanistan+Papiere%2C+Ausgabe+'+conf.week+'%2F'+conf.year+'+Seite+'+page+'+%23afghanistan+%23leak&via=wazrechereche&related=wazrechereche');
	$('#share-facebook').attr('href','http://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fafghanistan.derwesten-recherche.de%2F%23%21%2F'+issue+'-'+page+'&t=Die+Afghanistan+Papiere%2C+Ausgabe+'+conf.week+'%2F'+conf.year+'+Seite+'+page);
	$('#share-google').attr('href','https://plus.google.com/share?url=http%3A%2F%2Fafghanistan.derwesten-recherche.de%2F%23%21%2F'+issue+'-'+page);

 	disqus_identifier = currentDoc;
 	disqus_url = location.href;
	disqus_title = "Die Afghanistan Papiere "+conf.week+'/'+conf.year+' Seite '+page;

	if (typeof DISQUS !== "undefined") {
		DISQUS.reset({
			reload: true,
			config: function () {
			    this.page.identifier = disqus_identifier;
			    this.page.url = location.href;
				this.page.title = disqus_title;
			  }
		});
	}

	$('.lightbox-item').removeClass('active');
	$('#lightbox-item-'+issue+'-'+page).addClass('active');

}

function hashCheck() {

	if (location.hash && location.hash.substr(3,10).match(/^20[0-9]{2}-[0-9]{2}-[0-9]{2}/) && documentsIndex.hasOwnProperty(location.hash.substr(3,7))) {

		Lightbox(location.hash.substr(3));

	}

}

function randomDoc() {
	return documentsReverse[(parseInt(Math.random()*10000000)%documentsReverse.length)];
}
<|endoftext|>


<commit_before>
require("whatwg-fetch");
var URI = require("../utils/uri.js").URI;
var Options = require("../utils/options.js");

//var OPTION_RESOURCE_CORS = "resource-crossorigin-attribute";
//Options.register(OPTION_RESOURCE_CORS, "anonymous");

var c_requestHooks = [];

var RequestAbortedException = function(url) {
    this.message = "Request was aborted.";
    this.url = url;
    this.toString = function() {
        return "Failed to load "+url+": "+this.message;
    }
};

var Resource = {};
Resource.fetch = function(uri, opt) {
    opt = initOptions(opt);

    var result = new Promise(function(resolve, reject) {
        for (var i=0; i < c_requestHooks.length; i++) {
            var hook = c_requestHooks[i];
            if (uri.match(hook.pattern)) {
                hook.callback(uri, opt);
            }
        }

        if (opt.abort) {
            reject(new RequestAbortedException(uri));
            return;
        }

        fetch(uri, opt)
        .then(function(response) {
            resolve(response);
        }).catch(function (exception) {
            XML3D.debug.logError("Could not retrieve document at '"+uri.toString()+"'. Reason: "+exception);
            reject(exception);
        });
    });

    return result;
};

Resource.onRequest = function(pattern, callback) {
    var hook = {
        pattern : pattern,
        callback : callback
    };
    c_requestHooks.push(hook);
};

var initOptions = function(opt) {
    opt = opt || {};
    opt.headers = opt.headers || {};
    return opt;
};

// Add a hook to check for file:// requests to warn the user that a server is needed to use XML3D
Resource.onRequest(/(file:)\/+([A-Z]:\/)/, function(url, opt) {
    XML3D.debug.logError("Encountered a filesystem request: '"+url+"'. A local server is needed to use XML3D. More " +
        "information can be found at https://github.com/xml3d/xml3d.js/issues/162");
    opt.abort = true;
});

module.exports = Resource;
<commit_msg>Change the onRequest hook to a simple callback. The callback will be given the request URI and can decide for itself if it's of interest<commit_after>
require("whatwg-fetch");
var URI = require("../utils/uri.js").URI;
var Options = require("../utils/options.js");

//var OPTION_RESOURCE_CORS = "resource-crossorigin-attribute";
//Options.register(OPTION_RESOURCE_CORS, "anonymous");

var c_requestHooks = [];

var RequestAbortedException = function(url) {
    this.message = "Request was aborted.";
    this.url = url;
    this.toString = function() {
        return "Failed to load "+url+": "+this.message;
    }
};

var Resource = {};
Resource.fetch = function(uriString, opt) {
    opt = initOptions(opt);
    var uri = new URI(uriString);

    var result = new Promise(function(resolve, reject) {
        for (var i=0; i < c_requestHooks.length; i++) {
            c_requestHooks[i](uri, opt);
        }

        if (opt.abort) {
            reject(new RequestAbortedException(uri));
            return;
        }

        fetch(uri.toString(), opt)
        .then(function(response) {
            resolve(response);
        }).catch(function (exception) {
            XML3D.debug.logError("Could not retrieve document at '"+uri.toString()+"'. Reason: "+exception);
            reject(exception);
        });
    });

    return result;
};

Resource.onRequest = function(callback) {
    c_requestHooks.push(callback);
};

var initOptions = function(opt) {
    opt = opt || {};
    opt.headers = opt.headers || {};
    return opt;
};

// Add a hook to check for file:// requests to warn the user that a server is needed to use XML3D
Resource.onRequest(function(uri, opt) {
    if (uri.toString().match(/(file:)\/+([A-Z]:\/)/)) {
        XML3D.debug.logError("Encountered a filesystem request: '" + uri + "'. A local server is needed to use XML3D. More " +
            "information can be found at https://github.com/xml3d/xml3d.js/issues/162");
        opt.abort = true;
    }
});

module.exports = Resource;
<|endoftext|>


<commit_before>/*
 * Foundation Responsive Library
 * http://foundation.zurb.com
 * Copyright 2013, ZURB
 * Free to use under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
*/

(function ($, window, document, undefined) {
  'use strict';

  // Used to retrieve Foundation media queries from CSS.
  if($('head').has('.foundation-mq-small').length === 0) {
    $('head').append('<meta class="foundation-mq-small">');
  }

  if($('head').has('.foundation-mq-medium').length === 0) {
    $('head').append('<meta class="foundation-mq-medium">');
  }

  if($('head').has('.foundation-mq-large').length === 0) {
    $('head').append('<meta class="foundation-mq-large">');
  }

  if($('head').has('.foundation-mq-xlarge').length === 0) {
    $('head').append('<meta class="foundation-mq-xlarge">');
  }

  if($('head').has('.foundation-mq-xxlarge').length === 0) {
    $('head').append('<meta class="foundation-mq-xxlarge">');
  }

  // Enable FastClick if present

  $(function() {
    if(typeof FastClick !== 'undefined') {
      // Don't attach to body if undefined
      if (typeof document.body !== 'undefined') {
        FastClick.attach(document.body);
      }
    }
  });

  // private Fast Selector wrapper,
  // returns jQuery object. Only use where
  // getElementById is not available.
  var S = function (selector, context) {
    if (typeof selector === 'string') {
      if (context) {
        var cont;
        if (context.jquery) {
          cont = context[0];
        } else {
          cont = context;
        }
        return $(cont.querySelectorAll(selector));
      }

      return $(document.querySelectorAll(selector));
    }

    return $(selector, context);
  }

  /*
    https://github.com/paulirish/matchMedia.js
  */

  window.matchMedia = window.matchMedia || (function( doc, undefined ) {

    "use strict";

    var bool,
        docElem = doc.documentElement,
        refNode = docElem.firstElementChild || docElem.firstChild,
        // fakeBody required for <FF4 when executed in <head>
        fakeBody = doc.createElement( "body" ),
        div = doc.createElement( "div" );

    div.id = "mq-test-1";
    div.style.cssText = "position:absolute;top:-100em";
    fakeBody.style.background = "none";
    fakeBody.appendChild(div);

    return function(q){

      div.innerHTML = "<style media=\"" + q + "\"> #mq-test-1 { width: 42px; }</style>";

      docElem.insertBefore( fakeBody, refNode );
      bool = div.offsetWidth === 42;
      docElem.removeChild( fakeBody );

      return {
        matches: bool,
        media: q
      };

    };

  }( document ));

  /*
   * jquery.requestAnimationFrame
   * https://github.com/gnarf37/jquery-requestAnimationFrame
   * Requires jQuery 1.8+
   *
   * Copyright (c) 2012 Corey Frang
   * Licensed under the MIT license.
   */

  (function( $ ) {

  // requestAnimationFrame polyfill adapted from Erik Möller
  // fixes from Paul Irish and Tino Zijdel
  // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
  // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating

  var animating,
    lastTime = 0,
    vendors = ['webkit', 'moz'],
    requestAnimationFrame = window.requestAnimationFrame,
    cancelAnimationFrame = window.cancelAnimationFrame;

  for(; lastTime < vendors.length && !requestAnimationFrame; lastTime++) {
    requestAnimationFrame = window[ vendors[lastTime] + "RequestAnimationFrame" ];
    cancelAnimationFrame = cancelAnimationFrame ||
      window[ vendors[lastTime] + "CancelAnimationFrame" ] ||
      window[ vendors[lastTime] + "CancelRequestAnimationFrame" ];
  }

  function raf() {
    if ( animating ) {
      requestAnimationFrame( raf );
      jQuery.fx.tick();
    }
  }

  if ( requestAnimationFrame ) {
    // use rAF
    window.requestAnimationFrame = requestAnimationFrame;
    window.cancelAnimationFrame = cancelAnimationFrame;
    jQuery.fx.timer = function( timer ) {
      if ( timer() && jQuery.timers.push( timer ) && !animating ) {
        animating = true;
        raf();
      }
    };

    jQuery.fx.stop = function() {
      animating = false;
    };
  } else {
    // polyfill
    window.requestAnimationFrame = function( callback, element ) {
      var currTime = new Date().getTime(),
        timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ),
        id = window.setTimeout( function() {
          callback( currTime + timeToCall );
        }, timeToCall );
      lastTime = currTime + timeToCall;
      return id;
    };

    window.cancelAnimationFrame = function(id) {
      clearTimeout(id);
    };

  }

  }( jQuery ));

  function removeQuotes (string) {
    if (typeof string === 'string' || string instanceof String) {
      string = string.replace(/^[\\/'"]+|(;\s?})+|[\\/'"]+$/g, '');
    }

    return string;
  }

  window.Foundation = {
    name : 'Foundation',

    version : '5.1.0',

    media_queries : {
      small : S('.foundation-mq-small').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
      medium : S('.foundation-mq-medium').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
      large : S('.foundation-mq-large').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
      xlarge: S('.foundation-mq-xlarge').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
      xxlarge: S('.foundation-mq-xxlarge').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, '')
    },

    stylesheet : $('<style></style>').appendTo('head')[0].sheet,

    init : function (scope, libraries, method, options, response) {
      var library_arr,
          args = [scope, method, options, response],
          responses = [];

      // check RTL
      this.rtl = /rtl/i.test(S('html').attr('dir'));

      // set foundation global scope
      this.scope = scope || this.scope;

      if (libraries && typeof libraries === 'string' && !/reflow/i.test(libraries)) {
        if (this.libs.hasOwnProperty(libraries)) {
          responses.push(this.init_lib(libraries, args));
        }
      } else {
        for (var lib in this.libs) {
          responses.push(this.init_lib(lib, libraries));
        }
      }

      return scope;
    },

    init_lib : function (lib, args) {
      if (this.libs.hasOwnProperty(lib)) {
        this.patch(this.libs[lib]);

        if (args && args.hasOwnProperty(lib)) {
          return this.libs[lib].init.apply(this.libs[lib], [this.scope, args[lib]]);
        }

        args = args instanceof Array ? args : Array(args);    // PATCH: added this line
        return this.libs[lib].init.apply(this.libs[lib], args);
      }

      return function () {};
    },

    patch : function (lib) {
      lib.scope = this.scope;
      lib['data_options'] = this.utils.data_options;
      lib['bindings'] = this.utils.bindings;
      lib['S'] = this.utils.S;
      lib.rtl = this.rtl;
    },

    inherit : function (scope, methods) {
      var methods_arr = methods.split(' ');

      for (var i = methods_arr.length - 1; i >= 0; i--) {
        if (this.utils.hasOwnProperty(methods_arr[i])) {
          this.libs[scope.name][methods_arr[i]] = this.utils[methods_arr[i]];
        }
      }
    },

    random_str : function (length) {
      var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');

      if (!length) {
        length = Math.floor(Math.random() * chars.length);
      }

      var str = '';
      for (var i = 0; i < length; i++) {
        str += chars[Math.floor(Math.random() * chars.length)];
      }
      return str;
    },

    libs : {},

    // methods that can be inherited in libraries
    utils : {

      // Fast Selector wrapper returns jQuery object.
      // Only use where getElementById is not available.
      S : S,

      throttle : function(fun, delay) {
        var timer = null;

        return function () {
          var context = this, args = arguments;

          clearTimeout(timer);
          timer = setTimeout(function () {
            fun.apply(context, args);
          }, delay);
        };
      },

      // parses data-options attribute
      data_options : function (el) {
        var opts = {}, ii, p, opts_arr, opts_len,
            data_options = el.data('options');

        if (typeof data_options === 'object') {
          return data_options;
        }

        opts_arr = (data_options || ':').split(';'),
        opts_len = opts_arr.length;

        function isNumber (o) {
          return ! isNaN (o-0) && o !== null && o !== "" && o !== false && o !== true;
        }

        function trim(str) {
          if (typeof str === 'string') return $.trim(str);
          return str;
        }

        // parse options
        for (ii = opts_len - 1; ii >= 0; ii--) {
          p = opts_arr[ii].split(':');

          if (/true/i.test(p[1])) p[1] = true;
          if (/false/i.test(p[1])) p[1] = false;
          if (isNumber(p[1])) p[1] = parseInt(p[1], 10);

          if (p.length === 2 && p[0].length > 0) {
            opts[trim(p[0])] = trim(p[1]);
          }
        }

        return opts;
      },

      // test for empty object or array
      empty : function (obj) {
        if (obj.length && obj.length > 0)    return false;
        if (obj.length && obj.length === 0)  return true;

        for (var key in obj) {
          if (hasOwnProperty.call(obj, key))    return false;
        }

        return true;
      },

      register_media : function(media, media_class) {
        if(Foundation.media_queries[media] === undefined) {
          $('head').append('<meta class="' + media_class + '">');
          Foundation.media_queries[media] = removeQuotes($('.' + media_class).css('font-family'));
        }
      },

      addCustomRule : function(rule, media) {
        if(media === undefined) {
          Foundation.stylesheet.insertRule(rule, Foundation.stylesheet.cssRules.length);
        } else {
          var query = Foundation.media_queries[media];
          if(query !== undefined) {
            Foundation.stylesheet.insertRule('@media ' +
              Foundation.media_queries[media] + '{ ' + rule + ' }');
          }
        }
      },

      loaded : function (image, callback) {
        function loaded () {
          callback(image[0]);
        }

        function bindLoad () {
          this.one('load', loaded);

          if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) {
            var src = this.attr( 'src' ),
                param = src.match( /\?/ ) ? '&' : '?';

            param += 'random=' + (new Date()).getTime();
            this.attr('src', src + param);
          }
        }

        if (!image.attr('src')) {
          loaded();
          return;
        }

        if (image[0].complete || image[0].readyState === 4) {
          loaded();
        } else {
          bindLoad.call(image);
        }
      },

      bindings : function (method, options) {
        var self = this,
            should_bind_events = !S(this).data(this.name + '-init');

        if (typeof method === 'string') {
          return this[method].call(this, options);
        }

        if (S(this.scope).is('[data-' + this.name +']')) {
          S(this.scope).data(this.name + '-init', $.extend({}, this.settings, (options || method), this.data_options(S(this.scope))));

          if (should_bind_events) {
            this.events(this.scope);
          }

        } else {
          S('[data-' + this.name + ']', this.scope).each(function () {
            var should_bind_events = !S(this).data(self.name + '-init');

            S(this).data(self.name + '-init', $.extend({}, self.settings, (options || method), self.data_options(S(this))));

            if (should_bind_events) {
              self.events(this);
            }
          });
        }
      }
    }
  };

  $.fn.foundation = function () {
    var args = Array.prototype.slice.call(arguments, 0);

    return this.each(function () {
      Foundation.init.apply(Foundation, [this].concat(args));
      return this;
    });
  };

}(jQuery, this, this.document));
<commit_msg>Move bindings out of Foundation.utils to make it private<commit_after>/*
 * Foundation Responsive Library
 * http://foundation.zurb.com
 * Copyright 2013, ZURB
 * Free to use under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
*/

(function ($, window, document, undefined) {
  'use strict';

  // Used to retrieve Foundation media queries from CSS.
  if($('head').has('.foundation-mq-small').length === 0) {
    $('head').append('<meta class="foundation-mq-small">');
  }

  if($('head').has('.foundation-mq-medium').length === 0) {
    $('head').append('<meta class="foundation-mq-medium">');
  }

  if($('head').has('.foundation-mq-large').length === 0) {
    $('head').append('<meta class="foundation-mq-large">');
  }

  if($('head').has('.foundation-mq-xlarge').length === 0) {
    $('head').append('<meta class="foundation-mq-xlarge">');
  }

  if($('head').has('.foundation-mq-xxlarge').length === 0) {
    $('head').append('<meta class="foundation-mq-xxlarge">');
  }

  // Enable FastClick if present

  $(function() {
    if(typeof FastClick !== 'undefined') {
      // Don't attach to body if undefined
      if (typeof document.body !== 'undefined') {
        FastClick.attach(document.body);
      }
    }
  });

  // private Fast Selector wrapper,
  // returns jQuery object. Only use where
  // getElementById is not available.
  var S = function (selector, context) {
    if (typeof selector === 'string') {
      if (context) {
        var cont;
        if (context.jquery) {
          cont = context[0];
        } else {
          cont = context;
        }
        return $(cont.querySelectorAll(selector));
      }

      return $(document.querySelectorAll(selector));
    }

    return $(selector, context);
  }

  var bindings = function (method, options) {
    var self = this,
        should_bind_events = !S(this).data(this.name + '-init');

    if (typeof method === 'string') {
      return this[method].call(this, options);
    }

    if (S(this.scope).is('[data-' + this.name +']')) {
      S(this.scope).data(this.name + '-init', $.extend({}, this.settings, (options || method), this.data_options(S(this.scope))));

      if (should_bind_events) {
        this.events(this.scope);
      }

    } else {
      S('[data-' + this.name + ']', this.scope).each(function () {
        var should_bind_events = !S(this).data(self.name + '-init');

        S(this).data(self.name + '-init', $.extend({}, self.settings, (options || method), self.data_options(S(this))));

        if (should_bind_events) {
          self.events(this);
        }
      });
    }
  };

  /*
    https://github.com/paulirish/matchMedia.js
  */

  window.matchMedia = window.matchMedia || (function( doc, undefined ) {

    "use strict";

    var bool,
        docElem = doc.documentElement,
        refNode = docElem.firstElementChild || docElem.firstChild,
        // fakeBody required for <FF4 when executed in <head>
        fakeBody = doc.createElement( "body" ),
        div = doc.createElement( "div" );

    div.id = "mq-test-1";
    div.style.cssText = "position:absolute;top:-100em";
    fakeBody.style.background = "none";
    fakeBody.appendChild(div);

    return function(q){

      div.innerHTML = "<style media=\"" + q + "\"> #mq-test-1 { width: 42px; }</style>";

      docElem.insertBefore( fakeBody, refNode );
      bool = div.offsetWidth === 42;
      docElem.removeChild( fakeBody );

      return {
        matches: bool,
        media: q
      };

    };

  }( document ));

  /*
   * jquery.requestAnimationFrame
   * https://github.com/gnarf37/jquery-requestAnimationFrame
   * Requires jQuery 1.8+
   *
   * Copyright (c) 2012 Corey Frang
   * Licensed under the MIT license.
   */

  (function( $ ) {

  // requestAnimationFrame polyfill adapted from Erik Möller
  // fixes from Paul Irish and Tino Zijdel
  // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
  // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating

  var animating,
    lastTime = 0,
    vendors = ['webkit', 'moz'],
    requestAnimationFrame = window.requestAnimationFrame,
    cancelAnimationFrame = window.cancelAnimationFrame;

  for(; lastTime < vendors.length && !requestAnimationFrame; lastTime++) {
    requestAnimationFrame = window[ vendors[lastTime] + "RequestAnimationFrame" ];
    cancelAnimationFrame = cancelAnimationFrame ||
      window[ vendors[lastTime] + "CancelAnimationFrame" ] ||
      window[ vendors[lastTime] + "CancelRequestAnimationFrame" ];
  }

  function raf() {
    if ( animating ) {
      requestAnimationFrame( raf );
      jQuery.fx.tick();
    }
  }

  if ( requestAnimationFrame ) {
    // use rAF
    window.requestAnimationFrame = requestAnimationFrame;
    window.cancelAnimationFrame = cancelAnimationFrame;
    jQuery.fx.timer = function( timer ) {
      if ( timer() && jQuery.timers.push( timer ) && !animating ) {
        animating = true;
        raf();
      }
    };

    jQuery.fx.stop = function() {
      animating = false;
    };
  } else {
    // polyfill
    window.requestAnimationFrame = function( callback, element ) {
      var currTime = new Date().getTime(),
        timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ),
        id = window.setTimeout( function() {
          callback( currTime + timeToCall );
        }, timeToCall );
      lastTime = currTime + timeToCall;
      return id;
    };

    window.cancelAnimationFrame = function(id) {
      clearTimeout(id);
    };

  }

  }( jQuery ));

  function removeQuotes (string) {
    if (typeof string === 'string' || string instanceof String) {
      string = string.replace(/^[\\/'"]+|(;\s?})+|[\\/'"]+$/g, '');
    }

    return string;
  }

  window.Foundation = {
    name : 'Foundation',

    version : '5.1.0',

    media_queries : {
      small : S('.foundation-mq-small').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
      medium : S('.foundation-mq-medium').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
      large : S('.foundation-mq-large').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
      xlarge: S('.foundation-mq-xlarge').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
      xxlarge: S('.foundation-mq-xxlarge').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, '')
    },

    stylesheet : $('<style></style>').appendTo('head')[0].sheet,

    init : function (scope, libraries, method, options, response) {
      var library_arr,
          args = [scope, method, options, response],
          responses = [];

      // check RTL
      this.rtl = /rtl/i.test(S('html').attr('dir'));

      // set foundation global scope
      this.scope = scope || this.scope;

      if (libraries && typeof libraries === 'string' && !/reflow/i.test(libraries)) {
        if (this.libs.hasOwnProperty(libraries)) {
          responses.push(this.init_lib(libraries, args));
        }
      } else {
        for (var lib in this.libs) {
          responses.push(this.init_lib(lib, libraries));
        }
      }

      return scope;
    },

    init_lib : function (lib, args) {
      if (this.libs.hasOwnProperty(lib)) {
        this.patch(this.libs[lib]);

        if (args && args.hasOwnProperty(lib)) {
          return this.libs[lib].init.apply(this.libs[lib], [this.scope, args[lib]]);
        }

        args = args instanceof Array ? args : Array(args);    // PATCH: added this line
        return this.libs[lib].init.apply(this.libs[lib], args);
      }

      return function () {};
    },

    patch : function (lib) {
      lib.scope = this.scope;
      lib['data_options'] = this.utils.data_options;
      lib['bindings'] = bindings;
      lib['S'] = this.utils.S;
      lib.rtl = this.rtl;
    },

    inherit : function (scope, methods) {
      var methods_arr = methods.split(' ');

      for (var i = methods_arr.length - 1; i >= 0; i--) {
        if (this.utils.hasOwnProperty(methods_arr[i])) {
          this.libs[scope.name][methods_arr[i]] = this.utils[methods_arr[i]];
        }
      }
    },

    random_str : function (length) {
      var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');

      if (!length) {
        length = Math.floor(Math.random() * chars.length);
      }

      var str = '';
      for (var i = 0; i < length; i++) {
        str += chars[Math.floor(Math.random() * chars.length)];
      }
      return str;
    },

    libs : {},

    // methods that can be inherited in libraries
    utils : {

      // Fast Selector wrapper returns jQuery object.
      // Only use where getElementById is not available.
      S : S,

      throttle : function(fun, delay) {
        var timer = null;

        return function () {
          var context = this, args = arguments;

          clearTimeout(timer);
          timer = setTimeout(function () {
            fun.apply(context, args);
          }, delay);
        };
      },

      // parses data-options attribute
      data_options : function (el) {
        var opts = {}, ii, p, opts_arr, opts_len,
            data_options = el.data('options');

        if (typeof data_options === 'object') {
          return data_options;
        }

        opts_arr = (data_options || ':').split(';'),
        opts_len = opts_arr.length;

        function isNumber (o) {
          return ! isNaN (o-0) && o !== null && o !== "" && o !== false && o !== true;
        }

        function trim(str) {
          if (typeof str === 'string') return $.trim(str);
          return str;
        }

        // parse options
        for (ii = opts_len - 1; ii >= 0; ii--) {
          p = opts_arr[ii].split(':');

          if (/true/i.test(p[1])) p[1] = true;
          if (/false/i.test(p[1])) p[1] = false;
          if (isNumber(p[1])) p[1] = parseInt(p[1], 10);

          if (p.length === 2 && p[0].length > 0) {
            opts[trim(p[0])] = trim(p[1]);
          }
        }

        return opts;
      },

      // test for empty object or array
      empty : function (obj) {
        if (obj.length && obj.length > 0)    return false;
        if (obj.length && obj.length === 0)  return true;

        for (var key in obj) {
          if (hasOwnProperty.call(obj, key))    return false;
        }

        return true;
      },

      register_media : function(media, media_class) {
        if(Foundation.media_queries[media] === undefined) {
          $('head').append('<meta class="' + media_class + '">');
          Foundation.media_queries[media] = removeQuotes($('.' + media_class).css('font-family'));
        }
      },

      addCustomRule : function(rule, media) {
        if(media === undefined) {
          Foundation.stylesheet.insertRule(rule, Foundation.stylesheet.cssRules.length);
        } else {
          var query = Foundation.media_queries[media];
          if(query !== undefined) {
            Foundation.stylesheet.insertRule('@media ' +
              Foundation.media_queries[media] + '{ ' + rule + ' }');
          }
        }
      },

      loaded : function (image, callback) {
        function loaded () {
          callback(image[0]);
        }

        function bindLoad () {
          this.one('load', loaded);

          if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) {
            var src = this.attr( 'src' ),
                param = src.match( /\?/ ) ? '&' : '?';

            param += 'random=' + (new Date()).getTime();
            this.attr('src', src + param);
          }
        }

        if (!image.attr('src')) {
          loaded();
          return;
        }

        if (image[0].complete || image[0].readyState === 4) {
          loaded();
        } else {
          bindLoad.call(image);
        }
      }
    }
  };

  $.fn.foundation = function () {
    var args = Array.prototype.slice.call(arguments, 0);

    return this.each(function () {
      Foundation.init.apply(Foundation, [this].concat(args));
      return this;
    });
  };

}(jQuery, this, this.document));
<|endoftext|>