<commit_before>/**
 * scroll.js
 * normalizes the dom scroll api for human consumption
 * @author Jesse Baird <jebaird.com>
 *
 * licensed under MIT https://github.com/jebaird/scrolljs/license.txt
 *
 */
(function() {

	var jebaird = window.jebaird || ( window.jebaird = {} );

	var clone = (function() {
			return function( obj ) {
				Clone.prototype = obj;
				return new Clone()
			};
			function Clone(){}
		}());
	var scroll = (function() {

		var scroll = function( element ) {
			return new scroll.prototype.init( element );
		};
		/*@static */
		scroll._props = {
			//vertical
			'vert': {
				/*
				 * default to offsetHeight / width over client height because it include padding and border
				 * seems to be the more common case
				 */
				offset: 'offsetHeight',
				//where we are scrolling
				scroll: 'scrollTop',
				//the dimensions of the scroll content
				dim: 'scrollHeight'
			},
			//horizontal
			'hoz': {
				offset: 'offsetWidth',
				scroll: 'scrollLeft',
				dim: 'scrollWidth'
			}
		};

		scroll.prototype = {
			/*
			 * @param {object} element a dom element to create the instnace
			 */
			init: function( element ) {
				if( !element ){
					throw new Error('jebarid.scroll element can\'t be null')
				}
				this.element = element;
				//props for this instance
				this._props = clone( scroll._props );
			},

			/*
			 * can the element be scrolled vertical?
			 * @return {boolean}
			 */
			scrollableVert: function() {
				return this._scrollable( 'vert' );
			},
			/*
			 * can the element be scrolled horizontal?
			 * @return {boolean}
			 */
			scrollableHoz: function() {
				return this._scrollable( 'hoz' );
			},
			/*
			 * can the element be scrolled vertically or horizontally?
			 * @return { boolean }
			 */
			scrollable: function() {
				return ( this.scrollableVert() || this.scrollableHoz() ) ? true : false;
			},
			/*
			 * @private
			 * @param { string } orientation vert or hoz
			 * @return { boolean }
			 */
			_scrollable: function( orientation ){
				var element = this.element,
					props = this._props[ orientation ];
				return element[ props.dim ] > element[ props.offset ];
			},
			/*
			 * returns the number of vertical pages
			 * @return {int}
			 */
			pageCountVert: function() {
				return this._pageCount( 'vert' );
			},
			/*
			 * returns the number of pages horizontally
			 * @return {int}
			 */
			pageCountHoz: function() {

				return this._pageCount( 'hoz' );
			},
			/*
			 * returns the number of "pages" that is element has
			 * pages are the number of viewports the element has to its props.scroll
			 * say you have an element that has a height of 100px and a scrollHeight of 300
			 * this method would return 3
			 *
			 * if the element isnt scrollable it returns 1
			 *
			 * @param {string} orientation vert / hoz
			 * @return {int}
			 *
			 */
			_pageCount: function( orientation ){
				var dim = this._props[ orientation ].dim;
				return Math.ceil( this.element[ dim ] / this.viewPort()[ orientation ] );
			},
			/*
			 * returns the current vertical page
			 * @return {int}
			 */
			pageCurrentVert: function(){
				return this._pageCurrent( 'vert' );
			},
			/*
			 * returns the current horizontal page
			 * @return {int}
			 */
			pageCurrentHoz: function(){
				return this._pageCurrent( 'horz' );
			},
			/*
			 * returns the current page that the scroll content is on
			 *
			 * @param {string} orientation vert / hoz
			 * @return {int}
			 *
			 */
			_pageCurrent: function( orientation ){
				var element = this.element,
					dim = this._props[ orientation ].dim,
					scroll = this._props[ orientation ].scroll;

				return  Math.ceil( this.element[ scroll ]  / this.viewPort()[ orientation ] ) + 1;
			},
			/*
			 * move the scroll position up one viewport
			 *
			 */
			pageUp: function() {
				this.scrollVert( -this.viewPort()[ 'vert' ] );
			},
			/*
			 * move the scroll position down one viewport
			 *
			 */
			pageDown: function() {
				this.scrollVert( this.viewPort()[ 'vert' ] );
			},
			/*
			 * move the scroll position right one viewport
			 *
			 */
			pageRight: function() {
				this._scroll( this.viewPort()[ 'hoz' ], 'hoz' );
			},
			/*
			 * move the scroll position left one viewport
			 *
			 */
			pageLeft: function() {
				this._scroll( -this.viewPort()[ 'hoz' ], 'hoz' );
			},
			/*
			 * @param {int} offset the number of px to add to the scroll position
			 * @return {mixed} if offset is undefined returns the scroll Position
			 */
			scrollVert: function( offset ) {
				return this._scroll( offset, 'vert' );
			},
			/*
			 * @param {int} offset the number of px to add to the scroll position
			 * @return {mixed} if offset is undefined returns the scroll Position
			 */
			scrollHoz: function( offset ) {
				return this._scroll( offset, 'hoz' );
			},
			/*
			 * @param {int} offset the number of px to add to the scroll position
			 * @param {string} orientation vert / hoz
			 * @return {mixed} if offset is undefined returns the scroll Position
			 */
			_scroll: function( offset, orientation ){

				var element = this.element,
					props = this._props[ orientation ]

				if( offset === undefined ) {
					///return
					return element[ props.scroll ];
				}

					element[ props.scroll ] += offset;

			},
			/*
			 * @param {object} compareElement a dom element
			 * @return {float}
			 */
			pixelRatioVert: function( compareElement ) {
				return this._pixelRatio( compareElement, 'vert' );
			},
			/*
			 * @param {object} compareElement a dom element
			 * @return {float}
			 */
			pixelRatioHoz: function( compareElement ) {
				return this._pixelRatio( compareElement, 'hoz' );
			},
			/*
			 * this method is very useful for building custom scrollbars
			 * it will figure out the number of px the compareElement will need to move when this.element.scrollTop/left
			 * changes.
			 *
			 * @param {object} compareElement a dom elemetn to compare its dimensions with this.element
			 * @param {string} orientation vert / hoz
			 * @return {float}
			 */
			_pixelRatio: function( compareElement, orientation ) {
				var viewPort = this.viewPort()[ orientation ],
					props = this._props[ orientation ];

				return (
					( this.element[ props.dim ] - viewPort ) / ( viewPort - compareElement[ props.offset ] )
				);

			},
			/*
			 * get the the clientHeight / Width or as we call it the view port
			 * @return {object} { vert: 12, hoz: 100 }
			 */
			viewPort: function() {
				var element = this.element,
					props = this._props;
				return {
					vert: element[ props.vert.offset ],
					hoz: element[ props.hoz.offset ],
				};
			},
			/*
			 * figure out the direction the element is scrolling, useful for use in event handlers
			 * @return {object} an object that contains vert and hoz, 1 for scrolling down / right, -1 for up / left
			 * 		and 0 for no change
			 */
			direction: function(){
				var element = this.element,
					props = this._props,
					types = [
						'vert',
						'hoz'
					],
					i = 1,
					//first time coll?
					prev = this._scrollCords || {
						'hoz': element.scrollLeft,
						'vert': element.scrollTop
					},
					// 0, 0 no change
					ret = {
						'hoz': 0,
						'vert': 0
					};

					while( i-- ){
						var type = types[ i ],
							currentScroll = element[ props[ type ].scroll ],
							prevScroll = prev[ type ];

							//scrolling down / right
						if( currentScroll > prevScroll ){
							ret[ type ] = 1;
							//scrolling up/left
						}else if( currentScroll < prevScroll ){
							ret[ type ] = -1
						}
					}

					this._scrollCords = {
						'hoz': element.scrollLeft,
						'vert': element.scrollTop
					};

					return ret;
			}

		}

		scroll.prototype.init.prototype = scroll.prototype;

		return ( jebaird.scroll = scroll );

	})();

})();<commit_msg>updated docs<commit_after>/**
 * scroll.js
 * normalizes the dom scroll api for human consumption
 * useful for building widgets, pages and apps that depend on the scroll api
 * @author Jesse Baird <jebaird.com>
 *
 * licensed under MIT https://github.com/jebaird/scrolljs/license.txt
 *
 */
(function() {

	var jebaird = window.jebaird || ( window.jebaird = {} );

	var clone = (function() {
			return function( obj ) {
				Clone.prototype = obj;
				return new Clone()
			};
			function Clone(){}
		}());

	var scroll = (function() {

		var scroll = function( element ) {
			return new scroll.prototype.init( element );
		};
		/*@static */
		scroll._props = {
			//vertical
			'vert': {
				/*
				 * default to offsetHeight / width over client height because it include padding and border
				 * seems to be the more common case
				 */
				offset: 'offsetHeight',
				//where we are scrolling
				scroll: 'scrollTop',
				//the dimensions of the scroll content
				dim: 'scrollHeight'
			},
			//horizontal
			'hoz': {
				offset: 'offsetWidth',
				scroll: 'scrollLeft',
				dim: 'scrollWidth'
			}
		};

		scroll.prototype = {
			/*
			 * @param {object} element a dom element to create the instnace
			 */
			init: function( element ) {
				if( !element ){
					throw new Error('jebarid.scroll element can\'t be null');
				}
				this.element = element;
				//props for this instance
				this._props = clone( scroll._props );
			},

			/*
			 * can the element be scrolled vertical?
			 * @return {boolean}
			 */
			scrollableVert: function() {
				return this._scrollable( 'vert' );
			},
			/*
			 * can the element be scrolled horizontal?
			 * @return {boolean}
			 */
			scrollableHoz: function() {
				return this._scrollable( 'hoz' );
			},
			/*
			 * can the element be scrolled vertically or horizontally?
			 * @return { boolean }
			 */
			scrollable: function() {
				return ( this.scrollableVert() || this.scrollableHoz() ) ? true : false;
			},
			/*
			 * @private
			 * @param { string } orientation vert or hoz
			 * @return { boolean }
			 */
			_scrollable: function( orientation ){
				var element = this.element,
					props = this._props[ orientation ];
				return element[ props.dim ] > element[ props.offset ];
			},
			/*
			 * returns the number of vertical pages
			 * @return {int}
			 */
			pageCountVert: function() {
				return this._pageCount( 'vert' );
			},
			/*
			 * returns the number of pages horizontally
			 * @return {int}
			 */
			pageCountHoz: function() {

				return this._pageCount( 'hoz' );
			},
			/*
			 * returns the number of "pages" that is element has
			 * pages are the number of viewports the element has to its props.scroll
			 * say you have an element that has a height of 100px and a scrollHeight of 300
			 * this method would return 3
			 *
			 * if the element isnt scrollable it returns 1
			 *
			 * @param {string} orientation vert / hoz
			 * @return {int}
			 *
			 */
			_pageCount: function( orientation ){
				var dim = this._props[ orientation ].dim;
				return Math.ceil( this.element[ dim ] / this.viewPort()[ orientation ] );
			},
			/*
			 * returns the current vertical page
			 * @return {int}
			 */
			pageCurrentVert: function(){
				return this._pageCurrent( 'vert' );
			},
			/*
			 * returns the current horizontal page
			 * @return {int}
			 */
			pageCurrentHoz: function(){
				return this._pageCurrent( 'horz' );
			},
			/*
			 * returns the current page that the scroll content is on
			 *
			 * @param {string} orientation vert / hoz
			 * @return {int}
			 *
			 */
			_pageCurrent: function( orientation ){
				var element = this.element,
					dim = this._props[ orientation ].dim,
					scroll = this._props[ orientation ].scroll;

				return  Math.ceil( this.element[ scroll ]  / this.viewPort()[ orientation ] ) + 1;
			},
			/*
			 * move the scroll position up one viewport
			 *
			 */
			pageUp: function() {
				this.scrollVert( -this.viewPort()[ 'vert' ] );
			},
			/*
			 * move the scroll position down one viewport
			 *
			 */
			pageDown: function() {
				this.scrollVert( this.viewPort()[ 'vert' ] );
			},
			/*
			 * move the scroll position right one viewport
			 *
			 */
			pageRight: function() {
				this._scroll( this.viewPort()[ 'hoz' ], 'hoz' );
			},
			/*
			 * move the scroll position left one viewport
			 *
			 */
			pageLeft: function() {
				this._scroll( -this.viewPort()[ 'hoz' ], 'hoz' );
			},
			/*
			 * @param {int} offset the number of px to add to the scroll position
			 * @return {mixed} if offset is undefined returns the scroll Position
			 */
			scrollVert: function( offset ) {
				return this._scroll( offset, 'vert' );
			},
			/*
			 * @param {int} offset the number of px to add to the scroll position
			 * @return {mixed} if offset is undefined returns the scroll Position
			 */
			scrollHoz: function( offset ) {
				return this._scroll( offset, 'hoz' );
			},
			/*
			 * @param {int} offset the number of px to add to the scroll position
			 * @param {string} orientation vert / hoz
			 * @return {mixed} if offset is undefined returns the scroll Position
			 */
			_scroll: function( offset, orientation ){

				var element = this.element,
					props = this._props[ orientation ]

				if( offset === undefined ) {
					///return
					return element[ props.scroll ];
				}

					element[ props.scroll ] += offset;

			},
			/*
			 * @param {object} compareElement a dom element
			 * @return {float}
			 */
			pixelRatioVert: function( compareElement ) {
				return this._pixelRatio( compareElement, 'vert' );
			},
			/*
			 * @param {object} compareElement a dom element
			 * @return {float}
			 */
			pixelRatioHoz: function( compareElement ) {
				return this._pixelRatio( compareElement, 'hoz' );
			},
			/*
			 * this method is very useful for building custom scrollbars
			 * it will figure out the number of px the compareElement will need to move when this.element.scrollTop/left
			 * changes.
			 *
			 * @param {object} compareElement a dom elemetn to compare its dimensions with this.element
			 * @param {string} orientation vert / hoz
			 * @return {float}
			 */
			_pixelRatio: function( compareElement, orientation ) {
				var viewPort = this.viewPort()[ orientation ],
					props = this._props[ orientation ];

				return (
					( this.element[ props.dim ] - viewPort ) / ( viewPort - compareElement[ props.offset ] )
				);

			},
			/*
			 * get the the clientHeight / Width or as we call it the view port
			 * @return {object} { vert: 12, hoz: 100 }
			 */
			viewPort: function() {
				var element = this.element,
					props = this._props;
				return {
					vert: element[ props.vert.offset ],
					hoz: element[ props.hoz.offset ],
				};
			},
			/*
			 * figure out the direction the element is scrolling, useful for use in event handlers
			 * @return {object} an object that contains vert and hoz, 1 for scrolling down / right, -1 for up / left
			 * 		and 0 for no change
			 */
			direction: function(){
				var element = this.element,
					props = this._props,
					types = [
						'vert',
						'hoz'
					],
					i = 1,
					//first time coll?
					prev = this._scrollCords || {
						'hoz': element.scrollLeft,
						'vert': element.scrollTop
					},
					// 0, 0 no change
					ret = {
						'hoz': 0,
						'vert': 0
					};

					while( i-- ){
						var type = types[ i ],
							currentScroll = element[ props[ type ].scroll ],
							prevScroll = prev[ type ];

							//scrolling down / right
						if( currentScroll > prevScroll ){
							ret[ type ] = 1;
							//scrolling up/left
						}else if( currentScroll < prevScroll ){
							ret[ type ] = -1
						}
					}

					this._scrollCords = {
						'hoz': element.scrollLeft,
						'vert': element.scrollTop
					};

					return ret;
			}

		}

		scroll.prototype.init.prototype = scroll.prototype;

		return ( jebaird.scroll = scroll );

	})();

})();<|endoftext|>


<commit_before>---
---
require([
  '/public/js/jquery-2.1.1.min.js',
  '/public/js/lunr.min.js',
], function (_, lunr) {

  var docs =
  [
  {% for page in site.pages limit:100 %}
      {% unless page.exclude_from_search %}
        {% include page.json %},
      {% endunless %}
  {% endfor %}
  ];
  // init lunr
  var idx = lunr(function () {
    this.field('title', {boost: 10});
    this.field('content');
    this.ref('id');
  });

  // add each document to be index
  for(var index in docs) {
    idx.add(docs[index]);
  }

  var originalContent = {
    selector : '.page',
  };
  originalContent.content = $(originalContent.selector).html();
  originalContent.reset = function() {
    $(originalContent.selector).html(originalContent.content);
  };

  var debounce = function (fn) {
    var timeout;
    return function () {
      var args = Array.prototype.slice.call(arguments),
          ctx = this;

      clearTimeout(timeout);
      timeout = setTimeout(function () {
        fn.apply(ctx, args);
      }, 100);
    };
  };

  // Run a query as the user types
  $('#search').bind('input', debounce(function (e) {
    var query = $(this).val();
    if (query < 1) {
      originalContent.reset();
      return;
    }
    render( prepare( query ) );
  }))

  // Load the top result if the user hits enter
  $('.search-form').bind('submit', function(e) {
    window.location.href = idx.latestEntries[0].id;
    e.preventDefault();
  });

  function prepare( query ) {
    var entries = idx.search(query).map(function (result) {
      return docs.filter(function (q) { return q.id === result.ref })[0]
    })
    idx.latestEntries = entries;
    return entries;
  }

  function render( entries ) {
    var output = '';
    if (entries.length == 0) {
      output = '
Nothing found
';
    }
    for(var index in entries) {
      entry = entries[index];
      output += '
';
      output += '';
      output += '
';
      output += entry.title;
      output += '
';
      output += '';
      output += '
';
    }

    $(originalContent.selector).html( output );
  }

});
<commit_msg>Remove 100 page limit - suggestion from @lewisnyman<commit_after>---
---
require([
  '/public/js/jquery-2.1.1.min.js',
  '/public/js/lunr.min.js',
], function (_, lunr) {

  var docs =
  [
  {% for page in site.pages %}
      {% unless page.exclude_from_search %}
        {% include page.json %},
      {% endunless %}
  {% endfor %}
  ];
  // init lunr
  var idx = lunr(function () {
    this.field('title', {boost: 10});
    this.field('content');
    this.ref('id');
  });

  // add each document to be index
  for(var index in docs) {
    idx.add(docs[index]);
  }

  var originalContent = {
    selector : '.page',
  };
  originalContent.content = $(originalContent.selector).html();
  originalContent.reset = function() {
    $(originalContent.selector).html(originalContent.content);
  };

  var debounce = function (fn) {
    var timeout;
    return function () {
      var args = Array.prototype.slice.call(arguments),
          ctx = this;

      clearTimeout(timeout);
      timeout = setTimeout(function () {
        fn.apply(ctx, args);
      }, 100);
    };
  };

  // Run a query as the user types
  $('#search').bind('input', debounce(function (e) {
    var query = $(this).val();
    if (query < 1) {
      originalContent.reset();
      return;
    }
    render( prepare( query ) );
  }))

  // Load the top result if the user hits enter
  $('.search-form').bind('submit', function(e) {
    window.location.href = idx.latestEntries[0].id;
    e.preventDefault();
  });

  function prepare( query ) {
    var entries = idx.search(query).map(function (result) {
      return docs.filter(function (q) { return q.id === result.ref })[0]
    })
    idx.latestEntries = entries;
    return entries;
  }

  function render( entries ) {
    var output = '';
    if (entries.length == 0) {
      output = '
Nothing found
';
    }
    for(var index in entries) {
      entry = entries[index];
      output += '
';
      output += '';
      output += '
';
      output += entry.title;
      output += '
';
      output += '';
      output += '
';
    }

    $(originalContent.selector).html( output );
  }

});
<|endoftext|>


<commit_before>var path = require('path');
var webpack = require('webpack');

module.exports = {
  devtool: 'cheap-module-eval-source-map',
  entry: {
    app: [
      'webpack-hot-middleware/client',
      './docs/src/js/app.jsx',
      './docs/src/less/style.less'
    ],
    test: [
      'mocha!./test/components/index.js'
    ]
  },
  output: {
    path: path.join(__dirname, 'docs'),
    filename: 'js/[name].js',
    publicPath: '/'
  },
  externals: {'react': 'React', 'react-dom': 'ReactDOM'},
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],
  module: { loaders: [{
        test: /\.jsx?$/, loaders: ['babel'], include: [
        path.resolve(__dirname, 'src'),
        path.resolve(__dirname, 'test/components'),
        path.resolve(__dirname, 'docs'),
        path.resolve(__dirname, 'node_modules/refetch')
      ] },
      { test: /\.(css|less)$/, loader: 'style-loader!css-loader?localIdentName=[hash:base64:8]!less-loader' },
      { test: /\.(ttf|eot|woff|woff2|otf|svg)/, loader: 'file-loader?name=./font/[name].[ext]' },
      { test: /\.json$/, loader: 'file-loader?name=./json/[name].json' },
      { test: /\.(png|jpg|jpeg|gif)$/, loader: 'url-loader?limit=10000&name=./images/[name].[ext]' }
    ],
    preLoaders: [
      { test: /\.jsx?$/, loader: 'rctui-example-loader', include: [path.resolve(__dirname, 'docs')] }
    ]
  }
};
<commit_msg>recover webpack.config.dev.js to 9e98bd2ee29ea3bce4867c6f5a8ebe588b7051af<commit_after>var path = require('path');
var webpack = require('webpack');

module.exports = {
  devtool: 'cheap-module-eval-source-map',
  entry: [
    'webpack-hot-middleware/client',
    './docs/src/js/app.jsx',
    './docs/src/less/style.less'
  ],
  output: {
    path: path.join(__dirname, 'docs'),
    filename: 'js/app.js',
    publicPath: '/'
  },
  externals: {'react': 'React', 'react-dom': 'ReactDOM'},
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],
  module: {
    loaders: [
      { test: /\.jsx?$/, loaders: ['babel'], include: [
        path.resolve(__dirname, 'src'),
        path.resolve(__dirname, 'docs'),
        path.resolve(__dirname, 'node_modules/refetch')
      ] },
      { test: /\.(css|less)$/, loader: 'style-loader!css-loader?localIdentName=[hash:base64:8]!less-loader' },
      { test: /\.(ttf|eot|woff|woff2|otf|svg)/, loader: 'file-loader?name=./font/[name].[ext]' },
      { test: /\.json$/, loader: 'file-loader?name=./json/[name].json' },
      { test: /\.(png|jpg|jpeg|gif)$/, loader: 'url-loader?limit=10000&name=./images/[name].[ext]' }
    ],
    preLoaders: [
      { test: /\.jsx?$/, loader: 'rctui-example-loader', include: [path.resolve(__dirname, 'docs')] }
    ]
  }
};
<|endoftext|>


<commit_before>/** @license
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('shaka.ads.ServerSideAdManager');

goog.require('goog.asserts');
goog.require('shaka.ads.ServerSideAd');
goog.require('shaka.log');

/**
 * A class responsible for server-side ad interactions.
 */
shaka.ads.ServerSideAdManager = class {
  /**
   * @param {HTMLElement} adContainer
   * @param {HTMLMediaElement} video
   * @param {shaka.Player} player
   * @param {function(!shaka.util.FakeEvent)} onEvent
   */
  constructor(adContainer, video, player, onEvent) {
    /** @private {HTMLElement} */
    this.adContainer_ = adContainer;

    /** @private {HTMLMediaElement} */
    this.video_ = video;

    /** @private {shaka.Player} */
    // TODO: player should not be part of the ad manager,
    // since ad manager is part of player.
    this.player_ = player;

    /** @private {function(!shaka.util.FakeEvent)} */
    this.onEvent_ = onEvent;

    /**
     * Time to seek to after an ad if that ad was played as the result of
     * snapback.
     * @private {?number}
     */
    this.snapForwardTime_ = null;

    /** @private {shaka.ads.ServerSideAd} */
    this.ad_ = null;

    /** @private {?google.ima.dai.api.AdProgressData} */
    this.adProgressData_ = null;

    /** @private {string} */
    this.backupUrl_ = '';

    /** @private {?number} */
    this.startTime_ = null;

    /** @private {shaka.util.EventManager} */
    this.eventManager_ = new shaka.util.EventManager();

    /** @private {google.ima.dai.api.StreamManager} */
    this.streamManager_ = new google.ima.dai.api.StreamManager(this.video_);

    this.streamManager_.setClickElement(this.adContainer_);

    // Native HLS over Safari/iOS/iPadOS
    // This is a real EventTarget, but the compiler doesn't know that.
    // TODO: File a bug or send a PR to the compiler externs to fix this.
    const textTracks = /** @type {EventTarget} */(this.video_.textTracks);
    this.eventManager_.listen(textTracks, 'addtrack', (event) => {
      const track = event.track;
      if (track.kind == 'metadata') {
        track.mode = 'hidden';
        track.addEventListener('cuechange', () => {
          for (const cue of track.activeCues) {
            const metadata = {};
            metadata[cue.value.key] = cue.value.data;
            this.streamManager_.onTimedMetadata(metadata);
          }
        });
      }
    });

    // DASH managed by the player
    this.eventManager_.listen(
        this.player_, 'timelineregionadded', (event) => {
          const detail = event.detail;
          if (detail && detail.schemeIdUri == 'urn:google:dai:2018') {
            const type = detail.schemeIdUri;
            const data = detail.eventElement ?
                detail.eventElement.getAttribute('messageData') : null;
            const timestamp = detail.startTime;
            this.streamManager_.processMetadata(type, data, timestamp);
          }
        });

    // HLS managed by the player
    // TODO: There are not method to get the metadata in HLS

    // Events
    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.LOADED, (e) => {
          shaka.log.info('Ad SS Loaded');
          this.onLoaded_(
              /** @type {!google.ima.dai.api.StreamEvent} */ (e));
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.ERROR, () => {
          shaka.log.info('Ad SS Error');
          this.onError_();
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED, () => {
          shaka.log.info('Ad Break Started');
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.STARTED, (e) => {
          this.onAdStart_(/** @type {!google.ima.dai.api.StreamEvent} */ (e));
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED, () => {
          this.onAdBreakEnded_();
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.AD_PROGRESS, (e) => {
          this.onAdProgress_(
              /** @type {!google.ima.dai.api.StreamEvent} */ (e));
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.FIRST_QUARTILE, () => {
          shaka.log.info('Ad event: First Quartile');
          this.onEvent_(
              new shaka.util.FakeEvent(shaka.ads.AdManager.AD_FIRST_QUARTILE));
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.MIDPOINT, () => {
          shaka.log.info('Ad event: Midpoint');
          this.onEvent_(
              new shaka.util.FakeEvent(shaka.ads.AdManager.AD_MIDPOINT));
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.THIRD_QUARTILE, () => {
          shaka.log.info('Ad event: Third Quartile');
          this.onEvent_(
              new shaka.util.FakeEvent(shaka.ads.AdManager.AD_THIRD_QUARTILE));
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.COMPLETE, () => {
          shaka.log.info('Ad event: Complete');
          this.onEvent_(
              new shaka.util.FakeEvent(shaka.ads.AdManager.AD_COMPLETE));
          this.adContainer_.removeAttribute('ad-active');
          this.ad_ = null;
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.SKIPPED, () => {
          shaka.log.info('Ad event: Skipped');
          this.onEvent_(
              new shaka.util.FakeEvent(shaka.ads.AdManager.AD_SKIPPED));
        });
  }

  /**
   * @param {!google.ima.dai.api.StreamRequest} streamRequest
   * @param {string=} backupUrl
   * @param {?number=} startTime
   */
  streamRequest(streamRequest, backupUrl, startTime) {
    this.streamManager_.requestStream(streamRequest);
    this.backupUrl_ = backupUrl || '';
    this.startTime_ = startTime || null;
  }

  /**
   * @param {Object} adTagParameters
   */
  replaceAdTagParameters(adTagParameters) {
    this.streamManager_.replaceAdTagParameters(adTagParameters);
  }

  /**
   * Resets the stream manager and removes any continuous polling.
   */
  stop() {
    this.streamManager_.reset();
    this.backupUrl_ = '';
    this.snapForwardTime_ = null;
  }

  /**
   * Takes the current video time and snaps to the previous ad break if it
   * was not played.
   * @private
   */
  onLoadedEnd_() {
    if (this.player_.isLive()) {
      return;
    }
    const currentTime = this.video_.currentTime;
    if (currentTime == 0) {
      return;
    }

    this.streamManager_.streamTimeForContentTime(currentTime);
    const previousCuePoint =
        this.streamManager_.previousCuePointForStreamTime(currentTime);
    if (previousCuePoint && !previousCuePoint.played) {
      shaka.log.info('Seeking back to ' + previousCuePoint.start +
          ' and will return to ' + currentTime);
      this.snapForwardTime_ = currentTime;
      this.video_.currentTime = previousCuePoint.start;
    }
  }

  /**
   * @param {!google.ima.dai.api.StreamEvent} e
   * @private
   */
  onAdStart_(e) {
    goog.asserts.assert(this.streamManager_,
        'Should have a stream manager at this point!');

    const imaAd = e.getAd();
    this.ad_ = new shaka.ads.ServerSideAd(imaAd, this.video_);

    // Ad object and ad progress data come from two different IMA events.
    // It's a race, and we don't know, which one will fire first - the
    // event that contains an ad object (AD_STARTED) or the one that
    // contains ad progress info (AD_PROGRESS).
    // If the progress event fired first, we must've saved the progress
    // info and can now add it to the ad object.
    if (this.adProgressData_) {
      this.ad_.setProgressData(this.adProgressData_);
    }

    this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STARTED,
        {'ad': this.ad_}));
    this.adContainer_.setAttribute('ad-active', 'true');
    this.video_.pause();
  }

  /**
   * @private
   */
  onAdBreakEnded_() {
    this.adContainer_.removeAttribute('ad-active');
    const currentTime = this.video_.currentTime;
    if (this.snapForwardTime_ && this.snapForwardTime_ > currentTime) {
      this.video_.currentTime = this.snapForwardTime_;
      this.snapForwardTime_ = null;
    }
    this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STOPPED));
  }

  /**
   * @param {!google.ima.dai.api.StreamEvent} e
   * @private
   */
  onLoaded_(e) {
    const streamData = e.getStreamData();
    const url = streamData.url;
    this.player_.load(url, this.startTime_).then(() => {
      this.onLoadedEnd_();
    });
  }

  /**
   * @private
   */
  onError_() {
    if (!this.backupUrl_.length) {
      shaka.log.error('No backup url provided');
      // TODO: Throw a error up to the player if there was no backup url
      return;
    }
    this.player_.load(this.backupUrl_, this.startTime_);
  }

  /**
   * @param {!google.ima.dai.api.StreamEvent} e
   * @private
   */
  onAdProgress_(e) {
    const streamData = e.getStreamData();
    const adProgressData = streamData.adProgressData;
    this.adProgressData_ = adProgressData;
    if (this.ad_) {
      this.ad_.setProgressData(this.adProgressData_);
    }
  }
};
<commit_msg>Fix snapback feature on DAI streams.<commit_after>/** @license
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('shaka.ads.ServerSideAdManager');

goog.require('goog.asserts');
goog.require('shaka.ads.ServerSideAd');
goog.require('shaka.log');

/**
 * A class responsible for server-side ad interactions.
 */
shaka.ads.ServerSideAdManager = class {
  /**
   * @param {HTMLElement} adContainer
   * @param {HTMLMediaElement} video
   * @param {shaka.Player} player
   * @param {function(!shaka.util.FakeEvent)} onEvent
   */
  constructor(adContainer, video, player, onEvent) {
    /** @private {HTMLElement} */
    this.adContainer_ = adContainer;

    /** @private {HTMLMediaElement} */
    this.video_ = video;

    /** @private {shaka.Player} */
    // TODO: player should not be part of the ad manager,
    // since ad manager is part of player.
    this.player_ = player;

    /** @private {function(!shaka.util.FakeEvent)} */
    this.onEvent_ = onEvent;

    /**
     * Time to seek to after an ad if that ad was played as the result of
     * snapback.
     * @private {?number}
     */
    this.snapForwardTime_ = null;

    /** @private {shaka.ads.ServerSideAd} */
    this.ad_ = null;

    /** @private {?google.ima.dai.api.AdProgressData} */
    this.adProgressData_ = null;

    /** @private {string} */
    this.backupUrl_ = '';

    /** @private {?number} */
    this.startTime_ = null;

    /** @private {shaka.util.EventManager} */
    this.eventManager_ = new shaka.util.EventManager();

    /** @private {google.ima.dai.api.StreamManager} */
    this.streamManager_ = new google.ima.dai.api.StreamManager(this.video_);

    this.streamManager_.setClickElement(this.adContainer_);

    // Native HLS over Safari/iOS/iPadOS
    // This is a real EventTarget, but the compiler doesn't know that.
    // TODO: File a bug or send a PR to the compiler externs to fix this.
    const textTracks = /** @type {EventTarget} */(this.video_.textTracks);
    this.eventManager_.listen(textTracks, 'addtrack', (event) => {
      const track = event.track;
      if (track.kind == 'metadata') {
        track.mode = 'hidden';
        track.addEventListener('cuechange', () => {
          for (const cue of track.activeCues) {
            const metadata = {};
            metadata[cue.value.key] = cue.value.data;
            this.streamManager_.onTimedMetadata(metadata);
          }
        });
      }
    });

    // DASH managed by the player
    this.eventManager_.listen(
        this.player_, 'timelineregionadded', (event) => {
          const detail = event.detail;
          if (detail && detail.schemeIdUri == 'urn:google:dai:2018') {
            const type = detail.schemeIdUri;
            const data = detail.eventElement ?
                detail.eventElement.getAttribute('messageData') : null;
            const timestamp = detail.startTime;
            this.streamManager_.processMetadata(type, data, timestamp);
          }
        });

    // HLS managed by the player
    // TODO: There are not method to get the metadata in HLS

    // Events
    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.LOADED, (e) => {
          shaka.log.info('Ad SS Loaded');
          this.onLoaded_(
              /** @type {!google.ima.dai.api.StreamEvent} */ (e));
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.ERROR, () => {
          shaka.log.info('Ad SS Error');
          this.onError_();
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED, () => {
          shaka.log.info('Ad Break Started');
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.STARTED, (e) => {
          this.onAdStart_(/** @type {!google.ima.dai.api.StreamEvent} */ (e));
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED, () => {
          this.onAdBreakEnded_();
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.AD_PROGRESS, (e) => {
          this.onAdProgress_(
              /** @type {!google.ima.dai.api.StreamEvent} */ (e));
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.FIRST_QUARTILE, () => {
          shaka.log.info('Ad event: First Quartile');
          this.onEvent_(
              new shaka.util.FakeEvent(shaka.ads.AdManager.AD_FIRST_QUARTILE));
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.MIDPOINT, () => {
          shaka.log.info('Ad event: Midpoint');
          this.onEvent_(
              new shaka.util.FakeEvent(shaka.ads.AdManager.AD_MIDPOINT));
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.THIRD_QUARTILE, () => {
          shaka.log.info('Ad event: Third Quartile');
          this.onEvent_(
              new shaka.util.FakeEvent(shaka.ads.AdManager.AD_THIRD_QUARTILE));
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.COMPLETE, () => {
          shaka.log.info('Ad event: Complete');
          this.onEvent_(
              new shaka.util.FakeEvent(shaka.ads.AdManager.AD_COMPLETE));
          this.adContainer_.removeAttribute('ad-active');
          this.ad_ = null;
        });

    this.eventManager_.listen(this.streamManager_,
        google.ima.dai.api.StreamEvent.Type.SKIPPED, () => {
          shaka.log.info('Ad event: Skipped');
          this.onEvent_(
              new shaka.util.FakeEvent(shaka.ads.AdManager.AD_SKIPPED));
        });
  }

  /**
   * @param {!google.ima.dai.api.StreamRequest} streamRequest
   * @param {string=} backupUrl
   * @param {?number=} startTime
   */
  streamRequest(streamRequest, backupUrl, startTime) {
    this.streamManager_.requestStream(streamRequest);
    this.backupUrl_ = backupUrl || '';
    this.startTime_ = startTime || null;
  }

  /**
   * @param {Object} adTagParameters
   */
  replaceAdTagParameters(adTagParameters) {
    this.streamManager_.replaceAdTagParameters(adTagParameters);
  }

  /**
   * Resets the stream manager and removes any continuous polling.
   */
  stop() {
    this.streamManager_.reset();
    this.backupUrl_ = '';
    this.snapForwardTime_ = null;
  }

  /**
   * @private
   */
  onLoadedEnd_() {
    if (this.player_.isLive()) {
      return;
    }

    this.eventManager_.listen(this.video_, 'seeked', () => {
      this.checkForSnapback_();
    });
  }

  /**
   * If a seek jumped over the ad break, return to the start of the
   * ad break, then complete the seek after the ad played through.
   * @private
   */
  checkForSnapback_() {
    const currentTime = this.video_.currentTime;
    if (currentTime == 0) {
      return;
    }

    this.streamManager_.streamTimeForContentTime(currentTime);
    const previousCuePoint =
        this.streamManager_.previousCuePointForStreamTime(currentTime);
    // The cue point gets marked as 'played' as soon as the playhead hits it
    // (at the start of an ad), so when we come back to this method as a result
    // of seeking back to the user-selected time, the 'played' flag will be set.
    if (previousCuePoint && !previousCuePoint.played) {
      shaka.log.info('Seeking back to the start of the ad break at ' +
          previousCuePoint.start + ' and will return to ' + currentTime);
      this.snapForwardTime_ = currentTime;
      this.video_.currentTime = previousCuePoint.start;
    }
  }

  /**
   * @param {!google.ima.dai.api.StreamEvent} e
   * @private
   */
  onAdStart_(e) {
    goog.asserts.assert(this.streamManager_,
        'Should have a stream manager at this point!');

    const imaAd = e.getAd();
    this.ad_ = new shaka.ads.ServerSideAd(imaAd, this.video_);

    // Ad object and ad progress data come from two different IMA events.
    // It's a race, and we don't know, which one will fire first - the
    // event that contains an ad object (AD_STARTED) or the one that
    // contains ad progress info (AD_PROGRESS).
    // If the progress event fired first, we must've saved the progress
    // info and can now add it to the ad object.
    if (this.adProgressData_) {
      this.ad_.setProgressData(this.adProgressData_);
    }

    this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STARTED,
        {'ad': this.ad_}));
    this.adContainer_.setAttribute('ad-active', 'true');
    this.video_.pause();
  }

  /**
   * @private
   */
  onAdBreakEnded_() {
    this.adContainer_.removeAttribute('ad-active');
    const currentTime = this.video_.currentTime;
    // If the ad break was a result of snapping back (a user seeked over
    // an ad break and was returned to it), seek forward to the point,
    // originally chosen by the user.
    if (this.snapForwardTime_ && this.snapForwardTime_ > currentTime) {
      this.video_.currentTime = this.snapForwardTime_;
      this.snapForwardTime_ = null;
    }
  }

  /**
   * @param {!google.ima.dai.api.StreamEvent} e
   * @private
   */
  onLoaded_(e) {
    const streamData = e.getStreamData();
    const url = streamData.url;
    this.player_.load(url, this.startTime_).then(() => {
      this.onLoadedEnd_();
    });
  }

  /**
   * @private
   */
  onError_() {
    if (!this.backupUrl_.length) {
      shaka.log.error('No backup url provided');
      // TODO: Throw a error up to the player if there was no backup url
      return;
    }
    this.player_.load(this.backupUrl_, this.startTime_);
  }

  /**
   * @param {!google.ima.dai.api.StreamEvent} e
   * @private
   */
  onAdProgress_(e) {
    const streamData = e.getStreamData();
    const adProgressData = streamData.adProgressData;
    this.adProgressData_ = adProgressData;
    if (this.ad_) {
      this.ad_.setProgressData(this.adProgressData_);
    }
  }
};
<|endoftext|>


<commit_before>var Gaffa = require('gaffa'),
    crel = require('crel'),
    doc = require('doc-js'),
    statham = require('statham'),
    viewType = "select",
    cachedElement;

function Select(){}
Select = Gaffa.createSpec(Select, Gaffa.View);
Select.prototype.type = viewType;

Select.prototype.render = function(){
    var view = this,
        select,
        renderedElement = crel('span',
            select = crel('select')
        );

    renderedElement.className = 'select';

    doc.on(this.updateEventName || "change", select, function(event){
        var option,
            data;

        for(var i = 0; i < select.childNodes.length; i++){
            if(select.childNodes[i].value === select.value){
                option = select.childNodes[i];
            }
        }

        data = option && option.data || undefined;

        view.value.set(data);
    });

    this.renderedElement = renderedElement;
    this.selectElement = select;
};

Select.prototype.options = new Gaffa.Property({
    elements: [],
    update: function(view, value) {
        var property = this,
            element = view.selectElement;

        if(!element){
            return;
        }

        element.innerHTML = '';
        property.elements = [];

        if(!value){
            return;
        }

        if(view.showBlank.value)
        {
            element.appendChild(document.createElement("option"));
        }

        for(var key in value){
            var optionData = value[key];
            if(optionData !== undefined){
                var option = document.createElement('option');

                option.value = option.data = property.valueBinding ? gaffa.gedi.get(property.valueBinding, property.getPath(), {option: optionData}) : optionData;
                option.textContent = property.textBinding ? gaffa.gedi.get(property.textBinding, property.getPath(), {option: optionData}) : optionData;

                element.appendChild(option);
                property.elements.push(option);
            }
        }

        element.value = null;
        view.value.update(view, view.value.value);
    }
});

Select.prototype.value = new Gaffa.Property({
    update: function(view, value) {
        view.selectElement.value = value;
        for(var i = 0; i < view.options.elements.length; i++){
            if(view.options.elements[i].data === value){
                view.options.elements[i].selected = true;
                break;
            }
        }
    },
    sameAsPrevious: function(){
        var oldHash = this.getPreviousHash(),
            newHash = statham.stringify(this.value);

        this.setPreviousHash(newHash);

        return oldHash === newHash;
    }
});

Select.prototype.showBlank = new Gaffa.Property();

Select.prototype.required = new Gaffa.Property(function(view, value){
    if (value){
        view.renderedElement.setAttribute('required', 'required');
    }else{
        view.renderedElement.removeAttribute('required');
    }
});

Select.prototype.enabled = new Gaffa.Property({
    update: function(view, value){
        view.selectElement[value ? 'removeAttribute' : 'setAttribute']('disabled','disabled');
    },
    value: true
});

module.exports = Select;<commit_msg>Fixed object value'd options<commit_after>var Gaffa = require('gaffa'),
    crel = require('crel'),
    doc = require('doc-js'),
    statham = require('statham'),
    viewType = "select",
    cachedElement;

function Select(){}
Select = Gaffa.createSpec(Select, Gaffa.View);
Select.prototype.type = viewType;

Select.prototype.render = function(){
    var view = this,
        select,
        renderedElement = crel('span',
            select = crel('select')
        );

    renderedElement.className = 'select';

    doc.on(this.updateEventName || "change", select, function(event){
        var option,
            data;

        for(var i = 0; i < select.childNodes.length; i++){
            if(select.childNodes[i].value === select.value){
                option = select.childNodes[i];
            }
        }

        data = option && option.data || undefined;

        view.value.set(data);
    });

    this.renderedElement = renderedElement;
    this.selectElement = select;
};

Select.prototype.options = new Gaffa.Property({
    elements: [],
    update: function(view, value) {
        var property = this,
            element = view.selectElement;

        if(!element){
            return;
        }

        element.innerHTML = '';
        property.elements = [];

        if(!value){
            return;
        }

        if(view.showBlank.value)
        {
            element.appendChild(document.createElement("option"));
        }

        for(var key in value){
            var optionData = value[key];
            if(optionData !== undefined){
                var option = document.createElement('option');

                option.value = key;
                option.data = property.valueBinding ? gaffa.gedi.get(property.valueBinding, property.getPath(), {option: optionData}) : optionData;
                option.textContent = property.textBinding ? gaffa.gedi.get(property.textBinding, property.getPath(), {option: optionData}) : optionData;

                element.appendChild(option);
                property.elements.push(option);
            }
        }

        element.value = null;
        view.value.update(view, view.value.value);
    }
});

Select.prototype.value = new Gaffa.Property({
    update: function(view, value) {
        view.selectElement.value = value;
        for(var i = 0; i < view.options.elements.length; i++){
            if(view.options.elements[i].data === value){
                view.options.elements[i].selected = true;
                break;
            }
        }
    },
    sameAsPrevious: function(){
        var oldHash = this.getPreviousHash(),
            newHash = statham.stringify(this.value);

        this.setPreviousHash(newHash);

        return oldHash === newHash;
    }
});

Select.prototype.showBlank = new Gaffa.Property();

Select.prototype.required = new Gaffa.Property(function(view, value){
    if (value){
        view.renderedElement.setAttribute('required', 'required');
    }else{
        view.renderedElement.removeAttribute('required');
    }
});

Select.prototype.enabled = new Gaffa.Property({
    update: function(view, value){
        view.selectElement[value ? 'removeAttribute' : 'setAttribute']('disabled','disabled');
    },
    value: true
});

module.exports = Select;
<|endoftext|>


<commit_before>function setupRoutes($stateProvider, $urlRouterProvider) {
    $stateProvider
        .state('login', {
            url: '/login',
            template: '<mc-login></mc-login>'
        })
        .state('projects', {
            url: '/projects',
            abstract: true,
            template: '

',
        })
        .state('projects.list', {
            url: '/list',
            template: '<mc-projects></mc-projects>'
        })
        .state('projects.share', {
            url: '/share',
            template: '<mc-projects-share></mc-projects-share>'
        })
        .state('user', {
            url: '/user',
            abstract: true,
            template: '

'
        })
        .state('user.apikey', {
            url: '/apikey',
            template: '<mc-user-apikey></mc-user-apikey>'
        })
        .state('user.password', {
            url: '/password',
            template: '<mc-user-password></mc-user-password>'
        })
        .state('project', {
            url: '/project/:project_id',
            template: '<mc-project></mc-project>',
            resolve: {
                // Set the current project in the project service so all components
                // will resolve without having to worry if a promise has resolved.
                // The resolved object is ignored.
                _project: ["$stateParams", "projectsService", "project",
                    // Inject projects so that it resolves before looking up the project.
                    function($stateParams, projectsService, project) {
                        return projectsService.getProject($stateParams.project_id)
                            .then(function(proj) {
                                project.set(proj);
                                return proj;
                            });
                    }],

                _templates: ["processTemplates", "templates", "_project",
                    function(processTemplates, templates, project) {
                        var projectTemplates = processTemplates.templates(project.process_templates, project.id);
                        templates.set(projectTemplates);
                        return projectTemplates;
                    }]
            }
        })
        .state('project.home', {
            url: '/home',
            template: '<mc-project-home></mc-project-home>',
            // hack to update project view
            resolve: {
                // Set the current project in the project service so all components
                // will resolve without having to worry if a promise has resolved.
                // The resolved object is ignored.
                _project: ["$stateParams", "projectsService", "project",
                    // Inject projects so that it resolves before looking up the project.
                    function($stateParams, projectsService, project) {
                        return projectsService.getProject($stateParams.project_id)
                            .then(function(proj) {
                                project.set(proj);
                                return proj;
                            });
                    }]
            }
        })
        .state('project.search', {
            url: '/search/:query',
            template: '<mc-project-search></mc-project-search>'
        })
        .state('project.processes', {
            url: '/processes',
            template: '<mc-project-processes></mc-project-processes>'
        })
        .state('project.processes.create', {
            url: '/create-process/:template_id',
            template: '<mc-process-create></mc-process-create>',
            resolve: {
                _template: ['templates', 'template', '$stateParams', 'projectsService',
                    function(templates, template, $stateParams, projectsService) {
                        if ($stateParams.process_id) {
                            return projectsService.getProjectProcess($stateParams.project_id, $stateParams.process_id)
                                .then(function(process) {
                                    var t = templates.loadTemplateFromProcess($stateParams.template_id, process);
                                    template.set(t);
                                    return t;
                                });
                        } else {
                            var t = templates.getTemplate($stateParams.template_id);
                            template.set(t);
                            return t;
                        }
                    }]
            }
        })
        .state('project.processes.process', {
            url: '/process/:process_id',
            template: '<mc-process></mc-process>',
            resolve: {
                _process: ['process', 'projectsService', '$stateParams',
                    function(process, projectsService, $stateParams) {
                        return projectsService.getProjectProcess($stateParams.project_id, $stateParams.process_id)
                            .then(function(p) {
                                process.set(p);
                                return p;
                            });
                    }]
            }
        })
        .state('project.processes.edit', {
            url: '/edit/:process_id',
            template: '<mc-process-edit></mc-process-edit>',
            resolve: {
                _process: ['process', 'projectsService', '$stateParams', 'processEdit', 'templates',
                    function(process, projectsService, $stateParams, processEdit, templates) {
                        return projectsService.getProjectProcess($stateParams.project_id, $stateParams.process_id)
                            .then(function(proc) {
                                var t = templates.getTemplate(proc.process_name);
                                var p = processEdit.fillProcess(t, proc);
                                process.set(p);
                                return p;
                            });
                    }]
            }
        })
        .state('project.samples', {
            url: '/samples',
            template: '<mc-project-samples></mc-project-samples>'
        })
        .state('project.samples.sample', {
            url: '/sample/:sample_id',
            template: '<mc-sample></mc-sample>'
        })
        .state('project.files', {
            url: '/files',
            template: '<mc-file-tree2></mc-file-tree2>'
        })
        .state('project.files.file', {
            url: '/file/:file_id',
            template: '<mc-file></mc-file>'
        })
        .state('project.files.dir', {
            url: '/dir/:dir_id',
            template: '<mc-dir></mc-dir>'
        })
        .state('project.settings', {
            url: '/settings',
            template: '<mc-project-settings></mc-project-settings>'
        });

    $urlRouterProvider.otherwise('/login');
}
<commit_msg>Move create process under project.create.process state so that creating a process takes up the full screen.<commit_after>function setupRoutes($stateProvider, $urlRouterProvider) {
    $stateProvider
        .state('login', {
            url: '/login',
            template: '<mc-login></mc-login>'
        })
        .state('projects', {
            url: '/projects',
            abstract: true,
            template: '

'
        })
        .state('projects.list', {
            url: '/list',
            template: '<mc-projects></mc-projects>'
        })
        .state('projects.share', {
            url: '/share',
            template: '<mc-projects-share></mc-projects-share>'
        })
        .state('user', {
            url: '/user',
            abstract: true,
            template: '

'
        })
        .state('user.apikey', {
            url: '/apikey',
            template: '<mc-user-apikey></mc-user-apikey>'
        })
        .state('user.password', {
            url: '/password',
            template: '<mc-user-password></mc-user-password>'
        })
        .state('project', {
            url: '/project/:project_id',
            template: '<mc-project></mc-project>',
            resolve: {
                // Set the current project in the project service so all components
                // will resolve without having to worry if a promise has resolved.
                // The resolved object is ignored.
                _project: ["$stateParams", "projectsService", "project",
                    // Inject projects so that it resolves before looking up the project.
                    function($stateParams, projectsService, project) {
                        return projectsService.getProject($stateParams.project_id)
                            .then(function(proj) {
                                project.set(proj);
                                return proj;
                            });
                    }],

                _templates: ["processTemplates", "templates", "_project",
                    function(processTemplates, templates, project) {
                        var projectTemplates = processTemplates.templates(project.process_templates, project.id);
                        templates.set(projectTemplates);
                        return projectTemplates;
                    }]
            }
        })
        .state('project.home', {
            url: '/home',
            template: '<mc-project-home></mc-project-home>',
            // hack to update project view
            resolve: {
                // Set the current project in the project service so all components
                // will resolve without having to worry if a promise has resolved.
                // The resolved object is ignored.
                _project: ["$stateParams", "projectsService", "project",
                    // Inject projects so that it resolves before looking up the project.
                    function($stateParams, projectsService, project) {
                        return projectsService.getProject($stateParams.project_id)
                            .then(function(proj) {
                                project.set(proj);
                                return proj;
                            });
                    }]
            }
        })
        .state('project.search', {
            url: '/search/:query',
            template: '<mc-project-search></mc-project-search>'
        })
        .state('project.create', {
            url: '/create',
            abstract: true,
            template: '

'
        })
        .state('project.create.process', {
            url: '/process/:template_id/:process_id',
            template: '<mc-process-create></mc-process-create>',
            resolve: {
                _template: ['templates', 'template', '$stateParams', 'projectsService',
                    function(templates, template, $stateParams, projectsService) {
                        if ($stateParams.process_id) {
                            return projectsService.getProjectProcess($stateParams.project_id, $stateParams.process_id)
                                .then(function(process) {
                                    var t = templates.loadTemplateFromProcess($stateParams.template_id, process);
                                    template.set(t);
                                    return t;
                                });
                        } else {
                            var t = templates.getTemplate($stateParams.template_id);
                            template.set(t);
                            return t;
                        }
                    }]
            }
        })
        .state('project.processes', {
            url: '/processes',
            template: '<mc-project-processes></mc-project-processes>'
        })
        //.state('project.processes.create', {
        //    url: '/create-process/:template_id',
        //    template: '<mc-process-create></mc-process-create>',
        //    resolve: {
        //        _template: ['templates', 'template', '$stateParams', 'projectsService',
        //            function(templates, template, $stateParams, projectsService) {
        //                if ($stateParams.process_id) {
        //                    return projectsService.getProjectProcess($stateParams.project_id, $stateParams.process_id)
        //                        .then(function(process) {
        //                            var t = templates.loadTemplateFromProcess($stateParams.template_id, process);
        //                            template.set(t);
        //                            return t;
        //                        });
        //                } else {
        //                    var t = templates.getTemplate($stateParams.template_id);
        //                    template.set(t);
        //                    return t;
        //                }
        //            }]
        //    }
        //})
        .state('project.processes.process', {
            url: '/process/:process_id',
            template: '<mc-process></mc-process>',
            resolve: {
                _process: ['process', 'projectsService', '$stateParams',
                    function(process, projectsService, $stateParams) {
                        return projectsService.getProjectProcess($stateParams.project_id, $stateParams.process_id)
                            .then(function(p) {
                                process.set(p);
                                return p;
                            });
                    }]
            }
        })
        .state('project.processes.edit', {
            url: '/edit/:process_id',
            template: '<mc-process-edit></mc-process-edit>',
            resolve: {
                _process: ['process', 'projectsService', '$stateParams', 'processEdit', 'templates',
                    function(process, projectsService, $stateParams, processEdit, templates) {
                        return projectsService.getProjectProcess($stateParams.project_id, $stateParams.process_id)
                            .then(function(proc) {
                                var t = templates.getTemplate(proc.process_name);
                                var p = processEdit.fillProcess(t, proc);
                                process.set(p);
                                return p;
                            });
                    }]
            }
        })
        .state('project.samples', {
            url: '/samples',
            template: '<mc-project-samples></mc-project-samples>'
        })
        .state('project.samples.sample', {
            url: '/sample/:sample_id',
            template: '<mc-sample></mc-sample>'
        })
        .state('project.files', {
            url: '/files',
            template: '<mc-file-tree2></mc-file-tree2>'
        })
        .state('project.files.file', {
            url: '/file/:file_id',
            template: '<mc-file></mc-file>'
        })
        .state('project.files.dir', {
            url: '/dir/:dir_id',
            template: '<mc-dir></mc-dir>'
        })
        .state('project.settings', {
            url: '/settings',
            template: '<mc-project-settings></mc-project-settings>'
        });

    $urlRouterProvider.otherwise('/login');
}
<|endoftext|>


<commit_before>var PORT = 9529;
var http = require('http');
var qs = require('qs');

var TOKEN = 'rinesnow';

function checkSignature(params,token){
	//1.将token、timestamp、nonce三个参数进行字典序排序
	//2.将三个参数字符串拼接成一个字符串进行sha1加密
	//3.开发者获得加密后的字符串可与signature对比，标识该请求来源于微信  return true/false

	var key = [token, params.timestamp,params.nonce].sort().join('');//拼接
	var sha1 = require('crypto').createHash('sha1');
	sha1.update(key);
	return sha1.digest('hex')==params.signature;

}

var server = http.createServer(function(request,response){
	//解析URL中的Query部分，用qs模块（npm install qs）将Query解析成json
	var url=require('url');
	var query = url.parse(request.url).query;
	var params = qs.parse(query);

	console.log(params);
	console.log("token-->",TOKEN);

	if(checkSignature(params,TOKEN)){
		console.log("chenggong");
		response.end(params.echostr);

	}else{
		response.end('signature fail');
		return;
	}
		console.log(request.method+'/////');

	//若请求时GET，返回echostr用于通过服务器有效检验
	if(request.method=="GET"){
		response.end(params.echostr);
	}else{
	//否则是微信给开发者服务器的POST请求
		var postdata = "";
		console.log('else'+'/////');

		request.addListener("data",function(postchunk){
			postdata += postchunk;
			console.log(postchunk+'/////data');

		});
		console.log(postdata+'dfsdf');

		request.addListener("end",function(){
			console.log(postdata);
			response.end('success');
		});
	}

});
server.listen(PORT);
console.log("Server running at port:"+PORT+".");<commit_msg>add message.js debug<commit_after>var PORT = 9529;
var http = require('http');
var qs = require('qs');

var TOKEN = 'rinesnow';

function checkSignature(params,token){
	//1.将token、timestamp、nonce三个参数进行字典序排序
	//2.将三个参数字符串拼接成一个字符串进行sha1加密
	//3.开发者获得加密后的字符串可与signature对比，标识该请求来源于微信  return true/false

	var key = [token, params.timestamp,params.nonce].sort().join('');//拼接
	var sha1 = require('crypto').createHash('sha1');
	sha1.update(key);
	return sha1.digest('hex')==params.signature;

}

var server = http.createServer(function (request,response){
	//解析URL中的Query部分，用qs模块（npm install qs）将Query解析成json
	var url=require('url');
	var query = url.parse(request.url).query;
	var params = qs.parse(query);

	console.log(params);
	console.log("token-->",TOKEN);

	if(checkSignature(params,TOKEN)){
		console.log("chenggong");
		response.end(params.echostr);

	}else{
		response.end('signature fail');
		return;
	}
		console.log(request.method+'/////');

	//若请求时GET，返回echostr用于通过服务器有效检验
	if(request.method=="GET"){
		response.end(params.echostr);
	}else{
	//否则是微信给开发者服务器的POST请求
	 	//request.setEncoding('utf-8');
		var postdata = "";
		console.log('else'+'/////');

		request.addListener("data",function (postchunk){
			postdata += postchunk;
			console.log(postchunk+'/////data');
		});
		console.log(postdata+'dfsdf');

		request.addListener("end",function (){
			console.log(postdata);
			response.end('success');
		});
	}

});
server.listen(PORT);
console.log("Server running at port:"+PORT+".");<|endoftext|>


<commit_before>/**
 * A utility view for paging the display of many data items of varying sizes.
 * @class UIPagination
 */

import React, {PropTypes} from 'react';
import {findDOMNode} from 'react-dom';
import cx from 'classnames';
import omit from 'lodash.omit';

import UIView from '../UIView';
import UISegmentedControl from '../UISegmentedControl';
import UIArrowKeyNavigation from '../UIArrowKeyNavigation';
import noop from '../UIUtils/noop';

class Item extends UIView {
    static propTypes = {
        even: PropTypes.bool,
        data: PropTypes.object,
        index: PropTypes.number,
        itemToJSXConverterFunc: PropTypes.func,
    }

    static internal_keys = Object.keys(Item.propTypes)

    state = {
        data: this.maybeConvertToJSX(this.props.data),
    }

    __mounted = false

    componentWillReceiveProps(nextProps) {
        if (nextProps.data !== this.props.data) {
            this.setState({data: this.maybeConvertToJSX(nextProps.data)});
        }
    }

    maybeConvertToJSX(data) {
        return data instanceof Promise ? data : this.props.itemToJSXConverterFunc(data);
    }

    waitForContentIfNecessary() {
        if (this.state.data instanceof Promise) {
            this.state.data.then(function cautiouslySetItemData(promise, value) {
                if (this.__mounted && this.state.data === promise) {
                    this.setState({data: this.props.itemToJSXConverterFunc(value)});
                } // only replace if we're looking at the same promise, otherwise do nothing
            }.bind(this, this.state.data));
        }
    }

    componentDidMount() {
        this.__mounted = true;
        this.waitForContentIfNecessary();
    }

    componentWillUnmount() {
        this.__mounted = false;
    }

    componentDidUpdate() {
        this.waitForContentIfNecessary();
    }

    getClasses(extraClasses) {
        return cx({
            'ui-pagination-item': true,
            'ui-pagination-item-even': this.props.even,
            'ui-pagination-item-odd': !this.props.even,
            'ui-pagination-item-loading': this.state.data instanceof Promise,
        }) + (extraClasses ? ' ' + extraClasses : '');
    }

    render() {
        if (this.state.data instanceof Promise) {
            return (<div {...omit(this.props, Item.internal_keys)} className={this.getClasses()} />);
        }

        return React.cloneElement(this.state.data, {
            ...omit(this.props, Item.internal_keys),
            className: this.getClasses(this.state.data.props.className),
            'data-index': this.props.index,
        });
    }
}

export default class UIPagination extends UIView {
    static controls = {
        FIRST: 'FIRST',
        PREVIOUS: 'PREVIOUS',
        NEXT: 'NEXT',
        LAST: 'LAST',
    }

    static positions = {
        ABOVE: 'ABOVE',
        BELOW: 'BELOW',
        BOTH: 'BOTH',
    }

    static propTypes = {
        getItem: PropTypes.func,
        hidePagerIfNotNeeded: PropTypes.bool,
        identifier: PropTypes.string.isRequired,
        itemToJSXConverterFunc: PropTypes.func,
        jumpToFirstControlContent: PropTypes.node,
        jumpToLastControlContent: PropTypes.node,
        listWrapperProps: PropTypes.object,
        nextPageControlContent: PropTypes.node,

        numItemsPerPage: function validateNumItemsPerPage(props) {
            if (!Number.isInteger(props.numItemsPerPage)) {
                return new Error('`numItemsPerPage` must be an integer.');
            } else if (props.numItemsPerPage < 1) {
                return new Error('`numItemsPerPage` must be greater than zero.');
            }
        },

        numPageToggles: PropTypes.number,

        pagerPosition: function validatePagerPosition(props) {
            if (!Number.isInteger(props.pagerPosition)) {
                return new Error('`pagerPosition` must be an integer.');
            }

            const numberOfPages = Math.ceil(props.totalItems / props.numItemsPerPage);

            if (props.pagerPosition < 1 || props.pagerPosition > numberOfPages) {
                return new Error('`pagerPosition` must be between 1 and ' + numberOfPages + '.');
            }
        },

        position: PropTypes.oneOf(Object.keys(UIPagination.positions)),
        previousPageControlContent: PropTypes.node,
        showJumpToFirst: PropTypes.bool,
        showJumpToLast: PropTypes.bool,
        toggleWrapperProps: PropTypes.object,
        totalItems: PropTypes.number.isRequired,
    }

    static internal_keys = Object.keys(UIPagination.propTypes)

    static defaultProps = {
        getItem: noop,
        hidePagerIfNotNeeded: false,
        itemToJSXConverterFunc: data => data,
        jumpToFirstControlContent: '« First',
        jumpToLastControlContent: 'Last »',
        listWrapperProps: {},
        nextPageControlContent: 'Next ›',
        numItemsPerPage: 10,
        numPageToggles: 5,
        pagerPosition: 1,
        position: UIPagination.positions.ABOVE,
        previousPageControlContent: '‹ Previous',
        showJumpToFirst: true,
        showJumpToLast: true,
        toggleWrapperProps: {},
    }

    state = {
        currentPage: this.props.pagerPosition,
        numberOfPages: Math.ceil(this.props.totalItems / this.props.numItemsPerPage),
        totalItems: this.props.totalItems,
    }

    componentDidUpdate(prevProps, prevState) {
        if (prevState.currentPage !== this.state.currentPage) {
            findDOMNode(this.refs.item_0).focus();
        }
    }

    componentWillReceiveProps(nextProps) {
        const numberOfPages = Math.ceil(nextProps.totalItems / nextProps.numItemsPerPage);

        this.setState({
            currentPage:   nextProps.identifier === this.props.identifier
                         ? Math.min(this.state.currentPage, numberOfPages)
                         : 1,
            numberOfPages: numberOfPages,
            totalItems: nextProps.totalItems,
        });
    }

    currentPage = () => this.state.currentPage

    pageToIndex = i => {
        if (i < 0 || i >= this.state.totalItems) {
            return new Error(`Cannot page to invalid index ${i}.`);
        } else {
            this.setState({ currentPage: Math.ceil((i + 1) / this.props.numItemsPerPage) });
        }
    }

    createPageButtonOptions() {
        const options = [];
        const currentPage = this.state.currentPage;
        const numPageToggles = this.props.numPageToggles;
        const startPage = currentPage - ((currentPage - 1) % numPageToggles);
        const endPage = Math.min(startPage + numPageToggles - 1, this.state.numberOfPages);

        if (this.props.showJumpToFirst) {
            options.push({
                selected: false,
                content: this.props.jumpToFirstControlContent,
                value: UIPagination.controls.FIRST,
                disabled: this.state.currentPage === 1,
                className: 'ui-pagination-control ui-pagination-control-first',
            });
        }

        options.push({
            selected: false,
            content: this.props.previousPageControlContent,
            value: UIPagination.controls.PREVIOUS,
            disabled: this.state.currentPage === 1,
            className: 'ui-pagination-control ui-pagination-control-previous',
        });

        for (let i = startPage; i <= endPage; i++) {
            options.push({
                className: 'ui-pagination-control',
                'data-page-number': i,
                selected: i === this.state.currentPage,
                content: i,
                value: i,
            });
        }

        options.push({
            selected: false,
            content: this.props.nextPageControlContent,
            value: UIPagination.controls.NEXT,
            disabled: this.state.currentPage === this.state.numberOfPages,
            className: 'ui-pagination-control ui-pagination-control-next',
        });

        if (this.props.showJumpToLast) {
            options.push({
                selected: false,
                content: this.props.jumpToLastControlContent,
                value: UIPagination.controls.LAST,
                disabled: this.state.currentPage === this.state.numberOfPages,
                className: 'ui-pagination-control ui-pagination-control-last',
            });
        }

        return options;
    }

    generateItems(currentPage) {
        const generatedItems = [];
        const firstItemIndex = (currentPage - 1) * this.props.numItemsPerPage;
        const lastItemIndex = Math.min(this.props.totalItems, firstItemIndex + this.props.numItemsPerPage) - 1;

        for (let i = firstItemIndex; i <= lastItemIndex; i++) {
            generatedItems.push({data: this.props.getItem(i)});
        }

        return generatedItems;
    }

    handleClick = (value) => {
        const values = UIPagination.controls;
        let pageNumber;

        switch (value) {
        case values.FIRST:
            pageNumber = 1;
            break;
        case values.PREVIOUS:
            pageNumber = this.state.currentPage - 1;
            break;
        case values.NEXT:
            pageNumber = this.state.currentPage + 1;
            break;
        case values.LAST:
            pageNumber = this.state.numberOfPages;
            break;
        default:
            pageNumber = parseInt(value, 10);
        }

        this.setState({ currentPage: pageNumber });
    }

    renderItems() {
        const props = this.props.listWrapperProps;

        return (
            <UIArrowKeyNavigation
                {...props}
                ref='itemList'
                className={cx({
                    'ui-pagination-items': true,
                    [props.className]: !!props.className,
                })}>
                {this.generateItems(this.state.currentPage).map((item, index) => {
                    return (
                        <Item
                            ref={`item_${index}`}
                            key={index}
                            data={item.data}
                            even={index % 2 === 0}
                            index={this.state.currentPage - 1 + index}
                            itemToJSXConverterFunc={this.props.itemToJSXConverterFunc} />
                    );
                })}
            </UIArrowKeyNavigation>
        );
    }

    renderControls(position) {
        if (   this.props.hidePagerIfNotNeeded
            && this.props.totalItems <= this.props.numItemsPerPage) {
            return;
        }

        const props = this.props.toggleWrapperProps;
        const position_lower = position.toLowerCase();
        const position_capitalized = position_lower[0].toUpperCase() + position_lower.slice(1);

        return (
            <UISegmentedControl
                {...props}
                ref={`segmentedControl${position_capitalized}`}
                className={cx({
                    'ui-pagination-controls': true,
                    [`ui-pagination-controls-${position_lower}`]: true,
                    [props.className]: !!props.className,
                })}
                options={this.createPageButtonOptions()}
                onOptionSelected={this.handleClick} />
        );
    }

    renderView() {
        const {props} = this;
        const position = UIPagination.positions;

        return (
            <div
                ref='paginatedView'
                className='ui-pagination'>
                {
                      (props.position === position.ABOVE || props.position === position.BOTH)
                    ? this.renderControls(position.ABOVE)
                    : noop
                }

                {this.renderItems()}

                {
                      (props.position === position.BELOW || props.position === position.BOTH)
                    ? this.renderControls(position.BELOW)
                    : noop
                }
            </div>
        );
    }

    render() {
        return (
            <div
                {...omit(this.props, UIPagination.internal_keys)}
                ref='wrapper'
                className={cx({
                    'ui-pagination-wrapper': true,
                    [this.props.className]: !!this.props.className,
                })}>
                {this.renderView()}
            </div>
        );
    }
}
<commit_msg>Fix linting issue<commit_after>/**
 * A utility view for paging the display of many data items of varying sizes.
 * @class UIPagination
 */

import React, {PropTypes} from 'react';
import {findDOMNode} from 'react-dom';
import cx from 'classnames';
import omit from 'lodash.omit';

import UIView from '../UIView';
import UISegmentedControl from '../UISegmentedControl';
import UIArrowKeyNavigation from '../UIArrowKeyNavigation';
import noop from '../UIUtils/noop';

class Item extends UIView {
    static propTypes = {
        even: PropTypes.bool,
        data: PropTypes.object,
        index: PropTypes.number,
        itemToJSXConverterFunc: PropTypes.func,
    }

    static internal_keys = Object.keys(Item.propTypes)

    state = {
        data: this.maybeConvertToJSX(this.props.data),
    }

    __mounted = false

    componentWillReceiveProps(nextProps) {
        if (nextProps.data !== this.props.data) {
            this.setState({data: this.maybeConvertToJSX(nextProps.data)});
        }
    }

    maybeConvertToJSX(data) {
        return data instanceof Promise ? data : this.props.itemToJSXConverterFunc(data);
    }

    waitForContentIfNecessary() {
        if (this.state.data instanceof Promise) {
            this.state.data.then(function cautiouslySetItemData(promise, value) {
                if (this.__mounted && this.state.data === promise) {
                    this.setState({data: this.props.itemToJSXConverterFunc(value)});
                } // only replace if we're looking at the same promise, otherwise do nothing
            }.bind(this, this.state.data));
        }
    }

    componentDidMount() {
        this.__mounted = true;
        this.waitForContentIfNecessary();
    }

    componentWillUnmount() {
        this.__mounted = false;
    }

    componentDidUpdate() {
        this.waitForContentIfNecessary();
    }

    getClasses(extraClasses) {
        return cx({
            'ui-pagination-item': true,
            'ui-pagination-item-even': this.props.even,
            'ui-pagination-item-odd': !this.props.even,
            'ui-pagination-item-loading': this.state.data instanceof Promise,
        }) + (extraClasses ? ' ' + extraClasses : '');
    }

    render() {
        if (this.state.data instanceof Promise) {
            return (<div {...omit(this.props, Item.internal_keys)} className={this.getClasses()} />);
        }

        return React.cloneElement(this.state.data, {
            ...omit(this.props, Item.internal_keys),
            className: this.getClasses(this.state.data.props.className),
            'data-index': this.props.index,
        });
    }
}

export default class UIPagination extends UIView {
    static controls = {
        FIRST: 'FIRST',
        PREVIOUS: 'PREVIOUS',
        NEXT: 'NEXT',
        LAST: 'LAST',
    }

    static positions = {
        ABOVE: 'ABOVE',
        BELOW: 'BELOW',
        BOTH: 'BOTH',
    }

    static propTypes = {
        getItem: PropTypes.func,
        hidePagerIfNotNeeded: PropTypes.bool,
        identifier: PropTypes.string.isRequired,
        itemToJSXConverterFunc: PropTypes.func,
        jumpToFirstControlContent: PropTypes.node,
        jumpToLastControlContent: PropTypes.node,
        listWrapperProps: PropTypes.object,
        nextPageControlContent: PropTypes.node,

        numItemsPerPage: function validateNumItemsPerPage(props) {
            if (!Number.isInteger(props.numItemsPerPage)) {
                return new Error('`numItemsPerPage` must be an integer.');
            } else if (props.numItemsPerPage < 1) {
                return new Error('`numItemsPerPage` must be greater than zero.');
            }
        },

        numPageToggles: PropTypes.number,

        pagerPosition: function validatePagerPosition(props) {
            if (!Number.isInteger(props.pagerPosition)) {
                return new Error('`pagerPosition` must be an integer.');
            }

            const numberOfPages = Math.ceil(props.totalItems / props.numItemsPerPage);

            if (props.pagerPosition < 1 || props.pagerPosition > numberOfPages) {
                return new Error('`pagerPosition` must be between 1 and ' + numberOfPages + '.');
            }
        },

        position: PropTypes.oneOf(Object.keys(UIPagination.positions)),
        previousPageControlContent: PropTypes.node,
        showJumpToFirst: PropTypes.bool,
        showJumpToLast: PropTypes.bool,
        toggleWrapperProps: PropTypes.object,
        totalItems: PropTypes.number.isRequired,
    }

    static internal_keys = Object.keys(UIPagination.propTypes)

    static defaultProps = {
        getItem: noop,
        hidePagerIfNotNeeded: false,
        itemToJSXConverterFunc: data => data,
        jumpToFirstControlContent: '« First',
        jumpToLastControlContent: 'Last »',
        listWrapperProps: {},
        nextPageControlContent: 'Next ›',
        numItemsPerPage: 10,
        numPageToggles: 5,
        pagerPosition: 1,
        position: UIPagination.positions.ABOVE,
        previousPageControlContent: '‹ Previous',
        showJumpToFirst: true,
        showJumpToLast: true,
        toggleWrapperProps: {},
    }

    state = {
        currentPage: this.props.pagerPosition,
        numberOfPages: Math.ceil(this.props.totalItems / this.props.numItemsPerPage),
        totalItems: this.props.totalItems,
    }

    componentDidUpdate(prevProps, prevState) {
        if (prevState.currentPage !== this.state.currentPage) {
            findDOMNode(this.refs.item_0).focus();
        }
    }

    componentWillReceiveProps(nextProps) {
        const numberOfPages = Math.ceil(nextProps.totalItems / nextProps.numItemsPerPage);

        this.setState({
            currentPage:   nextProps.identifier === this.props.identifier
                         ? Math.min(this.state.currentPage, numberOfPages)
                         : 1,
            numberOfPages: numberOfPages,
            totalItems: nextProps.totalItems,
        });
    }

    currentPage = () => this.state.currentPage

    pageToIndex = i => {
        if (i < 0 || i >= this.state.totalItems) {
            return new Error(`Cannot page to invalid index ${i}.`);
        }

        this.setState({ currentPage: Math.ceil((i + 1) / this.props.numItemsPerPage) });
    }

    createPageButtonOptions() {
        const options = [];
        const currentPage = this.state.currentPage;
        const numPageToggles = this.props.numPageToggles;
        const startPage = currentPage - ((currentPage - 1) % numPageToggles);
        const endPage = Math.min(startPage + numPageToggles - 1, this.state.numberOfPages);

        if (this.props.showJumpToFirst) {
            options.push({
                selected: false,
                content: this.props.jumpToFirstControlContent,
                value: UIPagination.controls.FIRST,
                disabled: this.state.currentPage === 1,
                className: 'ui-pagination-control ui-pagination-control-first',
            });
        }

        options.push({
            selected: false,
            content: this.props.previousPageControlContent,
            value: UIPagination.controls.PREVIOUS,
            disabled: this.state.currentPage === 1,
            className: 'ui-pagination-control ui-pagination-control-previous',
        });

        for (let i = startPage; i <= endPage; i++) {
            options.push({
                className: 'ui-pagination-control',
                'data-page-number': i,
                selected: i === this.state.currentPage,
                content: i,
                value: i,
            });
        }

        options.push({
            selected: false,
            content: this.props.nextPageControlContent,
            value: UIPagination.controls.NEXT,
            disabled: this.state.currentPage === this.state.numberOfPages,
            className: 'ui-pagination-control ui-pagination-control-next',
        });

        if (this.props.showJumpToLast) {
            options.push({
                selected: false,
                content: this.props.jumpToLastControlContent,
                value: UIPagination.controls.LAST,
                disabled: this.state.currentPage === this.state.numberOfPages,
                className: 'ui-pagination-control ui-pagination-control-last',
            });
        }

        return options;
    }

    generateItems(currentPage) {
        const generatedItems = [];
        const firstItemIndex = (currentPage - 1) * this.props.numItemsPerPage;
        const lastItemIndex = Math.min(this.props.totalItems, firstItemIndex + this.props.numItemsPerPage) - 1;

        for (let i = firstItemIndex; i <= lastItemIndex; i++) {
            generatedItems.push({data: this.props.getItem(i)});
        }

        return generatedItems;
    }

    handleClick = (value) => {
        const values = UIPagination.controls;
        let pageNumber;

        switch (value) {
        case values.FIRST:
            pageNumber = 1;
            break;
        case values.PREVIOUS:
            pageNumber = this.state.currentPage - 1;
            break;
        case values.NEXT:
            pageNumber = this.state.currentPage + 1;
            break;
        case values.LAST:
            pageNumber = this.state.numberOfPages;
            break;
        default:
            pageNumber = parseInt(value, 10);
        }

        this.setState({ currentPage: pageNumber });
    }

    renderItems() {
        const props = this.props.listWrapperProps;

        return (
            <UIArrowKeyNavigation
                {...props}
                ref='itemList'
                className={cx({
                    'ui-pagination-items': true,
                    [props.className]: !!props.className,
                })}>
                {this.generateItems(this.state.currentPage).map((item, index) => {
                    return (
                        <Item
                            ref={`item_${index}`}
                            key={index}
                            data={item.data}
                            even={index % 2 === 0}
                            index={this.state.currentPage - 1 + index}
                            itemToJSXConverterFunc={this.props.itemToJSXConverterFunc} />
                    );
                })}
            </UIArrowKeyNavigation>
        );
    }

    renderControls(position) {
        if (   this.props.hidePagerIfNotNeeded
            && this.props.totalItems <= this.props.numItemsPerPage) {
            return;
        }

        const props = this.props.toggleWrapperProps;
        const position_lower = position.toLowerCase();
        const position_capitalized = position_lower[0].toUpperCase() + position_lower.slice(1);

        return (
            <UISegmentedControl
                {...props}
                ref={`segmentedControl${position_capitalized}`}
                className={cx({
                    'ui-pagination-controls': true,
                    [`ui-pagination-controls-${position_lower}`]: true,
                    [props.className]: !!props.className,
                })}
                options={this.createPageButtonOptions()}
                onOptionSelected={this.handleClick} />
        );
    }

    renderView() {
        const {props} = this;
        const position = UIPagination.positions;

        return (
            <div
                ref='paginatedView'
                className='ui-pagination'>
                {
                      (props.position === position.ABOVE || props.position === position.BOTH)
                    ? this.renderControls(position.ABOVE)
                    : noop
                }

                {this.renderItems()}

                {
                      (props.position === position.BELOW || props.position === position.BOTH)
                    ? this.renderControls(position.BELOW)
                    : noop
                }
            </div>
        );
    }

    render() {
        return (
            <div
                {...omit(this.props, UIPagination.internal_keys)}
                ref='wrapper'
                className={cx({
                    'ui-pagination-wrapper': true,
                    [this.props.className]: !!this.props.className,
                })}>
                {this.renderView()}
            </div>
        );
    }
}
<|endoftext|>


<commit_before>var utils = require('../lib/utils.js');
var forEach = require('lodash/collection/forEach');

var VALID_AUTOFOCUS_VALUES = {
  true: true,
  false: true,
  auto: true
};

/**
 * Instantiate a searchbox
 * @param  {String|DOMElement} options.container CSS Selector or DOMElement to insert the widget
 * @param  {String} [options.placeholder='Search here'] Input's placeholder
 * @param  {Object} [options.cssClass] CSS classes to add to the input
 * @param  {boolean} [poweredBy=false] Show a powered by Algolia link below the input
 * @param  {boolean|string} [autofocus='auto'] autofocus on the input
 * @return {Object}
 */
function searchbox(params) {
  // Hook on an existing input, or add one if none targeted
  var input = utils.getContainerNode(params.container);
  if (input.tagName !== 'INPUT') {
    input = input.appendChild(document.createElement('input'));
  }

  var autofocus = (typeof params.autofocus === 'boolean' || VALID_AUTOFOCUS_VALUES[params.autofocus]) ?
    params.autofocus :
    'auto';

  return {
    init: function(initialState, helper) {
      var defaultAttributes = {
        autocapitalize: 'off',
        autocomplete: 'off',
        autocorrect: 'off',
        className: params.cssClass,
        placeholder: params.placeholder,
        role: 'textbox',
        spellcheck: 'false',
        type: 'text',
        value: initialState.query
      };

      // Overrides attributes if not already set
      forEach(defaultAttributes, (value, key) => {
        if (input.hasAttribute(key)) {
          return;
        }
        input.setAttribute(key, value);
      });

      // Always add our own classes
      input.classList.add('as-search-box__input');

      input.addEventListener('keyup', () => {
        helper.setQuery(input.value).search();
      });

      // Optional "powered by Algolia" widget
      if (params.poweredBy) {
        var React = require('react');
        var PoweredBy = require('../components/PoweredBy');
        var poweredByContainer = document.createElement('div');
        input.parentNode.appendChild(poweredByContainer);
        React.render(<PoweredBy display />, poweredByContainer);
      }

      helper.on('change', function(state) {
        if (input.value !== state.query) {
          input.value = state.query;
        }
      });

      if (autofocus === 'true' ||
          autofocus === true ||
          autofocus === 'auto' && helper.state.query === '') {
        input.focus();
      }
    }
  };
}

module.exports = searchbox;
<commit_msg>Do not handle 'true' and 'false'<commit_after>var utils = require('../lib/utils.js');
var forEach = require('lodash/collection/forEach');

/**
 * Instantiate a searchbox
 * @param  {String|DOMElement} options.container CSS Selector or DOMElement to insert the widget
 * @param  {String} [options.placeholder='Search here'] Input's placeholder
 * @param  {Object} [options.cssClass] CSS classes to add to the input
 * @param  {boolean} [poweredBy=false] Show a powered by Algolia link below the input
 * @param  {boolean|string} [autofocus='auto'] autofocus on the input
 * @return {Object}
 */
function searchbox(params) {
  // Hook on an existing input, or add one if none targeted
  var input = utils.getContainerNode(params.container);
  if (input.tagName !== 'INPUT') {
    input = input.appendChild(document.createElement('input'));
  }

  var autofocus = (typeof params.autofocus === 'boolean' || params.autofocus === 'auto') ?
    params.autofocus :
    'auto';

  return {
    init: function(initialState, helper) {
      var defaultAttributes = {
        autocapitalize: 'off',
        autocomplete: 'off',
        autocorrect: 'off',
        className: params.cssClass,
        placeholder: params.placeholder,
        role: 'textbox',
        spellcheck: 'false',
        type: 'text',
        value: initialState.query
      };

      // Overrides attributes if not already set
      forEach(defaultAttributes, (value, key) => {
        if (input.hasAttribute(key)) {
          return;
        }
        input.setAttribute(key, value);
      });

      // Always add our own classes
      input.classList.add('as-search-box__input');

      input.addEventListener('keyup', () => {
        helper.setQuery(input.value).search();
      });

      // Optional "powered by Algolia" widget
      if (params.poweredBy) {
        var React = require('react');
        var PoweredBy = require('../components/PoweredBy');
        var poweredByContainer = document.createElement('div');
        input.parentNode.appendChild(poweredByContainer);
        React.render(<PoweredBy display />, poweredByContainer);
      }

      helper.on('change', function(state) {
        if (input.value !== state.query) {
          input.value = state.query;
        }
      });

      if (autofocus === true ||
          autofocus === 'auto' && helper.state.query === '') {
        input.focus();
      }
    }
  };
}

module.exports = searchbox;
<|endoftext|>