Aller au contenu

Utilisateur:Yopyop456/Brouillon/cnchar

Une page de Wikipédia, l'encyclopédie libre.
/*
chinesepod
chairmansbao

https://www.chinesepod.com/api/v1/lessons/get-lesson/?lessonId=6030
*/

class cnDict{
  static data = {};
  static settings = undefined;
  static searchIndex = (str, name='hsk')=>{
    return this.data[name].value[this.data[name].key.indexOf(str)];
  }
  static searchField(str, name='hsk', field='level'){
    let ret = []
    this.data[name].value.forEach(element => {
      if(element[field] == str) ret.push(element)
    });
    return ret
  }
}

async function start2(){
  await loadExternal()
  await loadDict()
  
  handleModal()
  handleDisplay()
}

async function loadExternal(){
  window.process = {env:{NODE_ENV: 'production'}}

  window._ ??= await import('/js/0utils/utils3.mjs')
  // window._ ??= await import('./utils3.mjs')
  window.VirtualCore ??= await import('./virtual-core/dist/esm/index.js')

  if(!document.querySelector('#dict_table_style')){
    let css = `
.field_simplified,
.field_traditional,
.field_pinyin,
.field_pinyin2,
.field_category_fr,
.field_definition_fr,
.field_example,
.field_id,
.field_level,
.field_list,
.field_checkbox {
  display: none;
}

.show_simplified .field_simplified,
.show_traditional .field_traditional,
.show_pinyin .field_pinyin,
.show_pinyin2 .field_pinyin2,
.show_category_fr .field_category_fr,
.show_definition_fr .field_definition_fr,
.show_example .field_example,
.show_id .field_id,
.show_level .field_level,
.show_list .field_list,
.show_checkbox .field_checkbox {
  display: initial;
}

#dict_table td{
  width: 100vw;
}

#dict_table label{
  display: block
}

body:has(.modal-toggle:checked), body:has(.drawer-toggle:checked) {
  overflow-y: hidden;
}

.modal-box .btn-ghost:hover {
  background-color: initial
}
`
    let elm = document.createElement('style')
    elm.innerHTML = css
    elm.id = 'dict_table_style'
    document.head.insertAdjacentElement('beforeend', elm)
  }
}

async function loadDict(){
  cnDict.settings ??= _.saveUserSettings('', 'localStorage') || {}
  delete cnDict.settings.search

  cnDict.data.hsk ??= await fetch('data/hskvocab/hskvocab.json').then(x=>x.json())

  ;['list-1', 'list-2', 'list-3', 'list-4'].forEach(key=>{
    if(cnDict.settings[key]){
      cnDict.settings[key].forEach(n=>{
        cnDict.data.hsk.value[n].list = key
      })
    }
    else cnDict.settings[key] = []
  })

  let pinyin2 = await fetch('data/hskvocab/hskextra.json').then(x=>x.json())
  pinyin2.forEach((val, n)=>{
    Object.keys(val).forEach(x=>{
      cnDict.data.hsk.value[n][x] = val[x]
    })
  })
}


function handleModal(event){
  // initialize
  if(typeof event == 'undefined'){
    let options, elms, name, option, type, html, userOptions
    options = {
      offset: [1, '20%', '40%', '60%', '80%'],
      checkbox: [1, 'list-1', 'list-2', 'list-3', 'list-4'],
      list: [1, 'list-1', 'list-2', 'list-3', 'list-4'],
      level: [1, 'hsk-1', 'hsk-2', 'hsk-3', 'hsk-4', 'hsk-5', 'hsk-6', 'extra'],
      field: Object.keys(cnDict.data.hsk.value[0]),
      sort: [1,  'random', 'pinyin'],
      export: [2, 'export_data'],
    }
    options.field.unshift(0)
    options.field.push('list', 'pinyin2')
    userOptions = cnDict.settings

    html = ''
    Object.keys(options).forEach(key=>{
      html += `<br><button class="btn btn-ghost no-animation btn-xs sm:btn-sm my-2">${key}</button>`
    })
    html = html.replace('<br>', '')
    document.querySelector('#dict_options_modal .modal-box').insertAdjacentHTML('beforeend', html)
    
    elms = document.querySelectorAll('#dict_options_modal .btn-ghost')
    elms.forEach(elm=>{
      name = elm.innerText.toLowerCase()
      option = options[name] || []
      type = ['accent', 'info', 'warning'][option.shift()]

      html = ''
      option.forEach(item=>{
        let itemName = type=='info'?name:name+'_'+item
        let itemState = (userOptions[itemName] == 1 || userOptions[itemName] == item) ? '' : 'btn-outline ';
        html += ` <button class="btn ${itemState}btn-${type} btn-xs sm:btn-sm my-2" name="${itemName}" value="${type=='info'?item:1}" onclick="handleModal(event)">${item}</button>`
      })
      elm.insertAdjacentHTML('afterend', html)

    })

    if(Object.keys(cnDict.settings).length < 5){
      elms = document.querySelectorAll('.modal-box [name^=field_]')
      elms.forEach(x=>{
        cnDict.settings[x.name] = +x.value
        x.classList.remove('btn-outline')
      })
    }

    document.querySelector('[name=search]').closest('form').onsubmit = (event)=>{
      cnDict.settings.search = event.target.querySelector('input').value
      document.querySelector('.drawer-overlay').click();
      handleDisplay('')
      return false
    }
  }
  // onclick button
  else {
    let elms, target, val
    target = event.target
    elms = document.querySelectorAll('.modal-box [name='+target.name+']')
    val = target.classList.contains('btn-outline') ? target.value : 0

    if(elms.length == 1 || val == 0){
      target.classList.toggle('btn-outline')
    }
    else {
      elms.forEach(elm=>elm.classList.add('btn-outline'))
      target.classList.remove('btn-outline')
    }

    if(!target.className.includes('warning')) {
      val = isNaN(+val) ? val : +val
      cnDict.settings[target.name] = val
      _.saveUserSettings(cnDict.settings, 'localStorage')
    }

    if(target.innerText.includes('export')){

      if(val == 1) {
        target.insertAdjacentHTML('afterend', `<div><textarea placeholder="json" class="textarea textarea-bordered textarea-sm w-full" ></textarea></div>
        `)
        elms = document.querySelector('.modal-box')
        elms.scrollTo(0, elms.scrollHeight)
        elms = elms.querySelector('textarea')
        elms.value = JSON.stringify(cnDict.settings)
      }
      else {
        elms = document.querySelector('.modal-box textarea')
        let json = elms.value
        try{
          JSON.parse(json)
          if(json != JSON.stringify(cnDict.settings)){
            _.saveUserSettings(json, 'localStorage')
            location.reload()
          }
        }
        catch(e){
        }
        elms.remove()
      }
    }
    else {
      handleDisplay(target.name)
    }
  }
}

/*
id
simplified 1
traditional 2
pinyin 3
category_fr 4
definition_fr 5
example
level
*/

function handleDisplay(event){
  let userOptions, data, filtered, html, tbody, row, elms, tmp, a, b, c
  userOptions = cnDict.settings
  data = cnDict.data.hsk.value
  tbody = document.querySelector('#dict_table tbody')

  // initialize
  if(typeof event == 'undefined'){
    Object.keys(userOptions).forEach(key=>{
      if(key.startsWith('field') && userOptions[key] == 1){
        tbody.classList.add(key.replace('field', 'show'))
      }
    })

    if(userOptions.checkbox) {
      tbody.classList.add('show_checkbox')
    }
  }
  // hide show checkbox
  else if(typeof event == 'string'){
    if(event.startsWith('field')) {
      tbody.classList.toggle(event.replace('field', 'show'))
      return
    }
    else if(event.startsWith('checkbox')){
      let fn
      elms = tbody.querySelectorAll('label')
      if(userOptions.checkbox) {
        tbody.classList.add('show_checkbox')
        fn = x=>x.removeAttribute('for')
      }
      else {
        tbody.classList.remove('show_checkbox')
        fn = x=>x.setAttribute('for', '')
      }
      elms.forEach(x=>{
        fn(x)
        x = x.querySelector('input')
        x.checked = data[x.value].list == userOptions.checkbox
        x.className = x.className.replace(/checkbox-\w{3,10}/, '') + ' ' + ['', 'checkbox-error', 'checkbox-warning', 'checkbox-success', 'checkbox-info'][userOptions.checkbox?userOptions.checkbox.substring(5):0]
      })
      return
    }
    else{
      tbody.innerHTML = ''
    }
  }
  // handle checkbox event
  else if(typeof event == 'object'){
    let prev, n, m, val
    elms = event.target
    prev = userOptions.checkbox
    val = ''
    m = 0

    if(elms.checked) {
      m = prev.at(-1)
      userOptions[prev].push(+elms.value)
      val = prev
      prev = data[elms.value].list
    }

    if(prev){
      row = userOptions[prev]
      n = row.indexOf(elms.value)
      row[n] = row.at(-1)
      row.pop()
      prev = -1
    }

    elms = elms.closest('label').querySelector('.field_list')
    elms.innerText = val
    elms.className = elms.className.replace(/ ?text-[^ ]+/, '') + ' ' + ['', 'text-error', 'text-warning', 'text-success', 'text-info'][m]

    _.saveUserSettings(cnDict.settings, 'localStorage')
    return
  }

  filtered = data
  ;['level', 'list'].forEach(key=>{
    if(userOptions[key]) {
      tmp = filtered
      filtered = []
      tmp.forEach(line=>{
        if(
          line[key] == userOptions[key] || 
          (userOptions[key] == 'extra' && line[key].length == 6)
        ){
          filtered.push(line)
        }
      })
    }
  })
  if(filtered == data) filtered = [...data]

  if(userOptions.search){
    let key, search, tmp, a
    key = /\w/.test(userOptions.search) ? (/\d/.test(userOptions.search) ? 'pinyin' : 'definition_fr') : 'simplified'
    search = userOptions.search.trim().split(' ')
    tmp = filtered
    filtered = []
    tmp.forEach(line=>{
      for(a=0; a<search.length; a++){
        if(line[key].includes(search[a])){
          filtered.push(line)
          break
        }
      }
    })
  }

  if(userOptions.offset){
    a = (+(userOptions.offset.replace('%',''))/100 * filtered.length) | 0
    filtered = [...filtered.slice(a)]
  }

  if(userOptions.sort == 'random'){
    for(a=filtered.length; a>1; a--){
      b = (Math.random() * a) | 0
      ;([filtered[b], filtered[a-1]] = [filtered[a-1], filtered[b]])
    }
  }
  else if(userOptions.sort == 'pinyin'){
    filtered.sort((x, y) => {
      return x.pinyin2 < y.pinyin2 ? -1 : x.pinyin2 > y.pinyin2 ? 1 : 0
    })
  }

  /*
  html = ''
  filtered.slice(0, 10).forEach(line=>{
    row = '<tr><td><label>'
    row += `<span class="field_checkbox"><input name="checkbox" type="checkbox" ${line.list == userOptions.checkbox ? 'checked' : ''} class="checkbox checkbox-xs ${['', 'checkbox-error', 'checkbox-warning', 'checkbox-success', 'checkbox-info'][userOptions.checkbox?userOptions.checkbox.substring(5):0]}" onchange="handleDisplay(event)" value="${line.id}">&#12288;</span>`
    row += `<span class="field_simplified">${line.simplified}&#12288;</span>`
    row += `<span class="field_traditional">【${line.traditional}】&#12288;</span>`
    row += `<span class="field_pinyin">${line.pinyin}</span>`
    row += '<br class="field_br1">'
    row += `<span class="field_category_fr">${line.category_fr}&#12288;</span>`
    row += `<span class="field_definition_fr">${line.definition_fr}</span>`
    row += '<br class="field_br2">'
    row += `<span class="field_example">${line.example}</span>`
    row += '<br class="field_br3">'
    row += `<span class="field_id">#${line.id}&#12288;</span>`
    row += `<span class="field_level">${line.level}&#12288;</span>`
    row += `<span class="field_list">${line.list || ''}</span>`
    row += '</label></td></tr>'

    html += row
  })

  tbody.insertAdjacentHTML('beforeend', html)
  */

  handleDisplay2(filtered)
}


function handleDisplay2(event){
  function renderHTML(line){
    let listOpt = ['', 'checkbox-error', 'checkbox-warning', 'checkbox-success', 'checkbox-info']

    let row = ''
    row = `<tr><td><label${cnDict.settings.checkbox ? '' : ' for'}><div>`
    row += `<span class="field_checkbox"><input name="checkbox" type="checkbox" ${line.list == cnDict.settings.checkbox ? 'checked' : ''} class="checkbox checkbox-xs ${listOpt[cnDict.settings.checkbox?cnDict.settings.checkbox.at(-1):0]}" onchange="handleDisplay(event)" value="${line.id}">&#12288;</span>`
    row += `<span class="field_simplified">${line.simplified}&#12288;</span>`
    row += `<span class="field_traditional">【${line.traditional}】&#12288;</span>`
    row += `<span class="field_pinyin">${line.pinyin}&#12288</span>`
    row += `<span class="field_pinyin2">${line.pinyin2}</span>`
    row += '</div><div>'
    row += `<span class="field_category_fr">${line.category_fr}&#12288;</span>`
    row += `<span class="field_definition_fr">${line.definition_fr}</span>`
    row += '</div><div>'
    row += `<span class="field_example">${line.example}</span>`
    row += '</div><div>'
    row += `<span class="field_id">#${line.id}&#12288;</span>`
    row += `<span class="field_level">${line.level}&#12288;</span>`
    row += `<span class="field_list ${listOpt[line.list?.at(-1)||0].replace('checkbox', 'text')}">${line.list || ''}</span>`
    row += '</label></td></tr>'
    return row
  }

  document.querySelector('#dict_table tbody').innerHTML = ''
  runVirtual2(event || cnDict.data.hsk.value.slice(0, 200), document.querySelector('#dict_table tbody'), renderHTML)
}

function runVirtual2(items, container, fn) {

  if(!document.querySelector('#virtualizer_style')){
    document.head.insertAdjacentHTML('beforeend', `
<style id="virtualizer_style">
html, body{
  height: 100vh;
  margin: 0px;
  overflow-y: auto;
}
</style>
`)

    // const items = new Array(500).fill(true).map(() => 25);

    const m = 48 + 8*2 + 40

    const virtualizer = new VirtualCore.Virtualizer({
      count: items.length,
      getScrollElement: () => document.body,
      estimateSize: () => 20,
      scrollMargin: m,
      overscan: 2,
      observeElementRect: VirtualCore.observeElementRect,
      observeElementOffset: VirtualCore.observeElementOffset,
      scrollToFn: VirtualCore.elementScroll,
      onChange: (instance) => {

        let indexes = []
        let h, element, element2, elements = instance.getVirtualItems()

        if(!elements.length) return

        if(!instance.options._container){
          instance.options._items = items
          instance.options._container = container
          instance.options._fn = fn
          items = container = fn = undefined
        }

        // const container = document.querySelector('#list');
        h = Math.min(instance.getTotalSize(), elements.at(-1).start-m+200)
        if (h>instance.options._container.offsetHeight) {
          instance.options._container.style.height = `${h}px`;
        }

        // console.log(elements.length)

        for(element of elements){
          // console.log(elements.length, element.index)

          indexes.push(element.index)
          element2 = instance.measureElementCache.get(element.index)
          if(element2){
            if(element2.style.transform != `translateY(${element.start-m}px)`){
              element2.style.transform = `translateY(${element.start-m}px)`
            }
            continue
          }

          const row = document.createElement('tr');
          row.style.position = 'absolute';
          // li.style.height = `${element.size}px`;
          row.style.transform = `translateY(${element.start-m}px)`;
          // row.innerHTML = `Item ${element.index}` + (element.index % 2 ? '<br>-----' : '');
          row.innerHTML = instance.options._fn(instance.options._items[element.index])
          row.style.top = 0;
          row.style.left = 0;
          row.dataset.index = element.index
          instance.options._container.append(row);

          instance.measureElement(row)

          return

        }

        for(element of instance.measureElementCache){
          if(indexes.includes(+element[0])) continue;
          element[1].remove();
          instance.measureElementCache.delete(element[0])
        }

      },
    });
    virtualizer._willUpdate();

    window.vv = virtualizer

    return
  }
  else {
    if(vv.options._items?.length && items.length) vv.scrollToIndex(0)
    vv.measurementsCache = []
    vv.measureElementCache = new Map()
    vv.itemSizeCache = new Map()
    vv.options.count = 0
    vv.options._items = []
    vv.options._container.style.height = '0px'

    // document.querySelector('tbody').innerHTML = ''
    for(let elm = document.querySelector('tbody'); elm.firstChild; elm.removeChild(elm.firstChild));
  
    vv.options._items = items
    vv.options.count = items.length
    
    vv.notify(true)
  }

}