<commit_before>/**
 * Copyright (c) Tiny Technologies, Inc. All rights reserved.
 * Licensed under the LGPL or a commercial license.
 * For LGPL see License.txt in the project root for license information.
 * For commercial licenses see https://www.tiny.cloud/
 */

import { Types } from '@ephox/bridge';
import { Element, HTMLElement } from '@ephox/dom-globals';
import { Arr, Cell, Merger, Obj, Option, Type } from '@ephox/katamari';
import Editor from 'tinymce/core/api/Editor';
import Settings from '../api/Settings';
import { dataToHtml } from '../core/DataToHtml';
import * as HtmlToData from '../core/HtmlToData';
import Service from '../core/Service';
import { MediaData } from '../core/Types';
import UpdateHtml from '../core/UpdateHtml';

type ApiSubData = {
  value: string;
  meta: Record<string, any>;
};

type ApiData = {
  source?: ApiSubData;
  altsource?: ApiSubData;
  poster?: ApiSubData;
  embed?: string;
  dimensions?: {
    width?: string;
    height?: string;
  };
};

const extractMeta = (sourceInput: string, data: ApiData): Option<Record<string, any>> => {
  return Obj.get(data, sourceInput as any).bind((mainData: ApiSubData) => Obj.get(mainData, 'meta'));
};

const getValue = (data: ApiData, metaData: Record<string, any>) => (prop: string): string => {
  // Cases:
  // 1. Get the nested value prop (component is the executed urlinput)
  // 2. Get from metadata (a urlinput was executed but urlinput != this component)
  // 3. Not a urlinput so just get string
  // ASSUMPTION: data will always contain an entry for every prop. Since it's typed
  // this seems extremely likely.

  const getFromMetaData = Obj.get(metaData, prop);
  const getFromData = Obj.get(data, prop as any);
  const getNonEmptyValue = (c) => Obj.get(c, 'value').bind((v) => v.length > 0 ? Option.some(v) : Option.none());

  return Obj.get(data, prop as any).bind((child) => {
    return Type.isObject(child) ? getNonEmptyValue(child).or(getFromMetaData) : getFromMetaData.or(getFromData);
  }).getOr('');
};

const getDimensions = (data: ApiData, metaData: Record<string, any>) => {
  const dimensions = {};
  Obj.get(data, 'dimensions').each((dims) => {
    Arr.each([ 'width', 'height' ] as ('width' | 'height')[], (prop) => {
      Obj.get(metaData, prop).or(Obj.get(dims, prop)).each((value) => dimensions[prop] = value);
    });
  });
  return dimensions;
};

const unwrap = (data: ApiData, sourceInput?: string): MediaData => {
  const metaData = sourceInput ? extractMeta(sourceInput, data).getOr({}) : {};
  const get = getValue(data, metaData);
  const dims = getDimensions(data, metaData);
  return {
    source: get('source'),
    altsource: get('altsource'),
    poster: get('poster'),
    embed: get('embed'),
    ...dims
  };
};

const wrap = (data: MediaData): ApiData => {
  const wrapped = Merger.merge(data, {
    source: { value: Obj.get(data, 'source').getOr('') },
    altsource: { value: Obj.get(data, 'altsource').getOr('') },
    poster: { value: Obj.get(data, 'poster').getOr('') }
  });

  // Add additional size values that may or may not have been in the html
  Arr.each([ 'width', 'height' ] as (keyof MediaData)[], (prop) => {
    Obj.get(data, prop).each((value) => {
      const dimensions = wrapped.dimensions || {};
      dimensions[prop] = value;
      wrapped.dimensions = dimensions;<commit_msg>TINY-4163 Types and clean up<commit_after>/**
 * Copyright (c) <NAME>, Inc. All rights reserved.
 * Licensed under the LGPL or a commercial license.
 * For LGPL see License.txt in the project root for license information.
 * For commercial licenses see https://www.tiny.cloud/
 */

import { Types } from '@ephox/bridge';
import { Element, HTMLElement } from '@ephox/dom-globals';
import { Arr, Cell, Merger, Obj, Option, Type } from '@ephox/katamari';
import Editor from 'tinymce/core/api/Editor';
import Settings from '../api/Settings';
import { dataToHtml } from '../core/DataToHtml';
import * as HtmlToData from '../core/HtmlToData';
import Service from '../core/Service';
import { MediaData } from '../core/Types';
import UpdateHtml from '../core/UpdateHtml';

type ApiSubData = {
  value: string;
  meta: Record<string, any>;
};

type ApiData = {
  source?: ApiSubData;
  altsource?: ApiSubData;
  poster?: ApiSubData;
  embed?: string;
  dimensions?: {
    width?: string;
    height?: string;
  };
};

const extractMeta = (sourceInput: string, data: ApiData): Option<Record<string, any>> => {
  return Obj.get(data, sourceInput as any).bind((mainData: ApiSubData) => Obj.get(mainData, 'meta'));
};

const getValue = (data: ApiData, metaData: Record<string, any>) => (prop: string): Record<string, string> => {
  // Cases:
  // 1. Get the nested value prop (component is the executed urlinput)
  // 2. Get from metadata (a urlinput was executed but urlinput != this component)
  // 3. Not a urlinput so just get string
  // ASSUMPTION: we only want to get values for props that already exist in data
  const getFromData: Option<string | Record<string, string>> = Obj.get(data, prop as any);
  const getFromMetaData: Option<string> = Obj.get(metaData, prop);
  const getNonEmptyValue = (c: Record<string, string>) => Obj.get(c, 'value').bind((v) => v.length > 0 ? Option.some(v) : Option.none());

  return getFromData.bind((child): Option<Record<string, string>> => {
    const val = Type.isObject(child) ? getNonEmptyValue(child as Record<string, string>).or(getFromMetaData) : getFromMetaData.or(getFromData as Option<string>);
    return val.fold(() => Option.none(), (v: string) => Option.some({ [prop]: v }));
  }).getOr({});
};

const getDimensions = (data: ApiData, metaData: Record<string, string>) => {
  const dimensions = {};
  Obj.get(data, 'dimensions').each((dims) => {
    Arr.each([ 'width', 'height' ] as ('width' | 'height')[], (prop) => {
      Obj.get(metaData, prop).or(Obj.get(dims, prop)).each((value) => dimensions[prop] = value);
    });
  });
  return dimensions;
};

const unwrap = (data: ApiData, sourceInput?: string): MediaData => {
  const metaData = sourceInput ? extractMeta(sourceInput, data).getOr({}) : {};
  const get = getValue(data, metaData);
  return {
    ...get('source'),
    ...get('altsource'),
    ...get('poster'),
    ...get('embed'),
    ...getDimensions(data, metaData)
  } as any;
};

const wrap = (data: MediaData): ApiData => {
  const wrapped = Merger.merge(data, {
    source: { value: Obj.get(data, 'source').getOr('') },
    altsource: { value: Obj.get(data, 'altsource').getOr('') },
    poster: { value: Obj.get(data, 'poster').getOr('') }
  });

  // Add additional size values that may or may not have been in the html
  Arr.each([ 'width', 'height' ] as (keyof MediaData)[], (prop) => {
    Obj.get(data, prop).each((value) => {
      const dimensions = wrapped.dimensions || {};
      dimensions[prop] = value;
      wrapped.dimensions = dimensions;
{'fraction_non_alphanumeric': 0.17150357299110397, 'fraction_numerical': 0.0017500364590928978, 'mean_word_length': 3.9587852494577005, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 57, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, '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': '5996363', 'n_tokens_mistral': 2206, 'n_tokens_neox': 2240, 'n_words': 636}