diff --git a/lib/ansiparse.js b/lib/ansiparse.js index 4a8938b..f6e9b49 100644 --- a/lib/ansiparse.js +++ b/lib/ansiparse.js @@ -1,184 +1,199 @@ -function ansiparse(str) { - // - // I'm terrible at writing parsers. - // - var matchingControl = null, - matchingData = null, - matchingText = '', - ansiState = [], - result = [], - state = {}, - eraseChar; +;(function() { - // - // General workflow for this thing is: - // \033\[33mText - // | | | - // | | matchingText - // | matchingData - // matchingControl - // - // In further steps we hope it's all going to be fine. It usually is. - // + function ansiparse(str) { + // + // I'm terrible at writing parsers. + // + var matchingControl = null, + matchingData = null, + matchingText = '', + ansiState = [], + result = [], + state = {}, + eraseChar; - // - // Erases a char from the output - // - eraseChar = function () { - var index, text; - if (matchingText.length) { - matchingText = matchingText.substr(0, matchingText.length - 1); - } - else if (result.length) { - index = result.length - 1; - text = result[index].text; - if (text.length === 1) { - // - // A result bit was fully deleted, pop it out to simplify the final output - // - result.pop(); + // + // General workflow for this thing is: + // \033\[33mText + // | | | + // | | matchingText + // | matchingData + // matchingControl + // + // In further steps we hope it's all going to be fine. It usually is. + // + + // + // Erases a char from the output + // + eraseChar = function () { + var index, text; + if (matchingText.length) { + matchingText = matchingText.substr(0, matchingText.length - 1); } - else { - result[index].text = text.substr(0, text.length - 1); + else if (result.length) { + index = result.length - 1; + text = result[index].text; + if (text.length === 1) { + // + // A result bit was fully deleted, pop it out to simplify the final output + // + result.pop(); + } + else { + result[index].text = text.substr(0, text.length - 1); + } } - } - }; + }; - for (var i = 0; i < str.length; i++) { - if (matchingControl != null) { - if (matchingControl == '\033' && str[i] == '\[') { - // - // We've matched full control code. Lets start matching formating data. - // + for (var i = 0; i < str.length; i++) { + if (matchingControl != null) { + if (matchingControl == '\033' && str[i] == '\[') { + // + // We've matched full control code. Lets start matching formating data. + // - // - // "emit" matched text with correct state - // - if (matchingText) { - state.text = matchingText; - result.push(state); - state = {}; - matchingText = ""; - } + // + // "emit" matched text with correct state + // + if (matchingText) { + state.text = matchingText; + result.push(state); + state = {}; + matchingText = ""; + } - matchingControl = null; - matchingData = ''; - } - else { - // - // We failed to match anything - most likely a bad control code. We - // go back to matching regular strings. - // - matchingText += matchingControl + str[i]; - matchingControl = null; + matchingControl = null; + matchingData = ''; + } + else { + // + // We failed to match anything - most likely a bad control code. We + // go back to matching regular strings. + // + matchingText += matchingControl + str[i]; + matchingControl = null; + } + continue; } - continue; - } - else if (matchingData != null) { - if (str[i] == ';') { - // - // `;` separates many formatting codes, for example: `\033[33;43m` - // means that both `33` and `43` should be applied. - // - // TODO: this can be simplified by modifying state here. - // - ansiState.push(matchingData); - matchingData = ''; + else if (matchingData != null) { + if (str[i] == ';') { + // + // `;` separates many formatting codes, for example: `\033[33;43m` + // means that both `33` and `43` should be applied. + // + // TODO: this can be simplified by modifying state here. + // + ansiState.push(matchingData); + matchingData = ''; + } + else if (str[i] == 'm') { + // + // `m` finished whole formatting code. We can proceed to matching + // formatted text. + // + ansiState.push(matchingData); + matchingData = null; + matchingText = ''; + + // + // Convert matched formatting data into user-friendly state object. + // + // TODO: DRY. + // + ansiState.forEach(function (ansiCode) { + if (ansiparse.foregroundColors[ansiCode]) { + state.foreground = ansiparse.foregroundColors[ansiCode]; + } + else if (ansiparse.backgroundColors[ansiCode]) { + state.background = ansiparse.backgroundColors[ansiCode]; + } + else if (ansiCode == 39) { + delete state.foreground; + } + else if (ansiCode == 49) { + delete state.background; + } + else if (ansiparse.styles[ansiCode]) { + state[ansiparse.styles[ansiCode]] = true; + } + else if (ansiCode == 22) { + state.bold = false; + } + else if (ansiCode == 23) { + state.italic = false; + } + else if (ansiCode == 24) { + state.underline = false; + } + }); + ansiState = []; + } + else { + matchingData += str[i]; + } + continue; } - else if (str[i] == 'm') { - // - // `m` finished whole formatting code. We can proceed to matching - // formatted text. - // - ansiState.push(matchingData); - matchingData = null; - matchingText = ''; - // - // Convert matched formatting data into user-friendly state object. - // - // TODO: DRY. - // - ansiState.forEach(function (ansiCode) { - if (ansiparse.foregroundColors[ansiCode]) { - state.foreground = ansiparse.foregroundColors[ansiCode]; - } - else if (ansiparse.backgroundColors[ansiCode]) { - state.background = ansiparse.backgroundColors[ansiCode]; - } - else if (ansiCode == 39) { - delete state.foreground; - } - else if (ansiCode == 49) { - delete state.background; - } - else if (ansiparse.styles[ansiCode]) { - state[ansiparse.styles[ansiCode]] = true; - } - else if (ansiCode == 22) { - state.bold = false; - } - else if (ansiCode == 23) { - state.italic = false; - } - else if (ansiCode == 24) { - state.underline = false; - } - }); - ansiState = []; + if (str[i] == '\033') { + matchingControl = str[i]; + } + else if (str[i] == '\u0008') { + eraseChar(); } else { - matchingData += str[i]; + matchingText += str[i]; } - continue; } - if (str[i] == '\033') { - matchingControl = str[i]; - } - else if (str[i] == '\u0008') { - eraseChar(); - } - else { - matchingText += str[i]; + if (matchingText) { + state.text = matchingText + (matchingControl ? matchingControl : ''); + result.push(state); } + return result; } - if (matchingText) { - state.text = matchingText + (matchingControl ? matchingControl : ''); - result.push(state); - } - return result; -} - -ansiparse.foregroundColors = { - '30': 'black', - '31': 'red', - '32': 'green', - '33': 'yellow', - '34': 'blue', - '35': 'magenta', - '36': 'cyan', - '37': 'white', - '90': 'grey' -}; + ansiparse.foregroundColors = { + '30': 'black', + '31': 'red', + '32': 'green', + '33': 'yellow', + '34': 'blue', + '35': 'magenta', + '36': 'cyan', + '37': 'white', + '90': 'grey' + }; -ansiparse.backgroundColors = { - '40': 'black', - '41': 'red', - '42': 'green', - '43': 'yellow', - '44': 'blue', - '45': 'magenta', - '46': 'cyan', - '47': 'white' -}; + ansiparse.backgroundColors = { + '40': 'black', + '41': 'red', + '42': 'green', + '43': 'yellow', + '44': 'blue', + '45': 'magenta', + '46': 'cyan', + '47': 'white' + }; -ansiparse.styles = { - '1': 'bold', - '3': 'italic', - '4': 'underline' -}; + ansiparse.styles = { + '1': 'bold', + '3': 'italic', + '4': 'underline' + }; -module.exports = ansiparse; + // export + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = ansiparse; + } + exports.ansiparse = ansiparse; + } else if (typeof define === 'function' && define.amd) { + define(function() { return ansiparse; }) + } else { + this.ansiparse = ansiparse; + } +}).call(function () { + return this || (typeof window !== 'undefined' ? window : global); +}());