You could do something like this:
\{(.*?)(?::(.*?))?\}
And a test:
> '{foo:bar}'.match(/\{(.*?)(?::(.*?))?\}/)
["{foo:bar}", "foo", "bar"]
> '{foo}'.match(/\{(.*?)(?::(.*?))?\}/)
["{foo}", "foo", undefined]
(.*?)
non-greedily matches the first group.(?:...)
is a non-capturing group. It's just like a regular capturing group, but it doesn't get captured.:(.*?)
captures the stuff after the colon.?
makes that last group (which contains the colon and the second capturing group) optional.