Merge branch 'BF-2656' of /home/git/repositories/brainfood/rivets-error-binder
Showing
11 changed files
with
348 additions
and
98 deletions
.gitignore
0 → 100644
Gruntfile.js
0 → 100644
This diff is collapsed.
Click to expand it.
bower.json
0 → 100644
1 | { | ||
2 | "name": "rivets-error-binder", | ||
3 | "version": "0.0.0", | ||
4 | "authors": [ | ||
5 | "Adam Heath <doogie@brainfood.com>" | ||
6 | ], | ||
7 | "main": [ | ||
8 | "src/scripts/rivets-error-binder.js" | ||
9 | ], | ||
10 | "private": true, | ||
11 | "ignore": [ | ||
12 | "**/.*", | ||
13 | "node_modules", | ||
14 | "src/lib", | ||
15 | "test" | ||
16 | ], | ||
17 | "dependencies": { | ||
18 | "backbone-seen": "git@gitlab.brainfood.com:brainfood/backbone-seen.git", | ||
19 | "backbone": "~1.1.0", | ||
20 | "backbone-validation": "0.9.1", | ||
21 | "jquery": "~1.10.2", | ||
22 | "requirejs": "~2.1.10", | ||
23 | "rivets": "~0.6.6", | ||
24 | "underscore": "~1.6.0" | ||
25 | }, | ||
26 | "devDependencies": { | ||
27 | "rivets-backbone-adapter": "~1.1.1" | ||
28 | } | ||
29 | } |
package.json
0 → 100644
1 | { | ||
2 | "name": "rivets-error-binder", | ||
3 | "version": "0.0.0", | ||
4 | "main": [ | ||
5 | "src/scripts/rivets-error-binder.js" | ||
6 | ], | ||
7 | "dependencies": { | ||
8 | "rivets": "~0.6.6", | ||
9 | "requirejs": "~2.1.10" | ||
10 | }, | ||
11 | "devDependencies": { | ||
12 | "bower-requirejs": "~0.9.2", | ||
13 | "grunt": "~0.4.1", | ||
14 | "grunt-contrib-copy": "~0.4.1", | ||
15 | "grunt-contrib-concat": "~0.3.0", | ||
16 | "grunt-contrib-uglify": "~0.2.0", | ||
17 | "grunt-contrib-jshint": "~0.7.0", | ||
18 | "grunt-contrib-cssmin": "~0.7.0", | ||
19 | "grunt-contrib-connect": "~0.5.0", | ||
20 | "grunt-contrib-clean": "~0.5.0", | ||
21 | "grunt-contrib-htmlmin": "~0.1.3", | ||
22 | "grunt-bower-install": "~0.7.0", | ||
23 | "grunt-contrib-imagemin": "~0.2.0", | ||
24 | "grunt-contrib-watch": "~0.5.2", | ||
25 | "grunt-rev": "~0.1.0", | ||
26 | "grunt-autoprefixer": "~0.5.0", | ||
27 | "grunt-usemin": "~0.1.10", | ||
28 | "grunt-mocha": "~0.4.0", | ||
29 | "grunt-newer": "~0.6.0", | ||
30 | "grunt-svgmin": "~0.2.0", | ||
31 | "grunt-concurrent": "~0.4.0", | ||
32 | "load-grunt-tasks": "~0.2.0", | ||
33 | "time-grunt": "~0.2.0", | ||
34 | "jshint-stylish": "~0.1.3", | ||
35 | "grunt-contrib-requirejs": "~0.4.0", | ||
36 | "grunt-bower-requirejs": "~0.8.4", | ||
37 | "grunt-template-jasmine-istanbul": "~0.2.6", | ||
38 | "grunt-template-jasmine-requirejs": "~0.1.10", | ||
39 | "grunt-contrib-jasmine": "~0.5.3" | ||
40 | }, | ||
41 | "engines": { | ||
42 | "node": ">=0.8.0" | ||
43 | } | ||
44 | } | ||
45 |
rivets-error-binder.js
deleted
100644 → 0
1 | define(['rivets', 'bootstrap'], function(rivets) { | ||
2 | var rivetsBinderCall = function(binding, binderName, methodName, args) { | ||
3 | var binder = rivets.binders[binderName]; | ||
4 | if (binder instanceof Function) { | ||
5 | if (methodName == 'routine') { | ||
6 | binder.apply(binding, args); | ||
7 | }; | ||
8 | } else if (binder) { | ||
9 | if (binder[methodName]) { | ||
10 | binder[methodName].apply(binding, args); | ||
11 | } | ||
12 | } | ||
13 | } | ||
14 | |||
15 | var diveIntoObject = function(obj, keypath, callback) { | ||
16 | if (!keypath) { | ||
17 | return callback(obj, null); | ||
18 | } | ||
19 | //return callback(obj, keypath); | ||
20 | var keyparts = keypath.replace(/^:/, '').split(/\:/); | ||
21 | //console.log('diveIntoObject(keyparts):', obj, keyparts); | ||
22 | while (keyparts.length > 1) { | ||
23 | var part = keyparts.shift(); | ||
24 | if (part.length == 0) { | ||
25 | continue; | ||
26 | } | ||
27 | //console.log('diveIntoObject:', obj, part); | ||
28 | obj = doObjectRead(obj, part); | ||
29 | } | ||
30 | //console.log('callback:', obj, keyparts[0]); | ||
31 | return callback(obj, keyparts.shift()); | ||
32 | }; | ||
33 | |||
34 | var doObjectRead = function(obj, id) { | ||
35 | if (obj === null) return obj; | ||
36 | if (!id) return obj; | ||
37 | //console.log('doObjectRead:', obj, id, obj instanceof Backbone.Model, obj instanceof Backbone.Collection); | ||
38 | if (obj instanceof Backbone.Model) { | ||
39 | return obj.get(id); | ||
40 | } else if (obj instanceof Backbone.Collection) { | ||
41 | return obj.at(id); | ||
42 | } else if (obj != null) { | ||
43 | return obj[id]; | ||
44 | } | ||
45 | }; | ||
46 | |||
47 | rivets.binders['error-*'] = { | ||
48 | bind: function(el) { | ||
49 | var self = this; | ||
50 | var holder = this.validationHolder = { | ||
51 | //marker: el.parentNode.insertBefore(document.createComment(" rivets: " + this.type + " "), el), | ||
52 | focus: function() { | ||
53 | $(holder.container).removeClass('focused'); | ||
54 | }, | ||
55 | blur: function() { | ||
56 | if (holder.lastObj) holder.lastObj.seen(holder.lastId, true); | ||
57 | $(holder.container).addClass('focused'); | ||
58 | if (holder.lastObj) holder.lastObj.validate(); | ||
59 | }, | ||
60 | validated: function(isValid, model, errors) { | ||
61 | var errorList = errors[holder.lastId]; | ||
62 | if (errorList && holder.lastObj.seen(holder.lastId)) { | ||
63 | $(el).tooltip({title: errorList, trigger: 'focus'}); | ||
64 | $(el).tooltip('show'); | ||
65 | $(el).parent().addClass('has-error'); | ||
66 | } else { | ||
67 | $(el).tooltip('destroy'); | ||
68 | $(el).parent().removeClass('has-error'); | ||
69 | } | ||
70 | } | ||
71 | }; | ||
72 | $(el).on('focus', holder.focus).on('blur', holder.blur); | ||
73 | rivetsBinderCall(this, this.args[0], 'bind', arguments); | ||
74 | }, | ||
75 | unbind: function(el) { | ||
76 | var holder = this.validationHolder; | ||
77 | $(this.validationHolder.marker).after(el).remove(); | ||
78 | $(el).off('focus', holder.focus).off('blur', holder.blur); | ||
79 | diveIntoObject(this.model, this.keypath, function(obj, id) { | ||
80 | obj.off('validated', holder.validated); | ||
81 | }); | ||
82 | delete this.validationHolder; | ||
83 | rivetsBinderCall(this, this.args[0], 'unbind', arguments); | ||
84 | }, | ||
85 | routine: function(el, value) { | ||
86 | var holder = this.validationHolder; | ||
87 | if (holder.lastObj) { | ||
88 | holder.lastObj.off('validated', holder.validated); | ||
89 | } | ||
90 | diveIntoObject(this.observer.target, this.observer.key.path, function(obj, id) { | ||
91 | holder.lastObj = obj; | ||
92 | holder.lastId = id; | ||
93 | obj.on('validated', holder.validated); | ||
94 | }); | ||
95 | rivetsBinderCall(this, this.args[0], 'routine', arguments); | ||
96 | } | ||
97 | }; | ||
98 | }); |
src/scripts/Backbone.js
0 → 100644
src/scripts/config.js
0 → 100644
1 | /* global require:true */ | ||
2 | var require; | ||
3 | require = (function() { | ||
4 | 'use strict'; | ||
5 | |||
6 | var require = { | ||
7 | baseUrl: 'scripts', | ||
8 | config: { | ||
9 | 'rivets-error-binder': {} | ||
10 | }, | ||
11 | shim: { | ||
12 | bootstrap: { | ||
13 | deps: [ | ||
14 | 'jquery' | ||
15 | ] | ||
16 | }, | ||
17 | rivets: { | ||
18 | deps: [ | ||
19 | 'jquery' | ||
20 | ] | ||
21 | } | ||
22 | }, | ||
23 | paths: { | ||
24 | 'backbone-validation': '../lib/backbone-validation/dist/backbone-validation-amd', | ||
25 | backbone: '../lib/backbone/backbone', | ||
26 | underscore: '../lib/underscore/underscore', | ||
27 | rivets: '../lib/rivets/dist/rivets', | ||
28 | bootstrap: '../lib/bootstrap/dist/js/bootstrap', | ||
29 | jquery: '../lib/jquery/dist/jquery', | ||
30 | 'rivets-backbone-adapter': '../lib/rivets-backbone-adapter/rivets-backbone', | ||
31 | 'backbone-seen': '../lib/backbone-seen/src/scripts/backbone-seen' | ||
32 | } | ||
33 | }; | ||
34 | |||
35 | return require; | ||
36 | })(); |
src/scripts/main.js
0 → 100644
src/scripts/rivets-error-binder.js
0 → 100644
1 | define([ | ||
2 | 'module', | ||
3 | 'rivets', | ||
4 | ], function( | ||
5 | module, | ||
6 | rivets | ||
7 | ) { | ||
8 | 'use strict'; | ||
9 | var rivetsBinderCall = function(binding, binderName, methodName, args) { | ||
10 | var binder = rivets.binders[binderName]; | ||
11 | binder[methodName].apply(binding, args); | ||
12 | }; | ||
13 | |||
14 | var render = function() { | ||
15 | var renderImpl = module.config().render; | ||
16 | if (renderImpl) { | ||
17 | return renderImpl.apply(this, arguments); | ||
18 | } | ||
19 | }; | ||
20 | |||
21 | rivets.binders['error-*'] = { | ||
22 | bind: function(el) { | ||
23 | var holder = this.validationHolder = { | ||
24 | //marker: el.parentNode.insertBefore(document.createComment(" rivets: " + this.type + " "), el), | ||
25 | focus: function() { | ||
26 | render(el, 'focus', false); | ||
27 | }, | ||
28 | blur: function() { | ||
29 | if (holder.observer && holder.observer.target) { | ||
30 | holder.observer.target.seen(holder.observer.key.path, true); | ||
31 | } | ||
32 | render(el, 'blur', false); | ||
33 | if (holder.observer && holder.observer.target) { | ||
34 | holder.observer.target.validate(); | ||
35 | } | ||
36 | }, | ||
37 | validated: function(isValid, model, errors) { | ||
38 | var errorList = errors[holder.observer.key.path]; | ||
39 | if (errorList && holder.observer.target && holder.observer.target.seen(holder.observer.key.path)) { | ||
40 | render(el, 'validated', errorList); | ||
41 | } else { | ||
42 | render(el, 'validated', false); | ||
43 | } | ||
44 | } | ||
45 | }; | ||
46 | $(el).on('focus', holder.focus).on('blur', holder.blur); | ||
47 | rivetsBinderCall(this, this.args[0], 'bind', arguments); | ||
48 | }, | ||
49 | unbind: function(el) { | ||
50 | var holder = this.validationHolder; | ||
51 | $(this.validationHolder.marker).after(el).remove(); | ||
52 | $(el).off('focus', holder.focus).off('blur', holder.blur); | ||
53 | if (holder.observer.target) { | ||
54 | holder.observer.target.off('validated', holder.validated); | ||
55 | } | ||
56 | delete this.validationHolder; | ||
57 | rivetsBinderCall(this, this.args[0], 'unbind', arguments); | ||
58 | }, | ||
59 | routine: function() { | ||
60 | var holder = this.validationHolder; | ||
61 | if (holder.observer) { | ||
62 | holder.observer.target.off('validated', holder.validated); | ||
63 | } | ||
64 | holder.observer = this.observer; | ||
65 | if (this.observer.target) { | ||
66 | this.observer.target.on('validated', holder.validated); | ||
67 | } | ||
68 | rivetsBinderCall(this, this.args[0], 'routine', arguments); | ||
69 | } | ||
70 | }; | ||
71 | }); |
test/specs/rivets-error-binder.spec.js
0 → 100644
1 | define(function(require) { | ||
2 | 'use strict'; | ||
3 | |||
4 | var $ = require('jquery'); | ||
5 | window.jQuery = $; | ||
6 | var RivetsErrorBinder = require('rivets-error-binder'); | ||
7 | var _ = require('underscore'); | ||
8 | var Backbone = require('backbone'); | ||
9 | var rivets = require('rivets'); | ||
10 | require('backbone-validation'); | ||
11 | var BackboneSeen = require('backbone-seen'); | ||
12 | require('rivets-backbone-adapter'); | ||
13 | _.extend(Backbone.Model.prototype, Backbone.Validation.mixin); | ||
14 | //rivets.config.rootInterface = ':'; | ||
15 | |||
16 | describe('RivetsErrorBinder', function() { | ||
17 | it('exists', function() { | ||
18 | expect(RivetsErrorBinder).toBeUndefined(); | ||
19 | }); | ||
20 | }); | ||
21 | describe('RivetsErrorBinder', function() { | ||
22 | var Model, Collection; | ||
23 | var scope; | ||
24 | var test; | ||
25 | var view; | ||
26 | var render; | ||
27 | beforeEach(function() { | ||
28 | render = function(el, cmd, errorList) { | ||
29 | render.lastCmd = cmd; | ||
30 | render.lastErrorList = errorList; | ||
31 | switch (cmd) { | ||
32 | case 'focus': | ||
33 | render.counts.focus++; | ||
34 | break; | ||
35 | case 'blur': | ||
36 | render.counts.blur++; | ||
37 | break; | ||
38 | case 'validated': | ||
39 | if (errorList) { | ||
40 | render.counts.validatedError++; | ||
41 | } else { | ||
42 | render.counts.validatedClean++; | ||
43 | } | ||
44 | break; | ||
45 | } | ||
46 | }; | ||
47 | render.counts = {focus: 0, blur: 0, validatedError: 0, validatedClean: 0}; | ||
48 | |||
49 | jasmine.Clock.useMock(); | ||
50 | Model = BackboneSeen.mixin(Backbone.Model.extend()); | ||
51 | Collection = Backbone.Collection.extend({model: Model}); | ||
52 | |||
53 | scope = new Model({ | ||
54 | test: test = new Model({ | ||
55 | constant: 'CONSTANT' | ||
56 | }) | ||
57 | }); | ||
58 | test.validation = { | ||
59 | constant: {required: true} | ||
60 | }; | ||
61 | }); | ||
62 | afterEach(function() { | ||
63 | view.unbind(); | ||
64 | }); | ||
65 | |||
66 | |||
67 | it('existing-no-render', function() { | ||
68 | var $node = $($.parseHTML('<div><form><input id="_1" rv-error-value="test:constant" /></form></div>')); | ||
69 | $(document).find('body').append($node[0]); | ||
70 | var $1 = $node.find('#_1'); | ||
71 | expect($1.length).toEqual(1); | ||
72 | expect($1.val()).toEqual(''); | ||
73 | expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0}); | ||
74 | view = rivets.bind($node, scope.attributes); | ||
75 | expect(view).toBeTruthy(); | ||
76 | expect($1.val()).toEqual('CONSTANT'); | ||
77 | //expect($node.html()).toBe(''); | ||
78 | test.set('constant', 'one'); | ||
79 | expect($1.val()).toEqual('one'); | ||
80 | test.set('constant', ''); | ||
81 | expect($1.val()).toEqual(''); | ||
82 | test.set('constant', 'CONSTANT'); | ||
83 | expect($1.val()).toEqual('CONSTANT'); | ||
84 | |||
85 | expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0}); | ||
86 | $1.focus().val('one').change().blur(); | ||
87 | jasmine.Clock.tick(1); | ||
88 | expect(test.get('constant')).toEqual('one'); | ||
89 | expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0}); | ||
90 | |||
91 | $1.focus().val('').change().blur(); | ||
92 | jasmine.Clock.tick(1); | ||
93 | expect(test.get('constant')).toEqual(''); | ||
94 | expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0}); | ||
95 | jasmine.Clock.tick(1); | ||
96 | expect(render.lastErrorList).toBeUndefined(); | ||
97 | }); | ||
98 | it('existing-with-render', function() { | ||
99 | /* global requirejs */ | ||
100 | requirejs.config({config: {'rivets-error-binder': { render: render } } }); | ||
101 | var $node = $($.parseHTML('<div><form><input id="_1" rv-error-value="test:constant" /></form></div>')); | ||
102 | $(document).find('body').append($node[0]); | ||
103 | var $1 = $node.find('#_1'); | ||
104 | expect($1.length).toEqual(1); | ||
105 | expect($1.val()).toEqual(''); | ||
106 | expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0}); | ||
107 | view = rivets.bind($node, scope.attributes); | ||
108 | expect(view).toBeTruthy(); | ||
109 | expect($1.val()).toEqual('CONSTANT'); | ||
110 | //expect($node.html()).toBe(''); | ||
111 | test.set('constant', 'one'); | ||
112 | expect($1.val()).toEqual('one'); | ||
113 | test.set('constant', ''); | ||
114 | expect($1.val()).toEqual(''); | ||
115 | test.set('constant', 'CONSTANT'); | ||
116 | expect($1.val()).toEqual('CONSTANT'); | ||
117 | |||
118 | expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0}); | ||
119 | $1.focus().val('one').change().blur(); | ||
120 | jasmine.Clock.tick(1); | ||
121 | expect(test.get('constant')).toEqual('one'); | ||
122 | expect(render.counts).toEqual({focus : 1, blur : 1, validatedError : 0, validatedClean : 1}); | ||
123 | |||
124 | $1.focus().val('').change().blur(); | ||
125 | jasmine.Clock.tick(1); | ||
126 | expect(test.get('constant')).toEqual(''); | ||
127 | expect(render.counts).toEqual({focus : 2, blur : 2, validatedError : 1, validatedClean : 1}); | ||
128 | jasmine.Clock.tick(1); | ||
129 | expect(render.lastErrorList).toEqual(jasmine.any(String)); | ||
130 | }); | ||
131 | it('missing', function() { | ||
132 | var $node = $($.parseHTML('<div><form><input id="_1" rv-error-value="test:missing:child" /></form></div>')); | ||
133 | $(document).find('body').append($node[0]); | ||
134 | var $1 = $node.find('#_1'); | ||
135 | expect($1.length).toEqual(1); | ||
136 | expect($1.val()).toEqual(''); | ||
137 | view = rivets.bind($node, scope.attributes); | ||
138 | expect(view).toBeTruthy(); | ||
139 | expect($1.val()).toEqual(''); | ||
140 | |||
141 | $1.focus().val('one').change().blur(); | ||
142 | jasmine.Clock.tick(1); | ||
143 | }); | ||
144 | }); | ||
145 | }); |
-
Please register or sign in to post a comment