700cfddc by Ean Schuessler

Merge branch 'BF-2656' of /home/git/repositories/brainfood/rivets-error-binder

2 parents 5a5b030e a1ae100f
{
"directory": "src/lib"
}
.*.swp
.tmp/
bin/coverage/
dist/
node_modules/
src/lib/
.grunt/
_SpecRunner.html
This diff is collapsed. Click to expand it.
{
"name": "rivets-error-binder",
"version": "0.0.0",
"authors": [
"Adam Heath <doogie@brainfood.com>"
],
"main": [
"src/scripts/rivets-error-binder.js"
],
"private": true,
"ignore": [
"**/.*",
"node_modules",
"src/lib",
"test"
],
"dependencies": {
"backbone-seen": "git@gitlab.brainfood.com:brainfood/backbone-seen.git",
"backbone": "~1.1.0",
"backbone-validation": "0.9.1",
"jquery": "~1.10.2",
"requirejs": "~2.1.10",
"rivets": "~0.6.6",
"underscore": "~1.6.0"
},
"devDependencies": {
"rivets-backbone-adapter": "~1.1.1"
}
}
{
"name": "rivets-error-binder",
"version": "0.0.0",
"main": [
"src/scripts/rivets-error-binder.js"
],
"dependencies": {
"rivets": "~0.6.6",
"requirejs": "~2.1.10"
},
"devDependencies": {
"bower-requirejs": "~0.9.2",
"grunt": "~0.4.1",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-uglify": "~0.2.0",
"grunt-contrib-jshint": "~0.7.0",
"grunt-contrib-cssmin": "~0.7.0",
"grunt-contrib-connect": "~0.5.0",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-htmlmin": "~0.1.3",
"grunt-bower-install": "~0.7.0",
"grunt-contrib-imagemin": "~0.2.0",
"grunt-contrib-watch": "~0.5.2",
"grunt-rev": "~0.1.0",
"grunt-autoprefixer": "~0.5.0",
"grunt-usemin": "~0.1.10",
"grunt-mocha": "~0.4.0",
"grunt-newer": "~0.6.0",
"grunt-svgmin": "~0.2.0",
"grunt-concurrent": "~0.4.0",
"load-grunt-tasks": "~0.2.0",
"time-grunt": "~0.2.0",
"jshint-stylish": "~0.1.3",
"grunt-contrib-requirejs": "~0.4.0",
"grunt-bower-requirejs": "~0.8.4",
"grunt-template-jasmine-istanbul": "~0.2.6",
"grunt-template-jasmine-requirejs": "~0.1.10",
"grunt-contrib-jasmine": "~0.5.3"
},
"engines": {
"node": ">=0.8.0"
}
}
define(['rivets', 'bootstrap'], function(rivets) {
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 self = this;
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, id) {
obj.off('validated', holder.validated);
});
delete this.validationHolder;
rivetsBinderCall(this, this.args[0], 'unbind', arguments);
},
routine: function(el, value) {
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);
}
};
});
define(['backbone'], function(Backbone) {
'use strict';
return Backbone;
});
/* global require:true */
var require;
require = (function() {
'use strict';
var require = {
baseUrl: 'scripts',
config: {
'rivets-error-binder': {}
},
shim: {
bootstrap: {
deps: [
'jquery'
]
},
rivets: {
deps: [
'jquery'
]
}
},
paths: {
'backbone-validation': '../lib/backbone-validation/dist/backbone-validation-amd',
backbone: '../lib/backbone/backbone',
underscore: '../lib/underscore/underscore',
rivets: '../lib/rivets/dist/rivets',
bootstrap: '../lib/bootstrap/dist/js/bootstrap',
jquery: '../lib/jquery/dist/jquery',
'rivets-backbone-adapter': '../lib/rivets-backbone-adapter/rivets-backbone',
'backbone-seen': '../lib/backbone-seen/src/scripts/backbone-seen'
}
};
return require;
})();
/* global require */
require(
[],
function() {
'use strict';
}
);
define([
'module',
'rivets',
], function(
module,
rivets
) {
'use strict';
var rivetsBinderCall = function(binding, binderName, methodName, args) {
var binder = rivets.binders[binderName];
binder[methodName].apply(binding, args);
};
var render = function() {
var renderImpl = module.config().render;
if (renderImpl) {
return renderImpl.apply(this, arguments);
}
};
rivets.binders['error-*'] = {
bind: function(el) {
var holder = this.validationHolder = {
//marker: el.parentNode.insertBefore(document.createComment(" rivets: " + this.type + " "), el),
focus: function() {
render(el, 'focus', false);
},
blur: function() {
if (holder.observer && holder.observer.target) {
holder.observer.target.seen(holder.observer.key.path, true);
}
render(el, 'blur', false);
if (holder.observer && holder.observer.target) {
holder.observer.target.validate();
}
},
validated: function(isValid, model, errors) {
var errorList = errors[holder.observer.key.path];
if (errorList && holder.observer.target && holder.observer.target.seen(holder.observer.key.path)) {
render(el, 'validated', errorList);
} else {
render(el, 'validated', false);
}
}
};
$(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);
if (holder.observer.target) {
holder.observer.target.off('validated', holder.validated);
}
delete this.validationHolder;
rivetsBinderCall(this, this.args[0], 'unbind', arguments);
},
routine: function() {
var holder = this.validationHolder;
if (holder.observer) {
holder.observer.target.off('validated', holder.validated);
}
holder.observer = this.observer;
if (this.observer.target) {
this.observer.target.on('validated', holder.validated);
}
rivetsBinderCall(this, this.args[0], 'routine', arguments);
}
};
});
define(function(require) {
'use strict';
var $ = require('jquery');
window.jQuery = $;
var RivetsErrorBinder = require('rivets-error-binder');
var _ = require('underscore');
var Backbone = require('backbone');
var rivets = require('rivets');
require('backbone-validation');
var BackboneSeen = require('backbone-seen');
require('rivets-backbone-adapter');
_.extend(Backbone.Model.prototype, Backbone.Validation.mixin);
//rivets.config.rootInterface = ':';
describe('RivetsErrorBinder', function() {
it('exists', function() {
expect(RivetsErrorBinder).toBeUndefined();
});
});
describe('RivetsErrorBinder', function() {
var Model, Collection;
var scope;
var test;
var view;
var render;
beforeEach(function() {
render = function(el, cmd, errorList) {
render.lastCmd = cmd;
render.lastErrorList = errorList;
switch (cmd) {
case 'focus':
render.counts.focus++;
break;
case 'blur':
render.counts.blur++;
break;
case 'validated':
if (errorList) {
render.counts.validatedError++;
} else {
render.counts.validatedClean++;
}
break;
}
};
render.counts = {focus: 0, blur: 0, validatedError: 0, validatedClean: 0};
jasmine.Clock.useMock();
Model = BackboneSeen.mixin(Backbone.Model.extend());
Collection = Backbone.Collection.extend({model: Model});
scope = new Model({
test: test = new Model({
constant: 'CONSTANT'
})
});
test.validation = {
constant: {required: true}
};
});
afterEach(function() {
view.unbind();
});
it('existing-no-render', function() {
var $node = $($.parseHTML('<div><form><input id="_1" rv-error-value="test:constant" /></form></div>'));
$(document).find('body').append($node[0]);
var $1 = $node.find('#_1');
expect($1.length).toEqual(1);
expect($1.val()).toEqual('');
expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0});
view = rivets.bind($node, scope.attributes);
expect(view).toBeTruthy();
expect($1.val()).toEqual('CONSTANT');
//expect($node.html()).toBe('');
test.set('constant', 'one');
expect($1.val()).toEqual('one');
test.set('constant', '');
expect($1.val()).toEqual('');
test.set('constant', 'CONSTANT');
expect($1.val()).toEqual('CONSTANT');
expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0});
$1.focus().val('one').change().blur();
jasmine.Clock.tick(1);
expect(test.get('constant')).toEqual('one');
expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0});
$1.focus().val('').change().blur();
jasmine.Clock.tick(1);
expect(test.get('constant')).toEqual('');
expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0});
jasmine.Clock.tick(1);
expect(render.lastErrorList).toBeUndefined();
});
it('existing-with-render', function() {
/* global requirejs */
requirejs.config({config: {'rivets-error-binder': { render: render } } });
var $node = $($.parseHTML('<div><form><input id="_1" rv-error-value="test:constant" /></form></div>'));
$(document).find('body').append($node[0]);
var $1 = $node.find('#_1');
expect($1.length).toEqual(1);
expect($1.val()).toEqual('');
expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0});
view = rivets.bind($node, scope.attributes);
expect(view).toBeTruthy();
expect($1.val()).toEqual('CONSTANT');
//expect($node.html()).toBe('');
test.set('constant', 'one');
expect($1.val()).toEqual('one');
test.set('constant', '');
expect($1.val()).toEqual('');
test.set('constant', 'CONSTANT');
expect($1.val()).toEqual('CONSTANT');
expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0});
$1.focus().val('one').change().blur();
jasmine.Clock.tick(1);
expect(test.get('constant')).toEqual('one');
expect(render.counts).toEqual({focus : 1, blur : 1, validatedError : 0, validatedClean : 1});
$1.focus().val('').change().blur();
jasmine.Clock.tick(1);
expect(test.get('constant')).toEqual('');
expect(render.counts).toEqual({focus : 2, blur : 2, validatedError : 1, validatedClean : 1});
jasmine.Clock.tick(1);
expect(render.lastErrorList).toEqual(jasmine.any(String));
});
it('missing', function() {
var $node = $($.parseHTML('<div><form><input id="_1" rv-error-value="test:missing:child" /></form></div>'));
$(document).find('body').append($node[0]);
var $1 = $node.find('#_1');
expect($1.length).toEqual(1);
expect($1.val()).toEqual('');
view = rivets.bind($node, scope.attributes);
expect(view).toBeTruthy();
expect($1.val()).toEqual('');
$1.focus().val('one').change().blur();
jasmine.Clock.tick(1);
});
});
});