ID ) || swiftling_is_translator() ) && is_post_type_viewable( $post->post_type ) && ! swiftling_copy_original_id( $post ) ) { $actions['swiftling'] = sprintf( '%s', esc_url( swiftling_editor_url( $post->ID ) ), esc_html__( 'Translate', 'swiftling' ) ); } return $actions; } add_filter( 'post_row_actions', 'swiftling_row_actions', 10, 2 ); add_filter( 'page_row_actions', 'swiftling_row_actions', 10, 2 ); /** * Renders the rows of the editor, by section: for a page its text, then its excerpt for lists of * posts and its texts for search results, when it has any; for a template its own text, then * what every page shows. The first section needs no heading. * * Images, videos, links and code are rows like the texts, in the order the page shows them, but * marked as extras, which the editor hides until the user asks to see them. * * @param array $subject Subject the editor is open for, see swiftling_subject(). * @param string $lang Language shown. * @param array $result Result of swiftling_save_probe() or swiftling_save_term_list(). * @return string HTML: one tbody per section. */ function swiftling_live_rows_html( $subject, $lang, $result ) { // Every place the editor shows, with its link and the previous version of a changed text. $places = array(); foreach ( $result['sections'] as $units ) { foreach ( $units as $unit ) { foreach ( isset( $unit['link'] ) ? array( $unit, $unit['link'] ) : array( $unit ) as $entry ) { $map = isset( $result['maps'][ $entry['o'] ] ) ? $result['maps'][ $entry['o'] ]['units'] : array(); array_push( $places, ...array_filter( swiftling_live_places( $entry, $map ) ) ); } } } $language = swiftling_target_language( $lang ); $translations = swiftling_get_translations( $language['locale'], $places ); swiftling_live_placeholder( $language ); // The fields stored with a page and its excerpt follow its texts without a title: each row // names its field, see swiftling_live_row(). $titles = array( 'search' => __( 'Search results', 'swiftling' ), 'site' => __( 'Shown on every page: header, footer, menu and site title', 'swiftling' ), ); // What a section holds, where its title alone does not say it. $intros = array(); $empty = 'post' === $subject['kind'] ? __( 'No text of this page shows on it.', 'swiftling' ) : __( 'This template has no text of its own: what it shows comes from the posts and from the parts every page shows.', 'swiftling' ); if ( in_array( $subject['kind'], array( 'terms', 'fields' ), true ) ) { $empty = __( 'Nothing to translate yet.', 'swiftling' ); } if ( 'terms' === $subject['kind'] && ! empty( $result['list']['search'] ) ) { $empty = __( 'No term has these words.', 'swiftling' ); } if ( 'package' === $subject['kind'] ) { $empty = __( 'No texts in this list.', 'swiftling' ); } if ( 'site' === $subject['kind'] ) { $empty = 'site:footer' === $subject['key'] ? __( 'The page it was checked on shows no text in its footer. A menu in it is translated on the Menus screen.', 'swiftling' ) : __( 'The page it was checked on shows no text in its header. A menu in it is translated on the Menus screen.', 'swiftling' ); } if ( 'comments' === $subject['kind'] ) { $empty = ! empty( $result['list']['search'] ) ? __( 'No comment has these words.', 'swiftling' ) : __( 'No comments here.', 'swiftling' ); } // Rows of a menu without a field, such as the language switcher, by the number of texts before them. $info = 'menu' === $subject['kind'] && ! empty( $result['info'] ) ? $result['info'] : array(); // Items of a menu left out in the language have no rows: a closed list names them instead. $left_out = 'menu' === $subject['kind'] ? swiftling_menu_left_out_names( (int) substr( $subject['key'], 5 ), $language['locale'] ) : array(); if ( $left_out ) { list( $result['sections']['page'], $info ) = swiftling_live_without_left_out( $result['sections']['page'], $info, $left_out ); } if ( 'menu' === $subject['kind'] ) { $empty = $info ? __( 'Nothing in this menu to translate here.', 'swiftling' ) : __( 'This menu has no items yet.', 'swiftling' ); // Said once for every item that shows its page's title, rather than under each of them. $follows = array_filter( $result['sections']['page'], static function ( $unit ) { return ! empty( $unit['follows'] ); } ); if ( $follows ) { $intros['page'] = __( 'An item without a label of its own shows its page’s title, translated with the page. A label written here shows in this menu only, instead of it.', 'swiftling' ); } } // The classic menus a template shows are translated on the Menus screen: its section of what // every page shows points there. $menus = in_array( $subject['kind'], array( 'template', 'view' ), true ) && isset( $result['units'] ) && current_user_can( 'edit_theme_options' ) ? swiftling_template_menus( $result['units'] ) : array(); // Each group of rows: the sections of a page or template; for a list of terms, each term, under // its name, with the fields of the term its page does not show. $groups = array(); if ( 'terms' === $subject['kind'] && ! empty( $result['terms'] ) && $result['sections']['page'] ) { foreach ( $result['sections']['page'] as $unit ) { if ( ! isset( $groups[ $unit['o'] ] ) ) { $groups[ $unit['o'] ] = array( 'section' => 'page', 'title' => isset( $result['terms'][ $unit['o'] ] ) ? $result['terms'][ $unit['o'] ] : '', 'units' => array(), // The list itself, as the name of an attribute, has no address or fields of a term. 'term' => 0 === strpos( $unit['o'], 'term:' ) ? $unit['o'] : '', ); } $groups[ $unit['o'] ]['units'][] = $unit; } } else { foreach ( $result['sections'] as $section => $units ) { $groups[] = array( 'section' => $section, 'title' => isset( $titles[ $section ] ) ? $titles[ $section ] : '', 'units' => $units, 'term' => '', ); } } // The translated slugs of the terms on this page, when their list has pages of its own. $addresses = null; if ( 'terms' === $subject['kind'] && '' !== $subject['url'] && $groups ) { $addresses = swiftling_directories_supported() ? swiftling_term_slug_entries( array_keys( $groups ), $language['locale'] ) : array(); } ob_start(); foreach ( $groups as $group ) { $section = $group['section']; $units = $group['units']; $pointer = 'site' === $section && $menus; // A section without a title that holds only images, links or code hides with them: a note // that it holds them would stand under the page's texts without saying whose they are. $extras_only = 'page' !== $section && '' === $group['title'] && $units && ! array_filter( $units, 'swiftling_live_is_text_row' ); if ( ! $units && 'page' !== $section && ! $pointer ) { continue; } ?>

$unit ) { foreach ( isset( $info[ $index ] ) ? $info[ $index ] : array() as $row ) { swiftling_live_info_row( $row ); } $map = isset( $result['maps'][ $unit['o'] ] ) ? $result['maps'][ $unit['o'] ]['units'] : array(); if ( 'search' === $section ) { // The title of the section already says where these texts show. $unit['region'] = ''; } if ( 'menu' === $subject['kind'] ) { $unit['note'] = swiftling_menu_row_note( $unit, $lang ); if ( ! empty( $unit['follows'] ) ) { $unit['fallback'] = swiftling_menu_row_fallback( $unit, $lang ); $unit['placeholder'] = '' !== $unit['fallback']['text'] /* translators: %s: the page's translated title. */ ? sprintf( __( 'Shows the page’s title: “%s”', 'swiftling' ), wp_strip_all_tags( $unit['fallback']['text'] ) ) : __( 'Shows the page’s title, not translated yet', 'swiftling' ); } } elseif ( 'post' === $subject['kind'] && $unit['o'] === $subject['key'] && 'field/post_title' === $unit['p'] ) { // The page's own title, wherever its page check found it: as a heading, or only // as a link of a menu, as a builder's page in a theme that leaves out the title. $unit['label'] = __( 'Title', 'swiftling' ); $unit['region'] = ''; $unit['note'] = swiftling_title_menus_note( $subject['post']->ID, $lang ); } swiftling_live_row( $unit, $map, $translations, swiftling_can_translate_object( $unit['o'] ) ); } // Rows after the last text, such as the language switcher at the end of a menu. foreach ( $info as $at => $after ) { if ( $at >= count( $rows ) ) { array_map( 'swiftling_live_info_row', $after ); } } if ( $pointer ) { swiftling_live_menu_pointer( $menus, $lang ); } if ( $left_out && 'page' === $section ) { swiftling_live_left_out_row( $subject, $language, $left_out ); } if ( '' !== $group['term'] && null !== $addresses ) { swiftling_live_term_address( $lang, $group['term'], $addresses ); } if ( '' !== $group['term'] ) { swiftling_live_term_more( $subject, $lang, $group['term'], $result['fields'] ); } ?> term_id; $link = get_term_link( $term ); $original = is_wp_error( $link ) ? '' : rawurldecode( (string) wp_parse_url( $link, PHP_URL_PATH ) ); ?>

taxonomy, $lang ); ?>
". * @param string $lang Language slug the editor shows. * @param string $owner Object key of the term. * @param array $fields Fields of the list's terms, see swiftling_term_fields(). */ function swiftling_live_term_more( $subject, $lang, $owner, $fields ) { $taxonomy = get_taxonomy( substr( $subject['key'], 6 ) ); $more = array(); foreach ( $fields as $field ) { if ( true === $field['shown'] ) { continue; } foreach ( $field['texts'] as $text ) { if ( $owner === $text['o'] ) { $more[] = $field + array( 'value' => $text['v'] ); break; } } } if ( ! $more || ! $taxonomy ) { return; } // One nonce for every form here; wp_nonce_field() would give each the same element ID. $nonce = wp_create_nonce( 'swiftling_term_field_' . $taxonomy->name ); ?>

labels->name ) ); ?>

$info Rows without a field, by the number of texts before them. * @param array $left_out Item ID => name, see swiftling_menu_left_out_names(). * @return array{0: array, 1: array} The texts and the rows without a field that remain. */ function swiftling_live_without_left_out( $units, $info, $left_out ) { $kept = array(); $moved = array(); $count = count( $units ); for ( $index = 0; $index <= $count; $index++ ) { foreach ( isset( $info[ $index ] ) ? $info[ $index ] : array() as $row ) { if ( empty( $row['item'] ) || ! isset( $left_out[ $row['item'] ] ) ) { $moved[ count( $kept ) ][] = $row; } } if ( $index < $count && ! ( isset( $units[ $index ]['item'] ) && isset( $left_out[ $units[ $index ]['item'] ] ) ) ) { $kept[] = $units[ $index ]; } } return array( $kept, $moved ); } /** * Prints the closed list of the items of a menu left out in the language the editor shows, with a * link to the menu's table of items by language. * * @param array $subject Subject "menu:". * @param array $language Language the editor shows. * @param array $left_out Item ID => name, see swiftling_menu_left_out_names(). */ function swiftling_live_left_out_row( $subject, $language, $left_out ) { $names = array_filter( array_map( 'trim', $left_out ) ); ?>

    term_id; ?>
  • name ); ?>
$unit ) { $next = isset( $rows[ $index + 1 ] ) ? $rows[ $index + 1 ] : null; if ( 'image' === $unit['kind'] && $next && 'alt' === $next['kind'] && isset( $unit['el'], $next['el'] ) && $unit['el'] >= 0 && $unit['el'] === $next['el'] ) { $rows[ $index ]['group'] = 'start'; $rows[ $index + 1 ]['group'] = 'end'; } } return $rows; } /** * Whether a value belongs to an image or a file of the media library, such as its address or its * alt text. * * @param string $owner Object key. * @return bool */ function swiftling_is_media_owner( $owner ) { return 0 === strpos( $owner, 'post:' ) && 'attachment' === get_post_type( (int) substr( $owner, 5 ) ); } /** * Whether a text is code: HTML that cannot be edited as formatted text, such as an embed or a * shortcode, what a code block holds, or a value from a field the builder declares as code. * * @param array $unit Text found by the probe. * @return bool */ function swiftling_live_is_code( $unit ) { if ( swiftling_is_address_kind( $unit['kind'] ) ) { return false; } if ( 1 === preg_match( '~<[a-z/!][^>]*>~i', $unit['v'] ) && null === swiftling_rich_parts( $unit['v'] ) ) { return true; } // What a code block holds is code, whatever its tags: edited by hand, never machine translated. if ( isset( $unit['r'] ) && 0 === strpos( (string) $unit['r'], 'block:core/code#html|' ) ) { return true; } return \SwiftLing\Builders\Registry::isCode( $unit['o'], $unit['p'], isset( $unit['r'] ) ? $unit['r'] : '' ); } /** * Returns what a row of code is called, after what it shows: a table, a quote, a verse, an image or * a list read better than "Code". Anything else is code. * * @param string $code Code. * @return string */ function swiftling_live_code_label( $code ) { $kinds = array( '~ __( 'Table', 'swiftling' ), '~\bwp-block-(?:pull)?quote\b| __( 'Quote', 'swiftling' ), '~\bwp-block-verse\b~i' => __( 'Verse', 'swiftling' ), '~]*>.* __( 'Image with caption', 'swiftling' ), '~^\s*<(?:ul|ol)\b~i' => __( 'List', 'swiftling' ), ); foreach ( $kinds as $pattern => $label ) { if ( 1 === preg_match( $pattern, $code ) ) { return $label; } } return \SwiftLing\Admin\Ui::partLabels()['code']; } /** * Whether code shows words to visitors: text between its tags, such as the cells of a table, a * quote or the caption of an image. The insides of scripts, styles and comments are no such * text, and neither are shortcodes or code without any tag, such as CSS a builder keeps in a field. * * @param string $code Code. * @return bool */ function swiftling_has_visitor_text( $code ) { if ( 1 !== preg_match( '~<[a-z/!][^>]*>~i', $code ) ) { return false; } $text = (string) preg_replace( '~<(script|style|noscript|template|svg)\b.*?|~is', ' ', $code ); $text = (string) preg_replace( '~\[/?[a-z][\w-]*(?:\s[^\]]*)?\]~i', ' ', wp_strip_all_tags( $text ) ); return 1 === preg_match( '~[\p{L}\p{N}]~u', html_entity_decode( $text, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) ); } /** * Whether a row is shown from the start: every text, and code with words for visitors. Its * translation counts toward the state of the page, so the editor never hides it. Images, videos, * link addresses and code without such words show only on request: addresses count for nothing, * as most stay the same in every language, and "Show only empty fields" shows the code too. * * @param array $unit Text found by the probe. * @return bool */ function swiftling_live_is_text_row( $unit ) { if ( swiftling_is_address_kind( $unit['kind'] ) ) { return false; } return ! swiftling_live_is_code( $unit ) || swiftling_has_visitor_text( $unit['v'] ); } /** * Counts what the tab of images, links and code shows: images, videos, links and all code, each * place once, also the addresses of links around texts. Code with words for visitors is among the * texts too. * * @param array $result Result of swiftling_save_probe(). * @return int */ function swiftling_live_extras_count( $result ) { $count = 0; foreach ( $result['sections'] as $units ) { foreach ( $units as $unit ) { $count += ( ! swiftling_live_is_text_row( $unit ) || swiftling_live_is_code( $unit ) ? 1 : 0 ) + ( isset( $unit['link'] ) ? 1 : 0 ); } } return $count; } /** * Renders one text: what it is, with the field it is stored in where a builder names it, the * original, the translation and its state. * * The translation field depends on the original. Plain text gets a plain field, and so does HTML * with nothing to format inside its wrapping tags, such as a heading. HTML with simple formatting, * such as a paragraph with bold text and links, gets the WordPress visual editor for the text * inside its wrapping tags. The wrapping tags stay as they are. Other HTML, such as an image with its * caption or a shortcode, gets a code field, and the original shows as code, as it is stored. A * link, image or video address gets an address field, an image also a button to choose another one * from the media library, and a text inside a link shows the address of that link under it. * * Every place is a row with a translation of its own, also where the page shows the same words * again. A text from a shared object, such as a widget's default text, is one place however often * the page shows it. * * A text may carry a depth, for an item of a submenu, and a note shown under its field: whose text * it is and links to where else it is translated, see swiftling_menu_row_note(). A text that falls * back to another while it is empty carries that one's translation and state as fallback, and what * its empty field says as placeholder: the row then counts as the fallback does, and filling empty * fields leaves it alone, see assets/js/editor.js. * * @param array $unit Text or link found by the probe. * @param array $map Map of the object the text belongs to. * @param array $translations Translations into the language shown. * @param bool $can_edit Whether the user may translate the object. */ function swiftling_live_row( $unit, $map, $translations, $can_edit ) { $labels = \SwiftLing\Admin\Ui::partLabels(); $regions = \SwiftLing\Admin\Ui::regionLabels(); $kind = isset( $labels[ $unit['kind'] ] ) ? $unit['kind'] : 'text'; $field = swiftling_live_translation( $unit, $map, $translations ); $is_link = swiftling_is_address_kind( $kind ); $is_html = ! $is_link && 1 === preg_match( '~<[a-z/!][^>]*>~i', $unit['v'] ); $is_code = swiftling_live_is_code( $unit ); // Code is edited as code, also when it would fit the visual editor. $parts = $is_html && ! $is_code ? swiftling_rich_parts( $unit['v'] ) : null; $state = $is_link && 'missing' === $field['state'] ? 'same' : $field['state']; // A text that falls back to another, such as a menu's label of a page's title: empty here, it // shows the other, in that one's state. $fallback = isset( $unit['fallback'] ) ? $unit['fallback'] : null; if ( $fallback && 'missing' === $field['state'] && '' !== $fallback['text'] ) { $state = $fallback['state']; } $classes = array( 'is-' . $state, 'is-type-' . $kind ); if ( ! swiftling_live_is_text_row( $unit ) ) { $classes[] = 'is-extra'; } // Code is in the tab of images, links and code, also code that shows among the texts. if ( $is_code ) { $classes[] = 'is-code'; } if ( ! empty( $unit['group'] ) ) { $classes[] = 'is-image-group'; $classes[] = 'is-group-' . $unit['group']; } // An item of a submenu, indented under its parent, see includes/menus.php. if ( ! empty( $unit['depth'] ) ) { $classes[] = 'is-depth-' . min( 3, (int) $unit['depth'] ); } $is_media = swiftling_is_media_owner( $unit['o'] ); // What the row is called: its kind, or the label of the field it is, such as a field of a term. $title = isset( $unit['label'] ) && '' !== $unit['label'] ? (string) $unit['label'] : $labels[ $kind ]; // The field the text is stored in, where a builder names it, such as "Team members 2 › Name". $place = isset( $unit['place'] ) ? (string) $unit['place'] : \SwiftLing\Builders\Registry::fieldLabel( $unit['o'], $unit['p'] ); if ( '' === $place && 'field/post_excerpt' === $unit['p'] && 0 === strpos( $unit['o'], 'post:' ) ) { $place = __( 'Excerpt', 'swiftling' ); } ?> >

$key, 'text' => $translations[ $key ]['text'], 'previous' => '', 'state' => swiftling_status_state( $translations[ $key ]['status'] ), ); } return array( 'key' => $key, 'text' => '', 'previous' => '' !== $prev && isset( $translations[ $prev ] ) ? $translations[ $prev ]['text'] : '', 'state' => '' !== $prev && isset( $translations[ $prev ] ) ? 'outdated' : 'missing', ); } /** * Returns where the translation of a text or link is stored: the key of its place for the current * version and, while a changed text waits for its new translation, for the previous one. * * @param array $unit Text or link found by the probe. * @param array $map Map of the object it belongs to. * @return array{0: string, 1: string} Key now, and of the previous version or "". */ function swiftling_live_places( $unit, $map ) { $unit_key = swiftling_unit_key( $unit ); $entry = isset( $map[ $unit_key ] ) ? $map[ $unit_key ] : swiftling_hash( $unit['v'] ); $hash = is_array( $entry ) ? $entry[0] : $entry; $previous = is_array( $entry ) ? $entry[1] : ''; return array( swiftling_place_key( $unit['o'], $unit_key, $hash ), '' === $previous ? '' : swiftling_place_key( $unit['o'], $unit_key, $previous ), ); } /** * Returns what an empty translation field says: what to write there, and what visitors read until * then, naming both languages, as "Write the Deutsch translation here. Until you do, visitors read * the original (Hrvatski)." * * @param array|null $language The language the editor shows; given once, before the rows render. * @return string */ function swiftling_live_placeholder( $language = null ) { static $text = ''; if ( null !== $language ) { $text = sprintf( /* translators: 1: language of the translation, e.g. Deutsch, 2: language of the original, e.g. Hrvatski. */ __( 'Write the %1$s translation here. Until you do, visitors read the original (%2$s).', 'swiftling' ), $language['name'], swiftling_languages()['source']['name'] ); } return '' !== $text ? $text : __( 'Write the translation here. Until you do, visitors read the original.', 'swiftling' ); } /** * Returns how many rows the field of a translation gets: room for its text, at about 70 * characters a row, and, while it is empty, for its placeholder too, which says what to do. The * placeholder gets 60 characters a row, as its words wrap earlier in a narrow column; a title of * a few words gave one row, and the placeholder showed half of its first line. * * @param string $text Text the field holds, or its original. * @param string $placeholder Placeholder the empty field shows; empty when it has a translation. * @return int From 1 to 12. */ function swiftling_live_field_rows( $text, $placeholder = '' ) { $rows = (int) ceil( strlen( $text ) / 70 ) + substr_count( $text, "\n" ); if ( '' !== $placeholder ) { $rows = max( $rows, (int) ceil( mb_strlen( $placeholder ) / 60 ) ); } return max( 1, min( 12, $rows ) ); } /** * Renders the field for the translation of a text. * * @param array $unit Text found by the probe. * @param array $field Its translation, from swiftling_live_translation(). * @param array|null $parts Wrapping tags and editable text, from swiftling_rich_parts(), or null. * @param bool $is_html Whether the original is HTML. * @param bool $can_edit Whether the user may translate it. * @param bool $is_code Whether it is code, which gets a code field. */ function swiftling_live_text_field( $unit, $field, $parts, $is_html, $can_edit, $is_code = false ) { $input_id = 'swiftling-t-' . $field['key']; $placeholder = isset( $unit['placeholder'] ) ? $unit['placeholder'] : swiftling_live_placeholder(); $rows = swiftling_live_field_rows( $unit['v'], '' === $field['text'] ? $placeholder : '' ); // A field whose rows its placeholder decides fits it exactly where the browser can, see admin.css. $fit = $rows > swiftling_live_field_rows( $unit['v'] ); // A text from code may go through sprintf(): its placeholders must stay, see swiftling_format_problems(). $placeholders = 0 === strpos( $unit['o'], 'gettext:' ) ? (array) swiftling_format_placeholders( $unit['v'] ) : array(); $format = $placeholders ? implode( ' ', array_unique( wp_list_pluck( $placeholders, 'raw' ) ) ) : ''; ?> swiftling_live_field_rows( $plain ); ?>

__( 'Address', 'swiftling' ), 'image' => __( 'Image address', 'swiftling' ), 'video' => __( 'Video address', 'swiftling' ), ); return isset( $labels[ $kind ] ) ? $labels[ $kind ] : $labels['url']; } /** * Renders the field for an address in the language shown: of a link, an image or a video. Empty * means the same address as in the original. * * @param array $link Address found by the probe. * @param array $field Its translation, from swiftling_live_translation(). * @param bool $can_edit Whether the user may translate it. * @param string $label Label of the field. * @param bool $pick Whether to offer a button that picks an image from the media library. * @param bool $media Whether the address is the file of an image in the media library, which * changes wherever the image is used. */ function swiftling_live_link_field( $link, $field, $can_edit, $label, $pick = false, $media = false ) { $input_id = 'swiftling-t-' . $field['key']; ?> > = $open_length + $close_length; $length = strlen( $inner ) ) { $opens = '' === $open || 0 === strpos( $inner, $open ); $closes = '' === $close || substr( $inner, -$close_length ) === $close; if ( ! $opens || ! $closes ) { break; } $inner = trim( substr( $inner, $open_length, $length - $open_length - $close_length ) ); $found = true; } return $found ? $inner : $text; } /** * Returns HTML without any tags as the text a person reads and types, entities decoded. * * @param string $html Part of an HTML value, such as the text inside a heading. * @return string|null The text, or null when the HTML holds a tag or a comment. */ function swiftling_plain_text( $html ) { if ( false !== strpos( $html, '<' ) ) { return null; } return html_entity_decode( $html, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); } /** * Splits an HTML value for the visual editor. The tags that wrap the whole text, such as a * paragraph or a list item, and tags a fragment opens or closes for its surroundings stay as they * are; only the text with simple formatting in between is edited, so a translation cannot change * the structure a builder relies on. * * @param string $html Original value. * @return array{0: string, 1: string, 2: string, 3: bool}|null Prefix, editable middle, suffix, and whether * the middle holds paragraphs or lists; null when the value holds more than simple formatting. */ function swiftling_rich_parts( $html ) { $inline = array( 'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'del', 'dfn', 'em', 'i', 'ins', 'kbd', 'mark', 'q', 's', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'time', 'u', 'var', 'wbr' ); $blocks = array( 'p', 'ul', 'ol', 'li', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote' ); $wraps = array( 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'figcaption', 'blockquote', 'cite', 'summary', 'td', 'th', 'dt', 'dd', 'div', 'label' ); $void = array( 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'source', 'track', 'wbr' ); // Comments and shortcodes are edited as code. if ( false !== strpos( $html, '