<commit_before>var app = require('../ridge');

var View = require('../view').extend();

_.extend(View.prototype, require('../mixins/observe'), {
	tagName: 'form',

	elements: {
		button: 'button'
	},

	events: {
		'focus select,input,textarea,[data-spytext]': 'onFocus',
		'blur select,input,textarea,[data-spytext]': 'onBlur',
		'input select,input,textarea,[data-spytext]': 'onChange',
		'change select,input,textarea,[data-spytext]': 'onChange',
		'click label': 'labelClick',
		'mouseover .invalid>label.icon': 'onHover',
		'mouseout .invalid>label.icon': 'onUnhover'
	},

	initialize: function(opts) {
		_.extend(this, _.pick(opts, 'onError', 'onSuccess', 'onComplete', 'submit'));

		// use properties from model validation to set up more bindings.
		// all model validation properties are assumed to be 'value' getter
		// and all selectors in form view default to [name=""] instead of [data-hook=""]
		this.bindings = _.mapObject(_.defaults(this.bindings || {}, _.mapObject(this.model.validation, function(value, key) {
			return 'value';
		})), function(value, key) {
			return _.isObject(value) ? value : {
				selector: '[name="' + key + '"],[data-name="' + key + '"]',
				type: value
			};
		});

		this.delegate('submit', this.submit.bind(this));

		this.listenTo(this.model, 'validated', this.setValid);
		this.listenTo(this.model, 'reset', this.reset);

		if(!this.model.isNew() && _.size(this.model.attributes) === 1)
			this.model.fetch();
	},

	attach: function() {
		this.$el.attr('novalidate', 'novalidate');

		var _view = this;

		this.observe({ delayInput: true, validate: true });

		// do not validate new or not fetched models
		if(!this.model.isNew() && _.size(this.model.attributes) > 1)
			this.model.validate(null, { validateAll: true });

		this.$('[data-spytext],[name]:not(:disabled)').each(function() {
			if(!/checkbox|radio/.test(this.type) && this.value) {
				delete this.delayInput;

				// TODO see how this handles defaults (meaning defaults might be
				// rendered with dust, and then this change event resets them
				if(_view.model.isNew())
					$(this).trigger('change');
			}

			// set non-empty classes
			_view.onChange({ currentTarget: this });<commit_msg>Enabled using form view without passing it a model.<commit_after>var app = require('../ridge');

var View = require('../view').extend();

_.extend(View.prototype, require('../mixins/observe'), {
	tagName: 'form',

	elements: {
		button: 'button'
	},

	events: {
		'focus select,input,textarea,[data-spytext]': 'onFocus',
		'blur select,input,textarea,[data-spytext]': 'onBlur',
		'input select,input,textarea,[data-spytext]': 'onChange',
		'change select,input,textarea,[data-spytext]': 'onChange',
		'click label': 'labelClick',
		'mouseover .invalid>label.icon': 'onHover',
		'mouseout .invalid>label.icon': 'onUnhover'
	},

	initialize: function(opts) {
		_.extend(this, _.pick(opts, 'onError', 'onSuccess', 'onComplete', 'submit'));

		this.delegate('submit', this.submit.bind(this));

		if(this.model) {
			// use properties from model validation to set up more bindings.
			// all model validation properties are assumed to be 'value' getter
			// and all selectors in form view default to [name=""] instead of [data-hook=""]
			this.bindings = _.mapObject(_.defaults(this.bindings || {}, _.mapObject(this.model.validation, function(value, key) {
				return 'value';
			})), function(value, key) {
				return _.isObject(value) ? value : {
					selector: '[name="' + key + '"],[data-name="' + key + '"]',
					type: value
				};
			});

			this.listenTo(this.model, 'validated', this.setValid);
			this.listenTo(this.model, 'reset', this.reset);

			if(!this.model.isNew() && _.size(this.model.attributes) === 1)
				this.model.fetch();
		}
	},

	attach: function() {
		this.$el.attr('novalidate', 'novalidate');

		var _view = this;

		this.observe({ delayInput: true, validate: true });

		// do not validate new or not fetched models
		if(this.model && !this.model.isNew() && _.size(this.model.attributes) > 1)
			this.model.validate(null, { validateAll: true });

		this.$('[data-spytext],[name]:not(:disabled)').each(function() {
			if(!/checkbox|radio/.test(this.type) && this.value) {
				delete this.delayInput;

				// TODO see how this handles defaults (meaning defaults might be
				// rendered with dust, and then this change event resets them
				if(_view.model && _view.model.isNew())
					$(this).trigger('change');
			}

			// set non-empty classes
			_view.onChange({ currentTarget: this });
{'fraction_non_alphanumeric': 0.18491484184914841, 'fraction_numerical': 0.0008847600088476001, 'mean_word_length': 4.052513966480447, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1145728', 'n_tokens_mistral': 1668, 'n_tokens_neox': 1460, 'n_words': 341}