title: Scope issue with Coffeescript
body: <p>I have some strange scope issue in Coffeescript.</p>
  
  <p>I can't access to _this from the function <code>@qr.callback</code>, the _this doesn't seem to pass well. I never change this.imgName so the only reason that It doesn't work could be that _this is'nt passed well.</p>
  
  <pre><code>decode:(@callback) -&gt;
      _this= this
      console.log 'before',_this.imgName
      @qr= new QrCode()
      @qr.callback= () -&gt;
          console.log "after:", _this.imgName
      @qr.decode("data:image/png;base64,#{@base64Data}")
  </code></pre>
  
  <p><img src="https://i.stack.imgur.com/gQgGo.png" alt="Console Output"></p>
  
  <p>Edit:</p>
  
  <p>I have tried using </p>
  
  <pre><code>    console.log 'before',@imgName
      @qr= new QrCode()
  
      @qr.callback= () =&gt;
          console.log "after:", @imgName
      @qr.decode("data:image/png;base64,#{@base64Data}")
  </code></pre>
  
  <p>But the output is the same</p>
  
  <p>Edit2: QrCode code: This code comes from  <a href="https://github.com/LazarSoft/jsqrcode" rel="nofollow noreferrer">https://github.com/LazarSoft/jsqrcode</a>. Howewer, as the source code of LazarSoft <a href="https://github.com/LazarSoft/jsqrcode/blob/master/src/qrcode.js" rel="nofollow noreferrer">https://github.com/LazarSoft/jsqrcode/blob/master/src/qrcode.js</a> did'nt contain a QrCode object that you could instantiate many times , I transformed the code to create many different instances of QrCode by creating a QrCode function instead of a global object qrcode.</p>
  
  <pre><code>QrCode= function ()
    {
      this.imagedata = null;
      this.width = 0;
      this.height = 0;
      this.qrCodeSymbol = null;
      this.debug = false;
  
  this.sizeOfDataLengthInfo =  [  [ 10, 9, 8, 8 ],  [ 12, 11, 16, 10 ],  [ 14, 13, 16, 12 ] ];
  
  this.callback = null;
  
  this.decode = function(src){
  
      if(arguments.length==0)
      {
          var canvas_qr = document.getElementById("qr-canvas");
          var context = canvas_qr.getContext('2d');
          this.width = canvas_qr.width;
          this.height = canvas_qr.height;
          this.imagedata = context.getImageData(0, 0, this.width, this.height);
          this.result = this.process(context);
          if(this.callback!=null)
              this.callback(this.result);
          return this.result;
      }
      else
      {
          var image = new Image();
          _this=this
          image.onload=function(){
              //var canvas_qr = document.getElementById("qr-canvas");
              var canvas_qr = document.createElement('canvas');
              var context = canvas_qr.getContext('2d');
              var canvas_out = document.getElementById("out-canvas");
              if(canvas_out!=null)
              {
                  var outctx = canvas_out.getContext('2d');
                  outctx.clearRect(0, 0, 320, 240);
                  outctx.drawImage(image, 0, 0, 320, 240);
              }
              canvas_qr.width = image.width;
              canvas_qr.height = image.height;
              context.drawImage(image, 0, 0);
              _this.width = image.width;
              _this.height = image.height;
              try{
                  _this.imagedata = context.getImageData(0, 0, image.width, image.height);
              }catch(e){
                  _this.result = "Cross domain image reading not supported in your browser! Save it to your computer then drag and drop the file!";
                  if(_this.callback!=null)
                      _this.callback(_this.result);
                  return;
              }
  
              try
              {
                  _this.result = _this.process(context);
              }
              catch(e)
              {
                  // console.log('error:'+e);
                  _this.result = "error decoding QR Code";
              }
              if(_this.callback!=null)
                  _this.callback(_this.result);
          }
          image.src = src;
      }
  }
  
  this.decode_utf8 = function ( s )
  {
    return decodeURIComponent( escape( s ) );
  }
  
  this.process = function(ctx){
  
      var start = new Date().getTime();
  
      var image = this.grayScaleToBitmap(this.grayscale());
      //var image = this.binarize(128);
  
      if(this.debug)
      {
          for (var y = 0; y &lt; this.height; y++)
          {
              for (var x = 0; x &lt; this.width; x++)
              {
                  var point = (x * 4) + (y * this.width * 4);
                  this.imagedata.data[point] = image[x+y*this.width]?0:0;
                  this.imagedata.data[point+1] = image[x+y*this.width]?0:0;
                  this.imagedata.data[point+2] = image[x+y*this.width]?255:0;
              }
          }
          ctx.putImageData(this.imagedata, 0, 0);
      }
  
      //var finderPatternInfo = new FinderPatternFinder().findFinderPattern(image);
  
      var detector = new Detector(image,this);
  
      var qRCodeMatrix = detector.detect();
  
      /*for (var y = 0; y &lt; qRCodeMatrix.bits.Height; y++)
      {
          for (var x = 0; x &lt; qRCodeMatrix.bits.Width; x++)
          {
              var point = (x * 4*2) + (y*2 * this.width * 4);
              this.imagedata.data[point] = qRCodeMatrix.bits.get_Renamed(x,y)?0:0;
              this.imagedata.data[point+1] = qRCodeMatrix.bits.get_Renamed(x,y)?0:0;
              this.imagedata.data[point+2] = qRCodeMatrix.bits.get_Renamed(x,y)?255:0;
          }
      }*/
      if(this.debug)
          ctx.putImageData(this.imagedata, 0, 0);
  
      var reader = Decoder.decode(qRCodeMatrix.bits,this);
      var data = reader.DataByte;
      var str="";
      for(var i=0;i&lt;data.length;i++)
      {
          for(var j=0;j&lt;data[i].length;j++)
              str+=String.fromCharCode(data[i][j]);
      }
  
      var end = new Date().getTime();
      var time = end - start;
      console.log(time);
  
      return this.decode_utf8(str);
      //alert("Time:" + time + " Code: "+str);
  }
  
  this.getPixel = function(x,y){
      if (this.width &lt; x) {
          throw "point error";
      }
      if (this.height &lt; y) {
          throw "point error";
      }
      point = (x * 4) + (y * this.width * 4);
      p = (this.imagedata.data[point]*33 + this.imagedata.data[point + 1]*34 + this.imagedata.data[point + 2]*33)/100;
      return p;
  }
  
  this.binarize = function(th){
      var ret = new Array(this.width*this.height);
      for (var y = 0; y &lt; this.height; y++)
      {
          for (var x = 0; x &lt; this.width; x++)
          {
              var gray = this.getPixel(x, y);
  
              ret[x+y*this.width] = gray&lt;=th?true:false;
          }
      }
      return ret;
  }
  
  this.getMiddleBrightnessPerArea=function(image)
  {
      var numSqrtArea = 4;
      //obtain middle brightness((min + max) / 2) per area
      var areaWidth = Math.floor(this.width / numSqrtArea);
      var areaHeight = Math.floor(this.height / numSqrtArea);
      var minmax = new Array(numSqrtArea);
      for (var i = 0; i &lt; numSqrtArea; i++)
      {
          minmax[i] = new Array(numSqrtArea);
          for (var i2 = 0; i2 &lt; numSqrtArea; i2++)
          {
              minmax[i][i2] = new Array(0,0);
          }
      }
      for (var ay = 0; ay &lt; numSqrtArea; ay++)
      {
          for (var ax = 0; ax &lt; numSqrtArea; ax++)
          {
              minmax[ax][ay][0] = 0xFF;
              for (var dy = 0; dy &lt; areaHeight; dy++)
              {
                  for (var dx = 0; dx &lt; areaWidth; dx++)
                  {
                      var target = image[areaWidth * ax + dx+(areaHeight * ay + dy)*this.width];
                      if (target &lt; minmax[ax][ay][0])
                          minmax[ax][ay][0] = target;
                      if (target &gt; minmax[ax][ay][1])
                          minmax[ax][ay][1] = target;
                  }
              }
              //minmax[ax][ay][0] = (minmax[ax][ay][0] + minmax[ax][ay][1]) / 2;
          }
      }
      var middle = new Array(numSqrtArea);
      for (var i3 = 0; i3 &lt; numSqrtArea; i3++)
      {
          middle[i3] = new Array(numSqrtArea);
      }
      for (var ay = 0; ay &lt; numSqrtArea; ay++)
      {
          for (var ax = 0; ax &lt; numSqrtArea; ax++)
          {
              middle[ax][ay] = Math.floor((minmax[ax][ay][0] + minmax[ax][ay][1]) / 2);
              //Console.out.print(middle[ax][ay] + ",");
          }
          //Console.out.println("");
      }
      //Console.out.println("");
  
      return middle;
  }
  
  this.grayScaleToBitmap=function(grayScale)
  {
      var middle = this.getMiddleBrightnessPerArea(grayScale);
      var sqrtNumArea = middle.length;
      var areaWidth = Math.floor(this.width / sqrtNumArea);
      var areaHeight = Math.floor(this.height / sqrtNumArea);
      var bitmap = new Array(this.height*this.width);
  
      for (var ay = 0; ay &lt; sqrtNumArea; ay++)
      {
          for (var ax = 0; ax &lt; sqrtNumArea; ax++)
          {
              for (var dy = 0; dy &lt; areaHeight; dy++)
              {
                  for (var dx = 0; dx &lt; areaWidth; dx++)
                  {
                      bitmap[areaWidth * ax + dx+ (areaHeight * ay + dy)*this.width] = (grayScale[areaWidth * ax + dx+ (areaHeight * ay + dy)*this.width] &lt; middle[ax][ay])?true:false;
                  }
              }
          }
      }
      return bitmap;
  }
  
  this.grayscale = function(){
      var ret = new Array(this.width*this.height);
      for (var y = 0; y &lt; this.height; y++)
      {
          for (var x = 0; x &lt; this.width; x++)
          {
              var gray = this.getPixel(x, y);
  
              ret[x+y*this.width] = gray;
          }
      }
      return ret;
  }
  
    }
  </code></pre>