diff --git a/.gitignore b/.gitignore
index 8b6c9ef..ecff5de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
*.map
+build
coverage
node_modules
bower_components
diff --git a/.travis.yml b/.travis.yml
index 5d47692..171bcfc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,12 @@
sudo: false
language: node_js
node_js:
- - "0.10"
+ - 0.10
+env:
+ - TEST=1
+ - TYPECHECK=1
+ - LINT=1
+
script:
- - npm run verify
+ - ./config/travis/test.sh
after_success: cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
diff --git a/Gulpfile.js b/Gulpfile.js
index ea5c81f..b68784d 100644
--- a/Gulpfile.js
+++ b/Gulpfile.js
@@ -1,50 +1,46 @@
-var gulp = require('gulp'),
- given = require('gulp-if'),
- jsx = require('gulp-react'),
- reactify = require('reactify'),
- buffer = require('vinyl-buffer'),
- minifyjs = require('gulp-uglify'),
- browserify = require('browserify'),
- sourcemaps = require('gulp-sourcemaps'),
- sourcestream = require('vinyl-source-stream');
+var gulp = require('gulp');
+var babel = require('gulp-babel');
+
+var browserifyCreator = require('./pipeline/browserify');
var args = require('yargs').alias('P', 'production')
.alias('D', 'development')
- .alias('E', 'example').argv,
- production = args.production,
- development = args.development,
- example = args.example;
-
-gulp.task('build', function() {
- // Build standalone bundle for the browser
- var b = browserify({
- entries: './src/react-infinite.jsx',
- standalone: 'Infinite'
- })
- .transform(reactify, {
- es6: true
- })
- .exclude('react')
- .bundle()
- .pipe(sourcestream('react-infinite.' + (production ? 'min.' : '') + 'js'))
- .pipe(buffer())
- .pipe(given(development, sourcemaps.init()))
- .pipe(given(production, minifyjs()))
- .pipe(given(development, sourcemaps.write('.')))
- .pipe(gulp.dest('dist'));
+ .alias('E', 'example').argv;
+
+var envObject = {
+ production: args.production,
+ development: args.development,
+ release: !(args.production || args.development)
+};
+var INFINITE_SOURCE = './src/react-infinite.jsx';
+gulp.task('build-bundle', browserifyCreator(false, envObject, INFINITE_SOURCE));
+gulp.task('watch-develop-bundle', browserifyCreator(true, {development: true}, INFINITE_SOURCE));
+
+// This task builds everything for release: the dist
+// folder is populated with react-infinite.js and
+// react-infinite.min.js, while the build folder is
+// provided with a copy of the source transpiled to ES5.
+gulp.task('release', ['build-bundle'], function() {
// Transpile CommonJS files to ES5 with React's tools.
gulp.src(['./src/**/*.js', './src/**/*.jsx'])
- .pipe(jsx({
- harmony: true
- }))
- .pipe(gulp.dest('build'))
-
- if (example) {
- gulp.src('./examples/*.jsx')
- .pipe(jsx())
- .pipe(gulp.dest('examples'))
- }
+ .pipe(babel())
+ .pipe(gulp.dest('build'));
+});
+
+// This task is used to build the examples. It is used
+// in the development watch function as well.
+gulp.task('examples', function() {
+ gulp.src('./examples/*.jsx')
+ .pipe(babel())
+ .pipe(gulp.dest('examples'));
+});
+
+// This task is used for development. When run, it sets up
+// a watch on the source files
+gulp.task('develop', ['watch-develop-bundle'], function() {
+ gulp.watch('Gulpfile.js', ['examples', 'build-bundle']);
+ gulp.watch('./examples/*.jsx', ['examples', 'build-bundle']);
});
-gulp.task('default', ['build']);
+gulp.task('default', ['release']);
diff --git a/README.md b/README.md
index 2cc4647..ad79c46 100644
--- a/README.md
+++ b/README.md
@@ -177,7 +177,11 @@ I am seated in an office, surrounded by heads and bodies. There I've written som
Tests are located in the `__tests__` directory2, and can be run with `npm test` after `npm install`.
## Developing
-React Infinite is built with Browserify and Gulp. To get started, install the development dependencies with `npm install`. If you do not already have Gulp, you might wish to install it globally with `npm install -g gulp`. Then run `gulp build -D`, which builds the development version. To build the production version, run `gulp build -P`, and to build the non-minified release version, run `gulp`.
+React Infinite is built with Browserify and Gulp. To get started, install the development dependencies with `npm install`. If you do not already have Gulp, you might wish to install it globally with `npm install -g gulp`.
+
+The newly revamped development environment is based on `gulp develop`. When run, this command sets up a watch on the `/src` folder with Browserify as well as the `examples` folder.
+
+The other major command, `gulp release`, prepares files for release. It produces normal and minified versions of `react-infinite.js` in the `dist` folder and also directly transpiles the source to ES5 in the `build` folder so it can be required by other asset pipelines.
### Infinite Computers
diff --git a/build/computers/array_infinite_computer.js b/build/computers/array_infinite_computer.js
deleted file mode 100644
index 5e86cb7..0000000
--- a/build/computers/array_infinite_computer.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/* @flow */
-
-var InfiniteComputer = require('./infinite_computer.js'),
- bs = require('../utils/binary_index_search.js');
-
-for(var InfiniteComputer____Key in InfiniteComputer){if(InfiniteComputer.hasOwnProperty(InfiniteComputer____Key)){ArrayInfiniteComputer[InfiniteComputer____Key]=InfiniteComputer[InfiniteComputer____Key];}}var ____SuperProtoOfInfiniteComputer=InfiniteComputer===null?null:InfiniteComputer.prototype;ArrayInfiniteComputer.prototype=Object.create(____SuperProtoOfInfiniteComputer);ArrayInfiniteComputer.prototype.constructor=ArrayInfiniteComputer;ArrayInfiniteComputer.__superConstructor__=InfiniteComputer;
-
- function ArrayInfiniteComputer(heightData/* : Array */, numberOfChildren )/* : void */ {"use strict";
- InfiniteComputer.call(this,heightData, numberOfChildren);
- this.prefixHeightData = this.heightData.reduce(function(acc, next) {
- if (acc.length === 0) {
- return [next];
- } else {
- acc.push(acc[acc.length - 1] + next);
- return acc;
- }
- }, []);
- }
-
- Object.defineProperty(ArrayInfiniteComputer.prototype,"maybeIndexToIndex",{writable:true,configurable:true,value:function(index )/* : number */ {"use strict";
- if (typeof index === 'undefined' || index === null) {
- return this.prefixHeightData.length - 1;
- } else {
- return index;
- }
- }});
-
- Object.defineProperty(ArrayInfiniteComputer.prototype,"getTotalScrollableHeight",{writable:true,configurable:true,value:function()/* : number */ {"use strict";
- var length = this.prefixHeightData.length;
- return length === 0 ? 0 : this.prefixHeightData[length - 1];
- }});
-
- Object.defineProperty(ArrayInfiniteComputer.prototype,"getDisplayIndexStart",{writable:true,configurable:true,value:function(windowTop )/* : number */ {"use strict";
- var foundIndex = bs.binaryIndexSearch(this.prefixHeightData, windowTop, bs.opts.CLOSEST_HIGHER);
- return this.maybeIndexToIndex(foundIndex);
- }});
-
- Object.defineProperty(ArrayInfiniteComputer.prototype,"getDisplayIndexEnd",{writable:true,configurable:true,value:function(windowBottom )/* : number */ {"use strict";
- var foundIndex = bs.binaryIndexSearch(this.prefixHeightData, windowBottom, bs.opts.CLOSEST_HIGHER);
- return this.maybeIndexToIndex(foundIndex);
- }});
-
- Object.defineProperty(ArrayInfiniteComputer.prototype,"getTopSpacerHeight",{writable:true,configurable:true,value:function(displayIndexStart )/* : number */ {"use strict";
- var previous = displayIndexStart - 1;
- return previous < 0 ? 0 : this.prefixHeightData[previous];
- }});
-
- Object.defineProperty(ArrayInfiniteComputer.prototype,"getBottomSpacerHeight",{writable:true,configurable:true,value:function(displayIndexEnd )/* : number */ {"use strict";
- if (displayIndexEnd === -1) {
- return 0;
- }
- return this.getTotalScrollableHeight() - this.prefixHeightData[displayIndexEnd];
- }});
-
-
-module.exports = ArrayInfiniteComputer;
diff --git a/build/computers/constant_infinite_computer.js b/build/computers/constant_infinite_computer.js
deleted file mode 100644
index d9cca28..0000000
--- a/build/computers/constant_infinite_computer.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/* @flow */
-
-var InfiniteComputer = require('./infinite_computer.js');
-
-for(var InfiniteComputer____Key in InfiniteComputer){if(InfiniteComputer.hasOwnProperty(InfiniteComputer____Key)){ConstantInfiniteComputer[InfiniteComputer____Key]=InfiniteComputer[InfiniteComputer____Key];}}var ____SuperProtoOfInfiniteComputer=InfiniteComputer===null?null:InfiniteComputer.prototype;ConstantInfiniteComputer.prototype=Object.create(____SuperProtoOfInfiniteComputer);ConstantInfiniteComputer.prototype.constructor=ConstantInfiniteComputer;ConstantInfiniteComputer.__superConstructor__=InfiniteComputer;function ConstantInfiniteComputer(){"use strict";if(InfiniteComputer!==null){InfiniteComputer.apply(this,arguments);}}
- Object.defineProperty(ConstantInfiniteComputer.prototype,"getTotalScrollableHeight",{writable:true,configurable:true,value:function()/* : number */ {"use strict";
- return this.heightData * this.numberOfChildren;
- }});
-
- Object.defineProperty(ConstantInfiniteComputer.prototype,"getDisplayIndexStart",{writable:true,configurable:true,value:function(windowTop )/* : number */ {"use strict";
- return Math.floor(windowTop / this.heightData);
- }});
-
- Object.defineProperty(ConstantInfiniteComputer.prototype,"getDisplayIndexEnd",{writable:true,configurable:true,value:function(windowBottom )/* : number */ {"use strict";
- var nonZeroIndex = Math.ceil(windowBottom / this.heightData);
- if (nonZeroIndex > 0) {
- return nonZeroIndex - 1;
- }
- return nonZeroIndex;
- }});
-
- Object.defineProperty(ConstantInfiniteComputer.prototype,"getTopSpacerHeight",{writable:true,configurable:true,value:function(displayIndexStart )/* : number */ {"use strict";
- return displayIndexStart * this.heightData;
- }});
-
- Object.defineProperty(ConstantInfiniteComputer.prototype,"getBottomSpacerHeight",{writable:true,configurable:true,value:function(displayIndexEnd )/* : number */ {"use strict";
- var nonZeroIndex = displayIndexEnd + 1;
- return Math.max(0, (this.numberOfChildren - nonZeroIndex) * this.heightData);
- }});
-
-
-module.exports = ConstantInfiniteComputer;
diff --git a/build/computers/infinite_computer.js b/build/computers/infinite_computer.js
deleted file mode 100644
index 230ff77..0000000
--- a/build/computers/infinite_computer.js
+++ /dev/null
@@ -1,43 +0,0 @@
-// An infinite computer must be able to do the following things:
-// 1. getTotalScrollableHeight()
-// 2. getDisplayIndexStart()
-// 3. getDisplayIndexEnd()
-
-
- function InfiniteComputer(heightData, numberOfChildren) {"use strict";
- this.heightData = heightData;
- this.numberOfChildren = numberOfChildren;
- }
-
- Object.defineProperty(InfiniteComputer.prototype,"getTotalScrollableHeight",{writable:true,configurable:true,value:function() {"use strict";
- throw new Error('getTotalScrollableHeight not implemented.');
- }});
-
- /* eslint-disable no-unused-vars */
- Object.defineProperty(InfiniteComputer.prototype,"getDisplayIndexStart",{writable:true,configurable:true,value:function(windowTop) {"use strict";
- /* eslint-enable no-unused-vars */
- throw new Error('getDisplayIndexStart not implemented.');
- }});
-
- /* eslint-disable no-unused-vars */
- Object.defineProperty(InfiniteComputer.prototype,"getDisplayIndexEnd",{writable:true,configurable:true,value:function(windowBottom) {"use strict";
- /* eslint-enable no-unused-vars */
- throw new Error('getDisplayIndexEnd not implemented.');
- }});
-
- // These are helper methods, and can be calculated from
- // the above details.
- /* eslint-disable no-unused-vars */
- Object.defineProperty(InfiniteComputer.prototype,"getTopSpacerHeight",{writable:true,configurable:true,value:function(displayIndexStart) {"use strict";
- /* eslint-enable no-unused-vars */
- throw new Error('getTopSpacerHeight not implemented.');
- }});
-
- /* eslint-disable no-unused-vars */
- Object.defineProperty(InfiniteComputer.prototype,"getBottomSpacerHeight",{writable:true,configurable:true,value:function(displayIndexEnd) {"use strict";
- /* eslint-enable no-unused-vars */
- throw new Error('getBottomSpacerHeight not implemented.');
- }});
-
-
-module.exports = InfiniteComputer;
diff --git a/build/react-infinite.js b/build/react-infinite.js
deleted file mode 100644
index 28df716..0000000
--- a/build/react-infinite.js
+++ /dev/null
@@ -1,296 +0,0 @@
-var React = global.React || require('react'),
- _isArray = require('lodash.isarray'),
- _isFinite = require('lodash.isfinite'),
- ConstantInfiniteComputer = require('./computers/constant_infinite_computer.js'),
- ArrayInfiniteComputer = require('./computers/array_infinite_computer.js');
-var _assign = require('object-assign');
-
-var Infinite = React.createClass({displayName: "Infinite",
-
- propTypes: {
- handleScroll: React.PropTypes.func,
-
- // preloadBatchSize causes updates only to
- // happen each preloadBatchSize pixels of scrolling.
- // Set a larger number to cause fewer updates to the
- // element list.
- preloadBatchSize: React.PropTypes.number,
- // preloadAdditionalHeight determines how much of the
- // list above and below the container is preloaded even
- // when it is not currently visible to the user. In the
- // regular scroll implementation, preloadAdditionalHeight
- // is equal to the entire height of the list.
- preloadAdditionalHeight: React.PropTypes.number, // page to screen ratio
-
- // The provided elementHeight can be either
- // 1. a constant: all elements are the same height
- // 2. an array containing the height of each element
- elementHeight: React.PropTypes.oneOfType([
- React.PropTypes.number,
- React.PropTypes.arrayOf(React.PropTypes.number)
- ]).isRequired,
- // This is the total height of the visible window.
- containerHeight: React.PropTypes.number.isRequired,
-
- infiniteLoadBeginBottomOffset: React.PropTypes.number,
- onInfiniteLoad: React.PropTypes.func,
- loadingSpinnerDelegate: React.PropTypes.node,
-
- isInfiniteLoading: React.PropTypes.bool,
- timeScrollStateLastsForAfterUserScrolls: React.PropTypes.number,
- useWindowAsScrollContainer: React.PropTypes.bool,
-
- className: React.PropTypes.string
- },
-
- getDefaultProps:function() {
- return {
- handleScroll: function() {},
- loadingSpinnerDelegate: React.createElement("div", null),
- onInfiniteLoad: function() {},
- isInfiniteLoading: false,
- timeScrollStateLastsForAfterUserScrolls: 150,
- useWindowAsScrollContainer: false,
- className: ''
- };
- },
-
- // automatic adjust to scroll direction
- // give spinner a ReactCSSTransitionGroup
- getInitialState:function() {
- this.computedProps = this.generateComputedProps(this.props);
- this.utils = this.generateComputedUtilityFunctions(this.props);
-
- var computer = this.createInfiniteComputer(this.computedProps.elementHeight, this.computedProps.children);
-
- var preloadBatchSize = this.computedProps.preloadBatchSize;
- var preloadAdditionalHeight = this.computedProps.preloadAdditionalHeight;
-
- return {
- infiniteComputer: computer,
-
- numberOfChildren: React.Children.count(this.computedProps.children),
- displayIndexStart: 0,
- displayIndexEnd: computer.getDisplayIndexEnd(
- preloadBatchSize + preloadAdditionalHeight
- ),
-
- isInfiniteLoading: false,
-
- preloadBatchSize: preloadBatchSize,
- preloadAdditionalHeight: preloadAdditionalHeight,
-
- scrollTimeout: undefined,
- isScrolling: false
- };
- },
-
- createInfiniteComputer:function(data, children) {
- var computer;
- var numberOfChildren = React.Children.count(children);
-
- if (_isFinite(data)) {
- computer = new ConstantInfiniteComputer(data, numberOfChildren);
- } else if (_isArray(data)) {
- computer = new ArrayInfiniteComputer(data, numberOfChildren);
- } else {
- throw new Error('You must provide either a number or an array of numbers as the elementHeight prop.');
- }
-
- return computer;
- },
-
- generateComputedProps:function(props) {
- var computedProps = _assign({}, props);
- computedProps.containerHeight = props.useWindowAsScrollContainer
- ? window.innerHeight : props.containerHeight;
- computedProps.preloadBatchSize = typeof props.preloadBatchSize === 'number'
- ? props.preloadBatchSize : computedProps.containerHeight / 2;
- computedProps.preloadAdditionalHeight = typeof props.preloadAdditionalHeight === 'number'
- ? props.preloadAdditionalHeight : computedProps.containerHeight;
- return computedProps;
- },
-
- generateComputedUtilityFunctions:function(props) {
- var utilities = {};
- if (props.useWindowAsScrollContainer) {
- utilities.subscribeToScrollListener = function() {
- window.onscroll = this.infiniteHandleScroll;
- }.bind(this);
- utilities.nodeScrollListener = function() {};
- utilities.getScrollTop = function() {return window.scrollY;};
- utilities.scrollShouldBeIgnored = function() {return false;};
- utilities.buildScrollableStyle = function() {return {};};
- } else {
- utilities.subscribeToScrollListener = function() {};
- utilities.nodeScrollListener = this.infiniteHandleScroll;
- utilities.getScrollTop = function() {return React.findDOMNode(this.refs.scrollable).scrollTop;}.bind(this);
- utilities.scrollShouldBeIgnored = function(event) {return event.target !== React.findDOMNode(this.refs.scrollable);}.bind(this);
- utilities.buildScrollableStyle = function() {
- return {
- height: this.computedProps.containerHeight,
- overflowX: 'hidden',
- overflowY: 'scroll'
- };
- }.bind(this);
- }
- return utilities;
- },
-
- componentWillReceiveProps:function(nextProps) {
- this.computedProps = this.generateComputedProps(nextProps);
- this.utils = this.generateComputedUtilityFunctions(nextProps);
-
- var newStateObject = {};
-
- // TODO: more efficient elementHeight change detection
- newStateObject.infiniteComputer = this.createInfiniteComputer(
- this.computedProps.elementHeight,
- this.computedProps.children
- );
-
- if (this.computedProps.isInfiniteLoading !== undefined) {
- newStateObject.isInfiniteLoading = this.computedProps.isInfiniteLoading;
- }
-
- newStateObject.preloadBatchSize = this.computedProps.preloadBatchSize;
- newStateObject.preloadAdditionalHeight = this.computedProps.preloadAdditionalHeight;
-
- this.setState(newStateObject, function() {
- this.setStateFromScrollTop(this.utils.getScrollTop());
- }.bind(this));
- },
-
- componentDidUpdate:function(prevProps) {
- if (React.Children.count(this.computedProps.children) !== React.Children.count(prevProps.children)) {
- this.setStateFromScrollTop(this.utils.getScrollTop());
- }
- },
-
- componentWillMount:function() {
- if (_isArray(this.computedProps.elementHeight)) {
- if (React.Children.count(this.computedProps.children) !== this.computedProps.elementHeight.length) {
- throw new Error('There must be as many values provided in the elementHeight prop as there are children.');
- }
- }
- },
-
- componentDidMount:function() {
- this.utils.subscribeToScrollListener();
- },
-
- // Given the scrollTop of the container, computes the state the
- // component should be in. The goal is to abstract all of this
- // from any actual representation in the DOM.
- // The window is the block with any preloadAdditionalHeight
- // added to it.
- setStateFromScrollTop:function(scrollTop) {
- var blockNumber = this.state.preloadBatchSize === 0 ? 0 : Math.floor(scrollTop / this.state.preloadBatchSize),
- blockStart = this.state.preloadBatchSize * blockNumber,
- blockEnd = blockStart + this.state.preloadBatchSize,
- apertureTop = Math.max(0, blockStart - this.state.preloadAdditionalHeight),
- apertureBottom = Math.min(this.state.infiniteComputer.getTotalScrollableHeight(),
- blockEnd + this.state.preloadAdditionalHeight);
-
- this.setState({
- displayIndexStart: this.state.infiniteComputer.getDisplayIndexStart(apertureTop),
- displayIndexEnd: this.state.infiniteComputer.getDisplayIndexEnd(apertureBottom)
- });
- },
-
- infiniteHandleScroll:function(e) {
- if (this.utils.scrollShouldBeIgnored(e)) {
- return;
- }
- this.computedProps.handleScroll(React.findDOMNode(this.refs.scrollable));
- this.handleScroll(this.utils.getScrollTop());
- },
-
- manageScrollTimeouts:function() {
- // Maintains a series of timeouts to set this.state.isScrolling
- // to be true when the element is scrolling.
-
- if (this.state.scrollTimeout) {
- clearTimeout(this.state.scrollTimeout);
- }
-
- var that = this,
- scrollTimeout = setTimeout(function() {
- that.setState({
- isScrolling: false,
- scrollTimeout: undefined
- });
- }, this.computedProps.timeScrollStateLastsForAfterUserScrolls);
-
- this.setState({
- isScrolling: true,
- scrollTimeout: scrollTimeout
- });
- },
-
- handleScroll:function(scrollTop) {
- this.manageScrollTimeouts();
- this.setStateFromScrollTop(scrollTop);
-
- var infiniteScrollBottomLimit = scrollTop >
- (this.state.infiniteComputer.getTotalScrollableHeight() -
- this.computedProps.containerHeight -
- this.computedProps.infiniteLoadBeginBottomOffset);
- if (infiniteScrollBottomLimit && !this.state.isInfiniteLoading) {
- this.setState({
- isInfiniteLoading: true
- });
- this.computedProps.onInfiniteLoad();
- }
- },
-
- // Helpers for React styles.
- buildScrollableStyle:function() {
- return {
- height: this.computedProps.containerHeight,
- overflowX: 'hidden',
- overflowY: 'scroll'
- };
- },
-
- buildHeightStyle:function(height) {
- return {
- width: '100%',
- height: Math.ceil(height)
- };
- },
-
- render:function() {
- var displayables = this.computedProps.children.slice(this.state.displayIndexStart,
- this.state.displayIndexEnd + 1);
-
- var infiniteScrollStyles = {};
- if (this.state.isScrolling) {
- infiniteScrollStyles.pointerEvents = 'none';
- }
-
- var topSpacerHeight = this.state.infiniteComputer.getTopSpacerHeight(this.state.displayIndexStart),
- bottomSpacerHeight = this.state.infiniteComputer.getBottomSpacerHeight(this.state.displayIndexEnd);
-
- // topSpacer and bottomSpacer take up the amount of space that the
- // rendered elements would have taken up otherwise
- return React.createElement("div", {className: this.computedProps.className,
- ref: "scrollable",
- style: this.utils.buildScrollableStyle(),
- onScroll: this.utils.nodeScrollListener},
- React.createElement("div", {ref: "smoothScrollingWrapper", style: infiniteScrollStyles},
- React.createElement("div", {ref: "topSpacer",
- style: this.buildHeightStyle(topSpacerHeight)}),
- displayables,
- React.createElement("div", {ref: "bottomSpacer",
- style: this.buildHeightStyle(bottomSpacerHeight)}),
- React.createElement("div", {ref: "loadingSpinner"},
- this.state.isInfiniteLoading ? this.computedProps.loadingSpinnerDelegate : null
- )
- )
- );
- }
-});
-
-module.exports = Infinite;
-global.Infinite = Infinite;
diff --git a/build/utils/binary_index_search.js b/build/utils/binary_index_search.js
deleted file mode 100644
index 1f6b0a8..0000000
--- a/build/utils/binary_index_search.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/* @flow */
-
-var opts = {
- CLOSEST_LOWER: 1,
- CLOSEST_HIGHER: 2
-};
-
-var binaryIndexSearch = function(array/* : Array */,
- item/* : number */,
- opt/* : number */)/* : ?number */ {
- var index;
-
- var high = array.length - 1,
- low = 0,
- middle,
- middleItem;
-
- while (low <= high) {
- middle = low + Math.floor((high - low) / 2);
- middleItem = array[middle];
-
- if (middleItem === item) {
- return middle;
- } else if (middleItem < item) {
- low = middle + 1;
- } else if (middleItem > item) {
- high = middle - 1;
- }
- }
-
- if (opt === opts.CLOSEST_LOWER && low > 0) {
- index = low - 1;
- } else if (opt === opts.CLOSEST_HIGHER && high < array.length - 1) {
- index = high + 1;
- }
-
- return index;
-};
-
-module.exports = {
- binaryIndexSearch: binaryIndexSearch,
- opts: opts
-};
diff --git a/config/travis/test.sh b/config/travis/test.sh
new file mode 100755
index 0000000..393930e
--- /dev/null
+++ b/config/travis/test.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+if [ "$TEST" = 1 ]; then
+ npm run test
+fi;
+
+if [ "$TYPECHECK" = 1 ]; then
+ npm run typecheck
+fi;
+
+if [ "$LINT" = 1 ]; then
+ npm run lint
+fi;
diff --git a/examples/borderless-window.html b/examples/borderless-window.html
index 6f5e0e8..2d4d0cb 100644
--- a/examples/borderless-window.html
+++ b/examples/borderless-window.html
@@ -26,7 +26,7 @@
-
+