<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>paper-hero</title>
  <link rel="stylesheet" href="https://unpkg.com/boltcss/bolt.min.css">
  <script type="importmap">
    {
      "imports": {
        "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
      }
    }
  </script>
  <style>
    body {
      max-width: 800px;
      margin: 40px auto;
      padding: 0 20px;
    }

    .form-group {
      display: flex;
      flex-direction: row;
      justify-content: flex-start;
      align-items: center;
    }

    label {
      margin-right: 1rem;
    }

    button {
      margin: 0.2rem 0.2rem;
    }

    button:hover {
      background-color: #dbdbdb;
    }

    footer {
      text-align: center;
      margin-top: 2rem;
    }

    .button-group {
      margin-top: 3rem;
    }

    .search-button {
      background-color: #ffc83d;
      color: #d67d00;
      font-weight: bold;
    }

    .search-button:hover {
      background-color: #ffc83dc0;
    }

    .download-button {
      background-color: #98ca56;
      color: white;
      font-weight: bold;
    }

    .download-button:hover {
      background-color: #98ca56d1;
    }

    .output-title {
      margin-top: 2rem;
      margin-bottom: 0;
      display: block;
      background-color: #98ca56;
      color: white;
      font-weight: bold;
      font-size: large;
      padding: 6px 15px;
      border-top-left-radius: 6px;
      border-top-right-radius: 6px;
    }

    .output-box {
      margin-top: 0;
      padding: 6px 15px;
      background-color: white;
      border: 2px solid #98ca56;
      border-bottom-left-radius: 6px;
      border-bottom-right-radius: 6px;
    }
  </style>
</head>

<body>
  <header>
    <h1>💪 Paper Hero</h1>
    <p>
      Paper Hero is a toolkit to help search for papers from aclanthology, arXiv and dblp.
    </p>
    <p>GitHub Address: <a href="https://github.com/Spico197/paper-hero" target="_blank">Spico197/paper-hero</a></p>
  </header>

  <main>
    <div id="app">
      <div class="form-group">
        <label for="method"><strong>Source</strong></label>
        <select id="method" v-model="method">
          <option value="" disabled>Please select a source</option>
          <option value="aclanthology">ACL Anthology</option>
          <option value="arxiv">ArXiv</option>
          <option value="dblp">DBLP</option>
        </select>
      </div>

      <div>
        <label for="max-res"><strong>Max Results</strong></label>
        <input id="max-res" type="number" v-model="maxResults">
      </div>

      <div class="form-group">
        <label for="add-field"><strong>New Field</strong></label>
        <select id="add-field" v-model="addField">
          <option value="" disabled>Please select a field</option>
          <option :value="field" v-for="field in restFields">{{ field }}</option>
        </select>
        <button @click.prevent="addNewField">Add</button>
      </div>

      <hr>

      <div>
        <p><strong>Fields</strong></p>
        <p>
          Add <code>&&</code> to represent <code>AND</code> logic, e.g. <code>span-based && event extraction</code>
          means <em>span-based</em> and <em>event extraction</em> both appear in a field.
        </p>
        <p>
          For <code>year</code> and <code>month</code> fields, the query should
          follow the <code>start && end</code> format,
          e.g. year <code>2006 && 2013</code> means searching for papers published
          between <code>2006</code> and <code>2013</code>.
        </p>
        <div v-for="(groups, field) in query">
          <label :for="field"><strong>{{ field }}</strong></label>
          <div v-for="(group, index) in groups">
            <input class="field-input" type="text" v-model="query[field][index]" placeholder="text1 && text2 && text3"
              size="50">
            <button @click.prevent="rmAnd(field, index)">X</button>
          </div>
          <button @click.prevent="addOr(field)">OR</button>
        </div>
      </div>

      <div v-if="timerHandler">
        <p>⏱️ {{ searchSecondsTwoDecimal }}</p>
      </div>

      <div v-if="output">
        <p class="output-title">Output Info</p>
        <p class="output-box">
          {{ output }}
          <br>
          You are ready to download the results by clicking the download button below.
          <br>
          Like this tool? ⭐ me on <a href="https://github.com/Spico197/paper-hero" target="_blank">GitHub</a> !
        </p>
      </div>

      <div class="button-group">
        <button @click.prevent="resetQuery">Reset</button>
        <button class="search-button" @click.prevent="search">Search</button>
        <a :href="downloadHref" :download="`${method}.json`" v-if="downloadHref">
          <button class="search-button download-button">Download</button>
        </a>
      </div>

    </div>
  </main>

  <footer>
    <hr>
    Made by <a href="https://spico197.github.io" target="_blank">Tong Zhu</a> w/ 💖
  </footer>

  <script type="module">
    import { createApp, ref, computed, toRaw, watch } from 'vue'

    createApp(
      {
        setup() {
          const method = ref("aclanthology")
          const query = ref({ title: [[]] })
          const maxResults = ref(2000)
          const addField = ref("")
          const allFields = ["title", "author", "abstract", "venue", "year", "month"]
          const downloadUrl = ref('')
          const downloadToken = ref('')
          const downloadHref = ref('')
          const output = ref('')
          const timerHandler = ref(0)
          const searchSeconds = ref(0.0)
          const searchSecondsTwoDecimal = computed(() => {
            return `${searchSeconds.value.toFixed(1)}s`
          })
          const restFields = computed(() => {
            let rest = []
            for (const field of allFields) {
              if (!(field in query.value)) {
                rest.push(field)
              }
            }
            return rest
          })

          function addNewField() {
            if (addField.value) {
              query.value[addField.value] = [[]]
              addField.value = ""
            }
          }

          function rmAnd(field, index) {
            if (query.value[field].length == 1) {
              delete query.value[field]
            } else {
              query.value[field].splice(index, 1)
            }
          }

          function addOr(field) {
            query.value[field].push([])
          }

          function resetOutput() {
            output.value = ""
            downloadUrl.value = ""
            downloadToken.value = ""
            URL.revokeObjectURL(downloadHref.value)
            downloadHref.value = ""
            searchSeconds.value = 0.0
            timerHandler.value = 0
            searchSecondsTwoDecimal.value = ""
          }

          function resetQuery() {
            query.value = { title: [[]] }
            resetOutput()
          }

          function startTimer() {
            console.log("start")
            timerHandler.value = setInterval(() => {
              searchSeconds.value += 0.1
            }, 100)
          }

          function endTimer() {
            console.log("end")
            if (timerHandler.value > 0) {
              console.log("endi")
              clearInterval(timerHandler.value)
            }
          }

          function search() {
            resetOutput()
            startTimer()
            let q = {}
            for (const prop in query.value) {
              q[prop] = []
              for (let i = 0; i < query.value[prop].length; i++) {
                if (query.value[prop][i].length > 0) {
                  let andString = toRaw(query.value[prop][i])
                  let andStrings = andString.split('&&')
                  for (let j = 0; j < andStrings.length; j++) {
                    andStrings[j] = andStrings[j].trim()
                  }
                  q[prop].push(andStrings)
                }
              }
              if (q[prop].length < 1) {
                delete q[prop]
              }
            }
            const postData = JSON.stringify({
              "method": method.value,
              "query": q,
              "max_results": maxResults.value,
              "return_content": false,
            })
            fetch(
              "/api/",
              {
                method: "POST",
                headers: {
                  'Content-Type': 'application/json',
                },
                body: postData,
              }
            )
              .then((response) => response.json())
              .then((json) => {
                if (json["ok"] === false) {
                  alert(json["msg"])
                } else {
                  downloadUrl.value = json["url"]
                  downloadToken.value = json["token"]
                  output.value = `${json["msg"]}, #matched paper: ${json["paper"]}`
                }
              })
              .catch((err) => { alert(err) })
              .finally(() => endTimer())
          }

          watch(downloadUrl, (newUrl, oldUrl) => {
            if (downloadToken.value) {
              fetch(
                `/download/?u=${downloadUrl.value}&t=${downloadToken.value}`,
                {
                  method: "GET",
                }
              )
                .then((response) => response.blob())
                .then((data) => {
                  downloadHref.value = URL.createObjectURL(data)
                })
            }
          })

          return {
            method,
            query,
            restFields,
            addField,
            addNewField,
            search,
            rmAnd,
            addOr,
            resetQuery,
            maxResults,
            output,
            downloadUrl,
            downloadHref,
            searchSeconds,
            timerHandler,
            searchSecondsTwoDecimal,
          }
        }
      }
    ).mount("#app")
  </script>
</body>

</html>