UNPKG

175 kBtext/coffeescriptView Raw
1if mocha? and window?
2 isBrowser = true
3 mocha.setup('tdd')
4 mocha.bail() unless @isSauceLabsTest or location.hostname
5else
6 isBrowser = false
7 global.window = global
8 chai = require 'chai'
9 chaiSpies = require 'chai-spies'
10 SimplyBind = require '../dist/simplybind.node.js'
11 startSandbox = require('./spec-helper.js').startSandbox
12 restartSandbox = require('./spec-helper.js').restartSandbox
13 chai.use chaiSpies
14
15expect = chai.expect
16should = chai.should()
17
18
19
20suite "SimplyBind", ()->
21 suiteSetup(startSandbox)
22
23 suite "Options", ()->
24 test "SimplyBind.options should contain the global options list", ()->
25 options = SimplyBind.options
26 expect(options.silent).not.to.be.undefined
27 expect(options.liveProps).not.to.be.undefined
28 expect(options.dispatchEvents).not.to.be.undefined
29 expect(options.updateEvenIfSame).not.to.be.undefined
30 expect(options.updateOnBind).not.to.be.undefined
31 expect(options.mutateInherited).not.to.be.undefined
32 expect(options.trackArrayChildren).not.to.be.undefined
33 expect(options.simpleSelector).not.to.be.undefined
34 expect(options.promiseTransforms).not.to.be.undefined
35 expect(options.placeholder).not.to.be.undefined
36
37
38
39 test "SimplyBind.options should return a non-editable copy of the global options list", ()->
40 expect(SimplyBind.options.dispatchEvents).to.be.false
41 SimplyBind.options.dispatchEvents = true
42 expect(SimplyBind.options.dispatchEvents).to.be.false
43
44
45
46 test "SimplyBind.setOption() should set a new value for a single option", ()->
47 expect(SimplyBind.options.silent).to.equal false
48 SimplyBind.setOption 'silent', true
49 expect(SimplyBind.options.silent).to.equal true
50 SimplyBind.setOption 'silent', false
51
52
53
54
55 test "SimplyBind.setOptions() should set new values for a all the options passed", ()->
56 expect(SimplyBind.options.silent).to.equal false
57 expect(SimplyBind.options.liveProps).to.equal true
58
59 SimplyBind.setOptions
60 'silent': true
61 'liveProps': false
62
63 expect(SimplyBind.options.silent).to.equal true
64 expect(SimplyBind.options.liveProps).to.equal false
65
66 SimplyBind.setOptions
67 'silent': false
68 'liveProps': true
69 SimplyBind.setOptions {}
70
71 expect(SimplyBind.options.silent).to.equal false
72 expect(SimplyBind.options.liveProps).to.equal true
73
74
75
76 test "SimplyBind.setOptions() should not add non-registered options", ()->
77 SimplyBind.setOptions
78 placeholder: ['{{', '}}']
79 youtube: 'yea!'
80
81 expect(SimplyBind.options.placeholder[0]).to.equal '{{'
82 expect(SimplyBind.options.placeholder[1]).to.equal '}}'
83 expect(SimplyBind.options.youtube).to.be.undefined
84
85
86
87 test "<bound instance>.setOption should update a registered option only for the instance", ()->
88 SimplyBind.setOption 'updateOnBind', false
89 expect(SimplyBind.options.updateOnBind).to.be.false
90
91 binding = SimplyBind('prop1').of(objectA)
92 expect(binding._.options.updateOnBind).to.be.false
93
94 binding.setOption 'updateOnBind', true
95 expect(binding._.options.updateOnBind).to.be.true
96 expect(SimplyBind.options.updateOnBind).to.be.false
97
98 SimplyBind.setOption 'updateOnBind', true
99 restartSandbox()
100
101
102
103 test "Creating a new binding interface for a cached binding without passing an options object shouldn't overwrite existing options", ()->
104 SimplyBind.setOption 'updateOnBind', false
105 expect(SimplyBind.options.updateOnBind).to.be.false
106
107 binding = SimplyBind('prop1').of(objectA)
108 expect(binding._.options.updateOnBind).to.be.false
109
110 binding.setOption 'updateOnBind', true
111 expect(SimplyBind('prop1').of(objectA)._.options.updateOnBind).to.be.true
112 expect(SimplyBind.options.updateOnBind).to.be.false
113
114 SimplyBind.setOption 'updateOnBind', true
115 restartSandbox()
116
117
118
119 test "<bound instance>.setOption should update a registered option only for the instance", ()->
120 binding = SimplyBind('prop1').of(objectA)
121
122 binding.setOption 'placeholder', ['{{', '}}']
123 binding.setOption 'youtube', 'yea'
124
125 expect(binding._.options.placeholder[0]).to.equal '{{'
126 expect(binding._.options.placeholder[1]).to.equal '}}'
127 expect(binding._.options.youtube).to.be.undefined
128
129 restartSandbox()
130
131
132
133 test "Passing an options object as the 2nd param of .of() should update registered options only for the instance", ()->
134 SimplyBind.setOption 'updateOnBind', false
135 expect(SimplyBind.options.updateOnBind).to.be.false
136
137 binding = SimplyBind('prop1', {'updateOnBind':true}).of(objectA)
138 expect(binding._.options.updateOnBind).to.be.true
139 expect(SimplyBind.options.updateOnBind).to.be.false
140
141 SimplyBind.setOption 'updateOnBind', true
142 restartSandbox()
143
144
145
146 test "Passing an options object as the 2nd param of .of() should update the options even for an existing binding instance", ()->
147 SimplyBind.setOption 'updateOnBind', false
148 expect(SimplyBind.options.updateOnBind).to.be.false
149
150 binding = SimplyBind('prop1').of(objectA)
151 expect(binding._.options.updateOnBind).to.be.false
152
153 binding = SimplyBind('prop1', {'updateOnBind':true, 'nonexistent':true}).of(objectA)
154 expect(binding._.options.updateOnBind).to.be.true
155 expect(binding._.options.nonexistent).to.be.undefined
156 expect(SimplyBind.options.updateOnBind).to.be.false
157
158 SimplyBind.setOption 'updateOnBind', true
159 restartSandbox()
160
161
162
163 test "Passing an options object as the 2nd param of .of() for an existing binding should invoke the required methods for these options", ()->
164 SimplyBind.setOption 'liveProps', false
165 SimplyBind.setOption 'updateOnBind', false
166 SimplyBind.setOption 'mutateInherited', false
167 SimplyBind.setOption 'trackArrayChildren', false
168 Object::id = ''
169
170 arrayInvokeCount = 0
171 newOptions = {'liveProps':true, 'mutateInherited':true, 'trackArrayChildren':true}
172 bindings =
173 'liveProps': SimplyBind('prop1').of(objectA)
174 'mutateInherited': SimplyBind('id').of(objectB)
175 'trackArrayChildren': SimplyBind(arrayA).to ()-> arrayInvokeCount++
176
177 objectA.prop1 = '1'
178 expect(bindings.liveProps.value).not.to.equal '1'
179
180 objectB.id = '1'
181 expect(bindings.mutateInherited.value).not.to.equal '1'
182
183 arrayA[3] = '1'
184 expect(arrayInvokeCount).to.equal 0
185
186
187
188 SimplyBind.setOption 'liveProps', true
189 SimplyBind.setOption 'updateOnBind', true
190
191 SimplyBind('prop1', newOptions).of(objectA)
192 SimplyBind('id', newOptions).of(objectB)
193 SimplyBind(arrayA, newOptions)
194
195 objectA.prop1 = '2'
196 expect(bindings.liveProps.value).to.equal '2'
197
198 objectB.id = '2'
199 expect(bindings.mutateInherited.value).to.equal '2'
200
201 arrayA[5] = '2'
202 expect(arrayInvokeCount).to.equal 1
203
204 delete Object::id
205 restartSandbox()
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224 suite "Argument Values", ()->
225 suiteSetup(restartSandbox)
226
227 suite "SimplyBind() function", ()->
228 test "Can only accept an object type of String, Number, Array, Function, or an existing binding instance", ()->
229 expect ()-> SimplyBind('s')
230 .not.to.throw()
231
232 expect ()-> SimplyBind(0)
233 .not.to.throw()
234
235 expect ()-> SimplyBind([])
236 .not.to.throw()
237
238 expect ()-> SimplyBind(()->)
239 .not.to.throw()
240
241 expect ()-> SimplyBind(SimplyBind('prop1').of(objectA))
242 .not.to.throw()
243
244 expect ()-> SimplyBind(true)
245 .to.throw()
246
247 expect ()-> SimplyBind(null)
248 .to.throw()
249
250 expect ()-> SimplyBind(undefined)
251 .to.throw()
252
253 expect ()-> SimplyBind({})
254 .to.throw()
255
256 expect ()-> SimplyBind(new Date())
257 .to.throw()
258
259 if isBrowser
260 expect ()-> SimplyBind($('<div />')[0])
261 .to.throw()
262
263 if window.Map and window.Set and window.Symbol
264 expect ()-> SimplyBind(new Map())
265 .to.throw()
266
267 expect ()-> SimplyBind(new WeakMap())
268 .to.throw()
269
270 expect ()-> SimplyBind(new Set())
271 .to.throw()
272
273 expect ()-> SimplyBind(new WeakSet())
274 .to.throw()
275
276 expect ()-> SimplyBind(Symbol('a'))
277 .to.throw()
278
279
280
281 test "Should throw when passing an empty string", ()->
282 expect ()-> SimplyBind('')
283 .to.throw()
284
285
286
287 suite ".of() method", ()->
288 test "Can only accept non-primitive objects", ()->
289 expect ()-> SimplyBind(0).of([])
290 .not.to.throw()
291
292 expect ()-> SimplyBind(0).of(()->)
293 .not.to.throw()
294
295 expect ()-> SimplyBind(0).of({})
296 .not.to.throw()
297
298 expect ()-> SimplyBind(0).of(new Date())
299 .not.to.throw()
300
301 if isBrowser
302 expect ()-> SimplyBind(0).of($('<div />')[0])
303 .not.to.throw()
304
305 if window.Map and window.Set and window.Symbol
306 expect ()-> SimplyBind(0).of(new Map())
307 .not.to.throw()
308
309 expect ()-> SimplyBind(0).of(new WeakMap())
310 .not.to.throw()
311
312 expect ()-> SimplyBind(0).of(new Set())
313 .not.to.throw()
314
315 expect ()-> SimplyBind(0).of(new WeakSet())
316 .not.to.throw()
317
318 expect ()-> SimplyBind(0).of(Symbol(0))
319 .to.throw()
320
321 expect ()-> SimplyBind(0).of(SimplyBind('prop1').of(objectA))
322 .not.to.throw()
323
324 expect ()-> SimplyBind(0).of('s')
325 .to.throw()
326
327 expect ()-> SimplyBind(0).of(0)
328 .to.throw()
329
330 expect ()-> SimplyBind(0).of(true)
331 .to.throw()
332
333 expect ()-> SimplyBind(0).of(null)
334 .to.throw()
335
336 expect ()-> SimplyBind(0).of(undefined)
337 .to.throw()
338
339
340
341
342 test "Will extract and use the subject bound object from the instance if passed a binding instance", ()->
343 bound = SimplyBind('prop3').of(objectA).to('prop3').of(objectB)
344
345 bound.set 'standard'
346 expect(objectA.prop3).to.equal 'standard'
347 expect(objectB.prop3).to.equal 'standard'
348
349 bound2 = SimplyBind('prop4').of(bound).to('prop4').of(objectB)
350
351 bound2.set 'ofBounded'
352 expect(objectA.prop4).to.equal 'ofBounded'
353 expect(objectB.prop4).to.equal 'ofBounded'
354
355
356
357 suite ".ofEvent() method", ()->
358 test "Can only accept string values", ()->
359 expect ()-> SimplyBind(0).ofEvent('s')
360 .not.to.throw()
361
362 expect ()-> SimplyBind(0).of(objectA).toEvent('s')
363 .not.to.throw()
364
365 expect ()-> SimplyBind(0).ofEvent([])
366 .to.throw()
367
368 expect ()-> SimplyBind(0).ofEvent(()->)
369 .to.throw()
370
371 expect ()-> SimplyBind(0).ofEvent({})
372 .to.throw()
373
374 expect ()-> SimplyBind(0).ofEvent(new Date())
375 .to.throw()
376
377 if isBrowser
378 expect ()-> SimplyBind(0).ofEvent($('<div />')[0])
379 .to.throw()
380
381 if window.Map and window.Set and window.Symbol
382 expect ()-> SimplyBind(0).ofEvent(new Map())
383 .to.throw()
384
385 expect ()-> SimplyBind(0).ofEvent(new WeakMap())
386 .to.throw()
387
388 expect ()-> SimplyBind(0).ofEvent(new Set())
389 .to.throw()
390
391 expect ()-> SimplyBind(0).ofEvent(new WeakSet())
392 .to.throw()
393
394 expect ()-> SimplyBind(0).ofEvent(Symbol(0))
395 .to.throw()
396
397 expect ()-> SimplyBind(0).ofEvent(SimplyBind(0).ofEvent('s'))
398 .to.throw()
399
400 expect ()-> SimplyBind(0).ofEvent(0)
401 .to.throw()
402
403 expect ()-> SimplyBind(0).ofEvent(true)
404 .to.throw()
405
406 expect ()-> SimplyBind(0).ofEvent(null)
407 .to.throw()
408
409 expect ()-> SimplyBind(0).ofEvent(undefined)
410 .to.throw()
411
412
413
414 suite ".transform/condition/All/Self() methods", ()->
415 test "Can only accept function values", ()->
416 window.origLog = console.warn
417 console.warn = chai.spy()
418 timesCalled = 0
419
420 SimplyBind(0).of({}).to(()->).transform(()->)
421 expect(console.warn).to.have.been.called.exactly(timesCalled += 0)
422
423 SimplyBind(0).of({}).to(()->).transformAll(()->)
424 expect(console.warn).to.have.been.called.exactly(timesCalled += 0)
425
426 SimplyBind(0).of({}).to(()->).condition(()->)
427 expect(console.warn).to.have.been.called.exactly(timesCalled += 0)
428
429 SimplyBind(0).of({}).to(()->).conditionAll(()->)
430 expect(console.warn).to.have.been.called.exactly(timesCalled += 0)
431
432 SimplyBind(0).of({}).transformSelf(()->)
433 expect(console.warn).to.have.been.called.exactly(timesCalled += 0)
434
435 SimplyBind(0).of({}).to(()->).transform([])
436 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
437
438 SimplyBind(0).of({}).to(()->).transformAll({})
439 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
440
441 SimplyBind(0).of({}).to(()->).conditionAll(new Date())
442 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
443
444 if isBrowser
445 SimplyBind(0).of({}).to(()->).condition($('<div />')[0])
446 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
447
448 if window.Map and window.Set and window.Symbol
449 SimplyBind(0).of({}).to(()->).transform(new Map())
450 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
451
452 SimplyBind(0).of({}).to(()->).transformAll(new WeakMap())
453 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
454
455 SimplyBind(0).of({}).to(()->).condition(new Set())
456 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
457
458 SimplyBind(0).of({}).to(()->).conditionAll(new WeakSet())
459 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
460
461 SimplyBind(0).of({}).transformSelf(Symbol(0))
462 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
463
464 SimplyBind(0).of({}).to(()->).transform(SimplyBind('prop1').of(objectA))
465 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
466
467 SimplyBind(0).of({}).to(()->).transformAll('s')
468 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
469
470 SimplyBind(0).of({}).to(()->).condition(0)
471 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
472
473 SimplyBind(0).of({}).to(()->).conditionAll(true)
474 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
475
476 SimplyBind(0).of({}).transformSelf(null)
477 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
478
479 SimplyBind(0).of({}).transformSelf(undefined)
480 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
481
482 console.warn = origLog
483
484
485
486 suite ".throttle()", ()->
487 test "Will only accept a truthy number-type argument or a boolean false", ()->
488 binding = SimplyBind('prop').of(objectA)._
489 expect(binding.throttleRate or binding.thR).to.be.undefined
490
491 SimplyBind('prop').of(objectA).throttle(200)
492 expect(binding.throttleRate or binding.thR).to.equal 200
493
494 SimplyBind('prop').of(objectA).throttle(false)
495 expect(binding.throttleRate or binding.thR).to.equal.undefined
496
497 SimplyBind('prop').of(objectA).throttle(0)
498 expect(binding.throttleRate or binding.thR).to.equal.undefined
499
500 SimplyBind('prop').of(objectA).throttle('0')
501 expect(binding.throttleRate or binding.thR).to.equal.undefined
502
503 SimplyBind('prop').of(objectA).throttle(NaN)
504 expect(binding.throttleRate or binding.thR).to.equal.undefined
505
506 SimplyBind('prop').of(objectA).throttle({})
507 expect(binding.throttleRate or binding.thR).to.equal.undefined
508
509 SimplyBind('prop').of(objectA).throttle([])
510 expect(binding.throttleRate or binding.thR).to.equal.undefined
511
512 SimplyBind('prop').of(objectA).throttle(()->)
513 expect(binding.throttleRate or binding.thR).to.equal.undefined
514
515 if window.Map and window.Set and window.Symbol
516 SimplyBind('prop').of(objectA).throttle(new Map())
517 expect(binding.throttleRate or binding.thR).to.equal.undefined
518
519 SimplyBind('prop').of(objectA).throttle(new WeakMap())
520 expect(binding.throttleRate or binding.thR).to.equal.undefined
521
522 SimplyBind('prop').of(objectA).throttle(new Set())
523 expect(binding.throttleRate or binding.thR).to.equal.undefined
524
525 SimplyBind('prop').of(objectA).throttle(new WeakSet())
526 expect(binding.throttleRate or binding.thR).to.equal.undefined
527
528 SimplyBind('prop').of(objectA).throttle(Symbol(9))
529 expect(binding.throttleRate or binding.thR).to.equal.undefined
530
531
532 restartSandbox()
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551 suite "Errors & Warnings", ()->
552 suiteSetup(restartSandbox)
553
554 test "Warning when binding a property of a jQuery object containing multiple els", ()-> if not isBrowser then @skip() else
555 window.origLog = console.warn
556 console.warn = chai.spy()
557
558 SimplyBind('value').of $('input')
559 expect(console.warn).to.have.been.called()
560
561 console.warn = origLog
562
563
564 test "Warning when binding a property of a jQuery/NodeList/HTMLCollection object containing zero els", ()-> if not isBrowser then @skip() else
565 expect ()-> SimplyBind('value').of $('nonexistent')
566 .to.throw()
567
568 expect ()-> SimplyBind('value').of document.querySelectorAll('nonexistent')
569 .to.throw()
570
571 expect ()-> SimplyBind('value').of document.getElementsByTagName('nonexistent')
572 .to.throw()
573
574
575 test "Warning when binding a property of a jQuery object containing zero els", ()-> if not isBrowser then @skip() else
576 expect ()-> SimplyBind('value').of $('input[type="nonexistent"]')
577 .to.throw()
578
579
580 test "Warning when binding a property of a NodeList/HTMLCollection containing multiple els", ()-> if not isBrowser then @skip() else
581 origLog = console.warn
582 console.warn = chai.spy()
583
584 SimplyBind('value').of document.querySelectorAll('input')
585 expect(console.warn).to.have.been.called()
586
587 console.warn = origLog
588
589
590 test "No Warning when binding 'checked' of a jQuery object containing multiple radio/checkbox inputs", ()-> if not isBrowser then @skip() else
591 origLog = console.warn
592 console.warn = chai.spy()
593
594 SimplyBind('checked').of $radioFields
595 expect(console.warn).not.to.have.been.called()
596
597 SimplyBind('value').of $radioFields
598 expect(console.warn).to.have.been.called.exactly(1)
599
600 console.warn = origLog
601
602
603 test "No Warning when binding 'checked' of a NodeList/HTMLCollection/Array containing multiple radio/checkbox inputs", ()-> if not isBrowser then @skip() else
604 origLog = console.warn
605 console.warn = chai.spy()
606
607 SimplyBind('checked').of $radioFields.toArray()
608 expect(console.warn).not.to.have.been.called()
609
610 SimplyBind('checked').of radioFields
611 expect(console.warn).not.to.have.been.called()
612
613 SimplyBind('value').of $radioFields.toArray()
614 SimplyBind('value').of radioFields
615 expect(console.warn).to.have.been.called.exactly(2)
616
617 console.warn = origLog
618
619
620 test "Warning when binding a property of a NodeList/HTMLCollection/Array/jQuery containing elements with mixed types", ()-> if not isBrowser then @skip() else
621 origLog = console.warn
622 console.warn = chai.spy()
623
624 SimplyBind('checked').of $radioFields.add($checkboxFields).toArray()
625 expect(console.warn).to.have.been.called.exactly(1)
626
627 SimplyBind('checked').of $radioFields.add($checkboxFields)
628 expect(console.warn).to.have.been.called.exactly(2)
629
630 SimplyBind('checked').of document.querySelectorAll 'input'
631 expect(console.warn).to.have.been.called.exactly(3)
632
633 console.warn = origLog
634
635
636 test "Warning when creating a binding and passing a non-function object to .transform/.condition/All", ()->
637 binding = SimplyBind('prop1').of(objectA).to('prop1').of(objectB)
638 origLog = console.warn
639 console.warn = chai.spy()
640 timesCalled = 0
641
642 binding.transform []
643 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
644
645 binding.transformAll []
646 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
647
648 binding.transform {}
649 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
650
651 binding.transform new Date()
652 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
653
654 if isBrowser
655 binding.transform $('<div />')[0]
656 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
657
658 if window.Map and window.Set and window.Symbol
659 binding.transform new Map()
660 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
661
662 binding.transform new WeakMap()
663 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
664
665 binding.transform new Set()
666 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
667
668 binding.transform new WeakSet()
669 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
670
671 binding.transform Symbol(0)
672 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
673
674 binding.transform 's'
675 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
676
677 binding.transform 0
678 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
679
680 binding.transform true
681 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
682
683 binding.transform null
684 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
685
686 binding.transform undefined
687 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
688
689 binding.condition []
690 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
691
692 binding.conditionAll []
693 expect(console.warn).to.have.been.called.exactly(timesCalled += 1)
694
695
696 console.warn = origLog
697 restartSandbox()
698
699
700 test "Warning when passed a non-number argument selector to an event binding", ()->
701 origLog = console.warn
702 console.warn = chai.spy()
703
704 SimplyBind('value').ofEvent 'someEvent'
705 SimplyBind('15').ofEvent 'someEvent'
706 SimplyBind(' 1aos95').ofEvent 'someEvent'
707
708 expect(console.warn).to.have.been.called.exactly(1)
709 console.warn = origLog
710
711
712
713 test "No warnings should be thrown when SimplyBind.options.silent is on", ()->
714 SimplyBind.setOption 'silent', true
715
716 origLog = console.warn
717 console.warn = chai.spy()
718
719 SimplyBind('value').ofEvent('someEvent')
720 SimplyBind('prop3').of(objectA).to('prop3').of(objectB).transform []
721
722 expect(console.warn).not.to.have.been.called()
723
724 console.warn = origLog
725 SimplyBind.setOption 'silent', false
726
727
728 test "Errors should still be thrown when SimplyBind.options.silent is on", ()->
729 SimplyBind.setOption 'silent', true
730
731 expect ()-> SimplyBind ''
732 .to.throw()
733
734 SimplyBind.setOption 'silent', false
735
736
737 test "No errors should be thrown when scanning for placeholders on a non-string object", ()->
738 dispatcher = 'prop':'value'
739 receiver = 'prop':null
740 expect(()->
741 SimplyBind('prop').of(dispatcher)
742 .to('prop.placeholder').of(receiver)
743 ).not.to.throw()
744
745 expect(receiver.prop).not.to.equal('value')
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764 suite "Internal Behavior", ()->
765 suiteSetup(restartSandbox)
766
767
768
769
770 test "A binding should have the correct public properies available", ()->
771 binding = SimplyBind('prop1').of(objectA).to('prop2').of(objectB)
772 expect(binding.ID).not.to.be.undefined
773 expect(binding.value).not.to.be.undefined
774 expect(binding.original).not.to.be.undefined
775 expect(binding.dependents).not.to.be.undefined
776 expect(binding._).not.to.be.undefined
777 restartSandbox()
778
779
780
781
782 test "Bindings to a nonexistent object prop should not create instantiate the object prop at first", ()->
783 expect(objectA.prop20).to.be.undefined
784 expect(objectB.prop20).to.be.undefined
785
786 binding = SimplyBind('prop20').of(objectA).to('prop20').of(objectB)
787 expect(objectA.prop20).to.be.undefined
788 expect(objectB.prop20).to.be.undefined
789
790 binding.set 'new value'
791 expect(objectA.prop20).to.equal 'new value'
792 expect(objectB.prop20).to.equal 'new value'
793 restartSandbox()
794
795
796
797
798 test "The binding.value property and binding.get() method should return the same value", ()->
799 tempObject =
800 'a': 'with'
801 'b': 'text {{verb}} a placeholder'
802
803 SimplyBind('a').of(tempObject).to('b.verb').of tempObject
804
805
806 bound = SimplyBind('b.verb').of(tempObject)
807 expect(tempObject.b).to.equal 'text with a placeholder'
808 expect(bound.value).to.equal 'text with a placeholder'
809 expect(bound.get()).to.equal 'with'
810
811
812 tempObject.a = 'without'
813 expect(tempObject.b).to.equal 'text without a placeholder'
814 expect(bound.value).to.equal 'text without a placeholder'
815 expect(bound.get()).to.equal 'without'
816
817
818
819
820 suite "Method availablity in different stages", ()->
821 test "Stage 0 (Selection Stage)", ()->
822 binding = ()-> SimplyBind('1')
823 expect(()-> binding().of objectA).not.to.throw()
824 expect(()-> binding().ofEvent objectA).to.throw()
825 expect(()-> binding().to 'prop2').to.throw()
826 expect(()-> binding().toEvent '/local').to.throw()
827 expect(()-> binding().and 'prop2').to.throw()
828 expect(()-> binding().set 'value').to.throw()
829 expect(()-> binding().get()).to.throw()
830 expect(()-> binding().transform ()->).to.throw()
831 expect(()-> binding().transformAll ()->).to.throw()
832 expect(()-> binding().transformSelf ()->).to.throw()
833 expect(()-> binding().condition ()->).to.throw()
834 expect(()-> binding().conditionAll ()->).to.throw()
835 expect(()-> binding().bothWays()).to.throw()
836 expect(()-> binding().stopPolling()).to.throw()
837 expect(()-> binding().pollEvery(1000).stopPolling()).to.throw()
838 expect(()-> binding().unBind()).to.throw()
839 expect(()-> binding().chainTo ()->).to.throw()
840 expect(()-> binding().updateDepsOnEvent 'a').to.throw()
841 expect(()-> binding().removeEvent 'a').to.throw()
842 expect(()-> binding().throttle 5).to.throw()
843
844 binding = ()-> SimplyBind('1').ofEvent 'click'
845 expect(()-> binding().of objectA).not.to.throw()
846 expect(()-> binding().ofEvent objectA).to.throw()
847 expect(()-> binding().to 'prop2').to.throw()
848 expect(()-> binding().toEvent '/local').to.throw()
849 expect(()-> binding().and 'prop2').to.throw()
850 expect(()-> binding().set 'value').to.throw()
851 expect(()-> binding().get()).to.throw()
852 expect(()-> binding().transform ()->).to.throw()
853 expect(()-> binding().transformAll ()->).to.throw()
854 expect(()-> binding().transformSelf ()->).to.throw()
855 expect(()-> binding().condition ()->).to.throw()
856 expect(()-> binding().conditionAll ()->).to.throw()
857 expect(()-> binding().bothWays()).to.throw()
858 expect(()-> binding().stopPolling()).to.throw()
859 expect(()-> binding().pollEvery(1000).stopPolling()).to.throw()
860 expect(()-> binding().unBind()).to.throw()
861 expect(()-> binding().chainTo ()->).to.throw()
862 expect(()-> binding().updateDepsOnEvent 'a').to.throw()
863 expect(()-> binding().removeEvent 'a').to.throw()
864 expect(()-> binding().throttle 5).to.throw()
865
866
867
868
869
870 test "Stage 1 (Indication Stage)", ()->
871 binding = ()-> SimplyBind('1').of objectA
872 expect(()-> binding().of objectA).to.throw()
873 expect(()-> binding().ofEvent objectA).to.throw()
874 expect(()-> binding().to 'prop2').not.to.throw()
875 expect(()-> binding().toEvent '/local').not.to.throw()
876 expect(()-> binding().and 'prop2').to.throw()
877 expect(()-> binding().set 'value').not.to.throw()
878 expect(()-> binding().get()).not.to.throw()
879 expect(()-> binding().transform ()->).to.throw()
880 expect(()-> binding().transformAll ()->).to.throw()
881 expect(()-> binding().transformSelf ()->).not.to.throw()
882 expect(()-> binding().condition ()->).to.throw()
883 expect(()-> binding().conditionAll ()->).to.throw()
884 expect(()-> binding().bothWays()).to.throw()
885 expect(()-> binding().stopPolling()).to.throw()
886 expect(()-> binding().pollEvery(1000).stopPolling()).to.throw()
887 expect(()-> binding().unBind()).to.throw()
888 expect(()-> binding().chainTo ()->).to.throw()
889 expect(()-> binding().updateDepsOnEvent 'a').to.throw()
890 expect(()-> binding().removeEvent 'a').to.throw()
891 expect(()-> binding().throttle 5).not.to.throw()
892
893 binding = ()-> SimplyBind('1').ofEvent('click').of eventEmitterA
894 expect(()-> binding().of objectA).to.throw()
895 expect(()-> binding().ofEvent objectA).to.throw()
896 expect(()-> binding().to 'prop2').not.to.throw()
897 expect(()-> binding().toEvent '/local').not.to.throw()
898 expect(()-> binding().and 'prop2').to.throw()
899 expect(()-> binding().set 'value').not.to.throw()
900 expect(()-> binding().get()).not.to.throw()
901 expect(()-> binding().transform ()->).to.throw()
902 expect(()-> binding().transformAll ()->).to.throw()
903 expect(()-> binding().transformSelf ()->).not.to.throw()
904 expect(()-> binding().condition ()->).to.throw()
905 expect(()-> binding().conditionAll ()->).to.throw()
906 expect(()-> binding().bothWays()).to.throw()
907 expect(()-> binding().stopPolling()).to.throw()
908 expect(()-> binding().pollEvery(1000).stopPolling()).to.throw()
909 expect(()-> binding().unBind()).to.throw()
910 expect(()-> binding().chainTo ()->).to.throw()
911 expect(()-> binding().updateDepsOnEvent 'a').to.throw()
912 expect(()-> binding().removeEvent 'a').to.throw()
913 expect(()-> binding().throttle 5).not.to.throw()
914
915
916
917
918
919 test "Stage 2 (Binding Selection Stage)", ()->
920 binding = ()-> SimplyBind('1').of(objectA).to 'prop1'
921 expect(()-> binding().of objectA).not.to.throw()
922 expect(()-> binding().ofEvent objectA).to.throw()
923 expect(()-> binding().to 'prop2').to.throw()
924 expect(()-> binding().toEvent '/local').to.throw()
925 expect(()-> binding().and 'prop2').to.throw()
926 expect(()-> binding().set 'value').to.throw()
927 expect(()-> binding().get()).to.throw()
928 expect(()-> binding().transform ()->).to.throw()
929 expect(()-> binding().transformAll ()->).to.throw()
930 expect(()-> binding().transformSelf ()->).to.throw()
931 expect(()-> binding().condition ()->).to.throw()
932 expect(()-> binding().conditionAll ()->).to.throw()
933 expect(()-> binding().bothWays()).to.throw()
934 expect(()-> binding().stopPolling()).to.throw()
935 expect(()-> binding().pollEvery(1000).stopPolling()).to.throw()
936 expect(()-> binding().unBind()).to.throw()
937 expect(()-> binding().chainTo ()->).to.throw()
938 expect(()-> binding().updateDepsOnEvent 'a').to.throw()
939 expect(()-> binding().removeEvent 'a').to.throw()
940 expect(()-> binding().throttle 5).to.throw()
941
942
943 binding = ()-> SimplyBind('1').of(objectA).to('prop:prop1').of(objectB).and 'prop2'
944 expect(()-> binding().of objectA).not.to.throw()
945 expect(()-> binding().ofEvent objectA).to.throw()
946 expect(()-> binding().to 'prop2').to.throw()
947 expect(()-> binding().toEvent '/local').to.throw()
948 expect(()-> binding().and 'prop2').to.throw()
949 expect(()-> binding().set 'value').to.throw()
950 expect(()-> binding().get()).to.throw()
951 expect(()-> binding().transform ()->).to.throw()
952 expect(()-> binding().transformAll ()->).to.throw()
953 expect(()-> binding().transformSelf ()->).to.throw()
954 expect(()-> binding().condition ()->).to.throw()
955 expect(()-> binding().conditionAll ()->).to.throw()
956 expect(()-> binding().bothWays()).to.throw()
957 expect(()-> binding().stopPolling()).to.throw()
958 expect(()-> binding().pollEvery(1000).stopPolling()).to.throw()
959 expect(()-> binding().unBind()).to.throw()
960 expect(()-> binding().chainTo ()->).to.throw()
961 expect(()-> binding().updateDepsOnEvent 'a').to.throw()
962 expect(()-> binding().removeEvent 'a').to.throw()
963 expect(()-> binding().throttle 5).to.throw()
964
965
966
967
968
969 test "Stage 3 (Binding Complete Stage)", ()->
970 binding = ()-> SimplyBind('1').of(objectA).to('prop1').of objectB
971 expect(()-> binding().of objectA).to.throw()
972 expect(()-> binding().ofEvent objectA).to.throw()
973 expect(()-> binding().to 'prop2').to.throw()
974 expect(()-> binding().toEvent '/local').to.throw()
975 expect(()-> binding().and 'prop2').not.to.throw()
976 expect(()-> binding().set 'value').not.to.throw()
977 expect(()-> binding().get()).not.to.throw()
978 expect(()-> binding().transform ()->).not.to.throw()
979 expect(()-> binding().transformAll ()->).not.to.throw()
980 expect(()-> binding().transformSelf ()->).to.throw()
981 expect(()-> binding().condition ()->).not.to.throw()
982 expect(()-> binding().conditionAll ()->).not.to.throw()
983 expect(()-> binding().bothWays()).not.to.throw()
984 expect(()-> binding().stopPolling()).not.to.throw()
985 expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw()
986 expect(()-> binding().unBind()).not.to.throw()
987 expect(()-> binding().chainTo ()->).not.to.throw()
988 expect(()-> binding().updateDepsOnEvent 'a').not.to.throw()
989 expect(()-> binding().removeEvent 'a').not.to.throw()
990 expect(()-> binding().throttle 5).not.to.throw()
991
992 binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).and('prop2').of objectB
993 expect(()-> binding().of objectA).to.throw()
994 expect(()-> binding().ofEvent objectA).to.throw()
995 expect(()-> binding().to 'prop2').to.throw()
996 expect(()-> binding().toEvent '/local').to.throw()
997 expect(()-> binding().and 'prop2').not.to.throw()
998 expect(()-> binding().set 'value').not.to.throw()
999 expect(()-> binding().get()).not.to.throw()
1000 expect(()-> binding().transform ()->).not.to.throw()
1001 expect(()-> binding().transformAll ()->).not.to.throw()
1002 expect(()-> binding().transformSelf ()->).to.throw()
1003 expect(()-> binding().condition ()->).not.to.throw()
1004 expect(()-> binding().conditionAll ()->).not.to.throw()
1005 expect(()-> binding().bothWays()).not.to.throw()
1006 expect(()-> binding().stopPolling()).not.to.throw()
1007 expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw()
1008 expect(()-> binding().unBind()).not.to.throw()
1009 expect(()-> binding().chainTo ()->).not.to.throw()
1010 expect(()-> binding().updateDepsOnEvent 'a').not.to.throw()
1011 expect(()-> binding().removeEvent 'a').not.to.throw()
1012 expect(()-> binding().throttle 5).not.to.throw()
1013
1014 binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).bothWays()
1015 expect(()-> binding().of objectA).to.throw()
1016 expect(()-> binding().ofEvent objectA).to.throw()
1017 expect(()-> binding().to 'prop2').to.throw()
1018 expect(()-> binding().toEvent '/local').to.throw()
1019 expect(()-> binding().and 'prop2').not.to.throw()
1020 expect(()-> binding().set 'value').not.to.throw()
1021 expect(()-> binding().get()).not.to.throw()
1022 expect(()-> binding().transform ()->).not.to.throw()
1023 expect(()-> binding().transformAll ()->).not.to.throw()
1024 expect(()-> binding().transformSelf ()->).to.throw()
1025 expect(()-> binding().condition ()->).not.to.throw()
1026 expect(()-> binding().conditionAll ()->).not.to.throw()
1027 expect(()-> binding().bothWays()).not.to.throw()
1028 expect(()-> binding().stopPolling()).not.to.throw()
1029 expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw()
1030 expect(()-> binding().unBind()).not.to.throw()
1031 expect(()-> binding().chainTo ()->).not.to.throw()
1032 expect(()-> binding().updateDepsOnEvent 'a').not.to.throw()
1033 expect(()-> binding().removeEvent 'a').not.to.throw()
1034 expect(()-> binding().throttle 5).not.to.throw()
1035
1036 binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).transform ()->
1037 expect(()-> binding().of objectA).to.throw()
1038 expect(()-> binding().ofEvent objectA).to.throw()
1039 expect(()-> binding().to 'prop2').to.throw()
1040 expect(()-> binding().toEvent '/local').to.throw()
1041 expect(()-> binding().and 'prop2').not.to.throw()
1042 expect(()-> binding().set 'value').not.to.throw()
1043 expect(()-> binding().get()).not.to.throw()
1044 expect(()-> binding().transform ()->).to.throw()
1045 expect(()-> binding().transformAll ()->).to.throw()
1046 expect(()-> binding().transformSelf ()->).to.throw()
1047 expect(()-> binding().condition ()->).not.to.throw()
1048 expect(()-> binding().conditionAll ()->).not.to.throw()
1049 expect(()-> binding().bothWays()).not.to.throw()
1050 expect(()-> binding().stopPolling()).not.to.throw()
1051 expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw()
1052 expect(()-> binding().unBind()).not.to.throw()
1053 expect(()-> binding().chainTo ()->).not.to.throw()
1054 expect(()-> binding().updateDepsOnEvent 'a').not.to.throw()
1055 expect(()-> binding().removeEvent 'a').not.to.throw()
1056 expect(()-> binding().throttle 5).not.to.throw()
1057
1058 binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).transformAll ()->
1059 expect(()-> binding().of objectA).to.throw()
1060 expect(()-> binding().ofEvent objectA).to.throw()
1061 expect(()-> binding().to 'prop2').to.throw()
1062 expect(()-> binding().toEvent '/local').to.throw()
1063 expect(()-> binding().and 'prop2').to.throw()
1064 expect(()-> binding().set 'value').not.to.throw()
1065 expect(()-> binding().get()).not.to.throw()
1066 expect(()-> binding().transform ()->).to.throw()
1067 expect(()-> binding().transformAll ()->).to.throw()
1068 expect(()-> binding().transformSelf ()->).to.throw()
1069 expect(()-> binding().condition ()->).not.to.throw()
1070 expect(()-> binding().conditionAll ()->).not.to.throw()
1071 expect(()-> binding().bothWays()).to.throw()
1072 expect(()-> binding().stopPolling()).not.to.throw()
1073 expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw()
1074 expect(()-> binding().unBind()).not.to.throw()
1075 expect(()-> binding().chainTo ()->).not.to.throw()
1076 expect(()-> binding().updateDepsOnEvent 'a').not.to.throw()
1077 expect(()-> binding().removeEvent 'a').not.to.throw()
1078 expect(()-> binding().throttle 5).not.to.throw()
1079
1080 binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).condition ()->
1081 expect(()-> binding().of objectA).to.throw()
1082 expect(()-> binding().ofEvent objectA).to.throw()
1083 expect(()-> binding().to 'prop2').to.throw()
1084 expect(()-> binding().toEvent '/local').to.throw()
1085 expect(()-> binding().and 'prop2').not.to.throw()
1086 expect(()-> binding().set 'value').not.to.throw()
1087 expect(()-> binding().get()).not.to.throw()
1088 expect(()-> binding().transform ()->).not.to.throw()
1089 expect(()-> binding().transformAll ()->).not.to.throw()
1090 expect(()-> binding().transformSelf ()->).to.throw()
1091 expect(()-> binding().condition ()->).not.to.throw()
1092 expect(()-> binding().conditionAll ()->).not.to.throw()
1093 expect(()-> binding().bothWays()).not.to.throw()
1094 expect(()-> binding().stopPolling()).not.to.throw()
1095 expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw()
1096 expect(()-> binding().unBind()).not.to.throw()
1097 expect(()-> binding().chainTo ()->).not.to.throw()
1098 expect(()-> binding().updateDepsOnEvent 'a').not.to.throw()
1099 expect(()-> binding().removeEvent 'a').not.to.throw()
1100 expect(()-> binding().throttle 5).not.to.throw()
1101
1102 binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).conditionAll ()->
1103 expect(()-> binding().of objectA).to.throw()
1104 expect(()-> binding().ofEvent objectA).to.throw()
1105 expect(()-> binding().to 'prop2').to.throw()
1106 expect(()-> binding().toEvent '/local').to.throw()
1107 expect(()-> binding().and 'prop2').not.to.throw()
1108 expect(()-> binding().set 'value').not.to.throw()
1109 expect(()-> binding().get()).not.to.throw()
1110 expect(()-> binding().transform ()->).not.to.throw()
1111 expect(()-> binding().transformAll ()->).not.to.throw()
1112 expect(()-> binding().transformSelf ()->).to.throw()
1113 expect(()-> binding().condition ()->).not.to.throw()
1114 expect(()-> binding().conditionAll ()->).not.to.throw()
1115 expect(()-> binding().bothWays()).not.to.throw()
1116 expect(()-> binding().stopPolling()).not.to.throw()
1117 expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw()
1118 expect(()-> binding().unBind()).not.to.throw()
1119 expect(()-> binding().chainTo ()->).not.to.throw()
1120 expect(()-> binding().updateDepsOnEvent 'a').not.to.throw()
1121 expect(()-> binding().removeEvent 'a').not.to.throw()
1122 expect(()-> binding().throttle 5).not.to.throw()
1123
1124 binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).transform(()->).and ()->
1125 expect(()-> binding().of objectA).to.throw()
1126 expect(()-> binding().ofEvent objectA).to.throw()
1127 expect(()-> binding().to 'prop2').to.throw()
1128 expect(()-> binding().toEvent '/local').to.throw()
1129 expect(()-> binding().and 'prop2').not.to.throw()
1130 expect(()-> binding().set 'value').not.to.throw()
1131 expect(()-> binding().get()).not.to.throw()
1132 expect(()-> binding().transform ()->).not.to.throw()
1133 expect(()-> binding().transformAll ()->).not.to.throw()
1134 expect(()-> binding().transformSelf ()->).to.throw()
1135 expect(()-> binding().condition ()->).not.to.throw()
1136 expect(()-> binding().conditionAll ()->).not.to.throw()
1137 expect(()-> binding().bothWays()).not.to.throw()
1138 expect(()-> binding().stopPolling()).not.to.throw()
1139 expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw()
1140 expect(()-> binding().unBind()).not.to.throw()
1141 expect(()-> binding().chainTo ()->).not.to.throw()
1142 expect(()-> binding().updateDepsOnEvent 'a').not.to.throw()
1143 expect(()-> binding().removeEvent 'a').not.to.throw()
1144 expect(()-> binding().throttle 5).not.to.throw()
1145
1146 binding = ()-> SimplyBind('1').of(objectA).to('prop1').of(objectB).transform(()->).and(()->).chainTo ()->
1147 expect(()-> binding().of objectA).to.throw()
1148 expect(()-> binding().ofEvent objectA).to.throw()
1149 expect(()-> binding().to 'prop2').to.throw()
1150 expect(()-> binding().toEvent '/local').to.throw()
1151 expect(()-> binding().and 'prop2').not.to.throw()
1152 expect(()-> binding().set 'value').not.to.throw()
1153 expect(()-> binding().get()).not.to.throw()
1154 expect(()-> binding().transform ()->).not.to.throw()
1155 expect(()-> binding().transformAll ()->).not.to.throw()
1156 expect(()-> binding().transformSelf ()->).to.throw()
1157 expect(()-> binding().condition ()->).not.to.throw()
1158 expect(()-> binding().conditionAll ()->).not.to.throw()
1159 expect(()-> binding().bothWays()).not.to.throw()
1160 expect(()-> binding().stopPolling()).not.to.throw()
1161 expect(()-> binding().pollEvery(1000).stopPolling()).not.to.throw()
1162 expect(()-> binding().unBind()).not.to.throw()
1163 expect(()-> binding().chainTo ()->).not.to.throw()
1164 expect(()-> binding().updateDepsOnEvent 'a').not.to.throw()
1165 expect(()-> binding().removeEvent 'a').not.to.throw()
1166 expect(()-> binding().throttle 5).not.to.throw()
1167
1168
1169
1170
1171 suite "Cached Bindings", ()->
1172 test "A cached version of the binding should be returned when re-creating an existing one", ()->
1173 # ==== ObjectProp =================================================================================
1174 expect(SimplyBind('prop1').of(objectA).ID)
1175 .to.equal(SimplyBind('prop1').of(objectA).ID)
1176
1177 expect(SimplyBind('prop1').of(objectA).to('prop1').of(objectB)._.deps[0].ID)
1178 .to.equal(SimplyBind('prop1').of(objectB).ID)
1179
1180 if isBrowser
1181 # ==== DOMAttr =================================================================================
1182 expect(SimplyBind('attr:someattr').of(regA).ID)
1183 .to.equal(SimplyBind('attr:someattr').of(regA).ID)
1184
1185 expect(SimplyBind('attr:someattr').of(regA).to('attr:someattr').of(regB)._.deps[0].ID)
1186 .to.equal(SimplyBind('attr:someattr').of(regB).ID)
1187
1188
1189 # ==== DOMValue =================================================================================
1190 expect(SimplyBind('value').of(inputA).ID)
1191 .to.equal(SimplyBind('value').of(inputA).ID)
1192
1193 expect(SimplyBind('value').of(inputA).to('value').of(inputB)._.deps[0].ID)
1194 .to.equal(SimplyBind('value').of(inputB).ID)
1195
1196
1197 # ==== DOMCheckbox Single =================================================================================
1198 expect(SimplyBind('checked').of(checkboxA).ID)
1199 .to.equal(SimplyBind('checked').of(checkboxA).ID)
1200
1201 expect(SimplyBind('checked').of(checkboxA).to('checked').of(checkboxB)._.deps[0].ID)
1202 .to.equal(SimplyBind('checked').of(checkboxB).ID)
1203
1204
1205 # ==== DOMCheckbox =================================================================================
1206 expect(SimplyBind('checked').of($checkboxFields).ID)
1207 .to.equal(SimplyBind('checked').of($checkboxFields).ID)
1208
1209
1210 # ==== DOMRadio =================================================================================
1211 expect(SimplyBind('checked').of($radioFields).ID)
1212 .to.equal(SimplyBind('checked').of($radioFields).ID)
1213
1214
1215 # ==== DOMText =================================================================================
1216 expect(SimplyBind('textContent').of(regA).ID)
1217 .to.equal(SimplyBind('textContent').of(regA).ID)
1218
1219 expect(SimplyBind('textContent').of(regA).to('textContent').of(regB)._.deps[0].ID)
1220 .to.equal(SimplyBind('textContent').of(regB).ID)
1221
1222
1223 # ==== Event =================================================================================
1224 expect(SimplyBind(0).ofEvent('someEvent').of(eventEmitterA).ID)
1225 .to.equal(SimplyBind(0).ofEvent('someEvent').of(eventEmitterA).ID)
1226
1227 expect(SimplyBind(0).ofEvent('someEvent').of(eventEmitterA).toEvent('someEvent').of(eventEmitterB)._.deps[0].ID)
1228 .to.equal(SimplyBind(0).ofEvent('someEvent').of(eventEmitterB).ID)
1229
1230
1231 # ==== Array =================================================================================
1232 expect(SimplyBind(arrayA).ID)
1233 .to.equal(SimplyBind(arrayA).ID)
1234
1235 expect(SimplyBind(arrayA).to(arrayB)._.deps[0].ID)
1236 .to.equal(SimplyBind(arrayB).ID)
1237
1238
1239 # ==== Function =================================================================================
1240 expect(SimplyBind(fnA).ID)
1241 .to.equal(SimplyBind(fnA).ID)
1242
1243 expect(SimplyBind(fnA).to(fnB)._.deps[0].ID)
1244 .to.equal(SimplyBind(fnB).ID)
1245
1246
1247 restartSandbox()
1248
1249
1250
1251 test "When binding an object prop with a placeholder indicator in the selector for an existing binding that didn't have indicators, placeholders will be rescanned", ()->
1252 dispatcher = 'prop':'This is an {{size}} size shirt'
1253 receiver = 'prop':''
1254
1255 SimplyBind('prop').of(dispatcher)
1256 .to('prop').of(receiver)
1257
1258 expect(receiver.prop).to.equal(dispatcher.prop)
1259 expect(dispatcher.prop).to.equal 'This is an {{size}} size shirt'
1260
1261
1262 SimplyBind('prop').of(dispatcher)
1263 .to('prop.size').of(receiver)
1264
1265 dispatcher.prop = 'XXL'
1266 expect(dispatcher.prop).to.equal 'XXL'
1267 expect(receiver.prop).to.equal 'This is an XXL size shirt'
1268
1269
1270
1271 test "When re-binding a binding with placeholder where the global placeholders regEx has changed to a diff regEx from present in the original binding, the binding's regEx shouldn't change", ()->
1272 dispatcher = 'noun':'', 'adjective':''
1273 receiver = 'prop':'This {{noun}} is **adjective**'
1274
1275 SimplyBind('noun').of(dispatcher)
1276 .to('prop.noun').of(receiver)
1277
1278 dispatcher.noun = 'test'
1279 expect(receiver.prop).to.equal 'This test is **adjective**'
1280
1281
1282 SimplyBind.setOption 'placeholder', ['**', '**']
1283
1284 SimplyBind('adjective').of(dispatcher)
1285 .to('prop.adjective').of(receiver)
1286
1287 dispatcher.adjective = 'good'
1288 expect(receiver.prop).to.equal 'This test is **adjective**'
1289
1290 dispatcher.noun = 'suite'
1291 expect(receiver.prop).to.equal 'This suite is **adjective**'
1292
1293 SimplyBind.setOption 'placeholder', ['{{', '}}']
1294
1295
1296
1297 test "When re-binding an object prop that was deleted manually make sure to re-make the property a live one", ()->
1298 objectA.prop = 1
1299 SimplyBind('prop').of(objectA)
1300 .to('prop').of(objectB)
1301
1302 expect(objectB.prop).to.equal(objectA.prop)
1303 objectA.prop = 2
1304 expect(objectB.prop).to.equal(objectA.prop)
1305
1306 delete objectB.prop
1307 objectA.prop = 3
1308 expect(objectB.prop).not.to.equal(objectA.prop)
1309
1310
1311 SimplyBind('prop').of(objectA)
1312 .to('prop').of(objectB)
1313
1314 expect(objectB.prop).to.equal(objectA.prop)
1315
1316 objectA.prop = 4
1317 expect(objectB.prop).to.equal(objectA.prop)
1318
1319 restartSandbox()
1320
1321
1322
1323 test "When binding an index of an array that's brand new and the array is tracked and so are its children, the new index should be made so it also tracked", ()->
1324 invokeCount = 0
1325 SimplyBind(arrayA, 'trackArrayChildren':true).to ()-> invokeCount++
1326
1327 expect(invokeCount).to.equal 1
1328
1329 arrayA[3] = 44
1330 expect(invokeCount).to.equal 2
1331
1332 arrayA[20] = 21
1333 expect(invokeCount).to.equal 2
1334
1335 SimplyBind('18').of(arrayA)
1336 arrayA[18] = 188
1337 expect(invokeCount).to.equal 4
1338
1339 arrayA[18] = 1888
1340 expect(invokeCount).to.equal 5
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379 suite "Data Binding Behavior", ()->
1380 suiteSetup(restartSandbox)
1381 test "Object properties should behave the same way across all object types", ()->
1382 dispatcher =
1383 'standard': objectA
1384 'array': arrayA
1385 'function': ()->
1386 'domDiv': if isBrowser then $('<div />')[0] else {}
1387 'domInput': if isBrowser then $('<input />')[0] else {}
1388 'domNode': if isBrowser then $('<div>text</div>')[0].childNodes[0] else {}
1389 'date': new Date()
1390 receiver =
1391 'standard': objectB
1392 'array': arrayB
1393 'function': ()->
1394 'domDiv': if isBrowser then $('<div />')[0] else {}
1395 'domInput': if isBrowser then $('<input />')[0] else {}
1396 'domNode': if isBrowser then $('<div>text</div>')[0].childNodes[0] else {}
1397 'date': new Date()
1398
1399 for name,object of dispatcher
1400 SimplyBind('prop').of(dispatcher[name]).to('prop').of(receiver[name])
1401 expect(receiver[name].prop).to.be.undefined
1402
1403 SimplyBind('prop').of(dispatcher[name]).set(100)
1404 expect(receiver[name].prop).to.equal(100)
1405
1406 restartSandbox()
1407
1408
1409
1410 test "Object properties should be made into live properties when SimplyBind.options.liveProps is on", ()->
1411 expect(SimplyBind.options.liveProps).to.be.true
1412 invokeCount = 0
1413
1414 SimplyBind('prop1').of(objectA).to ()-> invokeCount++
1415 expect(invokeCount).to.equal(1)
1416
1417 objectA.prop1 = !objectA.prop1
1418 expect(invokeCount).to.equal(2)
1419
1420 SimplyBind('prop1').of(objectA).set !objectA.prop1
1421 expect(invokeCount).to.equal(3)
1422
1423
1424 SimplyBind.setOption 'liveProps', false
1425 invokeCount = 0
1426
1427 SimplyBind('prop1').of(objectB).to ()-> invokeCount++
1428 expect(invokeCount).to.equal(1)
1429
1430 objectB.prop1 = !objectB.prop1
1431 expect(invokeCount).to.equal(1)
1432
1433 SimplyBind('prop1').of(objectB).set !objectB.prop1
1434 expect(invokeCount).to.equal(2)
1435
1436
1437
1438 SimplyBind.setOption 'liveProps', true
1439 restartSandbox()
1440
1441
1442
1443 test "Object properties inherited from the prototype should be made into live properties when SimplyBind.options.mutateInherited is on", ()->
1444 testArray = []
1445
1446 SimplyBind.setOption 'mutateInherited', true
1447 Array::inherited = 'I was inherited'
1448
1449 SimplyBind('inherited').of(testArray).to('prop1').of objectB
1450
1451 testArray.inherited = 'inherited live prop change :)'
1452 expect(objectB.prop1).to.equal 'inherited live prop change :)'
1453 delete Array::inherited
1454
1455
1456
1457 SimplyBind.setOption 'mutateInherited', false
1458 Array::inherited2 = 'I was inherited'
1459
1460 SimplyBind('inherited2').of(testArray).to('prop2').of objectB
1461
1462 testArray.inherited2 = 'inherited prop shouldn\'t change'
1463 expect(objectB.prop2).not.to.equal 'inherited prop shouldn\'t change'
1464 delete Array::inherited2
1465
1466
1467 SimplyBind.setOption 'mutateInherited', false
1468 restartSandbox()
1469
1470
1471
1472 test "The property's original setter (if exists) should be invoked during a live property update", ()->
1473 prop7 = 'hey!'
1474 invokeCountGet = 0
1475 invokeCountSet = 0
1476
1477 Object.defineProperty objectA, 'prop7',
1478 enumerable: true
1479 configurable: true
1480 get: ()->
1481 invokeCountGet++
1482 prop7
1483 set: (val)->
1484 invokeCountSet++
1485 prop7 = val
1486
1487
1488
1489 expect(objectA.prop7).to.equal 'hey!'
1490 expect(invokeCountGet).to.equal 1
1491 expect(invokeCountSet).to.equal 0
1492
1493 SimplyBind('prop7').of(objectA).to('prop3').of objectB
1494
1495 objectA.prop7 = 'hello!'
1496 expect(invokeCountGet).to.equal 2
1497 expect(invokeCountSet).to.equal 1
1498 expect(objectA.prop7).to.equal 'hello!'
1499
1500 objectA.prop7 = 'hi!'
1501 expect(invokeCountGet).to.equal 2
1502 expect(invokeCountSet).to.equal 2
1503 restartSandbox()
1504
1505
1506
1507 test "Bindings will update dependents even if their value is falsy", ()->
1508 binding = SimplyBind('prop1').of(objectA).to('prop1').of(objectB)
1509
1510 binding.set ''
1511 expect(objectB.prop1).to.equal ''
1512
1513 binding.set 0
1514 expect(objectB.prop1).to.equal 0
1515
1516 binding.set false
1517 expect(objectB.prop1).to.equal false
1518
1519 binding.set undefined
1520 expect(objectB.prop1).to.equal undefined
1521
1522 binding.set null
1523 expect(objectB.prop1).to.equal null
1524
1525 restartSandbox()
1526
1527
1528
1529 test "Update dependents of a binding even if it's unchanged when SimplyBind.options.updateEvenIfSame is on or if the binding type is Func or Event", ()->
1530 invokeCount = object:0, func:0, event:0
1531 objectA.prop6 = 'start'
1532 objectB.prop6 = ''
1533
1534 eventEmitterA.on 'alwaysEmit', ()-> invokeCount.event++
1535
1536 bindings =
1537 'object': SimplyBind('prop6').of(objectA).to('prop6').of(objectB).transform (value)-> invokeCount.object++; return value
1538 'func': SimplyBind('prop7', updateEvenIfSame:true).of(objectA).to(()-> invokeCount.func++)
1539 'event': SimplyBind('prop8', updateEvenIfSame:true).of(objectA).toEvent('alwaysEmit').of(eventEmitterA)
1540
1541 expect(objectB.prop6).to.equal 'start'
1542 expect(invokeCount.object).to.equal 1
1543
1544 objectA.prop6 = "shouldn't update"
1545 expect(objectB.prop6).to.equal "shouldn't update"
1546 expect(invokeCount.object).to.equal 2
1547
1548 objectA.prop6 = "shouldn't update"
1549 expect(objectB.prop6).to.equal "shouldn't update"
1550 expect(invokeCount.object).to.equal 2
1551
1552
1553 expect(invokeCount.func).to.equal 1
1554
1555 objectA.prop7 = 'should update'
1556 expect(invokeCount.func).to.equal 2
1557
1558 objectA.prop7 = 'should update'
1559 expect(invokeCount.func).to.equal 3
1560
1561
1562
1563 expect(invokeCount.event).to.equal 1
1564
1565 objectA.prop8 = 'should update'
1566 expect(invokeCount.event).to.equal 2
1567
1568 objectA.prop8 = 'should update'
1569 expect(invokeCount.event).to.equal 3
1570
1571
1572
1573 bindings.object.setOption 'updateEvenIfSame', true
1574
1575 objectA.prop6 = 'should update'
1576 expect(objectB.prop6).to.equal 'should update'
1577 expect(invokeCount.object).to.equal 3
1578
1579 objectA.prop6 = 'should update'
1580 objectA.prop6 = 'should update'
1581 objectA.prop6 = 'should update'
1582 objectA.prop6 = 'should update'
1583 expect(invokeCount.object).to.equal 7
1584
1585
1586 bindings.object.setOption 'updateEvenIfSame', false
1587
1588 objectA.prop6 = "shouldn't update"
1589 expect(objectB.prop6).to.equal "shouldn't update"
1590 expect(invokeCount.object).to.equal 8
1591
1592 objectA.prop6 = "shouldn't update"
1593 objectA.prop6 = "shouldn't update"
1594 objectA.prop6 = "shouldn't update"
1595 objectA.prop6 = "shouldn't update"
1596 expect(invokeCount.object).to.equal 8
1597 restartSandbox()
1598
1599
1600
1601 test "Should update dependents even when its new value is undefined", ()->
1602 binding = SimplyBind('prop1').of(objectA).to('prop1').of(objectB)
1603 objectA.prop1 = 10
1604 expect(objectB.prop1).to.equal 10
1605
1606 objectA.prop1 = window.nonexistent # undefined value
1607 expect(objectB.prop1).to.be.undefined
1608
1609
1610 objectA.prop1 = 20
1611 expect(objectB.prop1).to.equal 20
1612
1613 objectA.prop1 = window.nonexistent # undefined value
1614 expect(objectB.prop1).to.be.undefined
1615
1616 restartSandbox()
1617
1618
1619
1620 test "Dependents should update their subdependents when being updated by an independent, even if these dependents are not liveProps", ()-> if not isBrowser then @skip() else
1621 objectA.prop2 = 0.8
1622
1623 SimplyBind('prop2').of(objectA)
1624 .to('opacity').of regA.style
1625
1626 SimplyBind('opacity').of(regA.style)
1627 .to('font-size').of(regA.style)
1628 .transform (opacity)-> opacity * 20 + 'px'
1629 .and('font-size').of(regB.style)
1630 .transform (opacity)-> opacity * 40 + 'px'
1631
1632
1633 expect(regA.style.opacity.toString()).to.equal '0.8'
1634 expect(regA.style['font-size']).to.equal '16px'
1635 expect(regB.style['font-size']).to.equal '32px'
1636
1637
1638 objectA.prop2 = 0.5
1639 expect(regA.style.opacity.toString()).to.equal '0.5'
1640 expect(regA.style['font-size']).to.equal '10px'
1641 expect(regB.style['font-size']).to.equal '20px'
1642 restartSandbox()
1643
1644
1645
1646 test "Placeholder indicators in a property name will be ignored if SimplyBind.options.simpleSelector is on", ()->
1647 objA = 'first': 'This {{placeholder}} will change'
1648 objB = 'second': 'This {{nonplaceholder}} will remain the same'
1649
1650 SimplyBind('prop1').of(objectA).to('first.placeholder').of(objA)
1651 objectA.prop1 = 'placeholder'
1652
1653 SimplyBind('prop1').of(objectB).to('second.nonplaceholder', {'simpleSelector':true}).of(objB)
1654 objectB.prop1 = 'placeholder'
1655
1656 expect(objA['first']).to.equal 'This placeholder will change'
1657 expect(objB['second']).to.equal 'This {{nonplaceholder}} will remain the same'
1658
1659
1660
1661 test "Dependents shouldn't be updated when SimplyBind.options.updateOnBind is off even when a transform function is applied", ()->
1662 dispatcher = 'value': 123
1663 invokeCount =
1664 'object': 0
1665 'array': 0
1666 'function': 0
1667 'domAttr': 0
1668 'domText': 0
1669 'domInput': 0
1670 'jQueryProp': 0
1671 'event': 0
1672
1673 fakeTransform = (value)-> value
1674 SimplyBind.setOption 'updateOnBind', false
1675 SimplyBind('prop').of(objectA).to(()-> invokeCount.object++).transform(fakeTransform)
1676 SimplyBind(arrayA).to(()-> invokeCount.array++).transform(fakeTransform)
1677 SimplyBind(fn = ()-> true).to(()-> invokeCount.function++).transform(fakeTransform)
1678 SimplyBind('attr:prop').of(regA).to(()-> invokeCount.domAttr++).transform(fakeTransform) if isBrowser
1679 SimplyBind('textContent').of(regA).to(()-> invokeCount.domText++).transform(fakeTransform) if isBrowser
1680 SimplyBind('value').of(inputA).to(()-> invokeCount.domInput++).transform(fakeTransform) if isBrowser
1681 SimplyBind('prop').of($regB).to(()-> invokeCount.jQueryProp++).transform(fakeTransform) if isBrowser
1682 SimplyBind(0).ofEvent('someEvent').of(eventEmitterA).to(()-> invokeCount.event++).transform(fakeTransform)
1683
1684
1685 SimplyBind.setOption 'updateOnBind', true
1686
1687 SimplyBind('value').of(dispatcher).to('prop').of(objectA)
1688 SimplyBind('value').of(dispatcher).to(arrayA)
1689 SimplyBind('value').of(dispatcher).to(fn)
1690 SimplyBind('value').of(dispatcher).to('attr:prop').of(regA) if isBrowser
1691 SimplyBind('value').of(dispatcher).to('textContent').of(regA) if isBrowser
1692 SimplyBind('value').of(dispatcher).to('value').of(inputA) if isBrowser
1693 SimplyBind('value').of(dispatcher).to('prop').of($regB) if isBrowser
1694 SimplyBind('value').of(dispatcher).toEvent('someEvent').of(eventEmitterA)
1695
1696 expect(invokeCount.object).to.equal 1
1697 expect(invokeCount.array).to.equal 0 # Cannot get updated
1698 expect(invokeCount.function).to.equal 3 # Ignores updateOnBind
1699 expect(invokeCount.event).to.equal 1
1700 if isBrowser
1701 expect(invokeCount.domAttr).to.equal 1
1702 expect(invokeCount.domText).to.equal 1
1703 expect(invokeCount.domInput).to.equal 1
1704 expect(invokeCount.jQueryProp).to.equal 1
1705
1706 restartSandbox()
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723 suite "Data Binding", ()->
1724 suiteSetup(restartSandbox)
1725 test "Live properties should be supported on all object properties", ()->
1726 dispatcher =
1727 'standard': objectA
1728 'array': arrayA
1729 'function': ()->
1730 'domDiv': if isBrowser then $('<div />')[0] else {}
1731 'domInput': if isBrowser then $('<input />')[0] else {}
1732 'domNode': if isBrowser then $('<div>text</div>')[0].childNodes[0] else {}
1733 receiver =
1734 'standard': null
1735 'array': null
1736 'function': null
1737 'domDiv': null
1738 'domInput': null
1739 'domNode': null
1740
1741 for name,object of dispatcher
1742 SimplyBind('prop').of(dispatcher[name]).to(name).of(receiver)
1743 expect(receiver[name]).to.equal(undefined)
1744
1745 object.prop = 100
1746 expect(receiver[name]).to.equal(100)
1747
1748 restartSandbox()
1749
1750
1751
1752
1753
1754 test "Infinite loops should not occur in looping update chains", ()->
1755 SimplyBind.setOption 'updateEvenIfSame', true
1756
1757 # ==== Objects =================================================================================
1758 SimplyBind('prop').of(objectA)
1759 .to('prop').of(objectB).bothWays()
1760 .chainTo('prop').of(objectC).bothWays()
1761 .chainTo('prop').of(objectA).bothWays()
1762
1763 objectA.prop = 'from objectA'
1764 expect(objectA.prop).to.equal 'from objectA'
1765 expect(objectB.prop).to.equal 'from objectA'
1766 expect(objectC.prop).to.equal 'from objectA'
1767
1768 objectB.prop = 'from objectB'
1769 expect(objectA.prop).to.equal 'from objectB'
1770 expect(objectB.prop).to.equal 'from objectB'
1771 expect(objectC.prop).to.equal 'from objectB'
1772
1773
1774
1775 # ==== Arrays =================================================================================
1776 SimplyBind(0).of(arrayA)
1777 .to(0).of(arrayB).bothWays()
1778 .chainTo(0).of(arrayC).bothWays()
1779 .chainTo(0).of(arrayA).bothWays()
1780
1781 arrayA[0] = 'from arrayA'
1782 expect(arrayA[0]).to.equal 'from arrayA'
1783 expect(arrayB[0]).to.equal 'from arrayA'
1784 expect(arrayC[0]).to.equal 'from arrayA'
1785
1786 arrayB[0] = 'from arrayB'
1787 expect(arrayA[0]).to.equal 'from arrayB'
1788 expect(arrayB[0]).to.equal 'from arrayB'
1789 expect(arrayC[0]).to.equal 'from arrayB'
1790
1791
1792
1793 if isBrowser
1794 # ==== DOMAttr =================================================================================
1795 SimplyBind('attr:someattr').of(regA)
1796 .to('attr:someattr').of(regB).bothWays()
1797 .chainTo('attr:someattr').of(regC).bothWays()
1798 .chainTo('attr:someattr').of(regA).bothWays()
1799
1800 # regA.setAttribute 'someattr', 'from regElementA'
1801 SimplyBind('attr:someattr').of(regA).set('from regElementA')
1802 expect(regA.getAttribute 'someattr').to.equal 'from regElementA'
1803 expect(regB.getAttribute 'someattr').to.equal 'from regElementA'
1804 expect(regC.getAttribute 'someattr').to.equal 'from regElementA'
1805
1806 # regB.setAttribute 'someattr', 'from regElementB'
1807 SimplyBind('attr:someattr').of(regB).set('from regElementB')
1808 expect(regA.getAttribute 'someattr').to.equal 'from regElementB'
1809 expect(regB.getAttribute 'someattr').to.equal 'from regElementB'
1810 expect(regC.getAttribute 'someattr').to.equal 'from regElementB'
1811
1812
1813
1814 # ==== DOMText =================================================================================
1815 SimplyBind('textContent').of(regA)
1816 .to('textContent').of(regB).bothWays()
1817 .chainTo('textContent').of(regC).bothWays()
1818 .chainTo('textContent').of(regA).bothWays()
1819
1820 SimplyBind('textContent').of(regA).set 'from regElementA'
1821 expect(regA.textContent).to.equal 'from regElementA'
1822 expect(regB.textContent).to.equal 'from regElementA'
1823 expect(regC.textContent).to.equal 'from regElementA'
1824
1825 SimplyBind('textContent').of(regB).set 'from regElementB'
1826 expect(regA.textContent).to.equal 'from regElementB'
1827 expect(regB.textContent).to.equal 'from regElementB'
1828 expect(regC.textContent).to.equal 'from regElementB'
1829
1830
1831
1832 # ==== DOMValue =================================================================================
1833 SimplyBind('value').of(inputA)
1834 .to('value').of(inputB).bothWays()
1835 .chainTo('value').of(inputC).bothWays()
1836 .chainTo('value').of(inputA).bothWays()
1837
1838 inputA.value = 'from inputElementA'
1839 inputA.emit 'change'
1840 expect(inputA.value).to.equal 'from inputElementA'
1841 expect(inputB.value).to.equal 'from inputElementA'
1842 expect(inputC.value).to.equal 'from inputElementA'
1843
1844 inputB.value = 'from inputElementB'
1845 inputB.emit 'change'
1846 expect(inputA.value).to.equal 'from inputElementB'
1847 expect(inputB.value).to.equal 'from inputElementB'
1848 expect(inputC.value).to.equal 'from inputElementB'
1849
1850
1851
1852 # ==== Events =================================================================================
1853 SimplyBind(0).ofEvent('eventA').of(eventEmitterA)
1854 .toEvent('eventB').of(eventEmitterA).bothWays()
1855
1856 SimplyBind(0).ofEvent('eventB').of(eventEmitterA)
1857 .toEvent('eventC').of(eventEmitterA).bothWays()
1858
1859 SimplyBind(0).ofEvent('eventC').of(eventEmitterA)
1860 .toEvent('eventA').of(eventEmitterA).bothWays()
1861
1862 invokeCountA = invokeCountB = invokeCountC = 0
1863 eventEmitterA.on 'eventA', ()-> invokeCountA++
1864 eventEmitterA.on 'eventB', ()-> invokeCountB++
1865 eventEmitterA.on 'eventC', ()-> invokeCountC++
1866
1867 eventEmitterA.emit 'eventA'
1868 eventEmitterA.emit 'eventB'
1869 eventEmitterA.emit 'eventC'
1870
1871 expect(invokeCountA).to.equal 3
1872 expect(invokeCountB).to.equal 3
1873 expect(invokeCountC).to.equal 3
1874 SimplyBind.setOption 'updateEvenIfSame', false
1875 restartSandbox()
1876
1877
1878
1879
1880 test "Infinite loops should not occur for colliding two-way bindings", ()->
1881 SimplyBind.setOption 'updateEvenIfSame', true
1882 dispatcher = 'prop': 0
1883 objectA.prop1 = 0
1884 objectB.prop1 = 0
1885 objectC.prop1 = 0
1886 SimplyBind('prop').of(dispatcher)
1887 .to('prop1').of(objectA).bothWays().transform (v, ov)-> v+ov
1888 .and('prop1').of(objectB).bothWays().transform (v, ov)-> v+ov
1889 .and('prop1').of(objectC).bothWays().transform (v, ov)-> v+ov
1890
1891 expect(dispatcher.prop).to.equal 0
1892 expect(objectA.prop1).to.equal 0
1893 expect(objectB.prop1).to.equal 0
1894 expect(objectC.prop1).to.equal 0
1895
1896 dispatcher.prop = 1
1897 expect(dispatcher.prop).to.equal 1
1898 expect(objectA.prop1).to.equal 1
1899 expect(objectB.prop1).to.equal 1
1900 expect(objectC.prop1).to.equal 1
1901
1902 objectA.prop1 = 2
1903 expect(dispatcher.prop).to.equal 3
1904 expect(objectA.prop1).to.equal 2
1905 expect(objectB.prop1).to.equal 4 # not 3 because dispatcher.prop gets a 2 from objectA.prop1 but then it transforms it to 3 and passes to this binding.
1906 expect(objectC.prop1).to.equal 4 # not 3 because dispatcher.prop gets a 2 from objectA.prop1 but then it transforms it to 3 and passes to this binding.
1907
1908 objectC.prop1 = 3
1909 expect(dispatcher.prop).to.equal 6
1910 expect(objectA.prop1).to.equal 8
1911 expect(objectB.prop1).to.equal 10
1912 expect(objectC.prop1).to.equal 3
1913
1914
1915 SimplyBind.setOption 'updateEvenIfSame', false
1916 restartSandbox()
1917
1918
1919
1920 test "Infinite loops should occur only when a value is bound to a function and that function updates the value", ()->
1921 invokeCount = 0
1922
1923 SimplyBind('prop1').of(objectA)
1924 .to ()-> unless invokeCount is 15
1925 objectA.prop1 = ++invokeCount
1926
1927 expect(invokeCount).to.equal(objectA.prop1)
1928 expect(invokeCount).to.equal(15)
1929 restartSandbox()
1930
1931
1932
1933
1934 test "Update dependents", ()->
1935 invokeCount =
1936 'object': 0
1937 'array': 0
1938 'function': 0
1939 'domAttr': 0
1940 'domText': 0
1941 'domValue': 0
1942 'domCheckboxSingle': 0
1943 'domCheckbox': 0
1944 'domRadioSingle': 0
1945 'domRadio': 0
1946 'event': 0
1947 SimplyBind.setOption('updateOnBind', false)
1948
1949
1950 # ==== Objects =================================================================================
1951 SimplyBind('prop').of(objectA).to ()-> invokeCount.object++
1952 objectA.prop = true
1953 expect(invokeCount.object).to.equal(1)
1954
1955
1956 # ==== Arrays =================================================================================
1957 SimplyBind(arrayA).to ()-> invokeCount.array++
1958 arrayA.push(1)
1959 arrayA.unshift(1)
1960 arrayA.pop()
1961 arrayA.shift()
1962 arrayA.splice(0,1)
1963 expect(invokeCount.array).to.equal(5)
1964
1965
1966 # ==== Functions =================================================================================
1967 SimplyBind(()->true).to ()-> invokeCount.function++
1968 expect(invokeCount.function).to.equal(1)
1969
1970
1971 if isBrowser
1972 # ==== DOMAttr =================================================================================
1973 SimplyBind('attr:someattr').of(regA).to ()-> invokeCount.domAttr++
1974 regA.setAttribute('someattr', 10)
1975 SimplyBind('attr:someattr').of(regA).set(true)
1976 expect(invokeCount.domAttr).to.equal(1)
1977
1978
1979 # ==== DOMText =================================================================================
1980 SimplyBind('textContent').of(regA).to ()-> invokeCount.domText++
1981 SimplyBind('textContent').of(regA).set 'true'
1982 expect(invokeCount.domText).to.equal(1)
1983
1984
1985 # ==== DOMValue =================================================================================
1986 SimplyBind('value').of(inputA).to ()-> invokeCount.domValue++
1987 inputA.value = 'true'
1988 inputA.emit 'change'
1989 expect(invokeCount.domValue).to.equal(1)
1990
1991
1992 # ==== DOMCheckbox Single =================================================================================
1993 SimplyBind('checked').of(checkboxA).to ()-> invokeCount.domCheckboxSingle++
1994 checkboxA.checked = true
1995 checkboxA.emit 'change'
1996 expect(invokeCount.domCheckboxSingle).to.equal(1)
1997
1998
1999 # ==== DOMCheckbox =================================================================================
2000 SimplyBind('checked').of(checkboxFields).to ()-> invokeCount.domCheckbox++
2001 checkboxB.checked = true
2002 checkboxB.emit 'change'
2003 expect(invokeCount.domCheckbox).to.equal(1)
2004
2005
2006 # ==== DOMRadio Single =================================================================================
2007 SimplyBind('checked').of(radioA).to ()-> invokeCount.domRadioSingle++
2008 radioA.checked = true
2009 radioA.emit 'change'
2010 expect(invokeCount.domRadioSingle).to.equal(1)
2011
2012
2013 # ==== DOMRadio =================================================================================
2014 SimplyBind('checked').of(radioFields).to ()-> invokeCount.domRadio++
2015 radioB.checked = true
2016 radioB.emit 'change'
2017 expect(invokeCount.domRadio).to.equal(1)
2018
2019
2020 # ==== Event =================================================================================
2021 SimplyBind.setOption('updateEvenIfSame', true)
2022 SimplyBind(0).ofEvent('someEvent').of(eventEmitterA).to ()-> invokeCount.event++
2023 eventEmitterA.emit 'someEvent'
2024
2025
2026 expect(invokeCount.event).to.equal(1)
2027
2028 SimplyBind.setOption('updateEvenIfSame', false)
2029 SimplyBind.setOption('updateOnBind', true)
2030 restartSandbox()
2031
2032
2033
2034
2035
2036
2037
2038 test "Receive a value as a dependent", ()->
2039 invokeCount =
2040 'object': 0
2041 'array': 0
2042 'function': 0
2043 'domAttr': 0
2044 'domText': 0
2045 'domValue': 0
2046 'domCheckboxSingle': 0
2047 'domCheckbox': 0
2048 'domRadio': 0
2049 'event': 0
2050 SimplyBind.setOption('updateOnBind', false)
2051
2052
2053 # ==== Objects =================================================================================
2054 SimplyBind('prop').of(objectA).to ()-> invokeCount.object++
2055 SimplyBind(()-> true).to('prop').of(objectA)
2056
2057 expect(objectA.prop).to.equal(true)
2058 expect(invokeCount.object).to.equal(1)
2059
2060
2061 # ==== Arrays =================================================================================
2062 SimplyBind(arrayA).to ()-> invokeCount.array++
2063 SimplyBind(()-> []).to('prop').of(objectA)
2064
2065 expect(arrayA.length).not.to.equal(0)
2066 expect(invokeCount.array).to.equal(0)
2067
2068 if isBrowser
2069 # ==== DOMAttr =================================================================================
2070 SimplyBind('attr:someattr').of(regA).to ()-> invokeCount.domAttr++
2071 SimplyBind(()-> 'true').to('attr:someattr').of(regA)
2072
2073 expect(regA.getAttribute('someattr')).to.equal('true')
2074 expect(invokeCount.domAttr).to.equal(1)
2075
2076
2077 # ==== DOMText =================================================================================
2078 SimplyBind('textContent').of(regA).to ()-> invokeCount.domText++
2079 SimplyBind(()-> 'true').to('textContent').of(regA)
2080
2081 expect(regA.textContent).to.equal('true')
2082 expect(invokeCount.domText).to.equal(1)
2083
2084
2085 # ==== DOMValue =================================================================================
2086 SimplyBind('value').of(inputA).to ()-> invokeCount.domValue++
2087 SimplyBind(()-> 'true').to('value').of(inputA)
2088
2089 expect(inputA.value).to.equal('true')
2090 expect(invokeCount.domValue).to.equal(1)
2091
2092
2093 # ==== DOMCheckbox Single =================================================================================
2094 expect(checkboxA.checked).to.be.false
2095 SimplyBind('checked').of(checkboxA).to ()-> invokeCount.domCheckboxSingle++
2096 SimplyBind(()-> 'truthy').to('checked').of(checkboxA)
2097
2098 expect(checkboxA.checked).to.be.true
2099 expect(invokeCount.domCheckboxSingle).to.equal(1)
2100
2101
2102 # ==== DOMCheckbox =================================================================================
2103 SimplyBind('checked').of($checkboxFields).to ()-> invokeCount.domCheckbox++
2104 SimplyBind(()-> ['checkboxB', 'checkboxC']).to('checked').of($checkboxFields)
2105
2106 expect(checkboxA.checked).to.be.false
2107 expect(checkboxB.checked).to.be.true
2108 expect(checkboxC.checked).to.be.true
2109 expect(invokeCount.domCheckbox).to.equal(1)
2110
2111 SimplyBind(()-> 'checkboxA').to('checked').of($checkboxFields)
2112
2113 expect(checkboxA.checked).to.be.true
2114 expect(checkboxB.checked).to.be.false
2115 expect(checkboxC.checked).to.be.false
2116 expect(invokeCount.domCheckbox).to.equal(2)
2117
2118
2119 # ==== DOMRadio =================================================================================
2120 SimplyBind('checked').of($radioFields).to ()-> invokeCount.domRadio++
2121 SimplyBind(()-> 'radioC').to('checked').of($radioFields)
2122
2123 expect(radioA.checked).to.be.false
2124 expect(radioB.checked).to.be.false
2125 expect(radioC.checked).to.be.true
2126 expect(invokeCount.domRadio).to.equal(1)
2127
2128
2129 # ==== Event =================================================================================
2130 SimplyBind(0).ofEvent('someEvent').of(eventEmitterA).to ()-> invokeCount.event++
2131 SimplyBind(()-> true).toEvent('someEvent').of(eventEmitterA)
2132
2133 expect(invokeCount.event).to.equal(1)
2134
2135
2136 SimplyBind.setOption('updateOnBind', true)
2137 restartSandbox()
2138
2139
2140
2141
2142
2143
2144
2145
2146 suite "Placeholders", ()->
2147 test "Property names with periods (.) indicate a placeholder", ()->
2148 dispatcher = 'value': 'Medium'
2149
2150 objectA.prop = 'The size of this shirt is {{size}}, man!'
2151 if isBrowser
2152 inputA.value = 'The size of this shirt is {{size}}, man!'
2153 regA.textContent = 'The size of this shirt is {{size}}, man!'
2154 regA.setAttribute 'someattr', 'The size of this shirt is {{size}}, man!'
2155
2156 SimplyBind('value').of(dispatcher)
2157 .to('prop.size').of(objectA)
2158
2159 if isBrowser
2160 SimplyBind('value').of(dispatcher)
2161 .to('value.size').of(inputA)
2162 .and('textContent.size').of(regA)
2163 .and('attr:someattr.size').of(regA)
2164
2165 expect(objectA.prop).to.equal 'The size of this shirt is Medium, man!'
2166 if isBrowser
2167 expect(inputA.value).to.equal 'The size of this shirt is Medium, man!'
2168 expect(regA.textContent).to.equal 'The size of this shirt is Medium, man!'
2169 expect(regA.getAttribute 'someattr').to.equal 'The size of this shirt is Medium, man!'
2170 restartSandbox()
2171
2172
2173
2174 test "No changes should be made to the string when trying to update a placeholder that doesn't exist", ()->
2175 objectB.prop1 = 'The size of this shirt is _____, man!'
2176 binding = SimplyBind('prop1').of(objectA).to('prop1.size').of(objectB)
2177
2178 binding.set 'Medium'
2179 expect(objectB.prop1).to.equal 'The size of this shirt is _____, man!'
2180 restartSandbox()
2181
2182
2183
2184 test "The updating value doesn't have to be a string", ()->
2185 objectB.prop1 = 'The size of this shirt is {{size}}, man!'
2186 binding = SimplyBind('prop1').of(objectA).to('prop1.size').of(objectB)
2187 binding.set false
2188 expect(objectB.prop1).to.equal 'The size of this shirt is false, man!'
2189 binding.set null
2190 expect(objectB.prop1).to.equal 'The size of this shirt is null, man!'
2191 binding.set {}
2192 expect(objectB.prop1).to.equal 'The size of this shirt is [object Object], man!'
2193
2194 restartSandbox()
2195
2196
2197
2198 test "If a property name has more than one period, only the first will be accounted for", ()->
2199 objectB.prop1 = 'The size of this shirt is {{size.redundant}}, man!'
2200 binding = SimplyBind('prop1').of(objectA).to('prop1.size.redundant').of(objectB)
2201
2202 binding.set 'Medium'
2203 expect(objectB.prop1).to.equal 'The size of this shirt is Medium, man!'
2204 restartSandbox()
2205
2206
2207
2208 test "Custom placeholder markers can be set", ()->
2209 SimplyBind.setOption 'placeholder', ['^^', '$$']
2210
2211 objectB.prop1 = 'The size of this shirt is ^^size$$, man!'
2212
2213 binding = SimplyBind('prop1').of(objectA).to('prop1.size').of(objectB)
2214 binding.set 'XXS'
2215
2216 expect(objectB.prop1).to.equal 'The size of this shirt is XXS, man!'
2217 SimplyBind.setOption 'placeholder', ['{{', '}}']
2218
2219 restartSandbox()
2220
2221
2222
2223 test "Target strings can have multiple placeholders", ()->
2224 dispatcher = 'value': 'Large'
2225
2226 objectA.prop = 'The size of this shirt is {{size}}, and {{size}} is {{size}}ly rad!'
2227 if isBrowser
2228 inputA.value = 'The size of this shirt is {{size}}, and {{size}} is {{size}}ly rad!'
2229 regA.textContent = 'The size of this shirt is {{size}}, and {{size}} is {{size}}ly rad!'
2230 regA.setAttribute 'someattr', 'The size of this shirt is {{size}}, and {{size}} is {{size}}ly rad!'
2231
2232 SimplyBind('value').of(dispatcher)
2233 .to('prop.size').of(objectA)
2234
2235 if isBrowser
2236 SimplyBind('value').of(dispatcher)
2237 .to('value.size').of(inputA)
2238 .and('textContent.size').of(regA)
2239 .and('attr:someattr.size').of(regA)
2240
2241 expect(objectA.prop).to.equal 'The size of this shirt is Large, and Large is Largely rad!'
2242 if isBrowser
2243 expect(inputA.value).to.equal 'The size of this shirt is Large, and Large is Largely rad!'
2244 expect(regA.textContent).to.equal 'The size of this shirt is Large, and Large is Largely rad!'
2245 expect(regA.getAttribute 'someattr').to.equal 'The size of this shirt is Large, and Large is Largely rad!'
2246 restartSandbox()
2247
2248
2249
2250
2251 test "Multiple values can be set for the same target that has multiple placeholders", ()->
2252 dispatcher =
2253 'verb': 'not'
2254 'nounOne': 'small'
2255 'nounTwo': 'pretty'
2256
2257 objectB.prop = 'The following text is {{verb}} {{nounOne}} and {{nounTwo}}'
2258 if isBrowser
2259 inputB.value = 'The following text is {{verb}} {{nounOne}} and {{nounTwo}}'
2260 regB.setAttribute 'someattr', 'The following text is {{verb}} {{nounOne}} and {{nounTwo}}'
2261
2262 SimplyBind('verb').of(dispatcher)
2263 .to('prop.verb').of(objectB)
2264
2265 if isBrowser
2266 SimplyBind('verb').of(dispatcher)
2267 .to('value.verb').of(inputB)
2268 .and('textContent.verb').of(regB)
2269 .and('attr:someattr.verb').of(regB)
2270
2271
2272
2273 SimplyBind('nounOne').of(dispatcher)
2274 .to('prop.nounOne').of(objectB)
2275
2276 if isBrowser
2277 SimplyBind('nounOne').of(dispatcher)
2278 .to('value.nounOne').of(inputB)
2279 .and('textContent.nounOne').of(regB)
2280 .and('attr:someattr.nounOne').of(regB)
2281
2282
2283
2284 SimplyBind('nounTwo').of(dispatcher)
2285 .to('prop.nounTwo').of(objectB)
2286
2287 if isBrowser
2288 SimplyBind('nounTwo').of(dispatcher)
2289 .to('value.nounTwo').of(inputB)
2290 .and('textContent.nounTwo').of(regB)
2291 .and('attr:someattr.nounTwo').of(regB)
2292
2293
2294 values = ()-> if not isBrowser then [objectB.prop] else [objectB.prop, inputB.value, regB.textContent, regB.getAttribute 'someattr']
2295 for value in values()
2296 expect(value).to.equal 'The following text is not small and pretty'
2297
2298
2299
2300 dispatcher.verb = 'very'
2301 dispatcher.nounOne = 'big'
2302 dispatcher.nounTwo = 'ugly'
2303 for value in values()
2304 expect(value).to.equal 'The following text is very big and ugly'
2305
2306
2307 restartSandbox()
2308
2309
2310
2311
2312 test "Multiple values can be set for the same target that has multiple placeholders + transforms", ()->
2313 dispatcher =
2314 'verbOne': 'not'
2315 'verbTwo': 'not'
2316 'nounOne': 'small'
2317 'nounTwo': 'pretty'
2318
2319 objectC.prop = 'The following text is {{verbOne}} ({{verbTwo}}) {{nounOne}} and {{nounTwo}}'
2320 if isBrowser
2321 inputC.value = 'The following text is {{verbOne}} ({{verbTwo}}) {{nounOne}} and {{nounTwo}}'
2322 regC.textContent = 'The following text is {{verbOne}} ({{verbTwo}}) {{nounOne}} and {{nounTwo}}'
2323 regC.setAttribute 'someattr', 'The following text is {{verbOne}} ({{verbTwo}}) {{nounOne}} and {{nounTwo}}'
2324
2325
2326
2327 SimplyBind('verbOne').of(dispatcher)
2328 .to('prop.verbOne').of(objectC).transform (value)-> value.toUpperCase()
2329
2330 if isBrowser
2331 SimplyBind('verbOne').of(dispatcher)
2332 .to('value.verbOne').of(inputC)
2333 .and('textContent.verbOne').of(regC)
2334 .and('attr:someattr.verbOne').of(regC)
2335 .transformAll (value)-> value.toUpperCase()
2336
2337
2338
2339 SimplyBind('verbTwo').of(dispatcher)
2340 .to('prop.verbTwo').of(objectC).transform (value)-> value.toLowerCase()
2341
2342 if isBrowser
2343 SimplyBind('verbTwo').of(dispatcher)
2344 .to('value.verbTwo').of(inputC)
2345 .and('textContent.verbTwo').of(regC)
2346 .and('attr:someattr.verbTwo').of(regC)
2347 .transformAll (value)-> value.toLowerCase()
2348
2349
2350
2351 SimplyBind('nounOne').of(dispatcher)
2352 .to('prop.nounOne').of(objectC).transform (value)-> value.toUpperCase()
2353
2354 if isBrowser
2355 SimplyBind('nounOne').of(dispatcher)
2356 .to('value.nounOne').of(inputC)
2357 .and('textContent.nounOne').of(regC)
2358 .and('attr:someattr.nounOne').of(regC)
2359 .transformAll (value)-> value.toUpperCase()
2360
2361
2362
2363 SimplyBind('nounTwo').of(dispatcher)
2364 .to('prop.nounTwo').of(objectC).transform (value)-> value.toUpperCase()
2365
2366 if isBrowser
2367 SimplyBind('nounTwo').of(dispatcher)
2368 .to('value.nounTwo').of(inputC)
2369 .and('textContent.nounTwo').of(regC)
2370 .and('attr:someattr.nounTwo').of(regC)
2371 .transformAll (value)-> value.toUpperCase()
2372
2373
2374
2375 values = ()-> if not isBrowser then [objectC.prop] else [objectC.prop, inputC.value, regC.textContent, regC.getAttribute 'someattr']
2376 for value in values()
2377 expect(value).to.equal 'The following text is NOT (not) SMALL and PRETTY'
2378
2379
2380 dispatcher.verbOne = dispatcher.verbTwo = 'Very'
2381 dispatcher.nounOne = 'Big'
2382 dispatcher.nounTwo = 'Ugly'
2383 for value in values()
2384 expect(value).to.equal 'The following text is VERY (very) BIG and UGLY'
2385
2386
2387 restartSandbox()
2388
2389
2390
2391
2392 test "Multiple placeholder bindings to textContent with multiple placeholders in a single textNode containing nested placeholders should function properly", ()-> if not isBrowser then @skip() else
2393 dispatcher = 'wordA':'', 'wordB':''
2394
2395 expect(regD.textContent).to.equal 'The following {{wordA}} and {{wordB}} replaced correctly'
2396
2397 SimplyBind('wordA').of(dispatcher)
2398 .to('textContent.wordA').of(regD)
2399
2400 SimplyBind('wordB').of(dispatcher)
2401 .to('textContent.wordB').of(regD)
2402
2403 expect(regD.textContent).to.equal 'The following and replaced correctly'
2404
2405
2406 dispatcher.wordA = 'firstWord'
2407 dispatcher.wordB = 'secondWord'
2408 expect(regD.textContent).to.equal 'The following firstWord and secondWord replaced correctly'
2409 restartSandbox()
2410
2411
2412
2413
2414 test "When an object prop with placeholders is an updater, it can either update individual placeholder values or its entire value", ()->
2415 window.dispatcher = 'nounA':'color', 'nounB':'car', 'adverb':'very', 'color':'red'
2416 window.receiver = 'prop':'The {{nounA}} of this {{nounB}} is {{adverb}} {{color}}'
2417 window.lastReceiver = 'prop':''
2418
2419 SimplyBind('nounA').of(dispatcher).to('prop.nounA').of(receiver)
2420 SimplyBind('nounB').of(dispatcher).to('prop.nounB').of(receiver)
2421 SimplyBind('adverb').of(dispatcher).to('prop.adverb').of(receiver)
2422 SimplyBind('color').of(dispatcher).to('prop.color').of(receiver)
2423 expect(receiver.prop).to.equal 'The color of this car is very red'
2424
2425 SimplyBind('prop.nounA').of(receiver)
2426 .to('prop').of(lastReceiver)
2427
2428 expect(lastReceiver.prop).to.equal 'color'
2429 dispatcher.nounA = 'colour'
2430 expect(lastReceiver.prop).to.equal 'colour'
2431
2432
2433 SimplyBind('prop.nounB').of(receiver)
2434 .to('prop').of(lastReceiver)
2435
2436 expect(lastReceiver.prop).to.equal 'car'
2437 dispatcher.nounB = 'airplane'
2438 expect(lastReceiver.prop).to.equal 'airplane'
2439
2440
2441 SimplyBind('prop').of(receiver)
2442 .to('prop').of(lastReceiver)
2443
2444 expect(lastReceiver.prop).to.equal 'The colour of this airplane is very red'
2445
2446
2447
2448
2449 test "When manually changing the value of the prop after binding, the application of the placeholders will ignore the changes", ()-> if not isBrowser then @skip() else
2450 dispatcher = 'verb':'', 'nounOne':'', 'nounTwo':''
2451 objectA.prop1 = regB.textContent
2452
2453 SimplyBind('verb').of(dispatcher)
2454 .to('prop1.verb').of(objectA)
2455 .and('textContent.verb').of(regB)
2456
2457 SimplyBind('nounOne').of(dispatcher)
2458 .to('prop1.nounOne').of(objectA)
2459 .and('textContent.nounOne').of(regB)
2460
2461 SimplyBind('nounTwo').of(dispatcher)
2462 .to('prop1.nounTwo').of(objectA)
2463 .and('textContent.nounTwo').of(regB)
2464
2465 dispatcher.verb = 'looking'
2466 dispatcher.nounOne = 'nice'
2467 dispatcher.nounTwo = 'long'
2468 expect(objectA.prop1).to.equal 'The following text is looking nice and long'
2469 expect(objectA.prop1).to.equal(regB.textContent)
2470
2471
2472 upperCaseNodes = (nodes)-> for node in nodes
2473 if node.nodeType is 3
2474 node.textContent = node.textContent.toUpperCase()
2475 else
2476 upperCaseNodes(node)
2477
2478 upperCaseNodes(regB.childNodes)
2479 objectA.prop1 = 'modified'
2480
2481 dispatcher.verb = 'not looking'
2482 dispatcher.nounOne = 'ugly'
2483 dispatcher.nounTwo = 'short'
2484 expect(objectA.prop1).to.equal 'The following text is not looking ugly and short'
2485 expect(objectA.prop1).to.equal(regB.textContent)
2486
2487 # But new text nodes won't be replaced!
2488 $regBH1.append '<span> and some other attribute</span>'
2489 dispatcher.verb = 'looking'
2490 dispatcher.nounOne = 'nice'
2491 dispatcher.nounTwo = 'long'
2492 expect(objectA.prop1).to.equal 'The following text is looking nice and long'
2493 expect(regB.textContent).to.equal 'The following text is looking nice and long and some other attribute'
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505 suite "Binding-Type Specific", ()->
2506 suite "DOMValue", ()->
2507 suiteSetup ()-> if not isBrowser then @skip()
2508 test "Dispatching an event isn't necessary when updating the value for a binding's .set()/.update() method", ()->
2509 binding = SimplyBind('value').of(inputA).to('prop2').of(objectA)
2510 binding.set 'changed from binding instance'
2511 expect(objectA.prop2).to.equal 'changed from binding instance'
2512
2513
2514 test "Bindings to a DOM element's .value property will only create a DOMValue binding if it is an input/select/textarea field", ()->
2515 expect(SimplyBind('value').of(inputA)._.type).to.equal 'DOMValue'
2516 expect(SimplyBind('value').of(regA)._.type).to.equal 'ObjectProp'
2517 expect(SimplyBind('value').of(document)._.type).to.equal 'ObjectProp'
2518
2519
2520 test "When passing an element collection to .of() in the form of a jQuery object/NodeList/HTMLCollection, only the first element will be used", ()->
2521 SimplyBind('prop1').of(objectA)
2522 .to('value').of $inputA
2523 .and('value').of document.querySelectorAll('#input2')
2524
2525 objectA.prop1 = 'updated'
2526 expect($inputA.val()).to.equal 'updated'
2527 expect($inputB.val()).to.equal 'updated'
2528 restartSandbox()
2529
2530
2531 test "A change event will be dispatched upon value update if SimplyBind.options.dispatchEvents is on", ()->
2532 invokeCountSelect = 0
2533 invokeCountRadio = 0
2534 SimplyBind.setOption 'dispatchEvents', true
2535 SimplyBind.setOption 'updateEvenIfSame', true
2536
2537
2538 SimplyBind('prop4').of(objectA)
2539 .to('value').of $select
2540 .and('checked').of $radioFields
2541
2542 $select[0].on 'change', ()-> invokeCountSelect++
2543 $radioFields.each ()-> @on 'change', ()-> invokeCountRadio++
2544 objectA.prop4 = 'radioA'
2545 objectA.prop4 = 'radioB'
2546 objectA.prop4 = 'radioB'
2547
2548
2549 expect(invokeCountSelect).to.equal 3
2550 expect(invokeCountRadio).to.equal 3
2551
2552 SimplyBind.setOption 'dispatchEvents', false
2553 SimplyBind.setOption 'updateEvenIfSame', false
2554 restartSandbox()
2555
2556
2557
2558
2559
2560 suite "DOMCheckbox", ()->
2561 suiteSetup ()-> if not isBrowser then @skip()
2562 test "When attempting to bind a property other than 'checked' of a checkbox element list, only the first element will be used", ()->
2563 origLog = console.warn
2564 console.warn = chai.spy()
2565 SimplyBind('prop1').of(objectA)
2566 .to('newProp').of checkboxFields
2567 .and('newProp').of [].reverse.call($checkboxFields)
2568
2569 objectA.prop1 = 'updated'
2570 expect(checkboxA.newProp).to.equal 'updated'
2571 expect(checkboxC.newProp).to.equal 'updated'
2572 expect(checkboxB.newProp).not.to.be.defined
2573 expect(console.warn).to.have.been.called()
2574
2575 console.warn = origLog
2576 restartSandbox()
2577
2578
2579 test "A change event will be dispatched upon value update if SimplyBind.options.dispatchEvents is on", ()->
2580 invokeCount = 0
2581 SimplyBind.setOption 'dispatchEvents', true
2582 SimplyBind.setOption 'updateEvenIfSame', true
2583
2584
2585 SimplyBind('prop4').of(objectA)
2586 .to('checked').of checkboxFields
2587
2588 $checkboxFields.each ()-> @on 'change', ()-> invokeCount++
2589 objectA.prop4 = 'checkboxA'
2590 objectA.prop4 = 'checkboxB'
2591 objectA.prop4 = 'checkboxB'
2592
2593 expect(invokeCount).to.equal 3
2594
2595 SimplyBind.setOption 'dispatchEvents', false
2596 SimplyBind.setOption 'updateEvenIfSame', false
2597 restartSandbox()
2598
2599
2600 test "Initial checked values should be noted during initial binding", ()->
2601 expect(checkboxA.checked).to.be.false
2602 expect(checkboxB.checked).to.be.false
2603 expect(checkboxC.checked).to.be.false
2604
2605 checkboxB.checked = true
2606 checkboxC.checked = true
2607
2608 SimplyBind('checked').of(checkboxFields).to (checkedFields)->
2609 expect(checkedFields).to.be.instanceOf Array
2610 expect(checkedFields.length).to.equal 2
2611 expect(checkedFields[0]).to.equal 'checkboxB'
2612 expect(checkedFields[1]).to.equal 'checkboxC'
2613
2614
2615
2616
2617
2618 suite "DOMRadio", ()->
2619 suiteSetup ()-> if not isBrowser then @skip()
2620 test "When attempting to bind a property other than 'checked' of a radio element list, only the first element will be used", ()->
2621 origLog = console.warn
2622 console.warn = chai.spy()
2623 SimplyBind('prop1').of(objectA)
2624 .to('newProp').of radioFields
2625 .and('newProp').of [].reverse.call($radioFields)
2626
2627 objectA.prop1 = 'updated'
2628 expect(radioA.newProp).to.equal 'updated'
2629 expect(radioC.newProp).to.equal 'updated'
2630 expect(radioB.newProp).not.to.be.defined
2631 expect(console.warn).to.have.been.called()
2632
2633 console.warn = origLog
2634 restartSandbox()
2635
2636
2637 test "A change event will be dispatched upon value update if SimplyBind.options.dispatchEvents is on", ()->
2638 invokeCount = 0
2639 SimplyBind.setOption 'dispatchEvents', true
2640 SimplyBind.setOption 'updateEvenIfSame', true
2641
2642
2643 SimplyBind('prop4').of(objectA)
2644 .to('checked').of radioFields
2645
2646 $radioFields.each ()-> @on 'change', ()-> invokeCount++
2647 objectA.prop4 = 'radioA'
2648 objectA.prop4 = 'radioB'
2649 objectA.prop4 = 'radioB'
2650
2651 expect(invokeCount).to.equal 3
2652
2653 SimplyBind.setOption 'dispatchEvents', false
2654 SimplyBind.setOption 'updateEvenIfSame', false
2655 restartSandbox()
2656
2657
2658 test "Initial checked values should be noted during initial binding", ()->
2659 expect(radioA.checked).to.be.false
2660 expect(radioB.checked).to.be.false
2661 expect(radioC.checked).to.be.false
2662
2663 radioB.checked = true
2664
2665 SimplyBind('checked').of(radioFields).to (selectedRadio)->
2666 expect(selectedRadio).to.equal 'radioB'
2667
2668
2669
2670
2671 suite "DOMAttr", ()->
2672 suiteSetup ()-> if not isBrowser then @skip()
2673 test "Requires the 'attr:' prefix in the property name in order to bind the attribute values and not the property values", ()->
2674 nonPrefix = SimplyBind('first').of(regA).to('first').of(regB)
2675 withPrefix = SimplyBind('attr:second').of(regA).to('attr:second').of(regB)
2676
2677 nonPrefix.set 'someValue'
2678 expect(regA.first).to.equal 'someValue'
2679 expect(regB.first).to.equal 'someValue'
2680 expect(regA.getAttribute 'first').not.to.equal 'someValue'
2681 expect(regB.getAttribute 'first').not.to.equal 'someValue'
2682
2683 withPrefix.set 'someValue'
2684 expect(regA.second).not.to.equal 'someValue'
2685 expect(regB.second).not.to.equal 'someValue'
2686 expect(regA.getAttribute 'second').to.equal 'someValue'
2687 expect(regB.getAttribute 'second').to.equal 'someValue'
2688 restartSandbox()
2689
2690
2691
2692 suite "Function", ()->
2693 test "Will be invoked on bind and have its return value used as the value that the dependents will receive", ()->
2694 SimplyBind ()-> 123
2695 .to('prop').of(objectA)
2696
2697 expect(objectA.prop).to.equal 123
2698
2699
2700 test "Will be invoked on bind as a dependent and will receive 2 arguments - 1st is the new value passed while the 2nd is the prev value passed", ()->
2701 invokeCount = 0
2702 objectA.prop1 = 'some value'
2703 SimplyBind('prop1').of(objectA)
2704 .to (newValue, prevValue)->
2705 switch invokeCount
2706 when 0
2707 expect(newValue).to.equal 'some value'
2708 expect(prevValue).to.be.undefined
2709 when 1
2710 expect(newValue).to.equal 'new value'
2711 expect(prevValue).to.equal 'some value'
2712 when 2
2713 expect(newValue).to.equal 'newer value'
2714 expect(prevValue).to.equal 'new value'
2715 invokeCount++
2716
2717 objectA.prop1 = 'new value'
2718 objectA.prop1 = 'newer value'
2719
2720 restartSandbox()
2721
2722
2723 test "Will be invoked when is the first part of an argument regardless of whether or not SimplyBind.options.updateOnBind is on", ()->
2724 invokeCount = 0
2725 expect(SimplyBind.options.updateOnBind).to.be.true
2726
2727 SimplyBind ()-> invokeCount++; 123
2728 .to('prop').of(objectA)
2729 expect(invokeCount).to.equal 1
2730
2731
2732 SimplyBind.setOption 'updateOnBind', false
2733
2734 SimplyBind ()-> invokeCount++; 456
2735 .to('prop').of(objectA)
2736 expect(invokeCount).to.equal 2
2737
2738 SimplyBind.setOption 'updateOnBind', true
2739
2740
2741
2742
2743 suite "Array", ()->
2744 test "can bind an array to anything and update dependents even when calling the .push, .pop, .shift, .unshift, .splice, .reverse, and .sort methods", ()->
2745 sampleArray = [1,2,3,4,5,6,7,8,9,10]
2746 mutations = 0
2747 SimplyBind(sampleArray).to('prop4').of(objectB).and ()-> mutations++
2748
2749 expect(objectB.prop4.length).to.equal 10
2750 expect(mutations).to.equal 1
2751 sampleArray.push 11
2752 expect(objectB.prop4.length).to.equal 11
2753 expect(mutations).to.equal 2
2754 sampleArray.shift()
2755 sampleArray.shift()
2756 expect(objectB.prop4.length).to.equal 9
2757 expect(mutations).to.equal 4
2758 sampleArray.unshift 2
2759 sampleArray.unshift 1
2760 expect(objectB.prop4.length).to.equal 11
2761 expect(mutations).to.equal 6
2762 sampleArray.pop()
2763 sampleArray.pop()
2764 expect(objectB.prop4.length).to.equal 9
2765 expect(mutations).to.equal 8
2766 sampleArray.splice 0, 5
2767 expect(objectB.prop4.length).to.equal 4
2768 expect(mutations).to.equal 9
2769 sampleArray.reverse()
2770 expect(objectB.prop4.length).to.equal 4
2771 expect(mutations).to.equal 10
2772 sampleArray.sort()
2773 expect(objectB.prop4.length).to.equal 4
2774 expect(mutations).to.equal 11
2775 sampleArray.sort ()->
2776 1
2777 expect(objectB.prop4.length).to.equal 4
2778 expect(mutations).to.equal 12
2779
2780
2781
2782 test "it's impossible to replace the array object in an array binding.", ()->
2783 sampleArray = [1,2,3,4,5,6,7,8,9,10]
2784 mutations = 0
2785 SimplyBind(sampleArray).to('prop4').of(objectB).and ()-> mutations++
2786
2787 expect(objectB.prop4.length).to.equal 10
2788 expect(mutations).to.equal 1
2789 SimplyBind(sampleArray).set [1,2,3,4,5,6,7,8,9,10]
2790 # Replace array. If it were to replace it, the mutations count will increase by one.
2791 SimplyBind(sampleArray).set [1,2,3,4,5,6,7,8,9,10]
2792 SimplyBind(sampleArray).set [1,2,3,4,5,6,7,8,9,10]
2793 sampleArray.push 11
2794 expect(objectB.prop4.length).to.equal 11
2795 expect(mutations).to.equal 2
2796 sampleArray.shift()
2797 sampleArray.shift()
2798 expect(objectB.prop4.length).to.equal 9
2799 expect(mutations).to.equal 4
2800 sampleArray.unshift 2
2801 sampleArray.unshift 1
2802 expect(objectB.prop4.length).to.equal 11
2803 expect(mutations).to.equal 6
2804 sampleArray.pop()
2805 sampleArray.pop()
2806 expect(objectB.prop4.length).to.equal 9
2807 expect(mutations).to.equal 8
2808 sampleArray.splice 0, 5
2809 expect(objectB.prop4.length).to.equal 4
2810 expect(mutations).to.equal 9
2811
2812
2813
2814 test "will update dependents when its children get modified if specified via the trackArrayChildren option", ()->
2815 sampleArray = [1,2,3,4,5,6,7,8,9,10]
2816 sampleArray2 = [1,2,3,4,5,6,7,8,9,10]
2817 mutations = 0
2818 SimplyBind(sampleArray).to('prop5').of(objectA).and ()-> mutations++
2819
2820 expect(objectA.prop5).to.equal sampleArray
2821 sampleArray[0] = 100
2822 sampleArray[1] = 200
2823 sampleArray[2] = 300
2824 expect(objectA.prop5[0]).to.equal 100
2825 expect(objectA.prop5[1]).to.equal 200
2826 expect(objectA.prop5[2]).to.equal 300
2827 expect(mutations).to.equal 1
2828 mutations = 0
2829 SimplyBind(sampleArray2, 'trackArrayChildren': true).to('prop6').of(objectA).and ()-> mutations++
2830
2831 expect(objectA.prop6).to.equal sampleArray2
2832 sampleArray2[0] = 100
2833 sampleArray2[1] = 200
2834 sampleArray2[2] = 300
2835 expect(objectA.prop6[0]).to.equal 100
2836 expect(objectA.prop6[1]).to.equal 200
2837 expect(objectA.prop6[2]).to.equal 300
2838 expect(mutations).to.equal 4
2839
2840
2841
2842 test "will update dependents even when there is a sepearate binding on a reference of the same array", ()->
2843 sampleArrayA = [1,2,3,4,5,6,7,8,9,10]
2844 sampleArrayB = sampleArrayA
2845 mutationsA = 0
2846 mutationsB = 0
2847 SimplyBind(sampleArrayA)
2848 .to('prop6').of(objectA)
2849 .and ()-> mutationsA++
2850
2851 SimplyBind(sampleArrayB)
2852 .to('prop6').of(objectB)
2853 .and ()-> mutationsB++
2854
2855 expect(sampleArrayA).to.equal sampleArrayB
2856 expect(mutationsA).to.equal 1
2857 expect(mutationsB).to.equal 1
2858 sampleArrayA.push 11
2859 sampleArrayA.push 12
2860 sampleArrayB.push 13
2861 sampleArrayB.push 14
2862 expect(mutationsA).to.equal 5
2863 expect(mutationsB).to.equal 5
2864
2865
2866 test "Will update Func dependents with a clone to prevent possible infinite loops and for comparison", ()->
2867 receivedValues = 'current':null, 'prev':null
2868 middleMan = 'value':null
2869 prevCurrent = null
2870
2871 SimplyBind(arrayA).to (currentArray, prevArray)->
2872 receivedValues.current = currentArray
2873 receivedValues.prev = prevArray
2874
2875 prevCurrent = receivedValues.current
2876 expect(receivedValues.current).to.be.instanceOf(Array)
2877 expect(receivedValues.current.length).to.equal(arrayA.length)
2878 expect(receivedValues.current).not.to.equal(arrayA)
2879 expect(receivedValues.prev).to.be.undefined
2880
2881 arrayA.push(10)
2882 expect(receivedValues.current.length).to.equal(arrayA.length)
2883 expect(receivedValues.current).not.to.equal(arrayA)
2884 expect(receivedValues.current).not.to.equal(prevCurrent)
2885 expect(receivedValues.prev).to.equal(prevCurrent)
2886
2887
2888 SimplyBind(arrayB)
2889 .to('value').of(middleMan).transform (array)-> array.length
2890 .chainTo (currentArray, prevArray)->
2891 receivedValues.current = currentArray
2892 receivedValues.prev = prevArray
2893
2894 arrayB.unshift(-1)
2895 expect(middleMan.value).to.equal(arrayB.length)
2896 expect(receivedValues.current).to.equal(arrayB.length)
2897
2898 restartSandbox()
2899
2900
2901
2902
2903
2904 suite "Event", ()->
2905 test "The specified selector will indicate which argument index of the event emitter callback should be saved to the binding", ()->
2906 emptyObj = {}
2907 sampleObject = 'holder': emptyObj
2908
2909 SimplyBind(0).ofEvent('focus').of(eventEmitterA)
2910 .to('holder').of sampleObject
2911
2912 expect(sampleObject.holder).to.equal emptyObj
2913
2914 eventEmitterA.emit 'focus', eventEmitterA
2915 if isBrowser
2916 expect(sampleObject.holder.target).to.equal eventEmitterA
2917 else
2918 expect(sampleObject.holder).to.equal eventEmitterA
2919
2920 restartSandbox()
2921
2922
2923
2924
2925 test "Custom events will behave exactly like regular events", ()->
2926 emptyObj = {}
2927 sampleObject = 'holder': emptyObj
2928
2929 SimplyBind(0).ofEvent('somethingCustom').of(eventEmitterA).to('holder').of sampleObject
2930 expect(sampleObject.holder).to.equal emptyObj
2931
2932 eventEmitterA.emit 'somethingCustom', eventEmitterA
2933 if isBrowser
2934 expect(sampleObject.holder.target).to.equal eventEmitterA
2935 else
2936 expect(sampleObject.holder).to.equal eventEmitterA
2937
2938
2939
2940
2941 test "Custom listener/emitter methods can be used", ()->
2942 emitCount = 0
2943 receiveCount = 0
2944 $eventEmitterA.customListener 'click', ()-> emitCount++
2945
2946 SimplyBind('prop2').of(objectA)
2947 .toEvent('click', 'customEmitter', 'customListener').of($eventEmitterA).bothWays ()-> ++receiveCount
2948 expect(emitCount).to.equal 1
2949 expect(receiveCount).to.equal 0
2950
2951 objectA.prop2 = 'whatever'
2952 expect(emitCount).to.equal 2
2953
2954 $eventEmitterA.customEmitter 'click'
2955 expect(receiveCount).to.equal 1
2956 expect(objectA.prop2).to.equal 1
2957 expect(emitCount).to.equal 3
2958
2959 objectA.prop2 = 'whatever again'
2960 expect(emitCount).to.equal 4
2961
2962 $eventEmitterA.customEmitter 'click'
2963 expect(receiveCount).to.equal 2
2964 expect(objectA.prop2).to.equal 2
2965 restartSandbox()
2966
2967
2968
2969
2970 test "jQuery listener/emitter methods can be used", ()-> if not isBrowser then @skip() else
2971 emitCount = 0
2972 receiveCount = 0
2973 $eventEmitterA.on 'click', ()-> emitCount++
2974
2975 SimplyBind('prop2').of(objectA)
2976 .toEvent('click', 'trigger', 'on').of($eventEmitterA).bothWays ()-> ++receiveCount
2977 expect(emitCount).to.equal 1
2978 expect(receiveCount).to.equal 0
2979
2980 objectA.prop2 = 'whatever'
2981 expect(emitCount).to.equal 2
2982
2983 $eventEmitterA.trigger 'click'
2984 expect(receiveCount).to.equal 1
2985 expect(objectA.prop2).to.equal 1
2986 expect(emitCount).to.equal 3
2987
2988 objectA.prop2 = 'whatever again'
2989 expect(emitCount).to.equal 4
2990
2991 $eventEmitterA.trigger 'click'
2992 expect(receiveCount).to.equal 2
2993 expect(objectA.prop2).to.equal 2
2994 restartSandbox()
2995
2996
2997
2998
2999 test "Default listener/emitter methods will be used if custom listener/emitter methods don't exist on the target object", ()->
3000 emitCount = 0
3001 receiveCount = 0
3002 $eventEmitterA.customListener 'click', ()-> emitCount++
3003
3004 SimplyBind('prop2').of(objectA)
3005 .toEvent('click', 'nonexistentDispatcher', 'nonexistentListener').of($eventEmitterA).bothWays ()-> ++receiveCount
3006 expect(emitCount).to.equal 1
3007 expect(receiveCount).to.equal 0
3008
3009 objectA.prop2 = 'whatever'
3010 expect(emitCount).to.equal 2
3011
3012 $eventEmitterA.customEmitter 'click'
3013 expect(receiveCount).to.equal 1
3014 expect(objectA.prop2).to.equal 1
3015 expect(emitCount).to.equal 3
3016
3017 objectA.prop2 = 'whatever again'
3018 expect(emitCount).to.equal 4
3019
3020 $eventEmitterA.customEmitter 'click'
3021 expect(receiveCount).to.equal 2
3022 expect(objectA.prop2).to.equal 2
3023 restartSandbox()
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033 suite "Create bindings to/from multiple objects using the 'multi:' descriptor as a shortcut", ()->
3034
3035 suite "Basic Bindings", ()->
3036 test "Binding single prop to multiple ObjectProps", ()->
3037 dispatcher = 'prop':'value1'
3038 objectA.prop1 = objectB.prop1 = objectC.prop1 = null
3039
3040 SimplyBind('prop').of(dispatcher)
3041 .to('multi:prop1').of([objectA, objectB, objectC])
3042
3043 expect(objectA.prop1).to.equal 'value1'
3044 expect(objectB.prop1).to.equal 'value1'
3045 expect(objectC.prop1).to.equal 'value1'
3046
3047 dispatcher.prop = 'value2'
3048 expect(objectA.prop1).to.equal 'value2'
3049 expect(objectB.prop1).to.equal 'value2'
3050 expect(objectC.prop1).to.equal 'value2'
3051
3052 restartSandbox()
3053
3054
3055
3056 test "Binding multiple ObjectProp to a single prop", ()->
3057 receiver = 'prop':null
3058 objectA.prop1 = objectB.prop1 = objectC.prop1 = 'value1'
3059
3060 SimplyBind('multi:prop1').of([objectA, objectB, objectC])
3061 .to('prop').of(receiver)
3062
3063 expect(receiver.prop).to.equal 'value1'
3064 expect(objectA.prop1).to.equal 'value1'
3065 expect(objectB.prop1).to.equal 'value1'
3066 expect(objectC.prop1).to.equal 'value1'
3067
3068 objectA.prop1 = 'value2'
3069 expect(receiver.prop).to.equal 'value2'
3070 expect(objectA.prop1).to.equal 'value2'
3071 expect(objectB.prop1).to.equal 'value1'
3072 expect(objectC.prop1).to.equal 'value1'
3073
3074 objectB.prop1 = 'value3'
3075 expect(receiver.prop).to.equal 'value3'
3076 expect(objectA.prop1).to.equal 'value2'
3077 expect(objectB.prop1).to.equal 'value3'
3078 expect(objectC.prop1).to.equal 'value1'
3079
3080 objectC.prop1 = 'value4'
3081 expect(receiver.prop).to.equal 'value4'
3082 expect(objectA.prop1).to.equal 'value2'
3083 expect(objectB.prop1).to.equal 'value3'
3084 expect(objectC.prop1).to.equal 'value4'
3085
3086 restartSandbox()
3087
3088
3089
3090 test "Binding multiple ObjectProp to a single prop both ways", ()->
3091 receiverDispatcher = 'prop':'valueP1'
3092 objectA.prop1 = objectB.prop1 = objectC.prop1 = 'value1'
3093
3094 SimplyBind('multi:prop1').of([objectA, objectB, objectC])
3095 .to('prop').of(receiverDispatcher).bothWays()
3096
3097 expect(receiverDispatcher.prop).to.equal 'value1'
3098 expect(objectA.prop1).to.equal 'value1'
3099 expect(objectB.prop1).to.equal 'value1'
3100 expect(objectC.prop1).to.equal 'value1'
3101
3102 objectA.prop1 = 'value2'
3103 expect(receiverDispatcher.prop).to.equal 'value2'
3104 expect(objectA.prop1).to.equal 'value2'
3105 expect(objectB.prop1).to.equal 'value2'
3106 expect(objectC.prop1).to.equal 'value2'
3107
3108 objectB.prop1 = 'value3'
3109 receiverDispatcher.prop = 'valueP3'
3110 expect(receiverDispatcher.prop).to.equal 'valueP3'
3111 expect(objectA.prop1).to.equal 'valueP3'
3112 expect(objectB.prop1).to.equal 'valueP3'
3113 expect(objectC.prop1).to.equal 'valueP3'
3114
3115 objectC.prop1 = 'value4'
3116 expect(receiverDispatcher.prop).to.equal 'value4'
3117 expect(objectA.prop1).to.equal 'value4'
3118 expect(objectB.prop1).to.equal 'value4'
3119 expect(objectC.prop1).to.equal 'value4'
3120
3121 restartSandbox()
3122
3123
3124
3125
3126
3127 suite "Transform Bindings", ()->
3128 test "Binding single prop to multiple ObjectProps + transform", ()->
3129 dispatcher = 'prop':'value1'
3130 objectA.prop1 = objectB.prop1 = objectC.prop1 = null
3131
3132 SimplyBind('prop').of(dispatcher)
3133 .to('multi:prop1').of([objectA, objectB, objectC])
3134 .transform (value)-> value.toUpperCase()
3135
3136 dispatcher.prop = 'value2'
3137 expect(dispatcher.prop).to.equal 'value2'
3138 expect(objectA.prop1).to.equal 'VALUE2'
3139 expect(objectB.prop1).to.equal 'VALUE2'
3140 expect(objectC.prop1).to.equal 'VALUE2'
3141
3142 restartSandbox()
3143
3144
3145
3146 test "Binding multiple ObjectProp to a single prop + transform", ()->
3147 receiver = 'prop':null
3148 objectA.prop1 = objectB.prop1 = objectC.prop1 = 'value1'
3149
3150 SimplyBind('multi:prop1').of([objectA, objectB, objectC])
3151 .to('prop').of(receiver)
3152 .transform (value)-> value.toUpperCase()
3153
3154
3155 objectA.prop1 = 'value2'
3156 expect(receiver.prop).to.equal 'VALUE2'
3157 expect(objectA.prop1).to.equal 'value2'
3158 expect(objectB.prop1).to.equal 'value1'
3159 expect(objectC.prop1).to.equal 'value1'
3160
3161 objectB.prop1 = 'value3'
3162 expect(receiver.prop).to.equal 'VALUE3'
3163 expect(objectA.prop1).to.equal 'value2'
3164 expect(objectB.prop1).to.equal 'value3'
3165 expect(objectC.prop1).to.equal 'value1'
3166
3167 objectC.prop1 = 'value4'
3168 expect(receiver.prop).to.equal 'VALUE4'
3169 expect(objectA.prop1).to.equal 'value2'
3170 expect(objectB.prop1).to.equal 'value3'
3171 expect(objectC.prop1).to.equal 'value4'
3172
3173 restartSandbox()
3174
3175
3176
3177 test "Binding multiple ObjectProp to a single prop both ways + transform", ()->
3178 receiverDispatcher = 'prop':'valueP1'
3179 objectA.prop1 = objectB.prop1 = objectC.prop1 = 'value1'
3180
3181 SimplyBind('multi:prop1').of([objectA, objectB, objectC])
3182 .to('prop').of(receiverDispatcher).bothWays()
3183 .transform (value)-> value.toUpperCase()
3184
3185 objectA.prop1 = 'value2'
3186 expect(receiverDispatcher.prop).to.equal 'VALUE2'
3187 expect(objectA.prop1).to.equal 'value2'
3188 expect(objectB.prop1).to.equal 'VALUE2'
3189 expect(objectC.prop1).to.equal 'VALUE2'
3190
3191 objectB.prop1 = 'value3'
3192 receiverDispatcher.prop = 'valueP3'
3193 expect(receiverDispatcher.prop).to.equal 'valueP3'
3194 expect(objectA.prop1).to.equal 'VALUEP3'
3195 expect(objectB.prop1).to.equal 'VALUEP3'
3196 expect(objectC.prop1).to.equal 'VALUEP3'
3197
3198 objectC.prop1 = 'value4'
3199 expect(receiverDispatcher.prop).to.equal 'VALUE4'
3200 expect(objectA.prop1).to.equal 'VALUE4'
3201 expect(objectB.prop1).to.equal 'VALUE4'
3202 expect(objectC.prop1).to.equal 'value4'
3203
3204 restartSandbox()
3205
3206
3207
3208
3209
3210 suite "Condition Bindings", ()->
3211 test "Binding single prop to multiple ObjectProps + condition", ()->
3212 dispatcher = 'prop':'value1'
3213 objectA.prop1 = objectB.prop1 = objectC.prop1 = null
3214 allowUpdate = false
3215
3216 SimplyBind('prop', {updateOnBind:false}).of(dispatcher)
3217 .to('multi:prop1').of([objectA, objectB, objectC])
3218 .condition ()-> allowUpdate
3219
3220 expect(objectA.prop1).to.equal null
3221 expect(objectB.prop1).to.equal null
3222 expect(objectC.prop1).to.equal null
3223
3224 dispatcher.prop = 'value2'
3225 expect(dispatcher.prop).to.equal 'value2'
3226 expect(objectA.prop1).to.equal null
3227 expect(objectB.prop1).to.equal null
3228 expect(objectC.prop1).to.equal null
3229
3230 allowUpdate = true
3231 dispatcher.prop = 'value3'
3232 expect(dispatcher.prop).to.equal 'value3'
3233 expect(objectA.prop1).to.equal 'value3'
3234 expect(objectB.prop1).to.equal 'value3'
3235 expect(objectC.prop1).to.equal 'value3'
3236
3237 restartSandbox()
3238
3239
3240
3241 test "Binding multiple ObjectProp to a single prop + condition", ()->
3242 receiver = 'prop':null
3243 objectA.prop1 = objectB.prop1 = objectC.prop1 = 'value1'
3244 allowUpdate = false
3245
3246 SimplyBind('multi:prop1', {updateOnBind:false}).of([objectA, objectB, objectC])
3247 .to('prop').of(receiver)
3248 .condition ()-> allowUpdate
3249
3250 expect(receiver.prop).to.equal null
3251 expect(objectA.prop1).to.equal 'value1'
3252 expect(objectB.prop1).to.equal 'value1'
3253 expect(objectC.prop1).to.equal 'value1'
3254
3255 objectA.prop1 = 'value2'
3256 expect(receiver.prop).to.equal null
3257 expect(objectA.prop1).to.equal 'value2'
3258 expect(objectB.prop1).to.equal 'value1'
3259 expect(objectC.prop1).to.equal 'value1'
3260
3261 allowUpdate = true
3262 objectB.prop1 = 'value3'
3263 expect(receiver.prop).to.equal 'value3'
3264 expect(objectA.prop1).to.equal 'value2'
3265 expect(objectB.prop1).to.equal 'value3'
3266 expect(objectC.prop1).to.equal 'value1'
3267
3268 restartSandbox()
3269
3270
3271
3272 test "Binding multiple ObjectProp to a single prop both ways + condition", ()->
3273 receiverDispatcher = 'prop':'valueP1'
3274 objectA.prop1 = objectB.prop1 = objectC.prop1 = 'value1'
3275 allowUpdate = false
3276
3277 SimplyBind('multi:prop1', {updateOnBind:false}).of([objectA, objectB, objectC])
3278 .to('prop').of(receiverDispatcher).bothWays()
3279 .condition ()-> allowUpdate
3280
3281 expect(receiverDispatcher.prop).to.equal 'valueP1'
3282 expect(objectA.prop1).to.equal 'value1'
3283 expect(objectB.prop1).to.equal 'value1'
3284 expect(objectC.prop1).to.equal 'value1'
3285
3286 objectA.prop1 = 'value2'
3287 expect(receiverDispatcher.prop).to.equal 'valueP1'
3288 expect(objectA.prop1).to.equal 'value2'
3289 expect(objectB.prop1).to.equal 'value1'
3290 expect(objectC.prop1).to.equal 'value1'
3291
3292 allowUpdate = true
3293 receiverDispatcher.prop = 'valueP3'
3294 expect(receiverDispatcher.prop).to.equal 'valueP3'
3295 expect(objectA.prop1).to.equal 'valueP3'
3296 expect(objectB.prop1).to.equal 'valueP3'
3297 expect(objectC.prop1).to.equal 'valueP3'
3298
3299 objectC.prop1 = 'value4'
3300 expect(receiverDispatcher.prop).to.equal 'value4'
3301 expect(objectA.prop1).to.equal 'value4'
3302 expect(objectB.prop1).to.equal 'value4'
3303 expect(objectC.prop1).to.equal 'value4'
3304
3305 restartSandbox()
3306
3307
3308
3309
3310 suite "Miscellenious", ()->
3311 test "Binding single prop to multiple ObjectProps' placeholders", ()->
3312 dispatcher = 'prop':'word'
3313 objectA.prop1 = objectB.prop1 = objectC.prop1 = 'some {{noun}} here'
3314
3315 SimplyBind('prop').of(dispatcher)
3316 .to('multi:prop1.noun').of([objectA, objectB, objectC])
3317
3318 expect(objectA.prop1).to.equal 'some word here'
3319 expect(objectB.prop1).to.equal 'some word here'
3320 expect(objectC.prop1).to.equal 'some word here'
3321
3322 dispatcher.prop = 'letter'
3323 expect(objectA.prop1).to.equal 'some letter here'
3324 expect(objectB.prop1).to.equal 'some letter here'
3325 expect(objectC.prop1).to.equal 'some letter here'
3326
3327 restartSandbox()
3328
3329
3330
3331 test "Fetching the value using the .get() method of a multi binding should yield an array of all the values", ()->
3332 objectA.prop1 = 'A'
3333 objectB.prop1 = 'B'
3334 objectC.prop1 = 'C'
3335
3336 binding = SimplyBind('multi:prop1').of([objectA, objectB, objectC])
3337 values = binding.get()
3338
3339 expect(objectA.prop1).to.equal 'A'
3340 expect(objectB.prop1).to.equal 'B'
3341 expect(objectC.prop1).to.equal 'C'
3342 expect(values).to.be.instanceOf Array
3343 expect(values[0]).to.equal 'A'
3344 expect(values[1]).to.equal 'B'
3345 expect(values[2]).to.equal 'C'
3346
3347 restartSandbox()
3348
3349
3350
3351 test "Fetching the value using the .get() method of a multi binding w/ placeholders should yield an the value of the first binding only", ()->
3352 dispatcher = 'prop':'word'
3353 objectA.prop1 = objectB.prop1 = objectC.prop1 = 'some {{noun}} here'
3354
3355 SimplyBind('prop').of(dispatcher)
3356 .to('multi:prop1.noun').of([objectA, objectB, objectC])
3357
3358 binding = SimplyBind('multi:prop1.noun').of([objectA, objectB, objectC])
3359 value = binding.get()
3360
3361 expect(objectA.prop1).to.equal 'some word here'
3362 expect(objectB.prop1).to.equal 'some word here'
3363 expect(objectC.prop1).to.equal 'some word here'
3364 expect(value).to.equal 'word'
3365
3366 restartSandbox()
3367
3368
3369
3370 test "Binding single prop to multiple ObjectProps with a self transform should be applied to all bindings", ()->
3371 dispatcher = 'prop':'value1'
3372 objectA.prop1 = objectB.prop1 = objectC.prop1 = null
3373
3374 SimplyBind('prop').of(dispatcher)
3375 .to('multi:prop1').of([objectA, objectB, objectC])
3376
3377 SimplyBind('multi:prop1.noun').of([objectA, objectB, objectC])
3378 .transformSelf (value)-> value.toUpperCase()
3379
3380 expect(objectA.prop1).to.equal 'VALUE1'
3381 expect(objectB.prop1).to.equal 'VALUE1'
3382 expect(objectC.prop1).to.equal 'VALUE1'
3383
3384 dispatcher.prop = 'value2'
3385 expect(objectA.prop1).to.equal 'VALUE2'
3386 expect(objectB.prop1).to.equal 'VALUE2'
3387 expect(objectC.prop1).to.equal 'VALUE2'
3388
3389 restartSandbox()
3390
3391
3392
3393 test ".throttle() on a multi binding will throttle the dep update frequency to once in a given timeframe", (done)->
3394 invokeCount = 0
3395 lastValue = null
3396 objectA.prop1 = objectB.prop1 = objectC.prop1 = 'same'
3397
3398 SimplyBind('multi:prop1').of([objectA, objectB, objectC])
3399 .to (v)-> lastValue=v; invokeCount++
3400 .throttle(15)
3401
3402 expect(invokeCount).to.equal(3)
3403 expect(lastValue).to.equal 'same'
3404
3405 objectA.prop1 = 'another1'
3406 objectA.prop1 = 'another2'
3407 objectA.prop1 = 'another3'
3408 objectA.prop1 = 'another4'
3409 expect(invokeCount).to.equal(4)
3410 expect(lastValue).to.equal('another1')
3411
3412 objectB.prop1 = 'another5'
3413 expect(lastValue).to.equal('another5')
3414 expect(invokeCount).to.equal(5)
3415
3416 objectC.prop1 = 'another6'
3417 expect(lastValue).to.equal('another6')
3418 expect(invokeCount).to.equal(6)
3419
3420 objectC.prop1 = 'another7'
3421 expect(invokeCount).to.equal(6)
3422 expect(lastValue).to.equal('another6')
3423
3424 setTimeout ()->
3425 expect(invokeCount).to.equal(8)
3426 expect(lastValue).to.equal('another7')
3427 done()
3428 restartSandbox()
3429 , 25
3430
3431
3432
3433 test "Using the 'multi:' descriptor on a empty DOM list should throw an error", ()->
3434 expect ()-> SimplyBind('multi:prop').of($('nonexistent'))
3435 .to.throw()
3436
3437 expect ()-> SimplyBind('multi:prop').of(document.querySelectorAll('nonexistent'))
3438 .to.throw()
3439
3440 expect ()-> SimplyBind('multi:prop').of([])
3441 .to.throw()
3442
3443 restartSandbox()
3444
3445
3446
3447 suite "Unbinding", ()->
3448 test "Binding single prop to multiple ObjectProps and then calling .unBind() should unbind all", ()->
3449 dispatcher = 'prop':'value1'
3450 objectA.prop1 = objectB.prop1 = objectC.prop1 = null
3451
3452 binding = SimplyBind('prop').of(dispatcher)
3453 .to('multi:prop1').of([objectA, objectB, objectC])
3454
3455 expect(objectA.prop1).to.equal 'value1'
3456 expect(objectB.prop1).to.equal 'value1'
3457 expect(objectC.prop1).to.equal 'value1'
3458
3459 binding.unBind()
3460
3461 dispatcher.prop = 'value2'
3462 expect(objectA.prop1).to.equal 'value1'
3463 expect(objectB.prop1).to.equal 'value1'
3464 expect(objectC.prop1).to.equal 'value1'
3465
3466 restartSandbox()
3467
3468
3469
3470 test "Binding multiple ObjectProps to a single prop and then calling .unBind() should unbind all props from that single prop", ()->
3471 receiver = 'prop':null
3472 objectA.prop1 = objectB.prop1 = objectC.prop1 = 'value1'
3473
3474 binding = SimplyBind('multi:prop1').of([objectA, objectB, objectC])
3475 .to('prop').of(receiver)
3476
3477 expect(receiver.prop).to.equal 'value1'
3478 expect(objectA.prop1).to.equal 'value1'
3479 expect(objectB.prop1).to.equal 'value1'
3480 expect(objectC.prop1).to.equal 'value1'
3481
3482 binding.unBind()
3483
3484 objectA.prop1 = 'value2'
3485 expect(receiver.prop).to.equal 'value1'
3486 expect(objectA.prop1).to.equal 'value2'
3487 expect(objectB.prop1).to.equal 'value1'
3488 expect(objectC.prop1).to.equal 'value1'
3489
3490 objectB.prop1 = 'value3'
3491 expect(receiver.prop).to.equal 'value1'
3492 expect(objectA.prop1).to.equal 'value2'
3493 expect(objectB.prop1).to.equal 'value3'
3494 expect(objectC.prop1).to.equal 'value1'
3495
3496 objectC.prop1 = 'value4'
3497 expect(receiver.prop).to.equal 'value1'
3498 expect(objectA.prop1).to.equal 'value2'
3499 expect(objectB.prop1).to.equal 'value3'
3500 expect(objectC.prop1).to.equal 'value4'
3501
3502 restartSandbox()
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515 suite "Stage 3 methods", ()->
3516 suite ".and()", ()->
3517 suiteSetup(restartSandbox)
3518 test "Behaves just like .to() which allows to bind a value to multiple targets", ()->
3519 SimplyBind('prop1').of(objectA)
3520 .to('prop1').of(objectB)
3521 .and('prop2').of(objectB)
3522 .and('prop3').of(objectB)
3523 .and('prop4').of(objectB)
3524
3525 objectA.prop1 = 'all at once'
3526 expect(objectB.prop1).to.equal 'all at once'
3527 expect(objectB.prop2).to.equal 'all at once'
3528 expect(objectB.prop3).to.equal 'all at once'
3529 expect(objectB.prop4).to.equal 'all at once'
3530 restartSandbox()
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549 suite ".chainTo()/.pipe()", ()->
3550 suiteSetup(restartSandbox)
3551
3552 test "Helps create complex chainings", ()->
3553 outerstate =
3554 'first': null
3555 'second': null
3556 'third': null
3557
3558 objectA.prop1 = 'Hello World'
3559 SimplyBind('prop1').of(objectA)
3560 .to('prop1').of(objectB)
3561 .and('prop2').of(objectA)
3562 .and (val)-> outerstate.first = val
3563 .transformAll (val)-> val.toUpperCase()
3564
3565 .chainTo('prop2').of(objectB)
3566 .and('prop3').of(objectA)
3567 .transform (val)-> val.toLowerCase()
3568
3569 .chainTo('prop3').of(objectB)
3570 .and('prop4').of(objectA)
3571 .and (val)-> outerstate.second = val
3572
3573 .chainTo('prop4').of(objectB)
3574 .and (val)-> outerstate.third = val.slice(0, 5)
3575
3576
3577
3578 expect(outerstate.first).to.equal 'HELLO WORLD'
3579 expect(outerstate.second).to.equal 'hello world'
3580 expect(outerstate.third).to.equal 'hello'
3581 expect(objectA.prop1).to.equal 'Hello World'
3582 expect(objectA.prop2).to.equal 'HELLO WORLD'
3583 expect(objectB.prop1).to.equal 'HELLO WORLD'
3584 expect(objectB.prop2).to.equal 'HELLO WORLD'
3585 expect(objectA.prop3).to.equal 'hello world'
3586 expect(objectB.prop3).to.equal 'hello world'
3587 expect(objectA.prop4).to.equal 'hello world'
3588 expect(objectB.prop4).to.equal 'hello world'
3589 restartSandbox()
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610 suite ".transform()", ()->
3611 suiteSetup(restartSandbox)
3612 test "Will apply a transformation to the value before updating the dependent", ()->
3613 dispatcher = 'value': 'current'
3614 transformFn = (newValue, currentValue)-> newValue+'+'+currentValue
3615
3616 objectA.prop = ''
3617 if isBrowser
3618 inputA.value = ''
3619 regA.textContent = ''
3620 regA.setAttribute 'someattr', ''
3621
3622 SimplyBind('value').of(dispatcher)
3623 .to('prop').of(objectA).transform(transformFn)
3624
3625 if isBrowser
3626 SimplyBind('value').of(dispatcher)
3627 .to('value').of(inputA).transform(transformFn)
3628 .and('textContent').of(regA).transform(transformFn)
3629 .and('attr:someattr').of(regA).transform(transformFn)
3630
3631
3632 values = ()-> if not isBrowser then [objectA.prop] else [objectA.prop, inputA.value, regA.textContent, regA.getAttribute 'someattr']
3633 for value in values()
3634 expect(value).to.equal 'current+current'
3635
3636
3637 dispatcher.value = 'new'
3638 for value in values()
3639 expect(value).to.equal 'new+current+current'
3640
3641
3642 dispatcher.value = 'new'
3643 for value in values()
3644 expect(value).to.equal 'new+current+current'
3645
3646
3647 dispatcher.value = 'newer'
3648 for value in values()
3649 expect(value).to.equal 'newer+new+current+current'
3650
3651 restartSandbox()
3652
3653
3654
3655
3656 test "Will apply transformations to event bindings", ()-> if not isBrowser then @skip() else
3657 resultHolder = 'event':null
3658
3659 SimplyBind(0).ofEvent('someEvent').of(eventEmitterA)
3660 .to('event').of(resultHolder)
3661 .transform (event)-> event.type
3662
3663 eventEmitterA.emit 'someEvent'
3664 expect(resultHolder.event).to.equal 'someEvent'
3665
3666
3667
3668
3669 test "Will cause a re-update only to the last proxied dependent", ()->
3670 invokeCount =
3671 'A':0
3672 'B':0
3673 'C':0
3674
3675 SimplyBind('prop1').of(objectA)
3676 .to ()-> invokeCount.A++
3677 .transform (v)-> v
3678 .and ()-> invokeCount.B++
3679 .transform (v)-> v
3680 .and ()-> invokeCount.C++
3681 .transform (v)-> v
3682
3683 expect(invokeCount.A).to.equal 2
3684 expect(invokeCount.B).to.equal 2
3685 expect(invokeCount.C).to.equal 2
3686
3687 objectA.prop1 = !objectA.prop1
3688 expect(invokeCount.A).to.equal 3
3689 expect(invokeCount.B).to.equal 3
3690 expect(invokeCount.C).to.equal 3
3691
3692
3693
3694 test "Promise transforms can be used instead of regular return values if SimplyBind.options.promiseTransforms is on", (done)->
3695 objectA.prop2 = 'current'
3696
3697 SimplyBind('prop1', {'updateOnBind':false}).of(objectA)
3698 .to (result)-> expect(result.then).not.to.be.undefined
3699 .transform ()-> new Promise ()->
3700
3701 SimplyBind('prop2', {'promiseTransforms':true}).of(objectA)
3702 .to('prop2').of(objectB)
3703 .transform (newValue, currentValue)-> new Promise (resolve)-> resolve(newValue+'+'+currentValue)
3704
3705 setTimeout ()->
3706 expect(objectB.prop2).to.equal 'current+current'
3707 objectA.prop2 = 'new'
3708
3709 setTimeout ()->
3710 expect(objectB.prop2).to.equal 'new+current+current'
3711 restartSandbox()
3712 done()
3713 , 0
3714 , 0
3715
3716
3717
3718
3719 test "A transform will only be used for the last declared dependent", ()->
3720 dispatcher = 'value': 'test'
3721 objectA.prop1 = ''
3722 objectB.prop1 = ''
3723
3724 SimplyBind('value').of(dispatcher)
3725 .to('prop1').of(objectA)
3726 .and('prop1').of(objectB)
3727 .transform (value)-> value.toUpperCase()
3728
3729 expect(objectA.prop1).to.equal 'test'
3730 expect(objectB.prop1).to.equal 'TEST'
3731
3732 restartSandbox()
3733
3734
3735
3736 test "A transform will be used both ways when .bothWays() is called after the .transform declaration", ()->
3737 dispatcher = 'value': 'test'
3738 objectA.prop = ''
3739
3740 SimplyBind('value').of(dispatcher)
3741 .to('prop').of(objectA)
3742 .transform (value)-> value.toUpperCase()
3743 .bothWays()
3744
3745 expect(dispatcher.value).to.equal 'test'
3746 expect(objectA.prop).to.equal 'TEST'
3747
3748 dispatcher.value = 'from dispatcher'
3749 expect(dispatcher.value).to.equal 'from dispatcher'
3750 expect(objectA.prop).to.equal 'FROM DISPATCHER'
3751
3752 objectA.prop = 'from objectA'
3753 expect(dispatcher.value).to.equal 'FROM OBJECTA'
3754 expect(objectA.prop).to.equal 'from objectA'
3755
3756 restartSandbox()
3757
3758
3759
3760 test "A transform will be used both ways when .bothWays() is called before the .transform declaration", ()->
3761 dispatcher = 'value': 'test'
3762 objectA.prop = ''
3763
3764 SimplyBind('value').of(dispatcher)
3765 .to('prop').of(objectA).bothWays()
3766 .transform (value)-> value.toUpperCase()
3767
3768 expect(dispatcher.value).to.equal 'test'
3769 expect(objectA.prop).to.equal 'TEST'
3770
3771 dispatcher.value = 'from dispatcher'
3772 expect(dispatcher.value).to.equal 'from dispatcher'
3773 expect(objectA.prop).to.equal 'FROM DISPATCHER'
3774
3775 objectA.prop = 'from objectA'
3776 expect(dispatcher.value).to.equal 'FROM OBJECTA'
3777 expect(objectA.prop).to.equal 'from objectA'
3778
3779 restartSandbox()
3780
3781
3782
3783 test "A different transform will be used backwards if a function is passed to .bothWays() as the first argument", ()->
3784 dispatcher = 'value': 'test'
3785 objectA.prop = ''
3786
3787 SimplyBind('value').of(dispatcher)
3788 .to('prop').of(objectA)
3789 .transform (value)-> value.toUpperCase()
3790 .bothWays (value)-> value.toLowerCase()
3791
3792 expect(dispatcher.value).to.equal 'test'
3793 expect(objectA.prop).to.equal 'TEST'
3794
3795 dispatcher.value = 'From Dispatcher'
3796 expect(dispatcher.value).to.equal 'From Dispatcher'
3797 expect(objectA.prop).to.equal 'FROM DISPATCHER'
3798
3799 objectA.prop = 'From ObjectA'
3800 expect(dispatcher.value).to.equal 'from objecta'
3801 expect(objectA.prop).to.equal 'From ObjectA'
3802
3803
3804
3805 test "No transform will be used backwards if a false value is passed to .bothWays() as the first argument", ()->
3806 dispatcher = 'value': 'test'
3807 objectA.prop = ''
3808
3809 SimplyBind('value').of(dispatcher)
3810 .to('prop').of(objectA)
3811 .transform (value)-> value.toUpperCase()
3812 .bothWays(false)
3813
3814 expect(dispatcher.value).to.equal 'test'
3815 expect(objectA.prop).to.equal 'TEST'
3816
3817 dispatcher.value = 'From Dispatcher'
3818 expect(dispatcher.value).to.equal 'From Dispatcher'
3819 expect(objectA.prop).to.equal 'FROM DISPATCHER'
3820
3821 objectA.prop = 'From ObjectA'
3822 expect(dispatcher.value).to.equal 'From ObjectA'
3823 expect(objectA.prop).to.equal 'From ObjectA'
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842 suite ".transformAll()", ()->
3843 suiteSetup(restartSandbox)
3844 test "Behaves the same way as .transform() does, except it applies the transform to all of the declared dependents", ()->
3845 dispatcher = 'value': 'current'
3846
3847 objectA.prop = objectC.prop = objectC.prop = ''
3848
3849 SimplyBind('value').of(dispatcher)
3850 .to('prop').of(objectA)
3851 .and('prop').of(objectB)
3852 .and('prop').of(objectC)
3853 .transformAll (newValue, currentValue)-> newValue+'+'+currentValue
3854
3855 for value in [objectA.prop, objectB.prop, objectC.prop]
3856 expect(value).to.equal 'current+current'
3857
3858
3859 dispatcher.value = 'new'
3860 for value in [objectA.prop, objectB.prop, objectC.prop]
3861 expect(value).to.equal 'new+current+current'
3862
3863
3864 dispatcher.value = 'new'
3865 for value in [objectA.prop, objectB.prop, objectC.prop]
3866 expect(value).to.equal 'new+current+current'
3867
3868
3869 dispatcher.value = 'newer'
3870 for value in [objectA.prop, objectB.prop, objectC.prop]
3871 expect(value).to.equal 'newer+new+current+current'
3872
3873 restartSandbox()
3874
3875
3876
3877 test "Will not allow additional binding dependents after being called", ()->
3878 expect(()->
3879 SimplyBind('prop1').of(objectA)
3880 .to('prop1').of(objectB)
3881 .and('prop2').of(objectB)
3882 .and('prop3').of(objectB)
3883 .transformAll (value)-> value
3884 .and('prop4').of(objectB)
3885 ).to.throw()
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905 suite ".transformSelf()", ()->
3906 suiteSetup(restartSandbox)
3907
3908 suite "Will transform any received value", ()->
3909 test "ObjectProp", ()->
3910 object = 'prop':'value'
3911
3912 SimplyBind('prop').of(object)
3913 .transformSelf (value)-> value.toUpperCase()
3914
3915 expect(object.prop).to.equal 'VALUE'
3916
3917 object.prop = 'anotherValue'
3918 expect(object.prop).to.equal 'ANOTHERVALUE'
3919
3920
3921
3922 test "DOMText", ()-> if not isBrowser then @skip() else
3923 regA.textContent = 'value'
3924
3925 SimplyBind('textContent').of(regA)
3926 .transformSelf (value)-> value.toUpperCase()
3927
3928 expect(regA.textContent).to.equal 'VALUE'
3929
3930 SimplyBind('textContent').of(regA).update('anotherValue')
3931 expect(regA.textContent).to.equal 'ANOTHERVALUE'
3932
3933 restartSandbox()
3934
3935
3936
3937 test "DOMAttr", ()-> if not isBrowser then @skip() else
3938 regA.setAttribute 'someattr', 'value'
3939
3940 SimplyBind('attr:someattr').of(regA)
3941 .transformSelf (value)-> value.toUpperCase()
3942
3943 expect(regA.getAttribute 'someattr').to.equal 'VALUE'
3944
3945 SimplyBind('attr:someattr').of(regA).update('anotherValue')
3946 expect(regA.getAttribute 'someattr').to.equal 'ANOTHERVALUE'
3947
3948 restartSandbox()
3949
3950
3951
3952 test "DOMValue", ()-> if not isBrowser then @skip() else
3953 inputA.value = 'value'
3954
3955 SimplyBind('value').of(inputA)
3956 .transformSelf (value)-> value.toUpperCase()
3957
3958 expect(inputA.value).to.equal 'VALUE'
3959
3960 inputA.value = 'anotherValue'
3961 inputA.emit 'change'
3962 expect(inputA.value).to.equal 'ANOTHERVALUE'
3963
3964 restartSandbox()
3965
3966
3967
3968 test "Array", ()->
3969 expect ()-> SimplyBind(arrayA).transformSelf (v)->v
3970 .to.throw()
3971
3972 restartSandbox()
3973
3974
3975
3976 test "Function", ()->
3977 dispatcher = 'prop':'value'
3978 receiver = 'prop':'value'
3979 fn = (value)-> value
3980
3981 SimplyBind(fn).transformSelf (value)-> value?.toUpperCase()
3982 SimplyBind('prop').of(dispatcher)
3983 .to(fn)
3984 .chainTo('prop').of(receiver)
3985
3986 expect(dispatcher.prop).to.equal 'value'
3987 expect(receiver.prop).to.equal 'VALUE'
3988
3989 dispatcher.prop = 'anotherValue'
3990 expect(dispatcher.prop).to.equal 'anotherValue'
3991 expect(receiver.prop).to.equal 'ANOTHERVALUE'
3992
3993
3994
3995 test "Event", ()->
3996 dispatcher = 'prop':eventEmitterA
3997 receiver = 'prop':'N/A'
3998 eventEmitterA.type = 'someEvent'
3999 eventEmitterB.type = 'someEvent'
4000
4001 SimplyBind(0).ofEvent('someEvent').of(eventEmitterA)
4002 .transformSelf (event)-> event?.type or event
4003
4004 SimplyBind('prop').of(dispatcher)
4005 .toEvent('someEvent').of(eventEmitterA)
4006 .chainTo('prop').of(receiver)
4007
4008 expect(dispatcher.prop).to.equal eventEmitterA
4009 expect(receiver.prop).to.equal 'N/A'
4010
4011 receiver.prop = 'reset'
4012 dispatcher.prop = eventEmitterB
4013 expect(dispatcher.prop).to.equal eventEmitterB
4014 expect(receiver.prop).to.equal 'someEvent'
4015
4016
4017
4018
4019
4020
4021 suite "Will not transform value on bind if SimplyBind.options.updateOnBind is off", ()->
4022 suiteSetup ()-> SimplyBind.setOption 'updateOnBind', false
4023 suiteTeardown ()-> SimplyBind.setOption 'updateOnBind', true
4024
4025 test "ObjectProp", ()->
4026 object = 'prop':'value'
4027
4028 SimplyBind('prop').of(object)
4029 .transformSelf (value)-> value.toUpperCase()
4030
4031 expect(object.prop).to.equal 'value'
4032
4033 object.prop = 'anotherValue'
4034 expect(object.prop).to.equal 'ANOTHERVALUE'
4035
4036
4037
4038 test "DOMText", ()-> if not isBrowser then @skip() else
4039 regA.textContent = 'value'
4040
4041 SimplyBind('textContent').of(regA)
4042 .transformSelf (value)-> value.toUpperCase()
4043
4044 expect(regA.textContent).to.equal 'value'
4045
4046 SimplyBind('textContent').of(regA).update('anotherValue')
4047 expect(regA.textContent).to.equal 'ANOTHERVALUE'
4048
4049 restartSandbox()
4050
4051
4052
4053 test "DOMAttr", ()-> if not isBrowser then @skip() else
4054 regA.setAttribute 'someattr', 'value'
4055
4056 SimplyBind('attr:someattr').of(regA)
4057 .transformSelf (value)-> value.toUpperCase()
4058
4059 expect(regA.getAttribute 'someattr').to.equal 'value'
4060
4061 SimplyBind('attr:someattr').of(regA).update('anotherValue')
4062 expect(regA.getAttribute 'someattr').to.equal 'ANOTHERVALUE'
4063
4064 restartSandbox()
4065
4066
4067
4068 test "DOMValue", ()-> if not isBrowser then @skip() else
4069 inputA.value = 'value'
4070
4071 SimplyBind('value').of(inputA)
4072 .transformSelf (value)-> value.toUpperCase()
4073
4074 expect(inputA.value).to.equal 'value'
4075
4076 inputA.value = 'anotherValue'
4077 inputA.emit 'change'
4078 expect(inputA.value).to.equal 'ANOTHERVALUE'
4079
4080 restartSandbox()
4081
4082
4083
4084 test "Function", ()->
4085 dispatcher = 'prop':'value'
4086 receiver = 'prop':'value'
4087 fn = (value)-> value
4088
4089 SimplyBind(fn).transformSelf (value)-> value?.toUpperCase()
4090 SimplyBind('prop').of(dispatcher)
4091 .to(fn)
4092 .chainTo('prop').of(receiver)
4093
4094 expect(dispatcher.prop).to.equal 'value'
4095 expect(receiver.prop).to.be.undefined
4096
4097 dispatcher.prop = 'anotherValue'
4098 expect(dispatcher.prop).to.equal 'anotherValue'
4099 expect(receiver.prop).to.equal 'ANOTHERVALUE'
4100 restartSandbox()
4101
4102
4103
4104 test "Event", ()->
4105 dispatcher = 'prop':eventEmitterA
4106 receiver = 'prop':'N/A'
4107 eventEmitterA.type = 'someEvent'
4108 eventEmitterB.type = 'someEvent'
4109
4110 SimplyBind(0).ofEvent('someEvent').of(eventEmitterA)
4111 .transformSelf (event)-> event?.type or event
4112
4113 SimplyBind('prop').of(dispatcher)
4114 .toEvent('someEvent').of(eventEmitterA)
4115 .chainTo('prop').of(receiver)
4116
4117 expect(dispatcher.prop).to.equal eventEmitterA
4118 expect(receiver.prop).to.equal 'N/A'
4119
4120 receiver.prop = 'reset'
4121 dispatcher.prop = eventEmitterB
4122 expect(dispatcher.prop).to.equal eventEmitterB
4123 expect(receiver.prop).to.equal 'someEvent'
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144 suite ".condition()", ()->
4145 suiteSetup(restartSandbox)
4146
4147 test "Will pass the value to the condition before updating the dependent and will update the dep only if the return value is truthy", ()->
4148 shouldAllowUpdate = false
4149 objectA.prop1 = objectB.prop1 = 0
4150
4151 SimplyBind('prop1').of(objectA)
4152 .to('prop1').of(objectB)
4153 .condition ()-> shouldAllowUpdate
4154
4155 objectA.prop1 = 1
4156 expect(objectB.prop1).to.equal 0
4157
4158 objectA.prop1 = 2
4159 expect(objectB.prop1).to.equal 0
4160
4161 shouldAllowUpdate = true
4162
4163 objectA.prop1 = 3
4164 expect(objectB.prop1).to.equal 3
4165
4166 restartSandbox()
4167
4168
4169
4170 test "Will update the dependent upon initial bind regardless of the condition unless SimplyBind.options.updateOnBind is on", ()->
4171 shouldAllowUpdate = false
4172 objectA.prop1 = objectA.prop2 = 1
4173 objectB.prop1 = objectB.prop2 = 0
4174
4175 SimplyBind('prop1').of(objectA)
4176 .to('prop1').of(objectB)
4177 .condition ()-> shouldAllowUpdate
4178
4179 expect(objectA.prop1).to.equal 1
4180 expect(objectB.prop1).to.equal 1
4181
4182 objectA.prop1 = 2
4183 expect(objectB.prop1).to.equal 1
4184
4185
4186
4187 SimplyBind.setOption 'updateOnBind', false
4188
4189 SimplyBind('prop2').of(objectA)
4190 .to('prop2').of(objectB)
4191 .condition ()-> shouldAllowUpdate
4192
4193 expect(objectA.prop2).to.equal 1
4194 expect(objectB.prop2).to.equal 0
4195
4196 SimplyBind.setOption 'updateOnBind', true
4197 restartSandbox()
4198
4199
4200
4201 test "Condition will be added only to the last-proxied dependent", ()->
4202 shouldAllowUpdate = false
4203 objectA.prop1 = objectB.prop1 = objectC.prop1 = 0
4204
4205 SimplyBind('prop1').of(objectA)
4206 .to('prop1').of(objectB).condition ()-> shouldAllowUpdate
4207 .and('prop1').of(objectC)
4208
4209 objectA.prop1 = 1
4210 expect(objectB.prop1).to.equal 0
4211 expect(objectC.prop1).to.equal 1
4212
4213 objectA.prop1 = 2
4214 expect(objectB.prop1).to.equal 0
4215 expect(objectC.prop1).to.equal 2
4216
4217 shouldAllowUpdate = true
4218
4219 objectA.prop1 = 3
4220 expect(objectB.prop1).to.equal 3
4221 expect(objectC.prop1).to.equal 3
4222
4223 restartSandbox()
4224
4225
4226
4227 test "The condition will be added both ways if declared after the .bothWays() call", ()->
4228 shouldAllowUpdate = false
4229 objectA.prop1 = objectB.prop1 = 0
4230
4231 SimplyBind('prop1').of(objectA)
4232 .to('prop1').of(objectB).bothWays()
4233 .condition ()-> shouldAllowUpdate
4234
4235 objectA.prop1 = 1
4236 expect(objectA.prop1).to.equal 1
4237 expect(objectB.prop1).to.equal 0
4238
4239 objectB.prop1 = 2
4240 expect(objectA.prop1).to.equal 1
4241 expect(objectB.prop1).to.equal 2
4242
4243 shouldAllowUpdate = true
4244
4245 objectA.prop1 = 3
4246 expect(objectA.prop1).to.equal 3
4247 expect(objectB.prop1).to.equal 3
4248
4249 objectB.prop1 = 4
4250 expect(objectA.prop1).to.equal 4
4251 expect(objectB.prop1).to.equal 4
4252
4253 restartSandbox()
4254
4255
4256
4257 test "The condition will be added both ways if declared before the .bothWays() call", ()->
4258 shouldAllowUpdate = false
4259 objectA.prop1 = objectB.prop1 = 0
4260
4261 SimplyBind('prop1').of(objectA)
4262 .to('prop1').of(objectB)
4263 .condition ()-> shouldAllowUpdate
4264 .bothWays()
4265
4266 objectA.prop1 = 1
4267 expect(objectA.prop1).to.equal 1
4268 expect(objectB.prop1).to.equal 0
4269
4270 objectB.prop1 = 2
4271 expect(objectA.prop1).to.equal 1
4272 expect(objectB.prop1).to.equal 2
4273
4274 shouldAllowUpdate = true
4275
4276 objectA.prop1 = 3
4277 expect(objectA.prop1).to.equal 3
4278 expect(objectB.prop1).to.equal 3
4279
4280 objectB.prop1 = 4
4281 expect(objectA.prop1).to.equal 4
4282 expect(objectB.prop1).to.equal 4
4283
4284 restartSandbox()
4285
4286
4287
4288 test "If a the dependent has a transform and a condition, the new value will be run through the transform prior to testing the condition", ()->
4289 invokeCount = 0
4290 shouldAllowUpdate = true
4291
4292 SimplyBind('prop1').of(objectA)
4293 .to('prop1').of(objectB)
4294 .transform (val)-> invokeCount++; val
4295 .condition ()-> shouldAllowUpdate
4296
4297 expect(objectB.prop1).to.equal(objectA.prop1)
4298 expect(invokeCount).to.equal 1
4299
4300 objectA.prop1 = 2
4301 expect(objectB.prop1).to.equal(objectA.prop1)
4302 expect(invokeCount).to.equal 2
4303
4304 shouldAllowUpdate = false
4305 objectA.prop1 = 3
4306 expect(objectB.prop1).not.to.equal(objectA.prop1)
4307 expect(invokeCount).to.equal 3
4308
4309 objectA.prop1 = 4
4310 expect(objectB.prop1).not.to.equal(objectA.prop1)
4311 expect(invokeCount).to.equal 4
4312
4313 restartSandbox()
4314
4315
4316
4317 test "If a binding has a placeholder, the condition will be used only for that placeholder", ()->
4318 dispatcher = 'prop':''
4319 receiver = 'prop':'This {{noun}} works'
4320 lastPassedNoun = null
4321 shouldAllowUpdate = true
4322
4323 SimplyBind('prop').of(dispatcher)
4324 .to('prop.noun').of(receiver)
4325 .condition (noun)-> lastPassedNoun=noun; return shouldAllowUpdate
4326
4327 dispatcher.prop = 'test1'
4328 expect(lastPassedNoun).to.equal 'test1'
4329 expect(receiver.prop).to.equal 'This test1 works'
4330
4331 shouldAllowUpdate = false
4332 dispatcher.prop = 'test2'
4333 expect(lastPassedNoun).to.equal 'test2'
4334 expect(receiver.prop).to.equal 'This test1 works'
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351 suite ".conditionAll()", ()->
4352 suiteSetup(restartSandbox)
4353
4354 test "Behaves the same way as .condition() does, except it applies the condition to all of the declared dependents", ()->
4355 shouldAllowUpdate = false
4356 dispatcher = 'prop':0
4357 objectA.prop1 = objectB.prop1 = objectC.prop1 = 0
4358
4359 SimplyBind('prop').of(dispatcher)
4360 .to('prop1').of(objectA)
4361 .and('prop1').of(objectB)
4362 .and('prop1').of(objectC)
4363 .conditionAll ()-> shouldAllowUpdate
4364
4365 dispatcher.prop = 1
4366 expect(objectA.prop1).to.equal 0
4367 expect(objectB.prop1).to.equal 0
4368 expect(objectC.prop1).to.equal 0
4369
4370 dispatcher.prop = 2
4371 expect(objectA.prop1).to.equal 0
4372 expect(objectB.prop1).to.equal 0
4373 expect(objectC.prop1).to.equal 0
4374
4375 shouldAllowUpdate = true
4376
4377 dispatcher.prop = 3
4378 expect(objectA.prop1).to.equal 3
4379 expect(objectB.prop1).to.equal 3
4380 expect(objectC.prop1).to.equal 3
4381
4382 restartSandbox()
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402 suite ".bothWays()", ()->
4403 suiteSetup(restartSandbox)
4404 test "Create two-way bindings", ()->
4405 # ==== Objects =================================================================================
4406 SimplyBind('prop').of(objectA).to('prop').of(objectB).bothWays()
4407 expect(objectA.prop).to.be.undefined
4408
4409 objectA.prop = 'from objectA'
4410 expect(objectA.prop).to.equal 'from objectA'
4411 expect(objectB.prop).to.equal 'from objectA'
4412
4413 objectB.prop = 'from objectB'
4414 expect(objectA.prop).to.equal 'from objectB'
4415 expect(objectB.prop).to.equal 'from objectB'
4416
4417
4418 # ==== Arrays =================================================================================
4419 SimplyBind(arrayA).to(arrayB).bothWays()
4420 expect(arrayA.length).to.equal 10
4421 expect(arrayB.length).to.equal 10
4422
4423 arrayA.push(1)
4424 arrayA.push(1)
4425 expect(arrayA.length).to.equal 12
4426 expect(arrayB.length).to.equal 10
4427
4428 arrayB.push(1)
4429 arrayB.push(1)
4430 expect(arrayA.length).to.equal 12
4431 expect(arrayB.length).to.equal 12
4432
4433
4434 if isBrowser
4435 # ==== DOMAttr =================================================================================
4436 SimplyBind('attr:someattr').of(regA).to('attr:someattr').of(regB).bothWays()
4437 expect(regA.getAttribute('someattr')).to.equal null
4438
4439 SimplyBind('attr:someattr').of(regA).set 'from regElementA'
4440 expect(regA.getAttribute('someattr')).to.equal 'from regElementA'
4441 expect(regB.getAttribute('someattr')).to.equal 'from regElementA'
4442
4443 SimplyBind('attr:someattr').of(regB).set 'from regElementB'
4444 expect(regA.getAttribute('someattr')).to.equal 'from regElementB'
4445 expect(regB.getAttribute('someattr')).to.equal 'from regElementB'
4446
4447
4448 # ==== DOMText =================================================================================
4449 regA.textContent = ''
4450 SimplyBind('textContent').of(regA).to('textContent').of(regB).bothWays()
4451 expect(regA.textContent).to.equal ''
4452
4453 SimplyBind('textContent').of(regA).set 'from regElementA'
4454 expect(regA.textContent).to.equal 'from regElementA'
4455 expect(regB.textContent).to.equal 'from regElementA'
4456
4457 SimplyBind('textContent').of(regB).set 'from regElementB'
4458 expect(regA.textContent).to.equal 'from regElementB'
4459 expect(regB.textContent).to.equal 'from regElementB'
4460
4461
4462 # ==== DOMValue =================================================================================
4463 SimplyBind('value').of(inputA).to('value').of(inputB).bothWays()
4464 expect(inputA.value).to.equal ''
4465
4466 inputA.value = 'from inputA'
4467 inputA.emit 'change'
4468 expect(inputA.value).to.equal 'from inputA'
4469 expect(inputB.value).to.equal 'from inputA'
4470
4471 inputB.value = 'from inputB'
4472 inputB.emit 'change'
4473 expect(inputA.value).to.equal 'from inputB'
4474 expect(inputB.value).to.equal 'from inputB'
4475
4476
4477 # ==== DOMCheckbox =================================================================================
4478 binding = SimplyBind('checked').of(checkboxFields).to('prop2').of(objectB).bothWays()
4479 expect(objectB.prop2).to.be.instanceOf Array
4480 expect(objectB.prop2.length).to.equal 0
4481
4482 objectB.prop2 = 'changed from objectB'
4483 expect($checkboxFields.filter(':checked').length).to.equal 0
4484 expect(binding.value).to.be.instanceOf Array
4485 expect(binding.value.length).to.equal 0
4486
4487 objectB.prop2 = 'checkboxB'
4488 expect($checkboxFields.filter(':checked').val()).to.equal 'checkboxB'
4489 expect(binding.value.length).to.equal 1
4490
4491 objectB.prop2 = ['checkboxA', 'checkboxC']
4492 expect($checkboxFields.filter(':checked').length).to.equal 2
4493 expect(binding.value.length).to.equal 2
4494
4495 $checkboxA.click()
4496 expect(objectB.prop2).to.be.instanceOf Array
4497 expect(objectB.prop2.length).to.equal 1
4498 expect(objectB.prop2[0]).to.equal 'checkboxC'
4499
4500 restartSandbox()
4501
4502
4503 # ==== DOMRadio =================================================================================
4504 SimplyBind('checked').of(radioFields).to('prop3').of(objectB).bothWays()
4505
4506 objectB.prop3 = 'changed from objectB'
4507 expect($radioFields.filter(':checked').length).to.equal 0
4508
4509 objectB.prop3 = 'radioB'
4510 expect($radioFields.filter(':checked').val()).to.equal 'radioB'
4511
4512 $radioA.click()
4513 expect(objectB.prop3).to.equal 'radioA'
4514
4515 restartSandbox()
4516
4517
4518
4519 SimplyBind.setOption('updateOnBind', true)
4520 restartSandbox()
4521
4522
4523
4524
4525
4526
4527
4528 test "Will apply the call only to the last proxied dependent", ()->
4529 restartSandbox()
4530 SimplyBind('prop1').of(objectA)
4531 .to('prop1').of(objectB).bothWays()
4532 .and('prop2').of(objectB)
4533 .and('prop3').of(objectB).bothWays()
4534 .and('prop4').of objectB
4535
4536 objectA.prop1 = 'all at once'
4537 expect(objectB.prop1).to.equal 'all at once'
4538 expect(objectB.prop2).to.equal 'all at once'
4539 expect(objectB.prop3).to.equal 'all at once'
4540 expect(objectB.prop4).to.equal 'all at once'
4541
4542 objectB.prop1 = 'almost all as well since objectA.prop1 gets updated and updates all of its decendents'
4543 expect(objectA.prop1).to.equal 'almost all as well since objectA.prop1 gets updated and updates all of its decendents'
4544 expect(objectB.prop2).to.equal 'almost all as well since objectA.prop1 gets updated and updates all of its decendents'
4545 expect(objectB.prop3).to.equal 'almost all as well since objectA.prop1 gets updated and updates all of its decendents'
4546 expect(objectB.prop4).to.equal 'almost all as well since objectA.prop1 gets updated and updates all of its decendents'
4547
4548 objectB.prop3 = 'just a few'
4549 expect(objectA.prop1).to.equal 'just a few'
4550 expect(objectB.prop1).to.equal 'just a few'
4551 expect(objectB.prop2).to.equal 'just a few'
4552 expect(objectB.prop4).to.equal 'just a few'
4553
4554
4555
4556
4557
4558
4559 suite "DOMValue other than input", ()->
4560 suiteSetup ()-> if not isBrowser then @skip()
4561 test "Create two-way bindings between textarea fields", ()->
4562 SimplyBind('value').of(textarea).to('value').of(inputB).bothWays()
4563
4564 $textarea.val('changed from input1')[0].emit 'change'
4565 expect($inputB.val()).to.equal 'changed from input1'
4566
4567 $inputB.val('changed from input2')[0].emit 'change'
4568 expect($textarea.val()).to.equal 'changed from input2'
4569
4570
4571
4572
4573 test "Create two-way bindings between select fields", ()->
4574 SimplyBind('value').of($select).to('prop3').of(objectB).bothWays()
4575
4576 objectB.prop3 = 'valid'
4577 expect($select.val()).to.equal 'valid'
4578
4579 $select.val('valid2')[0].emit 'change'
4580 expect(objectB.prop3).to.equal 'valid2'
4581 restartSandbox()
4582
4583
4584
4585 # suite "DOMRadio", ()->
4586 # test "Create two-way bindings between radio fields", ()->
4587 # SimplyBind('value').of($('#radio').children()).to('prop3').of(objectB).bothWays()
4588
4589 # objectB.prop3 = 'changed from objectB'
4590 # expect($('input:checked').length).to.equal 0
4591
4592 # objectB.prop3 = 'radioB'
4593 # expect($('input:checked').val()).to.equal 'radioB'
4594
4595 # $radioA.click()[0].emit 'change'
4596 # expect(objectB.prop3).to.equal 'radioA'
4597
4598 # restartSandbox()
4599
4600
4601
4602 # suite "DOMCheckbox", ()->
4603 # test "Create two-way bindings between checkbox fields", ()->
4604 # $checkboxA.prop 'checked', true
4605 # SimplyBind('value').of($('#checkbox')[0].children).to('prop3').of(objectB).bothWays()
4606
4607 # objectB.prop3 = 'changed from objectB'
4608 # expect($('input:checked').length).to.equal 0
4609
4610 # objectB.prop3 = 'checkboxB'
4611 # expect($('input:checked').val()).to.equal 'checkboxB'
4612
4613 # $checkboxA.click()[0].emit 'change'
4614 # expect(objectB.prop3).to.equal 'checkboxA'
4615
4616 # restartSandbox()
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635 suite ".unBind()", ()->
4636 suiteSetup(restartSandbox)
4637 suite "Will unbind from the all defined proxied dependents one-way", ()->
4638 test "ObjectProp", ()->
4639 binding = SimplyBind('prop1').of(objectA)
4640 .to('prop1').of(objectB).bothWays()
4641
4642 objectA.prop1 = 1
4643 expect(objectB.prop1).to.equal 1
4644
4645 objectB.prop1 = 2
4646 expect(objectA.prop1).to.equal 2
4647
4648 binding.unBind()
4649
4650 objectA.prop1 = 3
4651 expect(objectB.prop1).to.equal 2
4652
4653 objectB.prop1 = 4
4654 expect(objectA.prop1).to.equal 4
4655
4656 restartSandbox()
4657
4658
4659
4660 test "DOMText", ()-> if not isBrowser then @skip() else
4661 binding = SimplyBind('textContent').of(regA)
4662 .to('textContent').of(regB).bothWays()
4663
4664 SimplyBind('textContent').of(regA).set '1'
4665 expect(regB.textContent).to.equal '1'
4666
4667 SimplyBind('textContent').of(regB).set '2'
4668 expect(regA.textContent).to.equal '2'
4669
4670 binding.unBind()
4671
4672 SimplyBind('textContent').of(regA).set '3'
4673 expect(regB.textContent).to.equal '2'
4674
4675 SimplyBind('textContent').of(regB).set '4'
4676 expect(regA.textContent).to.equal '4'
4677
4678 restartSandbox()
4679
4680
4681
4682 test "DOMAttr", ()-> if not isBrowser then @skip() else
4683 binding = SimplyBind('attr:someattr').of(regA)
4684 .to('attr:someattr').of(regB).bothWays()
4685
4686 SimplyBind('attr:someattr').of(regA).set '1'
4687 expect(regB.getAttribute 'someattr').to.equal '1'
4688
4689 SimplyBind('attr:someattr').of(regB).set '2'
4690 expect(regA.getAttribute 'someattr').to.equal '2'
4691
4692 binding.unBind()
4693
4694 SimplyBind('attr:someattr').of(regA).set '3'
4695 expect(regB.getAttribute 'someattr').to.equal '2'
4696
4697 SimplyBind('attr:someattr').of(regB).set '4'
4698 expect(regA.getAttribute 'someattr').to.equal '4'
4699
4700 restartSandbox()
4701
4702
4703
4704 test "DOMValue", ()-> if not isBrowser then @skip() else
4705 binding = SimplyBind('value').of(inputA)
4706 .to('value').of(inputB).bothWays()
4707
4708 inputA.value = '1'
4709 inputA.emit 'change'
4710 expect(inputB.value).to.equal '1'
4711
4712 inputB.value = '2'
4713 inputB.emit 'change'
4714 expect(inputA.value).to.equal '2'
4715
4716 binding.unBind()
4717
4718 inputA.value = '3'
4719 inputA.emit 'change'
4720 expect(inputB.value).to.equal '2'
4721
4722 inputB.value = '4'
4723 inputB.emit 'change'
4724 expect(inputA.value).to.equal '4'
4725
4726 restartSandbox()
4727
4728
4729
4730 test "Array", ()->
4731 binding = SimplyBind(arrayA, {'trackArrayChildren':true})
4732 .to('prop1').of(objectA)
4733
4734 expect(objectA.prop1).to.equal arrayA
4735
4736 objectA.prop1 = 'reset'
4737 arrayA.push(1)
4738 expect(objectA.prop1).to.equal arrayA
4739
4740 objectA.prop1 = 'reset'
4741 arrayA[5] = 19
4742 expect(objectA.prop1).to.equal arrayA
4743
4744
4745 objectA.prop1 = 'reset'
4746 binding.unBind()
4747
4748 arrayA.push(1)
4749 arrayA[3] = 23
4750 expect(objectA.prop1).to.equal 'reset'
4751
4752 restartSandbox()
4753
4754
4755
4756 test "Function", ()->
4757 passedValue = null
4758 valueToPass = 1
4759 takeFromReceiver = false
4760 fn = (fromReceiver)-> passedValue = if takeFromReceiver then fromReceiver else valueToPass
4761 invoker = 'prop':'value'
4762 receiver = 'prop':undefined
4763
4764 SimplyBind('prop').of(invoker).to(fn)
4765 binding = SimplyBind(fn).to('prop').of(receiver).bothWays()
4766
4767 expect(receiver.prop).to.equal 1
4768 expect(passedValue).to.equal 1
4769
4770 valueToPass = 5
4771 invoker.prop = 'anotherValue'
4772 expect(receiver.prop).to.equal 5
4773 expect(passedValue).to.equal 5
4774
4775 takeFromReceiver = true
4776 receiver.prop = 10
4777 takeFromReceiver = false
4778 expect(passedValue).to.equal 10
4779
4780
4781 binding.unBind()
4782 valueToPass = 15
4783 invoker.prop = 'newValue'
4784 expect(receiver.prop).to.equal 10
4785 expect(passedValue).to.equal 15
4786
4787 takeFromReceiver = true
4788 receiver.prop = 20
4789 takeFromReceiver = false
4790 expect(passedValue).to.equal 20
4791
4792
4793
4794 test "Event", ()->
4795 eventInvokeCount = 0
4796 eventObject = null
4797 objectA.prop = objectB.prop = 'someValue'
4798 eventEmitterA.on 'someEvent', (event)-> eventObject = event or @
4799
4800 binding = SimplyBind(0).ofEvent('someEvent').of(eventEmitterA).transformSelf ()-> eventObject
4801 .to('prop').of(objectA).bothWays()
4802 .and('prop').of(objectB).bothWays()
4803 .and ()-> eventInvokeCount++
4804
4805
4806 eventEmitterA.emit 'someEvent'
4807 expect(objectA.prop).to.equal eventObject
4808 expect(objectB.prop).to.equal(objectA.prop)
4809 expect(eventInvokeCount).to.equal 1
4810
4811 objectA.prop = 'different value'
4812 expect(objectA.prop).to.equal 'different value'
4813 # expect(objectB.prop).to.equal eventObject
4814 expect(eventInvokeCount).to.equal 2
4815
4816 objectB.prop = 'different value2'
4817 expect(eventInvokeCount).to.equal 3
4818
4819
4820 binding.unBind()
4821 objectA.prop = 'different value'
4822 eventEmitterA.emit 'someEvent'
4823 eventEmitterA.on 'someEvent', ()-> eventInvokeCount++
4824
4825 expect(objectA.prop).to.equal 'different value'
4826 expect(objectB.prop).to.equal 'different value2'
4827 expect(eventInvokeCount).to.equal 3
4828
4829
4830 objectA.prop = 'another value'
4831 expect(objectA.prop).to.equal 'another value'
4832 expect(objectB.prop).to.equal 'different value2'
4833
4834 objectB.prop = 'another value2'
4835 expect(objectA.prop).to.equal 'another value'
4836 expect(objectB.prop).to.equal 'another value2'
4837 expect(eventInvokeCount).to.equal 5
4838 expect(binding._.deps.length).to.equal 0
4839
4840 restartSandbox()
4841
4842
4843
4844 suite "Will unbind from the all defined proxied dependents both-ways", ()->
4845 test "ObjectProp", ()->
4846 binding = SimplyBind('prop1').of(objectA)
4847 .to('prop1').of(objectB).bothWays()
4848
4849 objectA.prop1 = 1
4850 expect(objectB.prop1).to.equal 1
4851
4852 objectB.prop1 = 2
4853 expect(objectA.prop1).to.equal 2
4854
4855 binding.unBind(true)
4856
4857 objectA.prop1 = 3
4858 expect(objectB.prop1).to.equal 2
4859
4860 objectB.prop1 = 4
4861 expect(objectA.prop1).to.equal 3
4862
4863 restartSandbox()
4864
4865
4866
4867 test "DOMText", ()-> if not isBrowser then @skip() else
4868 binding = SimplyBind('textContent').of(regA)
4869 .to('textContent').of(regB).bothWays()
4870
4871 SimplyBind('textContent').of(regA).set '1'
4872 expect(regB.textContent).to.equal '1'
4873
4874 SimplyBind('textContent').of(regB).set '2'
4875 expect(regA.textContent).to.equal '2'
4876
4877 binding.unBind(true)
4878
4879 SimplyBind('textContent').of(regA).set '3'
4880 expect(regB.textContent).to.equal '2'
4881
4882 SimplyBind('textContent').of(regB).set '4'
4883 expect(regA.textContent).to.equal '3'
4884
4885 restartSandbox()
4886
4887
4888
4889 test "DOMAttr", ()-> if not isBrowser then @skip() else
4890 binding = SimplyBind('attr:someattr').of(regA)
4891 .to('attr:someattr').of(regB).bothWays()
4892
4893 SimplyBind('attr:someattr').of(regA).set '1'
4894 expect(regB.getAttribute 'someattr').to.equal '1'
4895
4896 SimplyBind('attr:someattr').of(regB).set '2'
4897 expect(regA.getAttribute 'someattr').to.equal '2'
4898
4899 binding.unBind(true)
4900
4901 SimplyBind('attr:someattr').of(regA).set '3'
4902 expect(regB.getAttribute 'someattr').to.equal '2'
4903
4904 SimplyBind('attr:someattr').of(regB).set '4'
4905 expect(regA.getAttribute 'someattr').to.equal '3'
4906
4907 restartSandbox()
4908
4909
4910
4911 test "DOMValue", ()-> if not isBrowser then @skip() else
4912 binding = SimplyBind('value').of(inputA)
4913 .to('value').of(inputB).bothWays()
4914
4915 inputA.value = '1'
4916 inputA.emit 'change'
4917 expect(inputB.value).to.equal '1'
4918
4919 inputB.value = '2'
4920 inputB.emit 'change'
4921 expect(inputA.value).to.equal '2'
4922
4923 binding.unBind(true)
4924
4925 inputA.value = '3'
4926 inputA.emit 'change'
4927 expect(inputB.value).to.equal '2'
4928
4929 inputB.value = '4'
4930 inputB.emit 'change'
4931 expect(inputA.value).to.equal '3'
4932
4933 restartSandbox()
4934
4935
4936
4937 test "Function", ()->
4938 passedValue = null
4939 valueToPass = 1
4940 takeFromReceiver = false
4941 fn = (fromReceiver)-> passedValue = if takeFromReceiver then fromReceiver else valueToPass
4942 invoker = 'prop':'value'
4943 receiver = 'prop':undefined
4944
4945 SimplyBind('prop').of(invoker).to(fn)
4946 binding = SimplyBind(fn).to('prop').of(receiver).bothWays()
4947
4948 expect(receiver.prop).to.equal 1
4949 expect(passedValue).to.equal 1
4950
4951 valueToPass = 5
4952 invoker.prop = 'anotherValue'
4953 expect(receiver.prop).to.equal 5
4954 expect(passedValue).to.equal 5
4955
4956 takeFromReceiver = true
4957 receiver.prop = 10
4958 takeFromReceiver = false
4959 expect(passedValue).to.equal 10
4960
4961
4962 binding.unBind(true)
4963 valueToPass = 15
4964 invoker.prop = 'newValue'
4965 expect(receiver.prop).to.equal 10
4966 expect(passedValue).to.equal 15
4967
4968 takeFromReceiver = true
4969 receiver.prop = 20
4970 takeFromReceiver = false
4971 expect(passedValue).to.equal 15
4972
4973
4974
4975 test "Event", ()->
4976 eventInvokeCount = 0
4977 eventObject = null
4978 objectA.prop = objectB.prop = 'someValue'
4979 eventEmitterA.on 'someEvent', (event)-> eventObject = event or @
4980
4981 binding = SimplyBind(0).ofEvent('someEvent').of(eventEmitterA).transformSelf ()-> eventObject
4982 .to('prop').of(objectA).bothWays()
4983 .and('prop').of(objectB).bothWays()
4984 .and ()-> eventInvokeCount++
4985
4986
4987 eventEmitterA.emit 'someEvent'
4988 expect(objectA.prop).to.equal eventObject
4989 expect(objectB.prop).to.equal(objectA.prop)
4990 expect(eventInvokeCount).to.equal 1
4991
4992
4993 objectA.prop = 'different value'
4994 expect(objectA.prop).to.equal 'different value'
4995 # expect(objectB.prop).to.equal eventObject
4996 expect(eventInvokeCount).to.equal 2
4997
4998 objectB.prop = 'different value2'
4999 expect(eventInvokeCount).to.equal 3
5000
5001
5002 binding.unBind(true)
5003 objectA.prop = 'different value'
5004 eventEmitterA.emit 'someEvent'
5005 eventEmitterA.on 'someEvent', ()-> eventInvokeCount++
5006
5007 expect(objectA.prop).to.equal 'different value'
5008 expect(objectB.prop).to.equal 'different value2'
5009 expect(eventInvokeCount).to.equal 3
5010
5011
5012 objectA.prop = 'another value'
5013 expect(objectA.prop).to.equal 'another value'
5014 expect(objectB.prop).to.equal 'different value2'
5015
5016 objectB.prop = 'another value2'
5017 expect(objectA.prop).to.equal 'another value'
5018 expect(objectB.prop).to.equal 'another value2'
5019 expect(eventInvokeCount).to.equal 3
5020 expect(binding._.deps.length).to.equal 0
5021
5022 restartSandbox()
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046 suite ".pollEvery() + .stopPolling()", ()->
5047 suiteSetup(restartSandbox)
5048 test 'Will cause the value to be manually polled from the subject at a given interval', (done)->
5049 shouldFinish = false
5050 lastValue = undefined
5051 binding = undefined
5052 arr = []
5053
5054 fn = (length)->
5055 if shouldFinish
5056 expect(length).to.equal 2
5057 binding.stopPolling()
5058 done()
5059 else
5060 lastValue = length
5061
5062
5063
5064 binding = SimplyBind('length').of(arr).to(fn)
5065 expect(lastValue).to.equal 0
5066 arr.push 'some value'
5067 arr.push 'and another'
5068 expect(lastValue).to.equal 0
5069 arr = []
5070 binding = SimplyBind('length').of(arr).to(fn).pollEvery(5)
5071
5072 setTimeout ()->
5073 shouldFinish = true
5074 expect(lastValue).to.equal 0
5075 arr.push 'some value'
5076 arr.push 'and another'
5077 expect(lastValue).to.equal 0
5078 , 8
5079
5080
5081
5082 test 'Will invoke a function upon poll', (done)->
5083 invokeCountA = 0
5084 invokeCountB = 0
5085
5086 binding = SimplyBind(()-> ++invokeCountA).to(()-> ++invokeCountB).pollEvery(5)
5087 setTimeout ()->
5088 expect(invokeCountA).to.be.gt(1)
5089 expect(invokeCountB).to.be.gt(1)
5090 binding.stopPolling()
5091 done()
5092 , 15
5093
5094
5095
5096 test "Will re-create the interval poll if called .pollEvery() twice with different values", (done)->
5097 invokeCount = 0
5098 objectA.prop = 200
5099
5100 expect ()-> SimplyBind('prop', {'updateEvenIfSame':true}).of(objectA).to(()->invokeCount++).pollEvery(50).pollEvery(5)
5101 .not.to.throw()
5102
5103 setTimeout ()->
5104 expect(invokeCount).to.be.gte(3)
5105 done()
5106 restartSandbox()
5107 , 50
5108
5109
5110
5111 test "Will not throw errors when calling .stopPolling() on a binding with no pre-set interval", ()->
5112 expect ()-> SimplyBind('prop').of(objectA).to('prop').of(objectB).stopPolling()
5113 .not.to.throw()
5114
5115 restartSandbox()
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135 suite ".updateDepsOnEvent() + .removeEvent()", ()->
5136 suiteSetup(restartSandbox)
5137 test "Will listen for events on the target and upon emit will poll (and update if changed) the binding's value", ()->
5138 binding = SimplyBind('inheritedProp').of(eventEmitterA).to('prop1').of(objectA).updateDepsOnEvent('click')
5139
5140 binding.set 'one'
5141 expect(objectA.prop1).to.equal 'one'
5142
5143 eventEmitterA.inheritedProp = 'two'
5144 expect(objectA.prop1).to.equal 'one'
5145
5146 eventEmitterA.emit 'click'
5147 expect(objectA.prop1).to.equal 'two'
5148
5149 binding.removeEvent 'click'
5150 eventEmitterA.inheritedProp = 'three'
5151 expect(objectA.prop1).to.equal 'two'
5152
5153 eventEmitterA.emit 'click'
5154 expect(objectA.prop1).to.equal 'two'
5155 restartSandbox()
5156
5157
5158
5159 test "Can use custom supplied methods to apply/remove event listerners", ()->
5160 eventEmitterA.customAdd = ()-> eventEmitterA.on.apply eventEmitterA, arguments
5161 eventEmitterA.customRemove = ()-> eventEmitterA.removeListener.apply eventEmitterA, arguments
5162
5163 binding = SimplyBind('inheritedProp').of(eventEmitterA)
5164 .to('prop1').of(objectA)
5165 .updateDepsOnEvent('click', 'customAdd')
5166
5167 binding.set 'one'
5168 expect(objectA.prop1).to.equal 'one'
5169
5170 eventEmitterA.inheritedProp = 'two'
5171 expect(objectA.prop1).to.equal 'one'
5172
5173 eventEmitterA.emit 'click'
5174 expect(objectA.prop1).to.equal 'two'
5175
5176 binding.removeEvent 'click', 'customRemove'
5177 eventEmitterA.inheritedProp = 'three'
5178 expect(objectA.prop1).to.equal 'two'
5179
5180 eventEmitterA.emit 'click'
5181 expect(objectA.prop1).to.equal 'two'
5182
5183 restartSandbox()
5184
5185
5186
5187 test "Will do nothing when trying to remove an event that wasn't registered or when trying to add an event that was already registered", ()->
5188 binding = SimplyBind('inheritedProp').of(eventEmitterB).to('prop1').of(objectA).updateDepsOnEvent('click')
5189 atEvs = binding._.attachedEvents or binding._.atEV;
5190 expect(atEvs.length).to.equal 1
5191
5192 binding = SimplyBind('inheritedProp').of(eventEmitterB).to('prop1').of(objectA).updateDepsOnEvent('click')
5193 binding = SimplyBind('inheritedProp').of(eventEmitterB).to('prop1').of(objectA).updateDepsOnEvent('change')
5194 expect(atEvs.length).to.equal 2
5195
5196 binding.removeEvent 'clicker'
5197 expect(atEvs.length).to.equal 2
5198
5199 binding.removeEvent 'click'
5200 expect(atEvs.length).to.equal 1
5201
5202 binding.removeEvent 'change'
5203 expect(atEvs.length).to.equal 0
5204
5205 restartSandbox()
5206
5207
5208
5209 test "Will use the default event methods if the supplied custom methods don't exist", ()->
5210 binding = SimplyBind('inheritedProp').of(eventEmitterA).to('prop1').of(objectA).updateDepsOnEvent('click', 'customAdd')
5211
5212 binding.set 'one'
5213 expect(objectA.prop1).to.equal 'one'
5214
5215 eventEmitterA.inheritedProp = 'two'
5216 expect(objectA.prop1).to.equal 'one'
5217
5218 eventEmitterA.emit 'click'
5219 expect(objectA.prop1).to.equal 'two'
5220
5221 binding.removeEvent 'click', 'customRemove'
5222 eventEmitterA.inheritedProp = 'three'
5223 expect(objectA.prop1).to.equal 'two'
5224
5225 eventEmitterA.emit 'click'
5226 expect(objectA.prop1).to.equal 'two'
5227 restartSandbox()
5228
5229
5230
5231 test "Can use jQuery's .on and .off methods on DOM elements", ()-> if not isBrowser then @skip() else
5232 $h1 = $regAH1
5233 binding = SimplyBind('attr:data-attr').of($h1).to('prop1').of(objectA).updateDepsOnEvent('customevent', 'on')
5234
5235 binding.set 'one'
5236 expect(objectA.prop1).to.equal 'one'
5237
5238 $h1.attr 'data-attr', 'two'
5239 expect(objectA.prop1).to.equal 'one'
5240
5241 $h1.trigger 'customevent'
5242 expect(objectA.prop1).to.equal 'two'
5243 binding.removeEvent 'customevent', 'off'
5244
5245 $h1.attr 'data-attr', 'three'
5246 expect(objectA.prop1).to.equal 'two'
5247
5248 $h1.trigger 'customevent'
5249 expect(objectA.prop1).to.equal 'two'
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269 suite ".throttle()", ()->
5270 suiteSetup(restartSandbox)
5271 test "Will throttle the dep update frequency to once in a given timeframe", (done)->
5272 invokeCount = 0
5273 lastValue = null
5274 dispatcher = 'prop':1
5275
5276 SimplyBind('prop').of(dispatcher)
5277 .to (v)-> lastValue=v; invokeCount++
5278 .throttle(15)
5279
5280 expect(invokeCount).to.equal(1)
5281 expect(lastValue).to.equal(1)
5282
5283 dispatcher.prop = 2
5284 dispatcher.prop = 3
5285 dispatcher.prop = 4
5286 dispatcher.prop = 5
5287 expect(invokeCount).to.equal(2)
5288 expect(lastValue).to.equal(2)
5289
5290 setTimeout ()->
5291 expect(invokeCount).to.equal(3)
5292 expect(lastValue).to.equal(5)
5293 done()
5294 , 25
5295
5296
5297 test "Will remove existing throttles if passed boolean false", ()->
5298 invokeCount = 0
5299 lastValue = null
5300 dispatcher = 'prop':1
5301
5302 binding = SimplyBind('prop').of(dispatcher)
5303 .to (v)-> lastValue=v; invokeCount++
5304 .throttle(15)
5305
5306 expect(invokeCount).to.equal(1)
5307 expect(lastValue).to.equal(1)
5308
5309 binding.throttle(false)
5310 dispatcher.prop = 2
5311 dispatcher.prop = 3
5312 dispatcher.prop = 4
5313 dispatcher.prop = 5
5314 expect(invokeCount).to.equal(5)
5315 expect(lastValue).to.equal(5)
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346 suite "SimplyBind.unBindAll", ()->
5347 suite "Will unBind all bindings, but only in one direction", ()->
5348 suiteSetup(restartSandbox)
5349
5350 test "ObjectProp", ()->
5351 SimplyBind('prop1').of(objectA)
5352 .to('prop1').of(objectB).bothWays()
5353
5354 SimplyBind('prop2').of(objectA)
5355 .to('prop2').of(objectB).bothWays()
5356
5357 objectA.prop1 = 'objectA.prop1'
5358 expect(objectA.prop1).to.equal 'objectA.prop1'
5359 expect(objectB.prop1).to.equal 'objectA.prop1'
5360
5361 objectB.prop2 = 'objectB.prop2'
5362 expect(objectA.prop2).to.equal 'objectB.prop2'
5363 expect(objectB.prop2).to.equal 'objectB.prop2'
5364
5365 objectB.prop1 = 'reset'
5366 objectB.prop2 = 'reset'
5367 SimplyBind.unBindAll(objectA)
5368
5369 objectA.prop1 = 'objectA.prop1'
5370 expect(objectA.prop1).to.equal 'objectA.prop1'
5371 expect(objectB.prop1).to.equal 'reset'
5372
5373 objectA.prop2 = 'objectA.prop2'
5374 expect(objectA.prop2).to.equal 'objectA.prop2'
5375 expect(objectB.prop2).to.equal 'reset'
5376
5377 objectB.prop2 = 'objectB.prop2'
5378 expect(objectA.prop2).to.equal 'objectB.prop2'
5379 expect(objectB.prop2).to.equal 'objectB.prop2'
5380
5381 restartSandbox()
5382
5383
5384
5385 test "DOMText", ()-> if not isBrowser then @skip() else
5386 SimplyBind('textContent').of(regA)
5387 .to('textContent').of(regB).bothWays()
5388
5389 SimplyBind('textContent').of(regA).set 'regA.textContent'
5390 expect(regA.textContent).to.equal 'regA.textContent'
5391 expect(regB.textContent).to.equal 'regA.textContent'
5392
5393 SimplyBind('textContent').of(regB).set 'reset'
5394 SimplyBind.unBindAll(regA)
5395
5396 SimplyBind('textContent').of(regA).set 'regA.textContent'
5397 expect(regA.textContent).to.equal 'regA.textContent'
5398 expect(regB.textContent).to.equal 'reset'
5399
5400 SimplyBind('textContent').of(regB).set 'regB.textContent'
5401 expect(regA.textContent).to.equal 'regB.textContent'
5402 expect(regB.textContent).to.equal 'regB.textContent'
5403
5404 restartSandbox()
5405
5406
5407
5408 test "DOMValue", ()-> if not isBrowser then @skip() else
5409 SimplyBind('value').of(inputA)
5410 .to('value').of(inputB).bothWays()
5411
5412 inputA.value = 'inputA.value'
5413 inputA.emit 'change'
5414 expect(inputA.value).to.equal 'inputA.value'
5415 expect(inputB.value).to.equal 'inputA.value'
5416
5417
5418 inputB.value = 'reset'
5419 SimplyBind.unBindAll($inputA)
5420
5421 inputA.value = 'inputA.value'
5422 inputA.emit 'change'
5423 expect(inputA.value).to.equal 'inputA.value'
5424 expect(inputB.value).to.equal 'reset'
5425
5426 inputB.value = 'inputB.value'
5427 inputB.emit 'change'
5428 expect(inputA.value).to.equal 'inputB.value'
5429 expect(inputB.value).to.equal 'inputB.value'
5430
5431 restartSandbox()
5432
5433
5434
5435 test "Array", ()->
5436 SimplyBind(arrayA, {'trackArrayChildren':true})
5437 .to('prop1').of(objectA)
5438 .and('prop1').of(objectB)
5439
5440 expect(objectA.prop1).to.equal arrayA
5441 expect(objectB.prop1).to.equal arrayA
5442
5443 objectA.prop1 = 'reset'
5444 objectB.prop1 = 'reset'
5445 arrayA.push(1)
5446 expect(objectA.prop1).to.equal arrayA
5447 expect(objectB.prop1).to.equal arrayA
5448
5449 objectA.prop1 = 'reset'
5450 objectB.prop1 = 'reset'
5451 arrayA[5] = 19
5452 expect(objectA.prop1).to.equal arrayA
5453 expect(objectB.prop1).to.equal arrayA
5454
5455
5456
5457 objectA.prop1 = 'reset'
5458 objectB.prop1 = 'reset'
5459
5460 SimplyBind.unBindAll(arrayA)
5461 arrayA.push(1)
5462 arrayA[3] = 23
5463 expect(objectA.prop1).to.equal 'reset'
5464 expect(objectB.prop1).to.equal 'reset'
5465 restartSandbox()
5466
5467
5468
5469 test "Function", ()->
5470 passedValue = null
5471 valueToPass = 1
5472 takeFromReceiver = false
5473 fn = (fromReceiver)-> passedValue = if takeFromReceiver then fromReceiver else valueToPass
5474 invoker = 'prop':'value'
5475 receiver = 'prop':undefined
5476
5477 SimplyBind('prop').of(invoker).to(fn)
5478 SimplyBind(fn).to('prop').of(receiver).bothWays()
5479
5480 expect(receiver.prop).to.equal 1
5481 expect(passedValue).to.equal 1
5482
5483 valueToPass = 5
5484 invoker.prop = 'anotherValue'
5485 expect(receiver.prop).to.equal 5
5486 expect(passedValue).to.equal 5
5487
5488 takeFromReceiver = true
5489 receiver.prop = 10
5490 takeFromReceiver = false
5491 expect(passedValue).to.equal 10
5492
5493
5494 SimplyBind.unBindAll(fn)
5495 valueToPass = 15
5496 invoker.prop = 'newValue'
5497 expect(receiver.prop).to.equal 10
5498 expect(passedValue).to.equal 15
5499
5500 takeFromReceiver = true
5501 receiver.prop = 20
5502 takeFromReceiver = false
5503 expect(passedValue).to.equal 20
5504
5505
5506
5507 test "Event", ()->
5508 eventInvokeCount = 0
5509 eventObject = null
5510 objectA.prop = objectB.prop = 'someValue'
5511 eventEmitterA.on 'someEvent', (event)-> eventObject = event or @
5512
5513 binding = SimplyBind(0).ofEvent('someEvent').of(eventEmitterA).transformSelf ()-> eventObject
5514 .to('prop').of(objectA).bothWays()
5515 .and('prop').of(objectB).bothWays()
5516 .and ()-> eventInvokeCount++
5517
5518
5519 eventEmitterA.emit 'someEvent'
5520 expect(objectA.prop).to.equal eventObject
5521 expect(objectB.prop).to.equal(objectA.prop)
5522 expect(eventInvokeCount).to.equal 1
5523
5524
5525 objectA.prop = 'different value'
5526 expect(objectA.prop).to.equal 'different value'
5527 # expect(objectB.prop).to.equal eventObject
5528 expect(eventInvokeCount).to.equal 2
5529
5530 objectB.prop = 'different value2'
5531 expect(eventInvokeCount).to.equal 3
5532
5533
5534 SimplyBind.unBindAll(eventEmitterA)
5535 objectA.prop = 'different value'
5536 eventEmitterA.emit 'someEvent'
5537 eventEmitterA.on 'someEvent', ()-> eventInvokeCount++
5538
5539 expect(objectA.prop).to.equal 'different value'
5540 expect(objectB.prop).to.equal 'different value2'
5541 expect(eventInvokeCount).to.equal 3
5542
5543
5544 objectA.prop = 'another value'
5545 expect(objectA.prop).to.equal 'another value'
5546 expect(objectB.prop).to.equal 'different value2'
5547
5548 objectB.prop = 'another value2'
5549 expect(objectA.prop).to.equal 'another value'
5550 expect(objectB.prop).to.equal 'another value2'
5551 expect(eventInvokeCount).to.equal 5
5552 expect(binding._.deps.length).to.equal 0
5553
5554 restartSandbox()
5555
5556
5557
5558
5559
5560 suite "Will unBind all bindings in both directions if passed true as the second argument", ()->
5561 suiteSetup(restartSandbox)
5562
5563 test "ObjectProp", ()->
5564 SimplyBind('prop1').of(objectA)
5565 .to('prop1').of(objectB).bothWays()
5566
5567 SimplyBind('prop2').of(objectA)
5568 .to('prop2').of(objectB).bothWays()
5569
5570 objectA.prop1 = 'objectA.prop1'
5571 expect(objectA.prop1).to.equal 'objectA.prop1'
5572 expect(objectB.prop1).to.equal 'objectA.prop1'
5573
5574 objectB.prop2 = 'objectB.prop2'
5575 expect(objectA.prop2).to.equal 'objectB.prop2'
5576 expect(objectB.prop2).to.equal 'objectB.prop2'
5577
5578 objectB.prop1 = 'reset'
5579 objectB.prop2 = 'reset'
5580 SimplyBind.unBindAll(objectA, true)
5581
5582 objectA.prop1 = 'objectA.prop1'
5583 expect(objectA.prop1).to.equal 'objectA.prop1'
5584 expect(objectB.prop1).to.equal 'reset'
5585
5586 objectA.prop2 = 'objectA.prop2'
5587 expect(objectA.prop2).to.equal 'objectA.prop2'
5588 expect(objectB.prop2).to.equal 'reset'
5589
5590 objectB.prop2 = 'objectB.prop2'
5591 expect(objectA.prop2).to.equal 'objectA.prop2'
5592 expect(objectB.prop2).to.equal 'objectB.prop2'
5593
5594 restartSandbox()
5595
5596
5597
5598 test "DOMText", ()-> if not isBrowser then @skip() else
5599 SimplyBind('textContent').of(regA)
5600 .to('textContent').of(regB).bothWays()
5601
5602 SimplyBind('textContent').of(regA).set 'regA.textContent'
5603 expect(regA.textContent).to.equal 'regA.textContent'
5604 expect(regB.textContent).to.equal 'regA.textContent'
5605
5606 SimplyBind('textContent').of(regB).set 'reset'
5607 SimplyBind.unBindAll(regA, true)
5608
5609 SimplyBind('textContent').of(regA).set 'regA.textContent'
5610 expect(regA.textContent).to.equal 'regA.textContent'
5611 expect(regB.textContent).to.equal 'reset'
5612
5613 SimplyBind('textContent').of(regB).set 'regB.textContent'
5614 expect(regA.textContent).to.equal 'regA.textContent'
5615 expect(regB.textContent).to.equal 'regB.textContent'
5616
5617 restartSandbox()
5618
5619
5620
5621 test "DOMValue", ()-> if not isBrowser then @skip() else
5622 SimplyBind('value').of(inputA)
5623 .to('value').of(inputB).bothWays()
5624
5625 inputA.value = 'inputA.value'
5626 inputA.emit 'change'
5627 expect(inputA.value).to.equal 'inputA.value'
5628 expect(inputB.value).to.equal 'inputA.value'
5629
5630
5631 inputB.value = 'reset'
5632 SimplyBind.unBindAll(inputA, true)
5633
5634 inputA.value = 'inputA.value'
5635 inputA.emit 'change'
5636 expect(inputA.value).to.equal 'inputA.value'
5637 expect(inputB.value).to.equal 'reset'
5638
5639 inputB.value = 'inputB.value'
5640 inputB.emit 'change'
5641 expect(inputA.value).to.equal 'inputA.value'
5642 expect(inputB.value).to.equal 'inputB.value'
5643
5644 restartSandbox()
5645
5646
5647
5648 test "Function", ()->
5649 passedValue = null
5650 valueToPass = 1
5651 takeFromReceiver = false
5652 fn = (fromReceiver)-> passedValue = if takeFromReceiver then fromReceiver else valueToPass
5653 invoker = 'prop':'value'
5654 receiver = 'prop':undefined
5655
5656 SimplyBind('prop').of(invoker).to(fn)
5657 SimplyBind(fn).to('prop').of(receiver).bothWays()
5658
5659 expect(receiver.prop).to.equal 1
5660 expect(passedValue).to.equal 1
5661
5662 valueToPass = 5
5663 invoker.prop = 'anotherValue'
5664 expect(receiver.prop).to.equal 5
5665 expect(passedValue).to.equal 5
5666
5667 takeFromReceiver = true
5668 receiver.prop = 10
5669 takeFromReceiver = false
5670 expect(passedValue).to.equal 10
5671
5672
5673 SimplyBind.unBindAll(fn, true)
5674 valueToPass = 15
5675 invoker.prop = 'newValue'
5676 expect(receiver.prop).to.equal 10
5677 expect(passedValue).to.equal 15
5678
5679 takeFromReceiver = true
5680 receiver.prop = 20
5681 takeFromReceiver = false
5682 expect(passedValue).to.equal 15
5683
5684
5685
5686 test "Event", ()->
5687 eventInvokeCount = 0
5688 eventObject = null
5689 objectA.prop = objectB.prop = 'someValue'
5690 eventEmitterA.on 'someEvent', (event)-> eventObject = event or @
5691
5692 binding = SimplyBind(0).ofEvent('someEvent').of(eventEmitterA).transformSelf ()-> eventObject
5693 .to('prop').of(objectA).bothWays()
5694 .and('prop').of(objectB).bothWays()
5695 .and ()-> eventInvokeCount++
5696
5697
5698 eventEmitterA.emit 'someEvent'
5699 expect(objectA.prop).to.equal eventObject
5700 expect(objectB.prop).to.equal(objectA.prop)
5701 expect(eventInvokeCount).to.equal 1
5702
5703
5704 objectA.prop = 'different value'
5705 expect(objectA.prop).to.equal 'different value'
5706 # expect(objectB.prop).to.equal eventObject
5707 expect(eventInvokeCount).to.equal 2
5708
5709 objectB.prop = 'different value2'
5710 expect(eventInvokeCount).to.equal 3
5711
5712
5713 SimplyBind.unBindAll(eventEmitterA, true)
5714 objectA.prop = 'different value'
5715 eventEmitterA.emit 'someEvent'
5716 eventEmitterA.on 'someEvent', ()-> eventInvokeCount++
5717
5718 expect(objectA.prop).to.equal 'different value'
5719 expect(objectB.prop).to.equal 'different value2'
5720 expect(eventInvokeCount).to.equal 3
5721
5722
5723 objectA.prop = 'another value'
5724 expect(objectA.prop).to.equal 'another value'
5725 expect(objectB.prop).to.equal 'different value2'
5726
5727 objectB.prop = 'another value2'
5728 expect(objectA.prop).to.equal 'another value'
5729 expect(objectB.prop).to.equal 'another value2'
5730 expect(eventInvokeCount).to.equal 3
5731 expect(binding._.deps.length).to.equal 0
5732
5733 restartSandbox()
5734
5735
5736
5737
5738
5739
5740
5741
5742
5743 test "Passing objects that were never bound to anything should have no effects and shouldn't trigger any errors", ()->
5744 expect(()->
5745 SimplyBind.unBindAll 'nonbinded': 'object'
5746 SimplyBind.unBindAll 'string'
5747 SimplyBind('prop1').of(objectA).to ()->
5748 SimplyBind.unBindAll objectA
5749 SimplyBind.unBindAll objectA
5750 ).not.to.throw()
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
5775
5776
5777