@@ -5,8 +5,7 @@ import { assert } from './debug';
55@submodule ember-metal
66*/
77
8- const SPLIT_REGEX = / \{ | \} / ;
9- const END_WITH_EACH_REGEX = / \. @ e a c h $ / ;
8+ var END_WITH_EACH_REGEX = / \. @ e a c h $ / ;
109
1110/**
1211 Expands `pattern`, invoking `callback` for each expansion.
@@ -41,53 +40,55 @@ export default function expandProperties(pattern, callback) {
4140 'Brace expanded properties cannot contain spaces, e.g. "user.{firstName, lastName}" should be "user.{firstName,lastName}"' ,
4241 pattern . indexOf ( ' ' ) === - 1
4342 ) ;
44- assert (
45- `Brace expanded properties have to be balanced and cannot be nested, pattern: ${ pattern } ` ,
46- ( ( str ) => {
47- let inBrace = 0 ;
48- let char ;
49- for ( let i = 0 ; i < str . length ; i ++ ) {
50- char = str . charAt ( i ) ;
5143
52- if ( char === '{' ) {
53- inBrace ++ ;
54- } else if ( char === '}' ) {
55- inBrace -- ;
44+ let unbalancedNestedError = `Brace expanded properties have to be balanced and cannot be nested, pattern: ${ pattern } ` ;
45+ let properties = [ pattern ] ;
46+
47+ // Iterating backward over the pattern makes dealing with indices easier.
48+ let bookmark ;
49+ let inside = false ;
50+ for ( let i = pattern . length ; i > 0 ; -- i ) {
51+ let current = pattern [ i - 1 ] ;
52+
53+ switch ( current ) {
54+ // Closing curly brace will be the first character of the brace expansion we encounter.
55+ // Bookmark its index so long as we're not already inside a brace expansion.
56+ case '}' :
57+ if ( ! inside ) {
58+ bookmark = i - 1 ;
59+ inside = true ;
60+ } else {
61+ assert ( unbalancedNestedError , false ) ;
5662 }
57-
58- if ( inBrace > 1 || inBrace < 0 ) {
59- return false ;
63+ break ;
64+ // Opening curly brace will be the last character of the brace expansion we encounter.
65+ // Apply the brace expansion so long as we've already seen a closing curly brace.
66+ case '{' :
67+ if ( inside ) {
68+ let expansion = pattern . slice ( i , bookmark ) . split ( ',' ) ;
69+ // Iterating backward allows us to push new properties w/out affecting our "cursor".
70+ for ( let j = properties . length ; j > 0 ; -- j ) {
71+ // Extract the unexpanded property from the array.
72+ let property = properties . splice ( j - 1 , 1 ) [ 0 ] ;
73+ // Iterate over the expansion, pushing the newly formed properties onto the array.
74+ for ( let k = 0 ; k < expansion . length ; ++ k ) {
75+ properties . push ( property . slice ( 0 , i - 1 ) +
76+ expansion [ k ] +
77+ property . slice ( bookmark + 1 ) ) ;
78+ }
79+ }
80+ inside = false ;
81+ } else {
82+ assert ( unbalancedNestedError , false ) ;
6083 }
61- }
62-
63- return true ;
64- } ) ( pattern ) ) ;
65-
66- let parts = pattern . split ( SPLIT_REGEX ) ;
67- let properties = [ parts ] ;
68-
69- for ( let i = 0 ; i < parts . length ; i ++ ) {
70- let part = parts [ i ] ;
71- if ( part . indexOf ( ',' ) >= 0 ) {
72- properties = duplicateAndReplace ( properties , part . split ( ',' ) , i ) ;
84+ break ;
7385 }
7486 }
87+ if ( inside ) {
88+ assert ( unbalancedNestedError , false ) ;
89+ }
7590
7691 for ( let i = 0 ; i < properties . length ; i ++ ) {
77- callback ( properties [ i ] . join ( '' ) . replace ( END_WITH_EACH_REGEX , '.[]' ) ) ;
92+ callback ( properties [ i ] . replace ( END_WITH_EACH_REGEX , '.[]' ) ) ;
7893 }
7994}
80-
81- function duplicateAndReplace ( properties , currentParts , index ) {
82- let all = [ ] ;
83-
84- properties . forEach ( property => {
85- currentParts . forEach ( part => {
86- let current = property . slice ( 0 ) ;
87- current [ index ] = part ;
88- all . push ( current ) ;
89- } ) ;
90- } ) ;
91-
92- return all ;
93- }
0 commit comments