Best JavaScript code snippet using testcafe
CanvasDraw.js
Source:CanvasDraw.js  
...353      _this.isDrawing = false;354      _this.isPressing = false;355      if (_this.props.bSelect) {356        _this.ctx.select.globalCompositeOperation = "source-over";357        const xs = _this.getSelectionRectangle(_this.points);358        if (xs.length == 4 && xs[0] > 0) {359          _this.handleSelectionEnd();360          var x = xs[0] | 0;361          var y = xs[1] | 0;362          var w = xs[2] | 0;363          var h = xs[3] | 0;364          if (w < 0) { x += w; w=-w; }365          if (h < 0) { y += h; h=-h; }366          if (w !== 0 && h !== 0) {367            const imageData = _this.ctx.drawing.getImageData(x, y, w, h);368            _this.imageDataToJpg(imageData);369            _this.pasteImageData = imageData;370            _this.pasteMemo.push(_this.pasteImageData);371            _this.ctx.drawing.clearRect(x, y, w, h);372            _this.sel_x = _this.orig_x = x;373            _this.sel_y = _this.orig_y = y;374            _this.sel_w = w;375            _this.sel_h = h;376            _this.ctx.select.putImageData(imageData, x, y);377          }378        }379      }380      if (_this.props.bSelect === false && _this.props.disabled === false && _this.points.length >= 2) _this.props.onDrawFinish && _this.props.onDrawFinish(_this);381      382      if (_this.props.bSmooth === true) {383        console.log("etc");384        const new_format_points = _this.points.map(result => 385          [result["x"], result["y"], 0.5]386        );387        console.log(JSON.stringify(new_format_points));388        const stroked_points = _stroke.getStroke(new_format_points, {389          size: 8,390          thinning: 0.5,391          smoothing: 0.5,392          streamline: 0.5,393          easing: (t) => t,394          simulatePressure: true,395          last: true,396          start: {397            cap: true,398            taper: 0,399            easing: (t) => t,400          },401          end: {402            cap: true,403            taper: 0,404            easing: (t) => t,405          },406        });407        console.log(JSON.stringify(stroked_points));408        // console.log(JSON.stringify(new_format_stroked_points);409        410        for (var i = 0; i < _this.points.length && i < stroked_points.length; i++)411        {412          _this.points[i].x = Math.floor(stroked_points[i][0]);413          _this.points[i].y = Math.floor(stroked_points[i][1]);414        }415       416        console.log(JSON.stringify(_this.points));417        if (_this.points.length >= 2 )418        _this.drawPoints({419          points: _this.points,420          brushColor: _this.props.brushColor,421          brushRadius: _this.props.brushRadius422        });423      }424      _this.saveLine();425    };426    _this.handleCanvasResize = function (entries, observer) {427      var saveData = _this.getSaveData();428      for (var _iterator = entries, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {429        var _ref2;430        if (_isArray) {431          if (_i >= _iterator.length) break;432          _ref2 = _iterator[_i++];433        } else {434          _i = _iterator.next();435          if (_i.done) break;436          _ref2 = _i.value;437        }438        var entry = _ref2;439        var _entry$contentRect = entry.contentRect,440            width = _entry$contentRect.width,441            height = _entry$contentRect.height;442        _this.setCanvasSize(_this.canvas.interface, width, height);443        _this.setCanvasSize(_this.canvas.drawing, width, height);444        _this.setCanvasSize(_this.canvas.temp, width, height);445        _this.setCanvasSize(_this.canvas.select, width, height);446        _this.setCanvasSize(_this.canvas.borders, width, height);447        _this.setCanvasSize(_this.canvas.grid, width, height);448        _this.drawGrid(_this.ctx.grid);449        _this.drawImage();450        _this.loop({ once: true });451      }452      _this.loadSaveData(saveData, true);453    };454    _this.setCanvasSize = function (canvas, width, height) {455      canvas.width = width;456      canvas.height = height;457      canvas.style.width = width;458      canvas.style.height = height;459    };460    _this.isMouseInside = function() {461      const e = _this.latestPosition;462      if (e) {463        var rect = _this.canvas.interface.getBoundingClientRect();464        var clientX = e.clientX;465        var clientY = e.clientY;466        if (clientX < rect.left) return false;467        if (clientX > rect.right) return false;468        if (clientY > rect.bottom) return false;469        if (clientY < rect.top) return false;470        return true;471      }472      return false;473    }474    _this.getPointerPos = function (e) {475      _this.latestPosition = e;476      var rect = _this.canvas.interface.getBoundingClientRect();477      // use cursor pos as default478      var clientX = e.clientX;479      var clientY = e.clientY;480      // use first touch if available481      if (e.changedTouches && e.changedTouches.length > 0) {482        clientX = e.changedTouches[0].clientX;483        clientY = e.changedTouches[0].clientY;484      }485      // return mouse/touch position inside canvas486      const multiply = 1 / _this.props.scale;487      return {488        x: (clientX - rect.left) * multiply,489        y: (clientY - rect.top) * multiply490      };491    };492    _this.handlePointerMove = function (x, y) {493      if (_this.props.disabled) return;494      _this.lazy.update({ x: x, y: y });495      var isDisabled = !_this.lazy.isEnabled();496      if (_this.isPressing && !_this.isDrawing || isDisabled && _this.isPressing) {497        // Start drawing and add point498        _this.isDrawing = true;499        _this.points.push(_this.getPointData());500      }501      if (_this.isDrawing) {502        // Add new point503        _this.points.push(_this.getPointData());504        _this.redo_lines = [];505        // Draw current points506        if (_this.props.bSelect);507        else508          _this.drawPoints({509            points: _this.points,510            brushColor: _this.props.eraseCanvas ? "erase" : _this.props.brushColor,511            brushRadius: _this.props.brushRadius512          });513      }514      _this.mouseHasMoved = true;515    };516    _this.drawPoints = function (_ref3) {517      var points = _ref3.points,518          brushColor = _ref3.brushColor,519          brushRadius = _ref3.brushRadius;520      if (brushColor === "move-select" || brushColor === "paste-select") {521        return;522      }523      _this.ctx.temp.lineJoin = "round";524      _this.ctx.temp.lineCap = "round";525      _this.ctx.temp.strokeStyle = (brushColor === "erase") ? "#FFFFFF" : brushColor;526      this.ctx.drawing.globalCompositeOperation = (brushColor === "erase") ? "destination-out" : "source-over";527      _this.ctx.temp.clearRect(0, 0, _this.ctx.temp.canvas.width, _this.ctx.temp.canvas.height);528      _this.ctx.temp.lineWidth = brushRadius * 2;529      var p1 = points[0];530      var p2 = points[1];531      {532        _this.ctx.temp.moveTo(p2.x, p2.y);533        _this.ctx.temp.beginPath();534        for (var i = 1, len = points.length; i < len; i++) {535          // we pick the point between pi+1 & pi+2 as the536          // end point and p1 as our control point537          var midPoint = midPointBtw(p1, p2);538          _this.ctx.temp.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);539          p1 = points[i];540          p2 = points[i + 1];541        }542        // Draw last line as a straight line while543        // we wait for the next point to be able to calculate544        // the bezier control point545        _this.ctx.temp.lineTo(p1.x, p1.y);546        _this.ctx.temp.stroke();547      }548    };549    _this.saveLine = function () {550      var _ref4 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},551          brushColor = _ref4.brushColor,552          brushRadius = _ref4.brushRadius;553      if (_this.points.length < 2) return;554      if (_this.points[0].erase) {555        brushColor = "erase";556      }557      if (_this.points[0].select) {558        _this.points.length = 0;559        _this.ctx.temp.clearRect(0, 0, width, height);560        return;561      }562      // Save as new line563      _this.lines.push({564        points: [].concat(_this.points),565        brushColor: brushColor || _this.props.brushColor,566        brushRadius: brushRadius || _this.props.brushRadius567      });568      // Reset points array569      _this.points.length = 0;570      var width = _this.canvas.temp.width;571      var height = _this.canvas.temp.height;572      // Copy the line to the drawing canvas573      _this.ctx.drawing.drawImage(_this.canvas.temp, 0, 0, width, height);574      // Clear the temporary line-drawing canvas575      _this.ctx.temp.clearRect(0, 0, width, height);576      _this.triggerOnChange();577    };578    _this.triggerOnChange = function () {579      _this.props.onChange && _this.props.onChange(_this);580    };581    _this.clear = function () {582      _this.lines = [];583      _this.valuesChanged = true;584      _this.ctx.drawing.clearRect(0, 0, _this.canvas.drawing.width, _this.canvas.drawing.height);585      _this.ctx.temp.clearRect(0, 0, _this.canvas.temp.width, _this.canvas.temp.height);586    };587    _this.loop = function () {588      var _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},589          _ref5$once = _ref5.once,590          once = _ref5$once === undefined ? false : _ref5$once;591      _this.ctx.borders.clearRect(0, 0, _this.canvas.borders.width, _this.canvas.borders.height);592      _this.drawSelectBorder(_this.time);593      if (_this.mouseHasMoved || _this.valuesChanged) {594        var pointer = _this.lazy.getPointerCoordinates();595        var brush = _this.lazy.getBrushCoordinates();596        _this.drawInterface(_this.ctx.interface, pointer, brush);597        _this.mouseHasMoved = false;598        _this.valuesChanged = false;599      }600      if (!once) {601        setTimeout(() => {_this.time += 1}, 1);602        window.requestAnimationFrame(function () {603          _this.loop();604        });605      }606    };607    _this.drawGrid = function (ctx) {608      if (_this.props.hideGrid) return;609      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);610      ctx.beginPath();611      ctx.setLineDash([5, 1]);612      ctx.setLineDash([]);613      ctx.strokeStyle = _this.props.gridColor;614      ctx.lineWidth = 0.5;615      var gridSize = 25;616      var countX = 0;617      while (countX < ctx.canvas.width) {618        countX += gridSize;619        ctx.moveTo(countX, 0);620        ctx.lineTo(countX, ctx.canvas.height);621      }622      ctx.stroke();623      var countY = 0;624      while (countY < ctx.canvas.height) {625        countY += gridSize;626        ctx.moveTo(0, countY);627        ctx.lineTo(ctx.canvas.width, countY);628      }629      ctx.stroke();630    };631    632    _this.getSelectionRectangle = function (points) {633      var x = -100;634      var y = -100;635      var w = 10;636      var h = 10;637      if (_this.points.length > 0) {638        x = _this.points[0].x;639        y = _this.points[0].y;640        w = _this.points[_this.points.length-1].x - x;641        h = _this.points[_this.points.length-1].y - y;642      }643      return [x, y, w, h]; 644    };645    _this.drawInterface = function (ctx, pointer, brush) {646      if (_this.props.hideInterface) return;647      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);648      if (_this.props.bSelect) {649        var x = _this.getSelectionRectangle(_this.points);650        ctx.beginPath();651        ctx.globalAlpha = 0.4;652        ctx.fillRect(x[0], x[1], x[2], x[3]);653        ctx.globalAlpha = 1;654        ctx.fillStyle = "#17C69A";655        ctx.lineWidth = 5;656        ctx.stroke();657      }658      else {659        // Draw brush preview660        if (_this.isMouseInside()) {661            ctx.beginPath();662            ctx.fillStyle = this.props.eraseCanvas ? "#FFFFFF" : _this.props.brushColor;663            ctx.arc(brush.x, brush.y, _this.props.brushRadius, 0, Math.PI * 2, true);...image-editor-component.js
Source:image-editor-component.js  
...85  getSelectedIndicator() {86    return this.bitmapImage.indicators.find(i => i.highlighted);87  }88  // Necessary to get a nice rectangle of minimum 1x1. Not used in move mode which uses pixel selection.89  getSelectionRectangle() {90    if (!this.rectStart) return null;91    let [x0, y0] = this.rectStart, [x1, y1] = this.rectEnd;92    return makeSelectionRectangle(x0, y0, x1, y1, this.tool === 'move' ? 0 : 1);93  }94  getSuggestedPastePosition() {95    const start = this.posInTransformedImage([0, 0]);96    return {97      x: Math.ceil(Math.max(start[0], this.visibleArea.x0)),98      y: Math.ceil(Math.max(start[1], this.visibleArea.y0))99    };100  }101  // Tells that the bitmap (this.bitmapImage) has been modified, and the internal caches flushed102  notifyBitmapImageChanged() {103    this.cacheBitmapDirty = true;104  }105  onCopy() {106    if (this.tool === 'rect' && this.rectStart) {107      const result = { rect: this.getSelectionRectangle() };108      this.clearSelection();109      return result;110    } else if (this.tool === 'select') {111      const selected = this.getSelectedIndicator();112      if (selected) {113        return {114          indicator: selected,115          rect: makeRectangleWH(selected.x, selected.y, selected.w, selected.h)116        };117      }118    }119    return null;120  }121  onKeyDown(e) {122    if (e.key === 'Enter' && this.pastedImage) {123      this.bakePastedImage();124    } else if (e.key === 'Escape' && this.pastedImage) {125      this.cancelPaste();126    } else if (e.key === 'Z' && !e.metaKey && !e.ctrlKey) {127      this.resetZoom();128    } else if (e.key === 'y' && !e.metaKey && !e.ctrlKey) {129      this.zoomAt(this.lastMousePos, 0.9);130    } else if (e.key === 'z' && !e.metaKey && !e.ctrlKey) {131      this.zoomAt(this.lastMousePos, 1.1);132    } else if (e.key === 'f' && !e.metaKey && !e.ctrlKey) {133      const selected = this.bitmapImage.indicators.find(i => i.highlighted);134      if (selected) this.focusArea(selected.x, selected.y, selected.w, selected.h, false, true);135    }136    else return false;137    return true;138  }139  onChangeState(goingForward) {140    if (!goingForward) this.cancelPaste();141  }142  // Image: {x, y, width, height, pixels}143  pasteImage(image) {144    if (image.width > this.visibleArea.width || image.height > this.visibleArea.height) {145      return alert(`Image too big to paste here (${image.width}x${image.height}, available ${this.visibleArea.width}x${this.visibleArea.height})`);146    }147    const indicator = image.indicator ? {...image.indicator} : null;148    this.pastedImage = { ...image, indicator };149    this.clearSelection();150    this.notifyBitmapImageChanged();151  }152  onRender(dt) {153    const { context, dpr } = this;154    const { width, height } = this.canvas;155    // Pattern background, scaled according to device pixel ratio156    context.fillStyle = '#d0d0d0';157    context.setTransform(dpr, 0, 0, dpr, 0, 0);158    context.fillRect(0, 0, width, height);159    if (!this.bitmapImage) return;160    this.renderCachedBitmap(context);161    const topLeft = transform([this.visibleArea.x0, this.visibleArea.y0], this.transform);162    const bottomRight = transform([this.visibleArea.x1, this.visibleArea.y1], this.transform);163    context.fillStyle = this.backgroundPattern;164    context.fillRect(topLeft[0], topLeft[1], bottomRight[0] - topLeft[0], bottomRight[1] - topLeft[1]);165    context.transform(this.transform[0], this.transform[1], this.transform[3], this.transform[4], this.transform[6], this.transform[7]);166    context.imageSmoothingEnabled = false;167    this.renderImageList.forEach(p => {168      context.globalAlpha = p.opacity;169      context.drawImage(p.bitmap.getCanvasForDrawing(), this.visibleArea.x0, this.visibleArea.y0,170        p.bitmap.width / this.pixelW, p.bitmap.height / this.pixelH);171    });172    if (['brush', 'place'].includes(this.tool) && this.brushBitmap.isDrawable()) {173      let pos = this.posInTransformedImage(this.lastMousePos);174      if (this.tool === 'brush') pos = floorPos(pos);175      context.globalAlpha = this.blink([0], [1], 1000)[0];176      context.drawImage(this.brushBitmap.getCanvasForDrawing(), pos[0], pos[1], this.brushBitmap.width / this.pixelW, this.brushBitmap.height / this.pixelH);177      // To have a fixed line width (not dependent on transform)178      const posStart = transform(pos, this.transform);179      const posEnd = transform([pos[0] + this.brushBitmap.width / this.pixelW, pos[1] + this.brushBitmap.height / this.pixelH], this.transform);180      context.setTransform(dpr, 0, 0, dpr, 0, 0);181      this.drawDashedRectangle(context, posStart[0], posStart[1], posEnd[0], posEnd[1], '#ff0');182      context.globalAlpha = 1;183    }184    // Draw indicators185    context.font = 'Arial 8px';186    context.setTransform(dpr, 0, 0, dpr, 0, 0);187    this.bitmapImage.indicators.forEach(i => this.drawIndicator(i));188    if (this.pastedImage && this.pastedImage.indicator) {189      this.pastedImage.indicator.x = this.pastedImage.x;190      this.pastedImage.indicator.y = this.pastedImage.y;191      this.pastedImage.indicator.highlighted = false;192      this.drawIndicator(this.pastedImage.indicator, true);193    }194    else {195      this.drawPastedImageRectangle(context);196    }197    this.drawSelectionRectangle(context);198  }199  resetVisibleArea() {200    this.setVisibleArea(0, 0, this.bitmapImage.width, this.bitmapImage.height);201  }202  resetZoom() {203    this.setVisibleArea(this.visibleArea.x0, this.visibleArea.y0, this.visibleArea.width, this.visibleArea.height);204  }205  // Bitmap image is created in api.js (width, height, image, getPixel) and has a `indicators` field206  // which is an array of (x, y, w, h, text, highlighted)207  setBitmapImage(bitmapImage) {208    const needsInitialization = !this.bitmapImage;209    this.bitmapImage = bitmapImage;210    this.pixelW = this.bitmapImage.pixelsPerPixelW || 1;211    this.pixelH = this.bitmapImage.pixelsPerPixelH || 1;212    if (needsInitialization) {213      this.cacheBitmap = new CanvasImageData(this.context, this.bitmapImage.width * this.pixelW, this.bitmapImage.height * this.pixelH);214      mat3.identity(this.transform);215      this.resetVisibleArea();216    }217  }218  setTool(tool) {219    if (this.tool === tool) return;220    this.tool = tool;221    this.clearSelection();222  }223  setVisibleArea(x, y, w, h, focusOnIt = true, preserveZoom = false) {224    if (preserveZoom) {225      translate(this.transform, [this.visibleArea.x0 - x, this.visibleArea.y0 - y]);226    }227    Object.assign(this.visibleArea, { x0: x, y0: y, x1: x + w, y1: y + h });228    if (focusOnIt) this.focusArea(x, y, w, h, this.panMode === 'scroll');229    else this.ensureTransformInVisibleArea();230    this.notifyBitmapImageChanged();231  }232  // ------------------------------ PRIVATE ---------------------------------233  bakePastedImage() {234    if (this.onbakepastedimage && !this.onbakepastedimage(this.pastedImage)) {235      this.cancelPaste();236    }237  }238  clearSelection() {239    this.rectStart = null;240  }241  getHighlighted() {242    return this.bitmapImage.indicators.filter(i => i.highlighted);243  }244  getHighlightedIndices() {245    const indices = [];246    this.bitmapImage.indicators.forEach((i, index) => {247      if (i.highlighted) indices.push(index);248    });249    return indices;250  }251  dehighlightAll() {252    this.bitmapImage.indicators.forEach(i => i.highlighted = false);253  }254  drawIndicator(indicator, pasted) {255    if (indicator.indType === 'object' && !indicator.highlighted) return;256    //if (indicator.x + indicator.w <= this.visibleArea.x0 || indicator.y + indicator.h <= this.visibleArea.y0 || indicator.x >= this.visibleArea.x1 || indicator.y >= this.visibleArea.y1) return;257    const { context } = this;258    const { width, height } = this.canvas;259    const indicatorPos = [indicator.x, indicator.y];260    if (indicator.indType === 'object') {261    	indicatorPos[0] += this.visibleArea.x0;262			indicatorPos[1] += this.visibleArea.y0;263		}264    const start = this.posOnScreen([indicatorPos[0], indicatorPos[1]]);265    const end = this.posOnScreen([indicatorPos[0] + indicator.w, indicatorPos[1] + indicator.h]);266    const x0 = start[0], y0 = start[1], x1 = end[0], y1 = end[1];267    // Outside of screen268    if (x1 < 0 || y1 < 0 || x0 >= width || y0 >= height) return;269    const gradient = context.createLinearGradient(x0, y0, x0, y1);270    let draw = true;271    if (pasted) {272      const lineColor = this.blink([255, 255, 255, 1], [0, 128, 64, 1], 1000, FUNCTIONS.linear);273      gradient.addColorStop(0, makeCssColor(lineColor));274      gradient.addColorStop(1, makeCssColor(lineColor));275      context.lineDashOffset = this.blink([0], [7.999], 300, FUNCTIONS.sawtooth)[0];276      context.setLineDash([5, 3]);277    } else if (indicator.highlighted && !(this.pastedImage && this.pastedImage.indicator)) {278      const colorTop = this.blink([128, 128, 0, 1], [255, 255, 128, 1], 1000);279      const colorBottom = this.blink([255, 0, 0, 1], [255, 128, 0, 1], 1000);280      gradient.addColorStop(0, makeCssColor(colorTop));281      gradient.addColorStop(1, makeCssColor(colorBottom));282      context.lineDashOffset = this.blink([0], [7.999], 300, FUNCTIONS.sawtooth)[0];283      context.setLineDash([5, 3]);284    } else if (!indicator.focused) {285      gradient.addColorStop(0, '#def');286      gradient.addColorStop(1, '#08f');287      context.setLineDash([]);288    } else {289      draw = false;290    }291    if (draw) {292      context.strokeStyle = gradient;293      context.lineWidth = 2;294      context.fillStyle = gradient;295      context.strokeRect(x0, y0, x1 - x0, y1 - y0);296      context.setLineDash([]);297      if (indicator.text) {298        context.strokeStyle = '#000';299        context.strokeText(indicator.text, x0 + 2, y0 + 8);300        context.fillText(indicator.text, x0 + 2, y0 + 8);301      }302    }303    if (indicator.selected && indicator.tw && indicator.th) {304      context.strokeStyle = '#def';305      context.lineWidth = 2;306      context.beginPath();307      // In focused mode of sprites/alike, we will just draw the lines separating the sprites308      for (let y = 0; y + indicator.th <= indicator.h; y += indicator.th)309        for (let x = 0; x + indicator.tw <= indicator.w; x += indicator.tw) {310          const start = this.posOnScreen([indicator.x + x + indicator.tw, indicator.y + y]);311          const end = this.posOnScreen([indicator.x + x, indicator.y + y + indicator.th]);312          context.moveTo(start[0], start[1]);313          if (x + indicator.tw < indicator.w) context.lineTo(start[0], end[1]);314          context.moveTo(start[0], end[1]);315          if (y + indicator.th < indicator.h) context.lineTo(end[0], end[1]);316        }317      context.stroke();318    }319  }320  drawPastedImageRectangle(context) {321    if (!this.pastedImage) return;322    const lineColor = this.blink([255, 255, 255, 1], [0, 128, 64, 1], 1000, FUNCTIONS.linear);323    let [x0, y0] = this.posOnScreen([this.pastedImage.x, this.pastedImage.y]);324    let [x1, y1] = this.posOnScreen([this.pastedImage.x + this.pastedImage.width, this.pastedImage.y + this.pastedImage.height]);325    this.drawDashedRectangle(context, x0, y0, x1, y1, makeCssColor(lineColor));326  }327  drawSelectionRectangle(context) {328    if (this.rectStart) {329      const rect = this.getSelectionRectangle();330      let [x0, y0] = this.posOnScreen([rect.x0, rect.y0]);331      let [x1, y1] = this.posOnScreen([rect.x1, rect.y1]);332      this.drawDashedRectangle(context, x0, y0, x1, y1, '#fff', this.tool === 'cloner' ? 'rgba(0, 0, 255, 0.5)' : null);333    }334  }335  posInTransformedImageClamped(pos) {336    const [x, y] = this.posInTransformedImage(pos);337    return [338      Math.min(this.visibleArea.x1 - 1, Math.max(this.visibleArea.x0, x)),339      Math.min(this.visibleArea.y1 - 1, Math.max(this.visibleArea.y0, y))340    ];341  }342  ensureTransformInVisibleArea() {343    this.ensureTransformInArea(this.visibleArea);344  }345  ensureTransformInArea(visibleArea) {346    const sizeInPixels = subtract(347      this.posOnScreen([visibleArea.x1, visibleArea.y1]),348      this.posOnScreen([visibleArea.x0, visibleArea.y0]));349    const visible = [Math.min(sizeInPixels[0], this.width), Math.min(sizeInPixels[1], this.height)];350    const offset = [(this.width - visible[0]) / 2, (this.height - visible[1]) / 2];351    let posInImage = this.posInTransformedImage([this.width - offset[0], this.height - offset[1]]);352    if (posInImage[0] > visibleArea.x1) translate(this.transform, [posInImage[0] - visibleArea.x1, 0]);353    if (posInImage[1] > visibleArea.y1) translate(this.transform, [0, posInImage[1] - visibleArea.y1]);354    posInImage = this.posInTransformedImage([offset[0], offset[1]]);355    if (posInImage[0] < visibleArea.x0) translate(this.transform, [posInImage[0] - visibleArea.x0, 0]);356    if (posInImage[1] < visibleArea.y0) translate(this.transform, [0, posInImage[1] - visibleArea.y0]);357  }358  indicatorAtPosition(x, y) {359    return this.indicatorsAtPosition(x, y)[0];360  }361  indicatorsAtPosition(x, y) {362    return [...this.bitmapImage.indicators].reverse().filter(i =>363      x >= i.x && y >= i.y && x < i.x + i.w && y < i.y + i.h);364  }365  indicatorsInRect(rect) {366    return this.bitmapImage.indicators.filter(i =>367      i.x + i.w > rect.x0 && i.x < rect.x1 && i.y + i.h > rect.y0 && i.y < rect.y1);368  }369  inPastedImage(x, y) {370    return x >= this.pastedImage.x && y >= this.pastedImage.y && x < this.pastedImage.x + this.pastedImage.width && y < this.pastedImage.y + this.pastedImage.height;371  }372  inVisibleArea(x, y) {373    return x >= this.visibleArea.x0 && x < this.visibleArea.x1 && y >= this.visibleArea.y0 && y < this.visibleArea.y1;374  }375  isRectTool() { return ['rect', 'cloner'].includes(this.tool); }376  concatenateMoveToWriteBuffer(pos) {377    const prev = this.writePathBuffer.slice(this.writePathBuffer.length - 2);378    while (pos[0] !== prev[0] || pos[1] !== prev[1]) {379      // Interpolate move to avoid spots when moving too fast380      for (let i = 0; i < 2; i++) {381        if (pos[i] > prev[i]) prev[i]++;382        else if (pos[i] < prev[i]) prev[i]--;383      }384      this.writePathBuffer = this.writePathBuffer.concat([...prev]);385      this.ondrawpixel(this.cacheBitmap, prev[0] - this.visibleArea.x0, prev[1] - this.visibleArea.y0, this.onrequestpathcolor());386    }387  }388  onDoubleClick(e, mousePos) {389    if (this.tool === 'select') {390      const imagePosition = this.posInTransformedImage(mousePos);391      const indicator = this.indicatorAtPosition(imagePosition[0], imagePosition[1]);392      if (this.onedititem && indicator) this.onedititem(indicator);393    }394  }395  onMouseDown(e, mousePos) {396    // onMouseDown can be called again in case the tool changes immediately (right click, etc.)397    if (!this.isDown) {398      this.isDown = true;399      this.hasMoved = false;400      this.switchedToSecondaryTool = false;401      this.draggedPastedImagePos = this.draggingPastedImage = null;402      this.moveModeLastPos = null;403    }404    if (mouseEventShouldMove(e)) {405      this.moveLastPos = mousePos;406      e.preventDefault(); // middle click triggers a move tool on Windows407    } else if (this.pastedImage && e.button === 0) {408      const transformed = this.posInTransformedImage(mousePos);409      if (this.inPastedImage(transformed[0], transformed[1])) {410        this.draggingPastedImage = transformed;411        // Copy to avoid floating point in pastedImage.x/y412        this.draggedPastedImagePos = [this.pastedImage.x, this.pastedImage.y];413      } else {414        this.bakePastedImage();415        this.isDown = false;416      }417    } else if (this.tool === 'move') {418      const imagePosition = this.posInTransformedImage(mousePos);419      const indicator = this.indicatorAtPosition(imagePosition[0], imagePosition[1]);420      if (indicator) {421        if (e.ctrlKey) {422          this.hasMoved = false;423          indicator.highlighted = !indicator.highlighted;424        } else {425          // We need an extra move to start moving the selection if it's a new selection, not if it was already selected (this is to prevent moving accidentally when you just want to select the item, but when the item is already selected we want to allow precise movement)426          this.hasMoved = indicator.highlighted;427          if (!indicator.highlighted) this.dehighlightAll();428          indicator.highlighted = true;429          this.moveModeInitialPos = this.moveModeLastPos = imagePosition;430        }431        this.onmoveselect(this.getHighlightedIndices());432      } else {433        if (!e.ctrlKey) this.dehighlightAll();434        this.onmoveselect(this.getHighlightedIndices());435        this.rectStart = this.rectEnd = imagePosition;436        this.moveModeInitialPos = null;437        this.onMouseMove(e, mousePos);438      }439    } else if (this.isRectTool()) {440      this.rectStart = floorPos(this.posInTransformedImageClamped(mousePos));441      this.onMouseMove(e, mousePos);442    } else if (this.tool === 'place') {443      const pos = this.posInTransformedImage(mousePos);444      this.brushBitmap.isDrawable() && this.onplacetool(Math.round(pos[0] * this.pixelW), Math.round(pos[1] * this.pixelH));445    } else if (this.tool === 'brush') {446      if (e.button === 2 && this.onswitchtosecondarytool) {447        this.switchedToSecondaryTool = true;448        this.onswitchtosecondarytool(true);449        return this.onMouseDown(e, mousePos);450      }451      const pos = floorPos(this.posInTransformedImage(mousePos));452      this.brushBitmap.isDrawable() && this.onbrushpasted(pos[0], pos[1]);453    } else if (this.tool === 'eyedropper') {454      this.onMouseMove(e, mousePos);455    } else if (this.tool === 'pen') {456      const pos = this.posInTransformedImage(mousePos).map(p => Math.floor(p));457      if (!this.inVisibleArea(pos[0], pos[1])) return;458      if (e.button === 2 && this.onswitchtosecondarytool) {459        this.switchedToSecondaryTool = true;460        this.onswitchtosecondarytool(true);461        return this.onMouseDown(e, mousePos);462      }463      this.writePathBuffer = [...pos];464      this.ondrawpixel(this.cacheBitmap, pos[0] - this.visibleArea.x0, pos[1] - this.visibleArea.y0, this.onrequestpathcolor());465    } else if (this.tool === 'select') {466      const imagePosition = this.posInTransformedImage(mousePos);467      const imagePixelX = imagePosition[0] | 0, imagePixelY = imagePosition[1] | 0;468      if (!this.inVisibleArea(imagePixelX, imagePixelY)) {469        return this.onselectitem(null);470      }471      const indicators = this.indicatorsAtPosition(imagePosition[0], imagePosition[1]);472      if (indicators.length > 1) {473        showMultiSelectDialog(indicators, i => this.onselectitem(indicators[i]));474      } else {475        this.onselectitem(indicators[0]);476      }477    } else {478      const pos = floorPos(this.posInTransformedImageClamped(mousePos));479      this.onothertool && this.onothertool(this.tool, pos[0], pos[1]);480    }481  }482  onMouseMove(e, mousePos) {483    this.lastMousePos = mousePos;484    if (!this.isDown) return;485    if (this.draggingPastedImage) {486      const transformed = this.posInTransformedImage(mousePos);487      const dist = subtract(transformed, this.draggingPastedImage);488      this.draggedPastedImagePos = add(this.draggedPastedImagePos, dist);489      this.pastedImage.x = Math.round(this.draggedPastedImagePos[0]);490      this.pastedImage.y = Math.round(this.draggedPastedImagePos[1]);491      setStatusText(`Pasting at (${this.pastedImage.x}, ${this.pastedImage.y})`);492      this.draggingPastedImage = transformed;493      this.notifyBitmapImageChanged();494    } else if (this.moveLastPos) {495      const dist = subtract(mousePos, this.moveLastPos);496      // Require sensible movement else we consider it as a click497      if (!this.hasMoved && vec2.length(dist) < MIN_DISTANCE_FOR_MOVE) return;498      // Move view499      translate(this.transform, this.distanceInTransformedImage(dist));500      if (this.panMode === 'scroll') this.ensureTransformInVisibleArea();501      this.moveLastPos = mousePos;502      this.hasMoved = true;503    } else if (this.tool === 'move') {504      if (this.moveModeInitialPos) {505        const imagePosition = this.posInTransformedImage(mousePos);506        const dist = this.distanceOnScreen(subtract(imagePosition, this.moveModeLastPos));507        if (!this.hasMoved && vec2.length(dist) < MIN_DISTANCE_FOR_MOVE) return;508        this.getHighlighted().forEach(i => {509          i.x += imagePosition[0] - this.moveModeLastPos[0];510          i.y += imagePosition[1] - this.moveModeLastPos[1];511        });512        this.moveModeLastPos = imagePosition;513        this.hasMoved = true;514      } else {515        this.rectEnd = this.posInTransformedImage(mousePos);516      }517    } else if (this.isRectTool()) {518      this.rectEnd = floorPos(this.posInTransformedImageClamped(mousePos)).map(e => e + 1);519      if (this.tool === 'rect') {520        const sel = this.getSelectionRectangle();521        sel && setStatusText(`Rect size: ${sel.width}x${sel.height} (pos: ${sel.x0 - this.visibleArea.x0}, ${sel.y0 - this.visibleArea.y0})`);522      }523    } else if (this.tool === 'eyedropper') {524      const pos = this.posInTransformedImage(mousePos).map(p => Math.floor(p));525      this.oneyedropper && this.oneyedropper(pos[0], pos[1]);526    } else if (this.writePathBuffer) {527      // Pen mode528      const pos = this.posInTransformedImage(mousePos).map(p => Math.floor(p));529      if (!this.inVisibleArea(pos[0], pos[1])) return;530      this.concatenateMoveToWriteBuffer(pos);531    }532  }533  onMouseOut(e) {534    if (this.writePathBuffer) this.onpenwrite && this.onpenwrite(this.writePathBuffer);535    if (this.tool === 'move' && this.rectStart) {536      const indicators = this.indicatorsInRect(this.getSelectionRectangle());537      indicators.forEach(i => i.highlighted = true);538      this.onmoveselect(this.getHighlightedIndices());539      this.clearSelection();540    } else if (this.tool === 'cloner' && this.rectStart) {541      this.oncloner && this.oncloner(this.getSelectionRectangle());542      this.clearSelection();543      // Cloner always revert to brush tool544      this.onswitchtosecondarytool && this.onswitchtosecondarytool(false);545    }546    this.moveLastPos = this.isDown = this.writePathBuffer = null;547    this.hasMoved = false;548    if (this.switchedToSecondaryTool) this.onswitchtosecondarytool(false);549  }550  onMouseUp(e) {551    if (!this.isDown) return;552    if (this.tool === 'move' && this.hasMoved && this.moveModeLastPos) {553      const move = subtract(this.moveModeLastPos, this.moveModeInitialPos);554      this.onmovetool(this.getHighlightedIndices(), move[0] * this.pixelW, move[1] * this.pixelH);555    }...DrawingInput.js
Source:DrawingInput.js  
...138	const mouseData = useMouseSnapping(drawing, snappers, snappingDistance, applySnapping && !readOnly)139	const { snapper } = mouseData140	// Set up the selection rectangle.141	const isSelecting = !!processSelection && shouldBeSelecting(mouseDownData, startSelection) && mouseData && mouseData.position142	const selectionRectangle = isSelecting ? getSelectionRectangle(mouseDownData, mouseData, drawing) : undefined143	// Set up handler functions.144	const cancelDrag = useCallback(() => {145		setMouseDownData(undefined)146	}, [setMouseDownData])147	// Monitor the mouse going down and up.148	const startDrag = (evt) => {149		if (readOnly)150			return151		if (mouseDownData)152			return setMouseDownData(undefined) // Second touch! Cancel drawing to prevent confusion.153		const newMouseDownData = getMouseData(evt, snapper, drawing)154		const isSelecting = shouldBeSelecting(newMouseDownData, startSelection)155		if (!isSelecting && options.startDrag)156			options.startDrag(newMouseDownData)157		setMouseDownData(newMouseDownData)158	}159	const endDrag = (evt) => {160		if (readOnly || !mouseDownData)161			return162		const mouseUpData = getMouseData(evt, snapper, drawing)163		if (isSelecting && options.processSelection)164			options.processSelection(getSelectionRectangle(mouseDownData, mouseUpData, drawing), mouseUpData.utilKeys)165		if (!isSelecting && options.endDrag)166			options.endDrag(mouseDownData, mouseUpData)167		setMouseDownData(undefined)168	}169	useEventListener(['mousedown', 'touchstart'], startDrag, container, { passive: false })170	useEventListener(['mouseup', 'touchend'], endDrag)171	// Return all data.172	return { ...inputData, mouseData, mouseDownData, selectionRectangle, cancelDrag }173}174// The DrawingInput wrapper needs to be used to add the right classes and to properly position potential feedback.175export function DrawingInputUnforwarded({ Drawing, drawingProperties, className, inputData, options = {} }, drawingRef) {176	const drawingOptions = drawingProperties ? filterProperties(options, drawingProperties) : options177	options = processOptions(options, defaultDrawingInputOptions, true)178	let { maxWidth, stopSnapOnSelection, feedbackIconScale, onDelete } = options179	const { active, readOnly, mouseData, feedback, selectionRectangle } = inputData180	const drawing = drawingRef && drawingRef.current181	// Determine styling of the object.182	const classes = useStyles({183		maxWidth,184		active,185		readOnly,186		feedbackColor: feedback && feedback.color,187		feedbackType: feedback && feedback.type,188		hasFeedbackText: !!(feedback && feedback.text),189	})190	className = clsx(options.className, className, inputData.className, classes.DrawingInput, 'drawingInput', { active })191	// Add snap lines and a feedback icon.192	let { svgContents, htmlContents } = drawingOptions193	svgContents = addSelectionRectangle(svgContents, selectionRectangle, drawing)194	if (!selectionRectangle || !stopSnapOnSelection)195		svgContents = addSnapSvg(svgContents, mouseData, drawing)196	htmlContents = addFeedbackIcon(htmlContents, feedback, drawing, feedbackIconScale)197	// When an onDelete function is given, show a Garbage icon.198	if (onDelete) {199		htmlContents = <>200			{htmlContents}201			<PositionedElement anchor={[1, 1]} position={[drawing.width - 10, drawing.height - 10]} scale={1.5} ><DeleteButton onMouseDown={onDelete} onTouchStart={onDelete} /></PositionedElement>202		</>203	}204	// Show the drawing and the feedback box.205	return <div className={className}>206		<div className="drawing"><Drawing ref={drawingRef} {...{ ...drawingOptions, svgContents, htmlContents }} /></div>207		<div className="feedbackText">{feedback && feedback.text}</div>208	</div>209}210export const DrawingInput = forwardRef(DrawingInputUnforwarded)211export default DrawingInput212// useMouseSnapping wraps all the snapping functionalities into one hook. It takes a drawing, a set of snappers and a snapping distance and takes care of all the mouse functionalities.213function useMouseSnapping(drawing, snappers, snappingDistance, applySnapping) {214	// Process the current mouse position.215	const mousePosition = useMousePosition(drawing)216	const mouseInDrawing = drawing ? drawing.isInside(mousePosition) : false217	// Extract snapping lines and set up a snapper based on it.218	const snappingLines = useSnappingLines(snappers)219	const snapper = useCallback((point) => snapMousePosition(point, snappingLines, snappingDistance, applySnapping), [snappingLines, snappingDistance, applySnapping])220	const snapResult = snapper(mousePosition)221	// Return all data.222	return { mousePosition, mouseInDrawing, snappingLines, snapper, ...snapResult }223}224// useSnappingLines takes a snappers array and determines the snapping lines from it. It only recalculates on a change and filters duplicates.225function useSnappingLines(snappers) {226	snappers = useConsistentValue(snappers)227	return useMemo(() => {228		const snappingLines = []229		snappers.forEach(snapper => {230			if (snapper instanceof Line) {231				snappingLines.push(snapper)232			} else if (snapper instanceof Vector) {233				snappingLines.push(Line.getHorizontalThrough(snapper))234				snappingLines.push(Line.getVerticalThrough(snapper))235			} else if (snapper instanceof PositionedVector) {236				snappingLines.push(snapper.line)237			} else {238				throw new Error(`Invalid snapper: received a snapper with unexpected type. Make sure it is a vector, line or other allowed type.`)239			}240		})241		return filterDuplicates(snappingLines, (a, b) => a.equals(b))242	}, [snappers])243}244// snapMousePosition will calculate the position of the mouse after it's snapped to the nearest snapping line.245function snapMousePosition(position, snappingLines, snappingDistance, applySnapping) {246	// If there is no mouse position or no snapping should be applied, give a default response.247	if (!position || !applySnapping)248		return { position, snappedPosition: position, snapLines: [], isSnapped: false, isSnappedTwice: false }249	// Get all the lines that fall within snapping distance.250	const squaredSnappingDistance = snappingDistance ** 2251	const snappingLineSquaredDistances = snappingLines.map(line => line.getSquaredDistanceFrom(position)) // Calculate the squared distances.252	const selectedLines = numberArray(0, snappingLines.length - 1).filter(index => snappingLineSquaredDistances[index] <= squaredSnappingDistance) // Filter out all lines that are too far, and store the indices of the selected lines.253	let snapLines = sortByIndices(selectedLines.map(index => snappingLines[index]), selectedLines.map(index => snappingLineSquaredDistances[index])) // Sort by distance.254	// Depending on how many snap lines there are, snap the mouse position accordingly.255	let snappedPosition = position256	if (snapLines.length > 1) { // Multiple lines. Find the intersection and check that it's close enough to the mouse point.257		const intersection = snapLines[0].getIntersection(snapLines[1])258		if (intersection.squaredDistanceTo(position) <= squaredSnappingDistance) {259			snappedPosition = intersection260			snapLines = snapLines.filter(line => line.containsPoint(snappedPosition)) // Get rid of all snapping lines that don't go through this point.261		} else {262			snapLines = snapLines.slice(0, 1) // The snap position is too far from the mouse position. Only take the closest line and use that.263		}264	}265	if (snapLines.length === 1)266		snappedPosition = snapLines[0].getClosestPoint(position)267	const isSnapped = snapLines.length > 0268	const isSnappedTwice = snapLines.length > 1269	// Return the outcome.270	return { position, snappedPosition, snapLines, isSnapped, isSnappedTwice }271}272export function getMouseData(evt, snapper, drawing) {273	return { ...snapper(drawing.getPosition(getEventPosition(evt))), utilKeys: getUtilKeys(evt) }274}275// getSnapSvg takes a snapped mouse position and snap lines, and returns SVG to show the marker and the lines.276export function getSnapSvg(mouseData, drawing, lineStyle = {}, markerStyle = {}, snapMarkerSize = 6) {277	const { position, snappedPosition, snapLines } = mouseData278	const bounds = drawing && drawing.bounds279	// Don't show things when the mouse is outside the drawing.280	if (!drawing.isInside(position))281		return {}282	// Show the snap marker and lines.283	return {284		marker: snapLines.length > 0 ? <Square center={snappedPosition} side={snapMarkerSize} className="snapMarker" style={markerStyle} /> : null,285		lines: bounds ? snapLines.map((line, index) => {286			const linePart = bounds.getLinePart(line)287			return <SvgLine key={index} className="snapLine" points={[linePart.start, linePart.end]} style={lineStyle} />288		}) : [],289	}290}291// addSnapSvg takes SVG elements and adds snap lines to it.292export function addSnapSvg(svgContents, mouseData, drawing) {293	// If the drawing is not there yet, don't add lines.294	if (!drawing)295		return svgContents296	// Get the lines and marker and display them in the right order.297	const snapSvg = getSnapSvg(mouseData, drawing)298	return <>299		{snapSvg.lines}300		{svgContents}301		{snapSvg.marker}302	</>303}304// addFeedbackIcon takes HTML elements and adds a feedback icon to it.305export function addFeedbackIcon(htmlContents, feedback, drawing, scale = 1) {306	if (!feedback || !feedback.Icon)307		return htmlContents308	return <>309		{htmlContents}310		<PositionedElement anchor={[1, 0]} position={[drawing.width - 8, 6]} scale={scale} ><feedback.Icon className="icon" /></PositionedElement>311	</>312}313// shouldBeSelecting gets mouseDownData and startSelection options and determines if we're selecting.314function shouldBeSelecting(mouseDownData, startSelection) {315	// Don't start selecting if the mouse didn't go down.316	if (!mouseDownData)317		return false318	// Check the settings.319	switch (startSelection) {320		case startSelectionOptions.never:321			return false322		case startSelectionOptions.noDoubleSnap:323			return !mouseDownData.isSnappedTwice324		case startSelectionOptions.noSnap:325			return !mouseDownData.isSnapped326		case startSelectionOptions.always:327			return true328		default:329			throw new Error(`Invalid startSelection setting: received a setting of "${startSelection}" for startSelection on a DrawingInput, but this was not among the valid options.`)330	}331}332// getSelectionRectangle returns the selection rectangle based on two mouse data objects.333function getSelectionRectangle(downData, upData, drawing) {334	return new Rectangle({335		start: drawing.applyBounds(downData.position),336		end: drawing.applyBounds(upData.position),337	})338}339// addSelectionRectangle takes an svgContents object and adds a selection rectangle on top of it.340function addSelectionRectangle(svgContents, selectionRectangle) {341	return selectionRectangle ? <>342		{svgContents}343		<SvgRectangle className="selectionRectangle" dimensions={selectionRectangle} />344	</> : svgContents345}346// DeleteButton is a button of a garbage bin icon.347function DeleteButton(props) {...edit.js
Source:edit.js  
...345    ctx.stroke();346    ctx.setLineDash([]);347  }348  editSelection(uictx){349    const { left, right, top, bottom } = this.getSelectionRectangle(uictx);350    // update indices351    this.targets = [];352    const xform = this.curve.fullTransform; // .inverse()353    for(let i = 0; i < this.curve.length; ++i){354      const { x, y } = xform.applyTo(this.curve.getPoint(i));355      if(x >= left && x <= right356      && y >= top  && y <= bottom){357        this.targets.push(i);358      }359    }360  }361  getSelectionRectangle(uictx){362    const sketchPos = uictx.getSketchPos();363    let left, top, right, bottom;364    if(this.sketchStart.x < sketchPos.x){365      left = this.sketchStart.x;366      right = sketchPos.x;367    } else {368      left = sketchPos.x;369      right = this.sketchStart.x;370    }371    if(this.sketchStart.y < sketchPos.y){372      top = this.sketchStart.y;373      bottom = sketchPos.y;374    } else {375      top = sketchPos.y;376      bottom = this.sketchStart.y;377    }378    return { left, right, top, bottom };379  }380  drawSelection(uictx){381    const ctx = uictx.getDrawingContext();382    ctx.setLineDash([2, 2]);383    ctx.strokeStyle = '#999999AA';384    const { left, right, top, bottom } = this.getSelectionRectangle(uictx);385    ctx.strokeRect(left, top, right - left, bottom - top);386    ctx.setLineDash([]);387  }388  stop(uictx){389    // update edit mode or the curve390    if(this.targets.length){391      if(this.editMode !== SELECT){392        // commit history393        uictx.commitHistory('edit ' + this.editMode);394      }395      // change target type to selection by default396      this.editMode = SELECT;397      398    } else {...utils.js
Source:utils.js  
...130        rect = getSelectionRectangleInContentEditableElement(element, position);131    else if (typeof element.createTextRange === 'function')132        rect = getTextSelectionRectangle(element, position);133    else134        rect = getSelectionRectangle(element, position);135    if (!rect)136        return null;137    rect = ensureRectangleInsideElement(element, rect);138    rect = getAbsoluteRect(rect);139    return {140        x: rect.left,141        y: Math.floor(rect.top + (rect.bottom - rect.top) / 2)142    };143}144export function getSelectionCoordinatesByPosition (element, position) {145    const isTextEditable    = domUtils.isTextEditableElement(element);146    const isContentEditable = domUtils.isContentEditableElement(element);147    const hasText           = isTextEditable && domUtils.getElementValue(element).length > 0 ||148                            isContentEditable && contentEditable.getContentEditableValue(element).length;...tile-selector-component.js
Source:tile-selector-component.js  
...99      for (let x = 0; x < tilesWide; x++, tileNo++)100        drawTile32(this.cacheBitmap, x * tileset.tw, y * tileset.th, palette, tileset, tileNo);101    this.cacheBitmapDirty = false;102  }103	getSelectionRectangle() {104		if (!this.rectStart) return null;105		let [x0, y0] = this.rectStart, [x1, y1] = this.rectEnd;106		return makeSelectionRectangle(x0, y0, x1, y1);107	}108	drawSelectionRectangle(context) {109		if (this.rectStart) {110			const rect = this.getSelectionRectangle();111      const lineColor = this.blink([255, 255, 128, 1], [255, 0, 0, 1], 1000, FUNCTIONS.linear);112			let [x0, y0] = this.posOnScreen([rect.x0, rect.y0]);113			let [x1, y1] = this.posOnScreen([rect.x1, rect.y1]);114			const tileset = spriteNamed(this.tilesetName);115			this.drawDashedRectangle(context, x0 * tileset.tw, y0 * tileset.th, x1 * tileset.tw, y1 * tileset.th, makeCssColor(lineColor));116		}117	}118	getSingleTileSelectedIfAny() {119  	const rect = this.getSelectionRectangle();120  	if (rect && rect.width === 1 && rect.height === 1) {121  		return this.tileAtPosition(rect.x0, rect.y0);122		}123  	return -1;124	}125	roundPosToTile(posVec2) {126		const tileset = spriteNamed(this.tilesetName);127  	return [Math.floor(posVec2[0] / tileset.tw), Math.floor(posVec2[1] / tileset.th)];128	}129	positionForTile(tileNo) {130		const tileset = spriteNamed(this.tilesetName);131		const tilesWide = Math.min(Math.floor(this.width / tileset.tw), tilesWideInTileset(tileset));132		return { x: tileNo % tilesWide, y: Math.floor(tileNo / tilesWide) };133	}134	tileAtPosition(x, y) {135		const tileset = spriteNamed(this.tilesetName);136		const tilesWide = Math.min(Math.floor(this.width / tileset.tw), tilesWideInTileset(tileset));137		const tileNo = x + y * tilesWide;138		return (tileNo < tilesInTileset(tileset) && x < tilesWide) ? tileNo : -1;139	}140	updateUI() {141  	if (!this.tilesetName) return;142  	const tile = this.getSingleTileSelectedIfAny();143  	const tilePal = this.element('.tile-pal'), tileNo = this.element('.tile-no');144		const tileset = spriteNamed(this.tilesetName);145  	tileNo.value = tile;146		tileNo.max = tilesInTileset(tileset) - 1;147  	tilePal.disabled = tileNo.disabled = tile < 0;148    tilePal.max = paletteNamed(this.paletteName).h - 1;149    tilePal.value = Math.min(tilePal.value, tilePal.max);150	}151	selectRect() {152  	const rect = this.getSelectionRectangle();153  	const paletteMask = this.getPaletteMask();154  	if (rect) {155			const cells = new Array(rect.width * rect.height);156			let cell = 0;157			for (let j = rect.y0; j < rect.y1; j++)158				for (let i = rect.x0; i < rect.x1; i++, cell++)159					cells[cell] = this.tileAtPosition(i, j) | paletteMask;160			this.onselect(rect.width, rect.height, cells);161		}162	}163	// ------------------------------ OVERRIDE ---------------------------------164  get dpr() { return 1; }165	onMouseDown(e, mousePos) {166  	const pos = this.roundPosToTile(this.posInTransformedImage(mousePos));...snapshot.js
Source:snapshot.js  
...51            $('#snapshot_container').popover('show');52        }, 500);53    };54    $scope.upload_webcam = function(){55      if (cropObj.getSelectionRectangle()) {56        // Display loading; will display regardless of success or failure57        // $('.loader').fadeIn();58        $('#snapshot_container').popover('hide');59        $scope.loading = true;60        $scope.cutDisabled = true;61        var name = $scope.pyuserid;62        var formData = {"name":name, "data":canvas.toDataURL('image/png'), "count": selfieCount};63        $http.post('/fileupload', formData).success(function(data){64            $scope.cut();65        }).error(function(){66            $scope.loading = false;67            $scope.cutDisabled = true;68            alert("There was an issue uploading the image.");69        });70      }71    };72    //Call grabcut with coordinates73    $scope.cut = function(){74        var coord = cropObj.getSelectionRectangle().getOpenCVXYWH();75        var formData = {};76        formData['coords'] = coord.x + ' ' + coord.y  + ' ' + coord.width + ' ' + coord.height;77        formData['pyuserid'] = $scope.pyuserid;78        formData['count'] = selfieCount;79        $http.post('/grabcut', formData).success(function(data){80            if (!Date.now) {81                Date.now = function() { return new Date().getTime(); }82            }83            console.log(data)84            $scope.selfie = data + "?" + Date.now();85            $('.check_image').attr('src', data);86            $scope.check();87        })88        .error(function(){...PopUpMenu.js
Source:PopUpMenu.js  
...47  getNewHidestyle(){48    return { visibility: 'hidden' };49  }50  getNewShowStyle(){51    const selectionRect = getSelectionRectangle();52    const top = `${this.getMenuTopPosition(selectionRect)}px`;53    const left = `${this.getMenuLeftPosition(selectionRect)}px`;54    return {55      top,56      left,57    }58  }59  getMenuTopPosition(rect){60    const menuMargin = 15;61    return rect.top + getScrollAlongYAxis() - this.getMenuHeight() - menuMargin;62  }63  getMenuLeftPosition(rect){64    return rect.left + getScrollAlongXAxis() - this.getMenuWidht()/2 + rect.width/2;65  }...Using AI Code Generation
1import { Selector } from 'testcafe';2test('My first test', async t => {3        .click('#populate')4        .click('#submit-button')5        .takeElementScreenshot(Selector('#submit-button'));6});7import { Selector } from 'testcafe';8test('My first test', async t => {9        .click('#populate')10        .click('#submit-button')11        .takeElementScreenshot(Selector('#submit-button'));12});Using AI Code Generation
1import { Selector } from 'testcafe';2test('My first test', async t => {3        .click('#populate')4        .click('#submit-button')5        .takeElementScreenshot(Selector('#submit-button'));6});7test('My first test', async t => {8        .click('#populate')9        .click('#submit-button')10        .takeElementScreenshot(Selector('#submit-button'), { selector: Selector('#submit-button') });11});12test('My first test', async t => {13        .click('#populate')14        .click('#submit-button')15        .takeElementScreenshot(Selector('#submit-button'), { selector: Selector('#submit-button'), includeMargins: true });16});17test('My first test', async t => {18        .click('#populate')19        .click('#submit-button')20        .takeElementScreenshot(Selector('#submit-button'), { selector: Selector('#submit-button'), includeMargins: true, includeBorders: true });21});22test('My first test', async t => {23        .click('#populate')24        .click('#submit-button')25        .takeElementScreenshot(Selector('#submit-button'), { selector: Selector('#submit-button'), includeMargins: true, includeBorders: true, includePadding: true });26});27test('My first test', async t => {28        .click('#populate')29        .click('#submit-button')30        .takeElementScreenshot(Selector('#submit-button'), { selector: Selector('#submit-button'), includeMargins: true, includeBorders: true, includePadding: true, crop: { left: 10, top: 10, right: 10, bottom: 10 } });31});32test('My first test', async t => {33        .click('#populate')34        .click('#submit-button')35        .takeElementScreenshot(Selector('#submit-button'), { selector: Selector('#submit-button'),Using AI Code Generation
1import { Selector } from 'testcafe';2test('My first test', async t => {3        .typeText('#developer-name', 'John Smith')4        .click('#windows')5        .click('#submit-button');6    const articleHeader = await Selector('.result-content').find('h1');7    await t.expect(articleHeader.innerText).eql('Thank you, John Smith!');8});9const { Selector } = require('testcafe');10test('My first test', async t => {11        .typeText('#developer-name', 'John Smith')12        .click('#windows')13        .click('#submit-button');14    const articleHeader = await Selector('.result-content').find('h1');15    await t.expect(articleHeader.innerText).eql('Thank you, John Smith!');16});Using AI Code Generation
1import { Selector } from 'testcafe';2test('My first test', async t => {3        .click('#populate')4        .click('#submit-button')5        .takeScreenshot()6        .takeElementScreenshot(Selector('#submit-button'))7        .takeElementScreenshot(Selector('#submit-button'), {crop: {left: 1, right: 1, top: 1, bottom: 1}})8        .takeElementScreenshot(Selector('#submit-button'), {crop: {left: 1, right: 1, top: 1, bottom: 1}, includeMargins: true})9        .takeElementScreenshot(Selector('#submit-button'), {crop: {left: 1, right: 1, top: 1, bottom: 1}, includeBorders: true})10        .takeElementScreenshot(Selector('#submit-button'), {crop: {left: 1, right: 1, top: 1, bottom: 1}, includePaddings: true})11        .takeElementScreenshot(Selector('#submit-button'), {crop: {left: 1, right: 1, top: 1, bottom: 1}, includePaddings: true, includeBorders: true, includeMargins: true})12        .takeScreenshot({fullPage: true})13        .takeScreenshot({fullPage: true, path: 'screenshots/fullpage.png'})14        .takeScreenshot({path: 'screenshots/viewport.png'})15        .takeScreenshot({crop: {left: 1, right: 1, top: 1, bottom: 1}})16        .takeScreenshot({crop: {left: 1, right: 1, top: 1, bottom: 1}, path: 'screenshots/cropped.png'})17        .takeScreenshot({crop: {left: 1, right: 1, top: 1, bottom: 1}, fullPage: true})18        .takeScreenshot({crop: {left: 1, right: 1, top: 1, bottom: 1}, fullPage: true, path: 'screenshots/cropped-fullpage.png'})19        .takeScreenshot({path: 'screenshots/with-shadow-dom-element.png', selector: '#shadow-host'})20        .takeElementScreenshot(SelectorUsing AI Code Generation
1import { Selector } from 'testcafe';2test('My first test', async t => {3    const developerNameInput = Selector('#developer-name');4    const inputRectangle = await t.getSelectionRectangle(developerNameInput);5    const remoteCheckbox = Selector('#remote-testing');6    const checkboxRectangle = await t.getSelectionRectangle(remoteCheckbox);7    const windowsOption = Selector('option').withText('Windows');8    const optionRectangle = await t.getSelectionRectangle(windowsOption);9});Using AI Code Generation
1import { Selector } from 'testcafe';2test('Getting element\'s rectangle', async t => {3    const searchBox = Selector('#lst-ib');4        .typeText(searchBox, 'testcafe')5        .click(searchBox)6        .pressKey('enter')7        .click(Selector('h3').withText('TestCafe - Automated browser testing'))8        .takeElementScreenshot(await searchBox.getBoundingRect(), 'search-box.png');9});10import { t } from 'testcafe';11export default class TestCafe {12    constructor () {13        this.searchBox = Selector('#lst-ib');14    }15    async search (searchText) {16            .typeText(this.searchBox, searchText)17            .click(this.searchBox)18            .pressKey('enter');19    }20    async clickOnSearchResult (searchResult) {21        await t.click(Selector('h3').withText(searchResult));22    }23}24import { Selector } from 'testcafe';25import TestCafe from './testcafe';26test('Getting element\'s rectangle', async t => {27    const testCafe = new TestCafe();28    await testCafe.search('testcafe');29    await testCafe.clickOnSearchResult('TestCafe - Automated browser testing');30    await t.takeElementScreenshot(await testCafe.searchBox.getBoundingRect(), 'search-box.png');31});32import { t } from 'testcafe';33export default class TestCafe {34    constructor () {35        this.searchBox = Selector('#lst-ib');36    }37    async search (searchText) {38            .typeText(this.searchBox, searchText)39            .click(this.searchBox)40            .pressKey('enter');41    }42    async clickOnSearchResult (searchResult) {43        await t.click(Selector('h3').withText(searchResult));44    }45}Using AI Code Generation
1import {Selector} from 'testcafe';2test('Getting selection rectangle', async t => {3    await t.selectText(Selector('input[name="q"]')).pressKey('ctrl+c');4    const selectionRectangle = await t.getSelectionRectangle();5    console.log(selectionRectangle);6});7import {Selector} from 'testcafe';8test('Getting selection rectangle', async t => {9    await t.selectText(Selector('input[name="q"]')).pressKey('ctrl+c');10    const selectionRectangle = await t.getSelectionRectangle();11    console.log(selectionRectangle);12});13import {Selector} from 'testcafe';14test('Getting selection rectangle', async t => {15    await t.selectText(Selector('input[name="q"]')).pressKey('ctrl+c');16    const selectionRectangle = await t.getSelectionRectangle();17    console.log(selectionRectangle);18});19import {Selector} from 'testcafe';20test('Getting selection rectangle', async t => {21    await t.selectText(Selector('input[name="q"]')).pressKey('ctrl+c');22    const selectionRectangle = await t.getSelectionRectangle();23    console.log(selectionRectangle);24});25import {Selector} from 'testcafe';26test('Getting selection rectangle', async t => {27    await t.selectText(Selector('input[name="q"]')).pressKey('ctrl+c');28    const selectionRectangle = await t.getSelectionRectangle();Using AI Code Generation
1import { ClientFunction } from 'testcafe';2const getSelectionRectangle = ClientFunction(() => {3    const selection = window.getSelection();4    const range = selection.getRangeAt(0);5    const rect = range.getBoundingClientRect();6    return {7    };8});9test('Getting Selection Rectangle', async t => {10        .selectText('#text')11        .expect(getSelectionRectangle()).eql({ left: 8, top: 8, width: 29, height: 16 });12});13import { ClientFunction } from 'testcafe';14const getSelectionRectangle = ClientFunction(() => {15    const selection = window.getSelection();16    const range = selection.getRangeAt(0);17    const rect = range.getBoundingClientRect();18    return {19    };20});21test('Getting Selection Rectangle', async t => {22        .selectText('#text')23        .expect(getSelectionRectangle()).eql({ left: 8, top: 8, width: 29, height: 16 });24});25import { ClientFunction } from 'testcafe';26const getSelectionRectangle = ClientFunction(() => {27    const selection = window.getSelection();28    const range = selection.getRangeAt(0);29    const rect = range.getBoundingClientRect();30    return {31    };32});33test('Getting Selection Rectangle', async t => {34        .selectText('#text')35        .expect(getSelectionRectangle()).eql({ left: 8, top: 8,Using AI Code Generation
1import { Selector } from "testcafe";2test("Getting the Selected Text", async t => {3        .selectText(Selector("input[name='q']"))4        .expect(Selector("input[name='q']").getSelectionRectangle()).eql({ left: 0, top: 0, width: 0, height: 0 });5});6import { Selector } from "testcafe";7test("Getting the Selected Text", async t => {8        .selectText(Selector("input[name='q']"))9        .expect(Selector("input[name='q']").getSelectionRectangle()).eql({ left: 0, top: 0, width: 0, height: 0 });10});Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
