rivets-error-binder.js 3.25 KB
define(function(require) {
    'use strict';
    var module = require('module');
    var rivets = require('rivets');
    var Backbone = require('backbone');
    var $ = require('jquery');

    var rivetsBinderCall = function(binding, binderName, methodName, args) {
        var binder = rivets.binders[binderName];
        binder[methodName].apply(binding, args);
    };

    var render = function(el, cmd, errorList) {
        var viewClassPath = module.config().viewClassPath;
        if (viewClassPath) {
            var $el = $(el);
            var view = $el.data('error-view');
            if (view === undefined && cmd === 'bind') {
                $el.data('error-view', null);
                require([viewClassPath], function(ViewClass) {
                    $el.data('error-view', new ViewClass({el: el}));
                });
                return;
            }
            if (view) {
                view.errorCallback(cmd, errorList);
            }
            return;
        }
        var renderImpl = module.config().render;
        if (renderImpl) {
            return renderImpl.apply(this, arguments);
        }
    };

    function createTriggerProxy(obj, eventName) {
        return function() {
            obj.trigger.apply(obj, [eventName].concat(_.toArray(arguments)));
        };
    }

    function ErrorHandler(binding, el) {
        var seen = {};
        var observer = binding.observer;
        var oldParent;
        var parentObserver;
        var keyPath = observer.key.path;
        this.on('bind', function() {
            render(el, 'bind');
            render(el, 'validated', false);
        });
        this.on('focus', function() {
            render(el, 'focus', false);
        });
        this.on('blur', function() {
            seen[keyPath] = true;
            render(el, 'blur', false);
            if (observer.target) {
                observer.target.validate();
            }
        });
        this.on('validated', function(isValid, model, errors) {
            var errorList = errors[keyPath];
            if (errorList && seen[keyPath]) {
                render(el, 'validated', errorList);
            } else {
                render(el, 'validated', false);
            }
        });
        this.on('unbind', function() {
            render(el, 'unbind');
            $(el).off('.rivets-error-binder');
        });
        var $el = $(el);
        $el.on('focus.rivets-error-binder', createTriggerProxy(this, 'focus'));
        $el.on('blur.rivets-error-binder', createTriggerProxy(this, 'blur'));
        if (observer.target) {
            this.listenTo(observer.target, 'validated', createTriggerProxy(this, 'validated'));
        }
    }
    _.extend(ErrorHandler.prototype, Backbone.Events);

    rivets.binders['error-*'] = {
        bind: function(el) {
            this.errorHandler = new ErrorHandler(this, el);
            this.errorHandler.trigger('bind');
            rivetsBinderCall(this, this.args[0], 'bind', arguments);
        },
        unbind: function(el) {
            this.errorHandler.trigger('unbind');
            delete this.errorHandler;
            rivetsBinderCall(this, this.args[0], 'unbind', arguments);
        },
        routine: function() {
            rivetsBinderCall(this, this.args[0], 'routine', arguments);
        }
    };
});