rivets-backbone-validation.js 3.24 KB
define([
	'jquery',
	'underscore',
	'backbone',
], function($, _, Backbone) {
	var modelClass, collectionClass;

    var bindings = {};

	bindings['error-*'] = {
		publishes: true,
		bind: function(el) {
			var self = this;
			var holder = this.validationHolder = {
				marker: el.parentNode.insertBefore(document.createComment(" rivets: " + this.type + " "), el),
				focus: function() {
					$(holder.container).find('> .validationfielderrors').show();
				},
				blur: function() {
					holder.lastObj.seen(holder.lastId, true);
					$(holder.container).find('> .validationfielderrors').hide();
					holder.lastObj.validate();
				},
				validated: function(isValid, model, errors) {
					var errorList = errors[holder.lastId];
					var $container = $(holder.container);
					var $fieldErrors = $container.find('> .validationfielderrors').empty();
					if (errorList && holder.lastObj.seen(holder.lastId)) {
						$container.addClass('has-errors');
						localRequire(['text!./templates/fielderrorpopup.html!strip'], function(templateHtml) {
							$fieldErrors.empty().append($($.parseHTML(templateHtml)).find('.template-content').append($.parseHTML(errorList)).end());
						});
					} else {
						$container.removeClass('has-errors');
					}
				}
			};
			localRequire(['text!./templates/fielderrorpopup.html!strip']);
			localRequire(['css!./css/styles.css']);
			holder.container = $.parseHTML('<span class="validationerrorcontainer"><div class="validationfielderrors"></div></span>');
			$(holder.container).find('> .validationfielderrors').hide();
			$(holder.marker).after($(holder.container).append(el));
			diveIntoObject(this.model, this.keypath, function(obj, id) {
				holder.lastObj = obj;
				holder.lastId = id;
				obj.on('validated', holder.validated);
			});
			$(el).on('focus', holder.focus).on('blur', holder.blur);
			rivetsBinderCall(this, this.args[0], 'bind', arguments);
		},
		unbind: function(el) {
			var holder = this.validationHolder;
			$(this.validationHolder.marker).after(el).remove();
			$(el).off('focus', holder.focus).off('blur', holder.blur);
			diveIntoObject(this.model, this.keypath, function(obj, id) {
				obj.off('validated', holder.validated);
			});
			delete this.validationHolder;
			rivetsBinderCall(this, this.args[0], 'unbind', arguments);
		},
		routine: function(el, value) {
			rivetsBinderCall(this, this.args[0], 'routine', arguments);
		}
	}
    
	modelClass = Backbone.Model.extend({
		initialize: function(attrs, options) {
			modelClass.__super__.initialize.apply(this, arguments);
			this._seen = {};
		},
		seen: function(attrName, wasSeen) {
			if (attrName == null) {
				return _.keys(_.clone(this._seen));
			}
			if (wasSeen) {
				this._seen[attrName] = true;
			} else if (wasSeen == false) {
				delete this._seen[attrName];
			} else {
				return this._seen[attrName];
			}
			return this;
		},
	});

	collectionClass = Backbone.Collection.extend({
		model: modelClass,
		parse: function(models, options) {
			var newModels = [];
			var subOptions = _.extend({parse: true}, options);
			for (var i = 0; i < models.length; i++) {
				newModels[newModels.length] = new this.model(models[i], subOptions);
			}
			return newModels;
		}
	});

	return {
		Model: modelClass,
		Collection: collectionClass,
        bindings: bindings
	};
});