<commit_before>'use strict';

const Config = require('../util/Config.js');
const Time = require('../util/Time.js');
const Util = require('../util/Util.js');
const ConnectionsGenerator = require('./ConnectionsGenerator.js');
const DistanceHelpers = require('../util/DistanceHelpers');

class ParameterizedConnectionsGenerator extends ConnectionsGenerator {
    constructor(region, routes, pointToLatLon, config) {
        super(region, routes, config);

        this._pointToLatLon = pointToLatLon;

        var param_scope = "connections:";
        this.param_seed = Config.value(config, 'seed', 1); // Random seed
        //this.param_time_initial = 1412158210000; // The initial timestamp
        //this.param_time_final = 1417428610000; // The final timestamp
        this.param_time_initial = Config.value(config, param_scope + 'time_initial', 0); // The initial timestamp
        this.param_time_final = Config.value(config, param_scope + 'time_final', 24 * 3600000 * 31); // The final timestamp
        this.param_connections = Config.value(config, param_scope + 'connections', 30000); // The number of connections to generate
        this.param_stop_wait_min = Config.value(config, param_scope + 'stop_wait_min', 60000); // The minimum waiting time per stop in milliseconds
        this.param_stop_wait_size_factor = Config.value(config, param_scope + 'stop_wait_size_factor', 60000); // The factor in milliseconds of stop waiting time to add depending on the station size

        this.param_route_choice_power = Config.value(config, param_scope + 'route_choice_power', 2); // Higher values means higher chance on larger routes when selecting routes
        this.param_vehicle_max_speed = Config.value(config, param_scope + 'vehicle_max_speed', 160); // km/h
        this.param_vehicle_speedup = Config.value(config, param_scope + 'vehicle_speedup', 5000); // km/(h^2)
        this.hours_until_max_speed = this.param_vehicle_max_speed / this.param_vehicle_speedup;
        this.distance_until_max_speed = Math.pow(this.hours_until_max_speed, 2) * this.param_vehicle_speedup;
        this.param_hourly_weekday_distribution = Config.value(config, param_scope + 'hourly_weekday_distribution', Time.DEFAULT_HOURLY_WEEKDAY_DISTRIBUTION);
        this.param_hourly_weekend_distribution = Config.value(config, param_scope + 'hourly_weekend_distribution', Time.DEFAULT_HOURLY_WEEKEND_DISTRIBUTION);
        this.pool_week_hours = Time.createTimeChoicePool(this.param_hourly_weekday_distribution);
        this.pool_weekend_hours = Time.createTimeChoicePool(this.param_hourly_weekend_distribution);

        this.param_delay_chance = Config.value(config, param_scope + 'delay_chance', 0); // The 0-1 chance that a connection will have a delay, 0 will not produce any delays (default)
        this.param_delay_max = Config.value(config, param_scope + 'delay_max', 3600000); // The maximum delay in milliseconds
        this.param_delay_choice_power = Config.value(config, param_scope + 'delay_choice_power', 1); // Higher values means higher chance on larger delays
        this.param_delay_reasons = Config.value(config, param_scope + 'delay_reasons', Util.DEFAULT_DELAY_REASONS); // Default reasons for having delays with their respective chance
        this.param_delay_reduction_duration_fraction = Config.value(config, param_scope + 'delay_reduction_duration_fraction', 0.1); // The maximum fraction of connection duration that can be subtracted when there is a delay
        this.pool_delay_reasons = Util.createChoicePool(this.param_delay_reasons, 10);
        if (this.param_delay_chance > 0) this.region.delays = true;
    }

    random() {
        var x = Math.sin(this.param_seed++) * 10000;
        return x - Math.floor(x);
    }

    getRandomClosure() {
        return () => this.random();
    }

    getRandomInitialTime() {
        return Time.getRandomInitialTime(this.getRandomClosure(), this.pool_week_hours, this.pool_weekend_hours, this.param_time_initial, this.param_time_final);
    }

    getDistanceDuration(distance_km) {
        var halfway = distance_km / 2;
        if (this.distance_until_max_speed < halfway) {
            // We are able to reach max speed before having to slow down
            var constant_max_speed_duration = (distance_km - 2 * this.distance_until_max_speed) / this.param_vehicle_max_speed;
            return this.hours_until_max_speed * 2 + constant_max_speed_duration;
        } else {
            // We are not able to reach max speed before having to slow down again
            var duration_until_halfway = Math.sqrt(halfway / this.param_vehicle_speedup);
            return duration_until_halfway * 2;
        }
    }

    getTripDuration(trip) {
        return this.getDistanceDuration(DistanceHelpers.point2DHaversine(trip.from, trip.to, this._pointToLatLon)) * 3600000;
    }

    generate() {
        var routes = this.routes.sort((a, b) => b.size - a.size);
        while (this.connections.length < this.param_connections) {
            // Select random route, weighed by size
            var route = Util.getRandomElementWeightedBySize(this.getRandomClosure(), routes, this.param_route_choice_power);

            // Select random initial time, weighed by time distribution
            var initial = this.getRandomInitialTime();

            // For each trip
            var toAddConnections = [];
            var delayArrival = 0;
            var delayArrivalReason = null;
            route.trips.forEach(trip => {
                // Calculate trip duration (/\ speed distr, for longer trips, trains can go faster, until a max speed is reached)
                var duration = this.getTripDuration(trip);
                var delayDeparture = delayArrival;
                var delayDepartureReason = delayArrivalReason;

                // Each connection has a chance to experience delay, or gain additional delay for this trip.
                var wasDelay = !!delayArrival;
                delayArrival += this.random() < this.param_delay_chance ? Util.getRandomWeightedBySize(this.getRandomClosure(), this.param_delay_max, this.param_delay_choice_power) : 0;
                // All delays must have a reason.
                if (delayArrival && !delayArrivalReason) {
                    delayArrivalReason = this.pool_delay_reasons[Math.floor(this.random() * this.pool_delay_reasons.length)];
                }

                // Reduce the delay with a fraction of the duration of this connection
                if (delayArrival && wasDelay) {
                    delayArrival -= Math.floor(duration * this.param_delay_reduction_duration_fraction);
                    delayArrival = Math.max(0, delayArrival);
                }
                if (delayArrival == 0) {
                    delayArrivalReason = null;
                }

                // Also add a waiting time depending on the target station size
                var wait_time = this.param_stop_wait_min + trip.to.size * this.param_stop_wait_size_factor;

                // Set end time as new initial time
                toAddConnections.push({ departureTime: Math.floor(initial), arrivalTime: Math.floor(initial + duration),
                    trip: trip, delayDeparture: delayDeparture, delayArrival: delayArrival,
                    delayDepartureReason: delayDepartureReason, delayArrivalReason: delayArrivalReason });
                initial = initial + duration + wait_time;
            });

            // If end time > param_final, stop generating for this route
            if (initial > this.param_time_final) {
                continue;
            }

            // Add connections for added trip
            toAddConnections.forEach(connection => this.addConnection(connection.trip, connection.arrivalTime,
              connection.departureTime, connection.delayDeparture, connection.delayDepartureReason,
              connection.delayDepartureReason, connection.delayArrivalReason, connection.delayArrivalReason));
        }
        return this;
    }
}

module.exports = ParameterizedConnectionsGenerator;<commit_msg>Fix incorrect arrival delay value serialization<commit_after>'use strict';

const Config = require('../util/Config.js');
const Time = require('../util/Time.js');
const Util = require('../util/Util.js');
const ConnectionsGenerator = require('./ConnectionsGenerator.js');
const DistanceHelpers = require('../util/DistanceHelpers');

class ParameterizedConnectionsGenerator extends ConnectionsGenerator {
    constructor(region, routes, pointToLatLon, config) {
        super(region, routes, config);

        this._pointToLatLon = pointToLatLon;

        var param_scope = "connections:";
        this.param_seed = Config.value(config, 'seed', 1); // Random seed
        //this.param_time_initial = 1412158210000; // The initial timestamp
        //this.param_time_final = 1417428610000; // The final timestamp
        this.param_time_initial = Config.value(config, param_scope + 'time_initial', 0); // The initial timestamp
        this.param_time_final = Config.value(config, param_scope + 'time_final', 24 * 3600000 * 31); // The final timestamp
        this.param_connections = Config.value(config, param_scope + 'connections', 30000); // The number of connections to generate
        this.param_stop_wait_min = Config.value(config, param_scope + 'stop_wait_min', 60000); // The minimum waiting time per stop in milliseconds
        this.param_stop_wait_size_factor = Config.value(config, param_scope + 'stop_wait_size_factor', 60000); // The factor in milliseconds of stop waiting time to add depending on the station size

        this.param_route_choice_power = Config.value(config, param_scope + 'route_choice_power', 2); // Higher values means higher chance on larger routes when selecting routes
        this.param_vehicle_max_speed = Config.value(config, param_scope + 'vehicle_max_speed', 160); // km/h
        this.param_vehicle_speedup = Config.value(config, param_scope + 'vehicle_speedup', 5000); // km/(h^2)
        this.hours_until_max_speed = this.param_vehicle_max_speed / this.param_vehicle_speedup;
        this.distance_until_max_speed = Math.pow(this.hours_until_max_speed, 2) * this.param_vehicle_speedup;
        this.param_hourly_weekday_distribution = Config.value(config, param_scope + 'hourly_weekday_distribution', Time.DEFAULT_HOURLY_WEEKDAY_DISTRIBUTION);
        this.param_hourly_weekend_distribution = Config.value(config, param_scope + 'hourly_weekend_distribution', Time.DEFAULT_HOURLY_WEEKEND_DISTRIBUTION);
        this.pool_week_hours = Time.createTimeChoicePool(this.param_hourly_weekday_distribution);
        this.pool_weekend_hours = Time.createTimeChoicePool(this.param_hourly_weekend_distribution);

        this.param_delay_chance = Config.value(config, param_scope + 'delay_chance', 0); // The 0-1 chance that a connection will have a delay, 0 will not produce any delays (default)
        this.param_delay_max = Config.value(config, param_scope + 'delay_max', 3600000); // The maximum delay in milliseconds
        this.param_delay_choice_power = Config.value(config, param_scope + 'delay_choice_power', 1); // Higher values means higher chance on larger delays
        this.param_delay_reasons = Config.value(config, param_scope + 'delay_reasons', Util.DEFAULT_DELAY_REASONS); // Default reasons for having delays with their respective chance
        this.param_delay_reduction_duration_fraction = Config.value(config, param_scope + 'delay_reduction_duration_fraction', 0.1); // The maximum fraction of connection duration that can be subtracted when there is a delay
        this.pool_delay_reasons = Util.createChoicePool(this.param_delay_reasons, 10);
        if (this.param_delay_chance > 0) this.region.delays = true;
    }

    random() {
        var x = Math.sin(this.param_seed++) * 10000;
        return x - Math.floor(x);
    }

    getRandomClosure() {
        return () => this.random();
    }

    getRandomInitialTime() {
        return Time.getRandomInitialTime(this.getRandomClosure(), this.pool_week_hours, this.pool_weekend_hours, this.param_time_initial, this.param_time_final);
    }

    getDistanceDuration(distance_km) {
        var halfway = distance_km / 2;
        if (this.distance_until_max_speed < halfway) {
            // We are able to reach max speed before having to slow down
            var constant_max_speed_duration = (distance_km - 2 * this.distance_until_max_speed) / this.param_vehicle_max_speed;
            return this.hours_until_max_speed * 2 + constant_max_speed_duration;
        } else {
            // We are not able to reach max speed before having to slow down again
            var duration_until_halfway = Math.sqrt(halfway / this.param_vehicle_speedup);
            return duration_until_halfway * 2;
        }
    }

    getTripDuration(trip) {
        return this.getDistanceDuration(DistanceHelpers.point2DHaversine(trip.from, trip.to, this._pointToLatLon)) * 3600000;
    }

    generate() {
        var routes = this.routes.sort((a, b) => b.size - a.size);
        while (this.connections.length < this.param_connections) {
            // Select random route, weighed by size
            var route = Util.getRandomElementWeightedBySize(this.getRandomClosure(), routes, this.param_route_choice_power);

            // Select random initial time, weighed by time distribution
            var initial = this.getRandomInitialTime();

            // For each trip
            var toAddConnections = [];
            var delayArrival = 0;
            var delayArrivalReason = null;
            route.trips.forEach(trip => {
                // Calculate trip duration (/\ speed distr, for longer trips, trains can go faster, until a max speed is reached)
                var duration = this.getTripDuration(trip);
                var delayDeparture = delayArrival;
                var delayDepartureReason = delayArrivalReason;

                // Each connection has a chance to experience delay, or gain additional delay for this trip.
                var wasDelay = !!delayArrival;
                delayArrival += this.random() < this.param_delay_chance ? Util.getRandomWeightedBySize(this.getRandomClosure(), this.param_delay_max, this.param_delay_choice_power) : 0;
                // All delays must have a reason.
                if (delayArrival && !delayArrivalReason) {
                    delayArrivalReason = this.pool_delay_reasons[Math.floor(this.random() * this.pool_delay_reasons.length)];
                }

                // Reduce the delay with a fraction of the duration of this connection
                if (delayArrival && wasDelay) {
                    delayArrival -= Math.floor(duration * this.param_delay_reduction_duration_fraction);
                    delayArrival = Math.max(0, delayArrival);
                }
                if (delayArrival == 0) {
                    delayArrivalReason = null;
                }

                // Also add a waiting time depending on the target station size
                var wait_time = this.param_stop_wait_min + trip.to.size * this.param_stop_wait_size_factor;

                // Set end time as new initial time
                toAddConnections.push({ departureTime: Math.floor(initial), arrivalTime: Math.floor(initial + duration),
                    trip: trip, delayDeparture: delayDeparture, delayArrival: delayArrival,
                    delayDepartureReason: delayDepartureReason, delayArrivalReason: delayArrivalReason });
                initial = initial + duration + wait_time;
            });

            // If end time > param_final, stop generating for this route
            if (initial > this.param_time_final) {
                continue;
            }

            // Add connections for added trip
            toAddConnections.forEach(connection => this.addConnection(connection.trip, connection.arrivalTime,
              connection.departureTime, connection.delayDeparture, connection.delayArrival,
              connection.delayDepartureReason, connection.delayArrivalReason));
        }
        return this;
    }
}

module.exports = ParameterizedConnectionsGenerator;<|endoftext|>


<commit_before>frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
	make_input() {
		if (this.editor) return;
		this.load_lib().then(() => this.make_ace_editor());
	},

	make_ace_editor() {
		if (this.editor) return;
		this.ace_editor_target = $('

')
			.appendTo(this.input_area);

		this.expanded = false;
		this.$expand_button = $(`<button class="btn btn-xs btn-default">${__('Expand')}</button>`).click(() => {
			this.expanded = !this.expanded;
			this.refresh_height();
			this.toggle_label();
		}).appendTo(this.$input_wrapper);
		// styling
		this.ace_editor_target.addClass('border rounded');
		this.ace_editor_target.css('height', 300);

		// initialize
		const ace = window.ace;
		this.editor = ace.edit(this.ace_editor_target.get(0));
		this.editor.setTheme('ace/theme/tomorrow');
		this.set_language();

		// events
		this.editor.session.on('change', frappe.utils.debounce(() => {
			const input_value = this.get_input_value();
			this.parse_validate_and_set_in_model(input_value);
		}, 300));
	},

	refresh_height() {
		this.ace_editor_target.css('height', this.expanded ? 600 : 300);
		this.editor.resize();
	},

	toggle_label() {
		const button_label = this.expanded ? __('Collapse') : __('Expand');
		this.$expand_button && this.$expand_button.text(button_label);
	},

	set_language() {
		const language_map = {
			'Javascript': 'ace/mode/javascript',
			'JS': 'ace/mode/javascript',
			'Python': 'ace/mode/python',
			'Py': 'ace/mode/python',
			'HTML': 'ace/mode/html',
			'CSS': 'ace/mode/css',
			'Markdown': 'ace/mode/markdown',
			'SCSS': 'ace/mode/scss',
			'JSON': 'ace/mode/json',
			'Golang': 'ace/mode/golang',
			'Go': 'ace/mode/golang'
		};
		const language = this.df.options;

		const valid_languages = Object.keys(language_map);
		if (language && !valid_languages.includes(language)) {
			// eslint-disable-next-line
			console.warn(`Invalid language option provided for field "${this.df.label}". Valid options are ${valid_languages.join(', ')}.`);
		}

		const ace_language_mode = language_map[language] || '';
		this.editor.session.setMode(ace_language_mode);
	},

	parse(value) {
		if (value == null) {
			value = "";
		}
		return value;
	},

	set_formatted_input(value) {
		return this.load_lib().then(() => {
			if (!this.editor) return;
			if (!value) value = '';
			if (value === this.get_input_value()) return;
			this.editor.session.setValue(value);
		});
	},

	get_input_value() {
		return this.editor ? this.editor.session.getValue() : '';
	},

	load_lib() {
		if (this.library_loaded) return this.library_loaded;

		if (frappe.boot.developer_mode) {
			this.root_lib_path = '/assets/frappe/node_modules/ace-builds/src-noconflict/';
		} else {
			this.root_lib_path = '/assets/frappe/node_modules/ace-builds/src-min-noconflict/';
		}

		this.library_loaded = new Promise(resolve => {
			frappe.require(this.root_lib_path + 'ace.js', () => {
				window.ace.config.set('basePath', this.root_lib_path);
				resolve();
			});
		});

		return this.library_loaded;
	}
});
<commit_msg>fix: Set VSCode keybindings in ace editor<commit_after>frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
	make_input() {
		if (this.editor) return;
		this.load_lib().then(() => this.make_ace_editor());
	},

	make_ace_editor() {
		if (this.editor) return;
		this.ace_editor_target = $('

')
			.appendTo(this.input_area);

		this.expanded = false;
		this.$expand_button = $(`<button class="btn btn-xs btn-default">${__('Expand')}</button>`).click(() => {
			this.expanded = !this.expanded;
			this.refresh_height();
			this.toggle_label();
		}).appendTo(this.$input_wrapper);
		// styling
		this.ace_editor_target.addClass('border rounded');
		this.ace_editor_target.css('height', 300);

		// initialize
		const ace = window.ace;
		this.editor = ace.edit(this.ace_editor_target.get(0));
		this.editor.setTheme('ace/theme/tomorrow');
		this.set_language();

		// events
		this.editor.session.on('change', frappe.utils.debounce(() => {
			const input_value = this.get_input_value();
			this.parse_validate_and_set_in_model(input_value);
		}, 300));
	},

	refresh_height() {
		this.ace_editor_target.css('height', this.expanded ? 600 : 300);
		this.editor.resize();
	},

	toggle_label() {
		const button_label = this.expanded ? __('Collapse') : __('Expand');
		this.$expand_button && this.$expand_button.text(button_label);
	},

	set_language() {
		const language_map = {
			'Javascript': 'ace/mode/javascript',
			'JS': 'ace/mode/javascript',
			'Python': 'ace/mode/python',
			'Py': 'ace/mode/python',
			'HTML': 'ace/mode/html',
			'CSS': 'ace/mode/css',
			'Markdown': 'ace/mode/markdown',
			'SCSS': 'ace/mode/scss',
			'JSON': 'ace/mode/json',
			'Golang': 'ace/mode/golang',
			'Go': 'ace/mode/golang'
		};
		const language = this.df.options;

		const valid_languages = Object.keys(language_map);
		if (language && !valid_languages.includes(language)) {
			// eslint-disable-next-line
			console.warn(`Invalid language option provided for field "${this.df.label}". Valid options are ${valid_languages.join(', ')}.`);
		}

		const ace_language_mode = language_map[language] || '';
		this.editor.session.setMode(ace_language_mode);
		this.editor.setKeyboardHandler('ace/keyboard/vscode');
	},

	parse(value) {
		if (value == null) {
			value = "";
		}
		return value;
	},

	set_formatted_input(value) {
		return this.load_lib().then(() => {
			if (!this.editor) return;
			if (!value) value = '';
			if (value === this.get_input_value()) return;
			this.editor.session.setValue(value);
		});
	},

	get_input_value() {
		return this.editor ? this.editor.session.getValue() : '';
	},

	load_lib() {
		if (this.library_loaded) return this.library_loaded;

		if (frappe.boot.developer_mode) {
			this.root_lib_path = '/assets/frappe/node_modules/ace-builds/src-noconflict/';
		} else {
			this.root_lib_path = '/assets/frappe/node_modules/ace-builds/src-min-noconflict/';
		}

		this.library_loaded = new Promise(resolve => {
			frappe.require(this.root_lib_path + 'ace.js', () => {
				window.ace.config.set('basePath', this.root_lib_path);
				resolve();
			});
		});

		return this.library_loaded;
	}
});
<|endoftext|>


<commit_before>frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({
	html_element: "input",
	input_type: "text",
	make_input: function() {
		if(this.$input) return;

		this.$input = $("<"+ this.html_element +">")
			.attr("type", this.input_type)
			.attr("autocomplete", "off")
			.addClass("input-with-feedback form-control")
			.prependTo(this.input_area);

		if (in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image'],
			this.df.fieldtype)) {
			this.$input.attr("maxlength", this.df.length || 140);
		}

		this.set_input_attributes();
		this.input = this.$input.get(0);
		this.has_input = true;
		this.bind_change_event();
		this.bind_focusout();
		this.setup_autoname_check();

		// somehow this event does not bubble up to document
		// after v7, if you can debug, remove this
	},
	setup_autoname_check: function() {
		if (!this.df.parent) return;
		this.meta = frappe.get_meta(this.df.parent);
		if (this.meta && this.meta.autoname
			&& this.meta.autoname.substr(0, 6)==='field:') {
			this.$input.on('keyup', () => {
				this.set_description('');
				if (this.doc && this.doc.__islocal) {
					// check after 1 sec
					let timeout = setTimeout(() => {
						// clear any pending calls
						if (this.last_check) clearTimeout(this.last_check);

						// check if name exists
						frappe.db.get_value(this.doctype, this.$input.val(),
							'name', (val) => {
								if (val) {
									this.set_description(__('{0} already exists. Select another name', [val.name]));
								}
							});
						this.last_check = null;
					}, 1000);
					this.last_check = timeout;
				}
			});
		}
	},
	set_input_attributes: function() {
		this.$input
			.attr("data-fieldtype", this.df.fieldtype)
			.attr("data-fieldname", this.df.fieldname)
			.attr("placeholder", this.df.placeholder || "");
		if(this.doctype) {
			this.$input.attr("data-doctype", this.doctype);
		}
		if(this.df.input_css) {
			this.$input.css(this.df.input_css);
		}
		if(this.df.input_class) {
			this.$input.addClass(this.df.input_class);
		}
	},
	set_input: function(value) {
		this.last_value = this.value;
		this.value = value;
		this.set_formatted_input(value);
		this.set_disp_area(value);
		this.set_mandatory && this.set_mandatory(value);
	},
	set_formatted_input: function(value) {
		this.$input && this.$input.val(this.format_for_input(value));
	},
	get_input_value: function() {
		return this.$input ? this.$input.val() : undefined;
	},
	format_for_input: function(val) {
		return val==null ? "" : val;
	},
	validate: function(v) {
		if(this.df.options == 'Phone') {
			if(v+''=='') {
				return '';
			}
			var v1 = '';
			// phone may start with + and must only have numbers later, '-' and ' ' are stripped
			v = v.replace(/ /g, '').replace(/-/g, '').replace(/\(/g, '').replace(/\)/g, '');

			// allow initial +,0,00
			if(v && v.substr(0,1)=='+') {
				v1 = '+'; v = v.substr(1);
			}
			if(v && v.substr(0,2)=='00') {
				v1 += '00'; v = v.substr(2);
			}
			if(v && v.substr(0,1)=='0') {
				v1 += '0'; v = v.substr(1);
			}
			v1 += cint(v) + '';
			return v1;
		} else if(this.df.options == 'Email') {
			if(v+''=='') {
				return '';
			}

			var email_list = frappe.utils.split_emails(v);
			if (!email_list) {
				// invalid email
				return '';
			} else {
				var invalid_email = false;
				email_list.forEach(function(email) {
					if (!validate_email(email)) {
						frappe.msgprint(__("Invalid Email: {0}", [email]));
						invalid_email = true;
					}
				});

				if (invalid_email) {
					// at least 1 invalid email
					return '';
				} else {
					// all good
					return v;
				}
			}

		} else {
			return v;
		}
	}
});
<commit_msg>[hotfix] don't add trigger if the autoname field and fieldname is not same (#4074)<commit_after>frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({
	html_element: "input",
	input_type: "text",
	make_input: function() {
		if(this.$input) return;

		this.$input = $("<"+ this.html_element +">")
			.attr("type", this.input_type)
			.attr("autocomplete", "off")
			.addClass("input-with-feedback form-control")
			.prependTo(this.input_area);

		if (in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image'],
			this.df.fieldtype)) {
			this.$input.attr("maxlength", this.df.length || 140);
		}

		this.set_input_attributes();
		this.input = this.$input.get(0);
		this.has_input = true;
		this.bind_change_event();
		this.bind_focusout();
		this.setup_autoname_check();

		// somehow this event does not bubble up to document
		// after v7, if you can debug, remove this
	},
	setup_autoname_check: function() {
		if (!this.df.parent) return;
		this.meta = frappe.get_meta(this.df.parent);
		if (this.meta && this.meta.autoname
			&& this.meta.autoname.substr(0, 6)==='field:'
			&& this.meta.autoname.substr(6) === this.df.fieldname) {
			this.$input.on('keyup', () => {
				this.set_description('');
				if (this.doc && this.doc.__islocal) {
					// check after 1 sec
					let timeout = setTimeout(() => {
						// clear any pending calls
						if (this.last_check) clearTimeout(this.last_check);

						// check if name exists
						frappe.db.get_value(this.doctype, this.$input.val(),
							'name', (val) => {
								if (val) {
									this.set_description(__('{0} already exists. Select another name', [val.name]));
								}
							});
						this.last_check = null;
					}, 1000);
					this.last_check = timeout;
				}
			});
		}
	},
	set_input_attributes: function() {
		this.$input
			.attr("data-fieldtype", this.df.fieldtype)
			.attr("data-fieldname", this.df.fieldname)
			.attr("placeholder", this.df.placeholder || "");
		if(this.doctype) {
			this.$input.attr("data-doctype", this.doctype);
		}
		if(this.df.input_css) {
			this.$input.css(this.df.input_css);
		}
		if(this.df.input_class) {
			this.$input.addClass(this.df.input_class);
		}
	},
	set_input: function(value) {
		this.last_value = this.value;
		this.value = value;
		this.set_formatted_input(value);
		this.set_disp_area(value);
		this.set_mandatory && this.set_mandatory(value);
	},
	set_formatted_input: function(value) {
		this.$input && this.$input.val(this.format_for_input(value));
	},
	get_input_value: function() {
		return this.$input ? this.$input.val() : undefined;
	},
	format_for_input: function(val) {
		return val==null ? "" : val;
	},
	validate: function(v) {
		if(this.df.options == 'Phone') {
			if(v+''=='') {
				return '';
			}
			var v1 = '';
			// phone may start with + and must only have numbers later, '-' and ' ' are stripped
			v = v.replace(/ /g, '').replace(/-/g, '').replace(/\(/g, '').replace(/\)/g, '');

			// allow initial +,0,00
			if(v && v.substr(0,1)=='+') {
				v1 = '+'; v = v.substr(1);
			}
			if(v && v.substr(0,2)=='00') {
				v1 += '00'; v = v.substr(2);
			}
			if(v && v.substr(0,1)=='0') {
				v1 += '0'; v = v.substr(1);
			}
			v1 += cint(v) + '';
			return v1;
		} else if(this.df.options == 'Email') {
			if(v+''=='') {
				return '';
			}

			var email_list = frappe.utils.split_emails(v);
			if (!email_list) {
				// invalid email
				return '';
			} else {
				var invalid_email = false;
				email_list.forEach(function(email) {
					if (!validate_email(email)) {
						frappe.msgprint(__("Invalid Email: {0}", [email]));
						invalid_email = true;
					}
				});

				if (invalid_email) {
					// at least 1 invalid email
					return '';
				} else {
					// all good
					return v;
				}
			}

		} else {
			return v;
		}
	}
});
<|endoftext|>


<commit_before>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

import './timeline.js';
import './new_timeline.js';

frappe.ui.form.Footer = Class.extend({
	init: function(opts) {
		var me = this;
		$.extend(this, opts);
		this.make();
		this.make_comment_box();
		this.make_timeline();
		// render-complete
		$(this.frm.wrapper).on("render_complete", function() {
			me.refresh();
		});
	},
	make: function() {
		var me = this;
		this.wrapper = $(frappe.render_template("form_footer", {}))
			.appendTo(this.parent);
		this.wrapper.find(".btn-save").click(function() {
			me.frm.save('Save', null, this);
		});
	},
	make_comment_box: function() {
		this.comment_box = frappe.ui.form.make_control({
			parent: this.wrapper.find(".comment-box"),
			render_input: true,
			only_input: true,
			mentions: this.get_names_for_mentions(),
			df: {
				fieldtype: 'Comment',
				fieldname: 'comment'
			},
			on_submit: (comment) => {
				if (strip_html(comment).trim() != "") {
					frappe.xcall("frappe.desk.form.utils.add_comment", {
						reference_doctype: this.frm.doctype,
						reference_name: this.frm.docname,
						content: comment,
						comment_email: frappe.session.user,
						comment_by: frappe.session.user_fullname
					}).then(() => {
						this.comment_box.set_value('');
						frappe.utils.play_sound("click");
						this.frm.timeline.refresh();
					});
				}
			}
		});
	},
	get_names_for_mentions() {
		let names_for_mentions = Object.keys(frappe.boot.user_info)
			.filter(user => {
				return !["Administrator", "Guest"].includes(user)
					&& frappe.boot.user_info[user].allowed_in_mentions;
			})
			.map(user => {
				return {
					id: frappe.boot.user_info[user].name,
					value: frappe.boot.user_info[user].fullname,
				};
			});
		return names_for_mentions;
	},
	make_timeline() {
		this.frm.timeline = new frappe.ui.form.NewTimeline({
			parent: this.wrapper.find(".timeline"),
			frm: this.frm
		});
	},
	refresh: function() {
		if (this.frm.doc.__islocal) {
			this.parent.addClass("hide");
		} else {
			this.parent.removeClass("hide");
			this.frm.timeline.refresh();
		}
	},
});
<commit_msg>fix: Comment box reference<commit_after>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

import './timeline.js';
import './new_timeline.js';

frappe.ui.form.Footer = Class.extend({
	init: function(opts) {
		$.extend(this, opts);
		this.make();
		this.make_comment_box();
		this.make_timeline();
		// render-complete
		$(this.frm.wrapper).on("render_complete", () => {
			this.refresh();
		});
	},
	make: function() {
		this.wrapper = $(frappe.render_template("form_footer", {}))
			.appendTo(this.parent);
		this.wrapper.find(".btn-save").click(() => {
			this.frm.save('Save', null, this);
		});
	},
	make_comment_box: function() {
		this.frm.comment_box = frappe.ui.form.make_control({
			parent: this.wrapper.find(".comment-box"),
			render_input: true,
			only_input: true,
			mentions: this.get_names_for_mentions(),
			df: {
				fieldtype: 'Comment',
				fieldname: 'comment'
			},
			on_submit: (comment) => {
				if (strip_html(comment).trim() != "") {
					frappe.xcall("frappe.desk.form.utils.add_comment", {
						reference_doctype: this.frm.doctype,
						reference_name: this.frm.docname,
						content: comment,
						comment_email: frappe.session.user,
						comment_by: frappe.session.user_fullname
					}).then(() => {
						this.frm.comment_box.set_value('');
						frappe.utils.play_sound("click");
						this.frm.timeline.refresh();
					});
				}
			}
		});
	},
	get_names_for_mentions() {
		let names_for_mentions = Object.keys(frappe.boot.user_info)
			.filter(user => {
				return !["Administrator", "Guest"].includes(user)
					&& frappe.boot.user_info[user].allowed_in_mentions;
			})
			.map(user => {
				return {
					id: frappe.boot.user_info[user].name,
					value: frappe.boot.user_info[user].fullname,
				};
			});
		return names_for_mentions;
	},
	make_timeline() {
		this.frm.timeline = new frappe.ui.form.NewTimeline({
			parent: this.wrapper.find(".timeline"),
			frm: this.frm
		});
	},
	refresh: function() {
		if (this.frm.doc.__islocal) {
			this.parent.addClass("hide");
		} else {
			this.parent.removeClass("hide");
			this.frm.timeline.refresh();
		}
	},
});
<|endoftext|>


<commit_before>const WIDGET_DOCTYPE_MAP = {
	chart: "Desk Chart",
	shortcut: "Desk Shortcut",
}

export default class NewWidget {
	constructor(opts) {
		Object.assign(this, opts);
		this.make();
	}

	refresh() {
		//
	}

	customize() {
		return
	}

	make() {
		this.make_widget();
		this.widget.appendTo(this.container);
		this.setup_events();
	}

	get_title() {
		return __(`New ${frappe.utils.to_title_case(this.type)}`)
	}

	make_widget() {
		this.widget = $(`

				+ ${this.get_title()}

`);
		this.body = this.widget
	}

	setup_events() {
		this.widget.on('click', () => this.open_dialog())
	}

	delete() {
		this.widget.remove();
	}

	open_dialog() {
		let doctype = WIDGET_DOCTYPE_MAP[this.type]

		if (!doctype) {
			console.log(`Could not find ${this.type}`)
		}

		frappe.model.with_doctype(doctype, () => {
			let new_dialog = new frappe.ui.Dialog({
				title: this.get_title(),
				fields: frappe.get_meta(doctype).fields,
				primary_action: (data) => {
					if (this.type == 'chart' && !data.label) {
						data.label = data.chart_name;
					}

					if (this.type == 'shortcut') {
						data.label = data.link_to;
					}

					data.docname = frappe.utils.get_random(20);

					new_dialog.hide();
					this.on_create(data);
				},
				primary_action_label: __("Add"),
			});

			new_dialog.show()
		});
	}
}<commit_msg>refactor: use console.warn<commit_after>const WIDGET_DOCTYPE_MAP = {
	chart: "Desk Chart",
	shortcut: "Desk Shortcut",
}

export default class NewWidget {
	constructor(opts) {
		Object.assign(this, opts);
		this.make();
	}

	refresh() {
		//
	}

	customize() {
		return
	}

	make() {
		this.make_widget();
		this.widget.appendTo(this.container);
		this.setup_events();
	}

	get_title() {
		return __(`New ${frappe.utils.to_title_case(this.type)}`)
	}

	make_widget() {
		this.widget = $(`

				+ ${this.get_title()}

`);
		this.body = this.widget
	}

	setup_events() {
		this.widget.on('click', () => this.open_dialog())
	}

	delete() {
		this.widget.remove();
	}

	open_dialog() {
		let doctype = WIDGET_DOCTYPE_MAP[this.type]

		if (!doctype) {
			console.warn(`Could not find ${this.type}`)
		}

		frappe.model.with_doctype(doctype, () => {
			let new_dialog = new frappe.ui.Dialog({
				title: this.get_title(),
				fields: frappe.get_meta(doctype).fields,
				primary_action: (data) => {
					if (this.type == 'chart' && !data.label) {
						data.label = data.chart_name;
					}

					if (this.type == 'shortcut') {
						data.label = data.link_to;
					}

					data.docname = frappe.utils.get_random(20);

					new_dialog.hide();
					this.on_create(data);
				},
				primary_action_label: __("Add"),
			});

			new_dialog.show()
		});
	}
}<|endoftext|>


<commit_before>type('IntelligentSearchBox', ['RealtimeTextBox'],
     {
         _highlight: function(elem, token) {
             // Code for highlighting the matched part of the string
             var idx = elem.toLowerCase().indexOf(token.toLowerCase());
             if (idx >= 0) {
                 var before = elem.slice(0, idx);
                 var after = elem.slice(idx+token.length, elem.length);
                 return [before, Html.span("searchSuggestionHighlight", elem.slice(idx,idx+token.length)), after];

             }
         },

         _truncateList: function(list) {

             // truncate the category path, if it is too long

             var first = list.slice(0,1);
             var last = list.length>1?list.slice(-1):[];
             list = list.slice(1,-1);

             var truncated = false;

             var chars = list.join('');
             while(chars.length > 10) {
                 truncated = true;
                 list = list.slice(1);
                 chars = list.join('');
             }

             if (truncated) {
                 list = concat(['...'], list);
             }

             return translate(concat(first,list,last),
                        function(val) {
                            return escapeHTML(val);
                        });
         },

         _truncateTitle: function(title) {
             var max = 27;
             if (title.length > max) {

                 return title.slice(0,max/2) + ' ... ' +
                     title.slice(-max/2);

             } else {
                 return title;
             }
         },

         _showSuggestions: function(text, suggList, totalNumber) {

             var self= this;

             if (!this.suggestionBox) {
                 // Create the suggestion box only once

                 // totalNumber will change during time
                 this.totalNumber = new WatchValue(totalNumber);
                 this.suggestionList = Html.ul('suggestionBoxList');

                 var infoBox = Html.div('searchSuggestionHelpBox',
                     $T("Some category suggestions..."));

                 // Create the suggestion box
                 this.suggestionBox = Html.div({ style: {position: 'absolute',
                                                         top: pixels(23),
                                                         left: 0
                                                        },
                                                 className: 'suggestionBox'},
                                               infoBox,
                                               this.suggestionList,
                                               $B(Html.div("searchSuggestionOtherResults"), this.totalNumber, function(number) {return number===0?'':number + ' ' + $T('other results - please write more...'); })
                                              );

                 this.container.append(this.suggestionBox);
             }

             // Prepare regular expression for highlighting
             var tokens = filter(escapeHTML(text).split(' '), function(t) { return t !== '';});
             var tokenRE = new RegExp("("+tokens.join('|')+")", 'gi');

             // Bind the list to the DOM element

             var counter = 0;

             $B(this.suggestionList, suggList,
                function(elem) {
                    var titleHtml = escapeHTML(self._truncateTitle(elem.title)).replace(tokenRE, '$1');

                    var index = counter;

                    var title = Html.span('searchSuggestionTitle');
                    title.dom.innerHTML = titleHtml;
                    var pathText = self._truncateList(elem.path).join(' >> ');
                    var path = Html.div('searchSuggestionPath', pathText);
                    var liItem = Html.li({}, title, path);

                    // Use mouse to control selector as well

                    liItem.observeEvent('mouseover', function() {
                        if (self.selectorPos != index) {
                            self._clearSelector();
                            self.selectorPos = index;
                            self._setSelector();
                        }
                    });

                    liItem.observeClick(function() {
                        window.location = elem.url;
                    });

                    counter++;

                    return liItem;
                });

             this.suggestionList.observeEvent('mouseout', function() {
                 self._clearSelector();
                 self.selectorPos = -1;
             });

             // Update
             this.totalNumber.set(totalNumber);
         },

         _hideSuggestions: function() {
             this.container.remove(this.suggestionBox);
             this.suggestionBox = null;
             this.selectorPos = -1;
             this.suggestions = null;
         },

         _retrieveOptions: function(expression) {
             this.querying = true;
             var self = this;
             indicoRequest('search.categoryName',
                           {value: expression},
                           function(result, error) {
                               self.querying = false;
                               if (!error) {
                                   if (result.list.length > 0) {
                                       self.suggestions = result.list;
                                       self._showSuggestions(expression, result.list, result.number - result.list.length);
                                   } else {
                                       self._hideSuggestions();
                                   }

                                   if (self.get() != expression &&
                                       self.get().length > 1) {
                                       self._retrieveOptions(self.get());
                                   } else if (self.get().length <= 1){
                                       self._hideSuggestions();
                                   }
                               } else {
                                   IndicoUtil.errorReport(error);
                               }

                           });
         },

         _textTyped: function(key) {
             if(this.get().length > 1) {
                 if(!this.querying) {
                     this._retrieveOptions(this.get());
                 }
             } else if (this.suggestionBox) {
                 this._hideSuggestions();
             }
         },

         _openSelection: function(event) {
             if (this.selectorPos >= 0) {
                 window.location = this.suggestions[this.selectorPos].url;
                 return false;
             }
             return true;
         },

         _moveSelector: function(direction) {

             if (this.suggestionBox) {
                 var suggNum = this.suggestionList.length.get();

                 if(this.selectorPos < 0) {
                     this.selectorPos = direction=='down'?0:suggNum-1;
                 } else {
                     this._clearSelector();
                     this.selectorPos += direction=='up'?-1:1;

                     if (this.selectorPos >= suggNum) {
                         this.selectorPos = -1;
                     } else if (this.selectorPos < 0) {
                         this.selectorPos = -1;
                     }
                 }
             }

             this._setSelector();
         },

         _setSelector: function() {
             if (this.selectorPos >= 0) {
                 this.suggestionList.item(this.selectorPos).dom.className = 'searchSuggestionSelected';
             }
         },

         _clearSelector: function() {
             if (this.selectorPos >= 0) {
                 this.suggestionList.item(this.selectorPos).dom.className = '';
             }
         }

     },
     function(args, container, submitBtn){
         args.autocomplete = 'off';
         this.RealtimeTextBox(args);
         this.selectorPos = -1;
         this.querying = false;
         this.container = container;

         var self = this;

         this.observe(function(key, event) {
             self._textTyped(key);
             return true;
         });

         this.observeOtherKeys(function(text, key, event) {
             if (key == 38 || key == 40) {
                 self._moveSelector(key==38?'up':'down');
             } else if (key == 27) {
                 self._hideSuggestions();
             } else if (key == 13) {
                 return self._openSelection(event);
             }
             return false;
         });

         $E(document.body).observeClick(function(event) {
             // Close box if a click is done outside of it

             /* for some unknown reason, onclick is called on the submit button,
              * as soon as the return key is pressed in any of the textfields.
              * To make it even better, onclick is called before onkeyup,
              * which forces us to do the last two checks.
              */

             if (self.suggestionBox &&
                 !self.suggestionList.ancestorOf($E(eventTarget(event))) &&
                 $E(eventTarget(event)) != self.input &&
                 $E(eventTarget(event)) != submitBtn)
             {
                 self._hideSuggestions();
             }
         });

     }
    );<commit_msg>[FIX] Intelligent search box - whitespaces<commit_after>type('IntelligentSearchBox', ['RealtimeTextBox'],
     {
         _highlight: function(elem, token) {
             // Code for highlighting the matched part of the string
             var idx = elem.toLowerCase().indexOf(token.toLowerCase());
             if (idx >= 0) {
                 var before = elem.slice(0, idx);
                 var after = elem.slice(idx+token.length, elem.length);
                 return [before, Html.span("searchSuggestionHighlight", elem.slice(idx,idx+token.length)), after];

             }
         },

         _truncateList: function(list) {

             // truncate the category path, if it is too long

             var first = list.slice(0,1);
             var last = list.length>1?list.slice(-1):[];
             list = list.slice(1,-1);

             var truncated = false;

             var chars = list.join('');
             while(chars.length > 10) {
                 truncated = true;
                 list = list.slice(1);
                 chars = list.join('');
             }

             if (truncated) {
                 list = concat(['...'], list);
             }

             return translate(concat(first,list,last),
                        function(val) {
                            return escapeHTML(val);
                        });
         },

         _truncateTitle: function(title) {
             var max = 27;
             if (title.length > max) {

                 return title.slice(0,max/2) + ' ... ' +
                     title.slice(-max/2);

             } else {
                 return title;
             }
         },

         _showSuggestions: function(text, suggList, totalNumber) {

             var self= this;

             if (!this.suggestionBox) {
                 // Create the suggestion box only once

                 // totalNumber will change during time
                 this.totalNumber = new WatchValue(totalNumber);
                 this.suggestionList = Html.ul('suggestionBoxList');

                 var infoBox = Html.div('searchSuggestionHelpBox',
                     $T("Some category suggestions..."));

                 // Create the suggestion box
                 this.suggestionBox = Html.div({ style: {position: 'absolute',
                                                         top: pixels(23),
                                                         left: 0
                                                        },
                                                 className: 'suggestionBox'},
                                               infoBox,
                                               this.suggestionList,
                                               $B(Html.div("searchSuggestionOtherResults"), this.totalNumber, function(number) {return number===0?'':number + ' ' + $T('other results - please write more...'); })
                                              );

                 this.container.append(this.suggestionBox);
             }

             // Prepare regular expression for highlighting
             var tokens = filter(escapeHTML(text).split(' '), function(t) { return t !== '';});
             var tokenRE = new RegExp("("+tokens.join('|')+")", 'gi');

             // Bind the list to the DOM element

             var counter = 0;

             $B(this.suggestionList, suggList,
                function(elem) {
                    var titleHtml = escapeHTML(self._truncateTitle(elem.title)).replace(tokenRE, '$1');

                    var index = counter;

                    var title = Html.span('searchSuggestionTitle');
                    title.dom.innerHTML = titleHtml;
                    var pathText = self._truncateList(elem.path).join(' >> ');
                    var path = Html.div('searchSuggestionPath', pathText);
                    var liItem = Html.li({}, title, path);

                    // Use mouse to control selector as well

                    liItem.observeEvent('mouseover', function() {
                        if (self.selectorPos != index) {
                            self._clearSelector();
                            self.selectorPos = index;
                            self._setSelector();
                        }
                    });

                    liItem.observeClick(function() {
                        window.location = elem.url;
                    });

                    counter++;

                    return liItem;
                });

             this.suggestionList.observeEvent('mouseout', function() {
                 self._clearSelector();
                 self.selectorPos = -1;
             });

             // Update
             this.totalNumber.set(totalNumber);
         },

         _hideSuggestions: function() {
             this.container.remove(this.suggestionBox);
             this.suggestionBox = null;
             this.selectorPos = -1;
             this.suggestions = null;
         },

         _retrieveOptions: function(expression) {
             this.querying = true;
             var self = this;
             indicoRequest('search.categoryName',
                           {value: expression},
                           function(result, error) {
                               self.querying = false;
                               if (!error) {
                                   if (result.list.length > 0) {
                                       self.suggestions = result.list;
                                       self._showSuggestions(expression, result.list, result.number - result.list.length);
                                   } else {
                                       self._hideSuggestions();
                                   }

                                   currentText = self.get().trim();

                                   // if the text changed meanwhile and it
                                   // is still long enough
                                   if (currentText != expression &&
                                       currentText.length > 1) {
                                       // request
                                       self._retrieveOptions(currentText);
                                   } else if (currentText.length <= 1){
                                       // if it is not long enough
                                       self._hideSuggestions();
                                   }
                               } else {
                                   IndicoUtil.errorReport(error);
                               }

                           });
         },

         /*
          * Called each time a new character is typed
          * strips white spaces, and calls for a request if needed
          */
         _textTyped: function(key) {

             var text = this.get().trim();

             if(text.length > 1) {
                 if(!this.querying) {
                     this._retrieveOptions(text);
                 }
             } else if (this.suggestionBox) {
                 this._hideSuggestions();
             }
         },

         _openSelection: function(event) {
             if (this.selectorPos >= 0) {
                 window.location = this.suggestions[this.selectorPos].url;
                 return false;
             }
             return true;
         },

         /*
          * Move the selector (gray area) up or down
          */
         _moveSelector: function(direction) {

             if (this.suggestionBox) {
                 var suggNum = this.suggestionList.length.get();

                 if(this.selectorPos < 0) {
                     this.selectorPos = direction=='down'?0:suggNum-1;
                 } else {
                     this._clearSelector();
                     this.selectorPos += direction=='up'?-1:1;

                     if (this.selectorPos >= suggNum) {
                         this.selectorPos = -1;
                     } else if (this.selectorPos < 0) {
                         this.selectorPos = -1;
                     }
                 }
             }

             this._setSelector();
         },

         _setSelector: function() {
             if (this.selectorPos >= 0) {
                 this.suggestionList.item(this.selectorPos).dom.className = 'searchSuggestionSelected';
             }
         },

         _clearSelector: function() {
             if (this.selectorPos >= 0) {
                 this.suggestionList.item(this.selectorPos).dom.className = '';
             }
         }

     },
     function(args, container, submitBtn){
         args.autocomplete = 'off';
         this.RealtimeTextBox(args);
         this.selectorPos = -1;
         this.querying = false;
         this.container = container;

         var self = this;

         this.observe(function(key, event) {
             self._textTyped(key);
             return true;
         });

         this.observeOtherKeys(function(text, key, event) {
             if (key == 38 || key == 40) {
                 self._moveSelector(key==38?'up':'down');
             } else if (key == 27) {
                 self._hideSuggestions();
             } else if (key == 13) {
                 return self._openSelection(event);
             }
             return false;
         });

         $E(document.body).observeClick(function(event) {
             // Close box if a click is done outside of it

             /* for some unknown reason, onclick is called on the submit button,
              * as soon as the return key is pressed in any of the textfields.
              * To make it even better, onclick is called before onkeyup,
              * which forces us to do the last two checks.
              */

             if (self.suggestionBox &&
                 !self.suggestionList.ancestorOf($E(eventTarget(event))) &&
                 $E(eventTarget(event)) != self.input &&
                 $E(eventTarget(event)) != submitBtn)
             {
                 self._hideSuggestions();
             }
         });

     }
    );<|endoftext|>


<commit_before>function loadShows() {
    $.ajax({
        url: '/sickbeard/GetShowList',
        type: 'get',
        dataType: 'json',
        success: function (result) {
            if (result.data.length == 0) {
                var row = $('
')
                row.append($('').html('No shows found'));
                $('#tvshows_table_body').append(row);
            }
            $.each(result.data, function (showname, tvshow) {
                var name = $('').attr('href','/sickbeard/view/' + tvshow.tvdbid).text(showname);
                var row = $('
')
                row.append(
                  $('').html(name),
                  $('').html(sickbeardStatusLabel(tvshow.status)),
                  $('').html(tvshow.next_ep_airdate),
                  $('').html(tvshow.network),
                  $('').html(sickbeardStatusLabel(tvshow.quality))
                );
                $('#tvshows_table_body').append(row);
            });
            $('#tvshows_table_body').parent().trigger('update');
            $('#tvshows_table_body').parent().trigger("sorton",[[[0,0]]]);
        }
    });
}

function loadShow(tvdbid) {
    $.ajax({
        url: '/sickbeard/GetShow?tvdbid=' + tvdbid,
        type: 'get',
        dataType: 'json',
        success: function (data) {
            data = data.data;

            var table = $('
');
            table.addClass('table table-bordered table-striped table-condensed');

            row = $('
');
            row.append('Status	' + data.status + '	');
            table.append(row);

            row = $('
');
            row.append('Airs	' + data.airs + '	');
            table.append(row);

            row = $('
');
            row.append('Language	' + data.language + '	');
            table.append(row);

            row = $('
');
            row.append('Location	' + data.location + '	');
            table.append(row);

            row = $('
');
            row.append('Quality	' + data.quality + '	');
            table.append(row);

            row = $('
');
            row.append('Network	' + data.network + '	');
            table.append(row);

            modalContent = $('
');
            modalContent.append(
              $('').attr('src', '/sickbeard/GetBanner/'+tvdbid).addClass('img-rounded'),
              $('
'),
              table
             );

            var modalButtons = {
              'Show' : function() {
                window.location = '/sickbeard/view/' + tvdbid;
              }
            }

            showModal(data.show_name, modalContent, modalButtons);
        }
    });
}

function loadNextAired(options) {
    var defaults = {
       limit : 0
    };
    $.extend(defaults, options);

    $.ajax({
        url: '/sickbeard/GetNextAired',
        type: 'get',
        dataType: 'json',
        success: function (result) {
            // If sickbeard not configured, return false (Dashboard)
            if (result == null) return false;

            if (result.data.soon.legth == 0) {
                var row = $('
')
                row.append($('').html('No future episodes found'));
                $('#nextaired_table_body').append(row);
                return false;
            }

            var soonaired = result.data.soon;
            var todayaired = result.data.today;
            var nextaired = todayaired.concat(soonaired);

            $.each(nextaired, function (i, tvshow) {
                if (defaults.limit != 0 && i == defaults.limit) {
                    return false;
                }
                var row = $('
');
                var name = $('').attr('href','#').html(tvshow.show_name).click(function(e){
                    loadShow(tvshow.tvdbid);
                });

                row.append(
                  $('').append(name),
                  $('').html(tvshow.ep_name),
                  $('').html(tvshow.airdate)
                );

                $('#nextaired_table_body').append(row);
            });

            $('#nextaired_table_body').parent().trigger('update');
        }
    });
}

function loadSickbeardHistory(limit) {
    $.ajax({
        url: '/sickbeard/GetHistory?limit=' + limit,
        type: 'get',
        dataType: 'json',
        success: function (result) {
            if (result.data.legth == 0) {
                var row = $('
')
                row.append($('').html('History is empty'));
                $('#history_table_body').append(row);
            }

            $.each(result.data, function (tvdbid, tvshow) {
                var row = $('
');
                row.append(
                  $('').html(tvshow.date),
                  $('').html(tvshow.show_name),
                  $('').html(tvshow.season + 'x' + tvshow.episode),
                  $('').append(sickbeardStatusLabel(tvshow.status)),
                  $('').html(tvshow.quality)
                );

                $('#history_table_body').append(row);
            });
            $('#history_table_body').parent().trigger('update');
        }
    });
}

function loadLogs() {
    $.ajax({
        url: '/sickbeard/GetLogs',
        type: 'get',
        dataType: 'json',
        success: function (result) {
            if (result.data.legth == 0) {
                var row = $('
')
                row.append($('').html('Log is empty'));
                $('#log_table_body').append(row);
            }
            $.each(result.data, function (i, logitem) {
                var row = $('
');
                row.append($('').html(logitem));
                $('#log_table_body').append(row);
            });
        }
    });
}

function searchTvDb(query) {
    $.ajax({
        url: '/sickbeard/SearchShow?query=' + query,
        type: 'get',
        dataType: 'xml',
        success: function (result) {
            series = $(result).find('Series');
            if (series.length == 0) {
                $('#add_show_button').attr('disabled', false);
                return;
            }
            $('#add_show_select').html('');
            series.each(function() {
                var tvdbid = $(this).find('seriesid').text();
                var showname = $(this).find('SeriesName').text();
                var language = $(this).find('language').text();
                var option = $('<option>');
                option.attr('value', tvdbid);
                option.html(showname + ' (' + language + ')');
                $('#add_show_select').append(option);
            });
            $('#add_show_name').hide();
            $('#cancel_show_button').show();
            $('#add_show_select').fadeIn();
            $('#add_show_button').attr('disabled', false).hide();
            $('#add_tvdbid_button').show();
        }
    });
}

function addShow(tvdbid) {
    $.ajax({
        url: '/sickbeard/AddShow?tvdbid=' + tvdbid,
        type: 'get',
        dataType: 'json',
        success: function (data) {
            notify('Add TV show',data.message,'success');
            cancelAddShow();
        }
    });
}

function searchEpisode(tvdbid, season, episode, name) {
  var modalcontent = $('
');
  modalcontent.append($('
').html('Looking for episode "'+ name +'".'));
  modalcontent.append($('
').html('

'));
  showModal('Searching episode '+season + 'x'+episode, modalcontent, {});

  $.ajax({
    url: '/sickbeard/SearchEpisodeDownload?tvdbid=' + tvdbid +'&season=' + season +'&episode='+episode,
    type: 'get',
    dataType: 'json',
    timeout: 40000,
    success: function (data) {
      // If result is not 'succes' it must be a failure
      if (data.result != 'success') {
        notify('Error', data.message, 'error');
        return;
      } else {
        notify('OK', name+ ' ' + season + 'x'+episode+' found. ' + data.message, 'success');
        return;
      }
    },
    error: function (data) {
      notify('Error', 'Episode not found.', 'error', 1);
    },
    complete: function (data) {
      hideModal();
    }
  });
}

function cancelAddShow() {
    $('#add_show_name').val('');
    $('#add_show_select').hide();
    $('#cancel_show_button').hide();
    $('#add_show_name').fadeIn();
    $('#add_tvdbid_button').hide();
    $('#add_show_button').show();
}

function sickbeardStatusLabel(text){
  var statusOK = ['Continuing', 'Downloaded', 'HD'];
  var statusInfo = ['Snatched', 'Unaired'];
  var statusError = ['Ended'];
  var statusWarning = ['Skipped'];

  var label = $('').addClass('label').text(text);

  if (statusOK.indexOf(text) != -1) {
    label.addClass('label-success');
  }
  else if (statusInfo.indexOf(text) != -1) {
    label.addClass('label-info');
  }
  else if (statusError.indexOf(text) != -1) {
    label.addClass('label-important');
  }
  else if (statusWarning.indexOf(text) != -1) {
    label.addClass('label-warning');
  }

  var icon = sickbeardStatusIcon(text, true);
  if (icon != '') {
    label.prepend(' ').prepend(icon);
  }
  return label;
}

function sickbeardStatusIcon(iconText, white){
  var text =[
    'Downloaded',
    'Continuing',
    'Snatched',
    'Unaired',
    'Archived',
    'Skipped'
  ];
  var icons = [
    'icon-download-alt',
    'icon-repeat',
    'icon-share-alt',
    'icon-time',
    'icon-lock',
    'icon-fast-forward'
  ];

  if (text.indexOf(iconText) != -1) {
    var icon = $('').addClass(icons[text.indexOf(iconText)]);
    if (white == true) {
      icon.addClass('icon-white');
    }
    return icon;
  }
  return '';
}
<commit_msg>Sickbeard: Update season list after episode search<commit_after>function loadShows() {
    $.ajax({
        url: '/sickbeard/GetShowList',
        type: 'get',
        dataType: 'json',
        success: function (result) {
            if (result.data.length == 0) {
                var row = $('
')
                row.append($('').html('No shows found'));
                $('#tvshows_table_body').append(row);
            }
            $.each(result.data, function (showname, tvshow) {
                var name = $('').attr('href','/sickbeard/view/' + tvshow.tvdbid).text(showname);
                var row = $('
')
                row.append(
                  $('').html(name),
                  $('').html(sickbeardStatusLabel(tvshow.status)),
                  $('').html(tvshow.next_ep_airdate),
                  $('').html(tvshow.network),
                  $('').html(sickbeardStatusLabel(tvshow.quality))
                );
                $('#tvshows_table_body').append(row);
            });
            $('#tvshows_table_body').parent().trigger('update');
            $('#tvshows_table_body').parent().trigger("sorton",[[[0,0]]]);
        }
    });
}

function loadShow(tvdbid) {
    $.ajax({
        url: '/sickbeard/GetShow?tvdbid=' + tvdbid,
        type: 'get',
        dataType: 'json',
        success: function (data) {
            data = data.data;

            var table = $('
');
            table.addClass('table table-bordered table-striped table-condensed');

            row = $('
');
            row.append('Status	' + data.status + '	');
            table.append(row);

            row = $('
');
            row.append('Airs	' + data.airs + '	');
            table.append(row);

            row = $('
');
            row.append('Language	' + data.language + '	');
            table.append(row);

            row = $('
');
            row.append('Location	' + data.location + '	');
            table.append(row);

            row = $('
');
            row.append('Quality	' + data.quality + '	');
            table.append(row);

            row = $('
');
            row.append('Network	' + data.network + '	');
            table.append(row);

            modalContent = $('
');
            modalContent.append(
              $('').attr('src', '/sickbeard/GetBanner/'+tvdbid).addClass('img-rounded'),
              $('
'),
              table
             );

            var modalButtons = {
              'Show' : function() {
                window.location = '/sickbeard/view/' + tvdbid;
              }
            }

            showModal(data.show_name, modalContent, modalButtons);
        }
    });
}

function loadNextAired(options) {
    var defaults = {
       limit : 0
    };
    $.extend(defaults, options);

    $.ajax({
        url: '/sickbeard/GetNextAired',
        type: 'get',
        dataType: 'json',
        success: function (result) {
            // If sickbeard not configured, return false (Dashboard)
            if (result == null) return false;

            if (result.data.soon.legth == 0) {
                var row = $('
')
                row.append($('').html('No future episodes found'));
                $('#nextaired_table_body').append(row);
                return false;
            }

            var soonaired = result.data.soon;
            var todayaired = result.data.today;
            var nextaired = todayaired.concat(soonaired);

            $.each(nextaired, function (i, tvshow) {
                if (defaults.limit != 0 && i == defaults.limit) {
                    return false;
                }
                var row = $('
');
                var name = $('').attr('href','#').html(tvshow.show_name).click(function(e){
                    loadShow(tvshow.tvdbid);
                });

                row.append(
                  $('').append(name),
                  $('').html(tvshow.ep_name),
                  $('').html(tvshow.airdate)
                );

                $('#nextaired_table_body').append(row);
            });

            $('#nextaired_table_body').parent().trigger('update');
        }
    });
}

function loadSickbeardHistory(limit) {
    $.ajax({
        url: '/sickbeard/GetHistory?limit=' + limit,
        type: 'get',
        dataType: 'json',
        success: function (result) {
            if (result.data.legth == 0) {
                var row = $('
')
                row.append($('').html('History is empty'));
                $('#history_table_body').append(row);
            }

            $.each(result.data, function (tvdbid, tvshow) {
                var row = $('
');
                row.append(
                  $('').html(tvshow.date),
                  $('').html(tvshow.show_name),
                  $('').html(tvshow.season + 'x' + tvshow.episode),
                  $('').append(sickbeardStatusLabel(tvshow.status)),
                  $('').html(tvshow.quality)
                );

                $('#history_table_body').append(row);
            });
            $('#history_table_body').parent().trigger('update');
        }
    });
}

function loadLogs() {
    $.ajax({
        url: '/sickbeard/GetLogs',
        type: 'get',
        dataType: 'json',
        success: function (result) {
            if (result.data.legth == 0) {
                var row = $('
')
                row.append($('').html('Log is empty'));
                $('#log_table_body').append(row);
            }
            $.each(result.data, function (i, logitem) {
                var row = $('
');
                row.append($('').html(logitem));
                $('#log_table_body').append(row);
            });
        }
    });
}

function searchTvDb(query) {
    $.ajax({
        url: '/sickbeard/SearchShow?query=' + query,
        type: 'get',
        dataType: 'xml',
        success: function (result) {
            series = $(result).find('Series');
            if (series.length == 0) {
                $('#add_show_button').attr('disabled', false);
                return;
            }
            $('#add_show_select').html('');
            series.each(function() {
                var tvdbid = $(this).find('seriesid').text();
                var showname = $(this).find('SeriesName').text();
                var language = $(this).find('language').text();
                var option = $('<option>');
                option.attr('value', tvdbid);
                option.html(showname + ' (' + language + ')');
                $('#add_show_select').append(option);
            });
            $('#add_show_name').hide();
            $('#cancel_show_button').show();
            $('#add_show_select').fadeIn();
            $('#add_show_button').attr('disabled', false).hide();
            $('#add_tvdbid_button').show();
        }
    });
}

function addShow(tvdbid) {
    $.ajax({
        url: '/sickbeard/AddShow?tvdbid=' + tvdbid,
        type: 'get',
        dataType: 'json',
        success: function (data) {
            notify('Add TV show',data.message,'success');
            cancelAddShow();
        }
    });
}

function searchEpisode(tvdbid, season, episode, name) {
  var modalcontent = $('
');
  modalcontent.append($('
').html('Looking for episode "'+ name +'".'));
  modalcontent.append($('
').html('

'));
  showModal('Searching episode '+season + 'x'+episode, modalcontent, {});

  $.ajax({
    url: '/sickbeard/SearchEpisodeDownload?tvdbid=' + tvdbid +'&season=' + season +'&episode='+episode,
    type: 'get',
    dataType: 'json',
    timeout: 40000,
    success: function (data) {
      // If result is not 'succes' it must be a failure
      if (data.result != 'success') {
        notify('Error', data.message, 'error');
        return;
      } else {
        notify('OK', name+ ' ' + season + 'x'+episode+' found. ' + data.message, 'success');
        return;
      }
    },
    error: function (data) {
      notify('Error', 'Episode not found.', 'error', 1);
    },
    complete: function (data) {
      hideModal();
      // Trigger latest season
      $('#season-list li.active a').trigger('click');
    }
  });
}

function cancelAddShow() {
    $('#add_show_name').val('');
    $('#add_show_select').hide();
    $('#cancel_show_button').hide();
    $('#add_show_name').fadeIn();
    $('#add_tvdbid_button').hide();
    $('#add_show_button').show();
}

function sickbeardStatusLabel(text){
  var statusOK = ['Continuing', 'Downloaded', 'HD'];
  var statusInfo = ['Snatched', 'Unaired'];
  var statusError = ['Ended'];
  var statusWarning = ['Skipped'];

  var label = $('').addClass('label').text(text);

  if (statusOK.indexOf(text) != -1) {
    label.addClass('label-success');
  }
  else if (statusInfo.indexOf(text) != -1) {
    label.addClass('label-info');
  }
  else if (statusError.indexOf(text) != -1) {
    label.addClass('label-important');
  }
  else if (statusWarning.indexOf(text) != -1) {
    label.addClass('label-warning');
  }

  var icon = sickbeardStatusIcon(text, true);
  if (icon != '') {
    label.prepend(' ').prepend(icon);
  }
  return label;
}

function sickbeardStatusIcon(iconText, white){
  var text =[
    'Downloaded',
    'Continuing',
    'Snatched',
    'Unaired',
    'Archived',
    'Skipped'
  ];
  var icons = [
    'icon-download-alt',
    'icon-repeat',
    'icon-share-alt',
    'icon-time',
    'icon-lock',
    'icon-fast-forward'
  ];

  if (text.indexOf(iconText) != -1) {
    var icon = $('').addClass(icons[text.indexOf(iconText)]);
    if (white == true) {
      icon.addClass('icon-white');
    }
    return icon;
  }
  return '';
}
<|endoftext|>


<commit_before>const autoprefixer = require('autoprefixer');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const getClientEnvironment = require('./env');
const paths = require('./paths');

// Webpack uses `publicPath` to determine where the app is being served from.
// In development, we always serve from the root. This makes config easier.
const publicPath = '/';
// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
const publicUrl = '';
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);

// This is the development configuration.
// It is focused on developer experience and fast rebuilds.
// The production configuration is different and lives in a separate file.
module.exports = {
  // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
  // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
  devtool: 'source-map',
  // These are the "entry points" to our application.
  // This means they will be the "root" imports that are included in JS bundle.
  // The first two entry points enable "hot" CSS and auto-refreshes for JS.
  entry: [
    // Include an alternative client for WebpackDevServer. A client's job is to
    // connect to WebpackDevServer by a socket and get notified about changes.
    // When you save a file, the client will either apply hot updates (in case
    // of CSS changes), or refresh the page (in case of JS changes). When you
    // make a syntax error, this client will display a syntax error overlay.
    // Note: instead of the default WebpackDevServer client, we use a custom one
    // to bring better experience for Create React App users. You can replace
    // the line below with these two lines if you prefer the stock client:
    // require.resolve('webpack-dev-server/client') + '?/',
    // require.resolve('webpack/hot/dev-server'),
    require.resolve('react-dev-utils/webpackHotDevClient'),
    // We ship a few polyfills by default:
    require.resolve('./polyfills'),
    // Finally, this is your app's code:
    paths.appIndexJs,
    // We include the app code last so that if there is a runtime error during
    // initialization, it doesn't blow up the WebpackDevServer client, and
    // changing JS code would still trigger a refresh.
  ],
  output: {
    // Next line is not used in dev but WebpackDevServer crashes without it:
    path: paths.appBuild,
    // Add /* filename */ comments to generated require()s in the output.
    pathinfo: true,
    // This does not produce a real file. It's just the virtual path that is
    // served by WebpackDevServer in development. This is the JS bundle
    // containing code from all our entry points, and the Webpack runtime.
    filename: 'static/js/bundle.js',
    // This is the URL that app is served from. We use "/" in development.
    publicPath,
  },
  resolve: {
    // This allows you to set a fallback for where Webpack should look for modules.
    // We read `NODE_PATH` environment variable in `paths.js` and pass paths here.
    // We use `fallback` instead of `root` because we want `node_modules` to "win"
    // if there any conflicts. This matches Node resolution mechanism.
    // https://github.com/facebookincubator/create-react-app/issues/253
    modules: paths.nodePaths.concat(['node_modules']),
    // These are the reasonable defaults supported by the Node ecosystem.
    // We also include JSX as a common component filename extension to support
    // some tools, although we do not recommend using it, see:
    // https://github.com/facebookincubator/create-react-app/issues/290
    extensions: ['.js', '.json', '.jsx'],
    alias: {
      // Support React Native Web
      // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
      'react-native': 'react-native-web',
    },
  },

  module: {
    rules: [
      // ** ADDING/UPDATING LOADERS **
      // The "url" loader handles all assets unless explicitly excluded.
      // The `exclude` list *must* be updated with every change to loader extensions.
      // When adding a new loader, you must add its `test`
      // as a new entry in the `exclude` list for "url" loader.

      // First, run the linter.
      // It's important to do this before Babel processes the JS.
      {
        test: /\.(js|jsx)$/,
        enforce: 'pre',
        loader: 'eslint-loader',
        include: paths.appSrc,
      },

      // "url" loader embeds assets smaller than specified size as data URLs to avoid requests.
      // Otherwise, it acts like the "file" loader.
      {
        exclude: [
          /\.html$/,
          // We have to write /\.(js|jsx)(\?.*)?$/ rather than just /\.(js|jsx)$/
          // because you might change the hot reloading server from the custom one
          // to Webpack's built-in webpack-dev-server/client?/, which would not
          // get properly excluded by /\.(js|jsx)$/ because of the query string.
          // Webpack 2 fixes this, but for now we include this hack.
          // https://github.com/facebookincubator/create-react-app/issues/1713
          /\.(js|jsx)(\?.*)?$/,
          /\.css$/,
          /\.scss$/,
          /\.json$/,
          /\.svg$/,
        ],
        loader: 'url-loader',
        query: {
          limit: 10000,
          name: 'static/media/[name].[hash:8].[ext]',
        },
      },
      // Process JS with Babel.
      {
        test: /\.(js|jsx)$/,
        include: paths.appSrc,
        loader: 'babel-loader',
        query: {
          // This is a feature of `babel-loader` for webpack (not Babel itself).
          // It enables caching results in ./node_modules/.cache/babel-loader/
          // directory for faster rebuilds.
          cacheDirectory: true,
        },
      },
      {
        test: /\.s[ac]ss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              sourceMap: true,
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: function() {
                return [require('autoprefixer')];
              },
              sourceMap: true,
            },
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true,
              includePaths: [paths.bootstrap, paths.sassVariables],
            },
          },
        ],
      },
      // JSON is not enabled by default in Webpack but both Node and Browserify
      // allow it implicitly so we also enable it.
      // {
      //   test: /\.json$/,
      //   loader: 'json-loader',
      // },
      // "file" loader for svg
      {
        test: /\.svg$/,
        loader: 'file-loader',
        query: {
          name: 'static/media/[name].[hash:8].[ext]',
        },
      },
      // ** STOP ** Are you adding a new loader?
      // Remember to add the new extension(s) to the "url" loader exclusion list.
    ],
  },

  plugins: [
    // Makes some environment variables available in index.html.
    // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
    // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    // In development, this will be an empty string.
    new InterpolateHtmlPlugin(env.raw),
    // Generates an `index.html` file with the <script> injected.
    new HtmlWebpackPlugin({
      inject: true,
      template: paths.appHtml,
    }),
    // Makes some environment variables available to the JS code, for example:
    // if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
    new webpack.DefinePlugin(env.stringified),
    // This is necessary to emit hot updates (currently CSS only):
    new webpack.HotModuleReplacementPlugin(),
    // Watcher doesn't work well if you mistype casing in a path so we use
    // a plugin that prints an error when you attempt to do this.
    // See https://github.com/facebookincubator/create-react-app/issues/240
    new CaseSensitivePathsPlugin(),
    // If you require a missing module and then `npm install` it, you still have
    // to restart the development server for Webpack to discover it. This plugin
    // makes the discovery automatic so you don't have to restart.
    // See https://github.com/facebookincubator/create-react-app/issues/186
    new WatchMissingNodeModulesPlugin(paths.appNodeModules),
    new webpack.LoaderOptionsPlugin({
      options: {
        // We use PostCSS for autoprefixing only.
        postcss: [
          autoprefixer({
            browsers: [
              '>1%',
              'last 4 versions',
              'Firefox ESR',
              'not ie < 9', // React doesn't support IE8 anyway
            ],
          }),
        ],
      },
    }),
  ],
  // Some libraries import Node modules but don't use them in the browser.
  // Tell Webpack to provide empty mocks for them so importing them works.
  node: {
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
  },
};
<commit_msg>dev.config remove unused import<commit_after>const autoprefixer = require('autoprefixer');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const getClientEnvironment = require('./env');
const paths = require('./paths');

// Webpack uses `publicPath` to determine where the app is being served from.
// In development, we always serve from the root. This makes config easier.
const publicPath = '/';
// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
const publicUrl = '';
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);

// This is the development configuration.
// It is focused on developer experience and fast rebuilds.
// The production configuration is different and lives in a separate file.
module.exports = {
  // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
  // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
  devtool: 'source-map',
  // These are the "entry points" to our application.
  // This means they will be the "root" imports that are included in JS bundle.
  // The first two entry points enable "hot" CSS and auto-refreshes for JS.
  entry: [
    // Include an alternative client for WebpackDevServer. A client's job is to
    // connect to WebpackDevServer by a socket and get notified about changes.
    // When you save a file, the client will either apply hot updates (in case
    // of CSS changes), or refresh the page (in case of JS changes). When you
    // make a syntax error, this client will display a syntax error overlay.
    // Note: instead of the default WebpackDevServer client, we use a custom one
    // to bring better experience for Create React App users. You can replace
    // the line below with these two lines if you prefer the stock client:
    // require.resolve('webpack-dev-server/client') + '?/',
    // require.resolve('webpack/hot/dev-server'),
    require.resolve('react-dev-utils/webpackHotDevClient'),
    // We ship a few polyfills by default:
    require.resolve('./polyfills'),
    // Finally, this is your app's code:
    paths.appIndexJs,
    // We include the app code last so that if there is a runtime error during
    // initialization, it doesn't blow up the WebpackDevServer client, and
    // changing JS code would still trigger a refresh.
  ],
  output: {
    // Next line is not used in dev but WebpackDevServer crashes without it:
    path: paths.appBuild,
    // Add /* filename */ comments to generated require()s in the output.
    pathinfo: true,
    // This does not produce a real file. It's just the virtual path that is
    // served by WebpackDevServer in development. This is the JS bundle
    // containing code from all our entry points, and the Webpack runtime.
    filename: 'static/js/bundle.js',
    // This is the URL that app is served from. We use "/" in development.
    publicPath,
  },
  resolve: {
    // This allows you to set a fallback for where Webpack should look for modules.
    // We read `NODE_PATH` environment variable in `paths.js` and pass paths here.
    // We use `fallback` instead of `root` because we want `node_modules` to "win"
    // if there any conflicts. This matches Node resolution mechanism.
    // https://github.com/facebookincubator/create-react-app/issues/253
    modules: paths.nodePaths.concat(['node_modules']),
    // These are the reasonable defaults supported by the Node ecosystem.
    // We also include JSX as a common component filename extension to support
    // some tools, although we do not recommend using it, see:
    // https://github.com/facebookincubator/create-react-app/issues/290
    extensions: ['.js', '.json', '.jsx'],
    alias: {
      // Support React Native Web
      // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
      'react-native': 'react-native-web',
    },
  },

  module: {
    rules: [
      // ** ADDING/UPDATING LOADERS **
      // The "url" loader handles all assets unless explicitly excluded.
      // The `exclude` list *must* be updated with every change to loader extensions.
      // When adding a new loader, you must add its `test`
      // as a new entry in the `exclude` list for "url" loader.

      // First, run the linter.
      // It's important to do this before Babel processes the JS.
      {
        test: /\.(js|jsx)$/,
        enforce: 'pre',
        loader: 'eslint-loader',
        include: paths.appSrc,
      },

      // "url" loader embeds assets smaller than specified size as data URLs to avoid requests.
      // Otherwise, it acts like the "file" loader.
      {
        exclude: [
          /\.html$/,
          // We have to write /\.(js|jsx)(\?.*)?$/ rather than just /\.(js|jsx)$/
          // because you might change the hot reloading server from the custom one
          // to Webpack's built-in webpack-dev-server/client?/, which would not
          // get properly excluded by /\.(js|jsx)$/ because of the query string.
          // Webpack 2 fixes this, but for now we include this hack.
          // https://github.com/facebookincubator/create-react-app/issues/1713
          /\.(js|jsx)(\?.*)?$/,
          /\.css$/,
          /\.scss$/,
          /\.json$/,
          /\.svg$/,
        ],
        loader: 'url-loader',
        query: {
          limit: 10000,
          name: 'static/media/[name].[hash:8].[ext]',
        },
      },
      // Process JS with Babel.
      {
        test: /\.(js|jsx)$/,
        include: paths.appSrc,
        loader: 'babel-loader',
        query: {
          // This is a feature of `babel-loader` for webpack (not Babel itself).
          // It enables caching results in ./node_modules/.cache/babel-loader/
          // directory for faster rebuilds.
          cacheDirectory: true,
        },
      },
      {
        test: /\.s[ac]ss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              sourceMap: true,
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: function() {
                return [require('autoprefixer')];
              },
              sourceMap: true,
            },
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true,
              includePaths: [paths.bootstrap, paths.sassVariables],
            },
          },
        ],
      },
      // JSON is not enabled by default in Webpack but both Node and Browserify
      // allow it implicitly so we also enable it.
      // {
      //   test: /\.json$/,
      //   loader: 'json-loader',
      // },
      // "file" loader for svg
      {
        test: /\.svg$/,
        loader: 'file-loader',
        query: {
          name: 'static/media/[name].[hash:8].[ext]',
        },
      },
      // ** STOP ** Are you adding a new loader?
      // Remember to add the new extension(s) to the "url" loader exclusion list.
    ],
  },

  plugins: [
    // Makes some environment variables available in index.html.
    // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
    // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    // In development, this will be an empty string.
    new InterpolateHtmlPlugin(env.raw),
    // Generates an `index.html` file with the <script> injected.
    new HtmlWebpackPlugin({
      inject: true,
      template: paths.appHtml,
    }),
    // Makes some environment variables available to the JS code, for example:
    // if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
    new webpack.DefinePlugin(env.stringified),
    // This is necessary to emit hot updates (currently CSS only):
    new webpack.HotModuleReplacementPlugin(),
    // Watcher doesn't work well if you mistype casing in a path so we use
    // a plugin that prints an error when you attempt to do this.
    // See https://github.com/facebookincubator/create-react-app/issues/240
    new CaseSensitivePathsPlugin(),
    // If you require a missing module and then `npm install` it, you still have
    // to restart the development server for Webpack to discover it. This plugin
    // makes the discovery automatic so you don't have to restart.
    // See https://github.com/facebookincubator/create-react-app/issues/186
    new WatchMissingNodeModulesPlugin(paths.appNodeModules),
    new webpack.LoaderOptionsPlugin({
      options: {
        // We use PostCSS for autoprefixing only.
        postcss: [
          autoprefixer({
            browsers: [
              '>1%',
              'last 4 versions',
              'Firefox ESR',
              'not ie < 9', // React doesn't support IE8 anyway
            ],
          }),
        ],
      },
    }),
  ],
  // Some libraries import Node modules but don't use them in the browser.
  // Tell Webpack to provide empty mocks for them so importing them works.
  node: {
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
  },
};
<|endoftext|>