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

/**
 * A carousel UI component.
 * A set of N items is divided into M 'pages', based on how many items are visible in the carousel.
 * Pressing the next and previous buttons moves through the pages.
 * Movement through the pages is animated, so that items appear to scroll by.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var CarouselButton = require( 'SCENERY_PHET/buttons/CarouselButton' );
  var HSeparator = require( 'SUN/HSeparator' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var PageControl = require( 'SCENERY_PHET/PageControl' );
  var Property = require( 'AXON/Property' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var Util = require( 'DOT/Util' );
  var VSeparator = require( 'SUN/VSeparator' );

  // constants
  var DEFAULT_OPTIONS = {

    // container
    orientation: 'horizontal', // {string} 'horizontal'|'vertical'
    fill: 'white', // {Color|string|null} background color of the carousel
    stroke: 'black', // {Color|string|null} color used to stroke the border of the carousel
    lineWidth: 1, // {number} width of the border around the carousel
    cornerRadius: 4, // {number} radius applied to the carousel and next/previous buttons
    defaultPageNumber: 0, // {number} page that is initially visible

    // items
    itemsPerPage: 4, // {number} number of items per page, or how many items are visible at a time in the carousel
    spacing: 10, // {number} spacing between items, between items and optional separators, and between items and buttons
    margin: 10, // {number} margin between items and the edges of the carousel

    // next/previous buttons
    buttonColor: 'rgba( 200, 200, 200, 0.5 )', // {Color|string} base color for the buttons
    buttonStroke: undefined, // {Color|string|null|undefined} stroke around the buttons (null is no stroke, undefined derives color from buttonColor)
    buttonLineWidth: 1, // {number} lineWidth of borders on buttons
    hideDisabledButtons: false, // {boolean} whether to hide buttons when they are disabled

    // item separators
    separatorsVisible: true, // {boolean} whether to put separators between items
    separatorColor: 'rgb( 180, 180, 180 )', // {Color|string} color for separators
    separatorLineWidth: 0.5, // {number} lineWidth for separators

    // iOS-style page control
    pageControlVisible: true, // {boolean} whether to show an iOS-style page control
    pageControlLocation: 'bottom', // {string} where to place the page control, 'top'|'bottom'|'left'|'right'
    pageControlSpacing: 6, // {number} spacing between page control and carousel background
    dotRadius: 3, // {number} radius of the dots in the page control
    dotSpacing: 10, // {number} space between dots
    pageVisibleColor: 'black', // {Color|string} dot color for the page that is visible
    pageNotVisibleColor: 'rgb( 200, 200, 200 )' // {Color|string} dot color for pages that are not visible
  };

  /**
   * @param {Node[]} items - items in the carousel
   * @param {Object} [options]
   * @constructor
   */
  function Carousel( items, options ) {

    // Make a copy of default options, so we can modify it
    var defaultOptions = _.clone( DEFAULT_OPTIONS );

    // If options doesn't specify a location for dots, set a valid default
    if ( options.orientation && !options.pageControlLocation ) {
      defaultOptions.pageControlLocation = ( options.orientation === 'horizontal' ) ? 'bottom' : 'left';
    }

    // Override defaults with specified options
    options = _.extend( defaultOptions, options );

    // Validate options
    assert && assert( _.contains( [ 'horizontal', 'vertical' ], options.orientation ), 'invalid orientation=' + options.orientation );
    assert && assert( _.contains( [ 'bottom', 'top', 'left', 'right' ], options.pageControlLocation ), 'invalid pageControlLocation=' + options.pageControlLocation );

    // To improve readability
    var isHorizontal = ( options.orientation === 'horizontal' );

    // Dimensions of largest item
    var maxItemWidth = _.max( items, function( item ) { return item.width; } ).width;
    var maxItemHeight = _.max( items, function( item ) { return item.height; } ).height;

    // This quantity is used make some other computations independent of orientation.
    var maxItemLength = isHorizontal ? maxItemWidth : maxItemHeight;

    // Options common to both buttons
    var buttonOptions = {
      xMargin: 5,
      yMargin: 5,
      baseColor: options.buttonColor,
      disabledBaseColor: options.fill, // same as carousel background
      stroke: options.buttonStroke,
      lineWidth: options.buttonLineWidth,
      cornerRadius: options.cornerRadius, // same as carousel background
      minWidth: isHorizontal ? 0 : maxItemWidth + ( 2 * options.margin ), // fill the width of a vertical carousel
      minHeight: isHorizontal ? maxItemHeight + ( 2 * options.margin ) : 0 // fill the height of a horizontal carousel
    };

    // Next/previous buttons
    var nextButton = new CarouselButton( _.extend( {
      arrowDirection: isHorizontal ? 'right' : 'down'
    }, buttonOptions ) );
    var previousButton = new CarouselButton( _.extend( {
      arrowDirection: isHorizontal ? 'left' : 'up'
    }, buttonOptions ) );

    // Computations related to layout of items
    var numberOfSeparators = ( options.separatorsVisible ) ? ( items.length - 1 ) : 0;
    var scrollingLength = ( items.length * ( maxItemLength + options.spacing ) + ( numberOfSeparators * options.spacing) + options.spacing );
    var scrollingWidth = isHorizontal ? scrollingLength : ( maxItemWidth + 2 * options.margin );
    var scrollingHeight = isHorizontal ? ( maxItemHeight + 2 * options.margin ) : scrollingLength;
    var itemCenter = options.spacing + ( maxItemLength / 2 );

    // Options common to all separators
    var separatorOptions = {
      stroke: options.separatorColor,
      lineWidth: options.separatorLineWidth
    };

    // All items, arranged in the proper orientation, with margins and spacing.
    // Horizontal carousel arrange items left-to-right, vertical is top-to-bottom.
    // Translation of this node will be animated to give the effect of scrolling through the items.
    var scrollingNode = new Rectangle( 0, 0, scrollingWidth, scrollingHeight );
    items.forEach( function( item, index ) {

      // add the item
      if ( isHorizontal ) {
        item.centerX = itemCenter;
        item.centerY = options.margin + ( maxItemHeight / 2 );
      }
      else {
        item.centerX = options.margin + ( maxItemWidth / 2 );
        item.centerY = itemCenter;
      }
      scrollingNode.addChild( item );

      // center for the next item
      itemCenter += ( options.spacing + maxItemLength );

      // add optional separator
      if ( options.separatorsVisible ) {
        var separator;
        if ( isHorizontal ) {

          // vertical separator, to the left of the item
          separator = new VSeparator( scrollingHeight, _.extend( {
            centerX: item.centerX + ( maxItemLength / 2 ) + options.spacing,
            centerY: item.centerY
          }, separatorOptions ) );
          scrollingNode.addChild( separator );

          // center for the next item
          itemCenter = separator.centerX + options.spacing + ( maxItemLength / 2 );
        }
        else {

          // horizontal separator, below the item
          separator = new HSeparator( scrollingWidth, _.extend( {
            centerX: item.centerX,
            centerY: item.centerY + ( maxItemLength / 2 ) + options.spacing
          }, separatorOptions ) );
          scrollingNode.addChild( separator );

          // center for the next item
          itemCenter = separator.centerY + options.spacing + ( maxItemLength / 2 );
        }
      }
    } );

    // How much to translate scrollingNode each time a next/previous button is pressed
    var scrollingDelta = options.itemsPerPage * ( maxItemLength + options.spacing );
    if ( options.separatorsVisible ) {
      scrollingDelta += ( options.itemsPerPage * options.spacing );
    }

    // Clipping window, to show one page at a time.
    // Clips at the midpoint of spacing between items so that you don't see any stray bits of the items that shouldn't be visible.
    var windowLength = ( scrollingDelta + options.spacing );
    if ( options.separatorsVisible ) {
      windowLength -= options.spacing;
    }
    var windowWidth = isHorizontal ? windowLength : scrollingNode.width;
    var windowHeight = isHorizontal ? scrollingNode.height : windowLength;
    var clipArea = isHorizontal ?
                   Shape.rectangle( options.spacing / 2, 0, windowWidth - options.spacing, windowHeight ) :
                   Shape.rectangle( 0, options.spacing / 2, windowWidth, windowHeight - options.spacing );
    var windowNode = new Node( {
      children: [ scrollingNode ],
      clipArea: clipArea
    } );

    // Background - displays the carousel's fill color
    var backgroundWidth = isHorizontal ? ( windowWidth + nextButton.width + previousButton.width ) : windowWidth;
    var backgroundHeight = isHorizontal ? windowHeight : ( windowHeight + nextButton.height + previousButton.height );
    var backgroundNode = new Rectangle( 0, 0, backgroundWidth, backgroundHeight, options.cornerRadius, options.cornerRadius, {
      fill: options.fill
    } );

    // Foreground - displays the carousel's outline, created as a separate node so that it can be placed on top of everything, for a clean look.
    var foregroundNode = new Rectangle( 0, 0, backgroundWidth, backgroundHeight, options.cornerRadius, options.cornerRadius, {
      stroke: options.stroke
    } );

    // Layout
    if ( isHorizontal ) {
      nextButton.centerY = previousButton.centerY = windowNode.centerY = backgroundNode.centerY;
      nextButton.right = backgroundNode.right;
      previousButton.left = backgroundNode.left;
      windowNode.centerX = backgroundNode.centerX;
    }
    else {
      nextButton.centerX = previousButton.centerX = windowNode.centerX = backgroundNode.centerX;
      nextButton.bottom = backgroundNode.bottom;
      previousButton.top = backgroundNode.top;
      windowNode.centerY = backgroundNode.centerY;
    }

    // Number of pages
    var numberOfPages = items.length / options.itemsPerPage;
    if ( !Util.isInteger( numberOfPages ) ) {
      numberOfPages = Math.floor( numberOfPages + 1 );
    }

    // Number of the page that is visible in the carousel.
    var pageNumberProperty = new Property( options.defaultPageNumber );

    // iOS-style page control
    var pageControl = null;
    if ( options.pageControlVisible ) {

      pageControl = new PageControl( numberOfPages, pageNumberProperty, {
        orientation: options.orientation,
        dotRadius: options.dotRadius,
        pageVisibleColor: options.pageVisibleColor,
        pageNotVisibleColor: options.pageNotVisibleColor,
        dotSpacing: options.dotSpacing
      } );

      // Layout
      if ( isHorizontal ) {
        pageControl.centerX = backgroundNode.centerX;
        if ( options.pageControlLocation === 'top' ) {
          pageControl.bottom = backgroundNode.top - options.pageControlSpacing;
        }
        else if ( options.pageControlLocation === 'bottom' ) {
          pageControl.top = backgroundNode.bottom + options.pageControlSpacing;
        }
        else {
          throw new Error( 'incompatible pageControlLocation=' + options.pageControlLocation + ' for orientation=' + options.orientation );
        }
      }
      else {
        pageControl.centerY = backgroundNode.centerY;
        if ( options.pageControlLocation === 'left' ) {
          pageControl.right = backgroundNode.left - options.pageControlSpacing;
        }
        else if ( options.pageControlLocation === 'right' ) {
          pageControl.left = backgroundNode.right + options.pageControlSpacing;
        }
        else {
          throw new Error( 'incompatible pageControlLocation=' + options.pageControlLocation + ' for orientation=' + options.orientation );
        }
      }
    }

    // Scroll when the buttons are pressed
    var scrollTween;
    pageNumberProperty.link( function( pageNumber ) {

      assert && assert( pageNumber >= 0 && pageNumber <= numberOfPages - 1, 'pageNumber out of range: ' + pageNumber );

      // stop any animation that's in progress
      scrollTween && scrollTween.stop();

      // button state
      nextButton.enabled = pageNumber < ( numberOfPages - 1 );
      previousButton.enabled = pageNumber > 0;
      if ( options.hideDisabledButtons ) {
        nextButton.visible = nextButton.enabled;
        previousButton.visible = previousButton.enabled;
      }

      //TODO replace calls to Tween with a wrapper
      // Set up the animation to scroll the items in the carousel.
      var parameters;
      var animationDuration = 400; // ms
      var easing = TWEEN.Easing.Cubic.InOut;
      if ( isHorizontal ) {
        parameters = { left: scrollingNode.left };
        scrollTween = new TWEEN.Tween( parameters )
          .easing( easing )
          .to( { left: -pageNumber * scrollingDelta }, animationDuration )
          .onUpdate( function() {
            scrollingNode.left = parameters.left;
          } )
          .start();
      }
      else {
        parameters = { top: scrollingNode.top };
        scrollTween = new TWEEN.Tween( parameters )
          .easing( easing )
          .to( { top: -pageNumber * scrollingDelta }, animationDuration )
          .onUpdate( function() {
            scrollingNode.top = parameters.top;
          } )
          .start();
      }
    } );

    // Buttons modify the page number
    nextButton.addListener( function() {
      pageNumberProperty.set( pageNumberProperty.get() + 1 );
    } );
    previousButton.addListener( function() {
      pageNumberProperty.set( pageNumberProperty.get() - 1 );
    } );

    // public fields
    this.numberOfPages = numberOfPages; // @public (read-only) {number} number of pages in the carousel
    this.pageNumberProperty = pageNumberProperty; // @public {Property<number>} page number that is currently visible

    // @private
    this.disposeCarousel = function() {
      if ( pageControl ) {
        pageControl.dispose();
      }
    };

    options.children = [ backgroundNode, windowNode, nextButton, previousButton, foregroundNode ];
    if ( pageControl ) {
      options.children.push( pageControl );
    }

    Node.call( this, options );
  }

  return inherit( Node, Carousel, {

    dispose: function() { this.disposeCarousel(); }
  }, {

    // @static @public (read-only)
    DEFAULT_OPTIONS: DEFAULT_OPTIONS
  } );
} );
<commit_msg>turn off features that should be off<commit_after>// Copyright 2002-2015, University of Colorado Boulder

/**
 * A carousel UI component.
 * A set of N items is divided into M 'pages', based on how many items are visible in the carousel.
 * Pressing the next and previous buttons moves through the pages.
 * Movement through the pages is animated, so that items appear to scroll by.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var CarouselButton = require( 'SCENERY_PHET/buttons/CarouselButton' );
  var HSeparator = require( 'SUN/HSeparator' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var PageControl = require( 'SCENERY_PHET/PageControl' );
  var Property = require( 'AXON/Property' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var Util = require( 'DOT/Util' );
  var VSeparator = require( 'SUN/VSeparator' );

  // constants
  var DEFAULT_OPTIONS = {

    // container
    orientation: 'horizontal', // {string} 'horizontal'|'vertical'
    fill: 'white', // {Color|string|null} background color of the carousel
    stroke: 'black', // {Color|string|null} color used to stroke the border of the carousel
    lineWidth: 1, // {number} width of the border around the carousel
    cornerRadius: 4, // {number} radius applied to the carousel and next/previous buttons
    defaultPageNumber: 0, // {number} page that is initially visible

    // items
    itemsPerPage: 4, // {number} number of items per page, or how many items are visible at a time in the carousel
    spacing: 10, // {number} spacing between items, between items and optional separators, and between items and buttons
    margin: 10, // {number} margin between items and the edges of the carousel

    // next/previous buttons
    buttonColor: 'rgba( 200, 200, 200, 0.5 )', // {Color|string} base color for the buttons
    buttonStroke: undefined, // {Color|string|null|undefined} stroke around the buttons (null is no stroke, undefined derives color from buttonColor)
    buttonLineWidth: 1, // {number} lineWidth of borders on buttons
    hideDisabledButtons: false, // {boolean} whether to hide buttons when they are disabled

    // item separators
    separatorsVisible: false, // {boolean} whether to put separators between items
    separatorColor: 'rgb( 180, 180, 180 )', // {Color|string} color for separators
    separatorLineWidth: 0.5, // {number} lineWidth for separators

    // iOS-style page control
    pageControlVisible: false, // {boolean} whether to show an iOS-style page control
    pageControlLocation: 'bottom', // {string} where to place the page control, 'top'|'bottom'|'left'|'right'
    pageControlSpacing: 6, // {number} spacing between page control and carousel background
    dotRadius: 3, // {number} radius of the dots in the page control
    dotSpacing: 10, // {number} space between dots
    pageVisibleColor: 'black', // {Color|string} dot color for the page that is visible
    pageNotVisibleColor: 'rgb( 200, 200, 200 )' // {Color|string} dot color for pages that are not visible
  };

  /**
   * @param {Node[]} items - items in the carousel
   * @param {Object} [options]
   * @constructor
   */
  function Carousel( items, options ) {

    // Make a copy of default options, so we can modify it
    var defaultOptions = _.clone( DEFAULT_OPTIONS );

    // If options doesn't specify a location for page control, set a valid default
    if ( options.orientation && !options.pageControlLocation ) {
      defaultOptions.pageControlLocation = ( options.orientation === 'horizontal' ) ? 'bottom' : 'left';
    }

    // Override defaults with specified options
    options = _.extend( defaultOptions, options );

    // Validate options
    assert && assert( _.contains( [ 'horizontal', 'vertical' ], options.orientation ), 'invalid orientation=' + options.orientation );
    assert && assert( _.contains( [ 'bottom', 'top', 'left', 'right' ], options.pageControlLocation ), 'invalid pageControlLocation=' + options.pageControlLocation );

    // To improve readability
    var isHorizontal = ( options.orientation === 'horizontal' );

    // Dimensions of largest item
    var maxItemWidth = _.max( items, function( item ) { return item.width; } ).width;
    var maxItemHeight = _.max( items, function( item ) { return item.height; } ).height;

    // This quantity is used make some other computations independent of orientation.
    var maxItemLength = isHorizontal ? maxItemWidth : maxItemHeight;

    // Options common to both buttons
    var buttonOptions = {
      xMargin: 5,
      yMargin: 5,
      baseColor: options.buttonColor,
      disabledBaseColor: options.fill, // same as carousel background
      stroke: options.buttonStroke,
      lineWidth: options.buttonLineWidth,
      cornerRadius: options.cornerRadius, // same as carousel background
      minWidth: isHorizontal ? 0 : maxItemWidth + ( 2 * options.margin ), // fill the width of a vertical carousel
      minHeight: isHorizontal ? maxItemHeight + ( 2 * options.margin ) : 0 // fill the height of a horizontal carousel
    };

    // Next/previous buttons
    var nextButton = new CarouselButton( _.extend( {
      arrowDirection: isHorizontal ? 'right' : 'down'
    }, buttonOptions ) );
    var previousButton = new CarouselButton( _.extend( {
      arrowDirection: isHorizontal ? 'left' : 'up'
    }, buttonOptions ) );

    // Computations related to layout of items
    var numberOfSeparators = ( options.separatorsVisible ) ? ( items.length - 1 ) : 0;
    var scrollingLength = ( items.length * ( maxItemLength + options.spacing ) + ( numberOfSeparators * options.spacing) + options.spacing );
    var scrollingWidth = isHorizontal ? scrollingLength : ( maxItemWidth + 2 * options.margin );
    var scrollingHeight = isHorizontal ? ( maxItemHeight + 2 * options.margin ) : scrollingLength;
    var itemCenter = options.spacing + ( maxItemLength / 2 );

    // Options common to all separators
    var separatorOptions = {
      stroke: options.separatorColor,
      lineWidth: options.separatorLineWidth
    };

    // All items, arranged in the proper orientation, with margins and spacing.
    // Horizontal carousel arrange items left-to-right, vertical is top-to-bottom.
    // Translation of this node will be animated to give the effect of scrolling through the items.
    var scrollingNode = new Rectangle( 0, 0, scrollingWidth, scrollingHeight );
    items.forEach( function( item, index ) {

      // add the item
      if ( isHorizontal ) {
        item.centerX = itemCenter;
        item.centerY = options.margin + ( maxItemHeight / 2 );
      }
      else {
        item.centerX = options.margin + ( maxItemWidth / 2 );
        item.centerY = itemCenter;
      }
      scrollingNode.addChild( item );

      // center for the next item
      itemCenter += ( options.spacing + maxItemLength );

      // add optional separator
      if ( options.separatorsVisible ) {
        var separator;
        if ( isHorizontal ) {

          // vertical separator, to the left of the item
          separator = new VSeparator( scrollingHeight, _.extend( {
            centerX: item.centerX + ( maxItemLength / 2 ) + options.spacing,
            centerY: item.centerY
          }, separatorOptions ) );
          scrollingNode.addChild( separator );

          // center for the next item
          itemCenter = separator.centerX + options.spacing + ( maxItemLength / 2 );
        }
        else {

          // horizontal separator, below the item
          separator = new HSeparator( scrollingWidth, _.extend( {
            centerX: item.centerX,
            centerY: item.centerY + ( maxItemLength / 2 ) + options.spacing
          }, separatorOptions ) );
          scrollingNode.addChild( separator );

          // center for the next item
          itemCenter = separator.centerY + options.spacing + ( maxItemLength / 2 );
        }
      }
    } );

    // How much to translate scrollingNode each time a next/previous button is pressed
    var scrollingDelta = options.itemsPerPage * ( maxItemLength + options.spacing );
    if ( options.separatorsVisible ) {
      scrollingDelta += ( options.itemsPerPage * options.spacing );
    }

    // Clipping window, to show one page at a time.
    // Clips at the midpoint of spacing between items so that you don't see any stray bits of the items that shouldn't be visible.
    var windowLength = ( scrollingDelta + options.spacing );
    if ( options.separatorsVisible ) {
      windowLength -= options.spacing;
    }
    var windowWidth = isHorizontal ? windowLength : scrollingNode.width;
    var windowHeight = isHorizontal ? scrollingNode.height : windowLength;
    var clipArea = isHorizontal ?
                   Shape.rectangle( options.spacing / 2, 0, windowWidth - options.spacing, windowHeight ) :
                   Shape.rectangle( 0, options.spacing / 2, windowWidth, windowHeight - options.spacing );
    var windowNode = new Node( {
      children: [ scrollingNode ],
      clipArea: clipArea
    } );

    // Background - displays the carousel's fill color
    var backgroundWidth = isHorizontal ? ( windowWidth + nextButton.width + previousButton.width ) : windowWidth;
    var backgroundHeight = isHorizontal ? windowHeight : ( windowHeight + nextButton.height + previousButton.height );
    var backgroundNode = new Rectangle( 0, 0, backgroundWidth, backgroundHeight, options.cornerRadius, options.cornerRadius, {
      fill: options.fill
    } );

    // Foreground - displays the carousel's outline, created as a separate node so that it can be placed on top of everything, for a clean look.
    var foregroundNode = new Rectangle( 0, 0, backgroundWidth, backgroundHeight, options.cornerRadius, options.cornerRadius, {
      stroke: options.stroke
    } );

    // Layout
    if ( isHorizontal ) {
      nextButton.centerY = previousButton.centerY = windowNode.centerY = backgroundNode.centerY;
      nextButton.right = backgroundNode.right;
      previousButton.left = backgroundNode.left;
      windowNode.centerX = backgroundNode.centerX;
    }
    else {
      nextButton.centerX = previousButton.centerX = windowNode.centerX = backgroundNode.centerX;
      nextButton.bottom = backgroundNode.bottom;
      previousButton.top = backgroundNode.top;
      windowNode.centerY = backgroundNode.centerY;
    }

    // Number of pages
    var numberOfPages = items.length / options.itemsPerPage;
    if ( !Util.isInteger( numberOfPages ) ) {
      numberOfPages = Math.floor( numberOfPages + 1 );
    }

    // Number of the page that is visible in the carousel.
    var pageNumberProperty = new Property( options.defaultPageNumber );

    // iOS-style page control
    var pageControl = null;
    if ( options.pageControlVisible ) {

      pageControl = new PageControl( numberOfPages, pageNumberProperty, {
        orientation: options.orientation,
        pageVisibleColor: options.pageVisibleColor,
        pageNotVisibleColor: options.pageNotVisibleColor,
        dotRadius: options.dotRadius,
        dotSpacing: options.dotSpacing
      } );

      // Layout
      if ( isHorizontal ) {
        pageControl.centerX = backgroundNode.centerX;
        if ( options.pageControlLocation === 'top' ) {
          pageControl.bottom = backgroundNode.top - options.pageControlSpacing;
        }
        else if ( options.pageControlLocation === 'bottom' ) {
          pageControl.top = backgroundNode.bottom + options.pageControlSpacing;
        }
        else {
          throw new Error( 'incompatible pageControlLocation=' + options.pageControlLocation + ' for orientation=' + options.orientation );
        }
      }
      else {
        pageControl.centerY = backgroundNode.centerY;
        if ( options.pageControlLocation === 'left' ) {
          pageControl.right = backgroundNode.left - options.pageControlSpacing;
        }
        else if ( options.pageControlLocation === 'right' ) {
          pageControl.left = backgroundNode.right + options.pageControlSpacing;
        }
        else {
          throw new Error( 'incompatible pageControlLocation=' + options.pageControlLocation + ' for orientation=' + options.orientation );
        }
      }
    }

    // Scroll when the buttons are pressed
    var scrollTween;
    pageNumberProperty.link( function( pageNumber ) {

      assert && assert( pageNumber >= 0 && pageNumber <= numberOfPages - 1, 'pageNumber out of range: ' + pageNumber );

      // stop any animation that's in progress
      scrollTween && scrollTween.stop();

      // button state
      nextButton.enabled = pageNumber < ( numberOfPages - 1 );
      previousButton.enabled = pageNumber > 0;
      if ( options.hideDisabledButtons ) {
        nextButton.visible = nextButton.enabled;
        previousButton.visible = previousButton.enabled;
      }

      //TODO replace calls to Tween with a wrapper
      // Set up the animation to scroll the items in the carousel.
      var parameters;
      var animationDuration = 400; // ms
      var easing = TWEEN.Easing.Cubic.InOut;
      if ( isHorizontal ) {
        parameters = { left: scrollingNode.left };
        scrollTween = new TWEEN.Tween( parameters )
          .easing( easing )
          .to( { left: -pageNumber * scrollingDelta }, animationDuration )
          .onUpdate( function() {
            scrollingNode.left = parameters.left;
          } )
          .start();
      }
      else {
        parameters = { top: scrollingNode.top };
        scrollTween = new TWEEN.Tween( parameters )
          .easing( easing )
          .to( { top: -pageNumber * scrollingDelta }, animationDuration )
          .onUpdate( function() {
            scrollingNode.top = parameters.top;
          } )
          .start();
      }
    } );

    // Buttons modify the page number
    nextButton.addListener( function() {
      pageNumberProperty.set( pageNumberProperty.get() + 1 );
    } );
    previousButton.addListener( function() {
      pageNumberProperty.set( pageNumberProperty.get() - 1 );
    } );

    // public fields
    this.numberOfPages = numberOfPages; // @public (read-only) {number} number of pages in the carousel
    this.pageNumberProperty = pageNumberProperty; // @public {Property<number>} page number that is currently visible

    // @private
    this.disposeCarousel = function() {
      if ( pageControl ) {
        pageControl.dispose();
      }
    };

    options.children = [ backgroundNode, windowNode, nextButton, previousButton, foregroundNode ];
    if ( pageControl ) {
      options.children.push( pageControl );
    }

    Node.call( this, options );
  }

  return inherit( Node, Carousel, {

    dispose: function() { this.disposeCarousel(); }
  }, {

    // @static @public (read-only)
    DEFAULT_OPTIONS: DEFAULT_OPTIONS
  } );
} );
<|endoftext|>


<commit_before><commit_msg>Create microajax.js<commit_after><|endoftext|>


<commit_before>/*
 * Copyright 2017 Banco Bilbao Vizcaya Argentaria, S.A.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var Utils = (function() {
  'use strict';

  var breakFavIconTimeout;
  var breakTimeout;

  var colorMapping = {
    'daa038' : 'yellow', // Warning status color for Slack
    'd00000': 'red', // Danger status color for Slack
    'red': 'red',
    'yellow': 'yellow'
  };

  var notification;

  document.addEventListener('Message', checkNotification);
  document.addEventListener('HeadsUp', checkNotification);
  document.addEventListener('Message', notifyFavIcon);
  document.addEventListener('HeadsUp', notifyFavIcon);

  function checkNotification(data) {
    if(data.detail.description.indexOf('MIRRORBREAK!') >= 0) {
      $('.easter-egg').css('display','block');
      if(breakTimeout) {
        clearTimeout(breakTimeout);
      }
      breakTimeout = setTimeout(function () {
        $('.easter-egg').css('display', 'none');
        breakTimeout = undefined;
      }, 60000);
    } else if (Notification.permission === "granted") {
      if (data && data.detail) {
        if(notification) {
          notification.close();
        }
        notification = new Notification('MirrorGate Notification: ' + (data.detail.title || ''), {
          body: data.detail.description || '',
          icon: 'img/favicon-' + (colorMapping[data.detail.color] || 'blue') + '.png'
        });
      }
    }
  }

  function notifyFavIcon(event) {
    if(event && event.detail) {
      var icon = document.querySelector('link[rel*="icon"]');

      if(icon) {
        icon.href = 'img/favicon-' + (colorMapping[event.detail.color] || 'blue') + '.png';
        if(breakFavIconTimeout) {
          clearTimeout(breakFavIconTimeout);
        }
        breakFavIconTimeout = setTimeout(function() {
          icon.href = 'img/favicon.png';
          breakFavIconTimeout = undefined;
        }, 60000);
      }
    }
  }

  function clone(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null === obj || 'object' != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
      copy = new Date();
      copy.setTime(obj.getTime());
      return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
      copy = [];
      for (var i = 0, len = obj.length; i < len; i++) {
        copy[i] = clone(obj[i]);
      }
      return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
      copy = {};
      Object.setPrototypeOf(copy, Object.getPrototypeOf(obj));
      for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
      }
      return copy;
    }

    throw new Error('Unable to copy obj! Its type isn\'t supported.');
  }

  return {

    clone: clone,

    getDashboardId: function() {
      var dashboard = document.location.hash;
      if (dashboard && dashboard.length > 1) {
        dashboard = dashboard.substring(1);
      } else {
        var searchParams = new URLSearchParams(window.location.search);
        dashboard = searchParams.get('board');
      }
      return dashboard || '';
    },

    getSkin: function () {
      return this._skin || 'classic';
    },

    initSkin: function () {
      var self = this;
      return new Promise(function (resolve) {
        Service
          .get(Service.types.dashboard, Utils.getDashboardId())
          .addListener(function(details) {
            details = details && JSON.parse(details);
            self._skin = (details && details.skin) || 'classic';
            resolve(self._skin);
          });
      });
    },

    getWorkingDays: function(start, end) {
      var diff = 0;
      start = moment(start);
      end = moment(end);
      if (start.isBefore(end)) {
        end.add(-1, 'days');
        diff = moment().isoWeekdayCalc({
          rangeStart: start,
          rangeEnd: end,
          weekdays: [1, 2, 3, 4, 5],
          exclusions: [],
          inclusions: []
        });
      }
      return diff;
    },

    raiseEvent: function(source, detail) {
      source.dispatchEvent(
          new CustomEvent('dashboard-updated', {bubbles: true, detail: detail}));
    },

    toClassName: function(s) { return (s || s === 0) && ('' + s).toLowerCase().replace(' ', '-').replace('_','-'); },

    openDashboard : function(type, boardId) {
      document.location.href = type + '.html?board=' + encodeURIComponent( boardId || Utils.getDashboardId());
      return false;
    },

    openFeedbackForm: function () {
      window.mirrorGateConfig().then(function (config) {
        document.location.href = config.backOfficeUrl + 'index.html#/feedback';
      });
      return false;
    },

    openBackOffice: function (boardId) {
      window.mirrorGateConfig().then(function (config) {
        document.location.href = config.backOfficeUrl + (boardId ?
          'index.html#/edit/' + encodeURIComponent(boardId) + "?backToDashboard=true" :
          'index.html#/list');
      });
      return false;
    },

    compareVersions: function (version1, version2, regExp) {

      var v1parts = regExp.exec(version1) || [];
      var v2parts = regExp.exec(version2) || [];

      for (var i = 1; i < Math.max(v1parts.length, v2parts.length); ++i) {
        var part1 = parseInt(v1parts[i]) || 0;
        var part2 = parseInt(v2parts[i]) || 0;

        if (part1 !== part2) {
          return part1 - part2;
        }
      }

      return v1parts.length - v2parts.length;
    },

    rephraseVersion: function (version, regExp) {
      var parts = regExp.exec(version);
      return parts && (parts.length > 1) ? 'v' + parts.slice(1).join('.') : version;
    },

    normalEstimation: function (serie, confidence) {
      serie = serie || [];
      var z = {'80%': 0.84,'90%':1.29, '95%': 1.65, '99%': 2.33}[confidence || '80%'];

      var r = {
          count: 0,
          total: 0,
          avg: 0,
          sigma: 0,
          estimate: 0
      };

      serie.forEach(function(value) {
          r.count++;
          r.total += value;
      }, this);
      r.avg = r.total / r.count;

      serie.forEach(function(value) {
          r.sigma += Math.pow(value - r.avg,2);
      }, this);

      r.sigma = Math.sqrt(r.sigma / (r.total - 1));
      r.estimate = r.sigma * z + r.avg;
      return r;
    },

    getPercentageDifference: function (longPeriod, shortPeriod) {
      return longPeriod ? ((shortPeriod - longPeriod) / longPeriod) * 100 : 0;
    }
  };

})();

rivets.formatters.dateFrom = function(value, now) {
  return moment(value).from(now);
};

rivets.formatters.date = function(value, now) {
  return moment(value).calendar(now);
};

rivets.formatters.textCleanUp = function(value) {
  return decodeURIComponent(decodeURIComponent(value));
};

rivets.formatters.duration = function(value) {
  if(!value) {
    return "";
  }
  var duration = moment.duration(value);
  var rt = "" + duration.seconds() + " sec";
  if(duration.asMinutes() >= 1) {
    if(duration.seconds() < 1) {
      rt = "";
    } else {
      rt = " " + rt;
    }
    rt = "" + Math.round(duration.asMinutes()) + " min" + rt;
  }
  return rt;
};

rivets.formatters.round = function(value) {
  return Math.round(value);
};

rivets.formatters.bigNumber = function(value) {
  if(value < 10000) {
    return value;
  } else if(value < 10000000) {
    return Math.round(value / 1000) + 'K';
  } else {
    return Math.round(value / 1000000) + 'M';
  }
};

rivets.formatters.length = function(value) {
  return value ? (value.length || 0) : 0;
};

rivets.formatters['>='] = function (value, arg) {
  return value >= arg;
};

rivets.formatters['='] = function (value, arg) {
  return value === arg;
};

rivets.binders['pclass-*'] = function(el, value) {
  var prefix = this.args[0] + '-';
  var $el = $(el);
  var classList = el.className.split(/\s+/);
  classList.forEach(function(name) {
    if (name.startsWith(prefix)) {
      $el.removeClass(name);
    }
  });
  if (value !== null && value !== undefined) {
    $(el).addClass(prefix + Utils.toClassName(value));
  }
};

rivets.binders['setclass-*'] = function(el, value) {
  var cls = this.args[0];
  var $el = $(el);
  var classList = el.className.split(/\s+/);
  classList.forEach(function(name) {
    if (name === cls) {
      $el.removeClass(name);
    }
  });
  if (value) {
    $(el).addClass(cls);
  }
};

function styleBuilder(style, suffix) {
  suffix = suffix || '';

  return function(el, value) {
    $(el).css(style, '' + value + suffix);
  };
}

rivets.binders.ignore = {
    block: true,
    routine: function () { /* do nothing */ }
};

rivets.binders.width = styleBuilder('width','%');
rivets.binders.left = styleBuilder('left','%');
rivets.binders.right = styleBuilder('right','%');

rivets.binders.color = function(el, value) {
  $(el).css('color', '#' + value);
};
<commit_msg>fix(dashboard): avoid undefined TypeError<commit_after>/*
 * Copyright 2017 Banco Bilbao Vizcaya Argentaria, S.A.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var Utils = (function() {
  'use strict';

  var breakFavIconTimeout;
  var breakTimeout;

  var colorMapping = {
    'daa038' : 'yellow', // Warning status color for Slack
    'd00000': 'red', // Danger status color for Slack
    'red': 'red',
    'yellow': 'yellow'
  };

  var notification;

  document.addEventListener('Message', checkNotification);
  document.addEventListener('HeadsUp', checkNotification);
  document.addEventListener('Message', notifyFavIcon);
  document.addEventListener('HeadsUp', notifyFavIcon);

  function checkNotification(data) {
    if(data && data.detail && data.detail.description.indexOf('MIRRORBREAK!') >= 0) {
      $('.easter-egg').css('display','block');
      if(breakTimeout) {
        clearTimeout(breakTimeout);
      }
      breakTimeout = setTimeout(function () {
        $('.easter-egg').css('display', 'none');
        breakTimeout = undefined;
      }, 60000);
    } else if (Notification.permission === "granted") {
      if (data && data.detail) {
        if(notification) {
          notification.close();
        }
        notification = new Notification('MirrorGate Notification: ' + (data.detail.title || ''), {
          body: data.detail.description || '',
          icon: 'img/favicon-' + (colorMapping[data.detail.color] || 'blue') + '.png'
        });
      }
    }
  }

  function notifyFavIcon(event) {
    if(event && event.detail) {
      var icon = document.querySelector('link[rel*="icon"]');

      if(icon) {
        icon.href = 'img/favicon-' + (colorMapping[event.detail.color] || 'blue') + '.png';
        if(breakFavIconTimeout) {
          clearTimeout(breakFavIconTimeout);
        }
        breakFavIconTimeout = setTimeout(function() {
          icon.href = 'img/favicon.png';
          breakFavIconTimeout = undefined;
        }, 60000);
      }
    }
  }

  function clone(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null === obj || 'object' != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
      copy = new Date();
      copy.setTime(obj.getTime());
      return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
      copy = [];
      for (var i = 0, len = obj.length; i < len; i++) {
        copy[i] = clone(obj[i]);
      }
      return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
      copy = {};
      Object.setPrototypeOf(copy, Object.getPrototypeOf(obj));
      for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
      }
      return copy;
    }

    throw new Error('Unable to copy obj! Its type isn\'t supported.');
  }

  return {

    clone: clone,

    getDashboardId: function() {
      var dashboard = document.location.hash;
      if (dashboard && dashboard.length > 1) {
        dashboard = dashboard.substring(1);
      } else {
        var searchParams = new URLSearchParams(window.location.search);
        dashboard = searchParams.get('board');
      }
      return dashboard || '';
    },

    getSkin: function () {
      return this._skin || 'classic';
    },

    initSkin: function () {
      var self = this;
      return new Promise(function (resolve) {
        Service
          .get(Service.types.dashboard, Utils.getDashboardId())
          .addListener(function(details) {
            details = details && JSON.parse(details);
            self._skin = (details && details.skin) || 'classic';
            resolve(self._skin);
          });
      });
    },

    getWorkingDays: function(start, end) {
      var diff = 0;
      start = moment(start);
      end = moment(end);
      if (start.isBefore(end)) {
        end.add(-1, 'days');
        diff = moment().isoWeekdayCalc({
          rangeStart: start,
          rangeEnd: end,
          weekdays: [1, 2, 3, 4, 5],
          exclusions: [],
          inclusions: []
        });
      }
      return diff;
    },

    raiseEvent: function(source, detail) {
      source.dispatchEvent(
          new CustomEvent('dashboard-updated', {bubbles: true, detail: detail}));
    },

    toClassName: function(s) { return (s || s === 0) && ('' + s).toLowerCase().replace(' ', '-').replace('_','-'); },

    openDashboard : function(type, boardId) {
      document.location.href = type + '.html?board=' + encodeURIComponent( boardId || Utils.getDashboardId());
      return false;
    },

    openFeedbackForm: function () {
      window.mirrorGateConfig().then(function (config) {
        document.location.href = config.backOfficeUrl + 'index.html#/feedback';
      });
      return false;
    },

    openBackOffice: function (boardId) {
      window.mirrorGateConfig().then(function (config) {
        document.location.href = config.backOfficeUrl + (boardId ?
          'index.html#/edit/' + encodeURIComponent(boardId) + "?backToDashboard=true" :
          'index.html#/list');
      });
      return false;
    },

    compareVersions: function (version1, version2, regExp) {

      var v1parts = regExp.exec(version1) || [];
      var v2parts = regExp.exec(version2) || [];

      for (var i = 1; i < Math.max(v1parts.length, v2parts.length); ++i) {
        var part1 = parseInt(v1parts[i]) || 0;
        var part2 = parseInt(v2parts[i]) || 0;

        if (part1 !== part2) {
          return part1 - part2;
        }
      }

      return v1parts.length - v2parts.length;
    },

    rephraseVersion: function (version, regExp) {
      var parts = regExp.exec(version);
      return parts && (parts.length > 1) ? 'v' + parts.slice(1).join('.') : version;
    },

    normalEstimation: function (serie, confidence) {
      serie = serie || [];
      var z = {'80%': 0.84,'90%':1.29, '95%': 1.65, '99%': 2.33}[confidence || '80%'];

      var r = {
          count: 0,
          total: 0,
          avg: 0,
          sigma: 0,
          estimate: 0
      };

      serie.forEach(function(value) {
          r.count++;
          r.total += value;
      }, this);
      r.avg = r.total / r.count;

      serie.forEach(function(value) {
          r.sigma += Math.pow(value - r.avg,2);
      }, this);

      r.sigma = Math.sqrt(r.sigma / (r.total - 1));
      r.estimate = r.sigma * z + r.avg;
      return r;
    },

    getPercentageDifference: function (longPeriod, shortPeriod) {
      return longPeriod ? ((shortPeriod - longPeriod) / longPeriod) * 100 : 0;
    }
  };

})();

rivets.formatters.dateFrom = function(value, now) {
  return moment(value).from(now);
};

rivets.formatters.date = function(value, now) {
  return moment(value).calendar(now);
};

rivets.formatters.textCleanUp = function(value) {
  return decodeURIComponent(decodeURIComponent(value));
};

rivets.formatters.duration = function(value) {
  if(!value) {
    return "";
  }
  var duration = moment.duration(value);
  var rt = "" + duration.seconds() + " sec";
  if(duration.asMinutes() >= 1) {
    if(duration.seconds() < 1) {
      rt = "";
    } else {
      rt = " " + rt;
    }
    rt = "" + Math.round(duration.asMinutes()) + " min" + rt;
  }
  return rt;
};

rivets.formatters.round = function(value) {
  return Math.round(value);
};

rivets.formatters.bigNumber = function(value) {
  if(value < 10000) {
    return value;
  } else if(value < 10000000) {
    return Math.round(value / 1000) + 'K';
  } else {
    return Math.round(value / 1000000) + 'M';
  }
};

rivets.formatters.length = function(value) {
  return value ? (value.length || 0) : 0;
};

rivets.formatters['>='] = function (value, arg) {
  return value >= arg;
};

rivets.formatters['='] = function (value, arg) {
  return value === arg;
};

rivets.binders['pclass-*'] = function(el, value) {
  var prefix = this.args[0] + '-';
  var $el = $(el);
  var classList = el.className.split(/\s+/);
  classList.forEach(function(name) {
    if (name.startsWith(prefix)) {
      $el.removeClass(name);
    }
  });
  if (value !== null && value !== undefined) {
    $(el).addClass(prefix + Utils.toClassName(value));
  }
};

rivets.binders['setclass-*'] = function(el, value) {
  var cls = this.args[0];
  var $el = $(el);
  var classList = el.className.split(/\s+/);
  classList.forEach(function(name) {
    if (name === cls) {
      $el.removeClass(name);
    }
  });
  if (value) {
    $(el).addClass(cls);
  }
};

function styleBuilder(style, suffix) {
  suffix = suffix || '';

  return function(el, value) {
    $(el).css(style, '' + value + suffix);
  };
}

rivets.binders.ignore = {
    block: true,
    routine: function () { /* do nothing */ }
};

rivets.binders.width = styleBuilder('width','%');
rivets.binders.left = styleBuilder('left','%');
rivets.binders.right = styleBuilder('right','%');

rivets.binders.color = function(el, value) {
  $(el).css('color', '#' + value);
};
<|endoftext|>


<commit_before>module.exports.tasks = {

	/**
	* Watch
	* https://github.com/gruntjs/grunt-contrib-watch
	* Watches your scss, js etc for changes and compiles them
	*/
	watch: {
		scss: {
			files: ['<%%=config.css.scssDir%>/**/*.scss', '!<%%=config.css.scssDir%>/styleguide.scss'],
			<% if (statix == true) {%>
			tasks: ['sass:kickoff', 'autoprefixer:kickoff', 'copy:css']
			<% } else { %>
			tasks: ['sass:kickoff', 'autoprefixer:kickoff']
			<% } %>
		},

		"styleguide_scss": {
			files: ['<%%=config.css.scssDir%>/styleguide.scss'],
			tasks: [
				'sass:styleguide',
				'autoprefixer:styleguide'
			]
		},

		js: {
			<% if (browserify == true) {%>
			files: [
				'js/**/*.js',
				'!js/dist/**/*.js'
			],
			tasks: [
				'browserify:dev',
				<% if (statix == true) {%>,'copy:js'<% } %>
			]
			<% } else { %>
			files: ['<%%=config.js.fileList%>'],
			tasks: [
				'uglify',
				<% if (statix == true) {%>,'copy:js'<% } %>
			]
			<% } %>
		},

		livereload: {
			options: { livereload: true },
			<% if (statix == true) {%>
			files: ['<%%= config.statix.dir%>/dist/css/*.css']
			<% } else { %>
			files: ['<%%=config.css.distDir%>/*.css']
			<% } %>
		},

		grunticon : {
			files: ['img/src/*.svg', 'img/src/*.png'],
			tasks: [
				'clean:icons',
				'svgmin',
				'grunticon'
			]
		},

		grunt: {
			files: ['_grunt-configs/*.js', 'Gruntfile.js']
		}<% if (statix === true) {%>,

		assemble : {
			files: ['<%%= config.statix.dir%>/src/templates/**/*.hbs', '<%%= config.statix.dir%>/src/templates/**/*.md'],
			tasks: ['clean', 'assemble', 'newer:copy'],
			options: {
				livereload: true
			}
		},<% } %>
	}
};
<commit_msg>Updating statix JS watch process in gront<commit_after>module.exports.tasks = {

	/**
	* Watch
	* https://github.com/gruntjs/grunt-contrib-watch
	* Watches your scss, js etc for changes and compiles them
	*/
	watch: {
		scss: {
			files: ['<%%=config.css.scssDir%>/**/*.scss', '!<%%=config.css.scssDir%>/styleguide.scss'],
			<% if (statix == true) {%>
			tasks: ['sass:kickoff', 'autoprefixer:kickoff', 'copy:css']
			<% } else { %>
			tasks: ['sass:kickoff', 'autoprefixer:kickoff']
			<% } %>
		},

		"styleguide_scss": {
			files: ['<%%=config.css.scssDir%>/styleguide.scss'],
			tasks: [
				'sass:styleguide',
				'autoprefixer:styleguide'
			]
		},

		js: {
			<% if (browserify == true) {%>
			files: [
				'js/**/*.js',
				'!js/dist/**/*.js'
				<% if (statix == true) {%>,'!statix/assets/js/dist/**/*.js'<% } %>
			],
			tasks: [
				'browserify:dev',
				<% if (statix == true) {%>,'copy:js'<% } %>
			]
			<% } else { %>
			files: [
				'<%%=config.js.fileList%>'
				<% if (statix == true) {%>,'!statix/assets/js/dist/**/*.js'<% } %>
			],
			tasks: [
				'uglify',
				<% if (statix == true) {%>,'copy:js'<% } %>
			]
			<% } %>
		},

		livereload: {
			options: { livereload: true },
			<% if (statix == true) {%>
			files: ['<%%= config.statix.dir%>/dist/css/*.css']
			<% } else { %>
			files: ['<%%=config.css.distDir%>/*.css']
			<% } %>
		},

		grunticon : {
			files: ['img/src/*.svg', 'img/src/*.png'],
			tasks: [
				'clean:icons',
				'svgmin',
				'grunticon'
			]
		},

		grunt: {
			files: ['_grunt-configs/*.js', 'Gruntfile.js']
		}<% if (statix === true) {%>,

		assemble : {
			files: ['<%%= config.statix.dir%>/src/templates/**/*.hbs', '<%%= config.statix.dir%>/src/templates/**/*.md'],
			tasks: ['clean', 'assemble', 'newer:copy'],
			options: {
				livereload: true
			}
		},<% } %>
	}
};
<|endoftext|>


<commit_before>/*globals yankError: false, extend: false, call: false, parseDocId: false */

"use strict";

/*
 * A generic pouch adapter
 */
var PouchAdapter = function(opts, callback) {

  var api = {};
  var customApi = Pouch.adapters[opts.adapter](opts, function(err, db) {
    if (err) {
      if (callback) {
        callback(err);
      }
      return;
    }

    for (var j in api) {
      if (!db.hasOwnProperty(j)) {
        db[j] = api[j];
      }
    }
    callback(err, db);
  });

  api.post = function (doc, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    return customApi.bulkDocs({docs: [doc]}, opts, yankError(callback));
  };

  api.put = function(doc, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }

    if (!doc || !('_id' in doc)) {
      return call(callback, Pouch.Errors.MISSING_ID);
    }
    return customApi.bulkDocs({docs: [doc]}, opts, yankError(callback));
  };

  api.putAttachment = api.putAttachment = function (id, rev, doc, type, callback) {
    if (typeof type === 'function') {
      callback = type;
      type = undefined;
    }
    id = parseDocId(id);
    api.get(id.docId, function(err, obj) {
      if (err) {
        call(callback, err);
        return;
      }

      if (obj._rev !== rev) {
        call(callback, Pouch.Errors.REV_CONFLICT);
        return;
      }

      obj._attachments = obj._attachments || {};
      obj._attachments[id.attachmentId] = {
        content_type: type || doc.type,
        data: doc
      };
      api.put(obj, callback);
    });
  };

   api.removeAttachment = function (id, rev, callback) {
    id = parseDocId(id);
    api.get(id.docId, function(err, obj) {
      if (err) {
        call(callback, err);
        return;
      }

      if (obj._rev !== rev) {
        call(callback, Pouch.Errors.REV_CONFLICT);
        return;
      }

      obj._attachments = obj._attachments || {};
      delete obj._attachments[id.attachmentId];
      api.put(obj, callback);
    });
  };

  api.remove = function (doc, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    if (opts === undefined) {
      opts = {};
    }
    opts.was_delete = true;
    var newDoc = extend(true, {}, doc);
    newDoc._deleted = true;
    return customApi.bulkDocs({docs: [newDoc]}, opts, yankError(callback));
  };

  api.revsDiff = function (req, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    var ids = Object.keys(req);
    var count = 0;
    var missing = {};

    function readDoc(err, doc, id) {
      req[id].map(function(revId) {
        var matches = function(x) { return x.rev !== revId; };
        if (!doc || doc._revs_info.every(matches)) {
          if (!missing[id]) {
            missing[id] = {missing: []};
          }
          missing[id].missing.push(revId);
        }
      });

      if (++count === ids.length) {
        return call(callback, null, missing);
      }
    }

    ids.map(function(id) {
      api.get(id, {revs_info: true}, function(err, doc) {
        readDoc(err, doc, id);
      });
    });
  };

  /* Begin api wrappers. Specific funtionality to storage belongs in the _[method] */
  api.get = function (id, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }

    id = parseDocId(id);
    if (id.attachmentId !== '') {
      return customApi.getAttachment(id, callback);
    }
    return customApi._get(id, opts, callback);

  };

  api.getAttachment = function(id, opts, callback) {
    if (opts instanceof Function) {
      callback = opts;
      opts = {};
    }
    if (typeof id === 'string') {
      id = parseDocId(id);
    }

    return customApi._getAttachment(id, opts, callback);
  };

  api.allDocs = function(opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    if ('keys' in opts) {
      if ('startkey' in opts) {
        call(callback, extend({
          reason: 'Query parameter `start_key` is not compatible with multi-get'
        }, Pouch.Errors.QUERY_PARSE_ERROR));
        return;
      }
      if ('endkey' in opts) {
        call(callback, extend({
          reason: 'Query parameter `end_key` is not compatible with multi-get'
        }, Pouch.Errors.QUERY_PARSE_ERROR));
        return;
      }
    }

    return customApi._allDocs(opts, callback);
  };

  api.changes = function(opts) {
    return customApi._changes(opts);
  };

  api.close = function(callback) {
    return customApi._close(callback);
  };

  api.info = function(callback) {
    return customApi._info(callback);
  };

  api.bulkDocs = function(req, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    if (!opts) {
      opts = {};
    }

    if (!req || !req.docs || req.docs.length < 1) {
      return call(callback, Pouch.Errors.MISSING_BULK_DOCS);
    }

    if (!Array.isArray(req.docs)) {
      return call(callback, Pouch.Errors.QUERY_PARSE_ERROR);
    }

    return customApi._bulkDocs(req, opts, callback);
  };

  /* End Wrappers */

  api.replicate = {};

  api.replicate.from = function (url, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    return Pouch.replicate(url, customApi, opts, callback);
  };

  api.replicate.to = function (dbName, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    return Pouch.replicate(customApi, dbName, opts, callback);
  };

  for (var j in api) {
    if (!customApi.hasOwnProperty(j)) {
      customApi[j] = api[j];
    }
  }
  return customApi;
};

if (typeof module !== 'undefined' && module.exports) {
  module.exports = PouchAdapter;
}
<commit_msg>couple missing adapter methods<commit_after>/*globals yankError: false, extend: false, call: false, parseDocId: false */

"use strict";

/*
 * A generic pouch adapter
 */
var PouchAdapter = function(opts, callback) {

  var api = {};
  var customApi = Pouch.adapters[opts.adapter](opts, function(err, db) {
    if (err) {
      if (callback) {
        callback(err);
      }
      return;
    }

    for (var j in api) {
      if (!db.hasOwnProperty(j)) {
        db[j] = api[j];
      }
    }
    callback(err, db);
  });

  api.post = function (doc, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    return customApi.bulkDocs({docs: [doc]}, opts, yankError(callback));
  };

  api.put = function(doc, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }

    if (!doc || !('_id' in doc)) {
      return call(callback, Pouch.Errors.MISSING_ID);
    }
    return customApi.bulkDocs({docs: [doc]}, opts, yankError(callback));
  };

  api.putAttachment = api.putAttachment = function (id, rev, doc, type, callback) {
    if (typeof type === 'function') {
      callback = type;
      type = undefined;
    }
    id = parseDocId(id);
    api.get(id.docId, function(err, obj) {
      if (err) {
        call(callback, err);
        return;
      }

      if (obj._rev !== rev) {
        call(callback, Pouch.Errors.REV_CONFLICT);
        return;
      }

      obj._attachments = obj._attachments || {};
      obj._attachments[id.attachmentId] = {
        content_type: type || doc.type,
        data: doc
      };
      api.put(obj, callback);
    });
  };

   api.removeAttachment = function (id, rev, callback) {
    id = parseDocId(id);
    api.get(id.docId, function(err, obj) {
      if (err) {
        call(callback, err);
        return;
      }

      if (obj._rev !== rev) {
        call(callback, Pouch.Errors.REV_CONFLICT);
        return;
      }

      obj._attachments = obj._attachments || {};
      delete obj._attachments[id.attachmentId];
      api.put(obj, callback);
    });
  };

  api.remove = function (doc, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    if (opts === undefined) {
      opts = {};
    }
    opts.was_delete = true;
    var newDoc = extend(true, {}, doc);
    newDoc._deleted = true;
    return customApi.bulkDocs({docs: [newDoc]}, opts, yankError(callback));
  };

  api.revsDiff = function (req, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    var ids = Object.keys(req);
    var count = 0;
    var missing = {};

    function readDoc(err, doc, id) {
      req[id].map(function(revId) {
        var matches = function(x) { return x.rev !== revId; };
        if (!doc || doc._revs_info.every(matches)) {
          if (!missing[id]) {
            missing[id] = {missing: []};
          }
          missing[id].missing.push(revId);
        }
      });

      if (++count === ids.length) {
        return call(callback, null, missing);
      }
    }

    ids.map(function(id) {
      api.get(id, {revs_info: true}, function(err, doc) {
        readDoc(err, doc, id);
      });
    });
  };

  /* Begin api wrappers. Specific funtionality to storage belongs in the _[method] */
  api.get = function (id, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }

    id = parseDocId(id);
    if (id.attachmentId !== '') {
      return customApi.getAttachment(id, callback);
    }
    return customApi._get(id, opts, callback);

  };

  api.getAttachment = function(id, opts, callback) {
    if (opts instanceof Function) {
      callback = opts;
      opts = {};
    }
    if (typeof id === 'string') {
      id = parseDocId(id);
    }

    return customApi._getAttachment(id, opts, callback);
  };

  api.allDocs = function(opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    if ('keys' in opts) {
      if ('startkey' in opts) {
        call(callback, extend({
          reason: 'Query parameter `start_key` is not compatible with multi-get'
        }, Pouch.Errors.QUERY_PARSE_ERROR));
        return;
      }
      if ('endkey' in opts) {
        call(callback, extend({
          reason: 'Query parameter `end_key` is not compatible with multi-get'
        }, Pouch.Errors.QUERY_PARSE_ERROR));
        return;
      }
    }

    return customApi._allDocs(opts, callback);
  };

  api.changes = function(opts) {
    return customApi._changes(opts);
  };

  api.close = function(callback) {
    return customApi._close(callback);
  };

  api.info = function(callback) {
    return customApi._info(callback);
  };

  api.id = function(callback) {
    return customApi._id(callback);
  };

  api.type = function() {
    return (typeof customApi._type === 'function') ? customApi._type() : opts.adapter;
  };

  api.bulkDocs = function(req, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    if (!opts) {
      opts = {};
    }

    if (!req || !req.docs || req.docs.length < 1) {
      return call(callback, Pouch.Errors.MISSING_BULK_DOCS);
    }

    if (!Array.isArray(req.docs)) {
      return call(callback, Pouch.Errors.QUERY_PARSE_ERROR);
    }

    return customApi._bulkDocs(req, opts, callback);
  };

  /* End Wrappers */

  api.replicate = {};

  api.replicate.from = function (url, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    return Pouch.replicate(url, customApi, opts, callback);
  };

  api.replicate.to = function (dbName, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    return Pouch.replicate(customApi, dbName, opts, callback);
  };

  for (var j in api) {
    if (!customApi.hasOwnProperty(j)) {
      customApi[j] = api[j];
    }
  }
  return customApi;
};

if (typeof module !== 'undefined' && module.exports) {
  module.exports = PouchAdapter;
}
<|endoftext|>


<commit_before>/* ----------------------------------------------------------
  Utilities
---------------------------------------------------------- */

/* Console log fix
-------------------------- */
if (typeof(console) === 'undefined') {
    var console = {};
    console.log = console.error = console.info = console.debug = console.warn = console.trace = console.dir = console.dirxml = console.group = console.groupEnd = console.time = console.timeEnd = console.assert = console.profile = function() {};
}

/* Array utilities
  https://github.com/Darklg/JavaScriptUtilities/blob/master/assets/js/vanilla-js/libs/vanilla-arrays.js
-------------------------- */
Array.contains = function(needle, haystack) {
    var i = 0,
        length = haystack.length;

    for (; i < length; i++) {
        if (haystack[i] === needle) return true;
    }
    return false;
};
Array.each = function(arrayToParse, callback) {
    var i = 0,
        length = arrayToParse.length;
    for (; i < length; i++) {
        callback(arrayToParse[i]);
    }
};

/* CSS classes utilities
  https://github.com/Darklg/JavaScriptUtilities/blob/master/assets/js/vanilla-js/libs/vanilla-classes.js
-------------------------- */
Element.getClassNames = function(element) {
    var classNames = [],
        elementClassName = element.className;
    if (elementClassName !== '') {
        elementClassName = elementClassName.replace(/\s+/g, ' ');
        classNames = elementClassName.split(' ');
    }
    return classNames;
};
Element.hasClass = function(element, className) {
    if (element.classList) {
        return element.classList.contains(className);
    }
    return Array.contains(className, Element.getClassNames(element));
};
Element.addClass = function(element, className) {
    if (element.classList) {
        element.classList.add(className);
        return;
    }
    if (!Element.hasClass(element, className)) {
        var elementClasses = Element.getClassNames(element);
        elementClasses.push(className);
        element.className = elementClasses.join(' ');
    }
};
Element.removeClass = function(element, className) {
    if (element.classList) {
        element.classList.remove(className);
        return;
    }
    var elementClasses = Element.getClassNames(element);
    var newElementClasses = [];
    var i = 0,
        arLength = elementClasses.length;
    for (; i < arLength; i++) {
        if (elementClasses[i] !== className) {
            newElementClasses.push(elementClasses[i]);
        }
    }
    element.className = newElementClasses.join(' ');
};
Element.toggleClass = function(element, className) {
    if (!Element.hasClass(element, className)) {
        Element.addClass(element, className);
    }
    else {
        Element.removeClass(element, className);
    }
};

/* Add Event
  https://github.com/Darklg/JavaScriptUtilities/blob/master/assets/js/vanilla-js/libs/vanilla-events.js
-------------------------- */
window.addEvent = function(el, eventName, callback) {
    if (el.addEventListener) {
        el.addEventListener(eventName, callback, false);
    }
    else if (el.attachEvent) {
        el.attachEvent("on" + eventName, function(e) {
            return callback.call(el, e);
        });
    }
};
window.eventPreventDefault = function(event) {
    return (event.preventDefault) ? event.preventDefault() : event.returnValue = false;
};

/* Draggable

  Sources :
  http://jsfiddle.net/5t3Ju/
  http://stackoverflow.com/questions/9334084/moveable-draggable-div
  http://jsfiddle.net/tovic/Xcb8d/light/
-------------------------- */

var dragg = function(id) {

  // Variables
  this.elem = document.getElementById(id),
  this.selected = null,  // Selected element
  this.dragged = false,  // Dragging status
  this.x_pos = 0, this.y_pos = 0, // Stores x & y coordinates of the mouse pointer
  this.x_elem = 0, this.y_elem = 0; // Stores top, left values (edge) of the element

  // Start dragging
  window.addEvent(elem, 'mousedown', function(e){
    // Prevent firefox native D'n'D behavior
    window.eventPreventDefault(e);

    selected = elem;
    x_elem = x_pos - selected.offsetLeft;
    y_elem = y_pos - selected.offsetTop;
  });

  // Will be called when user dragging an element
  window.addEvent(window, 'mousemove', function(e){
    // Get position
    x_pos = document.all ? window.event.clientX : e.pageX;
    y_pos = document.all ? window.event.clientY : e.pageY;

    if (selected !== null) {
      dragged = true;
      selected.style.left = (x_pos - x_elem) + 'px';
      selected.style.top = (y_pos - y_elem) + 'px';
    }
  });

  // Destroy the object when we are done
  window.addEvent(window, 'mouseup', function(e){
      selected = null;
  });

  // Handle click event
  window.addEvent(elem, 'click', function(e){
      // Prevent default event
      window.eventPreventDefault(e);

      // Do not prapagate to other click event if dragged out
      if (dragged) {
        e.stopImmediatePropagation();
      }
      // Reset dragging status
      dragged = false;
  });
}

/* Smallest DOMReady
  http://dustindiaz.com/smallest-domready-ever
-------------------------- */
function domReady(cb) {
   /in/.test(document.readyState) // in = loadINg
      ? setTimeout('domReady('+cb+')', 9)
      : cb();
}

/* ----------------------------------------------------------
  Main
---------------------------------------------------------- */
domReady(function(){
  // Don't do this in iframe
  if (window.self !== window.top) {return false;}

  // Set and store meta viewport
  var meta_viewport = document.querySelector('meta[name="viewport"]');
  if (meta_viewport === null) {
    meta_viewport = document.createElement('meta');
    meta_viewport.setAttribute('name', "viewport");
    meta_viewport.setAttribute('content', "");
    document.getElementsByTagName('head')[0].insertBefore(meta_viewport, null);
  }
  meta_viewport = document.querySelector('meta[name="viewport"]');
  meta_viewport_content = meta_viewport.getAttribute('content');

  // Add portal stylesheet
  var portalStyle = document.createElement("link");
  portalStyle.setAttribute("rel", "stylesheet");
  portalStyle.setAttribute("type", "text/css");
  portalStyle.setAttribute("href", '/ynhpanel.css');
  document.getElementsByTagName("head")[0].insertBefore(portalStyle, null);

  // Create portal link
  var portal = document.createElement('a');
  portal.setAttribute('id', 'ynh-overlay-switch');
  portal.setAttribute('href', '/yunohost/sso/');
  portal.setAttribute('class', 'disableAjax');
  document.body.insertBefore(portal, null);

  // Portal link is draggable, for user convenience
  dragg('ynh-overlay-switch');

  // Create overlay element
  var overlay = document.createElement("div");
  overlay.setAttribute("id","ynh-overlay");
  overlay.setAttribute("style","display:none");

  document.body.insertBefore(overlay, null);

  //Color Application
  var colors = ['redbg','purpledarkbg','darkbluebg','orangebg','greenbg','darkbluebg','purpledarkbg','yellowbg','lightpinkbg','pinkbg','turquoisebg','yellowbg','lightbluebg','purpledarkbg', 'bluebg'];

  // Get user's app
  var r = new XMLHttpRequest();
  r.open("GET", "/ynhpanel.json", true);
  r.onreadystatechange = function () {
    // Die if error
    if (r.readyState != 4 || r.status != 200) return;

    // Response is JSON
    response = JSON.parse(r.responseText);

    // Add overlay header
    overlay.innerHTML += '
' +
                          '

'+response.t_logout+'

'+
                          '' +
                            '
'+ response.uid +'
' +
                            ''+ response.givenName + ' ' + response.sn +'' +
                            ''+ response.mail +'' +
                          '' +
                        '
';

    // Add application links
    var links = [];
    Array.prototype.forEach.call(response.app, function(app, n){
      randomColorNumber = parseInt(app.name, 36) % colors.length;
      links.push('
'+app.name+'
');
    });
    overlay.innerHTML += '

'+ links.join("\n") +'

';

    // Add footer links
    overlay.innerHTML += '
' + "\n" +
                          ''+ response.t_footerlink_edit +'' + "\n" +
                          ''+ response.t_footerlink_documentation +'' + "\n" +
                          ''+ response.t_footerlink_support +'' + "\n" +
                          ''+ response.t_footerlink_administration +'' + "\n" +
                        '
';

    // Add overlay to DOM
    var btn = document.getElementById('logo'),
        yunoverlay = document.getElementById('ynh-overlay'),
        user = document.getElementById('ynh-user'),
        apps = document.getElementById('ynh-apps');

    var pfx = ["webkit", "moz", "MS", "o", ""];
    function PrefixedEvent(element, type, callback) {
      for (var p = 0; p < pfx.length; p++) {
        if (!pfx[p]) type = type.toLowerCase();
        element.addEventListener(pfx[p]+type, callback, false);
      }
    }

    // Bind YNH Button
    window.addEvent(portal, 'click', function(e){
      // Prevent default click
      window.eventPreventDefault(e);
      // Toggle overlay on YNHPortal button
      //Element.toggleClass(overlay, 'visible');
      Element.toggleClass(portal, 'visible');
      Element.toggleClass(document.querySelector('html'), 'ynh-panel-active');

      if(yunoverlay.classList.contains('ynh-active')) {
          meta_viewport.setAttribute('content', meta_viewport_content);
          yunoverlay.classList.add('ynh-fadeOut');
          PrefixedEvent(yunoverlay, "AnimationEnd", function(){
            if(yunoverlay.classList.contains('ynh-fadeOut')) {
              yunoverlay.classList.remove('ynh-active');
            }
          });
        }else {
          meta_viewport.setAttribute('content', "width=device-width");
          yunoverlay.classList.remove('ynh-fadeOut');
          yunoverlay.classList.add('ynh-active');
        }
    });

  };
  r.send();

});
<commit_msg>Make tile dragging work on mobile devices<commit_after>/* ----------------------------------------------------------
  Utilities
---------------------------------------------------------- */

/* Console log fix
-------------------------- */
if (typeof(console) === 'undefined') {
    var console = {};
    console.log = console.error = console.info = console.debug = console.warn = console.trace = console.dir = console.dirxml = console.group = console.groupEnd = console.time = console.timeEnd = console.assert = console.profile = function() {};
}

/* Array utilities
  https://github.com/Darklg/JavaScriptUtilities/blob/master/assets/js/vanilla-js/libs/vanilla-arrays.js
-------------------------- */
Array.contains = function(needle, haystack) {
    var i = 0,
        length = haystack.length;

    for (; i < length; i++) {
        if (haystack[i] === needle) return true;
    }
    return false;
};
Array.each = function(arrayToParse, callback) {
    var i = 0,
        length = arrayToParse.length;
    for (; i < length; i++) {
        callback(arrayToParse[i]);
    }
};

/* CSS classes utilities
  https://github.com/Darklg/JavaScriptUtilities/blob/master/assets/js/vanilla-js/libs/vanilla-classes.js
-------------------------- */
Element.getClassNames = function(element) {
    var classNames = [],
        elementClassName = element.className;
    if (elementClassName !== '') {
        elementClassName = elementClassName.replace(/\s+/g, ' ');
        classNames = elementClassName.split(' ');
    }
    return classNames;
};
Element.hasClass = function(element, className) {
    if (element.classList) {
        return element.classList.contains(className);
    }
    return Array.contains(className, Element.getClassNames(element));
};
Element.addClass = function(element, className) {
    if (element.classList) {
        element.classList.add(className);
        return;
    }
    if (!Element.hasClass(element, className)) {
        var elementClasses = Element.getClassNames(element);
        elementClasses.push(className);
        element.className = elementClasses.join(' ');
    }
};
Element.removeClass = function(element, className) {
    if (element.classList) {
        element.classList.remove(className);
        return;
    }
    var elementClasses = Element.getClassNames(element);
    var newElementClasses = [];
    var i = 0,
        arLength = elementClasses.length;
    for (; i < arLength; i++) {
        if (elementClasses[i] !== className) {
            newElementClasses.push(elementClasses[i]);
        }
    }
    element.className = newElementClasses.join(' ');
};
Element.toggleClass = function(element, className) {
    if (!Element.hasClass(element, className)) {
        Element.addClass(element, className);
    }
    else {
        Element.removeClass(element, className);
    }
};

/* Add Event
  https://github.com/Darklg/JavaScriptUtilities/blob/master/assets/js/vanilla-js/libs/vanilla-events.js
-------------------------- */
window.addEvent = function(el, eventName, callback) {
    if (el.addEventListener) {
        el.addEventListener(eventName, callback, false);
    }
    else if (el.attachEvent) {
        el.attachEvent("on" + eventName, function(e) {
            return callback.call(el, e);
        });
    }
};
window.eventPreventDefault = function(event) {
    return (event.preventDefault) ? event.preventDefault() : event.returnValue = false;
};

/* Draggable

  Sources :
  http://jsfiddle.net/5t3Ju/
  http://stackoverflow.com/questions/9334084/moveable-draggable-div
  http://jsfiddle.net/tovic/Xcb8d/light/
-------------------------- */

var dragg = function(id) {

  // Variables
  this.elem = document.getElementById(id),
  this.selected = null,  // Selected element
  this.dragged = false,  // Dragging status
  this.x_pos = 0, this.y_pos = 0, // Stores x & y coordinates of the mouse pointer
  this.x_elem = 0, this.y_elem = 0; // Stores top, left values (edge) of the element

  var _initDrag = function(e){
    // Prevent firefox native D'n'D behavior
    window.eventPreventDefault(e);

    if (e.type === "touchstart"){
      x_pos = e.touches[0].clientX;
      y_pos = e.touches[0].clientY;
    }

    selected = elem;
    x_elem = x_pos - selected.offsetLeft;
    y_elem = y_pos - selected.offsetTop;
  };

  var _shutDrag = function(e){
      selected = null;
  };

  var _onMove = function(e){
    // Get position
    x_pos = document.all ? window.event: e.pageX;
    y_pos = document.all ? window.event : e.pageY;

    if (e.type === "touchmove"){
        x_pos = e.touches[0].clientX;
        y_pos = e.touches[0].clientY;
    }

    if (selected !== null) {
      dragged = true;
      selected.style.left = (x_pos - x_elem) + 'px';
      selected.style.top = (y_pos - y_elem) + 'px';
    }
  };

  // Start dragging
  window.addEvent(elem, 'mousedown', _initDrag);
  window.addEvent(elem, 'touchstart', _initDrag);

  // Will be called when user dragging an element
  window.addEvent(window, 'mousemove', _onMove);
  window.addEvent(window, 'touchmove', _onMove);

  // Destroy the object when we are done
  window.addEvent(window, 'mouseup', _shutDrag);
  window.addEvent(window, 'touchend', _shutDrag);
  window.addEvent(window, 'touchcancel', _shutDrag);

  // Handle click event
  window.addEvent(elem, 'click', function(e){
      // Prevent default event
      window.eventPreventDefault(e);

      // Do not prapagate to other click event if dragged out
      if (dragged) {
        e.stopImmediatePropagation();
      }
      // Reset dragging status
      dragged = false;
  });
}

/* Smallest DOMReady
  http://dustindiaz.com/smallest-domready-ever
-------------------------- */
function domReady(cb) {
   /in/.test(document.readyState) // in = loadINg
      ? setTimeout('domReady('+cb+')', 9)
      : cb();
}

/* ----------------------------------------------------------
  Main
---------------------------------------------------------- */
domReady(function(){
  // Don't do this in iframe
  if (window.self !== window.top) {return false;}

  // Set and store meta viewport
  var meta_viewport = document.querySelector('meta[name="viewport"]');
  if (meta_viewport === null) {
    meta_viewport = document.createElement('meta');
    meta_viewport.setAttribute('name', "viewport");
    meta_viewport.setAttribute('content', "");
    document.getElementsByTagName('head')[0].insertBefore(meta_viewport, null);
  }
  meta_viewport = document.querySelector('meta[name="viewport"]');
  meta_viewport_content = meta_viewport.getAttribute('content');

  // Add portal stylesheet
  var portalStyle = document.createElement("link");
  portalStyle.setAttribute("rel", "stylesheet");
  portalStyle.setAttribute("type", "text/css");
  portalStyle.setAttribute("href", '/ynhpanel.css');
  document.getElementsByTagName("head")[0].insertBefore(portalStyle, null);

  // Create portal link
  var portal = document.createElement('a');
  portal.setAttribute('id', 'ynh-overlay-switch');
  portal.setAttribute('href', '/yunohost/sso/');
  portal.setAttribute('class', 'disableAjax');
  document.body.insertBefore(portal, null);

  // Portal link is draggable, for user convenience
  dragg('ynh-overlay-switch');

  // Create overlay element
  var overlay = document.createElement("div");
  overlay.setAttribute("id","ynh-overlay");
  overlay.setAttribute("style","display:none");

  document.body.insertBefore(overlay, null);

  //Color Application
  var colors = ['redbg','purpledarkbg','darkbluebg','orangebg','greenbg','darkbluebg','purpledarkbg','yellowbg','lightpinkbg','pinkbg','turquoisebg','yellowbg','lightbluebg','purpledarkbg', 'bluebg'];

  // Get user's app
  var r = new XMLHttpRequest();
  r.open("GET", "/ynhpanel.json", true);
  r.onreadystatechange = function () {
    // Die if error
    if (r.readyState != 4 || r.status != 200) return;

    // Response is JSON
    response = JSON.parse(r.responseText);

    // Add overlay header
    overlay.innerHTML += '
' +
                          '

'+response.t_logout+'

'+
                          '' +
                            '
'+ response.uid +'
' +
                            ''+ response.givenName + ' ' + response.sn +'' +
                            ''+ response.mail +'' +
                          '' +
                        '
';

    // Add application links
    var links = [];
    Array.prototype.forEach.call(response.app, function(app, n){
      randomColorNumber = parseInt(app.name, 36) % colors.length;
      links.push('
'+app.name+'
');
    });
    overlay.innerHTML += '

'+ links.join("\n") +'

';

    // Add footer links
    overlay.innerHTML += '
' + "\n" +
                          ''+ response.t_footerlink_edit +'' + "\n" +
                          ''+ response.t_footerlink_documentation +'' + "\n" +
                          ''+ response.t_footerlink_support +'' + "\n" +
                          ''+ response.t_footerlink_administration +'' + "\n" +
                        '
';

    // Add overlay to DOM
    var btn = document.getElementById('logo'),
        yunoverlay = document.getElementById('ynh-overlay'),
        user = document.getElementById('ynh-user'),
        apps = document.getElementById('ynh-apps');

    var pfx = ["webkit", "moz", "MS", "o", ""];
    function PrefixedEvent(element, type, callback) {
      for (var p = 0; p < pfx.length; p++) {
        if (!pfx[p]) type = type.toLowerCase();
        element.addEventListener(pfx[p]+type, callback, false);
      }
    }

    // Bind YNH Button
    window.addEvent(portal, 'click', function(e){
      // Prevent default click
      window.eventPreventDefault(e);
      // Toggle overlay on YNHPortal button
      //Element.toggleClass(overlay, 'visible');
      Element.toggleClass(portal, 'visible');
      Element.toggleClass(document.querySelector('html'), 'ynh-panel-active');

      if(yunoverlay.classList.contains('ynh-active')) {
          meta_viewport.setAttribute('content', meta_viewport_content);
          yunoverlay.classList.add('ynh-fadeOut');
          PrefixedEvent(yunoverlay, "AnimationEnd", function(){
            if(yunoverlay.classList.contains('ynh-fadeOut')) {
              yunoverlay.classList.remove('ynh-active');
            }
          });
        }else {
          meta_viewport.setAttribute('content', "width=device-width");
          yunoverlay.classList.remove('ynh-fadeOut');
          yunoverlay.classList.add('ynh-active');
        }
    });

  };
  r.send();

});
<|endoftext|>


<commit_before>/**
 * MultiDRM loader
 */
( function( mw, $ ) {
	"use strict";
	var mseSupported = (window['MediaSource'] || window['WebKitMediaSource']);
	//Load 3rd party plugins if DRM sources are available
	mw.addKalturaConfCheck( function( embedPlayer, callback ){
		if( embedPlayer.isPluginEnabled( 'multiDrm' ) ) {
			var drmConfig = setEmbedPlayerConfig(embedPlayer);
			if ((mseSupported && !mw.isFirefox) || drmConfig.forceDASH) {
				mw.log("Media Source Extensions supported on this browser");
				registerDashPlayer();
				//Get multiDRM supported sources
				var allSources = embedPlayer.getSources();
				var drmSources = getMultiDrmSupportedSources(allSources);
				//If there are supported medias load the playback library
				if ( hasDrmSources(drmSources) ) {
					mw.log("Media sources found, loading DASH player");
					var clDashPlayerUrl = embedPlayer.getKalturaConfig( "multiDrm", "clDashPlayerUrl" ) || mw.getMwEmbedPath() + "node_modules/mwEmbed-Dash-Everywhere/video.js";
					var dashJsUrl = embedPlayer.getKalturaConfig( "multiDrm", "dashJsUrl" ) || mw.getMwEmbedPath() + "node_modules/mwEmbed-Dash-Everywhere/cldasheverywhere.min.js";
					if (clDashPlayerUrl && dashJsUrl) {
						$.getScript( clDashPlayerUrl)
							.then(function(){return $.getScript( dashJsUrl)})
							.done(function(){
								mw.log("DASH player loaded");
								//Set reference for DASH playback engine
								mw.dash = {
									player: videojs
								};
								callback();
							})
							.fail(function( ) {
								mw.log("Error::Playback engine couldn't be found");
								callback();
							});
					} else {
						mw.log("Playback engine couldn't be found, not loading DASH player");
						callback();
					}
				} else {
					mw.log("No media sources found, not loading DASH player");
					callback();
				}
			} else {
				mw.log("Media Source Extensions not supported on this browser");
				callback();
			}
		} else {
			callback();
		}
	});

	function setEmbedPlayerConfig(embedPlayer){
		//Get user configuration
		var drmUserConfig = embedPlayer.getKalturaConfig("multiDrm");
		//Get default config
		var drmConfig = getDefaultDrmConfig(embedPlayer.kpartnerid);
		//Deep extend custom config
		$.extend(true, drmConfig, drmUserConfig);
		embedPlayer.setKalturaConfig("multiDrm", drmConfig);
		return drmConfig;
	}

	function getMultiDrmSupportedSources(sources){
		var drmSources = sources.filter( function ( source ) {
			return ( ( source.mimeType === "application/dash+xml" ) ||
			( (source.mimeType === "video/ism" || source.mimeType === "video/playreadySmooth") && mw.isChrome() &&  !mw.isMobileDevice()) );
		} );
		return drmSources;
	}

	function hasDrmSources(drmSources){
		return drmSources.length > 0;
	}

	function registerDashPlayer(){
		// Add multidrm player:
		$( mw ).bind( 'EmbedPlayerUpdateMediaPlayers' , function ( event , mediaPlayers ) {
			mw.log("Register DASH player and extensions");
			var multiDRMProtocols = ['application/dash+xml'];
			//On chrome add smooth stream mimetype support
			if ( mw.isChrome() &&  !mw.isMobileDevice()) {
				multiDRMProtocols.push( "video/ism" );
				multiDRMProtocols.push( "video/playreadySmooth" );
			}
			var multiDRMPlayer = new mw.MediaPlayer( 'multidrm', multiDRMProtocols, 'MultiDRM' );
			mediaPlayers.addPlayer( multiDRMPlayer );
			// add
			$.each( multiDRMProtocols, function ( inx, mimeType ) {
				if ( mediaPlayers.defaultPlayers[mimeType] ) {
					mediaPlayers.defaultPlayers[mimeType].unshift( 'MultiDRM' );
					return true;
				}
				mediaPlayers.defaultPlayers[mimeType] = ['MultiDRM'];
			} );
		} );
	}

	if (!Array.prototype.filter) {
		Array.prototype.filter = function(fun/*, thisArg*/) {
			'use strict';

			if (this === void 0 || this === null) {
				throw new TypeError();
			}

			var t = Object(this);
			var len = t.length >>> 0;
			if (typeof fun !== 'function') {
				throw new TypeError();
			}

			var res = [];
			var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
			for (var i = 0; i < len; i++) {
				if (i in t) {
					var val = t[i];

					// NOTE: Technically this should Object.defineProperty at
					//       the next index, as push can be affected by
					//       properties on Object.prototype and Array.prototype.
					//       But that method's new, and collisions should be
					//       rare, so use the more-compatible alternative.
					if (fun.call(thisArg, val, i, t)) {
						res.push(val);
					}
				}
			}

			return res;
		};
	}

	function getDefaultDrmConfig(partnerId){
		var defaultConfig = {
			"drm": "auto",
			"customData": {
				"userId": partnerId ,
				"sessionId": "castlab-session",
				"merchant": "kaltura"
			},
			"sendCustomData": false,
			"generatePSSH": false,
			"authenticationToken": null ,
			"widevineLicenseServerURL": null,
			"playReadyLicenseServerURL": null,
			"accessLicenseServerURL": null,
			"flashFile": mw.getConfig("EmbedPlayer.dashAsUrl") || mw.getMwEmbedPath() + "node_modules/mwEmbed-Dash-Everywhere/dashas/dashas.swf",
			"silverlightFile": mw.getConfig("EmbedPlayer.dashCsUrl") || mw.getMwEmbedPath() + "node_modules/mwEmbed-Dash-Everywhere/dashcs/dashcs.xap",
			"techs": mw.isFirefox()? ["dashcs"] : ["dashjs", "dashcs"] ,
			"debug": false
		};
		return defaultConfig;
	}
} )( window.mediaWiki, window.jQuery );<commit_msg>Fix wrong method call in udrm loader<commit_after>/**
 * MultiDRM loader
 */
( function( mw, $ ) {
	"use strict";
	var mseSupported = (window['MediaSource'] || window['WebKitMediaSource']);
	//Load 3rd party plugins if DRM sources are available
	mw.addKalturaConfCheck( function( embedPlayer, callback ){
		if( embedPlayer.isPluginEnabled( 'multiDrm' ) ) {
			var drmConfig = setEmbedPlayerConfig(embedPlayer);
			if ((mseSupported && !mw.isFirefox()) || drmConfig.forceDASH) {
				mw.log("Media Source Extensions supported on this browser");
				registerDashPlayer();
				//Get multiDRM supported sources
				var allSources = embedPlayer.getSources();
				var drmSources = getMultiDrmSupportedSources(allSources);
				//If there are supported medias load the playback library
				if ( hasDrmSources(drmSources) ) {
					mw.log("Media sources found, loading DASH player");
					var clDashPlayerUrl = embedPlayer.getKalturaConfig( "multiDrm", "clDashPlayerUrl" ) || mw.getMwEmbedPath() + "node_modules/mwEmbed-Dash-Everywhere/video.js";
					var dashJsUrl = embedPlayer.getKalturaConfig( "multiDrm", "dashJsUrl" ) || mw.getMwEmbedPath() + "node_modules/mwEmbed-Dash-Everywhere/cldasheverywhere.min.js";
					if (clDashPlayerUrl && dashJsUrl) {
						$.getScript( clDashPlayerUrl)
							.then(function(){return $.getScript( dashJsUrl)})
							.done(function(){
								mw.log("DASH player loaded");
								//Set reference for DASH playback engine
								mw.dash = {
									player: videojs
								};
								callback();
							})
							.fail(function( ) {
								mw.log("Error::Playback engine couldn't be found");
								callback();
							});
					} else {
						mw.log("Playback engine couldn't be found, not loading DASH player");
						callback();
					}
				} else {
					mw.log("No media sources found, not loading DASH player");
					callback();
				}
			} else {
				mw.log("Media Source Extensions not supported on this browser");
				callback();
			}
		} else {
			callback();
		}
	});

	function setEmbedPlayerConfig(embedPlayer){
		//Get user configuration
		var drmUserConfig = embedPlayer.getKalturaConfig("multiDrm");
		//Get default config
		var drmConfig = getDefaultDrmConfig(embedPlayer.kpartnerid);
		//Deep extend custom config
		$.extend(true, drmConfig, drmUserConfig);
		embedPlayer.setKalturaConfig("multiDrm", drmConfig);
		return drmConfig;
	}

	function getMultiDrmSupportedSources(sources){
		var drmSources = sources.filter( function ( source ) {
			return ( ( source.mimeType === "application/dash+xml" ) ||
			( (source.mimeType === "video/ism" || source.mimeType === "video/playreadySmooth") && mw.isChrome() &&  !mw.isMobileDevice()) );
		} );
		return drmSources;
	}

	function hasDrmSources(drmSources){
		return drmSources.length > 0;
	}

	function registerDashPlayer(){
		// Add multidrm player:
		$( mw ).bind( 'EmbedPlayerUpdateMediaPlayers' , function ( event , mediaPlayers ) {
			mw.log("Register DASH player and extensions");
			var multiDRMProtocols = ['application/dash+xml'];
			//On chrome add smooth stream mimetype support
			if ( mw.isChrome() &&  !mw.isMobileDevice()) {
				multiDRMProtocols.push( "video/ism" );
				multiDRMProtocols.push( "video/playreadySmooth" );
			}
			var multiDRMPlayer = new mw.MediaPlayer( 'multidrm', multiDRMProtocols, 'MultiDRM' );
			mediaPlayers.addPlayer( multiDRMPlayer );
			// add
			$.each( multiDRMProtocols, function ( inx, mimeType ) {
				if ( mediaPlayers.defaultPlayers[mimeType] ) {
					mediaPlayers.defaultPlayers[mimeType].unshift( 'MultiDRM' );
					return true;
				}
				mediaPlayers.defaultPlayers[mimeType] = ['MultiDRM'];
			} );
		} );
	}

	if (!Array.prototype.filter) {
		Array.prototype.filter = function(fun/*, thisArg*/) {
			'use strict';

			if (this === void 0 || this === null) {
				throw new TypeError();
			}

			var t = Object(this);
			var len = t.length >>> 0;
			if (typeof fun !== 'function') {
				throw new TypeError();
			}

			var res = [];
			var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
			for (var i = 0; i < len; i++) {
				if (i in t) {
					var val = t[i];

					// NOTE: Technically this should Object.defineProperty at
					//       the next index, as push can be affected by
					//       properties on Object.prototype and Array.prototype.
					//       But that method's new, and collisions should be
					//       rare, so use the more-compatible alternative.
					if (fun.call(thisArg, val, i, t)) {
						res.push(val);
					}
				}
			}

			return res;
		};
	}

	function getDefaultDrmConfig(partnerId){
		var defaultConfig = {
			"drm": "auto",
			"customData": {
				"userId": partnerId ,
				"sessionId": "castlab-session",
				"merchant": "kaltura"
			},
			"sendCustomData": false,
			"generatePSSH": false,
			"authenticationToken": null ,
			"widevineLicenseServerURL": null,
			"playReadyLicenseServerURL": null,
			"accessLicenseServerURL": null,
			"flashFile": mw.getConfig("EmbedPlayer.dashAsUrl") || mw.getMwEmbedPath() + "node_modules/mwEmbed-Dash-Everywhere/dashas/dashas.swf",
			"silverlightFile": mw.getConfig("EmbedPlayer.dashCsUrl") || mw.getMwEmbedPath() + "node_modules/mwEmbed-Dash-Everywhere/dashcs/dashcs.xap",
			"techs": mw.isFirefox()? ["dashcs"] : ["dashjs", "dashcs"] ,
			"debug": false
		};
		return defaultConfig;
	}
} )( window.mediaWiki, window.jQuery );<|endoftext|>