suite "Data Binding Behavior", ()->
	suiteSetup(restartSandbox)
	test "Live properties should behave the same way across all object types", ()->
		dispatcher = 
			'standard': objectA
			'array': objectA.list
			'function': ()->
			'domDiv': if isBrowser then $('<div />')[0] else {}
			'domInput': if isBrowser then $('<input />')[0] else {}
			'domNode': if isBrowser then $('<div>text</div>')[0].childNodes[0] else {}
			'date': new Date()
		receiver =
			'standard': null
			'array': null
			'function': null
			'domDiv': null
			'domInput': null
			'domNode': null
			'date': null

		for name,object of dispatcher
			SimplyBind('prop').of(dispatcher[name]).to(name).of(receiver)
			expect(receiver[name]).to.equal(undefined)
			
			object.prop = 100
			expect(receiver[name]).to.equal(100)


		restartSandbox()


	test "Object properties will be converted to live properties based off prototype values if existent, even if they are unconfigurable", ()->
		SimplyBind.defaultOptions.updateOnBind = false
		invokeCount = A:0,B:0,C:0,D:0
		dispatcher = value:1
	
		valueC = 1
		valueD = 1
		obj = Object.create {A:1}
		Object.defineProperties Object.getPrototypeOf(obj),
			B: value: 1
			C: 
				configurable: true
				get: ()-> valueC
				set: ()-> valueC = arguments[0]
			D: 
				configurable: false
				get: ()-> valueD
				set: ()-> valueD = arguments[0]

		SimplyBind('A').of(obj).to ()-> invokeCount.A++
		SimplyBind('B').of(obj).to ()-> invokeCount.B++
		SimplyBind('C').of(obj).to ()-> invokeCount.C++
		SimplyBind('D').of(obj).to ()-> invokeCount.D++
		SimplyBind('value').of(dispatcher).to('A').of(obj).and.to('B').of(obj).and.to('C').of(obj).and.to('D').of(obj)
		expect(invokeCount.A).to.equal 0
		expect(invokeCount.B).to.equal 0
		expect(invokeCount.C).to.equal 0
		expect(invokeCount.D).to.equal 0
		
		dispatcher.value++
		expect(invokeCount.A).to.equal 1
		expect(invokeCount.B).to.equal 1
		expect(invokeCount.C).to.equal 1
		expect(invokeCount.D).to.equal 1
		expect(obj.A).to.equal 2
		expect(obj.B).to.equal 2
		expect(obj.C).to.equal 2
		expect(obj.D).to.equal 2

		obj.A++
		expect(invokeCount.A).to.equal 2
		obj.B++
		expect(invokeCount.B).to.equal 2
		obj.C++
		expect(invokeCount.C).to.equal 2
		obj.D++
		expect(invokeCount.D).to.equal 2


		SimplyBind.unBindAll(obj, true)
		expect(obj.A).to.equal 3
		expect(obj.B).to.equal 3
		expect(obj.C).to.equal 3
		expect(obj.D).to.equal 3

		SimplyBind.defaultOptions.updateOnBind = true



	test "Object properties should be made into live properties unless they are unconfigurable and not prototype-inherited", ()->
		SimplyBind.defaultOptions.updateOnBind = false
		invokeCount = 0
		
		### ========================================================================== ###
		SimplyBind('prop1').of(objectA).to ()-> invokeCount++
		expect(invokeCount).to.equal(0)

		objectA.prop1 = !objectA.prop1
		expect(invokeCount).to.equal(1)

		### ========================================================================== ###

		SimplyBind('nonexistent').of(objectA).to ()-> invokeCount++
		expect(invokeCount).to.equal(1)

		objectA.nonexistent = !objectA.nonexistent
		expect(invokeCount).to.equal(2)

		### ========================================================================== ###
		
		Object.defineProperty objectA, 'protected', value:'cant touch this'
		SimplyBind('protected').of(objectA).to ()-> invokeCount++
		expect(invokeCount).to.equal(2)

		objectA.protected = !objectA.protected
		expect(invokeCount).to.equal(2)

		### ========================================================================== ###
		
		obj = Object.create(protected:'CAN touch this, even though its a prototype-inherited value')
		SimplyBind('protected').of(obj).to ()-> invokeCount++
		expect(invokeCount).to.equal(2)

		obj.protected = !obj.protected
		expect(invokeCount).to.equal(3)
		

		SimplyBind.defaultOptions.updateOnBind = true
		restartSandbox()



	test "The property's original setter (if exists) should be invoked during a live property update", ()->
		prop7 = 'hey!'
		invokeCountGet = 0
		invokeCountSet = 0
		context = null
		
		Object.defineProperty objectA, 'prop7',
			enumerable: true
			configurable: true
			get: ()->
				invokeCountGet++
				prop7
			set: (val)->
				invokeCountSet++
				context = @
				prop7 = val



		expect(objectA.prop7).to.equal 'hey!'
		expect(invokeCountGet).to.equal 1
		expect(invokeCountSet).to.equal 0
		expect(context).to.equal null

		SimplyBind('prop7').of(objectA).to('prop3').of objectB
	
		objectA.prop7 = 'hello!'
		expect(invokeCountGet).to.equal 2
		expect(invokeCountSet).to.equal 1
		expect(objectA.prop7).to.equal 'hello!'
		expect(context).to.equal objectA
	
		objectA.prop7 = 'hi!'
		expect(invokeCountGet).to.equal 3
		expect(invokeCountSet).to.equal 2
		expect(context).to.equal objectA

		SimplyBind.unBindAll(objectA, true)
		expect(objectA.prop7).to.equal 'hi!'
		expect(invokeCountGet).to.equal 4
		expect(invokeCountSet).to.equal 2


		objectA.prop7 = 'HI!'
		expect(objectA.prop7).to.equal 'HI!'
		expect(invokeCountGet).to.equal 5
		expect(invokeCountSet).to.equal 3
		expect(objectB.prop3).not.to.equal 'HI!'

		restartSandbox()




	test "Destruction of non-liveprops bindings should not redefine property descriptors", ()->
		invokeCountA = invokeCountB = 0
		# proto = {A:10}; Object.defineProperty(proto, 'B', value:10)
		obj = Object.create({A:10}, B:{value:10, writable:true, configurable:false})
		descA = Object.getOwnPropertyDescriptor(obj, 'A')
		descB = Object.getOwnPropertyDescriptor(obj, 'B')
		expect(obj.A).to.equal 10
		expect(obj.B).to.equal 10
		expect(descA).to.be.undefined
		expect(descB).not.to.be.undefined

		
		binding = SimplyBind('A').of(obj).to ()-> invokeCountA++
		binding = SimplyBind('B').of(obj).to ()-> invokeCountB++
		expect(invokeCountA).to.equal 1
		expect(invokeCountB).to.equal 1
		expect(Object.getOwnPropertyDescriptor(obj, 'A')).not.to.be.undefined
		expect(Object.getOwnPropertyDescriptor(obj, 'B')).not.to.equal(descB)

		obj.A = 'anotherValue'
		obj.B = 'anotherValue'
		expect(invokeCountA).to.equal 2
		expect(invokeCountB).to.equal 1
		expect(obj.A).to.equal 'anotherValue'
		expect(obj.A).to.equal 'anotherValue'

		delete obj.A
		delete obj.B
		expect(invokeCountA).to.equal 2
		expect(invokeCountB).to.equal 1
		expect(obj.A).to.equal 10
		expect(obj.B).to.equal 'anotherValue'
		expect(Object.getOwnPropertyDescriptor(obj, 'A')).to.be.undefined
		expect(Object.getOwnPropertyDescriptor(obj, 'B')).not.to.be.undefined

		binding.unBind()
		expect(invokeCountA).to.equal 2
		expect(invokeCountB).to.equal 1
		expect(obj.A).to.equal 10
		expect(obj.B).to.equal 'anotherValue'
		expect(Object.getOwnPropertyDescriptor(obj, 'A')).to.be.undefined
		expect(Object.getOwnPropertyDescriptor(obj, 'B')).not.to.be.undefined
		restartSandbox()




	test "Publishers will update subscribers even if their value is falsy", ()->
		binding = SimplyBind('prop1').of(objectA).to('prop1').of(objectB)
		
		binding.set ''
		expect(objectB.prop1).to.equal ''
		
		binding.set 0
		expect(objectB.prop1).to.equal 0
		
		binding.set false
		expect(objectB.prop1).to.equal false
		
		binding.set undefined
		expect(objectB.prop1).to.equal undefined
		
		binding.set null
		expect(objectB.prop1).to.equal null

		restartSandbox()



	test "Update subscribers of a binding even if it's unchanged when SimplyBind.options.updateEvenIfSame is on", ()->
		expect(SimplyBind.defaultOptions.updateEvenIfSame).to.be.false
		invokeCount = object:0, func:0, event:0
		dispatcher = object:'start', func:'start', event:'start'

		eventEmitterA.on 'alwaysEmit', ()-> invokeCount.event++
		
		bindings =
			'object': SimplyBind('object').of(dispatcher).to('prop').of(objectB).chainTo (value)-> invokeCount.object++
			'func': SimplyBind('func').of(dispatcher).to(()-> invokeCount.func++)
			'event': SimplyBind('event').of(dispatcher).to('event:alwaysEmit').of(eventEmitterA)
		
		expect(objectB.prop).to.equal 'start'
		expect(invokeCount.object).to.equal 1
		
		dispatcher.object = "shouldn't update"
		expect(objectB.prop).to.equal "shouldn't update"
		expect(invokeCount.object).to.equal 2
		
		dispatcher.object = "shouldn't update"
		expect(objectB.prop).to.equal "shouldn't update"
		expect(invokeCount.object).to.equal 2
		

		expect(invokeCount.func).to.equal 1
		
		dispatcher.func = "shouldn't update"
		expect(invokeCount.func).to.equal 2

		dispatcher.func = "shouldn't update"
		expect(invokeCount.func).to.equal 2
		


		expect(invokeCount.event).to.equal 1
		
		dispatcher.event = "shouldn't update"
		expect(invokeCount.event).to.equal 2

		dispatcher.event = "shouldn't update"
		expect(invokeCount.event).to.equal 2


		bindings.object.setOption 'updateEvenIfSame', true
		SimplyBind('object').of(dispatcher).to('prop').of(objectB).setOption 'updateEvenIfSame', true
		
		dispatcher.object = 'should update'
		expect(objectB.prop).to.equal 'should update'
		expect(invokeCount.object).to.equal 3
	
		dispatcher.object = 'should update'
		dispatcher.object = 'should update'
		dispatcher.object = 'should update'
		dispatcher.object = 'should update'
		expect(invokeCount.object).to.equal 7
		

		bindings.object.setOption 'updateEvenIfSame', false
		SimplyBind('object').of(dispatcher).to('prop').of(objectB).setOption 'updateEvenIfSame', false
		
		dispatcher.object = "shouldn't update"
		expect(objectB.prop).to.equal "shouldn't update"
		expect(invokeCount.object).to.equal 8
		
		dispatcher.object = "shouldn't update"
		dispatcher.object = "shouldn't update"
		dispatcher.object = "shouldn't update"
		dispatcher.object = "shouldn't update"
		expect(invokeCount.object).to.equal 8
		restartSandbox()



	test "Should update subscribers even when its new value is undefined", ()->
		binding = SimplyBind('prop1').of(objectA).to('prop1').of(objectB)
		objectA.prop1 = 10
		expect(objectB.prop1).to.equal 10
		
		objectA.prop1 = window.nonexistent # undefined value
		expect(objectB.prop1).to.be.undefined
		

		objectA.prop1 = 20
		expect(objectB.prop1).to.equal 20
		
		objectA.prop1 = window.nonexistent # undefined value
		expect(objectB.prop1).to.be.undefined
		
		restartSandbox()



	test "Subscribers should update their own subscribers when being updated by a publisher, even if these subscribers are not liveProps", ()-> if not isBrowser then @skip() else
		objectA.prop2 = 0.8
		
		SimplyBind('prop2').of(objectA)
			.to('opacity').of regA.style
		
		SimplyBind('opacity').of(regA.style)
			.to('font-size').of(regA.style)
				.transform (opacity)-> opacity * 20 + 'px'
			.and.to('font-size').of(regB.style)
				.transform (opacity)-> opacity * 40 + 'px'
		

		expect(regA.style.opacity.toString()).to.equal '0.8'
		expect(regA.style['font-size']).to.equal '16px'
		expect(regB.style['font-size']).to.equal '32px'
		

		objectA.prop2 = 0.5
		expect(regA.style.opacity.toString()).to.equal '0.5'
		expect(regA.style['font-size']).to.equal '10px'
		expect(regB.style['font-size']).to.equal '20px'
		restartSandbox()



	test "Placeholder indicators in a property name will be ignored if SimplyBind.options.simpleSelector is on", ()->
		objA = 'first': 'This {{placeholder}} will change'
		objB = 'second': 'This {{nonplaceholder}} will remain the same'

		SimplyBind('prop1').of(objectA).to('first.placeholder').of(objA)
		objectA.prop1 = 'placeholder'

		SimplyBind('prop1').of(objectB).to('second.nonplaceholder', {'simpleSelector':true}).of(objB)
		objectB.prop1 = 'placeholder'

		expect(objA['first']).to.equal 'This placeholder will change'
		expect(objB['second']).to.equal 'This {{nonplaceholder}} will remain the same'



	test "Subscribers shouldn't be re-updated when a transform function is added if SimplyBind.options.updateOnBind is off", ()->
		dispatcher = 'value': 123
		invokeCount =
			'object': 0
			'array': 0
			'function': 0
			'domAttr': 0
			'domText': 0
			'domInput': 0
			'jQueryProp': 0
			'event': 0

		fakeTransform = (value)-> value
		SimplyBind.defaultOptions.updateOnBind = false
		SimplyBind.defaultOptions.updateEvenIfSame = true
	
		SimplyBind('prop').of(objectA).to(()-> invokeCount.object++).transform(fakeTransform)
		SimplyBind('array:list').of(objectA).to(()-> invokeCount.array++).transform(fakeTransform)
		SimplyBind(fn = ()-> true).to(()-> invokeCount.function++).transform(fakeTransform)
		SimplyBind('attr:prop').of(regA).to(()-> invokeCount.domAttr++).transform(fakeTransform) if isBrowser
		SimplyBind('textContent').of(regA).to(()-> invokeCount.domText++).transform(fakeTransform) if isBrowser
		SimplyBind('value').of(inputA).to(()-> invokeCount.domInput++).transform(fakeTransform) if isBrowser
		SimplyBind('prop').of($regB).to(()-> invokeCount.jQueryProp++).transform(fakeTransform) if isBrowser
		SimplyBind('event:someEvent').of(eventEmitterA).to(()-> invokeCount.event++).transform(fakeTransform)

		SimplyBind.defaultOptions.updateOnBind = true

		
		SimplyBind('value').of(dispatcher).to('prop').of(objectA)
		SimplyBind('value').of(dispatcher).to('array:list').of(objectA)
		SimplyBind('value').of(dispatcher).to(fn)
		SimplyBind('value').of(dispatcher).to('attr:prop').of(regA) if isBrowser
		SimplyBind('value').of(dispatcher).to('textContent').of(regA) if isBrowser
		SimplyBind('value').of(dispatcher).to('value').of(inputA) if isBrowser
		SimplyBind('value').of(dispatcher).to('prop').of($regB) if isBrowser
		SimplyBind('value').of(dispatcher).to('event:someEvent').of(eventEmitterA)

		expect(invokeCount.object).to.equal 1
		expect(invokeCount.array).to.equal 1
		expect(invokeCount.function).to.equal 3 # updateOnBind always true
		expect(invokeCount.event).to.equal 1
		if isBrowser
			expect(invokeCount.domAttr).to.equal 1
			expect(invokeCount.domText).to.equal 1
			expect(invokeCount.domInput).to.equal 1
			expect(invokeCount.jQueryProp).to.equal 1

		SimplyBind.defaultOptions.updateEvenIfSame = false
		restartSandbox()




	test "Publisher's shouldn't be able to add themselves as subscribers to themselves", ()->
		binding = SimplyBind('prop1').of(objectA)._
		expect(binding.subs.length).to.equal 0

		bindingInterface = SimplyBind('prop1').of(objectA).to('prop1').of(objectA)
		expect(binding.subs.length).to.equal 0
		expect(bindingInterface.subs.length).to.equal 0

		bindingInterface = SimplyBind('prop1').of(objectA).updateOn('prop1').of(objectA)
		expect(binding.subs.length).to.equal 0
		expect(bindingInterface.subs.length).to.equal 0

		restartSandbox()





