ractive-ractive
Version:
Ractive adaptor for Ractive objects
156 lines (134 loc) • 4.8 kB
JavaScript
// Ractive adaptor plugin
// =======================
//
// This plugin allows you to have several Ractive instances sharing
// a single model, without using any third party libraries.
//
// Usage:
//
// var ractiveOne = new Ractive({
// el: 'one',
// template: templateOne
// });
//
// var ractiveTwo = new Ractive({
// el: 'two',
// template: templateTwo,
// data: ractiveOne,
// adaptors: [ 'Ractive' ]
// });
//
// Changes to either Ractive will be reflected in both.
(function ( global, factory ) {
'use strict';
// CommonJS/Modules.
if ( typeof module !== 'undefined' && module.exports && typeof require === 'function' ) {
factory( require( 'ractive' ) );
}
// AMD.
else if ( typeof define === 'function' && define.amd ) {
define([ 'ractive' ], factory );
}
// Browser global.
else if ( global.Ractive ) {
factory( global.Ractive );
}
else {
throw new Error( 'Could not find Ractive! It must be loaded before the ractive-adaptor-ractive plugin' );
}
}( typeof window !== 'undefined' ? window : this, function ( Ractive ) {
'use strict';
if ( !Ractive ) {
throw new Error( 'Could not find Ractive! Check your paths config' );
}
var Wrapper;
// Save under this path.
Ractive.adaptors.Ractive = {
filter: function ( object ) {
return object instanceof Ractive;
},
wrap: function ( ractive, otherRactive, keypath, prefixer ) {
return new Wrapper( ractive, otherRactive, keypath, prefixer );
}
};
Wrapper = function ( ractive, otherRactive, keypath, prefixer ) {
var wrapper = this;
// The original Ractive.
this.otherRactive = otherRactive;
// Listen them `change`.
this.changeHandler = otherRactive.on( 'change', function ( changeHash ) {
// Only set if we are not setting them.
if (wrapper.otherSetting) {
return;
}
wrapper.setting = true;
ractive.set( prefixer( changeHash ) );
wrapper.setting = false;
});
// Listen them `reset`.
this.resetHandler = otherRactive.on( 'reset', function ( newData ) {
// Only set if we are not setting them.
if (wrapper.otherSetting) {
return;
}
wrapper.setting = true;
ractive.update( keypath );
wrapper.setting = false;
});
// The opposite of a prefixer, setting properties on one level "up".
// https://github.com/ractivejs/ractive/blob/ccbe31bbfc488e780c8ffbea9c8b17cad6bc1c52/src/viewmodel/prototype/adapt.js#L52-L67
var defixer = function( changeHash ) {
var obj = {}, key, $key;
for (key in changeHash) {
if (changeHash.hasOwnProperty(key)) {
// TODO: this is probably not complete!
$key = key.split('.').slice(1).join('.');
if ($key.length) {
obj[ $key ] = changeHash[ key ];
} else {
return null; // skip paths we can't set...
}
}
}
return obj;
}
// Listen to our changes. In two-way binding DOM events do not go through
// `set` but trigger a `change` event.
ractive.on('change', function( changeHash ) {
var hash;
if ((hash = defixer(changeHash)) == null) return;
wrapper.otherSetting = true;
otherRactive.set(hash);
wrapper.otherSetting = false;
});
};
Wrapper.prototype = {
// Returns the value at keypath or all data.
get: function () {
return this.otherRactive.get();
},
// Updates data notifying observers of affected keypaths.
set: function ( keypath, value ) {
// Only set if the we didn't originate the change.
if (!this.setting) {
this.otherRactive.set( keypath, value );
}
},
// Resets the entire ractive.data object.
reset: function ( object ) {
if (this.setting) {
return;
}
if ( object instanceof Ractive || typeof object !== 'object' ) {
return false;
}
this.otherRactive.reset( object );
},
// Unrenders this Ractive instance, removing any event handlers that
// were bound automatically by Ractive.
teardown: function () {
this.changeHandler.cancel();
this.resetHandler.cancel();
}
};
}));