<commit_before>import Morph from 'src/components/widgets/lively-morph.js';

export default class LivelyImageEditor extends Morph {
  async initialize() {
    this.windowTitle = "Image Editor";

    this.canvas = this.get("#canvas")
    this.canvas.height = 500
    this.canvas.width = 500

    this.ctx = this.canvas.getContext("2d");

    if(this.migrateCanvas) {
      this.ctx.drawImage(this.migrateCanvas, 0, 0)
    }
    
    
    this.get("#penColor").value = this.color;
    this.get("#penSize").value = this.penSize;

    lively.html.registerKeys(this, "keys", this, true)
    
    this.get("#crop").addEventListener("click", evt => this.onCrop(evt))
  
    
    lively.removeEventListener("pointer", this)
    lively.addEventListener("pointer", this, "pointerdown", e => this.onPointerDown(e))
    lively.addEventListener("pointer", this, "pointermove", e => this.onPointerMove(e))
    lively.addEventListener("pointer", this, "pointerup", e => this.onPointerUp(e))

    lively.addEventListener('pointer', this, "contextmenu", evt => {
      if (!evt.shiftKey) {
        this.onContextMenu(evt)
        evt.stopPropagation();
        evt.preventDefault();
        return true;
      }
    }, false);
    
    
    lively.addEventListener("imageeditor", this.get('#penColor'), "value-changed", 
      e => this.onPenColor(e.detail.value));  
    lively.addEventListener("imageeditor", this.get('#penSize'), "value-changed", 
      e => this.onPenSize(e.detail.value)); 
    
    if(this.target) {
      this.loadFromImageElement(this.target)
    } else {
      var url = this.getAttribute("src")
      if (url) {
        this.loadImage(url)
      }   
    }
  }
  
  // ACCESSORS
  
  get color() {
    return this.getAttribute("color")
  }
  
  set color(value) {
    this.setAttribute("color", value)
  }
  
  get penSize() {
    return this.getAttribute("pen-size")
  }
  
  set penSize(value) {
    this.setAttribute("pen-size", value)
  }
  
  onKeyDown(evt) {
    var key = String.fromCharCode(evt.keyCode)
    if (evt.ctrlKey && key == "S") {
      this.onSave()  
    }
  }

  
  // BEGIN EDITOR API
  saveFile() {
    
    var data = this.canvas.toDataURL()
    this.lastSource = data
    return lively.files.copyURLtoURL(data, this.getURL())
  }

  getURL(url) {
    return this.getAttribute("src")
  }

  setURL(url) {
    if(this.getURL() != url) {
      this.loadImage(url)
    }
  }
  
  currentEditor() {
    var canvas = this.canvas
    return {
      getValue() { return canvas.toDataURL()}
    }
  }
  // END EDITOR API
  
  
  onPenColor(color) {
    this.color = color
  }
  
  onPenSize(size) {
    this.penSize = size
  }
  
  
  
  loadImage(url) {
      
    this.setAttribute("src", url)
      
    if (!url.toString().match(/^https?:\/\//)) {
      var resolvedURL = lively.swxURL(url)
    } else {
      resolvedURL = url
    }
      
    
    var img = new Image();
    img.onload = () => {
      this.loadFromImageElement(img) 
    };
    img.src = resolvedURL
  }
  
  loadFromImageElement(img) {
    this.canvas.height = img.height
    this.canvas.width = img.width
    this.ctx.drawImage(img, 0, 0); // Or at whatever offset you like
    this.lastSource = this.canvas.toDataURL();

  }
  
  posFromEvent(evt) {
    return lively.getPosition(evt).subPt(lively.getGlobalPosition(this.canvas))
  }
  
  setTarget(img) {
    this.target = img
    this.loadFromImageElement(img)
  }
  

  // #important
  paint(pos) {
    this.ctx.strokeStyle =  this.color;
    this.ctx.lineWidth = this.penSize;
    if (this.lastPos) {
      this.ctx.lineCap =  "round" 
      this.ctx.moveTo(this.lastPos.x, this.lastPos.y);      
      this.ctx.lineTo(pos.x, pos.y);
      //this.ctx.closePath();
      this.ctx.stroke();
    }
    this.lastPos = pos
  }

  onPointerDown(evt) {
    var pos = this.posFromEvent(evt)
    this.downPos = pos
    if(evt.button != 0) return; // only left mouse (main button) paints.
    if(this.mode == "crop") {
    } else {    
      this.isDown = true
      this.ctx.beginPath()
      this.paint(pos)      
    }
    
  }
  
  get pen() {
     return this.get("#pen")
  }
  
  isOnCanvas(pos) {
    return lively.rect(lively.pt(0,0), lively.getExtent(this.canvas)).containsPoint(pos)
  }
  
  // #important
  onPointerMove(evt) {
    var pos = this.posFromEvent(evt)
    if (this.mode == "crop") {
      if (this.downPos) {
        this.pen.style.borderRadius = ""
        this.pen.style.background = "" 
        lively.setGlobalPosition(this.pen, lively.getGlobalPosition(this.canvas).addPt(this.downPos))
        this.pen.style.border = "1px solid gray"
        lively.setExtent(this.pen, pos.subPt(this.downPos))        
      }

    } else {
      if (this.isDown || this.isOnCanvas(pos)) {
        lively.setGlobalPosition(
          this.pen, 
          (lively.getGlobalPosition(this.canvas)
              .addPt(pos)).addPt(lively.pt( -this.penSize / 2 , -this.penSize / 2)))
        lively.setExtent(this.pen, lively.pt(this.penSize,this.penSize))
        this.pen.style.borderRadius = this.penSize / 2 + "px"
        this.pen.style.backgroundColor = this.color      
      } else {
        this.pen.style.backgroundColor = ""   
      }
      if (this.isDown) {
        this.paint(pos)
      }
    }
    
    
  }

  onPointerUp(evt) { 
    var pos = this.posFromEvent(evt)
    if (this.mode == "crop") {
      if (this.downPos) {
        var extent = pos.subPt(this.downPos)
        var tmpCanvas = document.createElement('canvas');
        tmpCanvas.width = this.canvas.width;
        tmpCanvas.height = this.canvas.height;
        tmpCanvas.getContext("2d").drawImage(
          this.canvas,
          0,0, this.canvas.width, this.canvas.height,  
          0,0, this.canvas.width, this.canvas.height);      
      
            // this.canvas.width = extent.x
            // this.canvas.height = extent.y

        this.ctx.drawImage(
          tmpCanvas,
          this.downPos.x,this.downPos.y,this.downPos.x + extent.x,this.downPos.y + extent.y,  
          0,0,extent.x,extent.y);
      
      }
      this.mode = ""
    } else {
      this.paint(pos)
      this.isDown = false
      this.lastPos = undefined
      this.updateChangeIndicator()     
    }
    this.downPos = null<commit_msg>[lively_image_editor] crop works<commit_after>import Morph from 'src/components/widgets/lively-morph.js';

export default class LivelyImageEditor extends Morph {
  async initialize() {
    this.windowTitle = "Image Editor";

    this.canvas = this.get("#canvas")
    this.canvas.height = 500
    this.canvas.width = 500

    this.ctx = this.canvas.getContext("2d");

    if(this.migrateCanvas) {
      this.ctx.drawImage(this.migrateCanvas, 0, 0)
    }
    
    
    this.get("#penColor").value = this.color;
    this.get("#penSize").value = this.penSize;

    lively.html.registerKeys(this, "keys", this, true)
    
    this.get("#crop").addEventListener("click", evt => this.onCrop(evt))
  
    
    lively.removeEventListener("pointer", this)
    lively.addEventListener("pointer", this, "pointerdown", e => this.onPointerDown(e))
    lively.addEventListener("pointer", this, "pointermove", e => this.onPointerMove(e))
    lively.addEventListener("pointer", this, "pointerup", e => this.onPointerUp(e))

    lively.addEventListener('pointer', this, "contextmenu", evt => {
      if (!evt.shiftKey) {
        this.onContextMenu(evt)
        evt.stopPropagation();
        evt.preventDefault();
        return true;
      }
    }, false);
    
    
    lively.addEventListener("imageeditor", this.get('#penColor'), "value-changed", 
      e => this.onPenColor(e.detail.value));  
    lively.addEventListener("imageeditor", this.get('#penSize'), "value-changed", 
      e => this.onPenSize(e.detail.value)); 
    
    if(this.target) {
      this.loadFromImageElement(this.target)
    } else {
      var url = this.getAttribute("src")
      if (url) {
        this.loadImage(url)
      }   
    }
  }
  
  // ACCESSORS
  
  get color() {
    return this.getAttribute("color")
  }
  
  set color(value) {
    this.setAttribute("color", value)
  }
  
  get penSize() {
    return this.getAttribute("pen-size")
  }
  
  set penSize(value) {
    this.setAttribute("pen-size", value)
  }
  
  onKeyDown(evt) {
    var key = String.fromCharCode(evt.keyCode)
    if (evt.ctrlKey && key == "S") {
      this.onSave()  
    }
  }

  
  // BEGIN EDITOR API
  saveFile() {
    
    var data = this.canvas.toDataURL()
    this.lastSource = data
    return lively.files.copyURLtoURL(data, this.getURL())
  }

  getURL(url) {
    return this.getAttribute("src")
  }

  setURL(url) {
    if(this.getURL() != url) {
      this.loadImage(url)
    }
  }
  
  currentEditor() {
    var canvas = this.canvas
    return {
      getValue() { return canvas.toDataURL()}
    }
  }
  // END EDITOR API
  
  
  onPenColor(color) {
    this.color = color
  }
  
  onPenSize(size) {
    this.penSize = size
  }
  
  
  
  loadImage(url) {
      
    this.setAttribute("src", url)
      
    if (!url.toString().match(/^https?:\/\//)) {
      var resolvedURL = lively.swxURL(url)
    } else {
      resolvedURL = url
    }
      
    
    var img = new Image();
    img.onload = () => {
      this.loadFromImageElement(img) 
    };
    img.src = resolvedURL
  }
  
  loadFromImageElement(img) {
    this.canvas.height = img.height
    this.canvas.width = img.width
    this.ctx.drawImage(img, 0, 0); // Or at whatever offset you like
    this.lastSource = this.canvas.toDataURL();

  }
  
  posFromEvent(evt) {
    return lively.getPosition(evt).subPt(lively.getGlobalPosition(this.canvas))
  }
  
  setTarget(img) {
    this.target = img
    this.loadFromImageElement(img)
  }
  

  // #important
  paint(pos) {
    this.ctx.strokeStyle =  this.color;
    this.ctx.lineWidth = this.penSize;
    if (this.lastPos) {
      this.ctx.lineCap =  "round" 
      this.ctx.moveTo(this.lastPos.x, this.lastPos.y);      
      this.ctx.lineTo(pos.x, pos.y);
      //this.ctx.closePath();
      this.ctx.stroke();
    }
    this.lastPos = pos
  }

  onPointerDown(evt) {
    var pos = this.posFromEvent(evt)
    this.downPos = pos
    if(evt.button != 0) return; // only left mouse (main button) paints.
    if(this.mode == "crop") {
    } else {    
      this.isDown = true
      this.ctx.beginPath()
      this.paint(pos)      
    }
    
  }
  
  get pen() {
     return this.get("#pen")
  }
  
  isOnCanvas(pos) {
    return lively.rect(lively.pt(0,0), lively.getExtent(this.canvas)).containsPoint(pos)
  }
  
  // #important
  onPointerMove(evt) {
    var pos = this.posFromEvent(evt)
    if (this.mode == "crop") {
      if (this.downPos) {
        this.pen.style.borderRadius = ""
        this.pen.style.background = "" 
        lively.setGlobalPosition(this.pen, lively.getGlobalPosition(this.canvas).addPt(this.downPos))
        this.pen.style.border = "1px solid gray"
        lively.setExtent(this.pen, pos.subPt(this.downPos))        
      }
    } else {
      if (this.isDown || this.isOnCanvas(pos)) {
        lively.setGlobalPosition(
          this.pen, 
          (lively.getGlobalPosition(this.canvas)
              .addPt(pos)).addPt(lively.pt( -this.penSize / 2 , -this.penSize / 2)))
        lively.setExtent(this.pen, lively.pt(this.penSize,this.penSize))
        this.pen.style.borderRadius = this.penSize / 2 + "px"
        this.pen.style.backgroundColor = this.color      
      } else {
        this.pen.style.backgroundColor = ""   
      }
      if (this.isDown) {
        this.paint(pos)
      }
    }
    
    
  }

  onPointerUp(evt) { 
    var pos = this.posFromEvent(evt)
    if (this.mode == "crop") {
      if (this.downPos) {
        var extent = pos.subPt(this.downPos)
        var tmpCanvas = document.createElement('canvas');
        tmpCanvas.width = this.canvas.width;
        tmpCanvas.height = this.canvas.height;
        tmpCanvas.getContext("2d").drawImage(
          this.canvas,
          0,0, this.canvas.width, this.canvas.height,  
          0,0, this.canvas.width, this.canvas.height);      
      
        this.canvas.width = extent.x
        this.canvas.height = extent.y
        this.ctx = this.canvas.getContext("2d");
        this.ctx.clearRect(0, 0, extent.x,extent.y);
        this.ctx.drawImage(
          tmpCanvas,
          this.downPos.x, this.downPos.y, extent.x, extent.y,  
          0,0,extent.x,extent.y);
      
      }
      this.mode = ""
    } else {
      this.paint(pos)
      this.isDown = false
      this.lastPos = undefined
      this.updateChangeIndicator()     
    }
    this.downPos = null
{'fraction_non_alphanumeric': 0.1449102747340003, 'fraction_numerical': 0.0042083531840559, 'mean_word_length': 2.228659318123558, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5516036', 'n_tokens_mistral': 4350, 'n_tokens_neox': 4324, 'n_words': 667}