File: packages/ember-metal/lib/expand_properties.js
import { assert } from './debug';
/**
@module ember
@submodule ember-metal
*/
const SPLIT_REGEX = /\{|\}/;
const END_WITH_EACH_REGEX = /\.@each$/;
/**
Expands `pattern`, invoking `callback` for each expansion.
The only pattern supported is brace-expansion, anything else will be passed
once to `callback` directly.
Example
```js
function echo(arg){ console.log(arg); }
Ember.expandProperties('foo.bar', echo); //=> 'foo.bar'
Ember.expandProperties('{foo,bar}', echo); //=> 'foo', 'bar'
Ember.expandProperties('foo.{bar,baz}', echo); //=> 'foo.bar', 'foo.baz'
Ember.expandProperties('{foo,bar}.baz', echo); //=> 'foo.baz', 'bar.baz'
Ember.expandProperties('foo.{bar,baz}.[]', echo) //=> 'foo.bar.[]', 'foo.baz.[]'
Ember.expandProperties('{foo,bar}.{spam,eggs}', echo) //=> 'foo.spam', 'foo.eggs', 'bar.spam', 'bar.eggs'
Ember.expandProperties('{foo}.bar.{baz}') //=> 'foo.bar.baz'
```
@method expandProperties
@for Ember
@private
@param {String} pattern The property pattern to expand.
@param {Function} callback The callback to invoke. It is invoked once per
expansion, and is passed the expansion.
*/
export default function expandProperties(pattern, callback) {
assert('A computed property key must be a string', typeof pattern === 'string');
assert(
'Brace expanded properties cannot contain spaces, e.g. "user.{firstName, lastName}" should be "user.{firstName,lastName}"',
pattern.indexOf(' ') === -1
);
assert(
`Brace expanded properties have to be balanced and cannot be nested, pattern: ${pattern}`,
((str) => {
let inBrace = 0;
let char;
for (let i = 0; i < str.length; i++) {
char = str.charAt(i);
if (char === '{') {
inBrace++;
} else if (char === '}') {
inBrace--;
}
if (inBrace > 1 || inBrace < 0) {
return false;
}
}
return true;
})(pattern));
let parts = pattern.split(SPLIT_REGEX);
let properties = [parts];
for (let i = 0; i < parts.length; i++) {
let part = parts[i];
if (part.indexOf(',') >= 0) {
properties = duplicateAndReplace(properties, part.split(','), i);
}
}
for (let i = 0; i < properties.length; i++) {
callback(properties[i].join('').replace(END_WITH_EACH_REGEX, '.[]'));
}
}
function duplicateAndReplace(properties, currentParts, index) {
let all = [];
properties.forEach((property) => {
currentParts.forEach((part) => {
let current = property.slice(0);
current[index] = part;
all.push(current);
});
});
return all;
}