var convert = require('convert-source-map')
  , smap = require('source-map')
  , jsdom = require('jsdom')

module.exports = run

function run(script, html) {
  var sourcemap = convert.fromSource(script)

  if(sourcemap) {
    sourcemap = new smap.SourceMapConsumer(sourcemap.toJSON())
  }

  jsdom.env({
      html: html || ' '
    , src: [script]
    , created: setup
    , loaded: loaded
  })

  function setup(err, window) {
    if(err) {
      throw err
    }

    window.Error.prepareStackTrace = prepareStackTrace
    Object.keys(console).forEach(function(name) {
      if(typeof console[name] === 'function') {
        window.console[name] = console[name].bind(console)
      }
    })
  }

  function loaded(errs, window) {
    if(errs && errs.length) {
      throw errs[0].data.error
    }
  }

  function prepareStackTrace(err, stack) {
    return stack.reduce(function(trace, frame) {
      var method = frame.getMethodName()
        , name = frame.getTypeName()
        , out = trace + '\n at '
        , original

      if(sourcemap) {
        original = sourcemap.originalPositionFor({
            line: frame.getLineNumber()
          , column: frame.getColumnNumber()
        })
      }

      if(!original) {
        return out + frame
      }

      if(name === '[object Object]') {
        name = frame.getFunctionName()
      } else if(method === null) {
        name = frame.getTypeName()
        method = frame.getFunctionName()
      }

      return out + ' ' + name + '.' + method + ' (' + original.source + ':' +
        original.line + ':' + original.column + ')'
    }, err)
  }
}

@@ -195,16 +195,48 @@
 (script)
+%0A  var originalPrepareStackTrace
 %0A%0A  if(s
@@ -486,24 +486,87 @@
  err%0A    %7D%0A%0A
+    originalPrepareStackTrace = window.Error.prepareStackTrace%0A
     window.E
@@ -938,22 +938,54 @@
 ) %7B%0A    
-return
+var message%0A%0A    try %7B%0A      message =
  stack.r
@@ -1017,24 +1017,26 @@
 me) %7B%0A      
+  
 var method =
@@ -1054,24 +1054,26 @@
 ethodName()%0A
+  
         , na
@@ -1105,16 +1105,18 @@
         
+  
 , out = 
@@ -1140,16 +1140,18 @@
         
+  
 , origin
@@ -1160,16 +1160,18 @@
 %0A%0A      
+ 
+ 
 if(sourc
@@ -1174,24 +1174,26 @@
 ourcemap) %7B%0A
+  
         orig
@@ -1243,16 +1243,18 @@
         
+  
 line: fr
@@ -1283,16 +1283,18 @@
         
+ 
+ 
 , column
@@ -1327,19 +1327,23 @@
         
+  
 %7D)%0A
+  
       %7D%0A
@@ -1349,16 +1349,18 @@
 %0A%0A      
+  
 if(!orig
@@ -1363,24 +1363,26 @@
 original) %7B%0A
+  
         retu
@@ -1396,16 +1396,18 @@
 + frame%0A
+  
       %7D%0A
@@ -1413,16 +1413,18 @@
 %0A%0A      
+  
 if(name 
@@ -1448,32 +1448,34 @@
 ct%5D') %7B%0A        
+  
 name = frame.get
@@ -1495,16 +1495,18 @@
 )%0A      
+ 
+ 
 %7D else i
@@ -1522,24 +1522,26 @@
 === null) %7B%0A
+  
         name
@@ -1571,16 +1571,18 @@
         
+  
 method =
@@ -1608,27 +1608,31 @@
 ame()%0A      
+ 
+ 
 %7D%0A%0A
+  
       return
@@ -1697,16 +1697,18 @@
 + ':' +%0A
+  
         
@@ -1759,16 +1759,18 @@
     
+  
 %7D, err)%0A
   %7D%0A
@@ -1765,14 +1765,111 @@
 %7D, err)%0A
+    %7D catch(e) %7B%0A      message = originalPrepareStackTrace(err, stack)%0A    %7D%0A%0A    return message%0A
   %7D%0A%7D%0A

revert to original stack trace in case of failure