First pass.
Showing
8 changed files
with
332 additions
and
0 deletions
Gruntfile.js
0 → 100644
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 | }; |
bower.json
0 → 100644
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#iterate-branch", | ||
20 | "jquery": "", | ||
21 | "requirejs": "", | ||
22 | "rivets": "", | ||
23 | "underscore": "" | ||
24 | } | ||
25 | } |
package.json
0 → 100644
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 |
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 | shim: { | ||
10 | rivets: { | ||
11 | deps: ['jquery'], | ||
12 | }, | ||
13 | }, | ||
14 | paths: { | ||
15 | backbone: '../lib/bower/backbone/backbone', | ||
16 | underscore: '../lib/bower/underscore/underscore', | ||
17 | sightglass: '../lib/bower/sightglass/index', | ||
18 | rivets: '../lib/bower/rivets/dist/rivets', | ||
19 | jquery: '../lib/bower/jquery/dist/jquery', | ||
20 | 'rivets-backbone-adapter-brainfood': '../lib/bower/rivets-backbone-adapter-brainfood/src/scripts/rivets-backbone-adapter-brainfood', | ||
21 | }, | ||
22 | }; | ||
23 | |||
24 | return require; | ||
25 | })(); |
src/scripts/main.js
0 → 100644
src/scripts/rivets-bfeach-binder.js
0 → 100644
1 | define(function(require) { | ||
2 | 'use strict'; | ||
3 | var Backbone = require('backbone'); | ||
4 | var rivets = require('rivets'); | ||
5 | var Sightglass = require('sightglass'); | ||
6 | |||
7 | var setStack = (function(sightglassPrototype) { | ||
8 | var origSet = sightglassPrototype.set; | ||
9 | var setStack = []; | ||
10 | sightglassPrototype.set = function set(active, key, obj, callback) { | ||
11 | try { | ||
12 | setStack.push(arguments); | ||
13 | return origSet.apply(this, arguments); | ||
14 | } finally { | ||
15 | setSack.pop(); | ||
16 | } | ||
17 | }; | ||
18 | return setStack; | ||
19 | })(Sightglass.prototype); | ||
20 | |||
21 | var bfEachUtil = { | ||
22 | makeOverrideAdapters: function makeOverrideAdapters(parentAdapters, roData) { | ||
23 | function getStackPath() { | ||
24 | var currentStackPath = ''; | ||
25 | _.each(setStack, function(stackFrame, stackFrameIndex) { | ||
26 | var key = stackFrame[1]; | ||
27 | currentStackPath += key.i + key.path; | ||
28 | }); | ||
29 | return currentStackPath; | ||
30 | currentStackPath += interfaceKey + leafKey; | ||
31 | if (currentStackPath in roData) { | ||
32 | givenObj = roData[currentStackPath]; | ||
33 | } | ||
34 | return givenObj; | ||
35 | }; | ||
36 | var subAdapters = {}; | ||
37 | _.each(parentAdapters, function(adapterFunctions, interfaceKey) { | ||
38 | var subAdapter = {}; | ||
39 | subAdapter.observe = function(obj, key, cb) { | ||
40 | var stackPath = getStackPath(); | ||
41 | if (stackPath + interfaceKey + key in roData) { | ||
42 | return; | ||
43 | } else if (stackPath in roData) { | ||
44 | obj = roData[stackPath]; | ||
45 | } | ||
46 | return adapterFunctions.observe(obj, key, cb); | ||
47 | }; | ||
48 | subAdapter.unobserve = function(obj, key, cb) { | ||
49 | var stackPath = getStackPath(); | ||
50 | if (stackPath in roData) { | ||
51 | obj = roData[stackPath]; | ||
52 | } | ||
53 | if (stackPath + interfaceKey + key in roData) { | ||
54 | return; | ||
55 | } else if (stackPath in roData) { | ||
56 | obj = roData[stackPath]; | ||
57 | } | ||
58 | return adapterFunctions.unobserve(obj, key, cb); | ||
59 | }; | ||
60 | subAdapter.get = function(obj, key) { | ||
61 | var stackPath = getStackPath(); | ||
62 | if (stackPath in roData) { | ||
63 | obj = roData[stackPath]; | ||
64 | } | ||
65 | var fullKeyPath = stackPath + interfaceKey + key; | ||
66 | if (fullKeyPath in roData) { | ||
67 | return roData[fullKeyPath]; | ||
68 | } else if (stackPath in roData) { | ||
69 | obj = roData[stackPath]; | ||
70 | } | ||
71 | return adapterFunctions.get(obj, key); | ||
72 | }; | ||
73 | subAdapter.set = function(obj, key, value) { | ||
74 | var stackPath = getStackPath(); | ||
75 | if (stackPath in roData) { | ||
76 | obj = roData[stackPath]; | ||
77 | } | ||
78 | var fullKeyPath = stackPath + interfaceKey + key; | ||
79 | if (fullKeyPath in roData) { | ||
80 | return roData[fullKeyPath]; | ||
81 | } else if (stackPath in roData) { | ||
82 | obj = roData[stackPath]; | ||
83 | } | ||
84 | return adapterFunctions.set(obj, key, value); | ||
85 | }; | ||
86 | if (adapterFunctions.iterate) { | ||
87 | subAdapter.iterate = adapterFunctions.iterate; | ||
88 | } | ||
89 | subAdapters[interfaceKey] = subAdapter; | ||
90 | }); | ||
91 | return subAdapters; | ||
92 | }, | ||
93 | getIterator: function getIterator(adapter) { | ||
94 | return adapter.iterate || function iterate(obj, cb) { | ||
95 | obj = obj || []; | ||
96 | var i; | ||
97 | for (i = 0; i < obj.length; i++) { | ||
98 | cb(obj[i], i); | ||
99 | } | ||
100 | }; | ||
101 | }, | ||
102 | }; | ||
103 | |||
104 | (function(rivetsBinders) { | ||
105 | rivetsBinders['bfeach-*'] = { | ||
106 | block: true, | ||
107 | bind: rivetsBinders['each-*'].bind, | ||
108 | unbind: rivetsBinders['each-*'].unbind, | ||
109 | |||
110 | routine: function(el, value) { | ||
111 | var parentView = this.view; | ||
112 | var observer = this.observer; | ||
113 | var adapters = observer.options.adapters; | ||
114 | var iterate = bfEachUtil.getIterator(adapters[observer.key.i]); | ||
115 | |||
116 | var rootInterface = observer.tokens.length ? observer.tokens[0].i : observer.key.i; | ||
117 | var modelName = this.args[0]; | ||
118 | var iterationAlias = rivets.iterationAlias(modelName); | ||
119 | var iterated = this.iterated; | ||
120 | var previous = this.marker; | ||
121 | iterate(value, function(value, index) { | ||
122 | if (iterated[index] === undefined) { | ||
123 | var subData = {}; | ||
124 | subData[rootInterface + 'index'] = index; | ||
125 | subData[rootInterface + modelName] = value; | ||
126 | subData[rootInterface + iterationAlias] = index; | ||
127 | |||
128 | var options = parentView.options(); | ||
129 | options.preloadData = true; | ||
130 | options.adapters = bfEachUtil.makeOverrideAdapters(options.adapters, subData); | ||
131 | var template = el.cloneNode(true); | ||
132 | var childView = new rivets._.View(template, parentView.models, options); | ||
133 | childView.bind(); | ||
134 | iterated.push(childView); | ||
135 | previous.parentNode.insertBefore(template, previous.nextSibling); | ||
136 | previous = template; | ||
137 | } else { | ||
138 | iterated[index].update(value); | ||
139 | } | ||
140 | }); | ||
141 | }, | ||
142 | }; | ||
143 | })(rivets.binders); | ||
144 | return bfEachUtil; | ||
145 | }); |
src/scripts/rivets-bfeach-binder.spec.js
0 → 100644
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 | it('returns defined', function() { | ||
17 | expect(RivetsBFEachUtil).toBeDefined(); | ||
18 | }); | ||
19 | var rootScope, rootNode, rivetsView; | ||
20 | function makeTest(rootData, rootHtml, rootTests) { | ||
21 | beforeEach(function() { | ||
22 | rootScope = new Backbone.Model(rootData); | ||
23 | rootNode = $($.parseHTML(rootHtml)); | ||
24 | rivetsView = rivets.bind(rootNode, rootScope); | ||
25 | }); | ||
26 | rootTests(); | ||
27 | } | ||
28 | makeTest({ | ||
29 | sub: new Backbone.Model({ | ||
30 | list: new Backbone.Collection([ | ||
31 | {key: 'A'}, | ||
32 | {key: 'B'}, | ||
33 | {key: 'C'}, | ||
34 | ], {parse: true}), | ||
35 | }), | ||
36 | list: new Backbone.Collection([ | ||
37 | {key: 'A'}, | ||
38 | {key: 'B'}, | ||
39 | {key: 'C'}, | ||
40 | ], {parse: true}), | ||
41 | constant: 'this-is-a-constant', | ||
42 | }, '<div>{:constant}<div rv-bfeach-item=":list">key:{:item:key} constant:{:constant}</div><div rv-bfeach-item=":sub:list">sub-key:{:item:key} constant:{:constant}</div</div>', function() { | ||
43 | it('foo', function() { | ||
44 | expect(rootNode.html()).toEqual( | ||
45 | 'this-is-a-constant' | ||
46 | + '<!-- rivets: bfeach-item -->' | ||
47 | + '<div>key:A constant:this-is-a-constant</div>' | ||
48 | + '<div>key:B constant:this-is-a-constant</div>' | ||
49 | + '<div>key:C constant:this-is-a-constant</div>' | ||
50 | + '<!-- rivets: bfeach-item -->' | ||
51 | + '<div>sub-key:A constant:this-is-a-constant</div>' | ||
52 | + '<div>sub-key:B constant:this-is-a-constant</div>' | ||
53 | + '<div>sub-key:C constant:this-is-a-constant</div>' | ||
54 | ); | ||
55 | }); | ||
56 | describe('foo', function() { | ||
57 | beforeEach(function() { | ||
58 | //rootScope.set('constant', 'updated-constant'); | ||
59 | rootScope.get('list').add({key: 'D'}, {at: -1, parse: true}); | ||
60 | }); | ||
61 | it('bar', function() { | ||
62 | expect(rootNode.html()).toEqual( | ||
63 | 'updated-constant' | ||
64 | + '<!-- rivets: bfeach-item -->' | ||
65 | + '<div>key:A constant:updated-constant</div>' | ||
66 | + '<div>key:B constant:updated-constant</div>' | ||
67 | + '<div>key:C constant:updated-constant</div>' | ||
68 | + '<!-- rivets: bfeach-item -->' | ||
69 | + '<div>sub-key:A constant:updated-constant</div>' | ||
70 | + '<div>sub-key:B constant:updated-constant</div>' | ||
71 | + '<div>sub-key:C constant:updated-constant</div>' | ||
72 | ); | ||
73 | }); | ||
74 | }); | ||
75 | }); | ||
76 | }); | ||
77 | }); |
-
Please register or sign in to post a comment