<commit_before>import React, {
  forwardRef, memo, useState, useCallback, useMemo,
} from 'react';
import {
  View, Dimensions, FlatList,
} from 'react-native';
import PropTypes from 'prop-types';
import { chunkArray, calculateDimensions, generateStyles } from './utils';

const FlatGrid = memo(
  forwardRef((props, ref) => {
    const {
      style,
      spacing,
      fixed,
      data,
      itemDimension,
      renderItem,
      horizontal,
      onLayout,
      staticDimension,
      maxDimension,
      additionalRowStyle: externalRowStyle,
      itemContainerStyle,
      keyExtractor,
      ...restProps
    } = props;

    if (props.items && !props.data) {
      // eslint-disable-next-line no-console
      throw new Error('React Native Super Grid - Prop "items" has been renamed to "data" in version 4');
    }

    const [totalDimension, setTotalDimension] = useState(() => {
      let defaultTotalDimension = staticDimension;

      if (!staticDimension) {
        const dimension = horizontal ? 'height' : 'width';
        defaultTotalDimension = maxDimension || Dimensions.get('window')[dimension];
      }

      return defaultTotalDimension;
    });

    const onLayoutLocal = useCallback(
      (e) => {
        if (!staticDimension) {
          const { width, height } = e.nativeEvent.layout || {};
          let newTotalDimension = horizontal ? height : width;

          if (maxDimension && newTotalDimension > maxDimension) {
            newTotalDimension = maxDimension;
          }

          if (totalDimension !== newTotalDimension) {
            setTotalDimension(newTotalDimension);
          }
        }

        // call onLayout prop if passed
        if (onLayout) {
          onLayout(e);
        }
      },
      [staticDimension, maxDimension, totalDimension, horizontal, onLayout],
    );

    const renderRow = useCallback(
      ({
        rowItems,
        rowIndex,
        separators,
        isLastRow,
        itemsPerRow,
        rowStyle,
        containerStyle,
      }) => {
        // To make up for the top padding
        let additionalRowStyle = {};
        if (isLastRow) {
          additionalRowStyle = {
            ...(!horizontal ? { marginBottom: spacing } : {}),
            ...(horizontal ? { marginRight: spacing } : {}),
          };
        }

        return (
          <View style={[rowStyle, additionalRowStyle, externalRowStyle]}>
            {rowItems.map((item, i) => (
              <View
                key={
                  keyExtractor
                    ? keyExtractor(item, i)
                    : `item_${rowIndex * itemsPerRow + i}`
                }
                style={[containerStyle, itemContainerStyle]}
              >
                {renderItem({
                  item,
                  index: rowIndex * itemsPerRow + i,
                  separators,
                  rowIndex,
                })}
              </View>
            ))}
          </View>
        );
      },
      [renderItem, spacing, keyExtractor, externalRowStyle, itemContainerStyle, horizontal],
    );

    const { containerDimension, itemsPerRow, fixedSpacing } = useMemo(
      () => calculateDimensions({
        itemDimension,
        staticDimension,
        totalDimension,
        spacing,
        fixed,
      }),
      [itemDimension, staticDimension, totalDimension, spacing, fixed],
    );

    const { containerStyle, rowStyle } = useMemo(
      () => generateStyles({
        horizontal,
        itemDimension,
        containerDimension,
        spacing,
        fixedSpacing,
        fixed,
      }),
      [horizontal, itemDimension, containerDimension, spacing, fixedSpacing, fixed],
    );

    const rows = chunkArray(data, itemsPerRow); // Splitting the data into rows

    const localKeyExtractor = useCallback(
      (rowItems, index) => {
        if (keyExtractor) {
          return rowItems
            .map((rowItem, rowItemIndex) => keyExtractor(rowItem, rowItemIndex))
            .join('_');
        }
        return `row_${index}`;
      },
      [keyExtractor],
    );

    return (
      <FlatList
        data={rows}
        ref={ref}
        extraData={totalDimension}
        renderItem={({ item, index }) => renderRow({
          rowItems: item,
          rowIndex: index,
          isLastRow: index === rows.length - 1,
          itemsPerRow,
          rowStyle,
          containerStyle,
        })
        }
        style={[
          {
            ...(horizontal
              ? { paddingLeft: spacing }
              : { paddingTop: spacing }),
          },
          style,
        ]}
        onLayout={onLayoutLocal}
        keyExtractor={localKeyExtractor}
        {...restProps}
        horizontal={horizontal}
      />
    );
  }),
);

FlatGrid.displayName = 'FlatGrid';

FlatGrid.propTypes = {
  renderItem: PropTypes.func.isRequired,
  data: PropTypes.arrayOf(PropTypes.any).isRequired,
  itemDimension: PropTypes.number,
  fixed: PropTypes.bool,
  spacing: PropTypes.number,
  style: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]),
  additionalRowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]),
  itemContainerStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]),
  staticDimension: PropTypes.number,
  horizontal: PropTypes.bool,
  onLayout: PropTypes.func,
  keyExtractor: PropTypes.func,
  listKey: PropTypes.string,
};

FlatGrid.defaultProps = {
  fixed: false,
  itemDimension: 120,
  spacing: 10,
  style: {},
  additionalRowStyle: undefined,
  itemContainerStyle: undefined,
  staticDimension: undefined,
  horizontal: false,
  onLayout: null,
  keyExtractor: null,
  listKey: undefined,
};

export default FlatGrid;
<commit_msg>Add invertedRow to FlatGrid<commit_after>import React, {
  forwardRef, memo, useState, useCallback, useMemo,
} from 'react';
import {
  View, Dimensions, FlatList,
} from 'react-native';
import PropTypes from 'prop-types';
import { chunkArray, calculateDimensions, generateStyles } from './utils';

const FlatGrid = memo(
  forwardRef((props, ref) => {
    const {
      style,
      spacing,
      fixed,
      data,
      itemDimension,
      renderItem,
      horizontal,
      onLayout,
      staticDimension,
      maxDimension,
      additionalRowStyle: externalRowStyle,
      itemContainerStyle,
      keyExtractor,
      invertedRow,
      ...restProps
    } = props;

    if (props.items && !props.data) {
      // eslint-disable-next-line no-console
      throw new Error('React Native Super Grid - Prop "items" has been renamed to "data" in version 4');
    }

    const [totalDimension, setTotalDimension] = useState(() => {
      let defaultTotalDimension = staticDimension;

      if (!staticDimension) {
        const dimension = horizontal ? 'height' : 'width';
        defaultTotalDimension = maxDimension || Dimensions.get('window')[dimension];
      }

      return defaultTotalDimension;
    });

    const onLayoutLocal = useCallback(
      (e) => {
        if (!staticDimension) {
          const { width, height } = e.nativeEvent.layout || {};
          let newTotalDimension = horizontal ? height : width;

          if (maxDimension && newTotalDimension > maxDimension) {
            newTotalDimension = maxDimension;
          }

          if (totalDimension !== newTotalDimension) {
            setTotalDimension(newTotalDimension);
          }
        }

        // call onLayout prop if passed
        if (onLayout) {
          onLayout(e);
        }
      },
      [staticDimension, maxDimension, totalDimension, horizontal, onLayout],
    );

    const renderRow = useCallback(
      ({
        rowItems,
        rowIndex,
        separators,
        isLastRow,
        itemsPerRow,
        rowStyle,
        containerStyle,
        invertedRow,
      }) => {
        // To make up for the top padding
        let additionalRowStyle = {};
        if (isLastRow) {
          additionalRowStyle = {
            ...(!horizontal ? { marginBottom: spacing } : {}),
            ...(horizontal ? { marginRight: spacing } : {}),
          };
        }

        return (
          <View style={[rowStyle, additionalRowStyle, externalRowStyle]}>
            {rowItems.map((item, index) => {
              const i = invertedRow ? -index + itemsPerRow - 1 : index;

              return (
                <View
                  key={
                    keyExtractor
                      ? keyExtractor(item, i)
                      : `item_${rowIndex * itemsPerRow + i}`
                  }
                  style={[containerStyle, itemContainerStyle]}
                >
                  {renderItem({
                    item,
                    index: rowIndex * itemsPerRow + i,
                    separators,
                    rowIndex,
                  })}
                </View>
              )
            }
            )}
          </View>
        );
      },
      [renderItem, spacing, keyExtractor, externalRowStyle, itemContainerStyle, horizontal],
    );

    const { containerDimension, itemsPerRow, fixedSpacing } = useMemo(
      () => calculateDimensions({
        itemDimension,
        staticDimension,
        totalDimension,
        spacing,
        fixed,
      }),
      [itemDimension, staticDimension, totalDimension, spacing, fixed],
    );

    const { containerStyle, rowStyle } = useMemo(
      () => generateStyles({
        horizontal,
        itemDimension,
        containerDimension,
        spacing,
        fixedSpacing,
        fixed,
      }),
      [horizontal, itemDimension, containerDimension, spacing, fixedSpacing, fixed],
    );

    let rows = chunkArray(data, itemsPerRow); // Splitting the data into rows
    if (invertedRow) {
      rows = rows.map(r => r.reverse())
    }

    const localKeyExtractor = useCallback(
      (rowItems, index) => {
        if (keyExtractor) {
          return rowItems
            .map((rowItem, rowItemIndex) => keyExtractor(rowItem, rowItemIndex))
            .join('_');
        }
        return `row_${index}`;
      },
      [keyExtractor],
    );

    return (
      <FlatList
        data={rows}
        ref={ref}
        extraData={totalDimension}
        renderItem={({ item, index }) => renderRow({
          rowItems: item,
          rowIndex: index,
          isLastRow: index === rows.length - 1,
          itemsPerRow,
          rowStyle,
          containerStyle,
        })
        }
        style={[
          {
            ...(horizontal
              ? { paddingLeft: spacing }
              : { paddingTop: spacing }),
          },
          style,
        ]}
        onLayout={onLayoutLocal}
        keyExtractor={localKeyExtractor}
        {...restProps}
        horizontal={horizontal}
      />
    );
  }),
);

FlatGrid.displayName = 'FlatGrid';

FlatGrid.propTypes = {
  renderItem: PropTypes.func.isRequired,
  data: PropTypes.arrayOf(PropTypes.any).isRequired,
  itemDimension: PropTypes.number,
  fixed: PropTypes.bool,
  spacing: PropTypes.number,
  style: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]),
  additionalRowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]),
  itemContainerStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]),
  staticDimension: PropTypes.number,
  horizontal: PropTypes.bool,
  onLayout: PropTypes.func,
  keyExtractor: PropTypes.func,
  listKey: PropTypes.string,
};

FlatGrid.defaultProps = {
  fixed: false,
  itemDimension: 120,
  spacing: 10,
  style: {},
  additionalRowStyle: undefined,
  itemContainerStyle: undefined,
  staticDimension: undefined,
  horizontal: false,
  onLayout: null,
  keyExtractor: null,
  listKey: undefined,
};

export default FlatGrid;
<|endoftext|>


<commit_before>/**
* This class is the front-end of the server, and performs all routing
* to individual controllers.
*/
"use strict";
var EthnologyController = require('./EthnologyController');
var ethnologyController = new EthnologyController();

var DatabaseAdapter = require('./DatabaseAdapter');
var database = new DatabaseAdapter({filename: './TestDatabase.db', verbose: true});

var AuthController = require('./AuthController');
var authController = new AuthController(database);

var log = require('./Logger');
//log.init('BibleApp.log');

var restify = require('restify');
var server = restify.createServer({
	name: 'BibleJS',
	version: "1.0.0"
});

server.use(restify.bodyParser({
	maxBodySize: 10000,
	mapParams: true
}));

// don't forget server.use(restify.throttle);

server.pre(restify.pre.userAgentConnection()); // if UA is curl, close connection.

/**
* This pre-step is to authorize transactions.
*/
server.pre(function(request, response, next) {
	var path = request.getPath().substr(1,5);
	if (path === 'bible' || path === 'versi' || path === 'quest' || path === 'login' || path === 'begin') return(next());
	authController.auth(request, function(err) {
		if (err) {
			return(next(err));
		}
		return(next());
	});
});

server.on('after', function(request, response, route, error) {
	var date = new Date();
	var duration = date.getTime() - request.time();
	var msg = { time: date.toISOString(), method: request.method, url: request.url, body: request.body, statusCode: response.statusCode, duration: duration };
	if (error) {
		msg.error = error;
		log.error(msg);
	} else {
		log.info(msg);
	}
});

/**
* This route should be commented out of a production server.
*/
server.get('/beginTest', function beginTest(request, response, next) {
	database = new DatabaseAdapter({filename: './AutoTestDatabase.db', verbose: true});
	database.create(function(err) {
		respond(err, {'message': 'AutoTestDatabase.db created'}, 201, response, next);
		authController.database = database;
	});
});

server.get(/\/bible\/?.*/, restify.serveStatic({
	directory: '../../StaticRoot'
}));

server.get('/versions/:locale', function getVersions(request, response, next) {
	var results = ethnologyController.availVersions(request.params.locale);
	respond(null, results, 200, response, next);
});

server.get('/login', function loginTeacher(request, response, next) {
	authController.login(request, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.put('/user', function registerTeacher(request, response, next) {
	request.params.authorizerId = request.headers.authId;
	authController.register(request.params, function(err, results) {
		respond(err, results, 201, response, next);
	});
});

server.post('/user', function updateTeacher(request, response, next) {
	database.updateTeacher(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.del('/user/', function deleteTeacher(request, response, next) {
	database.deleteTeacher(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.put('/position', function insertPosition(request, response, next) {
	database.insertPosition(request.params, function(err, results) {
		respond(err, results, 201, response, next);
	});
});

server.post('/position', function updatePosition(request, response, next) {
	database.updatePosition(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.del('/position', function deletePosition(request, response, next) {
	database.deletePosition(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.put('/question', function insertQuestion(request, response, next) {
	database.insertQuestion(request.params, function(err, results) {
		respond(err, results, 201, response, next);
	});
});

server.post('/question', function updateQuestion(request, response, next) {
	database.updateQuestion(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.del('/question', function deleteQuestion(request, response, next) {
	database.deleteQuestion(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.get('/open/:teacherId/:versionId', function openQuestionCount(request, response, next) {
	database.getAssignment(request.params, function(err, results) {
		if (err || results.length > 0) {
			respond(err, results, 200, response, next);
		} else {
			database.openQuestionCount(request.params, function(err, results) {
				respond(err, results, 200, response, next);
			});
		}
	});
});
/** teacherId, versionId, optional timestamp */
server.post('/assign', function assignQuestion(request, response, next) {
	database.getAssignment(request.params, function(err, results) {
		if (err || results.length > 0) {
			respond(err, results, 200, response, next);
		} else {
			database.assignQuestion(request.params, function(err, results) {
				respond(err, results, 200, response, next);
			});
		}
	});
});
/** teacherId, versionId, discourseId */
server.post('/return', function returnQuestion(request, response, next) {
	database.returnQuestion(request.params, function(err, results) {
		if (err) {
			respond(err, results, 200, response, next);
		} else {
			database.openQuestionCount(request.params, function(err, results) {
				respond(err, results, 200, response, next);
			});
		}
	});
});
/** teacherId, versionId, discourseId */
server.post('/another', function anotherQuestion(request, response, next) {
	database.returnQuestion(request.params, function(err, results) {
		if (err || results === undefined || results.timestamp === undefined) {
			respond(err, results, 200, response, next);
		} else {
			request.params.timestamp = results.timestamp; // set to assign a later question.
			database.assignQuestion(request.params, function(err, results) {
				respond(err, results, 200, response, next);
			});
		}
	});
});

server.post('/answer', function sendAnswer(request, response, next) {
	database.saveAnswer(request.params, function(err, saveResults) {
		if (err) {
			respond(err, saveResults, 200, response, next);
		} else {
			database.openQuestionCount(request.params, function(err, results) {
				results.rowCount = saveResults.rowCount;
				results.messageTimestamp = saveResults.timestamp;
				respond(err, results, 200, response, next);
			});
		}
	});
});

server.del('/answer', function deleteAnswer(request, response, next) {
	database.deleteAnswer(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.get('/answer/:discourseId', function getAnswers(request, response, next) {
	database.selectAnswer(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.post('/draft', function saveDraft(request, response, next) {
	database.saveDraft(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.del('/draft', function deleteDraft(request, response, next) {
	database.deleteDraft(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.get('/draft/:discourseId/:timestamp', function getDraft(request, response, next) {
	database.selectDraft(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.listen(8080, function() {
	console.log('listening on 8080');
});

function respond(error, results, successCode, response, next) {
	if (error) {
		if (! error.statusCode) {
			error.statusCode = errorStatusCode(error);
		}
		return(next(error));
	} else {
		response.send(successCode, results);
		return(next());
	}
}
/**
* I need to extend error so that I have a code that I can use in a language independent way to identify errors
*/
function errorStatusCode(err) {
	var message = err.message;
	if (message) {
		if (message.indexOf('SQLITE_CONSTRAINT') > -1) return(409);
		if (message.indexOf('actual=0') > -1) return(410);
		if (message.indexOf('expected=2  actual=1') > -1) return(410);
		if (message.indexOf('no questions') > -1) return(410);
		if (message.indexOf('Register') > -1) return(400);
	}
	return(500);
}
<commit_msg>Turned off verbose for regular use of database.<commit_after>/**
* This class is the front-end of the server, and performs all routing
* to individual controllers.
*/
"use strict";
var EthnologyController = require('./EthnologyController');
var ethnologyController = new EthnologyController();

var DatabaseAdapter = require('./DatabaseAdapter');
var database = new DatabaseAdapter({filename: './TestDatabase.db', verbose: false});

var AuthController = require('./AuthController');
var authController = new AuthController(database);

var log = require('./Logger');
//log.init('BibleApp.log');

var restify = require('restify');
var server = restify.createServer({
	name: 'BibleJS',
	version: "1.0.0"
});

server.use(restify.bodyParser({
	maxBodySize: 10000,
	mapParams: true
}));

// don't forget server.use(restify.throttle);

server.pre(restify.pre.userAgentConnection()); // if UA is curl, close connection.

/**
* This pre-step is to authorize transactions.
*/
server.pre(function(request, response, next) {
	var path = request.getPath().substr(1,5);
	if (path === 'bible' || path === 'versi' || path === 'quest' || path === 'login' || path === 'begin') return(next());
	authController.auth(request, function(err) {
		if (err) {
			return(next(err));
		}
		return(next());
	});
});

server.on('after', function(request, response, route, error) {
	var date = new Date();
	var duration = date.getTime() - request.time();
	var msg = { time: date.toISOString(), method: request.method, url: request.url, body: request.body, statusCode: response.statusCode, duration: duration };
	if (error) {
		msg.error = error;
		log.error(msg);
	} else {
		log.info(msg);
	}
});

/**
* This route should be commented out of a production server.
*/
server.get('/beginTest', function beginTest(request, response, next) {
	database = new DatabaseAdapter({filename: './AutoTestDatabase.db', verbose: true});
	database.create(function(err) {
		respond(err, {'message': 'AutoTestDatabase.db created'}, 201, response, next);
		authController.database = database;
	});
});

server.get(/\/bible\/?.*/, restify.serveStatic({
	directory: '../../StaticRoot'
}));

server.get('/versions/:locale', function getVersions(request, response, next) {
	var results = ethnologyController.availVersions(request.params.locale);
	respond(null, results, 200, response, next);
});

server.get('/login', function loginTeacher(request, response, next) {
	authController.login(request, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.put('/user', function registerTeacher(request, response, next) {
	request.params.authorizerId = request.headers.authId;
	authController.register(request.params, function(err, results) {
		respond(err, results, 201, response, next);
	});
});

server.post('/user', function updateTeacher(request, response, next) {
	database.updateTeacher(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.del('/user/', function deleteTeacher(request, response, next) {
	database.deleteTeacher(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.put('/position', function insertPosition(request, response, next) {
	database.insertPosition(request.params, function(err, results) {
		respond(err, results, 201, response, next);
	});
});

server.post('/position', function updatePosition(request, response, next) {
	database.updatePosition(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.del('/position', function deletePosition(request, response, next) {
	database.deletePosition(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.put('/question', function insertQuestion(request, response, next) {
	database.insertQuestion(request.params, function(err, results) {
		respond(err, results, 201, response, next);
	});
});

server.post('/question', function updateQuestion(request, response, next) {
	database.updateQuestion(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.del('/question', function deleteQuestion(request, response, next) {
	database.deleteQuestion(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.get('/open/:teacherId/:versionId', function openQuestionCount(request, response, next) {
	database.getAssignment(request.params, function(err, results) {
		if (err || results.length > 0) {
			respond(err, results, 200, response, next);
		} else {
			database.openQuestionCount(request.params, function(err, results) {
				respond(err, results, 200, response, next);
			});
		}
	});
});
/** teacherId, versionId, optional timestamp */
server.post('/assign', function assignQuestion(request, response, next) {
	database.getAssignment(request.params, function(err, results) {
		if (err || results.length > 0) {
			respond(err, results, 200, response, next);
		} else {
			database.assignQuestion(request.params, function(err, results) {
				respond(err, results, 200, response, next);
			});
		}
	});
});
/** teacherId, versionId, discourseId */
server.post('/return', function returnQuestion(request, response, next) {
	database.returnQuestion(request.params, function(err, results) {
		if (err) {
			respond(err, results, 200, response, next);
		} else {
			database.openQuestionCount(request.params, function(err, results) {
				respond(err, results, 200, response, next);
			});
		}
	});
});
/** teacherId, versionId, discourseId */
server.post('/another', function anotherQuestion(request, response, next) {
	database.returnQuestion(request.params, function(err, results) {
		if (err || results === undefined || results.timestamp === undefined) {
			respond(err, results, 200, response, next);
		} else {
			request.params.timestamp = results.timestamp; // set to assign a later question.
			database.assignQuestion(request.params, function(err, results) {
				respond(err, results, 200, response, next);
			});
		}
	});
});

server.post('/answer', function sendAnswer(request, response, next) {
	database.saveAnswer(request.params, function(err, saveResults) {
		if (err) {
			respond(err, saveResults, 200, response, next);
		} else {
			database.openQuestionCount(request.params, function(err, results) {
				results.rowCount = saveResults.rowCount;
				results.messageTimestamp = saveResults.timestamp;
				respond(err, results, 200, response, next);
			});
		}
	});
});

server.del('/answer', function deleteAnswer(request, response, next) {
	database.deleteAnswer(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.get('/answer/:discourseId', function getAnswers(request, response, next) {
	database.selectAnswer(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.post('/draft', function saveDraft(request, response, next) {
	database.saveDraft(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.del('/draft', function deleteDraft(request, response, next) {
	database.deleteDraft(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.get('/draft/:discourseId/:timestamp', function getDraft(request, response, next) {
	database.selectDraft(request.params, function(err, results) {
		respond(err, results, 200, response, next);
	});
});

server.listen(8080, function() {
	console.log('listening on 8080');
});

function respond(error, results, successCode, response, next) {
	if (error) {
		if (! error.statusCode) {
			error.statusCode = errorStatusCode(error);
		}
		return(next(error));
	} else {
		response.send(successCode, results);
		return(next());
	}
}
/**
* I need to extend error so that I have a code that I can use in a language independent way to identify errors
*/
function errorStatusCode(err) {
	var message = err.message;
	if (message) {
		if (message.indexOf('SQLITE_CONSTRAINT') > -1) return(409);
		if (message.indexOf('actual=0') > -1) return(410);
		if (message.indexOf('expected=2  actual=1') > -1) return(410);
		if (message.indexOf('no questions') > -1) return(410);
		if (message.indexOf('Register') > -1) return(400);
	}
	return(500);
}
<|endoftext|>


<commit_before>
/**
 * ctor for a Fraction
 *
 * @constructor
 * @param {(Number|String)} num The Numerator, if an int, or a String represntation. Eg, 3/4
 * @param {Number} [den] The Denominator
 */
function Fraction(num, den) {

    if (den === undefined && typeof num === "string") {
        // string constructor
        // todo: better parsing / error handling here.
        var split = num.split('/');
        num = parseInt(split[0], 10);
        den = parseInt(split[1], 10);
    }

    if (num > den) {
        throw new RangeException();
    }

    this.num = num;
    this.den = den;
}

/**
 * Returns the sum of fractions A and B.
 */
Fraction.sum = function (A,B) {
    Fraction.setCommonDenominators(A, B);
    return new Fraction(A.num + B.num, A.den);
}

/**
 * Finds a common denominator for fractions A and B so that
 * they can be added or subtracted.
 */
Fraction.setCommonDenominators = function (A, B) {
    // todo: this is not how to validate input in js. Fix it.
    //if (typeof A != 'Fraction' || typeof B != 'Fraction') {
    //    throw 1;
    //}

    if (A.den == B.den) {
        return false; // indicate that A and B already had common denominators
    } else {
        // todo: Replace / supplement this with LCM
        var aDen = A.den;
        A.num *= B.den;
        A.den *= B.den;
        B.num *= aDen;
        B.den *= aDen;
        return true;
    }
}

/**
 * Reduces the fraction to simplest terms
 */
Fraction.prototype.simplify = function () {
    // reduce until no further reductions possible
    while (this.reduce());
}

/**
 * Performs one step of 'reduction' on the fraction.
 * This is not an efficient implementation and should
 * not be used in any 'demanding' scenario.
 *
 * @returns {Boolean} False if the fraction was in lowest
 * terms before the call to reduce(). True otherwise.
 */
Fraction.prototype.reduce = function () {
    for (var i = 2; i <= this.num && i <= this.den; i++) {
        if (this.num % i === 0 && this.den % i === 0) {
            this.num = this.num / i;
            this.den = this.den / i;

            // indicate that the fraction may be
            // able to be reduced further.
            return true;
        }
    }
    // indicate that the fraction is in lowest terms
    return false;
}

/**
 * Generates an html5 canvas drawing representing the fraction.
 *
 * @param {int} size The width and height in pixels of the generated canvas element
 * @returns {HTMLElement} A (size) by (size) canvas which represents the fraction.
 */
Fraction.prototype.getCanvasDrawing = function (size) {
    if (size === undefined) {
        console.log("Fraction defaulting to 100 px");
        size = 100;
    }

    var canvas = document.createElement('canvas');
    canvas.height = size;
    canvas.width = size;
    var con = canvas.getContext('2d');

    var mid = size / 2;
    var radius = mid * 0.9;
    var angle = 2 * Math.PI / this.den;

    con.fillStyle = 'orange';

    // providing a randomized rotation for the drawing.
    // (outputs feel less 'mechanized' this way
    con.translate(mid, mid);
    con.rotate(Math.random() * Math.PI * 2); // param rotate?
    con.translate(-mid, -mid);

    // the 'arc' fills
    for (var i = 0; i < this.num; i++) {
        con.beginPath();
        con.arc(mid, mid, radius, angle * i, angle * (i + 1));
        con.fill();
    }

    // the 'triangular' segments
    con.beginPath();
    con.moveTo(mid, mid);
    for (var i = 0; i <= this.num; i++) {
        con.lineTo(mid + radius * Math.cos(angle * i),
                   mid + radius * Math.sin(angle * i));
    }
    con.moveTo(mid, mid);
    con.fill();

    // the dividing spokes
    if (this.den > 1) {
        // prevents drawing of 'solitary' spoke in 1/1
        for (var i = 0; i < this.den; i++) {
            con.beginPath();
            con.moveTo(mid, mid);
            con.lineTo(mid + radius * Math.cos(i * angle),
                       mid + radius * Math.sin(i * angle));
            con.stroke();
        }
    }

    // the exterior circle
    con.beginPath();
    con.arc(mid, mid, radius, 0, 2 * Math.PI);
    con.stroke();

    return canvas;
}<commit_msg>toString()<commit_after>
/**
 * ctor for a Fraction
 *
 * @constructor
 * @param {(Number|String)} num The Numerator, if an int, or a String represntation. Eg, 3/4
 * @param {Number} [den] The Denominator
 */
function Fraction(num, den) {

    if (den === undefined && typeof num === "string") {
        // string constructor
        // todo: better parsing / error handling here.
        var split = num.split('/');
        num = parseInt(split[0], 10);
        den = parseInt(split[1], 10);
    }

    if (num > den) {
        throw new RangeException();
    }

    this.num = num;
    this.den = den;
}

/**
 * Returns the sum of fractions A and B.
 */
Fraction.sum = function (A,B) {
    Fraction.setCommonDenominators(A, B);
    return new Fraction(A.num + B.num, A.den);
}

/**
 * Finds a common denominator for fractions A and B so that
 * they can be added or subtracted.
 */
Fraction.setCommonDenominators = function (A, B) {
    // todo: this is not how to validate input in js. Fix it.
    //if (typeof A != 'Fraction' || typeof B != 'Fraction') {
    //    throw 1;
    //}

    if (A.den == B.den) {
        return false; // indicate that A and B already had common denominators
    } else {
        // todo: Replace / supplement this with LCM
        var aDen = A.den;
        A.num *= B.den;
        A.den *= B.den;
        B.num *= aDen;
        B.den *= aDen;
        return true;
    }
}

/**
 * Returns simple string representation, eg "3/4"
 */
Fraction.prototype.toString = function () {
    return this.num + "/" + this.den;
}

/**
 * Reduces the fraction to simplest terms
 */
Fraction.prototype.simplify = function () {
    // reduce until no further reductions possible
    while (this.reduce());
}

/**
 * Performs one step of 'reduction' on the fraction.
 * This is not an efficient implementation and should
 * not be used in any 'demanding' scenario.
 *
 * @returns {Boolean} False if the fraction was in lowest
 * terms before the call to reduce(). True otherwise.
 */
Fraction.prototype.reduce = function () {
    for (var i = 2; i <= this.num && i <= this.den; i++) {
        if (this.num % i === 0 && this.den % i === 0) {
            this.num = this.num / i;
            this.den = this.den / i;

            // indicate that the fraction may be
            // able to be reduced further.
            return true;
        }
    }
    // indicate that the fraction is in lowest terms
    return false;
}

/**
 * Generates an html5 canvas drawing representing the fraction.
 *
 * @param {int} size The width and height in pixels of the generated canvas element
 * @returns {HTMLElement} A (size) by (size) canvas which represents the fraction.
 */
Fraction.prototype.getCanvasDrawing = function (size) {
    if (size === undefined) {
        console.log("Fraction defaulting to 100 px");
        size = 100;
    }

    var canvas = document.createElement('canvas');
    canvas.height = size;
    canvas.width = size;
    var con = canvas.getContext('2d');

    var mid = size / 2;
    var radius = mid * 0.9;
    var angle = 2 * Math.PI / this.den;

    con.fillStyle = 'orange';

    // providing a randomized rotation for the drawing.
    // (outputs feel less 'mechanized' this way
    con.translate(mid, mid);
    con.rotate(Math.random() * Math.PI * 2); // param rotate?
    con.translate(-mid, -mid);

    // the 'arc' fills
    for (var i = 0; i < this.num; i++) {
        con.beginPath();
        con.arc(mid, mid, radius, angle * i, angle * (i + 1));
        con.fill();
    }

    // the 'triangular' segments
    con.beginPath();
    con.moveTo(mid, mid);
    for (var i = 0; i <= this.num; i++) {
        con.lineTo(mid + radius * Math.cos(angle * i),
                   mid + radius * Math.sin(angle * i));
    }
    con.moveTo(mid, mid);
    con.fill();

    // the dividing spokes
    if (this.den > 1) {
        // prevents drawing of 'solitary' spoke in 1/1
        for (var i = 0; i < this.den; i++) {
            con.beginPath();
            con.moveTo(mid, mid);
            con.lineTo(mid + radius * Math.cos(i * angle),
                       mid + radius * Math.sin(i * angle));
            con.stroke();
        }
    }

    // the exterior circle
    con.beginPath();
    con.arc(mid, mid, radius, 0, 2 * Math.PI);
    con.stroke();

    return canvas;
}<|endoftext|>


<commit_before>/**
* This class is the front-end of the server, and performs all routing
* to individual controllers.
*/
var restify = require('restify');
var server = restify.createServer({
	name: 'BibleJS'
});
server.pre(restify.pre.userAgentConnection()); // if UA is curl, close connection.

server.use(restify.bodyParser({
	maxBodySize: 10000,
	mapParams: true
}));

server.post('/user', function(request, response, next) {
	console.log('Register a new user', request.params.username);
	response.send(200, 'Register a new user ' + request.params.username);
	return(next());
});

server.get('/version/:version', function(request, response, next) {
	console.log('Download version transaction ', request.params.version);
	response.send(200, 'Download Version TBD = ' + request.params.version);
	return(next());
});

server.get('/locale/:locale', function(request, response, next) {
	console.log('Download Ethnologe info ', request.params.locale);
	response.send(200, 'Download Ethnologe info = ' + request.params.locale);
	return(next());
});

server.post('/question', function(request, response, next) {
	console.log('POST question ' + request.params.question);
	response.send(200, 'POST Question ' + request.params.question + ' ' + request.params.hello);
	return(next());
});

server.get('/assign/:version', function(request, response, next) {
	console.log('Assign Question ', request.params.version);
	response.send(200, 'Assign Question ' + request.params.version);
	return(next());
});

server.get('/return/:questionId', function(request, response, next) {
	console.log('Return question ', request.params.questionId);
	response.send(200, 'Return question ' + request.params.questionId);
	return(next());
});

server.post('/send', function(request, response, next) {
	console.log('Send response ', request.params.message);
	response.send(200, 'Send response ' + request.params.message);
	return(next());
});

server.post('/draft', function(request, response, next) {
	console.log('Save Draft ' + request.params.message);
	response.send(200, 'Save Draft ' + request.params.message);
	return(next());
});

server.get('/draft/:draftId', function(request, response, next) {
	console.log('Get Draft ' + request.params.draftId);
	response.send(200, 'Get Draft ' + request.params.draftId);
	return(next());
});

server.del('/draft/:draftId', function(request, response, next) {
	console.log('Delete Draft ' + request.params.draftId);
	response.send(200, 'Delete Draft ' + request.params.draftId);
	return(next());
});

server.get(/^.*$/, function(request, response, next) {
	console.log('No Valid Route', request.url);
	response.send(404, 'No Valid Route for ' + request.url);
	return(next());
})

server.listen(8080, function() {
	console.log('listening on 8080');
});

<commit_msg>Added function names to routes.<commit_after>/**
* This class is the front-end of the server, and performs all routing
* to individual controllers.
*/
var restify = require('restify');
var server = restify.createServer({
	name: 'BibleJS'
});
server.pre(restify.pre.userAgentConnection()); // if UA is curl, close connection.

server.use(restify.bodyParser({
	maxBodySize: 10000,
	mapParams: true
}));

server.post('/user', function registerUser(request, response, next) {
	console.log('Register a new user', request.params.username);
	response.send(200, 'Register a new user ' + request.params.username);
	return(next());
});

server.get('/version/:version', function downloadVersion(request, response, next) {
	console.log('Download version transaction ', request.params.version);
	response.send(200, 'Download Version TBD = ' + request.params.version);
	return(next());
});

server.get('/locale/:locale', function getVersions(request, response, next) {
	console.log('Download Ethnologe info ', request.params.locale);
	response.send(200, 'Download Ethnologe info = ' + request.params.locale);
	return(next());
});

server.post('/question', function updateQuestion(request, response, next) {
	console.log('POST question ' + request.params.question);
	response.send(200, 'POST Question ' + request.params.question + ' ' + request.params.hello);
	return(next());
});

server.get('/assign/:version', function assignQuestion(request, response, next) {
	console.log('Assign Question ', request.params.version);
	response.send(200, 'Assign Question ' + request.params.version);
	return(next());
});

server.get('/return/:questionId', function returnQuestion(request, response, next) {
	console.log('Return question ', request.params.questionId);
	response.send(200, 'Return question ' + request.params.questionId);
	return(next());
});

server.post('/send', function sendQuestion(request, response, next) {
	console.log('Send response ', request.params.message);
	response.send(200, 'Send response ' + request.params.message);
	return(next());
});

server.post('/draft', function saveDraft(request, response, next) {
	console.log('Save Draft ' + request.params.message);
	response.send(200, 'Save Draft ' + request.params.message);
	return(next());
});

server.get('/draft/:draftId', function getDraft(request, response, next) {
	console.log('Get Draft ' + request.params.draftId);
	response.send(200, 'Get Draft ' + request.params.draftId);
	return(next());
});

server.del('/draft/:draftId', function deleteDraft(request, response, next) {
	console.log('Delete Draft ' + request.params.draftId);
	response.send(200, 'Delete Draft ' + request.params.draftId);
	return(next());
});

server.get(/^.*$/, function getCatchAll(request, response, next) {
	return(invalidRequest(request, response, next));
});
server.put(/^.*$/, function getCatchAll(request, response, next) {
	return(invalidRequest(request, response, next));
});
server.post(/^.*$/, function getCatchAll(request, response, next) {
	return(invalidRequest(request, response, next));
});
server.del(/^.*$/, function getCatchAll(request, response, next) {
	return(invalidRequest(request, response, next));
});
server.head(/^.*$/, function getCatchAll(request, response, next) {
	return(invalidRequest(request, response, next));
});

server.listen(8080, function() {
	console.log('listening on 8080');
});

function invalidRequest(request, response, next) {
	console.log('No Valid Route', request.url);
	response.send(404, 'No Valid Route ' + request.url);
	return(next());
}

<|endoftext|>


<commit_before>
import winston from "winston";
import zmq from "zmq";
import _ from "underscore";

import InputVertex from "./InputVertex";
import SK from "../../sk";
import m from "../MagicFilters";

const MAIN_SWITCHER_LABEL = "mainSwitcher";
const SPLIT_SCREEN_TOP_SWITCHER_LABEL = "splitScreenTopSwitcher";
const SPLIT_SCREEN_BOTTOM_SWITCHER_LABEL = "splitScreenBottomSwitcher";
const SPLIT_SCREEN_BOTTOM_CROP_LABEL = "splitScreenBottomCrop";
const SPLIT_SCREEN_BOTTOM_OVERLAY_LABEL = "splitScreenBottomOverlay";

// This is hacky as hell but it doesn't seem to work if I send all messages at once.
const ZMQ_SEND_INTERVAL = 1000;

export default class MagicVertex extends InputVertex {
  constructor({id}) {
    super({id});
    this.rewriteStream = false;
    // this.debug = true;
    this.zmqQueue = [];
    this.zmqIsRunning = false;
    this.videoOutputURL = this.transport.getOutputURL();
    this.audioOutputURL = this.transport.getOutputURL();
  }

  cleanup() {
    super.cleanup();
    this.positionVertexHandle.stop();

    if (this.zmqSocket) {
      try {
        this.zmqSocket.unmonitor();
        this.zmqSocket.disconnect(this.zmqAddress);
      }
      catch (e) {
        this.error("Error disconnecting from ZMQ: ", e.stack);
      }
    }
  }

  handleInitialPull() {
    this.currentPositions = this.doc.params.positions;
    this.positionVertexHandle = SK.vertices.watch({id: this.id}).on("updated", ([vertex]) => {
      const newPositions = vertex.params.positions;
      Object.keys(vertex.params.positions).forEach((inputName) => {
        if (!_(this.currentPositions[inputName]).isEqual(newPositions[inputName])) {
          this.currentPositions[inputName] = newPositions[inputName];
          this.doZMQUpdate(inputName);
        }
      });
      this.currentPositions = newPositions;
    });
    this.doc.inputs.forEach((input) => {
      input.sockets.forEach((socket) => {
        socket.url = this.transport.getInputURL();
      });
    });
    const newVertex = {
      inputs: this.doc.inputs,
      outputs: [{
        name: "default",
        sockets: [{
          url: this.videoOutputURL,
          type: "video",
        }, {
          url: this.audioOutputURL,
          type: "audio",
        }]
      }]

    };
    SK.vertices.update(this.doc.id, newVertex)
    .then(() => {
      this.init();
    })
    .catch((err) => {
      winston.error(err);
    });
  }

  _sendNextZMQMessage() {
    // If there's nothing to do, we're not running anymore.
    if (this.zmqQueue.length === 0) {
      this.zmqIsRunning = false;
      return;
    }
    // If we're not connected yet, chill. We'll get called again when we do.
    if (!this.zmqSocket) {
      this.zmqIsRunning = false;
      return;
    }
    // Otherwise, send a message and queue the next one.
    const msg = this.zmqQueue.pop();
    this.info(`ZMQ: ${msg}`);
    this.zmqSocket.send(msg);
    this.zmqIsRunning = true;
    setTimeout(this._sendNextZMQMessage.bind(this), ZMQ_SEND_INTERVAL);
  }

  doZMQUpdate(inputName) {
    const send = (msg) => {
      this.zmqQueue.push(msg);
    };
    const pos = this.currentPositions[inputName];
    const overlayLabel = this.ffmpeg.filterLabels[`${inputName}-overlay`];
    const scaleLabel = this.ffmpeg.filterLabels[`${inputName}-scale`];
    send(`${overlayLabel} x ${pos.x}`);
    send(`${overlayLabel} y ${pos.y}`);
    send(`${scaleLabel} width ${pos.width}`);
    send(`${scaleLabel} height ${pos.height}`);
    if (!this.zmqIsRunning) {
      this._sendNextZMQMessage();
    }
  }

  init() {
    super.init();
    try {
      this.ffmpeg = this.createffmpeg();
      this.zmqPort = this.getTCP();
      this.zmqAddress = `tcp://*:${this.zmqPort}`;

      const videoInputSockets = [];
      const audioInputSockets = [];
      let currentIdx = 0;

      this.doc.inputs.forEach((input, inputIdx) => {
        input.sockets.forEach((socket, socketIdx) => {
          socket.name = `${input.name}-${socketIdx}`;
          socket.inputName = input.name;
          this.ffmpeg
            .input(socket.url)
            .inputFormat("mpegts")
            .inputOptions([
              "-analyzeduration 10000000",
              "-noaccurate_seek", // Not really sure if this helps, but we certainly don't need
                                  // any kind of accurate seek.
              "-probesize 60000000",
              "-thread_queue_size 16384",
              // "-avioflags direct",
            ]);

          // Set up video input
          if (socket.type === "video") {
            videoInputSockets.push(socket);
            if (input.name === "background") {
              this.ffmpeg.magic(
                `${currentIdx}:v`,
                m.framerate("30"),
                m.scale(1920, 1080),
                `${socket.name}`
              );
            }
            else {
              const pos = this.doc.params.positions[input.name];
              this.ffmpeg.magic(
                `${currentIdx}:v`,
                m.framerate("30"),
                m.scale([pos.width, pos.height], {_label: `${input.name}-scale`}),
                `${socket.name}`
              );
            }
          }

          // Set up audio input
          else if (socket.type === "audio") {
            audioInputSockets.push(socket);
            this.ffmpeg.magic(
              `${currentIdx}:a`,
              m.aresample({
                async: 1,
                min_hard_comp: 0.100000,
                first_pts: 0
              }),
              m.volume({
                _label: `${socket.name}-volume`
              }),
              `${socket.name}-adjusted`
            );
          }

          else {
            throw new Error(`Unknown input type: ${input.type}`);
          }
          currentIdx += 1;
        });
      });

      // Do a series of overlays for the input
      let currentOverlayBG = "background-0";
      videoInputSockets.forEach((socket, i) => {
        if (socket.name === currentOverlayBG) {
          // First one doesn't need to overlay onto nothing. Return.
          return;
        }
        const newOverlayBG = `${socket.name}-overlay`;
        const pos = this.doc.params.positions[socket.inputName];
        this.ffmpeg.magic(
          currentOverlayBG,
          socket.name,
          m.overlay({
            x: pos.x,
            y: pos.y,
            _label: `${socket.inputName}-overlay`
          }),
          newOverlayBG
        );
        currentOverlayBG = newOverlayBG;
      });

      this.ffmpeg
        .outputOptions([
          "-copyts",
          "-copytb 1",
          "-vsync passthrough",

          // I think this flag just causes it to discard the analysis buffer when the stream starts
          "-fflags +nobuffer",

          // "-sws_flags +neighbor",
          "-pix_fmt yuv420p",
          // "-profile:v baseline",
          // "-use_wallclock_as_timestamps 1",
          "-fflags +igndts",
          "-loglevel verbose",
        ])
        .magic(
          currentOverlayBG,
          m.zmq({bind_address: this.zmqAddress}),
          m.framerate("30"),
          "videoOutput"
        )
        .outputOptions([
          "-map [videoOutput]",
          "-b:v 4000k",
          "-allow_skip_frames 1",
          "-preset veryfast",
          "-x264opts keyint=60",
          "-b:v 4000k",
          "-minrate 4000k",
          "-maxrate 4000k",
          // "-bufsize 1835k",
          // "-frame_drop_threshold 60",
        ])
        .once("progress", () => {
          const socket = zmq.socket("req");
          socket.on("connect", (fd, ep) => {
            this.zmqSocket = socket;
            if (!this.zmqIsRunning) {
              this._sendNextZMQMessage();
            }
            let idx = 0;
            let label = this.ffmpeg.filterLabels[MAIN_SWITCHER_LABEL];
          });
          socket.on("connect", (fd, ep) => {this.info("connect, endpoint:", ep);});
          socket.on("connect_delay", (fd, ep) => {this.info("connect_delay, endpoint:", ep);});
          socket.on("connect_retry", (fd, ep) => {this.info("connect_retry, endpoint:", ep);});
          socket.on("listen", (fd, ep) => {this.info("listen, endpoint:", ep);});
          socket.on("bind_error", (fd, ep) => {this.info("bind_error, endpoint:", ep);});
          socket.on("accept", (fd, ep) => {this.info("accept, endpoint:", ep);});
          socket.on("accept_error", (fd, ep) => {this.info("accept_error, endpoint:", ep);});
          socket.on("close", (fd, ep) => {this.info("close, endpoint:", ep);});
          socket.on("close_error", (fd, ep) => {this.info("close_error, endpoint:", ep);});
          socket.on("disconnect", (fd, ep) => {this.info("disconnect, endpoint:", ep);});
          socket.on("message", (msg) => {this.info("message: ", msg.toString());});
          socket.monitor(500, 0);
          socket.connect(`tcp://127.0.0.1:${this.zmqPort}`);
        })
        .output(this.videoOutputURL)
        .outputFormat("mpegts")
        .videoCodec("libx264")

        .magic(
          ...audioInputSockets.map(s => `${s.name}-adjusted`),
          m.amix({
            inputs: audioInputSockets.length
          }),
          "audioOutput"
        )

        .output(this.audioOutputURL)
        .outputOptions([
          "-map [audioOutput]"
        ])
        .outputFormat("mpegts")
        .audioCodec("aac");

        // .input(this.inputURL)
        // .inputFormat("mpegts")
        // // .inputOptions("-itsoffset 00:00:05")
        // .outputOptions([
        // ])
        // .videoCodec("libx264")
        // .audioCodec("pcm_s16le")
        // .outputOptions([
        //   "-preset ultrafast",
        //   "-tune zerolatency",
        //   "-x264opts keyint=5:min-keyint=",
        //   "-pix_fmt yuv420p",
        //   "-filter_complex",
        //   [
        //     `[0:a]asetpts='(RTCTIME - ${this.SERVER_START_TIME}) / (TB * 1000000)'[out_audio]`,
        //     `[0:v]setpts='(RTCTIME - ${this.SERVER_START_TIME}) / (TB * 1000000)'[out_video]`,
        //   ].join(";")
        // ])

        // // Video output
        // .output(this.videoOutputURL)
        // .outputOptions([
        //   "-map [out_video]",
        // ])
        // .outputFormat("mpegts")

        // // Audio output
        // .output(this.audioOutputURL)
        // .outputOptions([
        //   "-map [out_audio]",
        // ])
        // .outputFormat("mpegts");

      this.ffmpeg.run();
    }
    catch (err) {
      this.error(err);
      this.retry();
    }
  }
}
<commit_msg>Re-input ZMQ stuff upon filtergraph re-init<commit_after>
import winston from "winston";
import zmq from "zmq";
import _ from "underscore";

import InputVertex from "./InputVertex";
import SK from "../../sk";
import m from "../MagicFilters";

const MAIN_SWITCHER_LABEL = "mainSwitcher";
const SPLIT_SCREEN_TOP_SWITCHER_LABEL = "splitScreenTopSwitcher";
const SPLIT_SCREEN_BOTTOM_SWITCHER_LABEL = "splitScreenBottomSwitcher";
const SPLIT_SCREEN_BOTTOM_CROP_LABEL = "splitScreenBottomCrop";
const SPLIT_SCREEN_BOTTOM_OVERLAY_LABEL = "splitScreenBottomOverlay";

// This is hacky as hell but it doesn't seem to work if I send all messages at once.
const ZMQ_SEND_INTERVAL = 1000;

export default class MagicVertex extends InputVertex {
  constructor({id}) {
    super({id});
    this.rewriteStream = false;
    // this.debug = true;
    this.zmqQueue = [];
    this.zmqIsRunning = false;
    this.videoOutputURL = this.transport.getOutputURL();
    this.audioOutputURL = this.transport.getOutputURL();
  }

  cleanup() {
    super.cleanup();
    this.positionVertexHandle.stop();

    if (this.zmqSocket) {
      try {
        this.zmqSocket.unmonitor();
        this.zmqSocket.disconnect(this.zmqAddress);
      }
      catch (e) {
        this.error("Error disconnecting from ZMQ: ", e.stack);
      }
    }
  }

  handleInitialPull() {
    this.currentPositions = this.doc.params.positions;
    this.positionVertexHandle = SK.vertices.watch({id: this.id}).on("updated", ([vertex]) => {
      const newPositions = vertex.params.positions;
      Object.keys(vertex.params.positions).forEach((inputName) => {
        if (!_(this.currentPositions[inputName]).isEqual(newPositions[inputName])) {
          this.currentPositions[inputName] = newPositions[inputName];
          this.doZMQUpdate(inputName);
        }
      });
      this.currentPositions = newPositions;
    });
    this.doc.inputs.forEach((input) => {
      input.sockets.forEach((socket) => {
        socket.url = this.transport.getInputURL();
      });
    });
    const newVertex = {
      inputs: this.doc.inputs,
      outputs: [{
        name: "default",
        sockets: [{
          url: this.videoOutputURL,
          type: "video",
        }, {
          url: this.audioOutputURL,
          type: "audio",
        }]
      }]

    };
    SK.vertices.update(this.doc.id, newVertex)
    .then(() => {
      this.init();
    })
    .catch((err) => {
      winston.error(err);
    });
  }

  _sendNextZMQMessage() {
    // If there's nothing to do, we're not running anymore.
    if (this.zmqQueue.length === 0) {
      this.zmqIsRunning = false;
      return;
    }
    // If we're not connected yet, chill. We'll get called again when we do.
    if (!this.zmqSocket) {
      this.zmqIsRunning = false;
      return;
    }
    // Otherwise, send a message and queue the next one.
    const msg = this.zmqQueue.pop();
    this.info(`ZMQ: ${msg}`);
    this.zmqSocket.send(msg);
    this.zmqIsRunning = true;
    setTimeout(this._sendNextZMQMessage.bind(this), ZMQ_SEND_INTERVAL);
  }

  doZMQUpdate(inputName) {
    const send = (msg) => {
      this.zmqQueue.push(msg);
    };
    const pos = this.currentPositions[inputName];
    if (!pos) {
      return;
    }
    const overlayLabel = this.ffmpeg.filterLabels[`${inputName}-overlay`];
    const scaleLabel = this.ffmpeg.filterLabels[`${inputName}-scale`];
    send(`${overlayLabel} x ${pos.x}`);
    send(`${overlayLabel} y ${pos.y}`);
    send(`${scaleLabel} width ${pos.width}`);
    send(`${scaleLabel} height ${pos.height}`);
    if (!this.zmqIsRunning) {
      this._sendNextZMQMessage();
    }
  }

  init() {
    super.init();
    try {
      this.ffmpeg = this.createffmpeg();
      this.zmqPort = this.getTCP();
      this.zmqAddress = `tcp://*:${this.zmqPort}`;

      const videoInputSockets = [];
      const audioInputSockets = [];
      let currentIdx = 0;

      this.doc.inputs.forEach((input, inputIdx) => {
        input.sockets.forEach((socket, socketIdx) => {
          socket.name = `${input.name}-${socketIdx}`;
          socket.inputName = input.name;
          this.ffmpeg
            .input(socket.url)
            .inputFormat("mpegts")
            .inputOptions([
              "-analyzeduration 10000000",
              "-noaccurate_seek", // Not really sure if this helps, but we certainly don't need
                                  // any kind of accurate seek.
              "-probesize 60000000",
              "-thread_queue_size 16384",
              // "-avioflags direct",
            ]);

          // Set up video input
          if (socket.type === "video") {
            videoInputSockets.push(socket);
            if (input.name === "background") {
              this.ffmpeg.magic(
                `${currentIdx}:v`,
                m.framerate("30"),
                m.scale(1920, 1080),
                `${socket.name}`
              );
            }
            else {
              const pos = this.doc.params.positions[input.name];
              this.ffmpeg.magic(
                `${currentIdx}:v`,
                m.framerate("30"),
                m.scale([pos.width, pos.height], {_label: `${input.name}-scale`}),
                `${socket.name}`
              );
            }
          }

          // Set up audio input
          else if (socket.type === "audio") {
            audioInputSockets.push(socket);
            this.ffmpeg.magic(
              `${currentIdx}:a`,
              m.aresample({
                async: 1,
                min_hard_comp: 0.100000,
                first_pts: 0
              }),
              m.volume({
                _label: `${socket.name}-volume`
              }),
              `${socket.name}-adjusted`
            );
          }

          else {
            throw new Error(`Unknown input type: ${input.type}`);
          }
          currentIdx += 1;
        });
      });

      // Do a series of overlays for the input
      let currentOverlayBG = "background-0";
      videoInputSockets.forEach((socket, i) => {
        if (socket.name === currentOverlayBG) {
          // First one doesn't need to overlay onto nothing. Return.
          return;
        }
        const newOverlayBG = `${socket.name}-overlay`;
        const pos = this.doc.params.positions[socket.inputName];
        this.ffmpeg.magic(
          currentOverlayBG,
          socket.name,
          m.overlay({
            x: pos.x,
            y: pos.y,
            _label: `${socket.inputName}-overlay`
          }),
          newOverlayBG
        );
        currentOverlayBG = newOverlayBG;
      });

      this.ffmpeg
        .outputOptions([
          "-copyts",
          "-copytb 1",
          "-vsync passthrough",

          // I think this flag just causes it to discard the analysis buffer when the stream starts
          "-fflags +nobuffer",

          // "-sws_flags +neighbor",
          "-pix_fmt yuv420p",
          // "-profile:v baseline",
          // "-use_wallclock_as_timestamps 1",
          "-fflags +igndts",
          "-loglevel verbose",
        ])
        .magic(
          currentOverlayBG,
          m.zmq({bind_address: this.zmqAddress}),
          m.framerate("30"),
          "videoOutput"
        )
        .outputOptions([
          "-map [videoOutput]",
          "-b:v 4000k",
          "-allow_skip_frames 1",
          "-preset veryfast",
          "-x264opts keyint=60",
          "-b:v 4000k",
          "-minrate 4000k",
          "-maxrate 4000k",
          // "-bufsize 1835k",
          // "-frame_drop_threshold 60",
        ])
        .once("progress", () => {
          const socket = zmq.socket("req");
          socket.on("connect", (fd, ep) => {
            this.zmqSocket = socket;
            if (!this.zmqIsRunning) {
              this._sendNextZMQMessage();
            }
            // Unfortunately if the filtergraph re-inits, our ZMQ changes don't get preserved. So
            // as soon as ZMQ boots back up, go ahead and inform them of our changes again.
            this.doc.inputs.forEach((input) => {
              this.doZMQUpdate(input.name);
            });
            let idx = 0;
            let label = this.ffmpeg.filterLabels[MAIN_SWITCHER_LABEL];
          });
          socket.on("connect", (fd, ep) => {this.info("connect, endpoint:", ep);});
          socket.on("connect_delay", (fd, ep) => {this.info("connect_delay, endpoint:", ep);});
          socket.on("connect_retry", (fd, ep) => {this.info("connect_retry, endpoint:", ep);});
          socket.on("listen", (fd, ep) => {this.info("listen, endpoint:", ep);});
          socket.on("bind_error", (fd, ep) => {this.info("bind_error, endpoint:", ep);});
          socket.on("accept", (fd, ep) => {this.info("accept, endpoint:", ep);});
          socket.on("accept_error", (fd, ep) => {this.info("accept_error, endpoint:", ep);});
          socket.on("close", (fd, ep) => {this.info("close, endpoint:", ep);});
          socket.on("close_error", (fd, ep) => {this.info("close_error, endpoint:", ep);});
          socket.on("disconnect", (fd, ep) => {this.info("disconnect, endpoint:", ep);});
          socket.on("message", (msg) => {this.info("message: ", msg.toString());});
          socket.monitor(500, 0);
          socket.connect(`tcp://127.0.0.1:${this.zmqPort}`);
        })
        .output(this.videoOutputURL)
        .outputFormat("mpegts")
        .videoCodec("libx264")

        .magic(
          ...audioInputSockets.map(s => `${s.name}-adjusted`),
          m.amix({
            inputs: audioInputSockets.length
          }),
          "audioOutput"
        )

        .output(this.audioOutputURL)
        .outputOptions([
          "-map [audioOutput]"
        ])
        .outputFormat("mpegts")
        .audioCodec("aac");

        // .input(this.inputURL)
        // .inputFormat("mpegts")
        // // .inputOptions("-itsoffset 00:00:05")
        // .outputOptions([
        // ])
        // .videoCodec("libx264")
        // .audioCodec("pcm_s16le")
        // .outputOptions([
        //   "-preset ultrafast",
        //   "-tune zerolatency",
        //   "-x264opts keyint=5:min-keyint=",
        //   "-pix_fmt yuv420p",
        //   "-filter_complex",
        //   [
        //     `[0:a]asetpts='(RTCTIME - ${this.SERVER_START_TIME}) / (TB * 1000000)'[out_audio]`,
        //     `[0:v]setpts='(RTCTIME - ${this.SERVER_START_TIME}) / (TB * 1000000)'[out_video]`,
        //   ].join(";")
        // ])

        // // Video output
        // .output(this.videoOutputURL)
        // .outputOptions([
        //   "-map [out_video]",
        // ])
        // .outputFormat("mpegts")

        // // Audio output
        // .output(this.audioOutputURL)
        // .outputOptions([
        //   "-map [out_audio]",
        // ])
        // .outputFormat("mpegts");

      this.ffmpeg.run();
    }
    catch (err) {
      this.error(err);
      this.retry();
    }
  }
}
<|endoftext|>


<commit_before>const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const glob = require('glob-all');
const path = require('path');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const TerserPlugin = require('terser-webpack-plugin');
const PurgecssPlugin = require('purgecss-webpack-plugin');

module.exports = merge(common, {
  mode: "production",
  optimization: {
    namedChunks: true,
    minimizer: [
      new TerserPlugin({
        extractComments: false,
        terserOptions: {
          output: {
            comments: false,
          }
        }
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  plugins: [
    new PurgecssPlugin({
      paths: glob.sync([
        path.join(__dirname, 'resources/views/**/*.twig'),
        path.join(__dirname, 'resources/js/components/**/*.vue'),
      ]),
      whitelist: [
        'footer-bubble',
        'line-numbers',
        'line-numbers-rows',
        'token',
        'keyword',
        'comment',
        'prolog',
        'doctype',
        'cdata',
        'punctuation',
        'namespace',
        'property',
        'tag',
        'boolean',
        'number',
        'constant',
        'bs-popover-top',
        'modal-backdrop',
        'show',
        'fade',
        'fa-desktop',
        'fa-newspaper',
        'fa-bolt',
        'fa-column',
        'fa-shopping-cart',
        'fa-envelope',
        'fa-map-marker',
        'fa-star-half',
        'fa-chart-bar',
        'fa-wrench',
        'fa-key',
        'fa-minus',
        'fa-columns',
        'pre',
        'code',
        'breadcrumb-fixed',
        'mention',
        'user-deleted',
        'strikeout'
      ],
      whitelistPatterns: [
        /^logo/,
        /^language/,
        /^popover/,
        /^badge/
      ],
      whitelistPatternsChildren: [
        /hire-me$/,
        /dropdown-menu/,
        /^tooltip/,
        /^bs-tooltip/,
        /^ps/,
        /^cool-lightbox/,
        /^chosen/,
        /^tag/,
        /:not/,
        /^pre/,
        /^flatpickr/
      ]
    })
  ]
});
<commit_msg>dodanie do wyjatkow<commit_after>const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const glob = require('glob-all');
const path = require('path');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const TerserPlugin = require('terser-webpack-plugin');
const PurgecssPlugin = require('purgecss-webpack-plugin');

module.exports = merge(common, {
  mode: "production",
  optimization: {
    namedChunks: true,
    minimizer: [
      new TerserPlugin({
        extractComments: false,
        terserOptions: {
          output: {
            comments: false,
          }
        }
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  plugins: [
    new PurgecssPlugin({
      paths: glob.sync([
        path.join(__dirname, 'resources/views/**/*.twig'),
        path.join(__dirname, 'resources/js/components/**/*.vue'),
      ]),
      whitelist: [
        'footer-bubble',
        'line-numbers',
        'line-numbers-rows',
        'token',
        'keyword',
        'comment',
        'prolog',
        'doctype',
        'cdata',
        'punctuation',
        'namespace',
        'property',
        'tag',
        'boolean',
        'number',
        'constant',
        'bs-popover-top',
        'modal-backdrop',
        'show',
        'fade',
        'fa-desktop',
        'fa-newspaper',
        'fa-bolt',
        'fa-column',
        'fa-shopping-cart',
        'fa-envelope',
        'fa-map-marker',
        'fa-star-half',
        'fa-chart-bar',
        'fa-wrench',
        'fa-key',
        'fa-minus',
        'fa-columns',
        'pre',
        'code',
        'breadcrumb-fixed',
        'mention',
        'user-deleted',
        'strikeout',
        'ajax-loader'
      ],
      whitelistPatterns: [
        /^logo/,
        /^language/,
        /^popover/,
        /^badge/
      ],
      whitelistPatternsChildren: [
        /hire-me$/,
        /dropdown-menu/,
        /^tooltip/,
        /^bs-tooltip/,
        /^ps/,
        /^cool-lightbox/,
        /^chosen/,
        /^tag/,
        /:not/,
        /^pre/,
        /^flatpickr/
      ]
    })
  ]
});
<|endoftext|>


<commit_before>define([
    'arches',
    'knockout',
    'viewmodels/card-component',
    'viewmodels/map-editor',
    'bindings/chosen',
    'bindings/codemirror',
    'codemirror/mode/javascript/javascript'
], function(arches, ko, CardComponentViewModel, MapEditorViewModel) {
    var viewModel = function(params) {
        var self = this;

        params.configKeys = [
            'basemap',
            'overlayConfigs',
            'selectSource',
            'selectSourceLayer',
            'selectText',
            'zoom',
            'centerX',
            'centerY'
        ];

        CardComponentViewModel.apply(this, [params]);

        if (self.form && self.tile) {
            params.widgets = self.card.widgets().filter(function(widget) {
                var id = widget.node_id();
                var type = ko.unwrap(self.form.nodeLookup[id].datatype);
                return type === 'geojson-feature-collection';
            });

            for (var widget of params.widgets) {
                widget.config.centerX(self.centerX());
                widget.config.centerY(self.centerY());
                widget.config.zoom(self.zoom());

                widget.config.overlayConfigs.subscribe(function(overlayConfigs) {
                    if (self.overlayConfigs() !== overlayConfigs) {
                        self.overlayConfigs(overlayConfigs)
                    }
                })
            }
        }

        if (this.card.overlaysObservable) {
            params.overlaysObservable = this.card.overlaysObservable;
            params.activeBasemap = this.card.activeBasemap;
        }

        if (this.centerX() == 0 && this.centerY() == 0 && this.zoom() == 0) {
            this.centerX(arches.mapDefaultX);
            this.centerY(arches.mapDefaultY);
            this.zoom(arches.mapDefaultZoom);
        }

        // subscriptions need to stay explicit! DRY-ing will break
        this.centerX.subscribe(function(x) {
            if (params.widgets) {
                for (var widget of params.widgets) {
                    widget.config.centerX(x)
                }
            }
        });
        this.centerY.subscribe(function(y) {
            if (params.widgets) {
                for (var widget of params.widgets) {
                    widget.config.centerY(y)
                }
            }
        });
        this.zoom.subscribe(function(zoom) {
            if (params.widgets) {
                for (var widget of params.widgets) {
                    widget.config.zoom(zoom)
                }
            }
        });
        this.overlayConfigs.subscribe(function(overlayConfigs) {
            if (params.widgets) {
                for (var widget of params.widgets) {
                    widget.config.overlayConfigs(overlayConfigs)
                }
            }
        });
        this.basemap.subscribe(function(basemap) {
            if (params.widgets) {
                for (var widget of params.widgets) {
                    widget.config.basemap(basemap)
                }
            }
        });

        params.overlayConfigs = this.overlayConfigs;
        params.basemap = this.basemap;
        params.zoom = this.zoom;
        params.x = this.centerX;
        params.y = this.centerY;
        params.usePosition = true;

        MapEditorViewModel.apply(this, [params]);

        this.expandSidePanel = ko.computed(function(){
            if (self.tile) {
                return self.tile.hasprovisionaledits() && self.reviewer === true;
            } else {
                return false;
            }
        });

        this.card.allowProvisionalEditRerender(false);

        if (!this.card.overlaysObservable) {
            this.card.overlaysObservable = this.overlays;
            this.card.activeBasemap = this.activeBasemap;
        }
    };
    ko.components.register('map-card', {
        viewModel: viewModel,
        template: {
            require: 'text!templates/views/components/cards/map.htm'
        }
    });
    return viewModel;
});
<commit_msg>nit #6729<commit_after>define([
    'arches',
    'knockout',
    'viewmodels/card-component',
    'viewmodels/map-editor',
    'bindings/chosen',
    'bindings/codemirror',
    'codemirror/mode/javascript/javascript'
], function(arches, ko, CardComponentViewModel, MapEditorViewModel) {
    var viewModel = function(params) {
        var self = this;

        params.configKeys = [
            'basemap',
            'overlayConfigs',
            'selectSource',
            'selectSourceLayer',
            'selectText',
            'zoom',
            'centerX',
            'centerY'
        ];

        CardComponentViewModel.apply(this, [params]);

        if (self.form && self.tile) {
            params.widgets = self.card.widgets().filter(function(widget) {
                var id = widget.node_id();
                var type = ko.unwrap(self.form.nodeLookup[id].datatype);
                return type === 'geojson-feature-collection';
            });

            for (var widget of params.widgets) {
                widget.config.centerX(self.centerX());
                widget.config.centerY(self.centerY());
                widget.config.zoom(self.zoom());
            }
        }

        if (this.card.overlaysObservable) {
            params.overlaysObservable = this.card.overlaysObservable;
            params.activeBasemap = this.card.activeBasemap;
        }

        if (this.centerX() == 0 && this.centerY() == 0 && this.zoom() == 0) {
            this.centerX(arches.mapDefaultX);
            this.centerY(arches.mapDefaultY);
            this.zoom(arches.mapDefaultZoom);
        }

        // subscriptions need to stay explicit! DRY-ing will break
        this.centerX.subscribe(function(x) {
            if (params.widgets) {
                for (var widget of params.widgets) {
                    widget.config.centerX(x)
                }
            }
        });
        this.centerY.subscribe(function(y) {
            if (params.widgets) {
                for (var widget of params.widgets) {
                    widget.config.centerY(y)
                }
            }
        });
        this.zoom.subscribe(function(zoom) {
            if (params.widgets) {
                for (var widget of params.widgets) {
                    widget.config.zoom(zoom)
                }
            }
        });
        this.overlayConfigs.subscribe(function(overlayConfigs) {
            if (params.widgets) {
                for (var widget of params.widgets) {
                    widget.config.overlayConfigs(overlayConfigs)
                }
            }
        });
        this.basemap.subscribe(function(basemap) {
            if (params.widgets) {
                for (var widget of params.widgets) {
                    widget.config.basemap(basemap)
                }
            }
        });

        params.overlayConfigs = this.overlayConfigs;
        params.basemap = this.basemap;
        params.zoom = this.zoom;
        params.x = this.centerX;
        params.y = this.centerY;
        params.usePosition = true;

        MapEditorViewModel.apply(this, [params]);

        this.expandSidePanel = ko.computed(function(){
            if (self.tile) {
                return self.tile.hasprovisionaledits() && self.reviewer === true;
            } else {
                return false;
            }
        });

        this.card.allowProvisionalEditRerender(false);

        if (!this.card.overlaysObservable) {
            this.card.overlaysObservable = this.overlays;
            this.card.activeBasemap = this.activeBasemap;
        }
    };
    ko.components.register('map-card', {
        viewModel: viewModel,
        template: {
            require: 'text!templates/views/components/cards/map.htm'
        }
    });
    return viewModel;
});
<|endoftext|>


<commit_before>var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var ProblemSchema = new Schema({
  problem_id: { type String },
  category: { type String },
  subject: { type String},
  description: { type String },
  flag: { type String },
  score: { type Number },
  path: { type: String }
});

module.exports.Problem = mongoose.model('Problem', ProblemSchema);
<commit_msg>Commonize problem module<commit_after>var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var ProblemSchema = new Schema({
  problem_id: { type: String },
  category: { type: String },
  subject: { type: String},
  description: { type: String },
  flag: { type: String },
  score: { type: Number },
  path: { type: String }
});

module.exports.Problem = mongoose.model('Problem', ProblemSchema);
<|endoftext|>