| isPlainObject = require 'lodash/isPlainObject' | 
| defaultStyle = require './defaultStyle' | 
| ParsedError = require './ParsedError' | 
| nodePaths = require './nodePaths' | 
| RenderKid = require 'renderkid' | 
| merge = require 'lodash/merge' | 
|   | 
| arrayUtils =  | 
|   pluckByCallback: (a, cb) -> | 
|     return a if a.length < 1 | 
|     removed = 0 | 
|   | 
|     for value, index in a | 
|       if cb value, index | 
|         removed++ | 
|         continue | 
|   | 
|       if removed isnt 0 | 
|         a[index - removed] = a[index] | 
|   | 
|     if removed > 0 | 
|       a.length = a.length - removed | 
|   | 
|     a | 
|   | 
|   pluckOneItem: (a, item) -> | 
|     return a if a.length < 1 | 
|     reached = no | 
|   | 
|     for value, index in a | 
|       if not reached | 
|         if value is item | 
|           reached = yes | 
|           continue | 
|       else | 
|         a[index - 1] = a[index] | 
|   | 
|     a.length = a.length - 1 if reached | 
|     a | 
|   | 
| instance = null | 
|   | 
| module.exports = class PrettyError | 
|   self = @ | 
|   | 
|   @_filters: | 
|     'module.exports': (item) -> | 
|       return unless item.what? | 
|       item.what = item.what.replace /\.module\.exports\./g, ' - ' | 
|       return | 
|   | 
|   @_getDefaultStyle: -> | 
|     defaultStyle() | 
|   | 
|   @start: -> | 
|     unless instance? | 
|       instance = new self | 
|       instance.start() | 
|   | 
|     instance | 
|   | 
|   @stop: -> | 
|     instance?.stop() | 
|   | 
|   constructor: -> | 
|     @_useColors = yes | 
|     @_maxItems = 50 | 
|     @_packagesToSkip = [] | 
|     @_pathsToSkip = [] | 
|     @_skipCallbacks = [] | 
|     @_filterCallbacks = [] | 
|     @_parsedErrorFilters = [] | 
|     @_aliases = [] | 
|     @_renderer = new RenderKid | 
|     @_style = self._getDefaultStyle() | 
|     @_renderer.style @_style | 
|   | 
|   start: -> | 
|     @_oldPrepareStackTrace = Error.prepareStackTrace | 
|   | 
|     prepeare = @_oldPrepareStackTrace or (exc, frames) -> | 
|       result = exc.toString() | 
|       frames = frames.map (frame) -> "  at #{frame.toString()}" | 
|       result + "\n" + frames.join "\n" | 
|   | 
|     # https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi | 
|     Error.prepareStackTrace = (exc, trace) => | 
|       stack = prepeare.apply(null, arguments) | 
|       @render {stack, message: exc.toString().replace /^.*: /, ''}, no | 
|   | 
|     @ | 
|   | 
|   stop: -> | 
|     Error.prepareStackTrace = @_oldPrepareStackTrace | 
|     @_oldPrepareStackTrace = null | 
|   | 
|   config: (c) -> | 
|     if c.skipPackages? | 
|       if c.skipPackages is no | 
|         @unskipAllPackages() | 
|       else | 
|         @skipPackage.apply @, c.skipPackages | 
|   | 
|     if c.skipPaths? | 
|       if c.skipPaths is no | 
|         @unskipAllPaths() | 
|       else | 
|         @skipPath.apply @, c.skipPaths | 
|   | 
|     if c.skip? | 
|       if c.skip is no | 
|         @unskipAll() | 
|       else | 
|         @skip.apply @, c.skip | 
|   | 
|     if c.maxItems? | 
|       @setMaxItems c.maxItems | 
|   | 
|     if c.skipNodeFiles is yes | 
|       @skipNodeFiles() | 
|     else if c.skipNodeFiles is no | 
|       @unskipNodeFiles() | 
|   | 
|     if c.filters? | 
|       if c.filters is no | 
|         @removeAllFilters() | 
|       else | 
|         @filter.apply @, c.filters | 
|   | 
|     if c.parsedErrorFilters? | 
|       if c.parsedErrorFilters is no | 
|         @removeAllParsedErrorFilters() | 
|       else | 
|         @filterParsedError.apply @, c.parsedErrorFilters | 
|   | 
|     if c.aliases? | 
|       if isPlainObject c.aliases | 
|         @alias path, alias for path, alias of c.aliases | 
|       else if c.aliases is no | 
|         @removeAllAliases() | 
|   | 
|     @ | 
|   | 
|   withoutColors: -> | 
|     @_useColors = false | 
|     @ | 
|   | 
|   withColors: -> | 
|     @_useColors = true | 
|     @ | 
|   | 
|   skipPackage: (packages...) -> | 
|     @_packagesToSkip.push String pkg for pkg in packages | 
|     @ | 
|   | 
|   unskipPackage: (packages...) -> | 
|     arrayUtils.pluckOneItem(@_packagesToSkip, pkg) for pkg in packages | 
|     @ | 
|   | 
|   unskipAllPackages: -> | 
|     @_packagesToSkip.length = 0 | 
|     @ | 
|   | 
|   skipPath: (paths...) -> | 
|     @_pathsToSkip.push path for path in paths | 
|     @ | 
|   | 
|   unskipPath: (paths...) -> | 
|     arrayUtils.pluckOneItem(@_pathsToSkip, path) for path in paths | 
|     @ | 
|   | 
|   unskipAllPaths: -> | 
|     @_pathsToSkip.length = 0 | 
|     @ | 
|   | 
|   skip: (callbacks...) -> | 
|     @_skipCallbacks.push cb for cb in callbacks | 
|     @ | 
|   | 
|   unskip: (callbacks...) -> | 
|     arrayUtils.pluckOneItem(@_skipCallbacks, cb) for cb in callbacks | 
|     @ | 
|   | 
|   unskipAll: -> | 
|     @_skipCallbacks.length = 0 | 
|     @ | 
|   | 
|   skipNodeFiles: -> | 
|     @skipPath.apply @, nodePaths | 
|   | 
|   unskipNodeFiles: -> | 
|     @unskipPath.apply @, nodePaths | 
|   | 
|   filter: (callbacks...) -> | 
|     @_filterCallbacks.push cb for cb in callbacks | 
|     @ | 
|   | 
|   removeFilter: (callbacks...) -> | 
|     arrayUtils.pluckOneItem(@_filterCallbacks, cb) for cb in callbacks | 
|     @ | 
|   | 
|   removeAllFilters: -> | 
|     @_filterCallbacks.length = 0 | 
|     @ | 
|   | 
|   filterParsedError: (callbacks...) -> | 
|     @_parsedErrorFilters.push cb for cb in callbacks | 
|     @ | 
|   | 
|   removeParsedErrorFilter: (callbacks...) -> | 
|     arrayUtils.pluckOneItem(@_parsedErrorFilters, cb) for cb in callbacks | 
|     @ | 
|   | 
|   removeAllParsedErrorFilters: -> | 
|     @_parsedErrorFilters.length = 0 | 
|     @ | 
|   | 
|   setMaxItems: (maxItems = 50) -> | 
|     if maxItems is 0 then maxItems = 50 | 
|     @_maxItems = maxItems|0 | 
|     @ | 
|   | 
|   alias: (stringOrRx, alias) -> | 
|     @_aliases.push {stringOrRx, alias} | 
|     @ | 
|   | 
|   removeAlias: (stringOrRx) -> | 
|     arrayUtils.pluckByCallback @_aliases, (pair) -> | 
|       pair.stringOrRx is stringOrRx | 
|   | 
|     @ | 
|   | 
|   removeAllAliases: -> | 
|     @_aliases.length = 0 | 
|     @ | 
|   | 
|   _getStyle: -> | 
|     @_style | 
|   | 
|   appendStyle: (toAppend) -> | 
|     merge @_style, toAppend | 
|     @_renderer.style toAppend | 
|     @ | 
|   | 
|   _getRenderer: -> | 
|     @_renderer | 
|   | 
|   render: (e, logIt = no, useColors = @_useColors) -> | 
|     obj = @getObject e | 
|     rendered = @_renderer.render(obj, useColors) | 
|     console.error rendered if logIt is yes | 
|     rendered | 
|   | 
|   getObject: (e) -> | 
|     unless e instanceof ParsedError | 
|       e = new ParsedError e | 
|   | 
|     @_applyParsedErrorFiltersOn e | 
|   | 
|     header = | 
|       title: do -> | 
|         ret = {} | 
|   | 
|         # some errors are thrown to display other errors. | 
|         # we call them wrappers here. | 
|         if e.wrapper isnt '' | 
|           ret.wrapper = "#{e.wrapper}" | 
|   | 
|         ret.kind = e.kind | 
|         ret | 
|   | 
|       colon: ':' | 
|   | 
|       message: String(e.message).trim() | 
|   | 
|     traceItems = [] | 
|     count = -1 | 
|   | 
|     for item, i in e.trace | 
|       continue unless item? | 
|       continue if @_skipOrFilter(item, i) is yes | 
|   | 
|       count++ | 
|   | 
|       break if count > @_maxItems | 
|   | 
|       if typeof item is 'string' | 
|         traceItems.push item: custom: item | 
|         continue | 
|   | 
|       traceItems.push do -> | 
|         markupItem = item: | 
|           header: | 
|             pointer: do -> | 
|               return '' unless item.file? | 
|   | 
|               file: item.file | 
|               colon: ':' | 
|               line: item.line | 
|   | 
|           footer: do -> | 
|             foooter = addr: item.shortenedAddr | 
|             if item.extra? then foooter.extra = item.extra | 
|             foooter | 
|   | 
|         markupItem.item.header.what = item.what if typeof item.what is 'string' and item.what.trim().length > 0 | 
|         markupItem | 
|   | 
|   | 
|     obj = 'pretty-error': | 
|       header: header | 
|   | 
|     if traceItems.length > 0 | 
|       obj['pretty-error'].trace = traceItems | 
|   | 
|     obj | 
|   | 
|   _skipOrFilter: (item, itemNumber) -> | 
|     if typeof item is 'object' | 
|       return yes if item.modName in @_packagesToSkip | 
|       return yes if item.path in @_pathsToSkip | 
|   | 
|       for modName in item.packages | 
|         return yes if modName in @_packagesToSkip | 
|   | 
|       if typeof item.shortenedAddr is 'string' | 
|         for pair in @_aliases | 
|           item.shortenedAddr = item.shortenedAddr.replace pair.stringOrRx, pair.alias | 
|   | 
|     for cb in @_skipCallbacks | 
|       return yes if cb(item, itemNumber) is yes | 
|   | 
|     for cb in @_filterCallbacks | 
|       cb(item, itemNumber) | 
|   | 
|     return no | 
|   | 
|   _applyParsedErrorFiltersOn: (error) -> | 
|     for cb in @_parsedErrorFilters | 
|       cb error | 
|   | 
|     return | 
|   | 
| for prop in ['renderer', 'style'] then do -> | 
|   methodName = '_get' + prop[0].toUpperCase() + prop.substr(1, prop.length) | 
|   PrettyError::__defineGetter__ prop, -> do @[methodName] |