UNPKG

ractive

Version:

Next-generation DOM manipulation

262 lines (200 loc) 5.55 kB
define([ 'ractive' ], function ( Ractive ) { 'use strict'; return function () { var fixture, Model, adaptor; module( 'Adaptors' ); // setup fixture = document.getElementById( 'qunit-fixture' ); Model = function ( attributes ) { this.attributes = attributes; this.callbacks = {}; this.transformers = {}; }; Model.prototype = { set: function ( attr, newValue ) { var transformer, oldValue = this.attributes[ attr ]; if ( transformer = this.transformers[ attr ] ) { newValue = transformer.call( this, newValue, oldValue ); } if ( oldValue !== newValue ) { this.attributes[ attr ] = newValue; this.fire( 'change', attr, newValue ); } }, get: function ( attr ) { return this.attributes[ attr ]; }, reset: function ( newData ) { var attr; this.attributes = {}; for ( attr in newData ) { if ( newData.hasOwnProperty( attr ) ) { this.set( attr, newData[ attr ] ); } } }, transform: function ( attr, transformer ) { this.transformers[ attr ] = transformer; if ( this.attributes.hasOwnProperty( attr ) ) { this.set( attr, this.get( attr ) ); } }, on: function ( eventName, callback ) { var self = this; if ( !this.callbacks[ eventName ] ) { this.callbacks[ eventName ] = []; } this.callbacks[ eventName ].push( callback ); return { cancel: function () { self.off( eventName, callback ); } } }, off: function ( eventName, callback ) { var callbacks, index; callbacks = this.callbacks[ eventName ]; if ( !callbacks ) { return; } index = callbacks.indexOf( callback ); if ( index !== -1 ) { callbacks.splice( index, 1 ); } }, fire: function ( eventName ) { var args, callbacks, i; callbacks = this.callbacks[ eventName ]; if ( !callbacks ) { return; } args = Array.prototype.slice.call( arguments, 1 ); i = callbacks.length; while ( i-- ) { callbacks[i].apply( null, args ); } } }; adaptor = { filter: function ( object ) { return object instanceof Model; }, wrap: function ( ractive, object, keypath, prefix ) { var listener, setting; listener = object.on( 'change', function ( attr, value ) { if ( setting ) { return; } setting = true; ractive.set( prefix( attr, value ) ); setting = false; }); return { get: function () { return object.attributes; }, teardown: function () { listener.cancel(); }, set: function ( attr, value ) { if ( setting ) { return; } setting = true; object.set( attr, value ); setting = false; }, reset: function ( newData ) { var attr; if ( newData instanceof Model ) { return false; // teardown } if ( !newData || typeof newData !== 'object' ) { return false; } object.reset( newData ); ractive.update( keypath ); } }; } }; test( 'Adaptors can change data as it is .set() (#442)', function ( t ) { var model, ractive; model = new Model({ foo: 'BAR', percent: 150 }); model.transform( 'foo', function ( newValue, oldValue ) { return newValue.toLowerCase(); }); model.transform( 'percent', function ( newValue, oldValue ) { return Math.min( 100, Math.max( 0, newValue ) ); }); ractive = new Ractive({ el: fixture, template: '<p>{{model.foo}}</p><p>{{model.percent}}</p>', data: { model: model }, adapt: [ adaptor ] }); t.htmlEqual( fixture.innerHTML, '<p>bar</p><p>100</p>' ); ractive.set( 'model.foo', 'BAZ' ); ractive.set( 'model.percent', -20 ); t.htmlEqual( fixture.innerHTML, '<p>baz</p><p>0</p>' ); ractive.set( 'model', { foo: 'QUX', percent: 50 }); t.htmlEqual( fixture.innerHTML, '<p>qux</p><p>50</p>' ); }); test( 'ractive.reset() calls are forwarded to wrappers if the root data object is wrapped', function ( t ) { var model, ractive; model = new Model({ foo: 'BAR', unwanted: 'here' }); model.transform( 'foo', function ( newValue, oldValue ) { return newValue.toLowerCase(); }); ractive = new Ractive({ el: fixture, template: '<p>{{foo}}</p>{{unwanted}}', data: model, adapt: [ adaptor ] }); ractive.reset({ foo: 'BAZ' }); t.htmlEqual( fixture.innerHTML, '<p>baz</p>' ); model = new Model({ foo: 'QUX' }); model.transform( 'foo', function ( newValue, oldValue ) { return newValue.toLowerCase(); }); ractive.reset( model ); t.htmlEqual( fixture.innerHTML, '<p>qux</p>' ); }); test( 'If a wrapper\'s reset() method returns false, it should be torn down (#467)', function ( t ) { var model1, model2, ractive; model1 = new Model({ foo: 'bar' }); model2 = new Model({ foo: 'baz' }); ractive = new Ractive({ el: fixture, template: '<p>{{model.foo}}</p>', data: { model: model1 }, adapt: [ adaptor ] }); t.htmlEqual( fixture.innerHTML, '<p>bar</p>' ); ractive.set( 'model', model2 ); t.htmlEqual( fixture.innerHTML, '<p>baz</p>' ); }); test( 'A string can be supplied instead of an array for the `adapt` option (if there\'s only one adaptor listed', function ( t ) { var Subclass, instance; Subclass = Ractive.extend({ adapt: 'Foo' }); instance = new Subclass(); t.deepEqual( instance.adapt, ['Foo'] ); }); }; });