Skip to content

Commit b7403ab

Browse files
committed
Support custom properties on themes
Fixes #18
1 parent 648b3f3 commit b7403ab

File tree

4 files changed

+148
-25
lines changed

4 files changed

+148
-25
lines changed

README.md

+60-15
Original file line numberDiff line numberDiff line change
@@ -638,17 +638,18 @@ expect(pen.toString(), 'to equal', '******');
638638
639639
### installTheme(theme), installTheme(format, theme), installTheme(formats, theme)
640640
641-
MagicPen has support for theming text styles differently for each
642-
format. A theme is just a hash of aliases to built in text styles or
643-
aliases to other theme entries. You install the theme for one or more
644-
formats.
641+
MagicPen has support for theming text styles differently for each format. A
642+
number of theme properties together with a `styles` hash with aliases to built in
643+
text styles or to aliases to other theme entries.
645644
646645
```js
647646
pen.installTheme({
648-
keyword: 'yellow',
649-
functionName: ['green', 'italic'],
650-
primitive: '#FF8DFE',
651-
number: 'primitive'
647+
styles: {
648+
keyword: 'yellow',
649+
functionName: ['green', 'italic'],
650+
primitive: '#FF8DFE',
651+
number: 'primitive'
652+
}
652653
});
653654
```
654655
@@ -688,28 +689,72 @@ usually use when outputting to the html format:
688689
Let's tweak the html colors without touching the ansi colors:
689690
690691
```js
691-
pen.installTheme('html', {
692+
pen.installTheme('html', { styles: {
692693
keyword: 'bold',
693694
functionName: ['#403298', 'italic', 'bold'],
694695
primitive: '#80417F'
695-
});
696+
}});
696697
```
697698
698699
![Fibonacci html output](images/fib-theme-html.png "Fibonacci html output")
699700
700701
You can even extend the current theme:
701702
702703
```js
703-
pen.installTheme('html', {
704+
pen.installTheme('html', { styles: {
704705
functionName: ['#5B9832', 'bold']
705-
});
706+
}});
706707
```
707708
708709
![Fibonacci extended html output](images/fib-theme-extended-html.png "Fibonacci extended html output")
709710
710-
The theme is applied at serialization time, so you can change the theme
711-
and serialize again with the theme applied without touching the
712-
content of the pen.
711+
The theme is applied at serialization time, so you can extend the theme and
712+
serialize again without touching the content of the pen.
713+
714+
## theme(), theme(format)
715+
716+
You can retrieve information about a theme the following way:
717+
718+
```js
719+
pen.installTheme('html', {
720+
accentColors: ['#D50000', '#C51162', '#AA00FF'],
721+
styles: {}
722+
});
723+
724+
pen.installTheme('ansi', {
725+
accentColors: ['#F44336', '#E91E63', '#9C27B0'],
726+
styles: {}
727+
});
728+
729+
expect(pen.theme('html').accentColors,
730+
'to equal',
731+
['#D50000', '#C51162', '#AA00FF']);
732+
733+
expect(pen.theme('ansi').accentColors,
734+
'to equal',
735+
['#F44336', '#E91E63', '#9C27B0']);
736+
```
737+
738+
Calling the `theme` method will return the theme for the given format. If the
739+
format is not specified it will use the format of the pen.
740+
741+
Notice you need a `styles` property even when you don't specify any styles.
742+
743+
Let's use the accent colors define above to make a new style:
744+
745+
```js
746+
pen.addStyle('colored', function (content) {
747+
var accentColors = this.theme().accentColors;
748+
var colorIndex = Math.round(Math.random() * (accentColors.length - 1));
749+
var color = accentColors[colorIndex];
750+
this.text(content, color);
751+
});
752+
753+
console.log(pen.clone('ansi').colored('Colorful').toString());
754+
```
755+
756+
The above style will color the given text with one of the accent colors of the
757+
theme.
713758
714759
## Aliases
715760

lib/MagicPen.js

+22-2
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,17 @@ MagicPen.prototype.replaceText = function (regexp, cb) {
613613
return this;
614614
};
615615

616+
MagicPen.prototype.theme = function (format) {
617+
format = format || this.format;
618+
if (!format) {
619+
throw new Error("Could not detect which format you want to retrieve " +
620+
"theme information for. Set the format of the pen or " +
621+
"provide it as an argument to the theme method.");
622+
}
623+
624+
return this._themes[format];
625+
};
626+
616627
MagicPen.prototype.installTheme = function (formats, theme) {
617628
var that = this;
618629
if (arguments.length === 1) {
@@ -637,7 +648,13 @@ MagicPen.prototype.installTheme = function (formats, theme) {
637648
"Install theme for a list of formats: pen.installTheme(['ansi', 'html'], { comment: 'gray' })");
638649
}
639650

640-
Object.keys(theme).forEach(function (themeKey) {
651+
if (!theme.styles || typeof theme.styles !== 'object') {
652+
theme = {
653+
styles: theme
654+
};
655+
}
656+
657+
Object.keys(theme.styles).forEach(function (themeKey) {
641658
if (rgbRegexp.test(themeKey) || cssStyles[themeKey]) {
642659
throw new Error("Invalid theme key: '" + themeKey + "' you can't map build styles.");
643660
}
@@ -651,7 +668,10 @@ MagicPen.prototype.installTheme = function (formats, theme) {
651668

652669
that._themes = extend({}, that._themes);
653670
formats.forEach(function (format) {
654-
that._themes[format] = extend({}, that._themes[format] || {}, theme);
671+
var baseTheme = that._themes[format] || { styles: {} };
672+
var extendedTheme = extend({}, baseTheme, theme);
673+
extendedTheme.styles = extend({}, baseTheme.styles, theme.styles);
674+
that._themes[format] = extendedTheme;
655675
});
656676

657677

lib/themeMapper.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ module.exports = function (theme, styles) {
33
var count = 0;
44
var stack = [];
55
var themeMapping = styles[0];
6-
while(typeof themeMapping === 'string' && theme[themeMapping]) {
7-
themeMapping = theme[themeMapping];
6+
var themeStyles = theme.styles || {};
7+
while(typeof themeMapping === 'string' && themeStyles[themeMapping]) {
8+
themeMapping = themeStyles[themeMapping];
89
count += 1;
910
if (100 < count) {
1011
var index = stack.indexOf(themeMapping);

test/magicpen.spec.js

+63-6
Original file line numberDiff line numberDiff line change
@@ -1514,9 +1514,12 @@ describe('magicpen', function () {
15141514
beforeEach(function () {
15151515
pen = magicpen();
15161516
pen.installTheme('html', {
1517-
comment: ['#969896', 'italic'],
1518-
keyword: '#bf41ea',
1519-
string: 'keyword'
1517+
accentColors: ['#D50000', '#C51162', '#AA00FF'],
1518+
styles: {
1519+
comment: ['#969896', 'italic'],
1520+
keyword: '#bf41ea',
1521+
string: 'keyword'
1522+
}
15201523
});
15211524

15221525
pen.text('// This is a comment', 'comment').nl();
@@ -1527,8 +1530,48 @@ describe('magicpen', function () {
15271530
pen.text('}');
15281531

15291532
pen.installTheme('ansi', {
1530-
comment: 'grey',
1531-
keyword: 'cyan'
1533+
accentColors: ['#F44336', '#E91E63', '#9C27B0'],
1534+
styles: {
1535+
comment: 'grey',
1536+
keyword: 'cyan'
1537+
}
1538+
});
1539+
});
1540+
1541+
describe('retrieving theme information', function () {
1542+
describe('without specifying the format', function () {
1543+
it('fails if the format of the pen is not decided', function () {
1544+
expect(function () {
1545+
pen.theme();
1546+
}, 'to throw',
1547+
"Could not detect which format you want to retrieve " +
1548+
"theme information for. Set the format of the pen or " +
1549+
"provide it as an argument to the theme method.");
1550+
});
1551+
1552+
it('retrieves the theme information for the format of the pen', function () {
1553+
pen.format = 'html';
1554+
expect(pen.theme(), 'to equal', {
1555+
accentColors: ['#D50000', '#C51162', '#AA00FF'],
1556+
styles: {
1557+
comment: ['#969896', 'italic'],
1558+
keyword: '#bf41ea',
1559+
string: 'keyword'
1560+
}
1561+
});
1562+
});
1563+
});
1564+
1565+
describe('for at specific format', function () {
1566+
it('returns the theme information for the specified format', function () {
1567+
expect(pen.theme('ansi'), 'to equal', {
1568+
accentColors: ['#F44336', '#E91E63', '#9C27B0'],
1569+
styles: {
1570+
comment: 'grey',
1571+
keyword: 'cyan'
1572+
}
1573+
});
1574+
});
15321575
});
15331576
});
15341577

@@ -1561,7 +1604,7 @@ describe('magicpen', function () {
15611604
describe('when the theme is extended', function () {
15621605
beforeEach(function () {
15631606
pen.installTheme(['ansi', 'html'], {
1564-
'methodDefinition': '#55ab40'
1607+
methodDefinition: '#55ab40'
15651608
});
15661609

15671610
pen.installTheme('html', {
@@ -1577,6 +1620,20 @@ describe('magicpen', function () {
15771620
});
15781621
});
15791622

1623+
describe('retrieving theme information', function () {
1624+
it('returns the theme information for the extended theme for the specified format', function () {
1625+
expect(pen.theme('ansi'), 'to equal', {
1626+
accentColors: ['#F44336', '#E91E63', '#9C27B0'],
1627+
styles: {
1628+
comment: '#969896',
1629+
keyword: 'cyan',
1630+
methodDefinition: '#55ab40',
1631+
method: ['#55ab40', 'bold']
1632+
}
1633+
});
1634+
});
1635+
});
1636+
15801637
it('when serializing to html the output uses the extended html theme', function () {
15811638
expect(pen.toString('html'), 'to equal',
15821639
'<div style="font-family: monospace; white-space: nowrap">\n' +

0 commit comments

Comments
 (0)