UNPKG

ractive-ractive

Version:
156 lines (134 loc) 4.8 kB
// 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(); } }; }));