<commit_before>import {defineOption} from "../edit"
import {style, inline, Node} from "../model"
import {elt} from "../edit/dom"
import {Tooltip} from "./tooltip"
import {InlineStyleItem, ImageItem, LinkDialog} from "./menuitem"
import {openMenu, forceFontLoad} from "./tooltip-menu"

import "./inlinetooltip.css"

const classPrefix = "ProseMirror-inlinetooltip"

defineOption("inlineTooltip", false, function(pm, value) {
  if (pm.mod.inlineTooltip)
    pm.mod.inlineTooltip.detach()
  if (value)
    pm.mod.inlineTooltip = new InlineTooltip(pm, value)
})

export const defaultItems = [
  new InlineStyleItem("bold", "Strong text", style.strong),
  new InlineStyleItem("italic", "Emphasized text", style.em),
  new InlineStyleItem("chain", "Hyperlink", "link", new LinkDialog),
  new ImageItem("image"),
  new InlineStyleItem("code", "Code font", style.code)
]

class InlineTooltip {
  constructor(pm, config) {
    this.pm = pm
    this.items = (config && config.items) || defaultItems
    this.showLinks = config ? config.showLinks !== false : true
    this.pending = null

    pm.on("selectionChange", this.updateFunc = () => this.scheduleUpdate())
    pm.on("change", this.updateFunc)

    this.tooltip = new Tooltip(pm, "above", true)
    this.tooltip.reset = this.updateFunc

    forceFontLoad(pm)
  }

  detach() {
    this.tooltip.detach()
    
    pm.off("selectionChange", this.updateFunc)
    pm.off("change", this.updateFunc)
  }

  scheduleUpdate() {
    window.clearTimeout(this.pending)
    this.pending = window.setTimeout(() => {
      this.pending = null
      this.update()
    }, 100)
  }

  inCodeBlock(sel) {
    let start = this.pm.doc.path(sel.from.path)
    let end = this.pm.doc.path(sel.to.path)
    return start.type == Node.types.code_block && end.type == Node.types.code_block
  }

  update() {
    let sel = this.pm.selection, link
    if (!this.pm.hasFocus())
      this.tooltip.close()
    else if (!sel.empty && !this.inCodeBlock(sel))
      openMenu(this.tooltip, this.items, pm, topCenterOfSelection())
    else if (this.showLinks && (link = this.linkUnderCursor()))
      this.showLink(link, this.pm.coordsAtPos(sel.head))
    else
      this.tooltip.close()
  }

  linkUnderCursor() {
    let styles = inline.inlineStylesAt(this.pm.doc, this.pm.selection.head)
    if (!styles) console.log("none at " + this.pm.doc + " under " + this.pm.selection.head)
    return styles.reduce((found, st) => found || (st.type == "link" && st), null)
  }

  showLink(link, pos) {
    let node = elt("div", {class: classPrefix + "-linktext"}, elt("a", {href: link.href, title: link.title}, link.href))<commit_msg>Use the plainText property on node types<commit_after>import {defineOption} from "../edit"
import {style, inline, Node} from "../model"
import {elt} from "../edit/dom"
import {Tooltip} from "./tooltip"
import {InlineStyleItem, ImageItem, LinkDialog} from "./menuitem"
import {openMenu, forceFontLoad} from "./tooltip-menu"

import "./inlinetooltip.css"

const classPrefix = "ProseMirror-inlinetooltip"

defineOption("inlineTooltip", false, function(pm, value) {
  if (pm.mod.inlineTooltip)
    pm.mod.inlineTooltip.detach()
  if (value)
    pm.mod.inlineTooltip = new InlineTooltip(pm, value)
})

export const defaultItems = [
  new InlineStyleItem("bold", "Strong text", style.strong),
  new InlineStyleItem("italic", "Emphasized text", style.em),
  new InlineStyleItem("chain", "Hyperlink", "link", new LinkDialog),
  new ImageItem("image"),
  new InlineStyleItem("code", "Code font", style.code)
]

class InlineTooltip {
  constructor(pm, config) {
    this.pm = pm
    this.items = (config && config.items) || defaultItems
    this.showLinks = config ? config.showLinks !== false : true
    this.pending = null

    pm.on("selectionChange", this.updateFunc = () => this.scheduleUpdate())
    pm.on("change", this.updateFunc)

    this.tooltip = new Tooltip(pm, "above", true)
    this.tooltip.reset = this.updateFunc

    forceFontLoad(pm)
  }

  detach() {
    this.tooltip.detach()
    
    pm.off("selectionChange", this.updateFunc)
    pm.off("change", this.updateFunc)
  }

  scheduleUpdate() {
    window.clearTimeout(this.pending)
    this.pending = window.setTimeout(() => {
      this.pending = null
      this.update()
    }, 100)
  }

  inPlainText(sel) {
    let start = this.pm.doc.path(sel.from.path)
    let end = this.pm.doc.path(sel.to.path)
    return start.type.plainText && end.type.plainText
  }

  update() {
    let sel = this.pm.selection, link
    if (!this.pm.hasFocus())
      this.tooltip.close()
    else if (!sel.empty && !this.inPlainText(sel))
      openMenu(this.tooltip, this.items, pm, topCenterOfSelection())
    else if (this.showLinks && (link = this.linkUnderCursor()))
      this.showLink(link, this.pm.coordsAtPos(sel.head))
    else
      this.tooltip.close()
  }

  linkUnderCursor() {
    let styles = inline.inlineStylesAt(this.pm.doc, this.pm.selection.head)
    if (!styles) console.log("none at " + this.pm.doc + " under " + this.pm.selection.head)
    return styles.reduce((found, st) => found || (st.type == "link" && st), null)
  }

  showLink(link, pos) {
    let node = elt("div", {class: classPrefix + "-linktext"}, elt("a", {href: link.href, title: link.title}, link.href))
{'fraction_non_alphanumeric': 0.15948357698879817, 'fraction_numerical': 0.001139168407062844, 'mean_word_length': 3.617002629272568, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, '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': '2683237', 'n_tokens_mistral': 1754, 'n_tokens_neox': 1743, 'n_words': 379}