Source: editor/extensions/ext-imagelib/openclipart.js

/* eslint-disable node/no-unpublished-import */
import { jml, body, nbsp } from 'jamilih';
import $ from 'query-result';
import { manipulation } from 'qr-manipulation';

manipulation($, jml);

const baseAPIURL = 'https://openclipart.org/search/json/';

const jsVoid = 'javascript: void(0);'; // eslint-disable-line no-script-url

/**
 * Shows results after query submission.
 * @param {string} url
 * @returns {Promise<void>}
 */
async function processResults (url) {
  /**
   * @param {string} query
   * @returns {external:JamilihArray}
   */
  function queryLink (query) {
    return [ 'a', {
      href: jsVoid,
      dataset: { value: query },
      $on: { click (e) {
        e.preventDefault();
        const { value } = this.dataset;
        $('#query')[0].$set(value);
        $('#openclipart')[0].$submit();
      } }
    }, [ query ] ];
  }

  const r = await fetch(url);
  const json = await r.json();

  if (!json || json.msg !== 'success') {
    // Todo: This could use a generic alert library instead
    alert('There was a problem downloading the results');
    return;
  }
  const { payload, info: {
    results: numResults,
    pages,
    current_page: currentPage
  } } = json;

  // $('#page')[0].value = currentPage;
  // $('#page')[0].max = pages;

  // Unused properties:
  // - `svg_filesize` always 0?
  // - `dimensions: {
  //      png_thumb: {width, height},
  //      png_full_lossy: {width, height}
  //    }` object of relevance?
  // - No need for `tags` with `tags_array`
  // - `svg`'s: `png_thumb`, `png_full_lossy`, `png_2400px`
  const semiColonSep = '; ' + nbsp;
  $('#results').jml('div', [
    [ 'span', [
      'Number of results: ',
      numResults
    ] ],
    semiColonSep,
    [ 'span', [
      'page ',
      currentPage,
      ' out of ',
      pages
    ] ],
    ...payload.map(({
      title, description, id,
      uploader, created,
      svg: { url: svgURL },
      detail_link: detailLink,
      tags_array: tagsArray,
      downloaded_by: downloadedBy,
      total_favorites: totalFavorites
    }) => {
      const imgHW = '100px';
      const colonSep = ': ' + nbsp;
      return [ 'div', [
        [ 'button', { style: 'margin-right: 8px; border: 2px solid black;', dataset: { id, value: svgURL }, $on: {
          async click (e) {
            e.preventDefault();
            const { value: svgurl } = this.dataset;
            const post = (message) => {
              // Todo: Make origin customizable as set by opening window
              // Todo: If dropping IE9, avoid stringifying
              window.parent.postMessage(JSON.stringify({
                namespace: 'imagelib',
                ...message
              }), '*');
            };
            // Send metadata (also indicates file is about to be sent)
            post({
              name: title,
              id: svgurl
            });
            const result = await fetch(svgurl);
            const svg = await result.text();
            post({
              href: svgurl,
              data: svg
            });
          }
        } }, [
          // If we wanted interactive versions despite security risk:
          // ['object', {data: svgURL, type: 'image/svg+xml'}]
          [ 'img', { src: svgURL, style: `width: ${imgHW}; height: ${imgHW};` } ]
        ] ],
        [ 'b', [ title ] ],
        ' ',
        [ 'i', [ description ] ],
        ' ',
        [ 'span', [
          '(ID: ',
          [ 'a', {
            href: jsVoid,
            dataset: { value: id },
            $on: {
              click (e) {
                e.preventDefault();
                const { value } = this.dataset;
                $('#byids')[0].$set(value);
                $('#openclipart')[0].$submit();
              }
            }
          }, [ id ] ],
          ')'
        ] ],
        ' ',
        [ 'i', [
          [ 'a', {
            href: detailLink,
            target: '_blank'
          }, [ 'Details' ] ]
        ] ],
        [ 'br' ],
        [ 'span', [
          [ 'u', [ 'Uploaded by' ] ], colonSep,
          queryLink(uploader),
          semiColonSep
        ] ],
        [ 'span', [
          [ 'u', [ 'Download count' ] ], colonSep,
          downloadedBy,
          semiColonSep
        ] ],
        [ 'span', [
          [ 'u', [ 'Times used as favorite' ] ], colonSep,
          totalFavorites,
          semiColonSep
        ] ],
        [ 'span', [
          [ 'u', [ 'Created date' ] ], colonSep,
          created
        ] ],
        [ 'br' ],
        [ 'u', [ 'Tags' ] ], colonSep,
        ...tagsArray.map((tag) => {
          return [ 'span', [
            ' ',
            queryLink(tag)
          ] ];
        })
      ] ];
    }),
    [ 'br' ], [ 'br' ],
    (currentPage === 1 || pages <= 2
      ? ''
      : [ 'span', [
        [ 'a', {
          href: jsVoid,
          $on: {
            click (e) {
              e.preventDefault();
              $('#page')[0].value = 1;
              $('#openclipart')[0].$submit();
            }
          }
        }, [ 'First' ] ],
        ' '
      ] ]
    ),
    (currentPage === 1
      ? ''
      : [ 'span', [
        [ 'a', {
          href: jsVoid,
          $on: {
            click (e) {
              e.preventDefault();
              $('#page')[0].value = currentPage - 1;
              $('#openclipart')[0].$submit();
            }
          }
        }, [ 'Prev' ] ],
        ' '
      ] ]
    ),
    (currentPage === pages
      ? ''
      : [ 'span', [
        [ 'a', {
          href: jsVoid,
          $on: {
            click (e) {
              e.preventDefault();
              $('#page')[0].value = currentPage + 1;
              $('#openclipart')[0].$submit();
            }
          }
        }, [ 'Next' ] ],
        ' '
      ] ]
    ),
    (currentPage === pages || pages <= 2
      ? ''
      : [ 'span', [
        [ 'a', {
          href: jsVoid,
          $on: {
            click (e) {
              e.preventDefault();
              $('#page')[0].value = pages;
              $('#openclipart')[0].$submit();
            }
          }
        }, [ 'Last' ] ],
        ' '
      ] ]
    )
  ]);
}

jml('div', [
  [ 'style', [
    `.control {
      padding-top: 10px;
    }`
  ] ],
  [ 'form', {
    id: 'openclipart',
    $custom: {
      async $submit () {
        const url = new URL(baseAPIURL);
        [
          'query', 'sort', 'amount', 'page', 'byids'
        ].forEach((prop) => {
          const { value } = $('#' + prop)[0];
          if (value) {
            url.searchParams.set(prop, value);
          }
        });
        await processResults(url);
      }
    },
    $on: {
      submit (e) {
        e.preventDefault();
        this.$submit();
      }
    }
  }, [
    // Todo: i18nize
    [ 'fieldset', [
      [ 'legend', [ 'Search terms' ] ],
      [ 'div', { class: 'control' }, [
        [ 'label', [
          'Query (Title, description, uploader, or tag): ',
          [ 'input', { id: 'query', name: 'query', placeholder: 'cat', $custom: {
            $set (value) {
              $('#byids')[0].value = '';
              this.value = value;
            }
          }, $on: {
            change () {
              $('#byids')[0].value = '';
            }
          } } ]
        ] ]
      ] ],
      [ 'br' ],
      ' OR ',
      [ 'br' ],
      [ 'div', { class: 'control' }, [
        [ 'label', [
          'IDs (single or comma-separated): ',
          [ 'input', { id: 'byids', name: 'ids', placeholder: '271380, 265741', $custom: {
            $set (value) {
              $('#query')[0].value = '';
              this.value = value;
            }
          }, $on: {
            change () {
              $('#query')[0].value = '';
            }
          } } ]
        ] ]
      ] ]
    ] ],
    [ 'fieldset', [
      [ 'legend', [ 'Configuring results' ] ],
      [ 'div', { class: 'control' }, [
        [ 'label', [
          'Sort by: ',
          [ 'select', { id: 'sort' }, [
            // Todo: i18nize first values
            [ 'Date', 'date' ],
            [ 'Downloads', 'downloads' ],
            [ 'Favorited', 'favorites' ]
          ].map(([ text, value = text ]) => {
            return [ 'option', { value }, [ text ] ];
          }) ]
        ] ]
      ] ],
      [ 'div', { class: 'control' }, [
        [ 'label', [
          'Results per page: ',
          [ 'input', {
            id: 'amount', name: 'amount', value: 10,
            type: 'number', min: 1, max: 200, step: 1, pattern: '\\d+' } ]
        ] ]
      ] ],
      [ 'div', { class: 'control' }, [
        [ 'label', [
          'Page number: ',
          [ 'input', {
            // max: 1, // We'll change this based on available results
            id: 'page', name: 'page', value: 1, style: 'width: 40px;',
            type: 'number', min: 1, step: 1, pattern: '\\d+'
          } ]
        ] ]
      ] ]
    ] ],
    [ 'div', { class: 'control' }, [
      [ 'input', { type: 'submit' } ]
    ] ]
  ] ],
  [ 'div', { id: 'results' } ]
], body);