Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

            
              <div class="container">
  <div class="row header-panel">
    <div class="col-md-12">
      <h1>Back Up Screenshot Editor</h1>
    </div>
  </div>
  <div class="row">
    <div class="col-md-12">
      <p>Take a screenshot of the final frame of the banner. Mac: shift+command+4.<br />Drag &amp; Drop the  screenshot into the banner area. Change size accordingly.<br />Drag image into position. Use arrow keys to move the background 1 px at a time. Press SHIFT for 10px at a time.<br />Change quality if you need to reduce ksize.</p>
    </div>
  </div>
  <div class="row">
    <div class="col-md-12">
      <canvas frameborder="0" id="banner-canvas" width="300" height="250"></canvas>
    </div>
  </div>
  <div class="row">
    <div class="col-md-12">
      <div class="form-group">
        Name: <input id="filename" type="text" class="form-control" placeholder="backup" size="10" />.jpg
      </div>
    </div>
  </div>
  <div class="row">
    <div class="col-md-12">
    <div class="form-group">
      Size:
      <select id="sizes" class="form-control">
							<optgroup label="Horizontal:">
								<option value="300x250">300x250 Medium Rectangle</option>
								<option value="728x90">728x90 Leaderboard</option>
                <option value="468x60">468x60 Main Banner</option>
                <option value="320x50">320x50 Mobile</option>
                <option value="320x100">320x100 Large Mobile</option>
								<option value="970x66">970x66</option>
								<option value="970x250">970x250 Billboard Masthead</option>
								<option value="768x90">768x90 Tablet</option>
								<option value="428x60">428x60 Full Banner</option>
								<option value="250x250">250x250 Square Popup</option>
                <option value="200x200">200x200 Small Square</option>
								<option value="336x280">336x280 Large Rectangle</option>
								<option value="720x300">720x300 Pop-Under</option>
								<option value="940x400">940x400 Website Slider</option>
								<option value="234x60">234x60 Half Banner</option>
								<option value="180x150">180x150 Rectangle</option>
								<option value="125x125">125x125 Square Button</option>
								<option value="120x90">120x90 Button</option>
								<option value="120x60">120x60 Button</option>
								<option value="88x31">88x31 Micro Bar</option>
							</optgroup>
							<optgroup label="Vertical:">
								<option value="160x600">160x600 Wide Skyscraper</option>
								<option value="300x600">300x600 Half Page Ad</option>
								<option value="120x600">120x600 Skyscraper</option>
								<option value="120x240">120x240 Vertical Banner</option>
								<option value="240x400">240x400 Vertical Rectangle</option>
							</optgroup>
              <optgroup label="Facebook">
                <option value="851x315">851x315 Cover</option>
                <option value="1200x900">1200x900 Post</option>
                <option value="1200x628">1200x628 Ad</option>
                <option value="1200x444">1200x444 Page Like Ad</option>
                <option value="600x600">600x600 Click to Website Ad</option>
                <option value="400x400">400x400 Profile Picture</option>
              </optgroup>
              <optgroup label="Twitter">
                <option value="1500x500">1500x500 Cover</option>
                <option value="1024x512">1024x512 Post</option>
                <option value="800x320">800x320 Lead Generation Ad</option>
                <option value="400x400">400x400 Profile Picture</option>
              </optgroup>
              <optgroup label="YouTube">
                <option value="2580x1440">2580x1440 Channel Cover</option>
                <option value="1280x760">1280x760 Video Thumbnail</option>
              </optgroup>  
              <optgroup label="Social Networks">
                <option value="600x1200">600x1200 Pinterest Post</option>
                <option value="1080x608">1080x608 Google Plus Cover</option>
                <option value="1080x1080">1080x1080 Instagram Post</option>
                <option value="1200x628">1200x628 Instagram Ad</option>
              </optgroup> 
						</select>
    </div>
    </div>
  </div>
  <div class="row">
    <div class="col-md-12">
      <div class="form-group">
      <p>Custom: <input type="number" id="width" min="1" value="300" class="form-control num-input" /> x <input type="number" id="height" min="1" value="250" class="form-control num-input" /></p>
      </div>
    </div>
  </div>
  <div class="row">
    <div class="col-md-12">
      <div class="form-group">
      Border color: <input id="bordercolor" value="#666666" type="color" />&nbsp;|<input id="borderTransparent" type="checkbox" class="small-checkbox" checked /><label for="borderTransparent" class="small-checkbox-label">&nbsp;transparent</label>&nbsp;|&nbsp;<input id="isRetina" type="checkbox" class="small-checkbox" /><label for="isRetina" class="small-checkbox-label">&nbsp;Image is retina</label>&nbsp;|&nbsp;Quality percent: &nbsp;<input id="quality" type="number" min="10" step="1" max="100" value="85" class="form-control num-input" /> %&nbsp;|&nbsp;Size: <span class="ksize">0 KB</span>
      </div>
    </div>
  </div>
  <div class="row">
    <div class="col-md-12">
        <button id="create-image" class="btn btn-success">Download Back Up Image...</button>
    </div>

    <div class="row">
      <div class="col-md-12">
        <canvas id="target-canvas"></canvas>
      </div>
    </div>
  </div>
</div>
            
          
!
            
              @import url("https://fonts.googleapis.com/css?family=Righteous");
html,
body {
  min-height: 100% !important;
  height: 100%;
}

body {
  background-color: transparent;
}

a {
  color: white;
}

a:hover {
  color: yellow;
}

body,
html {
  min-height: 100% !important;
  height: 100%;
  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAKElEQVQoU2P8////fwY0cO/ePXQhBsahoPDu3bsYnlFSUsL0zBBQCAAnCzTl/0wlIgAAAABJRU5ErkJggg==
) repeat;
}

h1 {
  color: white;
  text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.7);
  padding-top: 10px;
  padding-left: 0;
  font-family: "Righteous", cursive;
}

p {
  color: black;
  text-shadow: 2px 2px 4px #dddddd;
}

#create-image {
  cursor: pointer;
}

#banner-canvas {
  width: 300px;
  height: 250px;
  background: white;
  cursor: move;
  /* fallback if grab cursor is unsupported */
  cursor: grab;
  cursor: -moz-grab;
  cursor: -webkit-grab;
  margin-bottom: 30px;
}

#banner-canvas.grababble {
  cursor: grabbing;
  cursor: -moz-grabbing;
  cursor: -webkit-grabbing;
}

.num-input {
  width: 80px;
  clear: none;
  display: inline;
}

#sizes,
#filename {
  display: inline;
}

#sizes {
  width: 500px;
}

#filename {
  width: 200px;
}

.ksize {
  font-weight: 800;
  color: white;
  cursor: pointer;
  border: 1px solid black;
  border-radius: 5px;
  padding: 3px;
  width: 100px;
  text-align: center;
  display: inline-block;
  /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#959595+0,0d0d0d+46,010101+50,0a0a0a+53,4e4e4e+76,383838+87,1b1b1b+100;Black+Gloss+Pipe */
  background: #959595;
  /* Old browsers */
  background: -moz-linear-gradient(
    top,
    #959595 0%,
    #0d0d0d 46%,
    #010101 50%,
    #0a0a0a 53%,
    #4e4e4e 76%,
    #383838 87%,
    #1b1b1b 100%
  );
  /* FF3.6-15 */
  background: -webkit-linear-gradient(
    top,
    #959595 0%,
    #0d0d0d 46%,
    #010101 50%,
    #0a0a0a 53%,
    #4e4e4e 76%,
    #383838 87%,
    #1b1b1b 100%
  );
  /* Chrome10-25,Safari5.1-6 */
  background: linear-gradient(
    to bottom,
    #959595 0%,
    #0d0d0d 46%,
    #010101 50%,
    #0a0a0a 53%,
    #4e4e4e 76%,
    #383838 87%,
    #1b1b1b 100%
  );
}

.header-panel {
  margin-bottom: 15px;
  border-radius: 0 0 30px 30px;
  /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#bfd255+0,8eb92a+50,72aa00+51,9ecb2d+100;Green+Gloss */
  background: #bfd255;
  /* Old browsers */
  background: -moz-linear-gradient(
    top,
    #bfd255 0%,
    #8eb92a 50%,
    #72aa00 51%,
    #9ecb2d 100%
  );
  /* FF3.6-15 */
  background: -webkit-linear-gradient(
    top,
    #bfd255 0%,
    #8eb92a 50%,
    #72aa00 51%,
    #9ecb2d 100%
  );
  /* Chrome10-25,Safari5.1-6 */
  background: linear-gradient(
    to bottom,
    #bfd255 0%,
    #8eb92a 50%,
    #72aa00 51%,
    #9ecb2d 100%
  );
}
.small-checkbox {
  display: none;
}
.small-checkbox-label {
  cursor: pointer;
}
.small-checkbox + .small-checkbox-label {
  &:before {
    content: "❌";
  }
}
.small-checkbox:checked + .small-checkbox-label {
  &:before {
    content: "✅";
  }
}

            
          
!
            
              /*
 +-+-+-+-+-+-+-+-+-+-+
 |J|P|G|E|n|c|o|d|e|r|
 +-+-+-+-+-+-+-+-+-+-+
*/
/*

  Basic GUI blocking jpeg encoder ported to JavaScript and optimized by 
  Andreas Ritter, www.bytestrom.eu, 11/2009.

  Example usage is given at the bottom of this file.

  ---------

  Copyright (c) 2008, Adobe Systems Incorporated
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:

  * Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.

  * Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

  * Neither the name of Adobe Systems Incorporated nor the names of its
    contributors may be used to endorse or promote products derived from
    this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

function JPEGEncoder(quality) {
  var self = this;
  var fround = Math.round;
  var ffloor = Math.floor;
  var YTable = new Array(64);
  var UVTable = new Array(64);
  var fdtbl_Y = new Array(64);
  var fdtbl_UV = new Array(64);
  var YDC_HT;
  var UVDC_HT;
  var YAC_HT;
  var UVAC_HT;

  var bitcode = new Array(65535);
  var category = new Array(65535);
  var outputfDCTQuant = new Array(64);
  var DU = new Array(64);
  var byteout = [];
  var bytenew = 0;
  var bytepos = 7;

  var YDU = new Array(64);
  var UDU = new Array(64);
  var VDU = new Array(64);
  var clt = new Array(256);
  var RGB_YUV_TABLE = new Array(2048);
  var currentQuality;

  var ZigZag = [
    0, 1, 5, 6, 14, 15, 27, 28,
    2, 4, 7, 13, 16, 26, 29, 42,
    3, 8, 12, 17, 25, 30, 41, 43,
    9, 11, 18, 24, 31, 40, 44, 53,
    10, 19, 23, 32, 39, 45, 52, 54,
    20, 22, 33, 38, 46, 51, 55, 60,
    21, 34, 37, 47, 50, 56, 59, 61,
    35, 36, 48, 49, 57, 58, 62, 63
  ];

  var std_dc_luminance_nrcodes = [0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0];
  var std_dc_luminance_values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
  var std_ac_luminance_nrcodes = [0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d];
  var std_ac_luminance_values = [
    0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
    0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
    0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
    0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
    0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
    0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
    0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
    0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
    0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
    0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
    0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
    0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
    0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
    0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
    0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
    0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
    0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
    0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
    0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
    0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
    0xf9, 0xfa
  ];

  var std_dc_chrominance_nrcodes = [0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0];
  var std_dc_chrominance_values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
  var std_ac_chrominance_nrcodes = [0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77];
  var std_ac_chrominance_values = [
    0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
    0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
    0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
    0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
    0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
    0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
    0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
    0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
    0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
    0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
    0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
    0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
    0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
    0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
    0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
    0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
    0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
    0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
    0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
    0xf9, 0xfa
  ];

  function initQuantTables(sf) {
    var YQT = [
      16, 11, 10, 16, 24, 40, 51, 61,
      12, 12, 14, 19, 26, 58, 60, 55,
      14, 13, 16, 24, 40, 57, 69, 56,
      14, 17, 22, 29, 51, 87, 80, 62,
      18, 22, 37, 56, 68, 109, 103, 77,
      24, 35, 55, 64, 81, 104, 113, 92,
      49, 64, 78, 87, 103, 121, 120, 101,
      72, 92, 95, 98, 112, 100, 103, 99
    ];

    for (var i = 0; i < 64; i++) {
      var t = ffloor((YQT[i] * sf + 50) / 100);
      if (t < 1) {
        t = 1;
      } else if (t > 255) {
        t = 255;
      }
      YTable[ZigZag[i]] = t;
    }
    var UVQT = [
      17, 18, 24, 47, 99, 99, 99, 99,
      18, 21, 26, 66, 99, 99, 99, 99,
      24, 26, 56, 99, 99, 99, 99, 99,
      47, 66, 99, 99, 99, 99, 99, 99,
      99, 99, 99, 99, 99, 99, 99, 99,
      99, 99, 99, 99, 99, 99, 99, 99,
      99, 99, 99, 99, 99, 99, 99, 99,
      99, 99, 99, 99, 99, 99, 99, 99
    ];
    for (var j = 0; j < 64; j++) {
      var u = ffloor((UVQT[j] * sf + 50) / 100);
      if (u < 1) {
        u = 1;
      } else if (u > 255) {
        u = 255;
      }
      UVTable[ZigZag[j]] = u;
    }
    var aasf = [
      1.0, 1.387039845, 1.306562965, 1.175875602,
      1.0, 0.785694958, 0.541196100, 0.275899379
    ];
    var k = 0;
    for (var row = 0; row < 8; row++) {
      for (var col = 0; col < 8; col++) {
        fdtbl_Y[k] = (1.0 / (YTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
        fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
        k++;
      }
    }
  }

  function computeHuffmanTbl(nrcodes, std_table) {
    var codevalue = 0;
    var pos_in_table = 0;
    var HT = new Array();
    for (var k = 1; k <= 16; k++) {
      for (var j = 1; j <= nrcodes[k]; j++) {
        HT[std_table[pos_in_table]] = [];
        HT[std_table[pos_in_table]][0] = codevalue;
        HT[std_table[pos_in_table]][1] = k;
        pos_in_table++;
        codevalue++;
      }
      codevalue *= 2;
    }
    return HT;
  }

  function initHuffmanTbl() {
    YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes, std_dc_luminance_values);
    UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes, std_dc_chrominance_values);
    YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes, std_ac_luminance_values);
    UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes, std_ac_chrominance_values);
  }

  function initCategoryNumber() {
    var nrlower = 1;
    var nrupper = 2;
    for (var cat = 1; cat <= 15; cat++) {
      //Positive numbers
      for (var nr = nrlower; nr < nrupper; nr++) {
        category[32767 + nr] = cat;
        bitcode[32767 + nr] = [];
        bitcode[32767 + nr][1] = cat;
        bitcode[32767 + nr][0] = nr;
      }
      //Negative numbers
      for (var nrneg = -(nrupper - 1); nrneg <= -nrlower; nrneg++) {
        category[32767 + nrneg] = cat;
        bitcode[32767 + nrneg] = [];
        bitcode[32767 + nrneg][1] = cat;
        bitcode[32767 + nrneg][0] = nrupper - 1 + nrneg;
      }
      nrlower <<= 1;
      nrupper <<= 1;
    }
  }

  function initRGBYUVTable() {
    for (var i = 0; i < 256; i++) {
      RGB_YUV_TABLE[i] = 19595 * i;
      RGB_YUV_TABLE[(i + 256) >> 0] = 38470 * i;
      RGB_YUV_TABLE[(i + 512) >> 0] = 7471 * i + 0x8000;
      RGB_YUV_TABLE[(i + 768) >> 0] = -11059 * i;
      RGB_YUV_TABLE[(i + 1024) >> 0] = -21709 * i;
      RGB_YUV_TABLE[(i + 1280) >> 0] = 32768 * i + 0x807FFF;
      RGB_YUV_TABLE[(i + 1536) >> 0] = -27439 * i;
      RGB_YUV_TABLE[(i + 1792) >> 0] = -5329 * i;
    }
  }

  // IO functions
  function writeBits(bs) {
    var value = bs[0];
    var posval = bs[1] - 1;
    while (posval >= 0) {
      if (value & (1 << posval)) {
        bytenew |= (1 << bytepos);
      }
      posval--;
      bytepos--;
      if (bytepos < 0) {
        if (bytenew == 0xFF) {
          writeByte(0xFF);
          writeByte(0);
        } else {
          writeByte(bytenew);
        }
        bytepos = 7;
        bytenew = 0;
      }
    }
  }

  function writeByte(value) {
    byteout.push(clt[value]); // write char directly instead of converting later
  }

  function writeWord(value) {
    writeByte((value >> 8) & 0xFF);
    writeByte((value) & 0xFF);
  }

  // DCT & quantization core
  function fDCTQuant(data, fdtbl) {
    var d0, d1, d2, d3, d4, d5, d6, d7;
    /* Pass 1: process rows. */
    var dataOff = 0;
    var i;
    const I8 = 8;
    const I64 = 64;
    for (i = 0; i < I8; ++i) {
      d0 = data[dataOff];
      d1 = data[dataOff + 1];
      d2 = data[dataOff + 2];
      d3 = data[dataOff + 3];
      d4 = data[dataOff + 4];
      d5 = data[dataOff + 5];
      d6 = data[dataOff + 6];
      d7 = data[dataOff + 7];

      var tmp0 = d0 + d7;
      var tmp7 = d0 - d7;
      var tmp1 = d1 + d6;
      var tmp6 = d1 - d6;
      var tmp2 = d2 + d5;
      var tmp5 = d2 - d5;
      var tmp3 = d3 + d4;
      var tmp4 = d3 - d4;

      /* Even part */
      var tmp10 = tmp0 + tmp3; /* phase 2 */
      var tmp13 = tmp0 - tmp3;
      var tmp11 = tmp1 + tmp2;
      var tmp12 = tmp1 - tmp2;

      data[dataOff] = tmp10 + tmp11; /* phase 3 */
      data[dataOff + 4] = tmp10 - tmp11;

      var z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */
      data[dataOff + 2] = tmp13 + z1; /* phase 5 */
      data[dataOff + 6] = tmp13 - z1;

      /* Odd part */
      tmp10 = tmp4 + tmp5; /* phase 2 */
      tmp11 = tmp5 + tmp6;
      tmp12 = tmp6 + tmp7;

      /* The rotator is modified from fig 4-8 to avoid extra negations. */
      var z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */
      var z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */
      var z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */
      var z3 = tmp11 * 0.707106781; /* c4 */

      var z11 = tmp7 + z3; /* phase 5 */
      var z13 = tmp7 - z3;

      data[dataOff + 5] = z13 + z2; /* phase 6 */
      data[dataOff + 3] = z13 - z2;
      data[dataOff + 1] = z11 + z4;
      data[dataOff + 7] = z11 - z4;

      dataOff += 8; /* advance pointer to next row */
    }

    /* Pass 2: process columns. */
    dataOff = 0;
    for (i = 0; i < I8; ++i) {
      d0 = data[dataOff];
      d1 = data[dataOff + 8];
      d2 = data[dataOff + 16];
      d3 = data[dataOff + 24];
      d4 = data[dataOff + 32];
      d5 = data[dataOff + 40];
      d6 = data[dataOff + 48];
      d7 = data[dataOff + 56];

      var tmp0p2 = d0 + d7;
      var tmp7p2 = d0 - d7;
      var tmp1p2 = d1 + d6;
      var tmp6p2 = d1 - d6;
      var tmp2p2 = d2 + d5;
      var tmp5p2 = d2 - d5;
      var tmp3p2 = d3 + d4;
      var tmp4p2 = d3 - d4;

      /* Even part */
      var tmp10p2 = tmp0p2 + tmp3p2; /* phase 2 */
      var tmp13p2 = tmp0p2 - tmp3p2;
      var tmp11p2 = tmp1p2 + tmp2p2;
      var tmp12p2 = tmp1p2 - tmp2p2;

      data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */
      data[dataOff + 32] = tmp10p2 - tmp11p2;

      var z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */
      data[dataOff + 16] = tmp13p2 + z1p2; /* phase 5 */
      data[dataOff + 48] = tmp13p2 - z1p2;

      /* Odd part */
      tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */
      tmp11p2 = tmp5p2 + tmp6p2;
      tmp12p2 = tmp6p2 + tmp7p2;

      /* The rotator is modified from fig 4-8 to avoid extra negations. */
      var z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */
      var z2p2 = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */
      var z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */
      var z3p2 = tmp11p2 * 0.707106781; /* c4 */
      var z11p2 = tmp7p2 + z3p2; /* phase 5 */
      var z13p2 = tmp7p2 - z3p2;

      data[dataOff + 40] = z13p2 + z2p2; /* phase 6 */
      data[dataOff + 24] = z13p2 - z2p2;
      data[dataOff + 8] = z11p2 + z4p2;
      data[dataOff + 56] = z11p2 - z4p2;

      dataOff++; /* advance pointer to next column */
    }

    // Quantize/descale the coefficients
    var fDCTQuant;
    for (i = 0; i < I64; ++i) {
      // Apply the quantization and scaling factor & Round to nearest integer
      fDCTQuant = data[i] * fdtbl[i];
      outputfDCTQuant[i] = (fDCTQuant > 0.0) ? ((fDCTQuant + 0.5) | 0) : ((fDCTQuant - 0.5) | 0);
      //outputfDCTQuant[i] = fround(fDCTQuant);

    }
    return outputfDCTQuant;
  }

  function writeAPP0() {
    writeWord(0xFFE0); // marker
    writeWord(16); // length
    writeByte(0x4A); // J
    writeByte(0x46); // F
    writeByte(0x49); // I
    writeByte(0x46); // F
    writeByte(0); // = "JFIF",'\0'
    writeByte(1); // versionhi
    writeByte(1); // versionlo
    writeByte(0); // xyunits
    writeWord(1); // xdensity
    writeWord(1); // ydensity
    writeByte(0); // thumbnwidth
    writeByte(0); // thumbnheight
  }

  function writeSOF0(width, height) {
    writeWord(0xFFC0); // marker
    writeWord(17); // length, truecolor YUV JPG
    writeByte(8); // precision
    writeWord(height);
    writeWord(width);
    writeByte(3); // nrofcomponents
    writeByte(1); // IdY
    writeByte(0x11); // HVY
    writeByte(0); // QTY
    writeByte(2); // IdU
    writeByte(0x11); // HVU
    writeByte(1); // QTU
    writeByte(3); // IdV
    writeByte(0x11); // HVV
    writeByte(1); // QTV
  }

  function writeDQT() {
    writeWord(0xFFDB); // marker
    writeWord(132); // length
    writeByte(0);
    for (var i = 0; i < 64; i++) {
      writeByte(YTable[i]);
    }
    writeByte(1);
    for (var j = 0; j < 64; j++) {
      writeByte(UVTable[j]);
    }
  }

  function writeDHT() {
    writeWord(0xFFC4); // marker
    writeWord(0x01A2); // length

    writeByte(0); // HTYDCinfo
    for (var i = 0; i < 16; i++) {
      writeByte(std_dc_luminance_nrcodes[i + 1]);
    }
    for (var j = 0; j <= 11; j++) {
      writeByte(std_dc_luminance_values[j]);
    }

    writeByte(0x10); // HTYACinfo
    for (var k = 0; k < 16; k++) {
      writeByte(std_ac_luminance_nrcodes[k + 1]);
    }
    for (var l = 0; l <= 161; l++) {
      writeByte(std_ac_luminance_values[l]);
    }

    writeByte(1); // HTUDCinfo
    for (var m = 0; m < 16; m++) {
      writeByte(std_dc_chrominance_nrcodes[m + 1]);
    }
    for (var n = 0; n <= 11; n++) {
      writeByte(std_dc_chrominance_values[n]);
    }

    writeByte(0x11); // HTUACinfo
    for (var o = 0; o < 16; o++) {
      writeByte(std_ac_chrominance_nrcodes[o + 1]);
    }
    for (var p = 0; p <= 161; p++) {
      writeByte(std_ac_chrominance_values[p]);
    }
  }

  function writeSOS() {
    writeWord(0xFFDA); // marker
    writeWord(12); // length
    writeByte(3); // nrofcomponents
    writeByte(1); // IdY
    writeByte(0); // HTY
    writeByte(2); // IdU
    writeByte(0x11); // HTU
    writeByte(3); // IdV
    writeByte(0x11); // HTV
    writeByte(0); // Ss
    writeByte(0x3f); // Se
    writeByte(0); // Bf
  }

  function processDU(CDU, fdtbl, DC, HTDC, HTAC) {
    var EOB = HTAC[0x00];
    var M16zeroes = HTAC[0xF0];
    var pos;
    const I16 = 16;
    const I63 = 63;
    const I64 = 64;
    var DU_DCT = fDCTQuant(CDU, fdtbl);
    //ZigZag reorder
    for (var j = 0; j < I64; ++j) {
      DU[ZigZag[j]] = DU_DCT[j];
    }
    var Diff = DU[0] - DC;
    DC = DU[0];
    //Encode DC
    if (Diff == 0) {
      writeBits(HTDC[0]); // Diff might be 0
    } else {
      pos = 32767 + Diff;
      writeBits(HTDC[category[pos]]);
      writeBits(bitcode[pos]);
    }
    //Encode ACs
    var end0pos = 63; // was const... which is crazy
    for (;
      (end0pos > 0) && (DU[end0pos] == 0); end0pos--) {};
    //end0pos = first element in reverse order !=0
    if (end0pos == 0) {
      writeBits(EOB);
      return DC;
    }
    var i = 1;
    var lng;
    while (i <= end0pos) {
      var startpos = i;
      for (;
        (DU[i] == 0) && (i <= end0pos); ++i) {}
      var nrzeroes = i - startpos;
      if (nrzeroes >= I16) {
        lng = nrzeroes >> 4;
        for (var nrmarker = 1; nrmarker <= lng; ++nrmarker)
          writeBits(M16zeroes);
        nrzeroes = nrzeroes & 0xF;
      }
      pos = 32767 + DU[i];
      writeBits(HTAC[(nrzeroes << 4) + category[pos]]);
      writeBits(bitcode[pos]);
      i++;
    }
    if (end0pos != I63) {
      writeBits(EOB);
    }
    return DC;
  }

  function initCharLookupTable() {
    var sfcc = String.fromCharCode;
    for (var i = 0; i < 256; i++) { ///// ACHTUNG // 255
      clt[i] = sfcc(i);
    }
  }

  this.encode = function(image, quality, toRaw) // image data object
    {
      var time_start = new Date().getTime();

      if (quality) setQuality(quality);

      // Initialize bit writer
      byteout = new Array();
      bytenew = 0;
      bytepos = 7;

      // Add JPEG headers
      writeWord(0xFFD8); // SOI
      writeAPP0();
      writeDQT();
      writeSOF0(image.width, image.height);
      writeDHT();
      writeSOS();

      // Encode 8x8 macroblocks
      var DCY = 0;
      var DCU = 0;
      var DCV = 0;

      bytenew = 0;
      bytepos = 7;

      this.encode.displayName = "_encode_";

      var imageData = image.data;
      var width = image.width;
      var height = image.height;

      var quadWidth = width * 4;
      var tripleWidth = width * 3;

      var x, y = 0;
      var r, g, b;
      var start, p, col, row, pos;
      while (y < height) {
        x = 0;
        while (x < quadWidth) {
          start = quadWidth * y + x;
          p = start;
          col = -1;
          row = 0;

          for (pos = 0; pos < 64; pos++) {
            row = pos >> 3; // /8
            col = (pos & 7) * 4; // %8
            p = start + (row * quadWidth) + col;

            if (y + row >= height) { // padding bottom
              p -= (quadWidth * (y + 1 + row - height));
            }

            if (x + col >= quadWidth) { // padding right
              p -= ((x + col) - quadWidth + 4)
            }

            r = imageData[p++];
            g = imageData[p++];
            b = imageData[p++];

            /* // calculate YUV values dynamically
            YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80
            UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b));
            VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b));
            */

            // use lookup table (slightly faster)
            YDU[pos] = ((RGB_YUV_TABLE[r] + RGB_YUV_TABLE[(g + 256) >> 0] + RGB_YUV_TABLE[(b + 512) >> 0]) >> 16) - 128;
            UDU[pos] = ((RGB_YUV_TABLE[(r + 768) >> 0] + RGB_YUV_TABLE[(g + 1024) >> 0] + RGB_YUV_TABLE[(b + 1280) >> 0]) >> 16) - 128;
            VDU[pos] = ((RGB_YUV_TABLE[(r + 1280) >> 0] + RGB_YUV_TABLE[(g + 1536) >> 0] + RGB_YUV_TABLE[(b + 1792) >> 0]) >> 16) - 128;

          }

          DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);
          DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
          DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
          x += 32;
        }
        y += 8;
      }

      ////////////////////////////////////////////////////////////////

      // Do the bit alignment of the EOI marker
      if (bytepos >= 0) {
        var fillbits = [];
        fillbits[1] = bytepos + 1;
        fillbits[0] = (1 << (bytepos + 1)) - 1;
        writeBits(fillbits);
      }

      writeWord(0xFFD9); //EOI

      if (toRaw) {
        var len = byteout.length;
        var data = new Uint8Array(len);

        for (var i = 0; i < len; i++) {
          data[i] = byteout[i].charCodeAt();
        }

        //cleanup
        byteout = [];

        // benchmarking
        var duration = new Date().getTime() - time_start;
        console.log('Encoding time: ' + duration + 'ms');

        return data;
      }

      var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join(''));

      byteout = [];

      // benchmarking
      var duration = new Date().getTime() - time_start;
      console.log('Encoding time: ' + duration + 'ms');

      return jpegDataUri
    }

  function setQuality(quality) {
    if (quality <= 0) {
      quality = 1;
    }
    if (quality > 100) {
      quality = 100;
    }

    if (currentQuality == quality) return // don't recalc if unchanged

    var sf = 0;
    if (quality < 50) {
      sf = Math.floor(5000 / quality);
    } else {
      sf = Math.floor(200 - quality * 2);
    }

    initQuantTables(sf);
    currentQuality = quality;
    console.log('Quality set to: ' + quality + '%');
  }

  function init() {
    var time_start = new Date().getTime();
    if (!quality) quality = 50;
    // Create tables
    initCharLookupTable()
    initHuffmanTbl();
    initCategoryNumber();
    initRGBYUVTable();

    setQuality(quality);
    var duration = new Date().getTime() - time_start;
    console.log('Initialization ' + duration + 'ms');
  }

  init();

};

/* Example usage. Quality is an int in the range [0, 100]
function example(quality){
    // Pass in an existing image from the page
    var theImg = document.getElementById('testimage');
    // Use a canvas to extract the raw image data
    var cvs = document.createElement('canvas');
    cvs.width = theImg.width;
    cvs.height = theImg.height;
    var ctx = cvs.getContext("2d");
    ctx.drawImage(theImg,0,0);
    var theImgData = (ctx.getImageData(0, 0, cvs.width, cvs.height));
    // Encode the image and get a URI back, toRaw is false by default
    var jpegURI = encoder.encode(theImgData, quality);
    var img = document.createElement('img');
    img.src = jpegURI;
    document.body.appendChild(img);
}

Example usage for getting back raw data and transforming it to a blob.
Raw data is useful when trying to send an image over XHR or Websocket,
it uses around 30% less bytes then a Base64 encoded string. It can
also be useful if you want to save the image to disk using a FileWriter.

NOTE: The browser you are using must support Blobs
function example(quality){
    // Pass in an existing image from the page
    var theImg = document.getElementById('testimage');
    // Use a canvas to extract the raw image data
    var cvs = document.createElement('canvas');
    cvs.width = theImg.width;
    cvs.height = theImg.height;
    var ctx = cvs.getContext("2d");
    ctx.drawImage(theImg,0,0);
    var theImgData = (ctx.getImageData(0, 0, cvs.width, cvs.height));
    // Encode the image and get a URI back, set toRaw to true
    var rawData = encoder.encode(theImgData, quality, true);

    blob = new Blob([rawData.buffer], {type: 'image/jpeg'});
    var jpegURI = URL.createObjectURL(blob);

    var img = document.createElement('img');
    img.src = jpegURI;
    document.body.appendChild(img);
}*/
function formatBytes(bytes, decimals) {
  if (bytes == 0) return '0 Bytes';
  var k = 1000,
    dm = decimals + 1 || 3,
    sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
    i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

/*
 +-+-+-+-+-+-+-+-+-+-+-+-+-+
 |d|o|w|n|l|o|a|d|A|s|J|P|G|
 +-+-+-+-+-+-+-+-+-+-+-+-+-+                                             
*/
function createBlob(ctx, quality, w, h) {
  var imgData = (ctx.getImageData(0, 0, w, h));
  var encoder = new JPEGEncoder(quality);
  var jpegURI = encoder.encode(imgData, quality, true);
  var blob = new Blob([jpegURI.buffer], {
    type: 'image/jpeg'
  });
  var size = formatBytes(blob.size, 1);
  return {
    blob: blob,
    size: size
  };
}

function downloadAsJPG(ctx, quality, w, h, borderColor) {
  /****/
  //Draw the border
  if ($("#borderTransparent").prop("checked")) {
  } else {
    ctx.beginPath();
    ctx.strokeStyle = borderColor;
    ctx.lineWidth = 2;
    ctx.lineCap = "square";
    ctx.lineJoin = "miter";
    ctx.rect(0, 0, w, h);
    ctx.stroke();
  }


  /****/
  /*
  var imgData = (ctx.getImageData(0, 0, w, h));
  var encoder = new JPEGEncoder(quality);
  var jpegURI = encoder.encode(imgData, quality, true);
  var blob = new Blob([jpegURI.buffer], {type: 'image/jpeg'});
  */
  var blob = createBlob(ctx, quality, w, h);
  //var dt = canvas.toDataURL('image/jpeg');
  var img = new Image();
  img.src = URL.createObjectURL(blob.blob); //jpegURI; //dt;
  var size = formatBytes(blob.size, 1);
  var link = document.createElement('a');
  link.href = img.src;
  var fn = $('#filename').val();
  if(fn == ''){
    fn = 'backup' + $('#width').val() + 'x' + $('#height').val();
  }
  link.download = fn + '.jpg';
  var evt = new MouseEvent('click', {
    "view": window,
    "bubbles": true,
    "cancelable": true
  });
  link.dispatchEvent(evt);
}
/*
 +-+-+-+-+ +-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+-+
 |D|r|a|g| |D|r|o|p| |I|m|a|g|e| |L|o|a|d|e|r|
 +-+-+-+-+ +-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+-+
*/
function dragDropImageLoader(dropTarget, imageHandler, onComplete) {
  dropTarget.addEventListener(
    "dragover",
    function(evt) {
      evt.stopPropagation();
      evt.preventDefault();
      evt.dataTransfer.dropEffect = "copy";
    },
    false
  );
  dropTarget.addEventListener(
    "drop",
    function(evt) {
      evt.stopPropagation();
      evt.preventDefault();
      var files = evt.dataTransfer.files,
        len = files.length;
      for (var i = 0; i < len; i++) {
        var file = files[i];
        if (file.type.match(/*image.*/)) {
          var reader = new FileReader();
          reader.onload = (function(file, l, j, onComplete) {
            return function(evt) {
              var img = new Image();
              img.src = evt.target.result;
              img.setAttribute("data-filename", file.name);
              img.setAttribute("data-name", file.name.split(".")[0]);
              //img.setAttribute("width", file.width);
              //img.setAttribute("height", file.height);
              if (img.complete) {
                imageHandler(img);
              } else {
                img.addEventListener("load", function() {
                  imageHandler(this);
                });
              }
              if (onComplete) {
                if (l - 1 == j) {
                  onComplete();
                }
              }
            };
          })(file, len, i, onComplete);
          reader.readAsDataURL(file);
        }
      }
    },
    false
  );
}
var _screenshot;
var _isRetina = 1;
var bannerCanvas;
var bannerCTX;
var _imgPos = {
  x: 0,
  y: 0
};

function getPointerPosition(element, evt) {
  var position = {
    x: 0,
    y: 0
  };
  var rect = element.getBoundingClientRect();
  if (evt.targetTouches) {
    if (evt.targetTouches.length) {
      position.x = evt.targetTouches[0].clientX - rect.left;
      position.y = evt.targetTouches[0].clientY - rect.top;
    }
  } else {
    position.x = evt.clientX - rect.left;
    position.y = evt.clientY - rect.top;
  }
  return position;
}

function renderImage(x, y, w, h) {
  if (_screenshot) {
    bannerCTX.clearRect(0, 0, w, h);
    bannerCTX.drawImage(_screenshot, x, y, Math.ceil(_screenshot.width * _isRetina), Math.ceil(_screenshot.height * _isRetina));
  }
}

/*
   _  _             _   
  | || |_ __ _ _ __| |_ 
 / __) __/ _` | '__| __|
 \__ \ || (_| | |  | |_ 
 (   /\__\__,_|_|   \__|
  |_|                   
*/
$(function() {
  //drop image into canvas
  bannerCanvas = $("#banner-canvas");
  bannerCTX = bannerCanvas[0].getContext('2d');
  var bannerW = 300;
  var bannerH = 250;

  function updateSize() {
    var size = createBlob(bannerCTX, +$('#quality').val(), +$('#width').val(), +$('#height').val()).size;
    $('.ksize').text(size);
  }

  function resizeCanvas(width, height) {
    bannerW = width;
    bannerH = height;
    $('#banner-canvas').attr('width', bannerW).attr('height', bannerH);
    $('#banner-canvas').prop('width', bannerW).prop('height', bannerH);
    $('#banner-canvas').css('width', bannerW + 'px').css('height', bannerH + 'px');
    _imgPos.x = 0;
    _imgPos.y = 0;
    renderImage(_imgPos.x, _imgPos.y, bannerW, bannerH);
  }
  $('.ksize').click(updateSize);
  dragDropImageLoader($('#banner-canvas')[0], function(img) {
    //$('#banner-canvas').append(img);
    _screenshot = img;
    renderImage(0, 0);
  },function(){
    updateSize();
  });
  $('#create-image').click(function(evt) {
    downloadAsJPG(bannerCTX, +$('#quality').val(), +$('#width').val(), +$('#height').val(), $('#bordercolor').val());
    evt.preventDefault();
    evt.stopPropagation();
  });
  $('#width, #height').change(function(evt) {
    resizeCanvas(+$('#width').val(), +$('#height').val());
    updateSize();
    evt.preventDefault();
    evt.stopPropagation();
  });
  $('#sizes').change(function(evt) {
    var thi$ = $(this);
    var val = thi$.val().split('x');
    $('#width').val(val[0]);
    $('#height').val(val[1]);
    resizeCanvas(+val[0], +val[1]);
    updateSize();
    evt.preventDefault();
    evt.stopPropagation();
  });
  $('#quality').change(function(evt) {
    updateSize();
  });
  var $isRetina = $("#isRetina");
  $isRetina.click(function(evt){
    bannerW = Math.round(_screenshot.width/2);
    bannerH = Math.round(_screenshot.height/2);
    $('#width').val(bannerW);
    $('#height').val(bannerH);
    resizeCanvas(bannerW, bannerH);
    /*
    if(_isRetina == 0.5){
      _isRetina = 1;
    }else{
      _isRetina = 0.5;
    }
    renderImage(0, 0);
    */
    if ($isRetina.prop("checked")) {
      _isRetina = 0.5;
    } else {
      _isRetina = 1;
    }
  });
  //UI EVENTS
  var mouse = {
    x: 0,
    y: 0,
    down: false
  };
  bannerCanvas.on('mousedown', function(evt) {
    mouse.down = true;
    var pos = getPointerPosition(bannerCanvas[0], evt);
    mouse.x = pos.x - _imgPos.x;
    mouse.y = pos.y - _imgPos.y;
    bannerCanvas.addClass('grababble');
  });
  bannerCanvas.on('mousemove', function(evt) {
    if (mouse.down) {
      var pos = getPointerPosition(bannerCanvas[0], evt);
      _imgPos.x = pos.x - mouse.x;
      _imgPos.y = pos.y - mouse.y;
      renderImage(_imgPos.x, _imgPos.y, bannerW, bannerH);
    }
  });
  bannerCanvas.on('mouseup', function(evt) {
    if (mouse.down) {
      var pos = getPointerPosition(bannerCanvas[0], evt);
      _imgPos.x = pos.x - mouse.x;
      _imgPos.y = pos.y - mouse.y;
      renderImage(_imgPos.x, _imgPos.y);
      bannerCanvas.removeClass('grababble');
    }
    mouse.down = false;
  });
  $(document).keyup(function(evt) {
    var step = (evt.shiftKey) ? 10 : 1;
    if (evt.which > 36 && evt.which < 41) {
      switch (evt.which) {
        case 38:
          _imgPos.y += -1 * step;
          break;
        case 40:
          _imgPos.y += 1 * step;
          break;
        case 37:
          _imgPos.x += -1 * step;
          break;
        case 39:
          _imgPos.x += 1 * step;
          break;
      }
      renderImage(_imgPos.x, _imgPos.y, bannerW, bannerH);
    }
    evt.preventDefault();
    evt.stopPropagation();
    evt.stopImmediatePropagation();
  });
});
//Prevent scrolling
document.body.addEventListener('touchmove', function(event) {
  event.preventDefault();
}, false);
            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.

Console