Skip to content

Commit

Permalink
Merge branch dev into ractivejsgh-1217
Browse files Browse the repository at this point in the history
  • Loading branch information
evs-chris committed Nov 29, 2014
2 parents 67b058f + f175879 commit aaa3329
Show file tree
Hide file tree
Showing 58 changed files with 927 additions and 301 deletions.
18 changes: 18 additions & 0 deletions src/Ractive/prototype/shared/eventStack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
var eventStack = {
enqueue: function( ractive, event ) {
if ( ractive.event ) {
ractive._eventQueue = ractive._eventQueue || [];
ractive._eventQueue.push( ractive.event );
}
ractive.event = event;
},
dequeue: function( ractive ) {
if ( ractive._eventQueue && ractive._eventQueue.length ) {
ractive.event = ractive._eventQueue.pop();
} else {
delete ractive.event;
}
}
};

export default eventStack;
9 changes: 3 additions & 6 deletions src/Ractive/prototype/shared/fireEvent.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import eventStack from 'Ractive/prototype/shared/eventStack';
import getPotentialWildcardMatches from 'utils/getPotentialWildcardMatches';

export default function fireEvent ( ractive, eventName, options = {} ) {
Expand Down Expand Up @@ -26,9 +27,7 @@ function fireEventAs ( ractive, eventNames, event, args, initialFire = false )

var subscribers, i, bubble = true;

if ( event ) {
ractive.event = event;
}
eventStack.enqueue( ractive, event );

for ( i = eventNames.length; i >= 0; i-- ) {
subscribers = ractive._subs[ eventNames[ i ] ];
Expand All @@ -38,9 +37,7 @@ function fireEventAs ( ractive, eventNames, event, args, initialFire = false )
}
}

if ( event ) {
delete ractive.event;
}
eventStack.dequeue( ractive );

if ( ractive.parent && bubble ) {

Expand Down
3 changes: 3 additions & 0 deletions src/config/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ export default {
noTwowayExpressions:
'Two-way binding does not work with expressions. Encountered ( {expression} ) on element {element}.',

computedCannotMapTo:
'Computed property "{key}" cannot be mapped to "{other}" because {reason}.',

notUsed:
'prevents forgetting trailing "," in cut and paste of previous line :)'
};
2 changes: 1 addition & 1 deletion src/config/options/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ function dispatch ( parent, child ) {
function copy ( from, to, fillOnly ) {
for ( let key in from ) {

if ( !(to._mappings && to._mappings[key].updatable) && fillOnly && key in to ) { continue; }
if ( !( to._mappings && to._mappings[ key ] && to._mappings[ key ].updatable ) && fillOnly && key in to ) { continue; }

to[ key ] = from[ key ];
}
Expand Down
11 changes: 9 additions & 2 deletions src/parse/converters/mustache/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import handlebarsBlockCodes from 'parse/converters/mustache/handlebarsBlockCodes
import 'legacy';

var indexRefPattern = /^\s*:\s*([a-zA-Z_$][a-zA-Z_$0-9]*)/,
keyIndexRefPattern = /^\s*,\s*([a-zA-Z_$][a-zA-Z_$0-9]*)/,
arrayMemberPattern = /^[0-9][1-9]*$/,
handlebarsBlockPattern = new RegExp( '^(' + Object.keys( handlebarsBlockCodes ).join( '|' ) + ')\\b' ),
legalReference;
Expand Down Expand Up @@ -181,9 +182,15 @@ export default function ( parser, delimiterType ) {
];
}

// optional index reference
// optional index and key references
if ( i = parser.matchPattern( indexRefPattern ) ) {
mustache.i = i;
let extra;

if ( extra = parser.matchPattern( keyIndexRefPattern ) ) {
mustache.i = i + ',' + extra;
} else {
mustache.i = i;
}
}

return mustache;
Expand Down
11 changes: 8 additions & 3 deletions src/shared/keypaths/decode.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import isNumeric from 'utils/isNumeric';

export default function decodeKeypath ( keypath ) {
var value = keypath.slice( 1 );
return isNumeric( value ) ? +value : value;
}
var value = keypath.slice( 2 );

if ( keypath[1] === 'i' ) {
return isNumeric( value ) ? +value : value;
} else {
return value;
}
}
4 changes: 2 additions & 2 deletions src/shared/parameters/ComplexParameter.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ ComplexParameter.prototype = {
this.dirty = false;
},

rebind: function ( indexRef, newIndex, oldKeypath, newKeypath ) {
this.fragment.rebind( indexRef, newIndex, oldKeypath, newKeypath );
rebind: function ( oldKeypath, newKeypath ) {
this.fragment.rebind( oldKeypath, newKeypath );
},

unbind: function () {
Expand Down
11 changes: 11 additions & 0 deletions src/shared/parameters/DataTracker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
function DataTracker ( key, viewmodel ) {
this.keypath = key;
this.viewmodel = viewmodel;
}

export default DataTracker;

DataTracker.prototype.setValue = function ( value ) {
this.viewmodel.set( this.keypath, value, { noMapping: true } );
};

136 changes: 69 additions & 67 deletions src/shared/parameters/Mapping.js
Original file line number Diff line number Diff line change
@@ -1,114 +1,106 @@
import equalsOrStartsWith from 'shared/keypaths/equalsOrStartsWith';
import getNewKeypath from 'shared/keypaths/getNew';
import DataTracker from 'shared/parameters/DataTracker';

function Mapping ( localKey, options ) {
this.localKey = localKey;
this.keypath = options.keypath;
this.origin = options.origin;
this.trackData = options.trackData;
this.resolved = this.ready = false;
this.trackData = options.trackData || options.reversed;
this.reversed = options.reversed;
this.resolved = false;
}

export default Mapping;

Mapping.prototype = {

setViewmodel: function ( viewmodel ){
this.local = viewmodel;
this.deps = [];
this.unresolved = [];
this.setup();
this.ready = true;
},

get: function ( keypath, options ) {
if ( !this.resolved ) {
return undefined;
}

return this.origin.get( this.map( keypath ), options );
},

getValue: function () {
if ( !this.keypath ) {
return undefined;
}

return this.origin.get( this.keypath );
},

initViewmodel: function ( viewmodel ){
this.local = viewmodel;
this.deps = [];
this.local.mappings[ this.localKey ] = this;
this.setup();
},

map: function ( keypath ) {
// TODO: should this be cached and only run when keypath changes?
return keypath.replace( this.localKey, this.keypath );
},

rebind: function ( indexRef, newIndex, oldKeypath, newKeypath ) {
if ( equalsOrStartsWith( this.keypath, oldKeypath ) ) {
this.deps.forEach( d => this.origin.unregister( this.map( d.keypath ), d.dep, d.group ) );
this.keypath = getNewKeypath( this.keypath, oldKeypath, newKeypath );
this.deps.forEach( d => this.origin.register( this.map( d.keypath ), d.dep, d.group ) );
}
rebind: function ( localKey ) {
this.unbind( true );
this.localKey = localKey;
this.local.mappings[ this.localKey ] = this;
this.setup();
},

register: function ( keypath, dependant, group ) {
var dep = { keypath: keypath, dep: dependant, group: group };

if ( !this.resolved ) {
this.unresolved.push( dep );
} else {
this.deps.push({ keypath: keypath, dep: dependant, group: group });
this.origin.register( this.map( keypath ), dependant, group );
}
this.deps.push({ keypath: keypath, dep: dependant, group: group });
this.origin.register( this.map( keypath ), dependant, group );
},

resolve: function ( keypath ) {

if ( this.keypath !== undefined ) {
this.origin.unregister( this.keypath, this, 'mappings' );
this.deps.forEach( d => this.origin.unregister( this.map( d.keypath ), d.dep, d.group ) );
this.unbind( true );
}

this.keypath = keypath;
this.setup();
},

set: function ( keypath, value ) {
// TODO: force resolution
if ( !this.resolved ) {
throw new Error( 'Something very odd happened. Please raise an issue at https://github.com/ractivejs/ractive/issues - thanks!' );
}

this.origin.set( this.map( keypath ), value );
},

setup: function () {

if ( this.keypath === undefined ) { return; }

this.resolved = true;

this.origin.register( this.keypath, this, 'mappings' );
// _if_ the local viewmodel isn't intializing, check
// for existing dependants that were registered and
// move them as they now belong to this key
if ( this.local.deps && this.keypath ) {
this.transfers = this.local.unregister( this.localKey );

if( this.trackData ) {
// keep local data in sync, for browsers w/ no defineProperty
this.origin.register( this.keypath, {
setValue: value => {
this.local.ractive.data[ this.localKey ] = value;
}
}, 'default' );
if( this.transfers ) {
this.transfers.forEach( d => this.origin.register( this.map( d.keypath ), d.dep, d.group ) );
this.origin.mark( this.keypath );
}
}

if ( this.ready ) {
this.unresolved.forEach( u => {
if ( u.group === 'mappings' ) { // TODO should these be treated w/ separate process?
u.dep.local.mark( u.dep.localKey );
u.dep.origin = this.origin;
u.dep.keypath = this.keypath;
} else {
this.register( u.keypath, u.dep, u.group );
u.dep.setValue( this.get( u.keypath ) );
}
});

this.deps.forEach( d => this.origin.register( this.map( d.keypath ), d.dep, d.group ) );
this.local.mark( this.localKey );
// keep local data in sync, for
// a) browsers w/ no defineProperty
// b) reversed mappings
if( this.trackData ) {
this.tracker = new DataTracker( this.localKey, this.local );
this.origin.register( this.keypath, this.tracker );
}
},

set: function ( keypath, value ) {
if ( !this.resolved ) {
throw new Error( 'Something very odd happened. Please raise an issue at https://github.com/ractivejs/ractive/issues - thanks!' );
// accumulated dependants can now be registered
if ( this.deps.length ) {
this.deps.forEach( d => this.origin.register( this.map( d.keypath ), d.dep, d.group ) );
this.origin.mark( this.keypath );
}

this.origin.set( this.map( keypath ), value );
},

setValue: function ( value ) {
Expand All @@ -119,27 +111,37 @@ Mapping.prototype = {
this.origin.set( this.keypath, value );
},

unbind: function () {
var dep;
unbind: function ( keepLocal ) {
if ( !keepLocal ) {
delete this.local.mappings[ this.localKey ];
}

this.deps.forEach( d => {
this.origin.unregister( this.map( d.keypath ), d.dep, d.group );
});

// revert transfers back to original viewmodel owner
if ( this.transfers ) {
this.transfers.forEach( d => {
this.origin.unregister( this.map( d.keypath ), d.dep, d.group );
this.local.register( d.keypath , d.dep, d.group );
});
}

while ( dep = this.deps.pop() ) {
this.origin.unregister( this.map( dep.keypath ), dep.dep, dep.group );
if ( this.tracker ) {
this.origin.unregister( this.keypath, this.tracker );
}
},

unregister: function ( keypath, dependant, group ) {
var deps, i;

deps = this.resolved ? this.deps : this.unresolved;
i = deps.length;
var deps = this.deps, i = deps.length;

while ( i-- ) {
if ( deps[i].dep === dependant ) {
deps.splice( i, 1 );
break;
}
}

this.origin.unregister( this.map( keypath ), dependant, group );
}
};
4 changes: 4 additions & 0 deletions src/shared/parameters/ParameterResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,12 @@ ParameterResolver.prototype = {
this.parameters.addData( this.key, decodeKeypath( keypath ) );
viewmodel.mark( this.key );
}
else if ( viewmodel.reversedMappings && viewmodel.reversedMappings[ this.key ] ) {
viewmodel.reversedMappings[ this.key ].rebind( keypath );
}
else {
viewmodel.mappings[ this.key ].resolve( keypath );
}

}
};
14 changes: 4 additions & 10 deletions src/shared/parameters/createParameters.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,12 @@ ComponentParameters.prototype = {
},

addMapping: function ( key, keypath ) {
// map directly to the source if possible...
var mapping = this.parentViewmodel.mappings[ keypath ];
return this.mappings[ key ] = new Mapping( key, {
origin: this.parentViewmodel,
keypath: keypath
origin: mapping ? mapping.origin : this.parentViewmodel,
keypath: mapping ? mapping.keypath : keypath
});

// TODO: not sure about reference expressions and such
// if this would actually work... need to test
// return this.mappings[ key ] = {
// get the "owner" of the data:
// origin: this.parentViewmodel.origin( keypath ),
// keypath: keypath
// };
}
};

Expand Down
Loading

0 comments on commit aaa3329

Please sign in to comment.