<commit_before>'use strict';

const util = require('../lib/util');
util.checkPlugin('eslint-plugin-react');

module.exports = {
    plugins: [
        'react',
    ],
    parserOptions: {
        ecmaFeatures: {
            jsx: true,
        },
    },

    // View link below for react rules documentation
    // https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules
    rules: {
        'react/no-unused-prop-types': 'warning',
        'react/no-array-index-key': 'error',

        // Prevent missing displayName in a React component definition
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md
        'react/display-name': [0, {
            ignoreTranspilerName: false,
        }],

        // Forbid certain propTypes (any, array, object)
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-prop-types.md
        'react/forbid-prop-types': [0, {
            forbid: ['any', 'array', 'object'],
        }],

        // Enforce boolean attributes notation in JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md
        'react/jsx-boolean-value': [2, 'never'],

        // Validate closing bracket location in JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md
        'react/jsx-closing-bracket-location': [0],

        // Enforce or disallow spaces inside of curly braces in JSX attributes
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md
        'react/jsx-curly-spacing': [2, 'never', {
            allowMultiline: true,
        }],

        // Enforce event handler naming conventions in JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md
        'react/jsx-handler-names': [0, {
            eventHandlerPrefix: 'handle',
            eventHandlerPropPrefix: 'on',
        }],

        // Validate props indentation in JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent-props.md
        'react/jsx-indent-props': [2, 4],

        // Validate JSX has key prop when in array or iterator
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-key.md
        'react/jsx-key': 0,

        // Limit maximum of props on a single line in JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-max-props-per-line.md
        'react/jsx-max-props-per-line': [0, {
            maximum: 1,
        }],

        // Prevent usage of .bind() in JSX props
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
        'react/jsx-no-bind': [2, {
            ignoreRefs: true,
            allowArrowFunctions: true,
            allowBind: false,
        }],

        // Prevent duplicate props in JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-duplicate-props.md
        'react/jsx-no-duplicate-props': [0, {
            ignoreCase: true,
        }],

        // Prevent usage of unwrapped JSX strings
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-literals.md
        'react/jsx-no-literals': 0,

        // Disallow undeclared variables in JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md
        'react/jsx-no-undef': 2,

        // Enforce PascalCase for user-defined JSX components
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md
        'react/jsx-pascal-case': [2, {
            allowAllCaps: true,
            ignore: [],
        }],

        // Enforce propTypes declarations alphabetical sorting
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-prop-types.md
        'react/sort-prop-types': [0, {
            ignoreCase: false,
            callbacksLast: false,
            requiredFirst: true,
        }],

        // Enforce props alphabetical sorting
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md
        'react/jsx-sort-props': [0, {
            ignoreCase: false,
            callbacksLast: false,
            shorthandFirst: false,
            shorthandLast: false,
        }],

        // Prevent React to be incorrectly marked as unused
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md
        'react/jsx-uses-react': 2,

        // Prevent variables used in JSX to be incorrectly marked as unused
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md
        'react/jsx-uses-vars': 2,

        // Prevent usage of dangerous JSX properties
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger.md
        'react/no-danger': 0,

        // Prevent usage of deprecated methods
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-deprecated.md
        'react/no-deprecated': 2,

        // Prevent usage of setState in componentDidMount
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md
        'react/no-did-mount-set-state': 1,

        // Prevent usage of setState in componentDidUpdate
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md
        'react/no-did-update-set-state': 2,

        // Prevent direct mutation of this.state
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-direct-mutation-state.md
        'react/no-direct-mutation-state': 0,

        // Prevent usage of isMounted
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md
        'react/no-is-mounted': 2,

        // Prevent multiple component definition per file
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md
        'react/no-multi-comp': [1, {
            ignoreStateless: true,
        }],

        // Prevent usage of setState
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-set-state.md
        'react/no-set-state': 0,

        // Prevent using string references
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md
        'react/no-string-refs': 2,

        // Prevent usage of unknown DOM property
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md
        'react/no-unknown-property': 2,

        // Require ES6 class declarations over React.createClass
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md
        'react/prefer-es6-class': [2, 'always'],

        // Require stateless functions when not using lifecycle methods, setState or ref
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md
        'react/prefer-stateless-function': 0,

        // Prevent missing props validation in a React component definition
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md
        'react/prop-types': [1, {
            ignore: [],
            customValidators: [],
        }],

        // Prevent missing React when using JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md
        'react/react-in-jsx-scope': 2,

        // Require render() methods to return something
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-render-return.md
        'react/require-render-return': 2,

        // Prevent extra closing tags for components without children
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md
        'react/self-closing-comp': 2,

        // Enforce spaces before the closing bracket of self-closing JSX elements
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-space-before-closing.md
        'react/jsx-space-before-closing': [2, 'always'],

        // Enforce component methods order
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md
        'react/sort-comp': [2, {
            order: [
                'static-methods',
                'lifecycle',
                '/^on.+$/',
                '/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/',
                'everything-else',
                '/^render.+$/',
                'render',
            ],
        }],

        // Prevent missing parentheses around multilines JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-wrap-multilines.md
        // @Attention it is opposite to no-extra-parens
        'react/jsx-wrap-multilines': [0, {
            declaration: true,
            assignment: true,
            return: true,
        }],

        // Require that the first prop in a JSX element be on a new line when the element is multiline
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-first-prop-new-line.md
        'react/jsx-first-prop-new-line': [1, 'multiline'],

        // Enforce spacing around jsx equals signs
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-equals-spacing.md
        'react/jsx-equals-spacing': [2, 'never'],

        // Enforce JSX indentation
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent.md
        'react/jsx-indent': [2, 4],

        // Disallow target="_blank" on links
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-target-blank.md
        'react/jsx-no-target-blank': 2,

        // only .jsx files may have JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-filename-extension.md
        'react/jsx-filename-extension': ['error', {
            extensions: ['.jsx'],
        }],

        // prevent accidental JS comments from being injected into JSX as text
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-comment-textnodes.md
        'react/jsx-no-comment-textnodes': 'error',

        // disallow using React.render/ReactDOM.render's return value
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-render-return-value.md
        'react/no-render-return-value': 'error',

        // require a shouldComponentUpdate method, or PureRenderMixin
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-optimization.md
        'react/require-optimization': ['off', {
            allowDecorators: [],
        }],

        // warn against using findDOMNode()
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-find-dom-node.md
        // @Maybe To be error. But how to get the offsetLeft of a dom?
        'react/no-find-dom-node': 'off',

        // Forbid certain props on Components
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-component-props.md
        'react/forbid-component-props': ['off', {
            forbid: [],
        }],

        // Prevent problem with children and props.dangerouslySetInnerHTML
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger-with-children.md
        // TODO: enable, semver-major
        'react/no-danger-with-children': 'off',

        'react/button-has-type': 'error',
        'react/no-access-state-in-setstate': 'error',
        'react/no-redundant-should-component-update': 'error',
        'react/style-prop-object': 'error',
        'react/no-unescaped-entities': 'warning',
        'react/no-this-in-sfc': 'error',
        'react/boolean-prop-naming': 'off',
        'react/destructuring-assignment': 'off',
    },
};
<commit_msg>fix: warning => warn<commit_after>'use strict';

const util = require('../lib/util');
util.checkPlugin('eslint-plugin-react');

module.exports = {
    plugins: [
        'react',
    ],
    parserOptions: {
        ecmaFeatures: {
            jsx: true,
        },
    },

    // View link below for react rules documentation
    // https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules
    rules: {
        'react/no-unused-prop-types': 'warn',
        'react/no-array-index-key': 'error',

        // Prevent missing displayName in a React component definition
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md
        'react/display-name': [0, {
            ignoreTranspilerName: false,
        }],

        // Forbid certain propTypes (any, array, object)
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-prop-types.md
        'react/forbid-prop-types': [0, {
            forbid: ['any', 'array', 'object'],
        }],

        // Enforce boolean attributes notation in JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md
        'react/jsx-boolean-value': [2, 'never'],

        // Validate closing bracket location in JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md
        'react/jsx-closing-bracket-location': [0],

        // Enforce or disallow spaces inside of curly braces in JSX attributes
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md
        'react/jsx-curly-spacing': [2, 'never', {
            allowMultiline: true,
        }],

        // Enforce event handler naming conventions in JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md
        'react/jsx-handler-names': [0, {
            eventHandlerPrefix: 'handle',
            eventHandlerPropPrefix: 'on',
        }],

        // Validate props indentation in JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent-props.md
        'react/jsx-indent-props': [2, 4],

        // Validate JSX has key prop when in array or iterator
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-key.md
        'react/jsx-key': 0,

        // Limit maximum of props on a single line in JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-max-props-per-line.md
        'react/jsx-max-props-per-line': [0, {
            maximum: 1,
        }],

        // Prevent usage of .bind() in JSX props
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
        'react/jsx-no-bind': [2, {
            ignoreRefs: true,
            allowArrowFunctions: true,
            allowBind: false,
        }],

        // Prevent duplicate props in JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-duplicate-props.md
        'react/jsx-no-duplicate-props': [0, {
            ignoreCase: true,
        }],

        // Prevent usage of unwrapped JSX strings
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-literals.md
        'react/jsx-no-literals': 0,

        // Disallow undeclared variables in JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md
        'react/jsx-no-undef': 2,

        // Enforce PascalCase for user-defined JSX components
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md
        'react/jsx-pascal-case': [2, {
            allowAllCaps: true,
            ignore: [],
        }],

        // Enforce propTypes declarations alphabetical sorting
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-prop-types.md
        'react/sort-prop-types': [0, {
            ignoreCase: false,
            callbacksLast: false,
            requiredFirst: true,
        }],

        // Enforce props alphabetical sorting
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md
        'react/jsx-sort-props': [0, {
            ignoreCase: false,
            callbacksLast: false,
            shorthandFirst: false,
            shorthandLast: false,
        }],

        // Prevent React to be incorrectly marked as unused
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md
        'react/jsx-uses-react': 2,

        // Prevent variables used in JSX to be incorrectly marked as unused
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md
        'react/jsx-uses-vars': 2,

        // Prevent usage of dangerous JSX properties
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger.md
        'react/no-danger': 0,

        // Prevent usage of deprecated methods
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-deprecated.md
        'react/no-deprecated': 2,

        // Prevent usage of setState in componentDidMount
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md
        'react/no-did-mount-set-state': 1,

        // Prevent usage of setState in componentDidUpdate
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md
        'react/no-did-update-set-state': 2,

        // Prevent direct mutation of this.state
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-direct-mutation-state.md
        'react/no-direct-mutation-state': 0,

        // Prevent usage of isMounted
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md
        'react/no-is-mounted': 2,

        // Prevent multiple component definition per file
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md
        'react/no-multi-comp': [1, {
            ignoreStateless: true,
        }],

        // Prevent usage of setState
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-set-state.md
        'react/no-set-state': 0,

        // Prevent using string references
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md
        'react/no-string-refs': 2,

        // Prevent usage of unknown DOM property
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md
        'react/no-unknown-property': 2,

        // Require ES6 class declarations over React.createClass
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md
        'react/prefer-es6-class': [2, 'always'],

        // Require stateless functions when not using lifecycle methods, setState or ref
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md
        'react/prefer-stateless-function': 0,

        // Prevent missing props validation in a React component definition
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md
        'react/prop-types': [1, {
            ignore: [],
            customValidators: [],
        }],

        // Prevent missing React when using JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md
        'react/react-in-jsx-scope': 2,

        // Require render() methods to return something
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-render-return.md
        'react/require-render-return': 2,

        // Prevent extra closing tags for components without children
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md
        'react/self-closing-comp': 2,

        // Enforce spaces before the closing bracket of self-closing JSX elements
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-space-before-closing.md
        'react/jsx-space-before-closing': [2, 'always'],

        // Enforce component methods order
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md
        'react/sort-comp': [2, {
            order: [
                'static-methods',
                'lifecycle',
                '/^on.+$/',
                '/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/',
                'everything-else',
                '/^render.+$/',
                'render',
            ],
        }],

        // Prevent missing parentheses around multilines JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-wrap-multilines.md
        // @Attention it is opposite to no-extra-parens
        'react/jsx-wrap-multilines': [0, {
            declaration: true,
            assignment: true,
            return: true,
        }],

        // Require that the first prop in a JSX element be on a new line when the element is multiline
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-first-prop-new-line.md
        'react/jsx-first-prop-new-line': [1, 'multiline'],

        // Enforce spacing around jsx equals signs
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-equals-spacing.md
        'react/jsx-equals-spacing': [2, 'never'],

        // Enforce JSX indentation
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent.md
        'react/jsx-indent': [2, 4],

        // Disallow target="_blank" on links
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-target-blank.md
        'react/jsx-no-target-blank': 2,

        // only .jsx files may have JSX
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-filename-extension.md
        'react/jsx-filename-extension': ['error', {
            extensions: ['.jsx'],
        }],

        // prevent accidental JS comments from being injected into JSX as text
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-comment-textnodes.md
        'react/jsx-no-comment-textnodes': 'error',

        // disallow using React.render/ReactDOM.render's return value
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-render-return-value.md
        'react/no-render-return-value': 'error',

        // require a shouldComponentUpdate method, or PureRenderMixin
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-optimization.md
        'react/require-optimization': ['off', {
            allowDecorators: [],
        }],

        // warn against using findDOMNode()
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-find-dom-node.md
        // @Maybe To be error. But how to get the offsetLeft of a dom?
        'react/no-find-dom-node': 'off',

        // Forbid certain props on Components
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-component-props.md
        'react/forbid-component-props': ['off', {
            forbid: [],
        }],

        // Prevent problem with children and props.dangerouslySetInnerHTML
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger-with-children.md
        // TODO: enable, semver-major
        'react/no-danger-with-children': 'off',

        'react/button-has-type': 'error',
        'react/no-access-state-in-setstate': 'error',
        'react/no-redundant-should-component-update': 'error',
        'react/style-prop-object': 'error',
        'react/no-unescaped-entities': 'warn',
        'react/no-this-in-sfc': 'error',
        'react/boolean-prop-naming': 'off',
        'react/destructuring-assignment': 'off',
    },
};
<|endoftext|>


<commit_before>'use strict';

var _ = require('isa.js');
var Route = require('../util/Route');

function getActions( refs ){
	return refs.filter( function( ref ){
		return _.isFunction( ref );
	} );
}
function getOptions( refs ){
	return refs.length > 0 && !_.isFunction(refs[refs.length-1]) ? (refs[refs.length-1] || {}) : {};
}

exports.addPath = function(key, path, refs){
	var self = this;

	var actions = getActions( refs );
	var config = getOptions( refs );

	if( config.options ){
		self.options( this.copyPath( path, { unprotected: true } ), function( request, content, cb ){
			cb( null, 'OK', { headers: { Allow: key } } );
		} );
	}

	actions.forEach( function(action){
		self.mapping[ key ].push( new Route( self.config, path, config,
			function(request, content, callback){
				if(action.length === 3) {
					action(request, content, function(err, result, resOptions){
						callback(err, { contentType: config.contentType, result: result, resOptions: resOptions || {} } );
					});
				} else {
					var result = action(request, content);
					var err;
					callback( err, { contentType: config.contentType, result: result, resOptions: {} } );
				}
			}, _
		) );
	} );
};

exports.head = function headRest(path, ...refs){
	this.addPath("HEAD", path, refs );
};
exports.get = function getRest(path, ...refs){
	this.addPath("GET", path, refs );
};
exports.post = function postRest(path, ...refs){
	this.addPath("POST", path, refs );
};
exports.put = function putRest(path, ...refs){
	this.addPath("PUT", path, refs );
};
exports.patch = function patchRest(path, ...refs){
	this.addPath("PATCH", path, refs );
};
exports.options = function optionsRest(path, ...refs){
	this.addPath("OPTIONS", path, refs );
};
exports.del = function deleteRest(path, ...refs){
	this.addPath("DELETE", path, refs );
};
exports.assign = function headRest(methods, path, ...refs){
	var self = this;

	if( Array.isArray(methods) ){
		methods.forEach(function(method){
			method = method.toLowerCase();
			method = (method === 'delete') ? 'del' : method;
			if( !self[ method ] )
				throw new Error('Not known rest type', method);
			self[ method ](path, refs);
		});
	}
	else if( methods === '*' )
		self.assign( ['head', 'get', 'post', 'put', 'patch', 'options', 'delete'], path, refs );
	else
		throw new Error('Not correct given methods', methods);
};
<commit_msg>refinements<commit_after>'use strict';

var _ = require('isa.js');
var Route = require('../util/Route');

exports.addPath = function(key, path, action, config){
	var self = this;

	config = config || {};

	if( config.options ){
		self.options( this.copyPath( path, { unprotected: true } ), function( request, content, cb ){
			cb( null, 'OK', { headers: { Allow: key } } );
		} );
	}

	self.mapping[ key ].push( new Route( self.config, path, config,
		function(request, content, callback){
			if(action.length === 3) {
				action(request, content, function(err, result, resOptions){
					callback(err, { contentType: config.contentType, result: result, resOptions: resOptions || {} } );
				});
			} else {
				var result = action(request, content);
				var err;
				callback( err, { contentType: config.contentType, result: result, resOptions: {} } );
			}
		}
	) );
};

exports.head = function headRest(path, action, option){
	this.addPath("HEAD", path, action, option );
};
exports.get = function getRest(path, action, option){
	this.addPath("GET", path, action, option );
};
exports.post = function postRest(path, action, option){
	this.addPath("POST", path, action, option );
};
exports.put = function putRest(path, action, option){
	this.addPath("PUT", path, action, option );
};
exports.patch = function patchRest(path, action, option){
	this.addPath("PATCH", path, action, option );
};
exports.options = function optionsRest(path, action, option){
	this.addPath("OPTIONS", path, action, option );
};
exports.del = function deleteRest(path, action, option){
	this.addPath("DELETE", path, action, option );
};
exports.assign = function headRest(methods, path, action, option){
	var self = this;

	if( Array.isArray(methods) ){
		methods.forEach(function(method){
			method = method.toLowerCase();
			method = (method === 'delete') ? 'del' : method;
			if( !self[ method ] )
				throw new Error('Not known rest type', method);
			self[ method ](path, action, option);
		});
	}
	else if( methods === '*' )
		self.assign( ['head', 'get', 'post', 'put', 'patch', 'options', 'delete'], path, action, option);
	else
		throw new Error('Not correct given methods', methods);
};
<|endoftext|>


<commit_before>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

type FrameType = 'auxiliary' | 'top-level' | 'nested' | 'none';
type VisibilityState = 'hidden' | 'visible' | 'prerender' | 'unloaded';

declare class WindowClient extends Client {
  visibilityState: VisibilityState,
  focused: boolean,
  focus(): Promise<WindowClient>,
  navigate(url: string): Promise<WindowClient>,
}

declare class Client {
  id: string,
  reserved: boolean,
  url: string,
  frameType: FrameType,
  postMessage(message: any, transfer?: Iterator<any> | Array<any>): void,
}

declare class ExtendableEvent extends Event {
  waitUntil(f: Promise<mixed>): void,
}

type ForeignFetchOptions = {
  scopes: Iterator<string>,
  origins: Iterator<string>,
};

declare class InstallEvent extends ExtendableEvent {
  registerForeignFetch(options: ForeignFetchOptions): void,
}

declare class FetchEvent extends ExtendableEvent {
  request: Request,
  clientId: string,
  isReload: boolean,
  respondWith(response: Response | Promise<Response>): void,
  preloadResponse: Promise<?Response>,
}

type ClientType = 'window' | 'worker' | 'sharedworker' | 'all';
type ClientQueryOptions = {
  includeUncontrolled?: boolean,
  includeReserved?: boolean,
  type?: ClientType,
};

declare class Clients {
  get(id: string): Promise<?Client>,
  matchAll(options?: ClientQueryOptions): Promise<Array<Client>>,
  openWindow(url: string): Promise<?WindowClient>,
  claim(): Promise<void>,
}

type ServiceWorkerState = 'installing'
  | 'installed'
  | 'activating'
  | 'activated'
  | 'redundant';

declare class ServiceWorker extends EventTarget {
  scriptURL: string,
  state: ServiceWorkerState,

  postMessage(message: any, transfer?: Iterator<any>): void,

  onstatechange?: EventHandler,
}

declare class NavigationPreloadState {
  enabled: boolean,
  headerValue: string,
}

declare class NavigationPreloadManager {
  enable: Promise<void>,
  disable: Promise<void>,
  setHeaderValue(value: string): Promise<void>,
  getState: Promise<NavigationPreloadState>,
}

declare class ServiceWorkerRegistration extends EventTarget {
  installing: ?ServiceWorker,
  waiting: ?ServiceWorker,
  active: ?ServiceWorker,
  navigationPreload: NavigationPreloadManager,

  scope: string,

  update(): Promise<void>,
  unregister(): Promise<boolean>,

  onupdatefound?: EventHandler,
}

type WorkerType = 'classic' | 'module';

type RegistrationOptions = {
  scope: string,
  type: WorkerType,
};

declare class ServiceWorkerContainer extends EventTarget {
  controller: ?ServiceWorker,
  ready: Promise<ServiceWorkerRegistration>,

  getRegistration(clientURL: string): Promise<?ServiceWorkerRegistration>,
  getRegistrations(): Promise<Iterator<ServiceWorkerRegistration>>,
  register(
    scriptURL: string,
    options: ?RegistrationOptions
  ): Promise<ServiceWorkerRegistration>,
  startMessages(): void,

  oncontrollerchange?: EventHandler,
  onmessage?: EventHandler,
}

/**
 * This feature has been removed from the Web standards.
 */
declare class ServiceWorkerMessageEvent extends Event {
  data: any,
  lastEventId: string,
  origin: string,
  ports: Array<MessagePort>,
  source: ?(ServiceWorker | MessagePort),
}

declare class ExtendableMessageEvent extends ExtendableEvent {
  data: any,
  lastEventId: string,
  origin: string,
  ports: Array<MessagePort>,
  source: ?(ServiceWorker | MessagePort),
}

type CacheQueryOptions = {
  ignoreSearch?: boolean,
  ignoreMethod?: boolean,
  ignoreVary?: boolean,
  cacheName?: string,
}

declare class Cache {
  match(request: RequestInfo, options?: CacheQueryOptions): Promise<Response>,
  matchAll(
    request: RequestInfo,
    options?: CacheQueryOptions
  ): Promise<Array<Response>>,
  add(request: RequestInfo): Promise<void>,
  addAll(requests: Array<RequestInfo>): Promise<void>,
  put(request: RequestInfo, response: Response): Promise<void>,
  delete(request: RequestInfo, options?: CacheQueryOptions): Promise<boolean>,
  keys(
    request?: RequestInfo,
    options?: CacheQueryOptions
  ): Promise<Array<Request>>,
}

declare class CacheStorage {
  match(request: RequestInfo, options?: CacheQueryOptions): Promise<Response>,
  has(cacheName: string): Promise<true>,
  open(cacheName: string): Promise<Cache>,
  delete(cacheName: string): Promise<boolean>,
  keys(): Promise<Array<string>>,
}

// Service worker global scope
// https://www.w3.org/TR/service-workers/#service-worker-global-scope
declare var clients: Clients;
declare var caches: CacheStorage;
declare var registration: ServiceWorkerRegistration;
declare function skipWaiting(): Promise<void>;
declare var onactivate: ?EventHandler;
declare var oninstall: ?EventHandler;
declare var onfetch: ?EventHandler;
declare var onforeignfetch: ?EventHandler;
declare var onmessage: ?EventHandler;
<commit_msg>[PR] Add type definitions for service worker PushManager<commit_after>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

type FrameType = 'auxiliary' | 'top-level' | 'nested' | 'none';
type VisibilityState = 'hidden' | 'visible' | 'prerender' | 'unloaded';

declare class WindowClient extends Client {
  visibilityState: VisibilityState,
  focused: boolean,
  focus(): Promise<WindowClient>,
  navigate(url: string): Promise<WindowClient>,
}

declare class Client {
  id: string,
  reserved: boolean,
  url: string,
  frameType: FrameType,
  postMessage(message: any, transfer?: Iterator<any> | Array<any>): void,
}

declare class ExtendableEvent extends Event {
  waitUntil(f: Promise<mixed>): void,
}

type ForeignFetchOptions = {
  scopes: Iterator<string>,
  origins: Iterator<string>,
};

declare class InstallEvent extends ExtendableEvent {
  registerForeignFetch(options: ForeignFetchOptions): void,
}

declare class FetchEvent extends ExtendableEvent {
  request: Request,
  clientId: string,
  isReload: boolean,
  respondWith(response: Response | Promise<Response>): void,
  preloadResponse: Promise<?Response>,
}

type ClientType = 'window' | 'worker' | 'sharedworker' | 'all';
type ClientQueryOptions = {
  includeUncontrolled?: boolean,
  includeReserved?: boolean,
  type?: ClientType,
};

declare class Clients {
  get(id: string): Promise<?Client>,
  matchAll(options?: ClientQueryOptions): Promise<Array<Client>>,
  openWindow(url: string): Promise<?WindowClient>,
  claim(): Promise<void>,
}

type ServiceWorkerState = 'installing'
  | 'installed'
  | 'activating'
  | 'activated'
  | 'redundant';

declare class ServiceWorker extends EventTarget {
  scriptURL: string,
  state: ServiceWorkerState,

  postMessage(message: any, transfer?: Iterator<any>): void,

  onstatechange?: EventHandler,
}

declare class NavigationPreloadState {
  enabled: boolean,
  headerValue: string,
}

declare class NavigationPreloadManager {
  enable: Promise<void>,
  disable: Promise<void>,
  setHeaderValue(value: string): Promise<void>,
  getState: Promise<NavigationPreloadState>,
}

type PushSubscriptionOptions = {
  userVisibleOnly?: boolean,
  applicationServerKey?: string | ArrayBuffer | $ArrayBufferView,
}

declare class PushSubscriptionJSON {
  endpoint: string,
  expirationTime: number | null,
  keys: {[string]: string};
}

declare class PushSubscription {
  +endpoint: string,
  +expirationTime: number | null,
  +options: PushSubscriptionOptions,
  getKey(name: string): ArrayBuffer | null,
  toJSON(): PushSubscriptionJSON,
  unsubscribe(): Promise<boolean>,
}

declare class PushManager {
  +supportedContentEncodings: Array<string>,
  subscribe(options?: PushSubscriptionOptions): Promise<PushSubscription>,
  getSubscription(): Promise<PushSubscription | null>,
  permissionState(options?: PushSubscriptionOptions): Promise<'granted' | 'denied' | 'prompt'>,
}

type ServiceWorkerUpdateViaCache = 'imports' | 'all' | 'none';

declare class ServiceWorkerRegistration extends EventTarget {
  +installing: ?ServiceWorker,
  +waiting: ?ServiceWorker,
  +active: ?ServiceWorker,
  +navigationPreload: NavigationPreloadManager,
  +scope: string,
  +updateViaCache: ServiceWorkerUpdateViaCache,
  +pushManager: PushManager,

  update(): Promise<void>,
  unregister(): Promise<boolean>,

  onupdatefound?: EventHandler,
}

type WorkerType = 'classic' | 'module';

type RegistrationOptions = {
  scope?: string,
  type?: WorkerType,
  updateViaCache?: ServiceWorkerUpdateViaCache,
};

declare class ServiceWorkerContainer extends EventTarget {
  +controller: ?ServiceWorker,
  +ready: Promise<ServiceWorkerRegistration>,

  getRegistration(clientURL: string): Promise<?ServiceWorkerRegistration>,
  getRegistrations(): Promise<Iterator<ServiceWorkerRegistration>>,
  register(
    scriptURL: string,
    options?: RegistrationOptions
  ): Promise<ServiceWorkerRegistration>,
  startMessages(): void,

  oncontrollerchange?: EventHandler,
  onmessage?: EventHandler,
  onmessageerror?: EventHandler,
}

/**
 * This feature has been removed from the Web standards.
 */
declare class ServiceWorkerMessageEvent extends Event {
  data: any,
  lastEventId: string,
  origin: string,
  ports: Array<MessagePort>,
  source: ?(ServiceWorker | MessagePort),
}

declare class ExtendableMessageEvent extends ExtendableEvent {
  data: any,
  lastEventId: string,
  origin: string,
  ports: Array<MessagePort>,
  source: ?(ServiceWorker | MessagePort),
}

type CacheQueryOptions = {
  ignoreSearch?: boolean,
  ignoreMethod?: boolean,
  ignoreVary?: boolean,
  cacheName?: string,
}

declare class Cache {
  match(request: RequestInfo, options?: CacheQueryOptions): Promise<Response>,
  matchAll(
    request: RequestInfo,
    options?: CacheQueryOptions
  ): Promise<Array<Response>>,
  add(request: RequestInfo): Promise<void>,
  addAll(requests: Array<RequestInfo>): Promise<void>,
  put(request: RequestInfo, response: Response): Promise<void>,
  delete(request: RequestInfo, options?: CacheQueryOptions): Promise<boolean>,
  keys(
    request?: RequestInfo,
    options?: CacheQueryOptions
  ): Promise<Array<Request>>,
}

declare class CacheStorage {
  match(request: RequestInfo, options?: CacheQueryOptions): Promise<Response>,
  has(cacheName: string): Promise<true>,
  open(cacheName: string): Promise<Cache>,
  delete(cacheName: string): Promise<boolean>,
  keys(): Promise<Array<string>>,
}

// Service worker global scope
// https://www.w3.org/TR/service-workers/#service-worker-global-scope
declare var clients: Clients;
declare var caches: CacheStorage;
declare var registration: ServiceWorkerRegistration;
declare function skipWaiting(): Promise<void>;
declare var onactivate: ?EventHandler;
declare var oninstall: ?EventHandler;
declare var onfetch: ?EventHandler;
declare var onforeignfetch: ?EventHandler;
declare var onmessage: ?EventHandler;
<|endoftext|>


<commit_before>var loadScript = require('./loadScript.js'),
    settings = require('./settings.js'),
    url2ClassPath = require('./url2ClassPath.js'),
    domAdapter = require('./domAdapter.js'),
    services = require('./services.js');

function isErrorCode(code) {
    code = String(code);

    return code.charAt(0) === "4" || code.charAt(0) === "5";
}

function getPage(url, onPageLoaded, onDataLoaded) {
    var PageClass,
        params,
        positionOfQuestionMark,
        classPath,
        service;

    function doPageLoadedCallback() {
        var error;

        try {
            PageClass = require(classPath);
        } catch (err) {
            error = err;
        }
        onPageLoaded(error || null, PageClass);
    }

    function doDataLoadedCallback(code, response) {
        if (isErrorCode(code)) {
            onDataLoaded(response);
        } else {
            onDataLoaded(null, response);
        }
    }

    onPageLoaded = onPageLoaded || function () {};
    onDataLoaded = onDataLoaded || function () {};
    positionOfQuestionMark = url.search(/\?/);
    if (positionOfQuestionMark === -1) {
        params = {};
    } else {
        params = url.substr(positionOfQuestionMark + 1);
        url = url.substr(0, positionOfQuestionMark);
        params = domAdapter.QueryParse(params);
    }
    url = settings.baseURL + '/pages/' + url;
    url = url.replace(/\/\//g, '/');    // remove double slashes
    classPath = url2ClassPath(url);
    try {
        PageClass = require(classPath);
        onPageLoaded(null, PageClass);
    } catch (err) {
        loadScript(url, doPageLoadedCallback);
    }
    service = services.getService(url);
    if (service) {
        service.GET(params, doDataLoadedCallback);
    } else {
        doDataLoadedCallback(400, 'Cannot load data: There is no service for this page that provides a GET method.');  // fake HTTP 400
    }
}

module.exports = getPage;<commit_msg>removed check for double slashes (they've stripped out the double slashes from the protocol)<commit_after>var loadScript = require('./loadScript.js'),
    settings = require('./settings.js'),
    url2ClassPath = require('./url2ClassPath.js'),
    domAdapter = require('./domAdapter.js'),
    services = require('./services.js');

function isErrorCode(code) {
    code = String(code);

    return code.charAt(0) === "4" || code.charAt(0) === "5";
}

function getPage(url, onPageLoaded, onDataLoaded) {
    var PageClass,
        params,
        positionOfQuestionMark,
        classPath,
        service;

    function doPageLoadedCallback() {
        var error;

        try {
            PageClass = require(classPath);
        } catch (err) {
            error = err;
        }
        onPageLoaded(error || null, PageClass);
    }

    function doDataLoadedCallback(code, response) {
        if (isErrorCode(code)) {
            onDataLoaded(response);
        } else {
            onDataLoaded(null, response);
        }
    }

    onPageLoaded = onPageLoaded || function () {};
    onDataLoaded = onDataLoaded || function () {};
    positionOfQuestionMark = url.search(/\?/);
    if (positionOfQuestionMark === -1) {
        params = {};
    } else {
        params = url.substr(positionOfQuestionMark + 1);
        url = url.substr(0, positionOfQuestionMark);
        params = domAdapter.QueryParse(params);
    }
    url = settings.baseURL + '/pages/' + url;
    classPath = url2ClassPath(url);
    try {
        PageClass = require(classPath);
        onPageLoaded(null, PageClass);
    } catch (err) {
        loadScript(url, doPageLoadedCallback);
    }
    service = services.getService(url);
    if (service) {
        service.GET(params, doDataLoadedCallback);
    } else {
        doDataLoadedCallback(400, 'Cannot load data: There is no service for this page that provides a GET method.');  // fake HTTP 400
    }
}

module.exports = getPage;<|endoftext|>


<commit_before>"use strict";

exports.init = function (y, config, messages, cron, logger) {
	messages.add('kill_reboot_confirm', "I'll be back.");

	y.on('message', function (message) {
		if (/!reboot\b/i.test(message.plainBody())) {
			logger.info("!reboot");

			var rebootConfirmMessage = messages.get('kill_reboot_confirm');

			y.sendMessage(function (err, msg) {
				logger.info('sending reboot confirm message: OK');
				var thread = y.thread(msg.threadId());
				thread.setProperty('type', 'reboot');
				thread.setProperty('status', 'open');
				y.persistThread(thread);
				logger.warn('exiting');
				process.exit(0);

			}, rebootConfirmMessage, { 'reply_to' : message.id() });
		}
	});

	y.on('threadloaded', function (thread) {
		console.log('thl', thread);
	};

};
<commit_msg>reboot<commit_after>"use strict";

exports.init = function (y, config, messages, cron, logger) {
	messages.add('kill_reboot_confirm', "I'll be back.");

	y.on('message', function (message) {
		if (/!reboot\b/i.test(message.plainBody())) {
			logger.info("!reboot");

			var rebootConfirmMessage = messages.get('kill_reboot_confirm');

			y.sendMessage(function (err, msg) {
				logger.info('sending reboot confirm message: OK');
				var thread = y.thread(msg.threadId());
				thread.setProperty('type', 'reboot');
				thread.setProperty('status', 'open');
				y.persistThread(thread);
				logger.warn('exiting');
				process.exit(0);

			}, rebootConfirmMessage, { 'reply_to' : message.id() });
		}
	});

	y.on('threadloaded', function (thread) {
		console.log('thl', thread);
	});

};
<|endoftext|>


<commit_before>import { Textcomplete } from "@textcomplete/core"
import { TextareaEditor } from "@textcomplete/textarea"
import { component, html, useCallback, useEffect, useRef, useState } from "haunted"
import { nothing } from "lit-html"
import { repeat } from "lit-html/directives/repeat.js"
import { authStore, useStore } from "../ctx.js"
import { ref } from "../directives/ref.js"
import { request, subscribe } from "../http.js"
import "./intersectable-comp.js"
import "./post-item.js"
import "./toast-item.js"

const pageSize = 10

export default function () {
    return html`<home-page></home-page>`
}

function HomePage() {
    const [timeline, setTimeline] = useState([])
    const [timelineEndCursor, setTimelineEndCursor] = useState(null)
    const [fetchingTimeline, setFetchingTimeline] = useState(timeline.length === 0)
    const [err, setErr] = useState(null)
    const [loadingMore, setLoadingMore] = useState(false)
    const [noMoreTimelineItems, setNoMoreTimelineItems] = useState(false)
    const [endReached, setEndReached] = useState(false)
    const [queue, setQueue] = useState([])
    const [toast, setToast] = useState(null)

    const onTimelineItemCreated = useCallback(ev => {
        const payload = ev.detail
        setTimeline(tt => [payload, ...queue, ...tt])
        setQueue([])
    }, [queue])

    const onNewTimelineItemArrive = useCallback(ti => {
        setQueue(tt => [ti, ...tt])
    }, [])

    const onRemovedFromTimeline = useCallback(ev => {
        const payload = ev.detail
        setTimeline(tt => tt.filter(ti => ti.id !== payload.timelineItemID))
    }, [])

    const onPostDeleted = useCallback(ev => {
        const payload = ev.detail
        setTimeline(tt => tt.filter(ti => ti.post.id !== payload.id))
    }, [])

    const onQueueBtnClick = useCallback(() => {
        setTimeline(tt => [...queue, ...tt])
        setQueue([])
    }, [queue])

    const loadMore = useCallback(() => {
        if (loadingMore || noMoreTimelineItems) {
            return
        }

        setLoadingMore(true)
        fetchTimeline(timelineEndCursor).then(({ items: timeline, endCursor }) => {
            setTimeline(tt => [...tt, ...timeline])
            setTimelineEndCursor(endCursor)

            if (timeline.length < pageSize) {
                setNoMoreTimelineItems(true)
                setEndReached(true)
            }
        }, err => {
            const msg = "could not fetch more timeline items: " + err.message
            console.error(msg)
            setToast({ type: "error", content: msg })
        }).finally(() => {
            setLoadingMore(false)
        })
    }, [loadingMore, noMoreTimelineItems, timelineEndCursor])

    useEffect(() => {
        setFetchingTimeline(true)
        fetchTimeline().then(({ items: timeline, endCursor }) => {
            setTimeline(timeline)
            setTimelineEndCursor(endCursor)

            if (timeline.length < pageSize) {
                setNoMoreTimelineItems(true)
            }
        }, err => {
            console.error("could not fetch timeline:", err)
            setErr(err)
        }).finally(() => {
            setFetchingTimeline(false)
        })
    }, [])

    useEffect(() => subscribeToTimeline(onNewTimelineItemArrive), [])

    return html`

Timeline

            <post-form @timeline-item-created=${onTimelineItemCreated}></post-form>
            ${err !== null ? html`

Could not fetch timeline: ${err.message}

            ` : fetchingTimeline ? html`

Loading timeline... please wait.

                    ` : html`
                    ${queue.length !== 0 ? html`
                    <button class="queue-btn" @click=${onQueueBtnClick}>${queue.length} new timeline items</button>
                    ` : nothing}
                    ${timeline.length === 0 ? html`

0 timeline items

                    ` : html`

                        ${repeat(timeline, ti => ti.id, ti => html`<post-item .post=${ti.post} .type=${"timeline_item"}
                            .timelineItemID=${ti.id} @removed-from-timeline=${onRemovedFromTimeline}
                            @resource-deleted=${onPostDeleted}></post-item>`)}

                    ${!noMoreTimelineItems ? html`
                    <intersectable-comp @is-intersecting=${loadMore}></intersectable-comp>

Loading timeline... please wait.

                            ` : endReached ? html`

End reached.

                            ` : nothing}
                            `}
                            `}

        ${toast !== null ? html`<toast-item .toast=${toast}></toast-item>` : nothing}
    `
}

customElements.define("home-page", component(HomePage, { useShadowDOM: false }))

const reMention = /\B@([\-+\w]*)$/

function PostForm() {
    const [auth] = useStore(authStore)
    const [content, setContent] = useState("")
    const [fetching, setFetching] = useState(false)
    const [nsfw, setNSFW] = useState(false)
    const [isSpoiler, setIsSpoiler] = useState(false)
    const [spoilerOf, setSpoilerOf] = useState("")
    const spoilerOfDialogRef = useRef(null)
    const [initialTextAreaHeight, setInitialTextAreaHeight] = useState(0)
    const textAreaRef = useRef(null)
    const textcompleteRef = useRef(null)
    const [toast, setToast] = useState(null)

    const dispatchTimelineItemCreated = post => {
        this.dispatchEvent(new CustomEvent("timeline-item-created", { bubbles: true, detail: post }))
    }

    const onSubmit = useCallback(ev => {
        ev.preventDefault()

        setFetching(true)
        createTimelineItem({
            content,
            spoilerOf: spoilerOf.trim() === "" ? null : spoilerOf.trim(),
            nsfw,
        }).then(ti => {
            ti.post.user = auth.user
            setContent("")
            setNSFW(false)
            setIsSpoiler(false)
            setSpoilerOf("")
            textcompleteRef.current.hide()
            textAreaRef.current.style.height = initialTextAreaHeight + "px"

            dispatchTimelineItemCreated(ti)
        }, err => {
            const msg = "could not create post: " + err.message
            console.error(msg)
            setToast({ type: "error", content: msg })
        }).finally(() => {
            setFetching(false)
        })
    }, [content, nsfw, spoilerOf, auth, textAreaRef, initialTextAreaHeight])

    const onTextAreaInput = useCallback(() => {
        setContent(textAreaRef.current.value)

        textAreaRef.current.style.height = initialTextAreaHeight + "px"
        if (textAreaRef.current.value !== "") {
            textAreaRef.current.style.height = Math.max(textAreaRef.current.scrollHeight, initialTextAreaHeight) + "px"
        }
    }, [content, initialTextAreaHeight, textAreaRef])

    const onNSFWInputChange = useCallback(ev => {
        const checked = ev.currentTarget.checked
        setNSFW(checked)
    }, [])

    const onIsSpoilerInputChange = useCallback(ev => {
        const checked = ev.currentTarget.checked
        setIsSpoiler(checked)
        if (!checked) {
            setSpoilerOf("")
        } else {
            spoilerOfDialogRef.current.showModal()
        }
    }, [spoilerOfDialogRef])

    const onSpoilerOfInput = useCallback(ev => {
        setSpoilerOf(ev.currentTarget.value)
    }, [])

    const onSpoilerOfDialogClose = useCallback(() => {
        if (spoilerOf.trim() === "") {
            setSpoilerOf("")
            setIsSpoiler(false)
        }
    }, [spoilerOf])

    const onSpoilerOfFormSubmit = useCallback(ev => {
        ev.preventDefault()
        spoilerOfDialogRef.current.close()
    }, [])

    const onSpoilerOfCancelBtnClick = useCallback(() => {
        setSpoilerOf("")
        setIsSpoiler(false)
        spoilerOfDialogRef.current.close()
    }, [spoilerOfDialogRef])

    useEffect(() => {
        if (spoilerOfDialogRef.current !== null && !(window.HTMLDialogElement || spoilerOfDialogRef.current.showModal)) {
            import("dialog-polyfill").then(m => m.default).then(dialogPolyfill => {
                dialogPolyfill.registerDialog(spoilerOfDialogRef.current)
            }).catch(err => {
                console.error("could not import dialog polyfill:", err)
            })
        }
    }, [spoilerOfDialogRef])

    useEffect(() => {
        if (textAreaRef.current === null) {
            return
        }

        const editor = new TextareaEditor(textAreaRef.current)
        textcompleteRef.current = new Textcomplete(editor, [{
            match: reMention,
            search: async (term, cb) => {
                cb(await fetchUsernames(term).then(page => page.items, err => {
                    console.error("could not fetch mentions usernames:", err)
                    return []
                }))
            },
            replace: username => `@${username} `,
        }])

        setInitialTextAreaHeight(textAreaRef.current.scrollHeight)

        return () => {
            textcompleteRef.current.destroy()
        }
    }, [textAreaRef, textcompleteRef])

    return html`
        <form class="post-form${content !== "" ? " has-content" : ""}" name="post-form" @submit=${onSubmit}>
            <textarea name="content" placeholder="Write something..." maxlenght="2048" aria-label="Content" required
                .value=${content} .disabled=${fetching} .ref=${ref(textAreaRef)} @input=${onTextAreaInput}></textarea>

                        <input type="checkbox" role="switch" name="nsfw" .disabled=${fetching} .checked=${nsfw} @change=${onNSFWInputChange}>
                        NSFW

                        <input type="checkbox" role="switch" name="is_spoiler" .disabled=${fetching} .checked=${isSpoiler} @change=${onIsSpoilerInputChange}>
                        Spoiler${spoilerOf.trim() !== "" ? ` of ${spoilerOf}` : ""}

                <button class="submit-btn" .disabled=${fetching}>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                        <g data-name="Layer 2">
                            <g data-name="paper-plane">
                                <rect width="24" height="24" opacity="0" />
                                <path
                                    d="M21 4a1.31 1.31 0 0 0-.06-.27v-.09a1 1 0 0 0-.2-.3 1 1 0 0 0-.29-.19h-.09a.86.86 0 0 0-.31-.15H20a1 1 0 0 0-.3 0l-18 6a1 1 0 0 0 0 1.9l8.53 2.84 2.84 8.53a1 1 0 0 0 1.9 0l6-18A1 1 0 0 0 21 4zm-4.7 2.29l-5.57 5.57L5.16 10zM14 18.84l-1.86-5.57 5.57-5.57z" />
                            </g>
                        </g>
                    </svg>
                    Publish</button>
                </button>

            ` : nothing}
        </form>
        <dialog .ref=${ref(spoilerOfDialogRef)} @close=${onSpoilerOfDialogClose}>
            <form method="dialog" class="spoiler-of-form" @submit=${onSpoilerOfFormSubmit}>
                Spoiler of:
                <input type="text" id="spoiler-of-input" name="spoiler_of" placeholder="Spoiler of..." maxlenght="64"
                    autocomplete="off" .value=${spoilerOf} ?required=${isSpoiler} @input=${onSpoilerOfInput}>

                    <button>OK</button>
                    <button type="reset" @click=${onSpoilerOfCancelBtnClick}>Cancel</button>

            </form>
        </dialog>
        ${toast !== null ? html`<toast-item .toast=${toast}></toast-item>` : nothing}
    `
}

customElements.define("post-form", component(PostForm, { useShadowDOM: false }))

function createTimelineItem({ content, spoilerOf, nsfw }) {
    return request("POST", "/api/timeline", { body: { content, spoilerOf, nsfw } })
        .then(resp => resp.body)
        .then(ti => {
            ti.post.createdAt = new Date(ti.post.createdAt)
            return ti
        })
}

function subscribeToTimeline(cb) {
    return subscribe("/api/timeline", ti => {
        ti.post.createdAt = new Date(ti.post.createdAt)
        cb(ti)
    })
}

function fetchTimeline(before = "", last = pageSize) {
    return request("GET", `/api/timeline?last=${encodeURIComponent(last)}&before=${encodeURIComponent(before)}`)
        .then(resp => resp.body)
        .then(page => {
            page.items = page.items.map(ti => ({
                ...ti,
                post: {
                    ...ti.post,
                    createdAt: new Date(ti.post.createdAt),
                },
            }))
            return page
        })
}

function fetchUsernames(startingWith = "", after = "", first = pageSize) {
    return request("GET", `/api/usernames?starting_with=${encodeURIComponent(startingWith)}&after=${encodeURIComponent(after)}&first=${encodeURIComponent(first)}`)
        .then(resp => resp.body)
}
<commit_msg>web/app: restore deleted div<commit_after>import { Textcomplete } from "@textcomplete/core"
import { TextareaEditor } from "@textcomplete/textarea"
import { component, html, useCallback, useEffect, useRef, useState } from "haunted"
import { nothing } from "lit-html"
import { repeat } from "lit-html/directives/repeat.js"
import { authStore, useStore } from "../ctx.js"
import { ref } from "../directives/ref.js"
import { request, subscribe } from "../http.js"
import "./intersectable-comp.js"
import "./post-item.js"
import "./toast-item.js"

const pageSize = 10

export default function () {
    return html`<home-page></home-page>`
}

function HomePage() {
    const [timeline, setTimeline] = useState([])
    const [timelineEndCursor, setTimelineEndCursor] = useState(null)
    const [fetchingTimeline, setFetchingTimeline] = useState(timeline.length === 0)
    const [err, setErr] = useState(null)
    const [loadingMore, setLoadingMore] = useState(false)
    const [noMoreTimelineItems, setNoMoreTimelineItems] = useState(false)
    const [endReached, setEndReached] = useState(false)
    const [queue, setQueue] = useState([])
    const [toast, setToast] = useState(null)

    const onTimelineItemCreated = useCallback(ev => {
        const payload = ev.detail
        setTimeline(tt => [payload, ...queue, ...tt])
        setQueue([])
    }, [queue])

    const onNewTimelineItemArrive = useCallback(ti => {
        setQueue(tt => [ti, ...tt])
    }, [])

    const onRemovedFromTimeline = useCallback(ev => {
        const payload = ev.detail
        setTimeline(tt => tt.filter(ti => ti.id !== payload.timelineItemID))
    }, [])

    const onPostDeleted = useCallback(ev => {
        const payload = ev.detail
        setTimeline(tt => tt.filter(ti => ti.post.id !== payload.id))
    }, [])

    const onQueueBtnClick = useCallback(() => {
        setTimeline(tt => [...queue, ...tt])
        setQueue([])
    }, [queue])

    const loadMore = useCallback(() => {
        if (loadingMore || noMoreTimelineItems) {
            return
        }

        setLoadingMore(true)
        fetchTimeline(timelineEndCursor).then(({ items: timeline, endCursor }) => {
            setTimeline(tt => [...tt, ...timeline])
            setTimelineEndCursor(endCursor)

            if (timeline.length < pageSize) {
                setNoMoreTimelineItems(true)
                setEndReached(true)
            }
        }, err => {
            const msg = "could not fetch more timeline items: " + err.message
            console.error(msg)
            setToast({ type: "error", content: msg })
        }).finally(() => {
            setLoadingMore(false)
        })
    }, [loadingMore, noMoreTimelineItems, timelineEndCursor])

    useEffect(() => {
        setFetchingTimeline(true)
        fetchTimeline().then(({ items: timeline, endCursor }) => {
            setTimeline(timeline)
            setTimelineEndCursor(endCursor)

            if (timeline.length < pageSize) {
                setNoMoreTimelineItems(true)
            }
        }, err => {
            console.error("could not fetch timeline:", err)
            setErr(err)
        }).finally(() => {
            setFetchingTimeline(false)
        })
    }, [])

    useEffect(() => subscribeToTimeline(onNewTimelineItemArrive), [])

    return html`

Timeline

            <post-form @timeline-item-created=${onTimelineItemCreated}></post-form>
            ${err !== null ? html`

Could not fetch timeline: ${err.message}

            ` : fetchingTimeline ? html`

Loading timeline... please wait.

                    ` : html`
                    ${queue.length !== 0 ? html`
                    <button class="queue-btn" @click=${onQueueBtnClick}>${queue.length} new timeline items</button>
                    ` : nothing}
                    ${timeline.length === 0 ? html`

0 timeline items

                    ` : html`

                        ${repeat(timeline, ti => ti.id, ti => html`<post-item .post=${ti.post} .type=${"timeline_item"}
                            .timelineItemID=${ti.id} @removed-from-timeline=${onRemovedFromTimeline}
                            @resource-deleted=${onPostDeleted}></post-item>`)}

                    ${!noMoreTimelineItems ? html`
                    <intersectable-comp @is-intersecting=${loadMore}></intersectable-comp>

Loading timeline... please wait.

                            ` : endReached ? html`

End reached.

                            ` : nothing}
                            `}
                            `}

        ${toast !== null ? html`<toast-item .toast=${toast}></toast-item>` : nothing}
    `
}

customElements.define("home-page", component(HomePage, { useShadowDOM: false }))

const reMention = /\B@([\-+\w]*)$/

function PostForm() {
    const [auth] = useStore(authStore)
    const [content, setContent] = useState("")
    const [fetching, setFetching] = useState(false)
    const [nsfw, setNSFW] = useState(false)
    const [isSpoiler, setIsSpoiler] = useState(false)
    const [spoilerOf, setSpoilerOf] = useState("")
    const spoilerOfDialogRef = useRef(null)
    const [initialTextAreaHeight, setInitialTextAreaHeight] = useState(0)
    const textAreaRef = useRef(null)
    const textcompleteRef = useRef(null)
    const [toast, setToast] = useState(null)

    const dispatchTimelineItemCreated = post => {
        this.dispatchEvent(new CustomEvent("timeline-item-created", { bubbles: true, detail: post }))
    }

    const onSubmit = useCallback(ev => {
        ev.preventDefault()

        setFetching(true)
        createTimelineItem({
            content,
            spoilerOf: spoilerOf.trim() === "" ? null : spoilerOf.trim(),
            nsfw,
        }).then(ti => {
            ti.post.user = auth.user
            setContent("")
            setNSFW(false)
            setIsSpoiler(false)
            setSpoilerOf("")
            textcompleteRef.current.hide()
            textAreaRef.current.style.height = initialTextAreaHeight + "px"

            dispatchTimelineItemCreated(ti)
        }, err => {
            const msg = "could not create post: " + err.message
            console.error(msg)
            setToast({ type: "error", content: msg })
        }).finally(() => {
            setFetching(false)
        })
    }, [content, nsfw, spoilerOf, auth, textAreaRef, initialTextAreaHeight])

    const onTextAreaInput = useCallback(() => {
        setContent(textAreaRef.current.value)

        textAreaRef.current.style.height = initialTextAreaHeight + "px"
        if (textAreaRef.current.value !== "") {
            textAreaRef.current.style.height = Math.max(textAreaRef.current.scrollHeight, initialTextAreaHeight) + "px"
        }
    }, [content, initialTextAreaHeight, textAreaRef])

    const onNSFWInputChange = useCallback(ev => {
        const checked = ev.currentTarget.checked
        setNSFW(checked)
    }, [])

    const onIsSpoilerInputChange = useCallback(ev => {
        const checked = ev.currentTarget.checked
        setIsSpoiler(checked)
        if (!checked) {
            setSpoilerOf("")
        } else {
            spoilerOfDialogRef.current.showModal()
        }
    }, [spoilerOfDialogRef])

    const onSpoilerOfInput = useCallback(ev => {
        setSpoilerOf(ev.currentTarget.value)
    }, [])

    const onSpoilerOfDialogClose = useCallback(() => {
        if (spoilerOf.trim() === "") {
            setSpoilerOf("")
            setIsSpoiler(false)
        }
    }, [spoilerOf])

    const onSpoilerOfFormSubmit = useCallback(ev => {
        ev.preventDefault()
        spoilerOfDialogRef.current.close()
    }, [])

    const onSpoilerOfCancelBtnClick = useCallback(() => {
        setSpoilerOf("")
        setIsSpoiler(false)
        spoilerOfDialogRef.current.close()
    }, [spoilerOfDialogRef])

    useEffect(() => {
        if (spoilerOfDialogRef.current !== null && !(window.HTMLDialogElement || spoilerOfDialogRef.current.showModal)) {
            import("dialog-polyfill").then(m => m.default).then(dialogPolyfill => {
                dialogPolyfill.registerDialog(spoilerOfDialogRef.current)
            }).catch(err => {
                console.error("could not import dialog polyfill:", err)
            })
        }
    }, [spoilerOfDialogRef])

    useEffect(() => {
        if (textAreaRef.current === null) {
            return
        }

        const editor = new TextareaEditor(textAreaRef.current)
        textcompleteRef.current = new Textcomplete(editor, [{
            match: reMention,
            search: async (term, cb) => {
                cb(await fetchUsernames(term).then(page => page.items, err => {
                    console.error("could not fetch mentions usernames:", err)
                    return []
                }))
            },
            replace: username => `@${username} `,
        }])

        setInitialTextAreaHeight(textAreaRef.current.scrollHeight)

        return () => {
            textcompleteRef.current.destroy()
        }
    }, [textAreaRef, textcompleteRef])

    return html`
        <form class="post-form${content !== "" ? " has-content" : ""}" name="post-form" @submit=${onSubmit}>
            <textarea name="content" placeholder="Write something..." maxlenght="2048" aria-label="Content" required
                .value=${content} .disabled=${fetching} .ref=${ref(textAreaRef)} @input=${onTextAreaInput}></textarea>
            ${content !== "" ? html`

                        <input type="checkbox" role="switch" name="nsfw" .disabled=${fetching} .checked=${nsfw} @change=${onNSFWInputChange}>
                        NSFW

                        <input type="checkbox" role="switch" name="is_spoiler" .disabled=${fetching} .checked=${isSpoiler} @change=${onIsSpoilerInputChange}>
                        Spoiler${spoilerOf.trim() !== "" ? ` of ${spoilerOf}` : ""}

                <button class="submit-btn" .disabled=${fetching}>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                        <g data-name="Layer 2">
                            <g data-name="paper-plane">
                                <rect width="24" height="24" opacity="0" />
                                <path
                                    d="M21 4a1.31 1.31 0 0 0-.06-.27v-.09a1 1 0 0 0-.2-.3 1 1 0 0 0-.29-.19h-.09a.86.86 0 0 0-.31-.15H20a1 1 0 0 0-.3 0l-18 6a1 1 0 0 0 0 1.9l8.53 2.84 2.84 8.53a1 1 0 0 0 1.9 0l6-18A1 1 0 0 0 21 4zm-4.7 2.29l-5.57 5.57L5.16 10zM14 18.84l-1.86-5.57 5.57-5.57z" />
                            </g>
                        </g>
                    </svg>
                    Publish</button>
                </button>

            ` : nothing}
        </form>
        <dialog .ref=${ref(spoilerOfDialogRef)} @close=${onSpoilerOfDialogClose}>
            <form method="dialog" class="spoiler-of-form" @submit=${onSpoilerOfFormSubmit}>
                Spoiler of:
                <input type="text" id="spoiler-of-input" name="spoiler_of" placeholder="Spoiler of..." maxlenght="64"
                    autocomplete="off" .value=${spoilerOf} ?required=${isSpoiler} @input=${onSpoilerOfInput}>

                    <button>OK</button>
                    <button type="reset" @click=${onSpoilerOfCancelBtnClick}>Cancel</button>

            </form>
        </dialog>
        ${toast !== null ? html`<toast-item .toast=${toast}></toast-item>` : nothing}
    `
}

customElements.define("post-form", component(PostForm, { useShadowDOM: false }))

function createTimelineItem({ content, spoilerOf, nsfw }) {
    return request("POST", "/api/timeline", { body: { content, spoilerOf, nsfw } })
        .then(resp => resp.body)
        .then(ti => {
            ti.post.createdAt = new Date(ti.post.createdAt)
            return ti
        })
}

function subscribeToTimeline(cb) {
    return subscribe("/api/timeline", ti => {
        ti.post.createdAt = new Date(ti.post.createdAt)
        cb(ti)
    })
}

function fetchTimeline(before = "", last = pageSize) {
    return request("GET", `/api/timeline?last=${encodeURIComponent(last)}&before=${encodeURIComponent(before)}`)
        .then(resp => resp.body)
        .then(page => {
            page.items = page.items.map(ti => ({
                ...ti,
                post: {
                    ...ti.post,
                    createdAt: new Date(ti.post.createdAt),
                },
            }))
            return page
        })
}

function fetchUsernames(startingWith = "", after = "", first = pageSize) {
    return request("GET", `/api/usernames?starting_with=${encodeURIComponent(startingWith)}&after=${encodeURIComponent(after)}&first=${encodeURIComponent(first)}`)
        .then(resp => resp.body)
}
<|endoftext|>


<commit_before>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

var _ = require('lodash');
var mapcat = require('../core/mapcat');

var config = require('../config');

var appState = {};

appState.bindApp = function(app) {
  this.app = app;
  return this;
};

appState.getInitial = function() {

  var uploads = [
    {
      name: 'Insulet OmniPod',
      key: 'omnipod',
      source: {type: 'block', driverId: 'InsuletOmniPod', extension: '.ibf'}
    },
    {
      name: 'Dexcom G4 Platinum',
      key: 'dexcom',
      source: {type: 'device', driverId: 'DexcomG4'}
    },
    {
      name: 'Abbott FreeStyle Precision Xtra',
      key: 'precisionxtra',
      source: {type: 'device', driverId: 'AbbottFreeStyle'}
    },
    {
      name: 'Asante SNAP',
      key: 'asantesnap',
      source: {type: 'device', driverId: 'AsanteSNAP'}
    }
  ];

  if (config.CARELINK) {
    uploads.unshift({name: 'Medtronic MiniMed (CareLink)', key: 'carelink', source: {type: 'carelink'}});
  }

  return {
    dropMenu: false,
    page: 'loading',
    user: null,
    targetId: null,
    targetDevices: [],
    uploads: uploads
  };
};

appState.isLoggedIn = function() {
  return Boolean(this.app.state.user);
};

// For now we only support only one upload at a time,
// so the "current" upload is the first one of the list "in progress"
appState.currentUploadIndex = function() {
  return _.findIndex(this.app.state.uploads, this._isUploadInProgress);
};

appState._isUploadInProgress = function(upload) {
  if (upload.progress && !upload.progress.finish) {
    return true;
  }
  return false;
};

appState.hasUploadInProgress = function() {
  return Boolean(this.currentUploadIndex() !== -1);
};

appState.deviceCount = function() {
  return _.filter(this.app.state.uploads, function(upload) {
    return upload.source.type === 'device';
  }).length;
};

appState.uploadsWithFlags = function() {
  var self = this;
  var currentUploadIndex = this.currentUploadIndex();
  var targetedUploads = _.filter(this.app.state.uploads, function(upload) {
    return _.contains(self.app.state.targetDevices, upload.key);
  });
  return _.map(targetedUploads, function(upload, index) {
    upload = _.clone(upload);
    var source = upload.source || {};

    if (currentUploadIndex !== -1 && currentUploadIndex !== index) {
      upload.disabled = true;
    }
    if (source.type === 'device' &&
        source.connected === false) {
      upload.disconnected = true;
      upload.disabled = true;
    }
    if (source.type === 'carelink') {
      upload.carelink = true;
    }
    if (self._isUploadInProgress(upload)) {
      upload.uploading = true;
      if (source.type === 'carelink' && upload.progress.step === 'start') {
        upload.fetchingCarelinkData = true;
      }
    }
    if (!upload.uploading && upload.progress) {
      upload.completed = true;
      var instance = upload.progress;
      if (instance.success) {
        upload.successful = true;
      }
      else if (instance.error) {
        upload.failed = true;
        upload.error = instance.error;
      }
    }

    return upload;
  });
};

module.exports = appState;
<commit_msg>remove Asante access from UI for RC<commit_after>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

var _ = require('lodash');
var mapcat = require('../core/mapcat');

var config = require('../config');

var appState = {};

appState.bindApp = function(app) {
  this.app = app;
  return this;
};

appState.getInitial = function() {

  var uploads = [
    {
      name: 'Insulet OmniPod',
      key: 'omnipod',
      source: {type: 'block', driverId: 'InsuletOmniPod', extension: '.ibf'}
    },
    {
      name: 'Dexcom G4 Platinum',
      key: 'dexcom',
      source: {type: 'device', driverId: 'DexcomG4'}
    },
    {
      name: 'Abbott FreeStyle Precision Xtra',
      key: 'precisionxtra',
      source: {type: 'device', driverId: 'AbbottFreeStyle'}
    },
    // {
    //   name: 'Asante SNAP',
    //   key: 'asantesnap',
    //   source: {type: 'device', driverId: 'AsanteSNAP'}
    // }
  ];

  if (config.CARELINK) {
    uploads.unshift({name: 'Medtronic MiniMed (CareLink)', key: 'carelink', source: {type: 'carelink'}});
  }

  return {
    dropMenu: false,
    page: 'loading',
    user: null,
    targetId: null,
    targetDevices: [],
    uploads: uploads
  };
};

appState.isLoggedIn = function() {
  return Boolean(this.app.state.user);
};

// For now we only support only one upload at a time,
// so the "current" upload is the first one of the list "in progress"
appState.currentUploadIndex = function() {
  return _.findIndex(this.app.state.uploads, this._isUploadInProgress);
};

appState._isUploadInProgress = function(upload) {
  if (upload.progress && !upload.progress.finish) {
    return true;
  }
  return false;
};

appState.hasUploadInProgress = function() {
  return Boolean(this.currentUploadIndex() !== -1);
};

appState.deviceCount = function() {
  return _.filter(this.app.state.uploads, function(upload) {
    return upload.source.type === 'device';
  }).length;
};

appState.uploadsWithFlags = function() {
  var self = this;
  var currentUploadIndex = this.currentUploadIndex();
  var targetedUploads = _.filter(this.app.state.uploads, function(upload) {
    return _.contains(self.app.state.targetDevices, upload.key);
  });
  return _.map(targetedUploads, function(upload, index) {
    upload = _.clone(upload);
    var source = upload.source || {};

    if (currentUploadIndex !== -1 && currentUploadIndex !== index) {
      upload.disabled = true;
    }
    if (source.type === 'device' &&
        source.connected === false) {
      upload.disconnected = true;
      upload.disabled = true;
    }
    if (source.type === 'carelink') {
      upload.carelink = true;
    }
    if (self._isUploadInProgress(upload)) {
      upload.uploading = true;
      if (source.type === 'carelink' && upload.progress.step === 'start') {
        upload.fetchingCarelinkData = true;
      }
    }
    if (!upload.uploading && upload.progress) {
      upload.completed = true;
      var instance = upload.progress;
      if (instance.success) {
        upload.successful = true;
      }
      else if (instance.error) {
        upload.failed = true;
        upload.error = instance.error;
      }
    }

    return upload;
  });
};

module.exports = appState;
<|endoftext|>


<commit_before>var _ = require('underscore');
var path = require('path');
var utils = require('./utils');
var isAbsolute = require('express/lib/utils').isAbsolute;
var assert = require('assert');

function normalizePath(templatepath) {
  var ext = path.extname(templatepath);
  if (ext.length)
    templatepath = templatepath.slice(0, -1*ext.length);
  return path.normalize(templatepath);
}

function addToClientModel(_ctx, domid, filename, model) {
  if (!_ctx.clientTemplates)
    _ctx.clientTemplates = {}
  _ctx.clientTemplates[domid] = {
    template: normalizePath(filename) //needs to match path name in routes/jswig.js
  }
  if (model) {
    _ctx.clientTemplates[domid].model = model;
  }
}

/*
If an object looks like a database obj, add it to clientData and serialize it out
as a string that would eval on the client as reference an object in the dbCache.
*/
function serializeRefs(k,v, clientData) {
  if (k == 'clientTemplates') {  //avoid circular refs but doesn't seem to work??
    return undefined;
  }
  if (v && typeof v === 'object') {
    //we have to do it like this to avoid toString being call internally
    var copy = new v.constructor();
    for (var prop in v) {
      if (!v.hasOwnProperty(prop)) continue;
      var val = v[prop]
      if (val && val._id) {
        var obj = val;
        clientData[obj._id] = _.extend(clientData[obj._id] || {}, obj);
        val = "@@@" + obj._id+ "@@@";
      }
      copy[prop] = val;
    }
    return copy;
  }
  return v;
}

//dictionary of persistent objects
//model objects have references to the values in that dictionary
//dom elements are associated with the template and model via jquery's data()
function renderClientside(_ctx, opt) {
  var out = '<script>if (!$.templates) $.templates = {}</script>';
  if (_ctx.clientTemplates) {
    var templates = {};
    ///jscript/templatename.html => $.templates['templatename'] = function() {}
    _.each(_ctx.clientTemplates, function(value) {
      if (!templates[value.template]) {
        var scriptpath = value.template;
        var ext = path.extname(scriptpath);
        if (ext.length)
          scriptpath = scriptpath.slice(0, -1*ext.length);
        out += "<script async defer src='/jswig/" + normalizePath(scriptpath) +".js'></script>\n";
        templates[value.template] = true;
      }
    });
    out += '<script src="' + path.join( (opt && opt.jsbase) || "/js/", "swig.min.js") + '"></script>\n';

    var clientData = {};
    var modelJson = JSON.stringify(_ctx.clientTemplates,
      function(k,v) {return serializeRefs(k,v,clientData)}).replace(/"@@@(.+?)@@@"/g, "\$.dbCache['$1']");
    out += "\n<script>$.dbCache=" + JSON.stringify(clientData) + ';\n';
    out += "$(document).ready(function() {$.each(" + modelJson
    +", function(domid, model) {\n"
    +"     $('#'+domid).data('_template', model.template);\n"
    +"     $('#'+domid).data('_model', model.model);});\n"
    +"});</script>\n";
    //XXX should be a generic way to introspect _ctx for variables intended for client and to render them
    if (_ctx.routes && _ctx.routes.getUrlMapSource) { //XXX dbRender() needs to add routes to template locals
      out += "\n<script>$.routes=" + _ctx.routes.getUrlMapSource() + '</script>\n';
    }
 }
  return out;
}

/*
top level invocation:

from: empty || any
to: absolute
absolute

from: empty
to: relative
search [derived, derived/base, base]

(enable access to files with same name)

from: empty
to:   "base/"
search [derived/base, base]

happens when including a file:

from: derived | derived/base
to:   relative
search [derived, derived/base, base]

(enable overrides)
from: base
to:  any relative
search [derived/base, base]

(enable access to files with same name)

from: derived
to:   "base/"
search [derived/base, base]

(enable equivalent of "super()")

from: derived/base | base
to:   "base/"
search [base]

*/
function getPathLoader(swig, viewpaths) {
  assert(viewpaths.length == 2);
  var loader = swig.loaders.fs();

  loader.resolve = function (to, from) {
    //console.log('start resolve', to, from, to && isAbsolute(to));
    if (!to) return '';
    if (isAbsolute(to)) {
      //XXX if starts with /base, strip it and skip normalize(join()) below
      return to;
    }

    var tobase = to.match("^base"+path.sep);
    if (tobase)
      to = to.slice(5);

    if (!from) {

      if (tobase) {
        return utils.searchPath([path.join(viewpaths[0], 'base'), viewpaths[1]], to);
      } else {
        return utils.searchPath([viewpaths[0],
          path.join(viewpaths[0], 'base', path.sep),
          viewpaths[1]], to)
      }

    } else {

      var rootlen, fromderivedbase, fromderived = from.indexOf(viewpaths[0]) === 0;
      if (fromderived) {
        rootlen = viewpaths[0].length;
        fromderivedbase = from.substr(rootlen, 5) == "base"+path.sep;
        if (fromderivedbase)
          rootlen += 5;
      } else {
        rootlen = viewpaths[1].length;
      }

      var fromroot = path.dirname(from).slice(rootlen);
      to = path.normalize(path.join(fromroot, to));
      //console.log("resolve resolve", fromroot, to);

      if (tobase) {
        if (fromderived && !fromderivedbase)  {
          //search [derived/base, base]
          return utils.searchPath([path.join(viewpaths[0], 'base', path.sep), viewpaths[1]], to);
        } else {
          assert(!fromderived || fromderivedbase);
          return utils.searchPath([viewpaths[1]], to); //[base]
        }
      } else {
        if (fromderived) { //[derived, derived/base, base]
          return utils.searchPath([viewpaths[0], path.join(viewpaths[0], 'base', path.sep), viewpaths[1]], to);
        } else { //base
          return utils.searchPath([path.join(viewpaths[0], 'base', path.sep), viewpaths[1]], to);
        }
      }
    }
  }

  var superload = loader.load;
  loader.load = function (identifier, cb) {
    //path resolve
    if (!identifier)
      throw new Error('can not load empty identifier');
    return superload(identifier, cb);
  }
  return loader;
}

module.exports = function(swig, opt) {
  var clientinclude = require('../lib/swigtags/clientinclude');
  swig.setTag('clienttemplate', clientinclude.parse, function(compiler, args) {return clientinclude.compile(compiler, args, false)});
  swig.setTag('clientmodel', clientinclude.parse, function(compiler, args) {return clientinclude.compile(compiler, args, true)});
  var clientrender = require('../lib/swigtags/clientrender');
  swig.setTag('clientrender', clientrender.parse, clientrender.compile);
  swig.setExtension('addToClientModel', addToClientModel);
  swig.setExtension('renderClientside', renderClientside);
  if (opt) {
    swig.setDefaults(
      _.defaults(opt, {
        loader: getPathLoader(swig, opt.loaderPath || [])
      })
    );
  }

  return {
    addToClientModel: addToClientModel,
    renderClientside: function(_ctx) {return renderClientside(_ctx,opt)}
  }
}
<commit_msg>dont assume $ in the default case<commit_after>var _ = require('underscore');
var path = require('path');
var utils = require('./utils');
var isAbsolute = require('express/lib/utils').isAbsolute;
var assert = require('assert');

function normalizePath(templatepath) {
  var ext = path.extname(templatepath);
  if (ext.length)
    templatepath = templatepath.slice(0, -1*ext.length);
  return path.normalize(templatepath);
}

function addToClientModel(_ctx, domid, filename, model) {
  if (!_ctx.clientTemplates)
    _ctx.clientTemplates = {}
  _ctx.clientTemplates[domid] = {
    template: normalizePath(filename) //needs to match path name in routes/jswig.js
  }
  if (model) {
    _ctx.clientTemplates[domid].model = model;
  }
}

/*
If an object looks like a database obj, add it to clientData and serialize it out
as a string that would eval on the client as reference an object in the dbCache.
*/
function serializeRefs(k,v, clientData) {
  if (k == 'clientTemplates') {  //avoid circular refs but doesn't seem to work??
    return undefined;
  }
  if (v && typeof v === 'object') {
    //we have to do it like this to avoid toString being call internally
    var copy = new v.constructor();
    for (var prop in v) {
      if (!v.hasOwnProperty(prop)) continue;
      var val = v[prop]
      if (val && val._id) {
        var obj = val;
        clientData[obj._id] = _.extend(clientData[obj._id] || {}, obj);
        val = "@@@" + obj._id+ "@@@";
      }
      copy[prop] = val;
    }
    return copy;
  }
  return v;
}

//dictionary of persistent objects
//model objects have references to the values in that dictionary
//dom elements are associated with the template and model via jquery's data()
function renderClientside(_ctx, opt) {
  var out = '<script>if (window.$ && !$.templates) $.templates = {}</script>';
  if (_ctx.clientTemplates) {
    var templates = {};
    ///jscript/templatename.html => $.templates['templatename'] = function() {}
    _.each(_ctx.clientTemplates, function(value) {
      if (!templates[value.template]) {
        var scriptpath = value.template;
        var ext = path.extname(scriptpath);
        if (ext.length)
          scriptpath = scriptpath.slice(0, -1*ext.length);
        out += "<script async defer src='/jswig/" + normalizePath(scriptpath) +".js'></script>\n";
        templates[value.template] = true;
      }
    });
    out += '<script src="' + path.join( (opt && opt.jsbase) || "/js/", "swig.min.js") + '"></script>\n';

    var clientData = {};
    var modelJson = JSON.stringify(_ctx.clientTemplates,
      function(k,v) {return serializeRefs(k,v,clientData)}).replace(/"@@@(.+?)@@@"/g, "\$.dbCache['$1']");
    out += "\n<script>$.dbCache=" + JSON.stringify(clientData) + ';\n';
    out += "$(document).ready(function() {$.each(" + modelJson
    +", function(domid, model) {\n"
    +"     $('#'+domid).data('_template', model.template);\n"
    +"     $('#'+domid).data('_model', model.model);});\n"
    +"});</script>\n";
    //XXX should be a generic way to introspect _ctx for variables intended for client and to render them
    if (_ctx.routes && _ctx.routes.getUrlMapSource) { //XXX dbRender() needs to add routes to template locals
      out += "\n<script>$.routes=" + _ctx.routes.getUrlMapSource() + '</script>\n';
    }
 }
  return out;
}

/*
top level invocation:

from: empty || any
to: absolute
absolute

from: empty
to: relative
search [derived, derived/base, base]

(enable access to files with same name)

from: empty
to:   "base/"
search [derived/base, base]

happens when including a file:

from: derived | derived/base
to:   relative
search [derived, derived/base, base]

(enable overrides)
from: base
to:  any relative
search [derived/base, base]

(enable access to files with same name)

from: derived
to:   "base/"
search [derived/base, base]

(enable equivalent of "super()")

from: derived/base | base
to:   "base/"
search [base]

*/
function getPathLoader(swig, viewpaths) {
  assert(viewpaths.length == 2);
  var loader = swig.loaders.fs();

  loader.resolve = function (to, from) {
    //console.log('start resolve', to, from, to && isAbsolute(to));
    if (!to) return '';
    if (isAbsolute(to)) {
      //XXX if starts with /base, strip it and skip normalize(join()) below
      return to;
    }

    var tobase = to.match("^base"+path.sep);
    if (tobase)
      to = to.slice(5);

    if (!from) {

      if (tobase) {
        return utils.searchPath([path.join(viewpaths[0], 'base'), viewpaths[1]], to);
      } else {
        return utils.searchPath([viewpaths[0],
          path.join(viewpaths[0], 'base', path.sep),
          viewpaths[1]], to)
      }

    } else {

      var rootlen, fromderivedbase, fromderived = from.indexOf(viewpaths[0]) === 0;
      if (fromderived) {
        rootlen = viewpaths[0].length;
        fromderivedbase = from.substr(rootlen, 5) == "base"+path.sep;
        if (fromderivedbase)
          rootlen += 5;
      } else {
        rootlen = viewpaths[1].length;
      }

      var fromroot = path.dirname(from).slice(rootlen);
      to = path.normalize(path.join(fromroot, to));
      //console.log("resolve resolve", fromroot, to);

      if (tobase) {
        if (fromderived && !fromderivedbase)  {
          //search [derived/base, base]
          return utils.searchPath([path.join(viewpaths[0], 'base', path.sep), viewpaths[1]], to);
        } else {
          assert(!fromderived || fromderivedbase);
          return utils.searchPath([viewpaths[1]], to); //[base]
        }
      } else {
        if (fromderived) { //[derived, derived/base, base]
          return utils.searchPath([viewpaths[0], path.join(viewpaths[0], 'base', path.sep), viewpaths[1]], to);
        } else { //base
          return utils.searchPath([path.join(viewpaths[0], 'base', path.sep), viewpaths[1]], to);
        }
      }
    }
  }

  var superload = loader.load;
  loader.load = function (identifier, cb) {
    //path resolve
    if (!identifier)
      throw new Error('can not load empty identifier');
    return superload(identifier, cb);
  }
  return loader;
}

module.exports = function(swig, opt) {
  var clientinclude = require('../lib/swigtags/clientinclude');
  swig.setTag('clienttemplate', clientinclude.parse, function(compiler, args) {return clientinclude.compile(compiler, args, false)});
  swig.setTag('clientmodel', clientinclude.parse, function(compiler, args) {return clientinclude.compile(compiler, args, true)});
  var clientrender = require('../lib/swigtags/clientrender');
  swig.setTag('clientrender', clientrender.parse, clientrender.compile);
  swig.setExtension('addToClientModel', addToClientModel);
  swig.setExtension('renderClientside', renderClientside);
  if (opt) {
    swig.setDefaults(
      _.defaults(opt, {
        loader: getPathLoader(swig, opt.loaderPath || [])
      })
    );
  }

  return {
    addToClientModel: addToClientModel,
    renderClientside: function(_ctx) {return renderClientside(_ctx,opt)}
  }
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2012, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://yuilibrary.com/license/
*/
var ycoverage = require('yuitest-coverage'),
    istanbul = require('istanbul'),
    log = require('../log');

exports.coverage = function (options, blob, done) {
    options = options || {}; //Not needed here??

    var type = options.type,
        inst;
    delete options.type;

    log.info(type + ' providing coverage');

    if (type === 'yuitest') {
        ycoverage.cover(blob.result, options, function (err, data) {
            if (err) {
                done(err);
            } else {
            data = data.replace(/\r\n/g, '\n');
                done(null, new blob.constructor(data, blob));
            }
        });
    } else {
        log.log('instrumenting with istanbul');
        inst = new istanbul.Instrumenter({
            embedSource: true
        });
        inst.instrument(blob.result, options.name, function (err, data) {
            if (err) {
                done(err);
            } else {
                done(null, new blob.constructor(data, blob));
            }
        });
    }
};
<commit_msg>Fixed spaces from windows commit<commit_after>/*
Copyright (c) 2012, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://yuilibrary.com/license/
*/
var ycoverage = require('yuitest-coverage'),
    istanbul = require('istanbul'),
    log = require('../log');

exports.coverage = function (options, blob, done) {
    options = options || {}; //Not needed here??

    var type = options.type,
        inst;
    delete options.type;

    log.info(type + ' providing coverage');

    if (type === 'yuitest') {
        ycoverage.cover(blob.result, options, function (err, data) {
            if (err) {
                done(err);
            } else {
                data = data.replace(/\r\n/g, '\n');
                done(null, new blob.constructor(data, blob));
            }
        });
    } else {
        log.log('instrumenting with istanbul');
        inst = new istanbul.Instrumenter({
            embedSource: true
        });
        inst.instrument(blob.result, options.name, function (err, data) {
            if (err) {
                done(err);
            } else {
                done(null, new blob.constructor(data, blob));
            }
        });
    }
};
<|endoftext|>