<commit_before>define(['util', 'graph/graph'], function(util, Graph) {
  var GraphUtil = {};

  function addRandomEdge(graph) {
    // Add a random edge in the graph that maintains planarity
    var TRIES = 10;
    var nodes = graph.nodes;

    for (var i = 0; i < TRIES; ++i) {
      var choices = util.random.choose(2, nodes);
      var u = choices[0];
      var v = choices[1];
      if (graph.addEdge(u, v)) {
        return true;
      }
    }

    return false;
  }

  function splitRandomNode(graph) {
    // Find a node and randomly split it
  }

  GraphUtil.generate = function(options) {
    // I imagine something like this:
    // First, generate a random planar wheel embedding
    // Next we continue with making the graph interesting
    // We only ever need to choose to split a vertex or add an edge

    // Decide whether to split a vertex or add an edge.
    // To do this, we also need to determine if we can split a vertex.
    // (We can split if graph.maxDegree >= 4)
    // Now, to split a vertex, choose two random non-adjacent vertices

    // To add an edge, simply add one and check for intersections
    // We can retry this some number of times before giving up
    var canvas = document.getElementById(options.canvas);
    var ctx = canvas.getContext('2d');

    //var graph = Graph.wheel(wheelSize);
    var success = false;
    while (success) {
      if (graph.maxDegree < 3) {
        // No choice but to add an edge
        success = addRandomEdge(graph);
        continue;
      }

      var split = Math.random() < 0.5;

      if (split) {
        success = splitRandomNode(graph);
      }
      else {
        success = addRandomEdge(graph);
      }

    }

    var mid = {
      x : canvas.width / 2,
      y : canvas.height / 2
    };

    var radius = util.random.number(100, 150);
    var wheelSize = util.random.number(5, 10);
    radius = 125;
    wheelSize = 6;

    var graph = new Graph();
    var points = convexPoints(wheelSize, radius, mid);
    var midId = graph.addNode(mid);
    var lastId;
    var firstId;

    for (var i = 0; i < points.length; ++i) {
      var id = graph.addNode(points[i]);
      if (lastId) graph.addEdge(id, lastId);
      else firstId = id;
      lastId = id;
      graph.addEdge(id, midId);
    }
    graph.addEdge(id, firstId);

    graph.split(0, [1, 2, 3], [4, 5, 6]);

    graph.draw(ctx);
  };

  function convexPoints(num, radius, mid) {
    // Return an array of points for a regular convex polygon with
    // given radius and center
    var points = [];
    var angle = Math.PI * 2 / num;
    for (var i = 0; i < num; ++i) {
      points.push({
        x : mid.x + (Math.cos(angle * i) * radius),
        y : mid.y + (Math.sin(angle * i) * radius)
      });
    }
    return points;
  }

  return GraphUtil;
});
<commit_msg>Slightly more interesting split<commit_after>define(['util', 'graph/graph'], function(util, Graph) {
  var GraphUtil = {};

  function addRandomEdge(graph) {
    // Add a random edge in the graph that maintains planarity
    var TRIES = 10;
    var nodes = graph.nodes;

    for (var i = 0; i < TRIES; ++i) {
      var choices = util.random.choose(2, nodes);
      var u = choices[0];
      var v = choices[1];
      if (graph.addEdge(u, v)) {
        return true;
      }
    }

    return false;
  }

  function splitRandomNode(graph) {
    // Find a node and randomly split it
  }

  GraphUtil.generate = function(options) {
    // I imagine something like this:
    // First, generate a random planar wheel embedding
    // Next we continue with making the graph interesting
    // We only ever need to choose to split a vertex or add an edge

    // Decide whether to split a vertex or add an edge.
    // To do this, we also need to determine if we can split a vertex.
    // (We can split if graph.maxDegree >= 4)
    // Now, to split a vertex, choose two random non-adjacent vertices

    // To add an edge, simply add one and check for intersections
    // We can retry this some number of times before giving up
    var canvas = document.getElementById(options.canvas);
    var ctx = canvas.getContext('2d');

    //var graph = Graph.wheel(wheelSize);
    var success = false;
    while (success) {
      if (graph.maxDegree < 3) {
        // No choice but to add an edge
        success = addRandomEdge(graph);
        continue;
      }

      var split = Math.random() < 0.5;

      if (split) {
        success = splitRandomNode(graph);
      }
      else {
        success = addRandomEdge(graph);
      }

    }

    var mid = {
      x : canvas.width / 2,
      y : canvas.height / 2
    };

    var radius = util.random.number(100, 150);
    var wheelSize = util.random.number(5, 10);

    var graph = new Graph();
    var points = convexPoints(wheelSize, radius, mid);
    var midId = graph.addNode(mid);
    var lastId;
    var firstId;

    for (var i = 0; i < points.length; ++i) {
      var id = graph.addNode(points[i]);
      if (lastId) graph.addEdge(id, lastId);
      else firstId = id;
      lastId = id;
      graph.addEdge(id, midId);
    }
    graph.addEdge(id, firstId);

    // A dumb split
    var n1 = [];
    var n2 = [];
    for (var i = 1; i < 4; ++i) n1.push(i);
    for (var i = 4; i < points.length; ++i) n2.push(i);
    graph.split(0, n1, n2);

    graph.draw(ctx);
  };

  function convexPoints(num, radius, mid) {
    // Return an array of points for a regular convex polygon with
    // given radius and center
    var points = [];
    var angle = Math.PI * 2 / num;
    for (var i = 0; i < num; ++i) {
      points.push({
        x : mid.x + (Math.cos(angle * i) * radius),
        y : mid.y + (Math.sin(angle * i) * radius)
      });
    }
    return points;
  }

  return GraphUtil;
});
<|endoftext|>


<commit_before>import {disableBodyScroll, enableBodyScroll} from 'body-scroll-lock'
import PropTypes from 'prop-types'
import React, {Component} from 'react'
import flexy from './flexy.module.css'
import styles from './scrollable.module.css'

export const ScrollableContainer = ({className, children}) => {
    return (

            {children}

    )
}

ScrollableContainer.propTypes = {
    children: PropTypes.any.isRequired,
    className: PropTypes.string
}

export const Unscrollable = ({className, children}) => {
    return (

            {children}

    )
}

Unscrollable.propTypes = {
    children: PropTypes.any,
    className: PropTypes.string
}

export class Scrollable extends Component {
    targetRef = React.createRef()

    render() {
        const {className, direction, children} = this.props
        return (

                {children}

        )
    }

    componentDidMount() {
        disableBodyScroll(this.targetRef.current)
    }

    componentWillUnmount() {
        enableBodyScroll(this.targetRef.current)
    }
}

Scrollable.defaultProps = {direction: 'y'}

Scrollable.propTypes = {
    children: PropTypes.any,
    className: PropTypes.string,
    direction: PropTypes.oneOf(['x', 'y', 'xy'])
}
<commit_msg>Implemented passing Scrollable height to children function.<commit_after>import {disableBodyScroll, enableBodyScroll} from 'body-scroll-lock'
import PropTypes from 'prop-types'
import React, {Component} from 'react'
import _ from 'lodash'
import flexy from './flexy.module.css'
import styles from './scrollable.module.css'

const Context = React.createContext()

export class ScrollableContainer extends React.Component {
    ref = React.createRef()
    state = {}

    render() {
        const {className, children} = this.props
        const {height} = this.state
        return (

                <Context.Provider value={{height}}>
                    {children}
                </Context.Provider>

        )
    }

    update(height) {
        this.updateState({height})
    }

    componentDidMount() {
        this.update(0)
    }

    componentDidUpdate() {
        this.update(this.ref.current.clientHeight)
    }

    updateState(state, callback) {
        const updatedState = (prevState, state) =>
            _.isEqual(_.pick(prevState, _.keys(state)), state) ? null : state
        this.setState(
            prevState =>
                updatedState(prevState, _.isFunction(state) ? state(prevState) : state),
            callback
        )
    }

}

ScrollableContainer.propTypes = {
    children: PropTypes.any.isRequired,
    className: PropTypes.string
}

export const Unscrollable = ({className, children}) => {
    return (

            {children}

    )
}

Unscrollable.propTypes = {
    children: PropTypes.any,
    className: PropTypes.string
}

export class Scrollable extends Component {
    targetRef = React.createRef()

    render() {
        return (
            <Context.Consumer>
                {({height}) => this.renderScrollable(height)}
            </Context.Consumer>
        )
    }

    renderScrollable(scrollableContainerHeight) {
        const {className, direction, children} = this.props
        return (

                {_.isFunction(children) ? children(scrollableContainerHeight) : children}

        )
    }

    componentDidMount() {
        disableBodyScroll(this.targetRef.current)
    }

    componentWillUnmount() {
        enableBodyScroll(this.targetRef.current)
    }
}

Scrollable.defaultProps = {direction: 'y'}

Scrollable.propTypes = {
    children: PropTypes.any,
    className: PropTypes.string,
    direction: PropTypes.oneOf(['x', 'y', 'xy'])
}
<|endoftext|>


<commit_before>module.exports = {
	"env": {
		"browser": true,
		"es6": true,
		"node": true
	},
	"extends": [
		"eslint:recommended",
		"plugin:@typescript-eslint/eslint-recommended",
		"plugin:@typescript-eslint/recommended",
		"prettier",
		"prettier/@typescript-eslint",
	], // prettier must be last in array to override other configs
	"parser": "@typescript-eslint/parser",
	"parserOptions": {
		"project": "tsconfig.json",
		"sourceType": "module"
	},
	"plugins": [
		"@typescript-eslint"
	],
	"rules": {
		"no-console": ["error", {allow : ["warn", "error"]}],
		"@typescript-eslint/camelcase": ["error", {properties: "never"}],
		"@typescript-eslint/no-unused-vars": ["error", { "vars": "all", "args": "none"}],

		// Do not apply inappropriate rules below
		"@typescript-eslint/no-inferrable-types": "off",
		"@typescript-eslint/no-use-before-define": "off",
		"@typescript-eslint/ban-ts-ignore": "off",

		// TODO fix and remove rules below
		"@typescript-eslint/explicit-function-return-type": "off",
		"@typescript-eslint/ban-types": "off",
		"@typescript-eslint/no-explicit-any": "off",
		"@typescript-eslint/no-var-requires": "off",
		"@typescript-eslint/consistent-type-assertions": "off",
		"@typescript-eslint/no-non-null-assertion": "off",
		"@typescript-eslint/no-empty-function": "off",
	}
}
<commit_msg>Eslint: Enable no-empty-functions (#937)<commit_after>module.exports = {
	"env": {
		"browser": true,
		"es6": true,
		"node": true
	},
	"extends": [
		"eslint:recommended",
		"plugin:@typescript-eslint/eslint-recommended",
		"plugin:@typescript-eslint/recommended",
		"prettier",
		"prettier/@typescript-eslint",
	], // prettier must be last in array to override other configs
	"parser": "@typescript-eslint/parser",
	"parserOptions": {
		"project": "tsconfig.json",
		"sourceType": "module"
	},
	"plugins": [
		"@typescript-eslint"
	],
	"rules": {
		"no-console": ["error", {allow : ["warn", "error"]}],
		"@typescript-eslint/no-empty-function": ["error", {allow: ["arrowFunctions"]}],
		"@typescript-eslint/camelcase": ["error", {properties: "never"}],
		"@typescript-eslint/no-unused-vars": ["error", { "vars": "all", "args": "none"}],

		// Do not apply inappropriate rules below
		"@typescript-eslint/no-inferrable-types": "off",
		"@typescript-eslint/no-use-before-define": "off",
		"@typescript-eslint/ban-ts-ignore": "off",

		// TODO fix and remove rules below
		"@typescript-eslint/explicit-function-return-type": "off",
		"@typescript-eslint/ban-types": "off",
		"@typescript-eslint/no-explicit-any": "off",
		"@typescript-eslint/no-var-requires": "off",
		"@typescript-eslint/consistent-type-assertions": "off",
		"@typescript-eslint/no-non-null-assertion": "off",
	}
}
<|endoftext|>


<commit_before>/**
 * Gruntfile template for Grunt
 *
 * @author Artem Sapegin, http://sapegin.me
 */

exports.description = 'Creates a Gruntfile.coffee gruntfile.';
exports.warnOn = ['grunt.js', 'Gruntfile.js', 'Gruntfile.coffee'];

exports.template = function(grunt, init, done) {
	'use strict';

	var fs = require('fs');
	var utils = require('../_src/utils').init(grunt, init);

	init.process({}, [
		// Prompt for these values
		{
			name: 'js',
			message: 'JavaScript?',
			'default': 'Y/n',
			warning: 'Yes: uglify + concat tasks. No: nothing to see here.'
		},
		{
			name: 'stylus',
			message: 'Stylus?',
			'default': 'Y/n',
			warning: 'Yes: stylus task. No: nothing to see here.'
		},
		{
			name: 'sweet',
			message: 'Sweet?',
			'default': 'y/N',
			warning: 'Yes: sweet task. No: nothing to see here.'
		},
		{
			name: 'fingerprint',
			message: 'Fingerprint?',
			'default': 'y/N',
			warning: 'Yes: fingerprint task. No: nothing to see here.'
		},
		{
			name: 'imgo',
			message: 'Optimize images?',
			'default': 'y/N',
			warning: 'Yes: imgo task. No: nothing to see here.'
		},
		{
			name: 'compress',
			message: 'Archive task?',
			'default': 'y/N',
			warning: 'Yes: compress task. No: nothing to see here.'
		},
		{
			name: 'library',
			message: 'Library name?',
			'default': 'none',
			warning: 'Name: creates big library banner. None: small copyright banner.'
		}
	], function(err, props) {
		grunt.util._.defaults(props, init.defaults);

		var library = props.library;
		var project_name = library || utils.projectName();
		var stylus = props.stylus = /y/i.test(props.stylus);
		var sweet = props.sweet = !/n/i.test(props.sweet);
		var fingerprint = props.fingerprint = !/n/i.test(props.fingerprint);
		var imgo = props.imgo = !/n/i.test(props.imgo);
		var compress = props.compress = !/n/i.test(props.compress);
		var js = /y/i.test(props.js);
		var package_json = fs.existsSync('package.json') && grunt.file.readJSON('package.json');
		var wordpress = utils.isWordpressTheme();
		var js_script = package_json ? '<%= pkg.name %>' : 'scripts';

		// Guess at some directories, if they exist
		var htdocs_dir = utils.preferDir(['htdocs', 'www']);
		var htdocs_prefix = htdocs_dir ? htdocs_dir + '/' : '';
		var lib_dir = utils.preferDir([htdocs_prefix + 'js/mylibs', htdocs_prefix + 'lib', htdocs_prefix + 'src']);
		var images_dir = utils.preferDir(['images', 'img', 'i']);

		// JS libraries
		props.jquery = grunt.file.expand('**/jquery*.js').length > 0;

		// Config
		var cfg = {};

		if (js) {
			if (library) {
				if (package_json) {
					cfg.pkg = utils.JS('grunt.file.readJSON("package.json")');
					cfg.banner = utils.JS([
						'"/*! <%= pkg.title || pkg.name %> v<%= pkg.version %>"\n',
						'" * © <%= pkg.author.name %>, <%= pkg.homepage ? pkg.homepage : "" %>, "',
							'"<%= grunt.template.today(\'yyyy\') %> - "',
							'" Licensed <%= _.pluck(pkg.licenses, \'type\').join(\', \') %> */"\n'
					].join(',\n'));
				}
				else {
					cfg.version = '0.0.0';
					cfg.banner = utils.JS([
						'"/*! ' + library + ' v<%= version %>' + '"\n',
						'" * © ' + props.author_name + ', ' + props.author_url + ', <%= grunt.template.today(\'yyyy\') %> - ' +
							'Licensed MIT */"\n'
					].join(',\n'));
				}
			}
			else {
				cfg.banner = '/*! Author: ' + props.author_name + ', ' + props.author_url + ', ' +
					"<%= grunt.template.today('yyyy') %> */\n";
			}

			cfg.jshint = {
				options: {
					jshintrc: '.jshintrc'
				},
				files: []
			};
			if (library) {
				cfg.jshint.files.push(lib_dir + '/*.js');
			}
			else {
				cfg.jshint.files.push('js/mylibs/*.js', htdocs_prefix + 'js/main.js');
			}

			cfg.concat = {
				main: {
					src: [
						htdocs_prefix + 'js/utils.js',
						htdocs_prefix + 'blocks/*/*.js',
						htdocs_prefix + 'js/main.js'
					],
					dest: htdocs_prefix + 'build/' + js_script + '.js'
				}
			};

			cfg.uglify = {
				main: {
					options: {
						banner: '<%= banner %>',
						compress: {
							global_defs: {
								DEBUG: utils.JS('debug')
							}
						}
					},
					files: {
						'<%= concat.main.dest %>': '<%= concat.main.dest %>'
					}
				}
			};
		}

		if (stylus) {
			cfg.stylus = {
				compile: {
					options: {
						'include css': true,
						'define': {
							DEBUG: utils.JS('debug')
						},
						'paths': ['blocks']
					},
					files: {}
				}
			};

			cfg.stylus.compile.files[wordpress ? 'style.css' : htdocs_prefix + 'build/styles.css'] = 'styles/index.styl';
		}

		if (sweet) {
			cfg.sweet = {
				content_dir: 'content',
				publish_dir: htdocs_dir || 'htdocs',
				templates_dir: 'templates',
				default_template_id: 'template',
				langs: ['ru', 'en'],
				url_prefixes: {
					ru: '/ru/',
					en: '/'
				},
				uri_prefixes: {
					ru: '/ru/',
					en: '/'
				},
				files: {
					css: {
						path: htdocs_prefix + 'build/styles.css',
						href: '/build/styles.css?{version}'
					},
					js: {
						path: htdocs_prefix + 'build/scripts.js',
						href: '/build/scripts.js?{version}'
					}
				}
			};
		}

		if (fingerprint) {
			cfg.fingerprint = {
				assets: {
					src: [
						'build/*.js',
						'build/*.css'
					],
					filename: 'build/fingerprint.txt'
				}
			};

			if (wordpress) {
				cfg.fingerprint.assets.filename = 'fingerprint.php';
				cfg.fingerprint.assets.template = "<?php define('V', '<%= fingerprint %>'); ?>";
			}
		}

		if (imgo) {
			cfg.imgo = {
				images: {
					src: htdocs_prefix + (images_dir ? images_dir : 'images') + '/*',
					skip: utils.JS('require("os").platform() == "win32"')
				}
			};
		}

		if (compress) {
			cfg.compress = {
				pack: {
					files: {}
				}
			};
			cfg.compress.pack.files['.zip'] = [
				'{styles,js,images,media,build}/**/*',
				'*.{html,coffee}'
			];
		}

		if (stylus || sweet || js) {
			cfg.watch = {};
			if (js) {
				cfg.watch.concat = {
					files: '<%= concat.main.src %>',
					tasks: 'concat'
				};
			}
			if (stylus) {
				cfg.watch.stylus = {
					files: 'styles/**',
					tasks: 'stylus'
				};
			}
			if (sweet) {
				cfg.watch.sweet = {
					files: [
						'<%= sweet.content_dir %>/**',
						'<%= sweet.templates_dir %>/**'
					],
					tasks: 'sweet'
				};
			}
		}

		if (sweet) {
			cfg.connect = {
				port: 8000,
				base: '<%= sweet.publish_dir %>'
			};
		}

		props.config = utils.jsToCoffeString(cfg, 2);

		// Default task
		var defaults = [];
		if (imgo) defaults.push('imgo');
		if (stylus) defaults.push('stylus');
		if (js) defaults.push('jshint', 'concat', 'uglify');
		if (sweet) defaults.push('sweet');
		if (fingerprint) defaults.push('fingerprint');
		props.defaults = utils.arrayToString(defaults);

		// Deploy task
		utils.removeFromArray(defaults, 'jshint');
		utils.removeFromArray(defaults, 'imgo');
		props.deploy = utils.arrayToString(defaults);

		// Files to copy (and process).
		var files = init.filesToCopy(props);
		delete files['Gruntfile.js'];

		// Actually copy (and process) files
		init.copyAndProcess(files, props);

		utils.writeEditorConfig();
		if (js) utils.writeJsHintRc();

		// package.json
		if (!package_json) package_json = {};
		if (!package_json.name) package_json.name = project_name;
		if (!package_json.version) package_json.version = '0.0.0';
		if (!package_json.devDependencies) package_json.devDependencies = {};
		init.writePackageJSON('package.json', package_json);

		// Install NPM packages
		var npms = ['grunt', 'matchdep'];
		if (js) npms.push('grunt-contrib-concat', 'grunt-contrib-uglify', 'grunt-contrib-jshint');
		if (stylus || sweet || js) npms.push('grunt-contrib-watch');
		if (stylus) npms.push('grunt-contrib-stylus');
		if (sweet) npms.push('grunt-contrib-connect', 'grunt-sweet');
		if (imgo) npms.push('grunt-imgo');
		if (compress) npms.push('grunt-contrib-compress');

		var args = ['install'];
		Array.prototype.push.apply(args, npms);
		args.push('--save-dev');
		grunt.log.writeln();
		grunt.log.write('Installing NPM packages...');
		grunt.util.spawn({
			cmd: 'npm',
			args: args
		}, function(err, result, code) {
			if (err) {
				grunt.log.writeln();
				grunt.fail.warn(result.stderr);
			}
			grunt.log.ok();
			done();
		});

	});

};
<commit_msg>Gruntfile: fix newlines. Add more paths to imgo.<commit_after>/**
 * Gruntfile template for Grunt
 *
 * @author Artem Sapegin, http://sapegin.me
 */

exports.description = 'Creates a Gruntfile.coffee gruntfile.';
exports.warnOn = ['grunt.js', 'Gruntfile.js', 'Gruntfile.coffee'];

exports.template = function(grunt, init, done) {
	'use strict';

	var fs = require('fs');
	var utils = require('../_src/utils').init(grunt, init);

	init.process({}, [
		// Prompt for these values
		{
			name: 'js',
			message: 'JavaScript?',
			'default': 'Y/n',
			warning: 'Yes: uglify + concat tasks. No: nothing to see here.'
		},
		{
			name: 'stylus',
			message: 'Stylus?',
			'default': 'Y/n',
			warning: 'Yes: stylus task. No: nothing to see here.'
		},
		{
			name: 'sweet',
			message: 'Sweet?',
			'default': 'y/N',
			warning: 'Yes: sweet task. No: nothing to see here.'
		},
		{
			name: 'fingerprint',
			message: 'Fingerprint?',
			'default': 'y/N',
			warning: 'Yes: fingerprint task. No: nothing to see here.'
		},
		{
			name: 'imgo',
			message: 'Optimize images?',
			'default': 'y/N',
			warning: 'Yes: imgo task. No: nothing to see here.'
		},
		{
			name: 'compress',
			message: 'Archive task?',
			'default': 'y/N',
			warning: 'Yes: compress task. No: nothing to see here.'
		},
		{
			name: 'library',
			message: 'Library name?',
			'default': 'none',
			warning: 'Name: creates big library banner. None: small copyright banner.'
		}
	], function(err, props) {
		grunt.util._.defaults(props, init.defaults);

		var library = props.library;
		var project_name = library || utils.projectName();
		var stylus = props.stylus = /y/i.test(props.stylus);
		var sweet = props.sweet = !/n/i.test(props.sweet);
		var fingerprint = props.fingerprint = !/n/i.test(props.fingerprint);
		var imgo = props.imgo = !/n/i.test(props.imgo);
		var compress = props.compress = !/n/i.test(props.compress);
		var js = /y/i.test(props.js);
		var package_json = fs.existsSync('package.json') && grunt.file.readJSON('package.json');
		var wordpress = utils.isWordpressTheme();
		var js_script = package_json ? '<%= pkg.name %>' : 'scripts';

		// Guess at some directories, if they exist
		var htdocs_dir = utils.preferDir(['htdocs', 'www']);
		var htdocs_prefix = htdocs_dir ? htdocs_dir + '/' : '';
		var lib_dir = utils.preferDir([htdocs_prefix + 'js/mylibs', htdocs_prefix + 'lib', htdocs_prefix + 'src']);
		var images_dir = utils.preferDir(['images', 'img', 'i']);

		// JS libraries
		props.jquery = grunt.file.expand('**/jquery*.js').length > 0;

		// Config
		var cfg = {};

		if (js) {
			if (library) {
				if (package_json) {
					cfg.pkg = utils.JS('grunt.file.readJSON("package.json")');
					cfg.banner = utils.JS([
						'"/*! <%= pkg.title || pkg.name %> v<%= pkg.version %>"\\n',
						'" * © <%= pkg.author.name %>, <%= pkg.homepage ? pkg.homepage : "" %>, "',
							'"<%= grunt.template.today(\'yyyy\') %> - "',
							'" Licensed <%= _.pluck(pkg.licenses, \'type\').join(\', \') %> */"\\n'
					].join(',\n'));
				}
				else {
					cfg.version = '0.0.0';
					cfg.banner = utils.JS([
						'"/*! ' + library + ' v<%= version %>' + '"\\n',
						'" * © ' + props.author_name + ', ' + props.author_url + ', <%= grunt.template.today(\'yyyy\') %> - ' +
							'Licensed MIT */"\\n'
					].join(',\n'));
				}
			}
			else {
				cfg.banner = '/*! Author: ' + props.author_name + ', ' + props.author_url + ', ' +
					"<%= grunt.template.today('yyyy') %> */\\n";
			}

			cfg.jshint = {
				options: {
					jshintrc: '.jshintrc'
				},
				files: []
			};
			if (library) {
				cfg.jshint.files.push(lib_dir + '/*.js');
			}
			else {
				cfg.jshint.files.push('js/mylibs/*.js', htdocs_prefix + 'js/main.js');
			}

			cfg.concat = {
				main: {
					src: [
						htdocs_prefix + 'js/utils.js',
						htdocs_prefix + 'blocks/*/*.js',
						htdocs_prefix + 'js/main.js'
					],
					dest: htdocs_prefix + 'build/' + js_script + '.js'
				}
			};

			cfg.uglify = {
				main: {
					options: {
						banner: '<%= banner %>',
						compress: {
							global_defs: {
								DEBUG: utils.JS('debug')
							}
						}
					},
					files: {
						'<%= concat.main.dest %>': '<%= concat.main.dest %>'
					}
				}
			};
		}

		if (stylus) {
			cfg.stylus = {
				compile: {
					options: {
						'include css': true,
						'define': {
							DEBUG: utils.JS('debug')
						},
						'paths': ['blocks']
					},
					files: {}
				}
			};

			cfg.stylus.compile.files[wordpress ? 'style.css' : htdocs_prefix + 'build/styles.css'] = 'styles/index.styl';
		}

		if (sweet) {
			cfg.sweet = {
				content_dir: 'content',
				publish_dir: htdocs_dir || 'htdocs',
				templates_dir: 'templates',
				default_template_id: 'template',
				langs: ['ru', 'en'],
				url_prefixes: {
					ru: '/ru/',
					en: '/'
				},
				uri_prefixes: {
					ru: '/ru/',
					en: '/'
				},
				files: {
					css: {
						path: htdocs_prefix + 'build/styles.css',
						href: '/build/styles.css?{version}'
					},
					js: {
						path: htdocs_prefix + 'build/scripts.js',
						href: '/build/scripts.js?{version}'
					}
				}
			};
		}

		if (fingerprint) {
			cfg.fingerprint = {
				assets: {
					src: [
						'build/*.js',
						'build/*.css'
					],
					filename: 'build/fingerprint.txt'
				}
			};

			if (wordpress) {
				cfg.fingerprint.assets.filename = 'fingerprint.php';
				cfg.fingerprint.assets.template = "<?php define('V', '<%= fingerprint %>'); ?>";
			}
		}

		if (imgo) {
			cfg.imgo = {
				images: {
					src: [
						htdocs_prefix + (images_dir ? images_dir : 'images') + '/**',  // Design images
						htdocs_prefix + 'media/**',  // Site media
						htdocs_prefix + 'build/*.png'  // Generated sprites
					]
				}
			};
		}

		if (compress) {
			cfg.compress = {
				pack: {
					files: {}
				}
			};
			cfg.compress.pack.files['.zip'] = [
				'{styles,js,images,media,build}/**/*',
				'*.{html,coffee}'
			];
		}

		if (stylus || sweet || js) {
			cfg.watch = {};
			if (js) {
				cfg.watch.concat = {
					files: '<%= concat.main.src %>',
					tasks: 'concat'
				};
			}
			if (stylus) {
				cfg.watch.stylus = {
					files: 'styles/**',
					tasks: 'stylus'
				};
			}
			if (sweet) {
				cfg.watch.sweet = {
					files: [
						'<%= sweet.content_dir %>/**',
						'<%= sweet.templates_dir %>/**'
					],
					tasks: 'sweet'
				};
			}
		}

		if (sweet) {
			cfg.connect = {
				port: 8000,
				base: '<%= sweet.publish_dir %>'
			};
		}

		props.config = utils.jsToCoffeString(cfg, 2);

		// Default task
		var defaults = [];
		if (imgo) defaults.push('imgo');
		if (stylus) defaults.push('stylus');
		if (js) defaults.push('jshint', 'concat', 'uglify');
		if (sweet) defaults.push('sweet');
		if (fingerprint) defaults.push('fingerprint');
		props.defaults = utils.arrayToString(defaults);

		// Deploy task
		utils.removeFromArray(defaults, 'jshint');
		utils.removeFromArray(defaults, 'imgo');
		props.deploy = utils.arrayToString(defaults);

		// Files to copy (and process).
		var files = init.filesToCopy(props);
		delete files['Gruntfile.js'];

		// Actually copy (and process) files
		init.copyAndProcess(files, props);

		utils.writeEditorConfig();
		if (js) utils.writeJsHintRc();

		// package.json
		if (!package_json) package_json = {};
		if (!package_json.name) package_json.name = project_name;
		if (!package_json.version) package_json.version = '0.0.0';
		if (!package_json.devDependencies) package_json.devDependencies = {};
		init.writePackageJSON('package.json', package_json);

		// Install NPM packages
		var npms = ['grunt', 'matchdep'];
		if (js) npms.push('grunt-contrib-concat', 'grunt-contrib-uglify', 'grunt-contrib-jshint');
		if (stylus || sweet || js) npms.push('grunt-contrib-watch');
		if (stylus) npms.push('grunt-contrib-stylus');
		if (sweet) npms.push('grunt-contrib-connect', 'grunt-sweet');
		if (imgo) npms.push('grunt-imgo');
		if (compress) npms.push('grunt-contrib-compress');

		var args = ['install'];
		Array.prototype.push.apply(args, npms);
		args.push('--save-dev');
		grunt.log.writeln();
		grunt.log.write('Installing NPM packages...');
		grunt.util.spawn({
			cmd: 'npm',
			args: args
		}, function(err, result, code) {
			if (err) {
				grunt.log.writeln();
				grunt.fail.warn(result.stderr);
			}
			grunt.log.ok();
			done();
		});

	});

};
<|endoftext|>


<commit_before>;(function ($, StateManager, window) {
    'use strict';

    var msPointerEnabled = window.navigator.msPointerEnabled,
        $body = $('body');

    /**
     * Shopware Search Plugin.
     *
     * The plugin controlling the search field behaviour in all possible states
     */
    $.plugin('swSearch', {

        defaults: {

            /**
             * Class which will be added when the drop down was triggered
             *
             * @type {String}
             */
            activeCls: 'is--active',

            /**
             * Class which will be used for generating search results
             *
             * @type {String}
             */
            searchFieldSelector: '.main-search--field',

            /**
             * Selector for the search result list.
             *
             * @type {String}
             */
            resultsSelector: '.main-search--results',

            /**
             * Selector for the link in a result entry.
             *
             * @type {String}
             */
            resultLinkSelector: '.search-result--link',

            /**
             * Selector for a single result entry.
             *
             * @type {String}
             */
            resultItemSelector: '.result--item',

            /**
             * Selector for the ajax loading indicator.
             *
             * @type {String}
             */
            loadingIndicatorSelector: '.form--ajax-loader',

            /**
             * Selector for the main header element.
             * On mobile viewport the header get an active class when the
             * search bar is opened for additional styling.
             *
             * @type {String}
             */
            headerSelector: '.header-main',

            /**
             * Gets added when the search bar is active on mobile viewport.
             * Handles additional styling.
             *
             * @type {String}
             */
            activeHeaderClass: 'is--active-searchfield',

            /**
             * Selector for the ajax loading indicator.
             *
             * @type {String}
             */
            triggerSelector: '.entry--trigger',

            /**
             * The URL used for the search request.
             * This option has to be set or an error will be thrown.
             *
             * @type {String}
             */
            requestUrl: '',

            /**
             * Flag whether or not the keyboard navigation is enabled
             *
             * @type {Boolean}
             */
            keyBoardNavigation: true,

            /**
             * Whether or not the active class is set by default
             *
             * @type {String}
             */
            activeOnStart: false,

            /**
             * Minimum amount of characters needed to trigger the search request
             *
             * @type {Number}
             */
            minLength: 3,

            /**
             * Time in milliseconds to wait after each key down event before
             * before starting the search request.
             * If a key was pressed in this time, the last request will be aborted.
             *
             * @type {Number}
             */
            searchDelay: 250,

            /**
             * The speed of all animations.
             *
             * @type {String|Number}
             */
            animationSpeed: 200,

            /**
             * The kay mapping for navigation the search results via keyboard.
             *
             * @type {Object}
             */
            keyMap: {
                'UP': 38,
                'DOWN': 40,
                'ENTER': 13
            }
        },

        /**
         * Initializes the plugin
         *
         * @public
         * @method init
         */
        init: function () {
            var me = this,
                $el = me.$el,
                opts = me.opts;

            me.applyDataAttributes();

            /**
             * The URL to which the search term will send via AJAX
             *
             * @public
             * @property requestURL
             * @type {String}
             */
            me.requestURL = opts.requestUrl || window.controller.ajax_search;

            if (!me.requestURL) {
                throw new Error('Parameter "requestUrl" needs to be set.');
            }

            /**
            * Converts the url to a protocol relative url, so we don't need to manually
            * check the used http protocol. See the example from paul irish to get an idea
            * how it should work:
            *    `http://www.paulirish.com/2010/the-protocol-relative-url/`
            *    `http://blog.httpwatch.com/2010/02/10/using-protocol-relative-urls-to-switch-between-http-and-https/`
            *
            * @param {String} url - the url which needs to be converted
            * @returns {String} converted string
            */
            var convertUrlToRelativeUrl = function(url) {
                url = url.replace('https:', '');
                url = url.replace('http:', '');

                return url;
            };

            me.requestURL = convertUrlToRelativeUrl(me.requestURL);

            /**
             * The search field itself.
             *
             * @public
             * @property $searchfield
             * @type {jQuery}
             */
            me.$searchField = $el.find(opts.searchFieldSelector);

            /**
             * The list in which the top results will be shown
             *
             * @public
             * @property $results
             * @type {jQuery}
             */
            me.$results = $el.find(opts.resultsSelector);

            /**
             * The loading indicator thats inside the search
             *
             * @public
             * @property $loader
             * @type {jQuery}
             */
            me.$loader = $el.find(opts.loadingIndicatorSelector);

            /**
             * The button to toggle the search field on mobile viewport
             *
             * @public
             * @property $toggleSearchBtn
             * @type {jQuery}
             */
            me.$toggleSearchBtn = $el.find(opts.triggerSelector);

            /**
             * The shop header to add a new class after opening
             *
             * @public
             * @property $mainHeader
             * @type {jQuery}
             */
            me.$mainHeader = $(opts.headerSelector);

            /**
             * The last search term that was entered in the search field.
             *
             * @public
             * @property lastSearchTerm
             * @type {String}
             */
            me.lastSearchTerm = '';

            /**
             * Timeout ID of the key up event.
             * The timeout is used to buffer fast key events.
             *
             * @public
             * @property keyupTimeout
             * @type {Number}
             */
            me.keyupTimeout = 0;

            /**
             * Indicates if the form is already submitted
             *
             * @type {boolean}
             * @private
             */
            me._isSubmitting = false;

            me.registerListeners();
        },

        /**
         * Registers all necessary events for the plugin.
         *
         * @public
         * @method registerListeners
         */
        registerListeners: function () {
            var me = this,
                opts = me.opts,
                $searchField = me.$searchField,
                $formElement = me.$searchField.closest('form');

            me._on($searchField, 'keyup', $.proxy(me.onKeyUp, me));
            me._on($searchField, 'keydown', $.proxy(me.onKeyDown, me));
            me._on(me.$toggleSearchBtn, 'click', $.proxy(me.onClickSearchEntry, me));
            me._on($formElement, 'submit', $.proxy(me.onSubmit, me));

            if (msPointerEnabled) {
                me.$results.on('click', opts.resultLinkSelector, function (event) {
                    window.location.href = $(event.currentTarget).attr('href');
                });
            }

            StateManager.registerListener({
                state: 'xs',
                enter: function () {
                    if (opts.activeOnStart) {
                        me.openMobileSearch();
                    }
                },
                exit: function () {
                    me.closeMobileSearch();
                }
            });

            $.publish('plugin/swSearch/onRegisterEvents', [ me ]);
        },

        /**
         * Event handler method which will be fired when the user presses a key when
         * focusing the field.
         *
         * @public
         * @method onKeyDown
         * @param {jQuery.Event} event
         */
        onKeyDown: function (event) {
            var me = this,
                opts = me.opts,
                keyMap = opts.keyMap,
                keyCode = event.which,
                navKeyPressed = opts.keyBoardNavigation && (keyCode === keyMap.UP || keyCode === keyMap.DOWN || keyCode === keyMap.ENTER);

            $.publish('plugin/swSearch/onKeyDown', [ me, event ]);

            if (navKeyPressed && me.$results.hasClass(opts.activeCls)) {
                me.onKeyboardNavigation(keyCode);
                event.preventDefault();
                return false;
            }

            return true;
        },

        /**
         * Will be called when a key was released on the search field.
         *
         * @public
         * @method onKeyUp
         * @param {jQuery.Event} event
         */
        onKeyUp: function (event) {
            var me = this,
                opts = me.opts,
                term = me.$searchField.val() + '',
                timeout = me.keyupTimeout;

            $.publish('plugin/swSearch/onKeyUp', [ me, event ]);

            if (timeout) {
                window.clearTimeout(timeout);
            }

            if (term.length < opts.minLength) {
                me.lastSearchTerm = '';
                me.closeResult();
                return;
            }

            if (term === me.lastSearchTerm) {
                return;
            }

            me.keyupTimeout = window.setTimeout($.proxy(me.triggerSearchRequest, me, term), opts.searchDelay);
        },

        /**
         * Blocks further submit events to throttle requests to the server
         *
         * @param event
         */
        onSubmit: function (event) {
            var me = this;

            if (me._isSubmitting) {
                event.preventDefault();
                return;
            }

            me._isSubmitting = true;
        },

        /**
         * Triggers an AJAX request with the given search term.
         *
         * @public
         * @method triggerSearchRequest
         * @param {String} searchTerm
         */
        triggerSearchRequest: function (searchTerm) {
            var me = this;

            me.$loader.fadeIn(me.opts.animationSpeed);

            me.lastSearchTerm = $.trim(searchTerm);

            $.publish('plugin/swSearch/onSearchRequest', [ me, searchTerm ]);

            $.ajax({
                'url': me.requestURL,
                'data': {
                    'sSearch': me.lastSearchTerm
                },
                'success': function (response) {
                    me.showResult(response);

                    $.publish('plugin/swSearch/onSearchResponse', [ me, searchTerm, response ]);
                }
            });
        },

        /**
         * Clears the result list and appends the given (AJAX) response to it.
         *
         * @public
         * @method showResult
         * @param {String} response
         */
        showResult: function (response) {
            var me = this,
                opts = me.opts;

            me.$loader.fadeOut(opts.animationSpeed);
            me.$results.empty().html(response).addClass(opts.activeCls).show();

            if (!StateManager.isCurrentState('xs')) {
                $body.on(me.getEventName('click touchstart'), $.proxy(me.onClickBody, me));
            }

            picturefill();

            $.publish('plugin/swSearch/onShowResult', [ me ]);
        },

        /**
         * Closes the result list and removes all its items.
         *
         * @public
         * @method closeResult
         */
        closeResult: function () {
            var me = this;

            me.$results.removeClass(me.opts.activeCls).hide().empty();

            $.publish('plugin/swSearch/onCloseResult', [ me ]);
        },

        /**
         * Called when the body was clicked after the search field went active.
         * Closes the search field and results.
         *
         * @public
         * @method onClickBody
         * @param {jQuery.Event} event
         */
        onClickBody: function (event) {
            var me = this,
                target = event.target,
                pluginEl = me.$el[0],
                resultsEl = me.$results[0];

            if (target === pluginEl || target === resultsEl || $.contains(pluginEl, target) || $.contains(resultsEl, target)) {
                return;
            }

            $body.off(me.getEventName('click touchstart'));

            me.closeMobileSearch();
        },

        /**
         * Adds support to navigate using the keyboard.
         *
         * @public
         * @method onKeyboardNavigation
         * @param {Number} keyCode
         */
        onKeyboardNavigation: function (keyCode) {
            var me = this,
                opts = me.opts,
                keyMap = opts.keyMap,
                $results = me.$results,
                activeClass = opts.activeCls,
                $selected = $results.find('.' + activeClass),
                $resultItems;

            $.publish('plugin/swSearch/onKeyboardNavigation', [ me, keyCode ]);

            if (keyCode === keyMap.UP || keyCode === keyMap.DOWN) {
                $resultItems = $results.find(opts.resultItemSelector);

                // First time the user hits the navigation key "DOWN"
                if (!$selected.length && keyCode == keyMap.DOWN) {
                    me.selectFirstResultItem($resultItems);
                    return;
                }

                // First time the user hits the navigation key "UP"
                if (!$selected.length && keyCode == keyMap.UP) {
                    me.selectLastResultItem($resultItems);
                    return;
                }

                $resultItems.removeClass(activeClass);
                if (me.selectResultItem(keyCode, $selected)) {
                    return;
                }
            }

            // Start on top or bottom if the user reached the end of the list
            switch (keyCode) {
            case keyMap.DOWN:
                me.selectFirstResultItem($resultItems);
                break;
            case keyMap.UP:
                me.selectLastResultItem($resultItems);
                break;
            case keyMap.ENTER:
                me.onPressEnter($selected);
                break;
            }
        },

        /**
         * onClickSearchTrigger event for displaying and hiding
         * the search field
         *
         * @public
         * @method onClickSearchEntry
         * @param event
         */
        onClickSearchEntry: function (event) {
            var me = this,
                $el = me.$el,
                opts = me.opts;

            $.publish('plugin/swSearch/onClickSearchEntry', [ me, event ]);

            if (!StateManager.isCurrentState('xs')) {
                return;
            }

            event.preventDefault();
            event.stopPropagation();

            $el.hasClass(opts.activeCls) ? me.closeMobileSearch() : me.openMobileSearch();
        },

        /**
         * Opens the mobile search bar and focuses it.
         *
         * @public
         * @method openMobileSearch
         */
        openMobileSearch: function () {
            var me = this,
                $el = me.$el,
                opts = me.opts,
                activeCls = opts.activeCls;

            $body.on(me.getEventName('click touchstart'), $.proxy(me.onClickBody, me));

            $el.addClass(activeCls);
            me.$toggleSearchBtn.addClass(activeCls);
            me.$mainHeader.addClass(opts.activeHeaderClass);

            me.$searchField.focus();

            $.publish('plugin/swSearch/onOpenMobileSearch', [ me ]);
        },

        /**
         * Closes the mobile search bar and removes its focus.
         *
         * @public
         * @method closeMobileSearch
         */
        closeMobileSearch: function () {
            var me = this,
                $el = me.$el,
                opts = me.opts,
                activeCls = opts.activeCls;

            $el.removeClass(activeCls);
            me.$toggleSearchBtn.removeClass(activeCls);
            me.$mainHeader.removeClass(opts.activeHeaderClass);

            me.$searchField.blur();

            $.publish('plugin/swSearch/onCloseMobileSearch', [ me ]);

            me.closeResult();
        },

        /**
         * @param {Object} resultItems
         */
        selectFirstResultItem: function (resultItems) {
            var me = this,
                opts = me.opts,
                activeClass = opts.activeCls;

            $.publish('plugin/swSearch/onSelectFirstResultItem', [ me, resultItems ]);

            resultItems.first().addClass(activeClass);
        },

        /**
         * @param {Object} resultItems
         */
        selectLastResultItem: function (resultItems) {
            var me = this,
                opts = me.opts,
                activeClass = opts.activeCls;

            $.publish('plugin/swSearch/onSelectLastResultItem', [ me, resultItems ]);

            resultItems.last().addClass(activeClass);
        },

        /**
         * Selects the next or previous result item based on the pressed navigation key.
         *
         * @param {Number} keyCode
         * @param {Object} $selected
         */
        selectResultItem: function (keyCode, $selected) {
            var me = this,
                opts = me.opts,
                keyMap = opts.keyMap,
                activeClass = opts.activeCls,
                $nextSibling;

            $.publish('plugin/swSearch/onSelectNextResultItem', [ me, keyCode ]);

            $nextSibling = $selected[(keyCode === keyMap.DOWN) ? 'next' : 'prev'](opts.resultItemSelector);
            if ($nextSibling.length) {
                $nextSibling.addClass(activeClass);
                return true;
            }
            return false;
        },

        /**
         * Redirects the user to the search result page on enter.
         *
         * @param {Object} $selected
         */
        onPressEnter: function ($selected) {
            var me = this,
                opts = me.opts;

            $.publish('plugin/swSearch/onPressEnter', [ me, $selected ]);

            if ($selected.length) {
                window.location.href = $selected.find(opts.resultLinkSelector).attr('href');
                return;
            }

            me.$parent.submit();
        },

        /**
         * Destroys the plugin and removes registered events.
         *
         * @public
         * @method destroy
         */
        destroy: function () {
            var me = this;

            me.closeMobileSearch();

            $body.off(me.getEventName('click touchstart'));

            me._destroy();
        }
    });
})(jQuery, StateManager, window);
<commit_msg>SW-18231 - Abort previous ajax-requests Fixes https://github.com/shopware/shopware/pull/1057<commit_after>;(function ($, StateManager, window) {
    'use strict';

    var msPointerEnabled = window.navigator.msPointerEnabled,
        $body = $('body');

    /**
     * Shopware Search Plugin.
     *
     * The plugin controlling the search field behaviour in all possible states
     */
    $.plugin('swSearch', {

        defaults: {

            /**
             * Class which will be added when the drop down was triggered
             *
             * @type {String}
             */
            activeCls: 'is--active',

            /**
             * Class which will be used for generating search results
             *
             * @type {String}
             */
            searchFieldSelector: '.main-search--field',

            /**
             * Selector for the search result list.
             *
             * @type {String}
             */
            resultsSelector: '.main-search--results',

            /**
             * Selector for the link in a result entry.
             *
             * @type {String}
             */
            resultLinkSelector: '.search-result--link',

            /**
             * Selector for a single result entry.
             *
             * @type {String}
             */
            resultItemSelector: '.result--item',

            /**
             * Selector for the ajax loading indicator.
             *
             * @type {String}
             */
            loadingIndicatorSelector: '.form--ajax-loader',

            /**
             * Selector for the main header element.
             * On mobile viewport the header get an active class when the
             * search bar is opened for additional styling.
             *
             * @type {String}
             */
            headerSelector: '.header-main',

            /**
             * Gets added when the search bar is active on mobile viewport.
             * Handles additional styling.
             *
             * @type {String}
             */
            activeHeaderClass: 'is--active-searchfield',

            /**
             * Selector for the ajax loading indicator.
             *
             * @type {String}
             */
            triggerSelector: '.entry--trigger',

            /**
             * The URL used for the search request.
             * This option has to be set or an error will be thrown.
             *
             * @type {String}
             */
            requestUrl: '',

            /**
             * Flag whether or not the keyboard navigation is enabled
             *
             * @type {Boolean}
             */
            keyBoardNavigation: true,

            /**
             * Whether or not the active class is set by default
             *
             * @type {String}
             */
            activeOnStart: false,

            /**
             * Minimum amount of characters needed to trigger the search request
             *
             * @type {Number}
             */
            minLength: 3,

            /**
             * Time in milliseconds to wait after each key down event before
             * before starting the search request.
             * If a key was pressed in this time, the last request will be aborted.
             *
             * @type {Number}
             */
            searchDelay: 250,

            /**
             * The speed of all animations.
             *
             * @type {String|Number}
             */
            animationSpeed: 200,

            /**
             * The kay mapping for navigation the search results via keyboard.
             *
             * @type {Object}
             */
            keyMap: {
                'UP': 38,
                'DOWN': 40,
                'ENTER': 13
            }
        },

        /**
         * Initializes the plugin
         *
         * @public
         * @method init
         */
        init: function () {
            var me = this,
                $el = me.$el,
                opts = me.opts;

            me.applyDataAttributes();

            /**
             * The URL to which the search term will send via AJAX
             *
             * @public
             * @property requestURL
             * @type {String}
             */
            me.requestURL = opts.requestUrl || window.controller.ajax_search;

            if (!me.requestURL) {
                throw new Error('Parameter "requestUrl" needs to be set.');
            }

            /**
            * Converts the url to a protocol relative url, so we don't need to manually
            * check the used http protocol. See the example from paul irish to get an idea
            * how it should work:
            *    `http://www.paulirish.com/2010/the-protocol-relative-url/`
            *    `http://blog.httpwatch.com/2010/02/10/using-protocol-relative-urls-to-switch-between-http-and-https/`
            *
            * @param {String} url - the url which needs to be converted
            * @returns {String} converted string
            */
            var convertUrlToRelativeUrl = function(url) {
                url = url.replace('https:', '');
                url = url.replace('http:', '');

                return url;
            };

            me.requestURL = convertUrlToRelativeUrl(me.requestURL);

            /**
             * The search field itself.
             *
             * @public
             * @property $searchfield
             * @type {jQuery}
             */
            me.$searchField = $el.find(opts.searchFieldSelector);

            /**
             * The list in which the top results will be shown
             *
             * @public
             * @property $results
             * @type {jQuery}
             */
            me.$results = $el.find(opts.resultsSelector);

            /**
             * The loading indicator thats inside the search
             *
             * @public
             * @property $loader
             * @type {jQuery}
             */
            me.$loader = $el.find(opts.loadingIndicatorSelector);

            /**
             * The button to toggle the search field on mobile viewport
             *
             * @public
             * @property $toggleSearchBtn
             * @type {jQuery}
             */
            me.$toggleSearchBtn = $el.find(opts.triggerSelector);

            /**
             * The shop header to add a new class after opening
             *
             * @public
             * @property $mainHeader
             * @type {jQuery}
             */
            me.$mainHeader = $(opts.headerSelector);

            /**
             * The last search term that was entered in the search field.
             *
             * @public
             * @property lastSearchTerm
             * @type {String}
             */
            me.lastSearchTerm = '';

            /**
             * Timeout ID of the key up event.
             * The timeout is used to buffer fast key events.
             *
             * @public
             * @property keyupTimeout
             * @type {Number}
             */
            me.keyupTimeout = 0;

            /**
             * Indicates if the form is already submitted
             *
             * @type {boolean}
             * @private
             */
            me._isSubmitting = false;

            me.registerListeners();
        },

        /**
         * Registers all necessary events for the plugin.
         *
         * @public
         * @method registerListeners
         */
        registerListeners: function () {
            var me = this,
                opts = me.opts,
                $searchField = me.$searchField,
                $formElement = me.$searchField.closest('form');

            me._on($searchField, 'keyup', $.proxy(me.onKeyUp, me));
            me._on($searchField, 'keydown', $.proxy(me.onKeyDown, me));
            me._on(me.$toggleSearchBtn, 'click', $.proxy(me.onClickSearchEntry, me));
            me._on($formElement, 'submit', $.proxy(me.onSubmit, me));

            if (msPointerEnabled) {
                me.$results.on('click', opts.resultLinkSelector, function (event) {
                    window.location.href = $(event.currentTarget).attr('href');
                });
            }

            StateManager.registerListener({
                state: 'xs',
                enter: function () {
                    if (opts.activeOnStart) {
                        me.openMobileSearch();
                    }
                },
                exit: function () {
                    me.closeMobileSearch();
                }
            });

            $.publish('plugin/swSearch/onRegisterEvents', [ me ]);
        },

        /**
         * Event handler method which will be fired when the user presses a key when
         * focusing the field.
         *
         * @public
         * @method onKeyDown
         * @param {jQuery.Event} event
         */
        onKeyDown: function (event) {
            var me = this,
                opts = me.opts,
                keyMap = opts.keyMap,
                keyCode = event.which,
                navKeyPressed = opts.keyBoardNavigation && (keyCode === keyMap.UP || keyCode === keyMap.DOWN || keyCode === keyMap.ENTER);

            $.publish('plugin/swSearch/onKeyDown', [ me, event ]);

            if (navKeyPressed && me.$results.hasClass(opts.activeCls)) {
                me.onKeyboardNavigation(keyCode);
                event.preventDefault();
                return false;
            }

            return true;
        },

        /**
         * Will be called when a key was released on the search field.
         *
         * @public
         * @method onKeyUp
         * @param {jQuery.Event} event
         */
        onKeyUp: function (event) {
            var me = this,
                opts = me.opts,
                term = me.$searchField.val() + '',
                timeout = me.keyupTimeout;

            $.publish('plugin/swSearch/onKeyUp', [ me, event ]);

            if (timeout) {
                window.clearTimeout(timeout);
            }

            if (term.length < opts.minLength) {
                me.lastSearchTerm = '';
                me.closeResult();
                return;
            }

            if (term === me.lastSearchTerm) {
                return;
            }

            me.keyupTimeout = window.setTimeout($.proxy(me.triggerSearchRequest, me, term), opts.searchDelay);
        },

        /**
         * Blocks further submit events to throttle requests to the server
         *
         * @param event
         */
        onSubmit: function (event) {
            var me = this;

            if (me._isSubmitting) {
                event.preventDefault();
                return;
            }

            me._isSubmitting = true;
        },

        /**
         * Triggers an AJAX request with the given search term.
         *
         * @public
         * @method triggerSearchRequest
         * @param {String} searchTerm
         */
        triggerSearchRequest: function (searchTerm) {
            var me = this;

            me.$loader.fadeIn(me.opts.animationSpeed);

            me.lastSearchTerm = $.trim(searchTerm);

            $.publish('plugin/swSearch/onSearchRequest', [ me, searchTerm ]);

            if (me.lastSearchAjax){
                me.lastSearchAjax.abort();
            }

            me.lastSearchAjax = $.ajax({
                'url': me.requestURL,
                'data': {
                    'sSearch': me.lastSearchTerm
                },
                'success': function (response) {
                    me.showResult(response);

                    $.publish('plugin/swSearch/onSearchResponse', [ me, searchTerm, response ]);
                }
            });
        },

        /**
         * Clears the result list and appends the given (AJAX) response to it.
         *
         * @public
         * @method showResult
         * @param {String} response
         */
        showResult: function (response) {
            var me = this,
                opts = me.opts;

            me.$loader.fadeOut(opts.animationSpeed);
            me.$results.empty().html(response).addClass(opts.activeCls).show();

            if (!StateManager.isCurrentState('xs')) {
                $body.on(me.getEventName('click touchstart'), $.proxy(me.onClickBody, me));
            }

            picturefill();

            $.publish('plugin/swSearch/onShowResult', [ me ]);
        },

        /**
         * Closes the result list and removes all its items.
         *
         * @public
         * @method closeResult
         */
        closeResult: function () {
            var me = this;

            me.$results.removeClass(me.opts.activeCls).hide().empty();

            $.publish('plugin/swSearch/onCloseResult', [ me ]);
        },

        /**
         * Called when the body was clicked after the search field went active.
         * Closes the search field and results.
         *
         * @public
         * @method onClickBody
         * @param {jQuery.Event} event
         */
        onClickBody: function (event) {
            var me = this,
                target = event.target,
                pluginEl = me.$el[0],
                resultsEl = me.$results[0];

            if (target === pluginEl || target === resultsEl || $.contains(pluginEl, target) || $.contains(resultsEl, target)) {
                return;
            }

            $body.off(me.getEventName('click touchstart'));

            me.closeMobileSearch();
        },

        /**
         * Adds support to navigate using the keyboard.
         *
         * @public
         * @method onKeyboardNavigation
         * @param {Number} keyCode
         */
        onKeyboardNavigation: function (keyCode) {
            var me = this,
                opts = me.opts,
                keyMap = opts.keyMap,
                $results = me.$results,
                activeClass = opts.activeCls,
                $selected = $results.find('.' + activeClass),
                $resultItems;

            $.publish('plugin/swSearch/onKeyboardNavigation', [ me, keyCode ]);

            if (keyCode === keyMap.UP || keyCode === keyMap.DOWN) {
                $resultItems = $results.find(opts.resultItemSelector);

                // First time the user hits the navigation key "DOWN"
                if (!$selected.length && keyCode == keyMap.DOWN) {
                    me.selectFirstResultItem($resultItems);
                    return;
                }

                // First time the user hits the navigation key "UP"
                if (!$selected.length && keyCode == keyMap.UP) {
                    me.selectLastResultItem($resultItems);
                    return;
                }

                $resultItems.removeClass(activeClass);
                if (me.selectResultItem(keyCode, $selected)) {
                    return;
                }
            }

            // Start on top or bottom if the user reached the end of the list
            switch (keyCode) {
            case keyMap.DOWN:
                me.selectFirstResultItem($resultItems);
                break;
            case keyMap.UP:
                me.selectLastResultItem($resultItems);
                break;
            case keyMap.ENTER:
                me.onPressEnter($selected);
                break;
            }
        },

        /**
         * onClickSearchTrigger event for displaying and hiding
         * the search field
         *
         * @public
         * @method onClickSearchEntry
         * @param event
         */
        onClickSearchEntry: function (event) {
            var me = this,
                $el = me.$el,
                opts = me.opts;

            $.publish('plugin/swSearch/onClickSearchEntry', [ me, event ]);

            if (!StateManager.isCurrentState('xs')) {
                return;
            }

            event.preventDefault();
            event.stopPropagation();

            $el.hasClass(opts.activeCls) ? me.closeMobileSearch() : me.openMobileSearch();
        },

        /**
         * Opens the mobile search bar and focuses it.
         *
         * @public
         * @method openMobileSearch
         */
        openMobileSearch: function () {
            var me = this,
                $el = me.$el,
                opts = me.opts,
                activeCls = opts.activeCls;

            $body.on(me.getEventName('click touchstart'), $.proxy(me.onClickBody, me));

            $el.addClass(activeCls);
            me.$toggleSearchBtn.addClass(activeCls);
            me.$mainHeader.addClass(opts.activeHeaderClass);

            me.$searchField.focus();

            $.publish('plugin/swSearch/onOpenMobileSearch', [ me ]);
        },

        /**
         * Closes the mobile search bar and removes its focus.
         *
         * @public
         * @method closeMobileSearch
         */
        closeMobileSearch: function () {
            var me = this,
                $el = me.$el,
                opts = me.opts,
                activeCls = opts.activeCls;

            $el.removeClass(activeCls);
            me.$toggleSearchBtn.removeClass(activeCls);
            me.$mainHeader.removeClass(opts.activeHeaderClass);

            me.$searchField.blur();

            $.publish('plugin/swSearch/onCloseMobileSearch', [ me ]);

            me.closeResult();
        },

        /**
         * @param {Object} resultItems
         */
        selectFirstResultItem: function (resultItems) {
            var me = this,
                opts = me.opts,
                activeClass = opts.activeCls;

            $.publish('plugin/swSearch/onSelectFirstResultItem', [ me, resultItems ]);

            resultItems.first().addClass(activeClass);
        },

        /**
         * @param {Object} resultItems
         */
        selectLastResultItem: function (resultItems) {
            var me = this,
                opts = me.opts,
                activeClass = opts.activeCls;

            $.publish('plugin/swSearch/onSelectLastResultItem', [ me, resultItems ]);

            resultItems.last().addClass(activeClass);
        },

        /**
         * Selects the next or previous result item based on the pressed navigation key.
         *
         * @param {Number} keyCode
         * @param {Object} $selected
         */
        selectResultItem: function (keyCode, $selected) {
            var me = this,
                opts = me.opts,
                keyMap = opts.keyMap,
                activeClass = opts.activeCls,
                $nextSibling;

            $.publish('plugin/swSearch/onSelectNextResultItem', [ me, keyCode ]);

            $nextSibling = $selected[(keyCode === keyMap.DOWN) ? 'next' : 'prev'](opts.resultItemSelector);
            if ($nextSibling.length) {
                $nextSibling.addClass(activeClass);
                return true;
            }
            return false;
        },

        /**
         * Redirects the user to the search result page on enter.
         *
         * @param {Object} $selected
         */
        onPressEnter: function ($selected) {
            var me = this,
                opts = me.opts;

            $.publish('plugin/swSearch/onPressEnter', [ me, $selected ]);

            if ($selected.length) {
                window.location.href = $selected.find(opts.resultLinkSelector).attr('href');
                return;
            }

            me.$parent.submit();
        },

        /**
         * Destroys the plugin and removes registered events.
         *
         * @public
         * @method destroy
         */
        destroy: function () {
            var me = this;

            me.closeMobileSearch();

            $body.off(me.getEventName('click touchstart'));

            me._destroy();
        }
    });
})(jQuery, StateManager, window);
<|endoftext|>


<commit_before>var app = angular.module('myApp', ['angularMoment']);
		// http://stackoverflow.com/questions/20858395/how-to-use-ng-repeat-with-filter-and-index
		app.filter('_customFilterByPosition', function() {
			return function (arr, targetIndexes) {
				return arr.filter(function(arrItem, arrPosIndex) {
					_targetIndexesArray = targetIndexes.split(',');
					for (var i = 0; i < _targetIndexesArray.length; i++) {
						var starshipUrl = arrItem.url;
						var starshipId = starshipUrl.match(/\/([^\/]+)\/?$/)[1];
						if(_targetIndexesArray[i]===starshipId) {
							//console.log(arrPosIndex, starshipId)
							return true;
						}
					}
					//console.log(div);
					//return arrPosIndex % div === (val || 0);
					return false;
				});
			};
		});
		app.service('_customRestService', [
			'$http', '$q',
			function($http, $q) {
				var _this = this;
				this.fetch = function (targetedRestUrl, callDepth) {
					var _deferred = $q.defer();

					if(callDepth===undefined) callDepth = 0;

					console.log(targetedRestUrl);
					$http.get(targetedRestUrl)
						.then(function(response) {
							var _resultsArray = response.data.results;
							//var _next = response.data.next.replace("http", "https");

							console.log(callDepth, _resultsArray.length, response.data);
							// if(_next!==null) {
							// 	callDepth++;
							// 	console.log(_next, callDepth);
							// 	_this.fetch(_next, callDepth).then(function(returnedResultArray) {
							// 		_resultsArray = _resultsArray.concat(returnedResultArray);
							// 		_deferred.resolve(_resultsArray);
							// 	});
							// }
							// else {
							// 	_deferred.resolve(_resultsArray);
							// }
							_deferred.resolve(_resultsArray);
		        });

						return _deferred.promise;
				}
			}
		]);
		// =========================================
		app.controller('myCtrl', function($scope, $http, $filter, _customRestService) {
			_customRestService
				.fetch('https://swapi.co/api/starships')
				.then(function(returnedResultArray) {
					//$scope.starships = $filter('filter')(returnedResultArray, {crew: '5'})[0];
					// for (var i = 0; i < returnedResultArray.length; i++) {
					// 	returnedResultArray[i].crewAsInt = parseInt(returnedResultArray[i].crew);
					// 	returnedResultArray[i].crewAsIntHidden = parseInt(returnedResultArray[i].crew);
					// }
					$scope.starships = returnedResultArray;
					$scope.isJsonReady = true;
				});

			$scope.starships = [];
			$scope.firstName= "Wei Khong";
			$scope.lastName= "Teh";
			$scope.isJsonReady = false;
			$scope.orderByFunction = function(starship) {
				return parseInt(starship.crew);
			};
		});<commit_msg>- Fixed HTTPS error (4)<commit_after>var app = angular.module('myApp', ['angularMoment']);
		// http://stackoverflow.com/questions/20858395/how-to-use-ng-repeat-with-filter-and-index
		app.filter('_customFilterByPosition', function() {
			return function (arr, targetIndexes) {
				return arr.filter(function(arrItem, arrPosIndex) {
					_targetIndexesArray = targetIndexes.split(',');
					for (var i = 0; i < _targetIndexesArray.length; i++) {
						var starshipUrl = arrItem.url;
						var starshipId = starshipUrl.match(/\/([^\/]+)\/?$/)[1];
						if(_targetIndexesArray[i]===starshipId) {
							//console.log(arrPosIndex, starshipId)
							return true;
						}
					}
					//console.log(div);
					//return arrPosIndex % div === (val || 0);
					return false;
				});
			};
		});
		app.service('_customRestService', [
			'$http', '$q',
			function($http, $q) {
				var _this = this;
				this.fetch = function (targetedRestUrl, callDepth) {
					var _deferred = $q.defer();

					if(callDepth===undefined) callDepth = 0;

					console.log(targetedRestUrl);
					$http.get(targetedRestUrl)
						.then(function(response) {
							var _resultsArray = response.data.results;
							//var _next = response.data.next.replace("http", "https");

							console.log(callDepth, _resultsArray.length, response.data);
							// if(_next!==null) {
							// 	callDepth++;
							// 	console.log(_next, callDepth);
							// 	_this.fetch(_next, callDepth).then(function(returnedResultArray) {
							// 		_resultsArray = _resultsArray.concat(returnedResultArray);
							// 		_deferred.resolve(_resultsArray);
							// 	});
							// }
							// else {
							// 	_deferred.resolve(_resultsArray);
							// }
							_deferred.resolve(_resultsArray);
		        });

						return _deferred.promise;
				}
			}
		]);
		// =========================================
		app.controller('myCtrl', function($scope, $http, $filter, _customRestService) {
			_customRestService
				.fetch('https://swapi.co/api/starships/')
				.then(function(returnedResultArray) {
					//$scope.starships = $filter('filter')(returnedResultArray, {crew: '5'})[0];
					// for (var i = 0; i < returnedResultArray.length; i++) {
					// 	returnedResultArray[i].crewAsInt = parseInt(returnedResultArray[i].crew);
					// 	returnedResultArray[i].crewAsIntHidden = parseInt(returnedResultArray[i].crew);
					// }
					$scope.starships = returnedResultArray;
					$scope.isJsonReady = true;
				});

			$scope.starships = [];
			$scope.firstName= "Wei Khong";
			$scope.lastName= "Teh";
			$scope.isJsonReady = false;
			$scope.orderByFunction = function(starship) {
				return parseInt(starship.crew);
			};
		});<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc directive
 * @name flowsimUiApp.directive:fginstructionset
 * @description
 * # fginstructionset
 */
angular.module('flowsimUiApp')
    .directive('fgInstructionSet', function() {
        return {
            restrict: 'E',
            templateUrl: 'views/simulation/fgpinstructionsview.html',
            replace: true,
            scope: {
                view: '='
            },
            controller: function($scope) {
                /**
                 * Parses Instruction and return name:(value1, value2)
                 * @param  {[Instruction Object]} val
                 * @return {[String]}     [name:(value1, value2)]
                 */
                $scope.instructionsTooltip = function(ins) {
                    var tip = '';
                    if(ins.tip){
                        tip += ins.tip;
                    }
                    return tip;

                };
            },
            link: function(scope) {

                scope.$watch('view', function(){
                    if(_.isNull(scope.view)){
                      scope.applyActionList = null;
                      scope.writeActionSet = null;

                    }
                }, true);
                scope.$watch('view.applyActions', function(){
                    if(scope.view){
                        if((_.isUndefined(scope.applyActionList) || _.isNull(scope.applyActionList)) && !(_.isUndefined(scope.view.applyActions))){
                            scope.applyActionList = _.clone(scope.view.applyActions);
                        }else {
                            scope.applyActionList.actions.pop();
                        }
                    }
                }, true);

                scope.$watch('view.writeActions', function(){
                    if(scope.view){
                        if((_.isUndefined(scope.writeActionSet) || _.isNull(scope.writeActionSet)) && !(_.isUndefined(scope.view.writeActions))){
                            scope.writeActionSet = _.clone(scope.view.writeActions);
                        }else {
                            scope.writeActionSet.actions.pop();
                        }

                    }
                }, true);

                scope.$watch('view.insList', function(){
                    if(scope.view){
                        scope.instructionList = _(scope.view.insList).map(function(ins){
                            return ins.name;
                        });
                        scope.insListView = scope.instructions();
                        scope.insSetListApplyIdx = _.indexOf(scope.instructionList, 'Apply');
                        scope.insSetListWriteIdx = _.indexOf(scope.instructionList, 'Write');
                    }
                }, true);

                scope.instructions = function() {
                        var insListView = '';
                        _(scope.view.insList).each(function(ins, idx){
                            insListView += ins.shortName;
                            if(idx < scope.instructionList.length - 1){
                                insListView += ' / ';
                            }
                        });
                        return insListView;
                };
            }
        };
    });
<commit_msg>Fix for issue #74<commit_after>'use strict';

/**
 * @ngdoc directive
 * @name flowsimUiApp.directive:fginstructionset
 * @description
 * # fginstructionset
 */
angular.module('flowsimUiApp')
    .directive('fgInstructionSet', function() {
        return {
            restrict: 'E',
            templateUrl: 'views/simulation/fgpinstructionsview.html',
            replace: true,
            scope: {
                view: '='
            },
            controller: function($scope) {
                /**
                 * Parses Instruction and return name:(value1, value2)
                 * @param  {[Instruction Object]} val
                 * @return {[String]}     [name:(value1, value2)]
                 */
                $scope.instructionsTooltip = function(ins) {
                    var tip = '';
                    if(ins.tip){
                        tip += ins.tip;
                    }
                    return tip;

                };
            },
            link: function(scope) {

                scope.$watch('view', function(){
                    if(_.isNull(scope.view)){
                      scope.applyActionList = null;
                      scope.writeActionSet = null;

                    }
                }, true);
                scope.$watch('view.applyActions', function(){
                    if(scope.view){
                        if((_.isUndefined(scope.applyActionList) || _.isNull(scope.applyActionList)) && !(_.isUndefined(scope.view.applyActions))){
                            scope.applyActionList = _.clone(scope.view.applyActions);
                        }else {
                            scope.applyActionList.actions.shift();
                        }
                    }
                }, true);

                scope.$watch('view.writeActions', function(){
                    if(scope.view){
                        if((_.isUndefined(scope.writeActionSet) || _.isNull(scope.writeActionSet)) && !(_.isUndefined(scope.view.writeActions))){
                            scope.writeActionSet = _.clone(scope.view.writeActions);
                        }else {
                            scope.writeActionSet.actions.shift();
                        }

                    }
                }, true);

                scope.$watch('view.insList', function(){
                    if(scope.view){
                        scope.instructionList = _(scope.view.insList).map(function(ins){
                            return ins.name;
                        });
                        scope.insListView = scope.instructions();
                        scope.insSetListApplyIdx = _.indexOf(scope.instructionList, 'Apply');
                        scope.insSetListWriteIdx = _.indexOf(scope.instructionList, 'Write');
                    }
                }, true);

                scope.instructions = function() {
                        var insListView = '';
                        _(scope.view.insList).each(function(ins, idx){
                            insListView += ins.shortName;
                            if(idx < scope.instructionList.length - 1){
                                insListView += ' / ';
                            }
                        });
                        return insListView;
                };
            }
        };
    });
<|endoftext|>


<commit_before>// @flow

import * as React from "react";
import QRCode from 'qrcode.react';
import { parseUrl } from 'query-string';
import ValidationErrors from 'app/lib/ValidationErrors';
import Panel from 'app/components/shared/Panel';
import Button from 'app/components/shared/Button';
import TokenCodeInput from 'app/components/shared/TokenCodeInput';
import buildkiteqr from './buildkite.svg';

type ValidationError = {
  field: string,
  message: string
};

type Props = {
  onActivateOtp: (token: string, callback?: () => void) => void,
  provisioningUri: string
};

type State = {
  errors: ?Array<ValidationError>,
  isActivating: boolean,
  totpCodeValue: string,
  copiedOTPSecretKey: boolean
};

export default class TwoFactorConfigureActivate extends React.PureComponent<Props, State> {
  tokenInputRef: React.Ref<typeof TokenCodeInput>;
  state = {
    errors: [],
    isActivating: false,
    totpCodeValue: '',
    copiedOTPSecretKey: false
  };

  constructor(props: Props) {
    super(props);
    this.tokenInputRef = React.createRef();
  }

  render() {
    const errors = new ValidationErrors(this.state.errors);
    const provisioningUriSecret = parseUrl(this.props.provisioningUri).query.secret;

    return (
      <React.Fragment>

          To activate two-factor authentication, scan this barcode with your authenticator application, then enter the generated One Time Password below.

        <Panel className="mb3">

                <QRCode
                  renderAs="svg"
                  fgColor="currentColor"
                  bgColor="transparent"
                  width="260"
                  height="260"
                  className="block mx-auto"
                  level="H" // approx 30% error correction
                  style={{ maxWidth: '100%' }}
                  value={this.props.provisioningUri}
                />

                Can’t use the barcode?
Type in this secret key instead: {provisioningUriSecret}

        </Panel>

          <TokenCodeInput
            ref={this.tokenInputRef}
            errors={errors.findForField('token')}
            disabled={this.state.isActivating}
            value={this.state.totpCodeValue}
            onChange={this.handleTotpCodeChange}
          />

        <Button
          className="col-12"
          disabled={this.state.isActivating}
          theme="success"
          onClick={this.handleTotpActivate}
        >
          Activate
        </Button>
      </React.Fragment>
    );
  }

  handleTotpCodeChange = (totpCodeValue: string) => {
    this.setState({ totpCodeValue });
  }

  handleTotpActivate = () => {
    this.setState({ isActivating: true }, () => {
      this.props.onActivateOtp(this.state.totpCodeValue, (errors) => {
        if (errors) {
          this.setState({ errors, isActivating: false }, () => {
            if (this.tokenInputRef.current) {
              // $FlowExpectError Typing createRefs seems broken at the moment, hopefully we can fis this at some point.
              this.tokenInputRef.current.focus();
            }
          });
        } else {
          this.setState({ errors: [], isActivating: false });
        }
      });
    });
  }
}
<commit_msg>Fixed eslint errors<commit_after>// @flow

import * as React from "react";
import QRCode from 'qrcode.react';
import { parseUrl } from 'query-string';
import ValidationErrors from 'app/lib/ValidationErrors';
import Panel from 'app/components/shared/Panel';
import Button from 'app/components/shared/Button';
import TokenCodeInput from 'app/components/shared/TokenCodeInput';
import buildkiteqr from './buildkite.svg';

type ValidationError = {
  field: string,
  message: string
};

type Props = {
  onActivateOtp: (token: string, callback?: () => void) => void,
  provisioningUri: string
};

type State = {
  errors: ?Array<ValidationError>,
  isActivating: boolean,
  totpCodeValue: string,
  copiedOTPSecretKey: boolean
};

export default class TwoFactorConfigureActivate extends React.PureComponent<Props, State> {
  tokenInputRef: React.Ref<typeof TokenCodeInput>;
  state = {
    errors: [],
    isActivating: false,
    totpCodeValue: '',
    copiedOTPSecretKey: false
  };

  constructor(props: Props) {
    super(props);
    this.tokenInputRef = React.createRef();
  }

  render() {
    const errors = new ValidationErrors(this.state.errors);
    const provisioningUriSecret = parseUrl(this.props.provisioningUri).query.secret;

    return (
      <React.Fragment>

          To activate two-factor authentication, scan this barcode with your authenticator application, then enter the generated One Time Password below.

        <Panel className="mb3">

                <QRCode
                  renderAs="svg"
                  fgColor="currentColor"
                  bgColor="transparent"
                  width="260"
                  height="260"
                  className="block mx-auto"
                  level="H" // approx 30% error correction
                  style={{ maxWidth: '100%' }}
                  value={this.props.provisioningUri}
                />

                Can’t use the barcode?
Type in this secret key instead: {provisioningUriSecret}

        </Panel>

          <TokenCodeInput
            ref={this.tokenInputRef}
            errors={errors.findForField('token')}
            disabled={this.state.isActivating}
            value={this.state.totpCodeValue}
            onChange={this.handleTotpCodeChange}
          />

        <Button
          className="col-12"
          disabled={this.state.isActivating}
          theme="success"
          onClick={this.handleTotpActivate}
        >
          Activate
        </Button>
      </React.Fragment>
    );
  }

  handleTotpCodeChange = (totpCodeValue: string) => {
    this.setState({ totpCodeValue });
  }

  handleTotpActivate = () => {
    this.setState({ isActivating: true }, () => {
      this.props.onActivateOtp(this.state.totpCodeValue, (errors) => {
        if (errors) {
          this.setState({ errors, isActivating: false }, () => {
            if (this.tokenInputRef.current) {
              // $FlowExpectError Typing createRefs seems broken at the moment, hopefully we can fis this at some point.
              this.tokenInputRef.current.focus();
            }
          });
        } else {
          this.setState({ errors: [], isActivating: false });
        }
      });
    });
  }
}
<|endoftext|>