Comparing version 0.0.4 to 0.0.5
349
magicpen.js
@@ -54,30 +54,2 @@ (function (global, factory) { | ||
var requireStyles = ['lines', 'text', 'block']; | ||
function Serializer(styles) { | ||
forEach(requireStyles, function (style) { | ||
if (!styles[style]) { | ||
throw new Error("Required style '" + style + "' is missing"); | ||
} | ||
}); | ||
this.styles = styles; | ||
} | ||
Serializer.prototype.serialize = function (lines) { | ||
return this.serializeLines(lines); | ||
}; | ||
Serializer.prototype.serializeLines = function (lines) { | ||
return this.styles.lines(map(lines, function (line) { | ||
return { | ||
indentation: line.indentation, | ||
content: this.serializeLineContent(line) | ||
}; | ||
}, this)); | ||
}; | ||
Serializer.prototype.serializeLineContent = function (content) { | ||
return map(content, this.serializeEntry, this); | ||
}; | ||
function isOutputEntry(obj) { | ||
@@ -87,17 +59,2 @@ return typeof obj === 'object' && 'style' in obj && 'args' in obj; | ||
Serializer.prototype.serializeEntry = function (entry) { | ||
if (entry.style in this.styles) { | ||
return this.styles[entry.style].apply(this.styles, entry.args); | ||
} else { | ||
throw new Error('Unknown style: "' + entry.style + '"'); | ||
} | ||
}; | ||
function createSerializer(styles) { | ||
function CustomSerializer() { | ||
} | ||
CustomSerializer.prototype = new Serializer(styles); | ||
return CustomSerializer; | ||
} | ||
function duplicateText(content, times) { | ||
@@ -114,46 +71,77 @@ var result = ''; | ||
var ansiRegex = /\u001b\[(?:[0-9]{1,3}(?:;[0-9]{1,3})*)?[m|K]/g; | ||
function stripAnsi(text) { | ||
return text.replace(ansiRegex, ''); | ||
} | ||
var TextSerializer = createSerializer({ | ||
text: function (content) { | ||
return content; | ||
}, | ||
lines: function (lines) { | ||
function serializeLine(line) { | ||
var serializedLines = ['']; | ||
function TextSerializer() {} | ||
forEach(line.content, function (inlineBlock, blockIndex) { | ||
var blockLines = map(String(inlineBlock).split('\n'), function (serializedBlockLine) { | ||
return { | ||
content: serializedBlockLine, | ||
length: serializedBlockLine.replace(ansiRegex, '').length | ||
}; | ||
}); | ||
var longestBlockLine = 0; | ||
forEach(blockLines, function (blockLine) { | ||
longestBlockLine = Math.max(longestBlockLine, blockLine.length); | ||
}); | ||
TextSerializer.prototype.serialize = function (lines) { | ||
return map(lines, this.serializeLine, this).join('\n'); | ||
}; | ||
var blockStartIndex = serializedLines[0].replace(ansiRegex, '').length; | ||
serializedLines[0] += blockLines[0].content; | ||
if (blockLines.length > 1 && blockIndex < line.content.length - 1) { | ||
serializedLines[0] += duplicateText(' ', longestBlockLine - blockLines[0].length); | ||
} | ||
TextSerializer.prototype.serializeLine = function (line) { | ||
var serializedLines = ['']; | ||
forEach(blockLines.slice(1), function (blockLine, index) { | ||
var lineIndex = index + 1; | ||
serializedLines[lineIndex] = serializedLines[lineIndex] || ''; | ||
var padding = duplicateText(' ', blockStartIndex - serializedLines[lineIndex].replace(ansiRegex, '').length); | ||
serializedLines[lineIndex] += padding + blockLine.content; | ||
}); | ||
}); | ||
var startIndex = 0; | ||
forEach(line, function (outputEntry, blockIndex) { | ||
var inlineBlock = this[outputEntry.style] ? | ||
this[outputEntry.style].apply(this, outputEntry.args) : | ||
''; | ||
return serializedLines.join('\n'); | ||
var blockLines = map(String(inlineBlock).split('\n'), function (serializedBlockLine) { | ||
return { | ||
content: serializedBlockLine, | ||
length: stripAnsi(serializedBlockLine).length | ||
}; | ||
}); | ||
var longestBlockLine = 0; | ||
forEach(blockLines, function (blockLine) { | ||
longestBlockLine = Math.max(longestBlockLine, blockLine.length); | ||
}); | ||
forEach(blockLines, function (blockLine, index) { | ||
serializedLines[index] = serializedLines[index] || ''; | ||
var padding = duplicateText(' ', startIndex - stripAnsi(serializedLines[index]).length); | ||
serializedLines[index] += padding + blockLine.content; | ||
}); | ||
startIndex += longestBlockLine; | ||
}, this); | ||
return serializedLines.join('\n'); | ||
}; | ||
TextSerializer.prototype.text = function (content) { | ||
return content; | ||
}; | ||
TextSerializer.prototype.block = function (content) { | ||
return this.serialize(content); | ||
}; | ||
TextSerializer.prototype.lineSize = function (line) { | ||
var size = { height: 1, width: 0 }; | ||
forEach(line, function (outputEntry) { | ||
switch (outputEntry.style) { | ||
case 'text': | ||
size.width += String(outputEntry.args[0]).length; | ||
break; | ||
case 'block': | ||
var blockSize = this.size(outputEntry.args[0]); | ||
size.width += blockSize.width; | ||
size.height = Math.max(blockSize.height, size.height); | ||
break; | ||
} | ||
}, this); | ||
return size; | ||
}; | ||
return map(lines, serializeLine).join('\n'); | ||
}, | ||
block: function (content) { | ||
return content; | ||
} | ||
}); | ||
TextSerializer.prototype.size = function (lines) { | ||
var size = { height: 0, width: 0 }; | ||
forEach(lines, function (line) { | ||
var lineSize = this.lineSize(line); | ||
size.height += lineSize.height; | ||
size.width = Math.max(size.width, lineSize.width); | ||
}, this); | ||
return size; | ||
}; | ||
@@ -204,18 +192,19 @@ var ansiStyles = (function () { | ||
var AnsiSerializer = createSerializer(extend({}, TextSerializer.prototype.styles, { | ||
text: function (content) { | ||
if (arguments.length > 1) { | ||
var stylesString = Array.prototype.slice.call(arguments, 1).join(','); | ||
var styles = stylesString.split(/\s*,\s*/); | ||
forEach(styles, function (style) { | ||
if (ansiStyles[style]) { | ||
content = ansiStyles[style].open + content + ansiStyles[style].close; | ||
} | ||
}); | ||
} | ||
function AnsiSerializer() {} | ||
AnsiSerializer.prototype = new TextSerializer(); | ||
return content; | ||
AnsiSerializer.prototype.text = function (content) { | ||
if (arguments.length > 1) { | ||
var stylesString = Array.prototype.slice.call(arguments, 1).join(','); | ||
var styles = stylesString.split(/\s*,\s*/); | ||
forEach(styles, function (style) { | ||
if (ansiStyles[style]) { | ||
content = ansiStyles[style].open + content + ansiStyles[style].close; | ||
} | ||
}); | ||
} | ||
})); | ||
return content; | ||
}; | ||
var htmlStyles = { | ||
@@ -250,56 +239,116 @@ bold: 'font-weight: bold', | ||
var HtmlSerializer = createSerializer({ | ||
text: function (content) { | ||
content = String(content) | ||
.replace(/&/g, '&') | ||
.replace(/ /g, ' ') | ||
.replace(/</g, '<') | ||
.replace(/>/g, '>') | ||
.replace(/"/g, '"'); | ||
function HtmlSerializer() {} | ||
if (arguments.length > 1) { | ||
var stylesString = Array.prototype.slice.call(arguments, 1).join(','); | ||
var styles = filter(stylesString.split(/\s*,\s*/), function (styleName) { | ||
return htmlStyles[styleName]; | ||
}); | ||
HtmlSerializer.prototype.serialize = function (lines) { | ||
return '<code>\n' + this.serializeLines(lines) + '\n</code>'; | ||
}; | ||
content = '<span style="' + map(styles, function (styleName) { | ||
return htmlStyles[styleName]; | ||
}).join('; ') + '">' + content + '</span>'; | ||
} | ||
return content; | ||
}, | ||
lines: function (lines) { | ||
return '<code>\n' + | ||
map(lines, function (line) { | ||
return ' <div>' + line.content.join('') + '</div>'; | ||
}).join('\n') + '\n' + | ||
'</code>'; | ||
}, | ||
block: function (content) { | ||
return '<div style="display: inline-block; vertical-align: top">' + content + '</div>'; | ||
HtmlSerializer.prototype.serializeLines = function (lines) { | ||
return map(lines, function (line) { | ||
return ' <div>' + this.serializeLine(line).join('') + '</div>'; | ||
}, this).join('\n'); | ||
}; | ||
HtmlSerializer.prototype.serializeLine = function (line) { | ||
return map(line, function (outputEntry) { | ||
return this[outputEntry.style] ? | ||
this[outputEntry.style].apply(this, outputEntry.args) : | ||
''; | ||
}, this); | ||
}; | ||
HtmlSerializer.prototype.block = function (content) { | ||
return '<div style="display: inline-block; vertical-align: top">' + | ||
this.serialize(content) + | ||
'</div>'; | ||
}; | ||
HtmlSerializer.prototype.text = function (content) { | ||
content = String(content) | ||
.replace(/&/g, '&') | ||
.replace(/ /g, ' ') | ||
.replace(/</g, '<') | ||
.replace(/>/g, '>') | ||
.replace(/"/g, '"'); | ||
if (arguments.length > 1) { | ||
var stylesString = Array.prototype.slice.call(arguments, 1).join(','); | ||
var styles = filter(stylesString.split(/\s*,\s*/), function (styleName) { | ||
return htmlStyles[styleName]; | ||
}); | ||
content = '<span style="' + map(styles, function (styleName) { | ||
return htmlStyles[styleName]; | ||
}).join('; ') + '">' + content + '</span>'; | ||
} | ||
}); | ||
return content; | ||
}; | ||
var defaults = { | ||
mode: 'text' | ||
HtmlSerializer.prototype.lineSize = function (line) { | ||
var size = { height: 1, width: 0 }; | ||
forEach(line, function (outputEntry) { | ||
switch (outputEntry.style) { | ||
case 'text': | ||
size.width += String(outputEntry.args[0]).length; | ||
break; | ||
case 'block': | ||
var blockSize = this.size(outputEntry.args[0]); | ||
size.width += blockSize.width; | ||
size.height = Math.max(blockSize.height, size.height); | ||
break; | ||
} | ||
}, this); | ||
return size; | ||
}; | ||
function MagicPen(mode) { | ||
HtmlSerializer.prototype.size = function (lines) { | ||
var size = { height: 0, width: 0 }; | ||
forEach(lines, function (line) { | ||
var lineSize = this.lineSize(line); | ||
size.height += lineSize.height; | ||
size.width = Math.max(size.width, lineSize.width); | ||
}, this); | ||
return size; | ||
}; | ||
function MagicPen(options) { | ||
if (this === global) { | ||
return new MagicPen(mode); | ||
return new MagicPen(options); | ||
} | ||
extend(this, defaults, { | ||
indentationLevel: 0, | ||
output: [], | ||
styles: {} | ||
}); | ||
if (typeof options === 'string') { | ||
var mode = options; | ||
options = { | ||
mode: mode | ||
}; | ||
} | ||
this.mode = mode || this.mode; | ||
options = options || {}; | ||
var indentationWidth = 'indentationWidth' in options ? | ||
options.indentationWidth : 2; | ||
this.indentationWidth = Math.max(indentationWidth, 0); | ||
this.mode = options.mode || 'text'; | ||
this.indentationLevel = 0; | ||
this.output = []; | ||
this.styles = {}; | ||
this.serializer = new MagicPen.serializers[this.mode](); | ||
} | ||
MagicPen.prototype.newline = MagicPen.prototype.nl = function () { | ||
this.output.push([]); | ||
MagicPen.prototype.newline = MagicPen.prototype.nl = function (count) { | ||
if (typeof count === 'undefined') { | ||
count = 1; | ||
} | ||
if (count === 0) { | ||
return this; | ||
} | ||
this.output[0] = this.output[0] || []; | ||
for (var i = 0; i < count; i += 1) { | ||
this.output.push([]); | ||
} | ||
return this; | ||
@@ -337,3 +386,3 @@ }; | ||
for (var i = 0; i < this.indentationLevel; i += 1) { | ||
this.space(2); | ||
this.space(this.indentationWidth); | ||
} | ||
@@ -377,3 +426,6 @@ return this; | ||
this.ensurePenWithSameMode(pen); | ||
return this.write('block', pen.toString()); | ||
var blockOutput = map(pen.output, function (line) { | ||
return [].concat(line); | ||
}); | ||
return this.write('block', blockOutput); | ||
}; | ||
@@ -388,3 +440,4 @@ | ||
this.output[0] = this.output[0] || []; | ||
this.output[0].push.apply(this.output[0], pen.output[0]); | ||
var lastLine = this.output[this.output.length - 1]; | ||
Array.prototype.push.apply(lastLine, pen.output[0]); | ||
@@ -401,4 +454,9 @@ this.output.push.apply(this.output, pen.output.slice(1)); | ||
} | ||
var outputToPrepend = Array.prototype.concat.apply([], pen.output); | ||
if (pen.output.length > 1) { | ||
throw new Error('PrependLinesWith only supports a pen with single line content'); | ||
} | ||
var outputToPrepend = [].concat(pen.output[0]); | ||
this.output = map(this.output, function (line) { | ||
@@ -412,3 +470,11 @@ return outputToPrepend.concat(line); | ||
MagicPen.prototype.space = MagicPen.prototype.sp = function (count) { | ||
this.text(duplicateText(' ', count || 1)); | ||
if (count === 0) { | ||
return this; | ||
} | ||
if (typeof count === 'undefined') { | ||
count = 1; | ||
} | ||
this.text(duplicateText(' ', count)); | ||
return this; | ||
@@ -440,3 +506,8 @@ }; | ||
MagicPen.prototype.size = function () { | ||
return this.serializer.size(this.output); | ||
}; | ||
return MagicPen; | ||
})); |
{ | ||
"name": "magicpen", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"description": "Styled output in both consoles and browsers", | ||
@@ -5,0 +5,0 @@ "main": "magicpen.js", |
@@ -100,2 +100,25 @@ # MagicPen | ||
### magicpen(options) or new MagicPen(options) | ||
Creates a new instance of MagicPen with the given options. | ||
The options can be a string or an object. When given a string it will | ||
be interpreted as the mode of the pen. When given an options object | ||
you can set the mode and the indentation width. | ||
Example: | ||
```js | ||
// Pen in text mode with indentation width 2 | ||
magicpen(); | ||
// Pen in text mode with indentation width 4 | ||
magicpen({ indentationWidth: 4 }); | ||
// Pen in ansi mode with indentation width 2 | ||
magicpen('ansi'); | ||
// Pen in html mode with indentation width 2 | ||
magicpen('html'); | ||
// Pen in ansi mode with indentation width 4 | ||
magicpen({ mode: 'ansi', indentationWidth: 4 }); | ||
``` | ||
### text(content, styleString...) | ||
@@ -141,5 +164,5 @@ | ||
### newline(), nl() | ||
### newline(count = 1), nl(count = 1) | ||
Starts a new line. | ||
Starts the given number of new lines. | ||
@@ -174,2 +197,12 @@ ### Indentation | ||
#### indentationWidth | ||
You can control the indentation size by setting indentationWidth | ||
option when creating the pen. | ||
```js | ||
var pen = magicpen({ mode: 'ansi', indentationWidth: 4 }); | ||
``` | ||
### append(pen) | ||
@@ -230,2 +263,18 @@ | ||
### size() | ||
Returns the dimensions of the content of this pen. | ||
Example: | ||
```js | ||
var pen magicpen(); | ||
pen.text('First line').nl() | ||
.text('Second line'); | ||
expect(pen.size(), 'to equal', { | ||
height: 2, | ||
width: 11 | ||
}); | ||
``` | ||
### clone() | ||
@@ -232,0 +281,0 @@ |
@@ -16,7 +16,30 @@ /*global describe, it, beforeEach, before*/ | ||
it('throws if an unknown style is used', function () { | ||
expect(function () { | ||
magicpen().write('test', 'text').toString(); | ||
}, 'to throw', 'Unknown style: "test"'); | ||
}); | ||
function writeComplicatedExampleWithPen(pen) { | ||
pen.addStyle('dragon', function (author) { | ||
this.gray(" / /").nl() | ||
.gray(" /' .,,,, ./").nl() | ||
.gray(" /';' ,/").nl() | ||
.gray(" / / ,,//,`'`").nl() | ||
.gray(" ( ,, '_, ,,,' ``").nl() | ||
.gray(" | /").red("@").gray(" ,,, ;\" `").nl() | ||
.gray(" / . ,''/' `,``").nl() | ||
.gray(" / . ./, `,, ` ;").nl() | ||
.gray(" ,./ . ,-,',` ,,/''\\,'").nl() | ||
.gray("| /; ./,,'`,,'' | |").nl() | ||
.gray("| / ',' / |").nl() | ||
.gray(" \\___/' ' | |").nl() | ||
.gray(" `,,' | / `\\").nl() | ||
.gray(" / | ~\\").nl() | ||
.gray(" ' (").nl() | ||
.gray(" :").nl() | ||
.gray(" ; . \\--").nl() | ||
.gray(" : \\ ; ").blue(author); | ||
}); | ||
pen.blue('This').sp().red('will').nl() | ||
.green('output').sp().yellow('a').sp().cyan('dragon:') | ||
.sp().block(pen.clone().dragon('Ooyamaneko')).append( | ||
pen.clone().nl(2).text('stolen from the interwebs.')) | ||
.prependLinesWith(pen.clone().text(' ')); | ||
return pen; | ||
} | ||
@@ -31,2 +54,9 @@ it('throws when creating a custom style with a name that already exists', function () { | ||
it('throw when prepending lines with multi line pen', function () { | ||
expect(function () { | ||
var multiLinePen = magicpen().text('Multiple').nl().text('Lines'); | ||
magicpen().text('Hello').nl().text('world').prependLinesWith(multiLinePen); | ||
}, 'to throw', 'PrependLinesWith only supports a pen with single line content'); | ||
}); | ||
describe('in text mode', function () { | ||
@@ -37,2 +67,7 @@ beforeEach(function () { | ||
it('ignores unknown styles', function () { | ||
pen.text('>').write('test', 'text').text('<'); | ||
expect(pen.toString(), 'to equal', '><'); | ||
}); | ||
it('handles multi line output', function () { | ||
@@ -113,2 +148,7 @@ pen.red('Hello').nl().green('world'); | ||
}); | ||
it('known the size of the output', function () { | ||
writeComplicatedExampleWithPen(pen); | ||
expect(pen.size(), 'to equal', { height: 21, width: 48 }); | ||
}); | ||
}); | ||
@@ -121,2 +161,6 @@ | ||
it('ignores unknown styles', function () { | ||
pen.text('>').write('test', 'text').text('<'); | ||
expect(pen.toString(), 'to equal', '><'); | ||
}); | ||
@@ -153,2 +197,14 @@ it('handles multi line output', function () { | ||
it('the content of a pen can be appended in a block', function () { | ||
pen.red('Hello').block( | ||
pen.clone() | ||
.gray(' // ').text('This is a').nl() | ||
.indentLines() | ||
.gray(' // ').indent().text('multiline comment')); | ||
expect(pen.toString(), 'to equal', | ||
'\u001b[31mHello\u001b[39m\u001b[90m // \u001b[39mThis is a\n' + | ||
' \u001b[90m // \u001b[39m multiline comment'); | ||
}); | ||
it('styles an be called as methods', function () { | ||
@@ -162,2 +218,7 @@ pen.red('Hello').sp().green('world').text('!', 'red, bold'); | ||
}); | ||
it('known the size of the output', function () { | ||
writeComplicatedExampleWithPen(pen); | ||
expect(pen.size(), 'to equal', { height: 21, width: 48 }); | ||
}); | ||
}); | ||
@@ -170,2 +231,11 @@ | ||
it('ignores unknown styles', function () { | ||
pen.text('>').write('test', 'text').text('<'); | ||
expect(pen.toString(), 'to equal', | ||
'<code>\n' + | ||
' <div>><</div>\n' + | ||
'</code>' | ||
); | ||
}); | ||
it('styles an be called as methods', function () { | ||
@@ -265,2 +335,6 @@ pen.red('Hello').sp().green('world').text('!', 'red, bold'); | ||
it('known the size of the output', function () { | ||
writeComplicatedExampleWithPen(pen); | ||
expect(pen.size(), 'to equal', { height: 21, width: 48 }); | ||
}); | ||
}); | ||
@@ -267,0 +341,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
71523
945
385