04d7f787 by Adam Heath

First pass.

1 parent 155b7073
1 .*.sw?
2
3 /.grunt/
4 /node_modules/
5 /src/lib/bower/
6 /dist/
1 /* global module */
2
3 module.exports = function (grunt) {
4 /* global require */
5 'use strict';
6
7 var config = {};
8 config.base = 'src';
9 config.jshint = {
10 options: {
11 },
12 browserOptions: {
13 },
14 };
15 config.bower = {
16 directory: 'lib/bower',
17 };
18 config.jscs = {
19 options: {
20 validateIndentation: 4,
21 reporter: 'console',
22 maxErrors: -1,
23 },
24 };
25 config.jasmine = {
26 withCoverage: true,
27 };
28 var montyPython = require('grunt-monty-python')(grunt);
29 montyPython.createConfig(config);
30 };
1 {
2 "name": "rivets-bfeach-binder",
3 "version": "0.0.0",
4 "authors": [
5 "Adam Heath <doogie@brainfood.com>"
6 ],
7 "main": [
8 "src/scripts/rivets-bfeach-binder.js"
9 ],
10 "private": true,
11 "ignore": [
12 "**/.*",
13 "node_modules",
14 "src/lib"
15 ],
16 "dependencies": {
17 "backbone": "",
18 "backbone-validation": "",
19 "rivets-backbone-adapter-brainfood": "git@gitlab.brainfood.com:brainfood/rivets-backbone-adapter-brainfood.git",
20 "backbone-model-overlay": "git@gitlab.brainfood.com:brainfood/backbone-model-overlay.git",
21 "jquery": "",
22 "requirejs": "",
23 "rivets": "",
24 "underscore": ""
25 }
26 }
1 {
2 "name": "rivets-bfeach-binder",
3 "version": "0.0.0",
4 "main": [
5 "src/scripts/rivets-bfeach-binder.js"
6 ],
7 "dependencies": {
8 "rivets": "",
9 "requirejs": ""
10 },
11 "devDependencies": {
12 "bower-requirejs": "~0.9.2",
13 "grunt": "~0",
14 "grunt-monty-python": "git+ssh://git@gitlab.brainfood.com:brainfood/grunt-monty-python.git"
15 },
16 "engines": {
17 "node": ">=0.8.0"
18 }
19 }
20
1 /* global require:true */
2 var require;
3 require = (function() {
4 'use strict';
5
6 var require = {
7 baseUrl: 'scripts',
8 config: {},
9 shim: {
10 rivets: {
11 deps: ['jquery'],
12 },
13 },
14 _map: {
15 '*': {
16 'sightglass': 'sightglass-overlay',
17 },
18 'sightglass-overlay': {
19 'sightglass': 'sightglass',
20 },
21 },
22 paths: {
23 backbone: '../lib/bower/backbone/backbone',
24 underscore: '../lib/bower/underscore/underscore',
25 sightglass: '../lib/bower/sightglass/index',
26 rivets: '../lib/bower/rivets/dist/rivets',
27 jquery: '../lib/bower/jquery/dist/jquery',
28 'rivets-backbone-adapter-brainfood': '../lib/bower/rivets-backbone-adapter-brainfood/src/scripts/rivets-backbone-adapter-brainfood',
29 'backbone-model-overlay': '../lib/bower/backbone-model-overlay/src/scripts/backbone-model-overlay',
30 },
31 };
32
33 return require;
34 })();
1 /* global require */
2 define(function() {
3 'use strict';
4 });
1 define(function(require) {
2 'use strict';
3 var Backbone = require('backbone');
4 var rivets = require('rivets');
5 var BackboneModelOverlay = require('backbone-model-overlay');
6
7 var bfEachUtil = {
8 getIterator: function getIterator(adapter) {
9 return adapter.iterate || function iterate(obj, cb) {
10 obj = obj || [];
11 var i;
12 for (i = 0; i < obj.length; i++) {
13 cb(obj[i], i);
14 }
15 };
16 },
17 };
18 (function(rivetsBinders) {
19 rivetsBinders['bfeach-*'] = {
20 block: true,
21 bind: function(el) {
22 var insertionPoint = this.insertionPoint;
23 if (this.insertionPoint === undefined) {
24 var type = this.type, parentNode = el.parentNode;
25 this.insertionPoint = insertionPoint = document.createComment(' rivets: ' + type + ' ');
26 el.removeAttribute([this.view.prefix, type].join('-').replace('--', '-'));
27 this.subItems = [];
28 parentNode.insertBefore(insertionPoint, el);
29 parentNode.removeChild(el);
30 } else {
31 var i;
32 var subItems = this.subItems;
33 for (i = 0; i < subItems.length; i++) {
34 subItems[i].view.bind();
35 }
36 }
37 },
38 unbind: function(el) {
39 var i;
40 var subItems = this.subItems;
41 for (i = 0; i < subItems.length; i++) {
42 subItems[i].view.unbind();
43 }
44 },
45 routine: function(el, value) {
46 var parentView = this.view;
47 var observer = this.observer;
48 var adapters = observer.options.adapters;
49 var iterate = bfEachUtil.getIterator(adapters[observer.key.i]);
50
51 var rootInterface = observer.tokens.length ? observer.tokens[0].i : observer.key.i;
52 var modelName = this.args[0];
53 var iterationAlias = rivets.iterationAlias(modelName);
54 var subItems = this.subItems;
55 var insertionPoint = this.insertionPoint;
56 var newItemCount = 0;
57 iterate(value, function(value, index) {
58 var subItem = subItems[index];
59 var subData = {};
60 subData[modelName] = value;
61 subData['index'] = index;
62 subData[iterationAlias] = index;
63 if (subItem === undefined) {
64 var clonedEl = el.cloneNode(true);
65 subItem = {model: new BackboneModelOverlay(subData, {parent: parentView.models}), el: clonedEl};
66 var options = parentView.options();
67 options.preloadData = true;
68 //options.adapters = bfEachUtil.makeOverrideAdapters(options.adapters, subData);
69 subItem.view = rivets.bind(clonedEl, subItem.model, options);
70 subItems.push(subItem);
71 insertionPoint.parentNode.insertBefore(clonedEl, insertionPoint.nextSibling);
72 } else {
73 subItem.model.set(subData, {overlay: true});
74 }
75 insertionPoint = subItem.el;
76 newItemCount++;
77 });
78 while (subItems.length > newItemCount) {
79 var extraItem = subItems.pop();
80 extraItem.view.unbind();
81 extraItem.el.parentNode.removeChild(extraItem.el);
82 }
83 },
84 // update function not needed
85 };
86 })(rivets.binders);
87 return bfEachUtil;
88 });
1 define(function(require) {
2 'use strict';
3
4 var $ = require('jquery');
5 window.jQuery = $;
6 var RivetsBFEachUtil = require('rivets-bfeach-binder');
7 require('rivets-backbone-adapter-brainfood');
8 var _ = require('underscore');
9 var Backbone = require('backbone');
10 var rivets = require('rivets');
11
12 //rivets.config.rootInterface = ':';
13 /* global console:false */
14
15 describe('RivetsBFEachUtil', function() {
16 var rootScope, rootNode;
17 beforeEach(function() {
18 rootScope = new Backbone.Model({
19 sub: new Backbone.Model({
20 subKey: 'sub-key',
21 list: new Backbone.Collection([
22 {key: 'A'},
23 {key: 'B'},
24 {key: 'C'},
25 ], {parse: true}),
26 }),
27 list: new Backbone.Collection([
28 {key: 'A'},
29 {key: 'B'},
30 {key: 'C'},
31 ], {parse: true}),
32 constant: 'this-is-a-constant',
33 });
34 rootNode = $($.parseHTML('<div>{:constant}<div rv-bfeach-item=":list">key:{:item:key} constant:{:constant}<ul><li rv-bfeach-subitem=":sub:list">sub-key:{:subitem:key} constant:{:constant}<input rv-value=":inputValue" /></li></ul></div></div>'));
35 });
36 it('returns defined', function() {
37 expect(RivetsBFEachUtil).toBeDefined();
38 });
39 describe('binder', function() {
40 var rivetsView;
41 beforeEach(function() {
42 rivetsView = rivets.bind(rootNode, rootScope);
43 });
44 it('first-pass', function() {
45 var subListHtml = '<ul><!-- rivets: bfeach-subitem --><li>sub-key:A constant:this-is-a-constant<input rv-value=":inputValue"></li><li>sub-key:B constant:this-is-a-constant<input rv-value=":inputValue"></li><li>sub-key:C constant:this-is-a-constant<input rv-value=":inputValue"></li></ul>';
46 expect(rootNode.html()).toEqual(
47 'this-is-a-constant' +
48 '<!-- rivets: bfeach-item -->' +
49 '<div>key:A constant:this-is-a-constant' + subListHtml + '</div>' +
50 '<div>key:B constant:this-is-a-constant' + subListHtml + '</div>' +
51 '<div>key:C constant:this-is-a-constant' + subListHtml + '</div>'
52 );
53 });
54 describe('add-insert', function() {
55 beforeEach(function() {
56 rootScope.get('list').add({key: 'D'}, {at: -1, parse: true});
57 rootScope.get('list').add({key: 'preA'}, {at: 0, parse: true});
58 });
59 it('test', function() {
60 var subListHtml = '<ul><!-- rivets: bfeach-subitem --><li>sub-key:A constant:this-is-a-constant<input rv-value=":inputValue"></li><li>sub-key:B constant:this-is-a-constant<input rv-value=":inputValue"></li><li>sub-key:C constant:this-is-a-constant<input rv-value=":inputValue"></li></ul>';
61 expect(rootNode.html()).toEqual(
62 'this-is-a-constant' +
63 '<!-- rivets: bfeach-item -->' +
64 '<div>key:preA constant:this-is-a-constant' + subListHtml + '</div>' +
65 '<div>key:A constant:this-is-a-constant' + subListHtml + '</div>' +
66 '<div>key:B constant:this-is-a-constant' + subListHtml + '</div>' +
67 '<div>key:C constant:this-is-a-constant' + subListHtml + '</div>' +
68 '<div>key:D constant:this-is-a-constant' + subListHtml + '</div>'
69 );
70 });
71 describe('update-constant', function() {
72 var subListHtml = '<ul><!-- rivets: bfeach-subitem --><li>sub-key:A constant:updated-constant<input rv-value=":inputValue"></li><li>sub-key:B constant:updated-constant<input rv-value=":inputValue"></li><li>sub-key:C constant:updated-constant<input rv-value=":inputValue"></li></ul>';
73 var wantedHtml =
74 'updated-constant' +
75 '<!-- rivets: bfeach-item -->' +
76 '<div>key:preA constant:updated-constant' + subListHtml + '</div>' +
77 '<div>key:A constant:updated-constant' + subListHtml + '</div>' +
78 '<div>key:B constant:updated-constant' + subListHtml + '</div>' +
79 '<div>key:C constant:updated-constant' + subListHtml + '</div>' +
80 '<div>key:D constant:updated-constant' + subListHtml + '</div>';
81 beforeEach(function() {
82 rootScope.set('constant', 'updated-constant');
83 });
84 it('test', function() {
85 expect(rootNode.html()).toEqual(wantedHtml);
86 });
87 describe('unbind+change', function() {
88 beforeEach(function() {
89 rivetsView.unbind();
90 rootScope.set('constant', 'changed-constant');
91 });
92 it('test', function() {
93 expect(rootNode.html()).toEqual(wantedHtml);
94 });
95 describe('bind', function() {
96 beforeEach(function() {
97 rivetsView.bind();
98 });
99 it('test', function() {
100 expect(rootNode.html()).toEqual(wantedHtml.replace(/updated-constant/g, 'changed-constant'));
101 });
102 describe('shift', function() {
103 beforeEach(function() {
104 rootScope.get('sub').get('list').shift();
105 });
106 it('test', function() {
107 expect(rootNode.html()).toEqual(wantedHtml.replace(/updated-constant/g, 'changed-constant').replace(/<li>sub-key:A constant:changed-constant<input rv-value=":inputValue"><\/li>/g, ''));
108 expect(rootScope.get('inputValue')).toBeUndefined();
109 });
110 describe('input-set-value', function() {
111 beforeEach(function() {
112 var lastInput = rootNode.find('input:last');
113 lastInput.trigger('focus');
114 lastInput[0].value = 'typed';
115 lastInput.trigger('input');
116 lastInput.trigger('change').trigger('blur');
117 });
118 it('test', function() {
119 expect(rootScope.get('inputValue')).toEqual('typed');
120 var inputElementValues = [];
121 rootNode.find('input').each(function(i) {
122 inputElementValues.push(this.value);
123 });
124 expect(inputElementValues).toEqual([
125 'typed', 'typed', // preA
126 'typed', 'typed', // A
127 'typed', 'typed', // B
128 'typed', 'typed', // C
129 'typed', 'typed', // D
130 ]);
131 });
132 });
133 });
134 });
135 });
136 });
137 });
138 });
139 });
140 });
1 define(function(require) {
2 'use strict';
3 var Sightglass = require('sightglass');
4
5 function callIt(container, methodName, args) {
6 return container[methodName].apply(container, args);
7 }
8 var tokenStack;
9 function sightglass() {
10 tokenStack = [];
11 try {
12 return Sightglass.apply(this, arguments);
13 } finally {
14 tokenStack = [];
15 }
16 };
17 sightglass.makeOverrideAdapters = function makeOverrideAdapters(parentAdapters, roData) {
18 function getStackPath() {
19 var currentStackPath = '';
20 _.each(tokenStack, function(token) {
21 currentStackPath += token.i + token.path;
22 });
23 return currentStackPath;
24 }
25 var subAdapters = {};
26 _.each(parentAdapters, function(adapterFunctions, interfaceKey) {
27 var subAdapter = {};
28 _.each(['observe', 'unobserve', 'get', 'set'], function wrapApiMethod(methodName) {
29 subAdapter[methodName] = function(obj, key, cbOrValue) {
30 var stackPath = getStackPath();
31 var fullKeyPath = stackPath + interfaceKey + key;
32 if (fullKeyPath in roData) {
33 return callIt(parentAdapters['.'], methodName, [roData, fullKeyPath, cbOrValue]);
34 } else if (stackPath in roData) {
35 obj = roData[stackPath];
36 }
37 try {
38 return callIt(adapterFunctions, methodName, [obj, key, cbOrValue]);
39 } finally {
40 tokenStack.push({i: interfaceKey, path: key});
41 }
42 };
43 });
44 if (adapterFunctions.iterate) {
45 subAdapter.iterate = adapterFunctions.iterate;
46 }
47 subAdapters[interfaceKey] = subAdapter;
48 });
49 return subAdapters;
50 };
51
52 return sightglass;
53 });
1 define(function(require) {
2 'use strict';
3
4 var $ = require('jquery');
5 window.jQuery = $;
6 var SightglassOverlay = require('sightglass-overlay');
7 require('rivets-backbone-adapter-brainfood');
8 var _ = require('underscore');
9 var Backbone = require('backbone');
10 var rivets = require('rivets');
11
12 //rivets.config.rootInterface = ':';
13 /* global console:false */
14
15 describe('SightglassOverlay', function() {
16 var rootScope, rootNode;
17 beforeEach(function() {
18 rootScope = new Backbone.Model({
19 sub: new Backbone.Model({
20 subKey: 'sub-key',
21 list: new Backbone.Collection([
22 {key: 'A'},
23 {key: 'B'},
24 {key: 'C'},
25 ], {parse: true}),
26 }),
27 list: new Backbone.Collection([
28 {key: 'A'},
29 {key: 'B'},
30 {key: 'C'},
31 ], {parse: true}),
32 constant: 'this-is-a-constant',
33 });
34 rootNode = $($.parseHTML('<div>{:constant}<div rv-bfeach-item=":list">key:{:item:key} constant:{:constant}<ul><li rv-bfeach-subitem=":sub:list">sub-key:{:subitem:key} constant:{:constant}<input rv-value=":inputValue" /></li></ul></div></div>'));
35 });
36 it('returns defined', function() {
37 expect(SightglassOverlay).toBeDefined();
38 });
39 describe('makeOverrideAdapters', function() {
40 var newAdapters, roData, rivetsView;
41 beforeEach(function() {
42 roData = {};
43 roData[':sub:list'] = new Backbone.Collection([
44 {key: 'sub1'},
45 {key: 'sub2'},
46 {key: 'sub3'},
47 ], {parse: true});
48 roData[':list'] = new Backbone.Collection([
49 {key: '1'},
50 {key: '2'},
51 {key: '3'},
52 ], {parse: true});
53 newAdapters = SightglassOverlay.makeOverrideAdapters(rivets.adapters, roData);
54 rivetsView = rivets.bind(rootNode, rootScope, {adapters: newAdapters});
55 });
56 xit('test', function() {
57 var subListHtml = '<ul><!-- rivets: bfeach-subitem --><li>sub-key:sub1 constant:this-is-a-constant<input rv-value=":inputValue"></li><li>sub-key:sub2 constant:this-is-a-constant<input rv-value=":inputValue"></li><li>sub-key:sub3 constant:this-is-a-constant<input rv-value=":inputValue"></li></ul>';
58 expect(rootNode.html()).toEqual(
59 'this-is-a-constant' +
60 '<!-- rivets: bfeach-item -->' +
61 '<div>key:1 constant:this-is-a-constant' + subListHtml + '</div>' +
62 '<div>key:2 constant:this-is-a-constant' + subListHtml + '</div>' +
63 '<div>key:3 constant:this-is-a-constant' + subListHtml + '</div>'
64 );
65 });
66 });
67 });
68 });