700cfddc by Ean Schuessler

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

2 parents 5a5b030e a1ae100f
1 {
2 "directory": "src/lib"
3 }
1 .*.swp
2 .tmp/
3 bin/coverage/
4 dist/
5 node_modules/
6 src/lib/
7 .grunt/
8 _SpecRunner.html
1 // Generated on 2014-02-06 using generator-webapp 0.4.7
2 /* global module */
3
4 // # Globbing
5 // for performance reasons we're only matching one level down:
6 // 'test/spec/{,*/}*.js'
7 // use this if you want to recursively match all subfolders:
8 // 'test/spec/**/*.js'
9
10 module.exports = function (grunt) {
11 /* global require */
12 'use strict';
13
14 var jasmineRequirejsTemplateOptions = function(withInstanbul) {
15 /* global requirejs */
16 var callback;
17 if (withInstanbul) {
18 callback = function() {
19 var oldLoad = requirejs.load;
20 requirejs.load = function (context, moduleName, url) {
21 //console.log('context=' + JSON.stringify(arguments), 'moduleName=' + moduleName, 'url=' + url);
22 var parts = url.split('/');
23 for (var i = 0; i < parts.length; ) {
24 var part = parts[i];
25 if (part === '.') {
26 parts.splice(i, 1);
27 } else if (part === '') {
28 parts.splice(i, 1);
29 } else if (part === '..') {
30 if (i > 0) {
31 i--;
32 parts.splice(i, 2);
33 } else {
34 parts.splice(i, 1);
35 }
36 } else {
37 i++;
38 }
39 }
40 url = parts.join('/');
41 if (url.indexOf('src/scripts/') === 0) {
42 url = './.grunt/grunt-contrib-jasmine/' + url;
43 }
44 if (url.indexOf('test/specs/') === 0) {
45 url = './.grunt/grunt-contrib-jasmine/' + url;
46 }
47 //console.log('url=' + url);
48 return oldLoad.apply(this, [context, moduleName, url]);
49 };
50 };
51 }
52 return {
53 requireConfigFile: '<%= yeoman.src %>/scripts/config.js',
54 requireConfig: {
55 baseUrl: '<%= yeoman.src %>/scripts',
56 callback: callback
57 }
58 };
59 };
60
61 var jasmineInstanbulTemplateOptions = function(nestedTemplate, nestedOptions) {
62 return {
63 coverage: 'bin/coverage/coverage.json',
64 report: 'bin/coverage',
65 replace: false,
66 template: require(nestedTemplate),
67 templateOptions: nestedOptions
68 };
69 };
70
71 // Load grunt tasks automatically
72 require('load-grunt-tasks')(grunt);
73
74 // Time how long tasks take. Can help when optimizing build times
75 require('time-grunt')(grunt);
76
77 // Define the configuration for all the tasks
78 grunt.initConfig({
79 bower: {
80 target: {
81 options: {
82 exclude: [
83 'requirejs',
84 ],
85 transitive: true,
86 },
87 rjsConfig: '<%= yeoman.src %>/scripts/config.js'
88 }
89 },
90
91 // Project settings
92 yeoman: {
93 // Configurable paths
94 app: 'app',
95 dist: 'dist',
96 src: 'src',
97 },
98
99 // Watches files for changes and runs tasks based on the changed files
100 watch: {
101 js: {
102 files: ['<%= yeoman.src %>/scripts/{,*/}*.js'],
103 tasks: ['jshint'],
104 },
105 jstest: {
106 files: ['test/spec/{,*/}*.js'],
107 tasks: ['test:watch']
108 },
109 gruntfile: {
110 files: ['Gruntfile.js']
111 },
112 styles: {
113 files: ['<%= yeoman.src %>/styles/{,*/}*.css'],
114 tasks: ['newer:copy:styles', 'autoprefixer']
115 }
116 },
117
118 // The actual grunt server settings
119 connect: {
120 options: {
121 port: 9000,
122 // Change this to '0.0.0.0' to access the server from outside
123 hostname: 'localhost'
124 },
125 app: {
126 options: {
127 open: false,
128 base: [
129 '.tmp',
130 '<%= yeoman.src %>'
131 ]
132 }
133 },
134 test: {
135 options: {
136 port: 9001,
137 base: [
138 '.tmp',
139 'test',
140 '<%= yeoman.src %>'
141 ]
142 }
143 },
144 dist: {
145 options: {
146 open: false,
147 base: '<%= yeoman.dist %>',
148 }
149 }
150 },
151
152 // Empties folders to start fresh
153 clean: {
154 dist: {
155 files: [{
156 dot: true,
157 src: [
158 '.tmp',
159 '<%= yeoman.dist %>/*',
160 '!<%= yeoman.dist %>/.git*'
161 ]
162 }]
163 },
164 server: '.tmp'
165 },
166
167 // Make sure code styles are up to par and there are no obvious mistakes
168 jshint: {
169 options: {
170 browser: true,
171 esnext: true,
172 bitwise: true,
173 camelcase: true,
174 curly: true,
175 eqeqeq: true,
176 immed: true,
177 indent: 4,
178 latedef: true,
179 newcap: true,
180 noarg: true,
181 quotmark: 'single',
182 undef: true,
183 unused: true,
184 strict: true,
185 trailing: true,
186 smarttabs: true,
187 jquery: true,
188 reporter: require('jshint-stylish')
189 },
190 all: [
191 'Gruntfile.js',
192 ],
193 scripts: {
194 options: {
195 globals: {
196 define: false,
197 }
198 },
199 files: {
200 src: [
201 '<%= yeoman.src %>/scripts/**/*.js',
202 '!<%= yeoman.src %>/scripts/vendor/*',
203 ]
204 }
205 },
206 specs: {
207 options: {
208 globals: {
209 afterEach: false,
210 beforeEach: false,
211 define: false,
212 describe: false,
213 expect: false,
214 it: false,
215 jasmine: false,
216 }
217 },
218 files: {
219 src: [
220 'test/specs/**/*.spec.js'
221 ]
222 }
223 }
224 },
225
226 jasmine: {
227 all: {
228 src: [
229 '<%= yeoman.src %>/scripts/{,**/}*.js',
230 'test/specs/**/*.spec.js',
231 ],
232 options: {
233 template: require('grunt-template-jasmine-istanbul'),
234 templateOptions: jasmineInstanbulTemplateOptions('grunt-template-jasmine-requirejs', jasmineRequirejsTemplateOptions(true))
235 }
236 }
237 },
238
239 // Mocha testing framework configuration options
240 mocha: {
241 all: {
242 options: {
243 run: true,
244 urls: ['http://<%= connect.test.options.hostname %>:<%= connect.test.options.port %>/index.html']
245 }
246 }
247 },
248
249 // Add vendor prefixed styles
250 autoprefixer: {
251 options: {
252 browsers: ['last 1 version']
253 },
254 dist: {
255 files: [{
256 expand: true,
257 cwd: '.tmp/styles/',
258 src: '{,*/}*.css',
259 dest: '.tmp/styles/'
260 }]
261 }
262 },
263
264 // Automatically inject Bower components into the HTML file
265 'bower-install': {
266 app: {
267 html: '<%= yeoman.src %>/index.html',
268 ignorePath: '<%= yeoman.src %>/'
269 }
270 },
271
272 // Renames files for browser caching purposes
273 rev: {
274 dist: {
275 files: {
276 src: [
277 '<%= yeoman.dist %>/scripts/*/**/*.js',
278 '<%= yeoman.dist %>/scripts/!(config)*.js',
279 '<%= yeoman.dist %>/styles/{,*/}*.css',
280 '<%= yeoman.dist %>/images/{,*/}*.{gif,jpeg,jpg,png,webp}',
281 '<%= yeoman.dist %>/styles/fonts/{,*/}*.*'
282 ]
283 }
284 },
285 requireconfig: {
286 files: {
287 src: [
288 '<%= yeoman.dist %>/scripts/config.js'
289 ]
290 }
291 }
292 },
293
294 requirejs: {
295 dist: {
296 options: {
297 done: function(done) {
298 var requireModules = grunt.config('requireModules') || {};
299 var lines = [
300 'require.bundles = (function(bundles) {',
301 ];
302 for (var key in requireModules) {
303 var keyS = JSON.stringify(key);
304 var value = requireModules[key];
305 var included = [];
306 for (var i = 0; i < value.included.length; i++) {
307 var file = value.included[i];
308 if (file.match(/\.js$/)) {
309 included.push(file.substring(0, file.length - 3));
310 }
311 }
312 lines.push('bundles[' + keyS + '] = ' + JSON.stringify(included) + ';');
313 }
314 lines.push('return bundles;');
315 lines.push('})(require.bundles || {});');
316 grunt.file.write('.tmp/scripts/bundles.js', lines.join('\n'));
317 done();
318 },
319 baseUrl: '<%= yeoman.src %>/scripts',
320 mainConfigFile: '<%= yeoman.src %>/scripts/config.js',
321 wrapShim: true,
322 dir: '<%= yeoman.dist %>/scripts',
323 optimize: 'none',
324 removeCombined: true,
325 onModuleBundleComplete: function(data) {
326 if (data.name.slice(0, 'bundles/'.length) === 'bundles/') {
327 var requireModules = grunt.config('requireModules') || {};
328 requireModules[data.name] = data;
329 grunt.config('requireModules', requireModules);
330 }
331 },
332 }
333 },
334 },
335
336 // Reads HTML for usemin blocks to enable smart builds that automatically
337 // concat, minify and revision files. Creates configurations in memory so
338 // additional tasks can operate on them
339 useminPrepare: {
340 options: {
341 dest: '<%= yeoman.dist %>'
342 },
343 html: '<%= yeoman.src %>/index.html'
344 },
345
346 // Performs rewrites based on rev and the useminPrepare configuration
347 usemin: {
348 options: {
349 assetsDirs: ['<%= yeoman.dist %>']
350 },
351 html: ['<%= yeoman.dist %>/{,*/}*.html'],
352 css: ['<%= yeoman.dist %>/styles/{,*/}*.css']
353 },
354
355 // The following *-min tasks produce minified files in the dist folder
356 imagemin: {
357 dist: {
358 files: [{
359 expand: true,
360 cwd: '<%= yeoman.src %>/images',
361 src: '{,*/}*.{gif,jpeg,jpg,png}',
362 dest: '<%= yeoman.dist %>/images'
363 }]
364 }
365 },
366 svgmin: {
367 dist: {
368 files: [{
369 expand: true,
370 cwd: '<%= yeoman.src %>/images',
371 src: '{,*/}*.svg',
372 dest: '<%= yeoman.dist %>/images'
373 }]
374 }
375 },
376 htmlmin: {
377 dist: {
378 options: {
379 collapseBooleanAttributes: true,
380 collapseWhitespace: true,
381 removeAttributeQuotes: true,
382 removeCommentsFromCDATA: true,
383 removeEmptyAttributes: true,
384 removeOptionalTags: true,
385 removeRedundantAttributes: true,
386 useShortDoctype: true
387 },
388 files: [{
389 expand: true,
390 cwd: '<%= yeoman.dist %>',
391 src: '{,*/}*.html',
392 dest: '<%= yeoman.dist %>'
393 }]
394 }
395 },
396
397 // By default, your `index.html`'s <!-- Usemin block --> will take care of
398 // minification. These next options are pre-configured if you do not wish
399 // to use the Usemin blocks.
400 // cssmin: {
401 // dist: {
402 // files: {
403 // '<%= yeoman.dist %>/styles/main.css': [
404 // '.tmp/styles/{,*/}*.css',
405 // '<%= yeoman.src %>/styles/{,*/}*.css'
406 // ]
407 // }
408 // }
409 // },
410 // uglify: {
411 // dist: {
412 // files: {
413 // '<%= yeoman.dist %>/scripts/scripts.js': [
414 // '<%= yeoman.dist %>/scripts/scripts.js'
415 // ]
416 // }
417 // }
418 // },
419 // concat: {
420 // dist: {}
421 // },
422
423 concat: {
424 requireconfig: {
425 }
426 },
427
428 uglify: {
429 dist: {
430 },
431 requireconfig: {
432 files: {
433 '<%= yeoman.dist %>/scripts/config.js': [
434 '<%= yeoman.dist %>/scripts/config.js',
435 '.tmp/scripts/config.js',
436 ],
437 }
438 }
439 },
440
441 // Copies remaining files to places other tasks can use
442 copy: {
443 dist: {
444 files: [{
445 expand: true,
446 dot: true,
447 cwd: '<%= yeoman.src %>',
448 dest: '<%= yeoman.dist %>',
449 src: [
450 '*.{ico,png,txt}',
451 '.htaccess',
452 'images/{,*/}*.webp',
453 '{,*/}*.html',
454 'styles/fonts/{,*/}*.*'
455 ]
456 }]
457 },
458 styles: {
459 expand: true,
460 dot: true,
461 cwd: '<%= yeoman.src %>/styles',
462 dest: '.tmp/styles/',
463 src: '{,*/}*.css'
464 }
465 },
466
467
468 // Run some tasks in parallel to speed up build process
469 concurrent: {
470 server: [
471 'copy:styles'
472 ],
473 test: [
474 'copy:styles'
475 ],
476 dist: [
477 'copy:styles',
478 'imagemin',
479 'svgmin'
480 ]
481 }
482 });
483
484 grunt.loadNpmTasks('grunt-bower-requirejs');
485
486 grunt.registerTask('revconfig', function () {
487 var prefix = grunt.template.process('<%= yeoman.dist %>/scripts/');
488 var pattern = prefix + '**/*.{js,html}';
489 var files = grunt.file.expand(pattern);
490 var lines = [];
491 grunt.util._.each(files, function(file) {
492 file = file.substring(prefix.length);
493 var res = file.match(/^(.*\/)?([0-9a-f]+)\.([^\/]+)\.([^\.]+)$/);
494 if (!res) {
495 return;
496 }
497 //grunt.log.oklns(JSON.stringify(res));
498 var dir = res[1] || '';
499 //var hash = res[2];
500 var base = res[3];
501 var ext = res[4];
502 var id;
503 if (ext === 'js') {
504 id = dir + base;
505 file = file.substring(0, file.length - ext.length - 1);
506 } else if (ext === 'html') {
507 id = 'text!' + dir + base + '.' + ext;
508 }
509 grunt.log.oklns('map: ' + id + ' -> ' + file);
510 lines.push('require.paths[' + JSON.stringify(id) + ']=' + JSON.stringify(file) + ';\n');
511 });
512 grunt.file.write('.tmp/scripts/config.js', lines.join(''));
513 });
514
515 grunt.registerTask('serve', function (target) {
516 if (target === 'dist') {
517 return grunt.task.run(['build', 'connect:dist:keepalive']);
518 }
519
520 grunt.task.run([
521 'clean:server',
522 'concurrent:server',
523 'autoprefixer',
524 'connect:app',
525 'watch'
526 ]);
527 });
528
529 grunt.registerTask('server', function () {
530 grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
531 grunt.task.run(['serve']);
532 });
533
534 grunt.registerTask('test', function(target) {
535 if (target !== 'watch') {
536 grunt.task.run([
537 'clean:server',
538 'concurrent:test',
539 'autoprefixer',
540 ]);
541 }
542
543 grunt.task.run([
544 'connect:test',
545 'mocha'
546 ]);
547 });
548
549 grunt.registerTask('build', [
550 'clean:dist',
551 'useminPrepare',
552 'requirejs',
553 'concurrent:dist',
554 'autoprefixer',
555 'concat',
556 // 'cssmin',
557 'uglify:dist',
558 'copy:dist',
559 // 'rev:dist',
560 'revconfig',
561 'uglify:requireconfig',
562 // 'rev:requireconfig',
563 'usemin',
564 'htmlmin'
565 ]);
566
567 grunt.registerTask('dist', [
568 'bower',
569 'newer:jshint',
570 // 'test',
571 'build'
572 ]);
573 grunt.registerTask('default', []);
574 };
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 }
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
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 });
1 define(['backbone'], function(Backbone) {
2 'use strict';
3 return Backbone;
4 });
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 })();
1 /* global require */
2 require(
3 [],
4 function() {
5 'use strict';
6 }
7 );
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 });
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 });