rivets-error-binder.js 4.06 KB
/* global Backbone */
define(['rivets', 'bootstrap'], function(rivets) {
    'use strict';
    var rivetsBinderCall = function(binding, binderName, methodName, args) {
        var binder = rivets.binders[binderName];
        if (binder instanceof Function) {
            if (methodName === 'routine') {
                binder.apply(binding, args);
            }
        } else if (binder) {
            if (binder[methodName]) {
                binder[methodName].apply(binding, args);
            }
        }
    };

    var diveIntoObject = function(obj, keypath, callback) {
        if (!keypath) {
            return callback(obj, null);
        }
        //return callback(obj, keypath);
        var keyparts = keypath.replace(/^:/, '').split(/\:/);
        //console.log('diveIntoObject(keyparts):', obj, keyparts);
        while (keyparts.length > 1) {
            var part = keyparts.shift();
            if (part.length === 0) {
                continue;
            }
            //console.log('diveIntoObject:', obj, part);
            obj = doObjectRead(obj, part);
        }
        //console.log('callback:', obj, keyparts[0]);
        return callback(obj, keyparts.shift());
    };

    var doObjectRead = function(obj, id) {
        if (obj === null) {
            return obj;
        }
        if (!id) {
            return obj;
        }
        //console.log('doObjectRead:', obj, id, obj instanceof Backbone.Model, obj instanceof Backbone.Collection);
        if (obj instanceof Backbone.Model)  {
            return obj.get(id);
        } else if (obj instanceof Backbone.Collection)  {
            return obj.at(id);
        } else if (obj !== null) {
            return obj[id];
        }
    };

    rivets.binders['error-*'] = {
        bind: function(el) {
            var holder = this.validationHolder = {
                //marker: el.parentNode.insertBefore(document.createComment(" rivets: " + this.type + " "), el),
                focus: function() {
                    $(holder.container).removeClass('focused');
                },
                blur: function() {
                    if (holder.lastObj) {
                        holder.lastObj.seen(holder.lastId, true);
                    }
                    $(holder.container).addClass('focused');
                    if (holder.lastObj) {
                        holder.lastObj.validate();
                    }
                },
                validated: function(isValid, model, errors) {
                    var errorList = errors[holder.lastId];
                    if (errorList && holder.lastObj.seen(holder.lastId)) {
                        $(el).tooltip({title: errorList, trigger: 'focus'});
                        $(el).tooltip('show');
                        $(el).parent().addClass('has-error');
                    } else {
                        $(el).tooltip('destroy');
                        $(el).parent().removeClass('has-error');
                    }
                }
            };
            $(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) {
                obj.off('validated', holder.validated);
            });
            delete this.validationHolder;
            rivetsBinderCall(this, this.args[0], 'unbind', arguments);
        },
        routine: function() {
            var holder = this.validationHolder;
            if (holder.lastObj) {
                holder.lastObj.off('validated', holder.validated);
            }
            diveIntoObject(this.observer.target, this.observer.key.path, function(obj, id) {
                holder.lastObj = obj;
                holder.lastId = id;
                obj.on('validated', holder.validated);
            });
            rivetsBinderCall(this, this.args[0], 'routine', arguments);
        }
    };
});