diff --git a/includes/Classifai/Admin/PreviewClassifierData.php b/includes/Classifai/Admin/PreviewClassifierData.php
index 37f3f5cfa..7f090464c 100644
--- a/includes/Classifai/Admin/PreviewClassifierData.php
+++ b/includes/Classifai/Admin/PreviewClassifierData.php
@@ -20,7 +20,7 @@ public function __construct() {
public function get_post_classifier_preview_data() {
$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : false;
- if ( ! $nonce || ! wp_verify_nonce( $nonce, 'classifai-previewer-action' ) ) {
+ if ( ! $nonce || ! wp_verify_nonce( $nonce, 'classifai-previewer-watson_nlu-action' ) ) {
wp_send_json_error( esc_html__( 'Failed nonce check.', 'classifai' ) );
}
@@ -45,7 +45,13 @@ public function get_post_classifier_preview_data() {
public function get_post_search_results() {
$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : false;
- if ( ! $nonce || ! wp_verify_nonce( $nonce, 'classifai-previewer-action' ) ) {
+ if (
+ ! $nonce
+ || (
+ ! wp_verify_nonce( $nonce, 'classifai-previewer-openai_embeddings-action' )
+ && ! wp_verify_nonce( $nonce, 'classifai-previewer-watson_nlu-nonce' )
+ )
+ ) {
wp_send_json_error( esc_html__( 'Failed nonce check.', 'classifai' ) );
}
diff --git a/includes/Classifai/Providers/OpenAI/EmbeddingCalculations.php b/includes/Classifai/Providers/OpenAI/EmbeddingCalculations.php
index 994037c65..7d41b162e 100644
--- a/includes/Classifai/Providers/OpenAI/EmbeddingCalculations.php
+++ b/includes/Classifai/Providers/OpenAI/EmbeddingCalculations.php
@@ -15,11 +15,10 @@ class EmbeddingCalculations {
*
* @param array $source_embedding Embedding data of the source item.
* @param array $compare_embedding Embedding data of the item to compare.
- * @param float $threshold The threshold to use for the similarity calculation.
*
* @return bool|float
*/
- public function similarity( array $source_embedding = [], array $compare_embedding = [], $threshold = 1 ) {
+ public function similarity( array $source_embedding = [], array $compare_embedding = [] ) {
if ( empty( $source_embedding ) || empty( $compare_embedding ) ) {
return false;
}
@@ -58,20 +57,8 @@ function( $x ) {
// Do the math.
$distance = 1.0 - ( $combined_average / sqrt( $source_average * $compare_average ) );
- /**
- * Filter the threshold for the similarity calculation.
- *
- * @since 2.5.0
- * @hook classifai_threshold
- *
- * @param {float} $threshold The threshold to use.
- *
- * @return {float} The threshold to use.
- */
- $threshold = apply_filters( 'classifai_threshold', $threshold );
-
- // Ensure we are within the range of 0 to 1.0 (i.e. $threshold).
- return max( 0, min( abs( (float) $distance ), $threshold ) );
+ // Ensure we are within the range of 0 to 1.0.
+ return max( 0, min( abs( (float) $distance ), 1.0 ) );
}
}
diff --git a/includes/Classifai/Providers/OpenAI/Embeddings.php b/includes/Classifai/Providers/OpenAI/Embeddings.php
index a6450fb7b..d6415fd47 100644
--- a/includes/Classifai/Providers/OpenAI/Embeddings.php
+++ b/includes/Classifai/Providers/OpenAI/Embeddings.php
@@ -85,6 +85,7 @@ public function register() {
add_filter( 'rest_api_init', [ $this, 'add_process_content_meta_to_rest_api' ] );
add_action( 'add_meta_boxes', [ $this, 'add_metabox' ] );
add_action( 'save_post', [ $this, 'save_metabox' ] );
+ add_action( 'wp_ajax_get_post_classifier_embeddings_preview_data', array( $this, 'get_post_classifier_embeddings_preview_data' ) );
}
}
@@ -456,12 +457,36 @@ public function supported_taxonomies() {
return apply_filters( 'classifai_openai_embeddings_taxonomies', $this->get_supported_taxonomies() );
}
+ /**
+ * Get the data to preview terms.
+ *
+ * @since 2.5.0
+ *
+ * @return array
+ */
+ public function get_post_classifier_embeddings_preview_data() {
+ $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : false;
+
+ if ( ! $nonce || ! wp_verify_nonce( $nonce, 'classifai-previewer-openai_embeddings-action' ) ) {
+ wp_send_json_error( esc_html__( 'Failed nonce check.', 'classifai' ) );
+ }
+
+ $post_id = filter_input( INPUT_POST, 'post_id', FILTER_SANITIZE_NUMBER_INT );
+
+ $embeddings_terms = $this->generate_embeddings_for_post( $post_id, true );
+
+ return wp_send_json_success( $embeddings_terms );
+ }
+
/**
* Trigger embedding generation for content being saved.
*
- * @param int $post_id ID of post being saved.
+ * @param int $post_id ID of post being saved.
+ * @param bool $dryrun Whether to run the process or just return the data.
+ *
+ * @return array|WP_Error
*/
- public function generate_embeddings_for_post( $post_id ) {
+ public function generate_embeddings_for_post( $post_id, $dryrun = false ) {
// Don't run on autosaves.
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
@@ -476,14 +501,17 @@ public function generate_embeddings_for_post( $post_id ) {
// Only run on supported post types and statuses.
if (
- ! in_array( $post->post_type, $this->supported_post_types(), true ) ||
- ! in_array( $post->post_status, $this->supported_post_statuses(), true )
+ ! $dryrun
+ && (
+ ! in_array( $post->post_type, $this->supported_post_types(), true ) ||
+ ! in_array( $post->post_status, $this->supported_post_statuses(), true )
+ )
) {
return;
}
// Don't run if turned off for this particular post.
- if ( 'no' === get_post_meta( $post_id, '_classifai_process_content', true ) ) {
+ if ( 'no' === get_post_meta( $post_id, '_classifai_process_content', true ) && ! $dryrun ) {
return;
}
@@ -491,8 +519,12 @@ public function generate_embeddings_for_post( $post_id ) {
// Add terms to this item based on embedding data.
if ( $embeddings && ! is_wp_error( $embeddings ) ) {
- update_post_meta( $post_id, 'classifai_openai_embeddings', array_map( 'sanitize_text_field', $embeddings ) );
- $this->set_terms( $post_id, $embeddings );
+ if ( $dryrun ) {
+ return $this->get_terms( $embeddings );
+ } else {
+ update_post_meta( $post_id, 'classifai_openai_embeddings', array_map( 'sanitize_text_field', $embeddings ) );
+ return $this->set_terms( $post_id, $embeddings );
+ }
}
}
@@ -513,6 +545,102 @@ private function set_terms( int $post_id = 0, array $embedding = [] ) {
$settings = $this->get_settings();
$number_to_add = $settings['number'] ?? 1;
+ $embedding_similarity = $this->get_embeddings_similarity( $embedding );
+
+ if ( empty( $embedding_similarity ) ) {
+ return;
+ }
+
+ // Set terms based on similarity.
+ foreach ( $embedding_similarity as $tax => $terms ) {
+ // Sort embeddings from lowest to highest.
+ asort( $terms );
+
+ // Only add the number of terms specified in settings.
+ if ( count( $terms ) > $number_to_add ) {
+ $terms = array_slice( $terms, 0, $number_to_add, true );
+ }
+
+ wp_set_object_terms( $post_id, array_map( 'absint', array_keys( $terms ) ), $tax, false );
+ }
+ }
+
+ /**
+ * Get the terms of a post based on embeddings.
+ *
+ * @param array $embedding Embedding data.
+ *
+ * @return array|WP_Error
+ */
+ private function get_terms( array $embedding = [] ) {
+ if ( empty( $embedding ) ) {
+ return new WP_Error( 'data_required', esc_html__( 'Valid embedding data is required to get terms.', 'classifai' ) );
+ }
+
+ $settings = $this->get_settings();
+ $number_to_add = $settings['number'] ?? 1;
+ $embedding_similarity = $this->get_embeddings_similarity( $embedding, false );
+
+ if ( empty( $embedding_similarity ) ) {
+ return;
+ }
+
+ // Set terms based on similarity.
+ $index = 0;
+ $result = [];
+
+ foreach ( $embedding_similarity as $tax => $terms ) {
+ // Get the taxonomy name.
+ $taxonomy = get_taxonomy( $tax );
+ $tax_name = $taxonomy->labels->singular_name;
+
+ // Sort embeddings from lowest to highest.
+ asort( $terms );
+
+ // Return the terms.
+ $result[ $index ] = new \stdClass();
+
+ $result[ $index ]->{$tax_name} = [];
+
+ $term_added = 0;
+ foreach ( $terms as $term_id => $similarity ) {
+ // Stop if we have added the number of terms specified in settings.
+ if ( $number_to_add <= $term_added ) {
+ break;
+ }
+
+ // Convert $similarity to percentage.
+ $similarity = round( ( 1 - $similarity ), 10 );
+
+ $result[ $index ]->{$tax_name}[] = [// phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found
+ 'label' => get_term( $term_id )->name,
+ 'score' => $similarity,
+ ];
+ $term_added++;
+ }
+
+ // Only add the number of terms specified in settings.
+ if ( count( $terms ) > $number_to_add ) {
+ $terms = array_slice( $terms, 0, $number_to_add, true );
+ }
+
+ $index++;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get the similarity between an embedding and all terms.
+ *
+ * @since 2.5.0
+ *
+ * @param array $embedding Embedding data.
+ * @param bool $consider_threshold Whether to consider the threshold setting.
+ *
+ * @return array
+ */
+ private function get_embeddings_similarity( $embedding, $consider_threshold = true ) {
$embedding_similarity = [];
$taxonomies = $this->supported_taxonomies();
$calculations = new EmbeddingCalculations();
@@ -544,30 +672,15 @@ private function set_terms( int $post_id = 0, array $embedding = [] ) {
$term_embedding = get_term_meta( $term_id, 'classifai_openai_embeddings', true );
if ( $term_embedding ) {
- $similarity = $calculations->similarity( $embedding, $term_embedding, $threshold );
- if ( false !== $similarity ) {
+ $similarity = $calculations->similarity( $embedding, $term_embedding );
+ if ( false !== $similarity && ( ! $consider_threshold || $similarity <= $threshold ) ) {
$embedding_similarity[ $tax ][ $term_id ] = $similarity;
}
}
}
}
- if ( empty( $embedding_similarity ) ) {
- return;
- }
-
- // Set terms based on similarity.
- foreach ( $embedding_similarity as $tax => $terms ) {
- // Sort embeddings from lowest to highest.
- asort( $terms );
-
- // Only add the number of terms specified in settings.
- if ( count( $terms ) > $number_to_add ) {
- $terms = array_slice( $terms, 0, $number_to_add, true );
- }
-
- wp_set_object_terms( $post_id, array_map( 'absint', array_keys( $terms ) ), $tax, false );
- }
+ return $embedding_similarity;
}
/**
diff --git a/includes/Classifai/Services/Service.php b/includes/Classifai/Services/Service.php
index b5d9cd331..bc8f1a4ad 100644
--- a/includes/Classifai/Services/Service.php
+++ b/includes/Classifai/Services/Service.php
@@ -151,8 +151,17 @@ public function render_settings_page() {
provider_classes ?? [], 'Natural Language Understanding' );
-
- if ( ! is_wp_error( $provider ) && ! empty( $provider->can_register() ) && $provider->is_feature_enabled( 'content_classification' ) ) :
+ if ( 'openai_embeddings' === $active_tab ) {
+ $provider = find_provider_class( $this->provider_classes ?? [], 'Embeddings' );
+ }
+
+ if (
+ ! is_wp_error( $provider )
+ && ! empty(
+ $provider->can_register()
+ && ( $provider->is_feature_enabled( 'content_classification' ) || $provider->is_feature_enabled( 'classification' ) )
+ )
+ ) :
?>
-
+
-
+
+ $feature ) :
+ ?>
+
+
+
diff --git a/src/js/language-processing.js b/src/js/language-processing.js
index 3ea8dd16a..43d7e568d 100644
--- a/src/js/language-processing.js
+++ b/src/js/language-processing.js
@@ -2,193 +2,306 @@ import Choices from 'choices.js';
import '../scss/language-processing.scss';
( () => {
- const nonceElement = document.getElementById( 'classifai-previewer-nonce' );
- if ( ! nonceElement ) {
+ let featureStatuses = {};
+
+ const nonceElementNLU = document.getElementById(
+ 'classifai-previewer-watson_nlu-nonce'
+ );
+
+ const nonceElementEmbeddings = document.getElementById(
+ 'classifai-previewer-openai_embeddings-nonce'
+ );
+
+ if ( ! nonceElementNLU && ! nonceElementEmbeddings ) {
return;
}
- /** Previewer nonce. */
- const previewerNonce = nonceElement.value;
-
- /** Feature statuses. */
- const featureStatuses = {
- categoriesStatus: document.getElementById(
- 'classifai-settings-category'
- ).checked,
- keywordsStatus: document.getElementById( 'classifai-settings-keyword' )
- .checked,
- entitiesStatus: document.getElementById( 'classifai-settings-entity' )
- .checked,
- conceptsStatus: document.getElementById( 'classifai-settings-concept' )
- .checked,
- };
+ const previewWatson = () => {
+ if ( ! nonceElementNLU ) {
+ return;
+ }
- const plurals = {
- category: 'categories',
- keyword: 'keywords',
- entity: 'entities',
- concept: 'concepts',
- };
+ const getClassifierDataBtn = document.getElementById(
+ 'get-classifier-preview-data-btn'
+ );
+ getClassifierDataBtn.addEventListener( 'click', showPreviewWatson );
+
+ /** Previewer nonce. */
+ const previewerNonce = nonceElementNLU.value;
+
+ /** Feature statuses. */
+ featureStatuses = {
+ categoriesStatus: document.getElementById(
+ 'classifai-settings-category'
+ ).checked,
+ keywordsStatus: document.getElementById(
+ 'classifai-settings-keyword'
+ ).checked,
+ entitiesStatus: document.getElementById(
+ 'classifai-settings-entity'
+ ).checked,
+ conceptsStatus: document.getElementById(
+ 'classifai-settings-concept'
+ ).checked,
+ };
- document
- .querySelectorAll(
- '#classifai-settings-category, #classifai-settings-keyword, #classifai-settings-entity, #classifai-settings-concept'
- )
- .forEach( ( item ) => {
- item.addEventListener( 'change', ( e ) => {
- if ( 'classifai-settings-category' === e.target.id ) {
- featureStatuses.categoriesStatus = e.target.checked;
- }
+ const plurals = {
+ category: 'categories',
+ keyword: 'keywords',
+ entity: 'entities',
+ concept: 'concepts',
+ };
- if ( 'classifai-settings-keyword' === e.target.id ) {
- featureStatuses.keywordsStatus = e.target.checked;
- }
+ document
+ .querySelectorAll(
+ '#classifai-settings-category, #classifai-settings-keyword, #classifai-settings-entity, #classifai-settings-concept'
+ )
+ .forEach( ( item ) => {
+ item.addEventListener( 'change', ( e ) => {
+ if ( 'classifai-settings-category' === e.target.id ) {
+ featureStatuses.categoriesStatus = e.target.checked;
+ }
- if ( 'classifai-settings-entity' === e.target.id ) {
- featureStatuses.entitiesStatus = e.target.checked;
- }
+ if ( 'classifai-settings-keyword' === e.target.id ) {
+ featureStatuses.keywordsStatus = e.target.checked;
+ }
- if ( 'classifai-settings-concept' === e.target.id ) {
- featureStatuses.conceptsStatus = e.target.checked;
- }
+ if ( 'classifai-settings-entity' === e.target.id ) {
+ featureStatuses.entitiesStatus = e.target.checked;
+ }
- const taxType = e.target.id.split( '-' ).at( -1 );
+ if ( 'classifai-settings-concept' === e.target.id ) {
+ featureStatuses.conceptsStatus = e.target.checked;
+ }
- if ( e.target.checked ) {
- document
- .querySelector( `.tax-row--${ plurals[ taxType ] }` )
- .classList.remove( 'tax-row--hide' );
- } else {
- document
- .querySelector( `.tax-row--${ plurals[ taxType ] }` )
- .classList.add( 'tax-row--hide' );
- }
+ const taxType = e.target.id.split( '-' ).at( -1 );
+
+ if ( e.target.checked ) {
+ document
+ .querySelector(
+ `.tax-row--${ plurals[ taxType ] }`
+ )
+ .classList.remove( 'tax-row--hide' );
+ } else {
+ document
+ .querySelector(
+ `.tax-row--${ plurals[ taxType ] }`
+ )
+ .classList.add( 'tax-row--hide' );
+ }
+ } );
} );
- } );
- /**
- * Live preview features.
- *
- * @param {Object} e The event object.
- */
- function showPreview( e ) {
- /** Category thresholds. */
- const categoryThreshold = Number(
- document.querySelector( '#classifai-settings-category_threshold' )
- .value
- );
- const keywordThreshold = Number(
- document.querySelector( '#classifai-settings-keyword_threshold' )
- .value
- );
- const entityThreshold = Number(
- document.querySelector( '#classifai-settings-entity_threshold' )
- .value
- );
- const conceptThreshold = Number(
- document.querySelector( '#classifai-settings-concept_threshold' )
- .value
- );
+ /**
+ * Live preview features.
+ *
+ * @param {Object} e The event object.
+ */
+ function showPreviewWatson( e ) {
+ /** Category thresholds. */
+ const categoryThreshold = Number(
+ document.querySelector(
+ '#classifai-settings-category_threshold'
+ ).value
+ );
+ const keywordThreshold = Number(
+ document.querySelector(
+ '#classifai-settings-keyword_threshold'
+ ).value
+ );
+ const entityThreshold = Number(
+ document.querySelector( '#classifai-settings-entity_threshold' )
+ .value
+ );
+ const conceptThreshold = Number(
+ document.querySelector(
+ '#classifai-settings-concept_threshold'
+ ).value
+ );
- const postId = document.getElementById(
- 'classifai-preview-post-selector'
- ).value;
+ const postId = document.getElementById(
+ 'classifai-preview-post-selector'
+ ).value;
- const previewWrapper = document.getElementById(
- 'classifai-post-preview-wrapper'
- );
- const thresholds = {
- categories: categoryThreshold,
- keywords: keywordThreshold,
- entities: entityThreshold,
- concepts: conceptThreshold,
- };
+ const previewWrapper = document.getElementById(
+ 'classifai-post-preview-wrapper'
+ );
+ const thresholds = {
+ categories: categoryThreshold,
+ keywords: keywordThreshold,
+ entities: entityThreshold,
+ concepts: conceptThreshold,
+ };
- e.target
- .closest( '.button' )
- .classList.add( 'get-classifier-preview-data-btn--loading' );
+ e.target
+ .closest( '.button' )
+ .classList.add( 'get-classifier-preview-data-btn--loading' );
- const formData = new FormData();
- formData.append( 'action', 'get_post_classifier_preview_data' );
- formData.append( 'post_id', postId );
- formData.append( 'nonce', previewerNonce );
+ const formData = new FormData();
+ formData.append( 'action', 'get_post_classifier_preview_data' );
+ formData.append( 'post_id', postId );
+ formData.append( 'nonce', previewerNonce );
- fetch( `${ ajaxurl }`, {
- method: 'POST',
- body: formData,
- } )
- .then( ( response ) => {
- return response.json();
+ fetch( `${ ajaxurl }`, {
+ method: 'POST',
+ body: formData,
} )
- .then( ( data ) => {
- if ( ! data.success ) {
+ .then( ( response ) => {
+ return response.json();
+ } )
+ .then( ( data ) => {
+ if ( ! data.success ) {
+ previewWrapper.style.display = 'block';
+ previewWrapper.innerHTML = data.data;
+ e.target
+ .closest( '.button' )
+ .classList.remove(
+ 'get-classifier-preview-data-btn--loading'
+ );
+ return;
+ }
+
+ const {
+ data: {
+ categories = [],
+ concepts = [],
+ entities = [],
+ keywords = [],
+ },
+ } = data;
+
+ const dataToFilter = {
+ categories,
+ keywords,
+ entities,
+ concepts,
+ };
+
+ const filteredItems = filterByScoreOrRelevance(
+ dataToFilter,
+ thresholds
+ );
+ const htmlData = buildPreviewUI( filteredItems );
previewWrapper.style.display = 'block';
- previewWrapper.innerHTML = data.data;
+ previewWrapper.innerHTML = htmlData;
+
e.target
.closest( '.button' )
.classList.remove(
'get-classifier-preview-data-btn--loading'
);
- return;
- }
-
- const {
- data: {
- categories = [],
- concepts = [],
- entities = [],
- keywords = [],
- },
- } = data;
-
- const dataToFilter = {
- categories,
- keywords,
- entities,
- concepts,
- };
-
- const filteredItems = filterByScoreOrRelevance(
- dataToFilter,
- thresholds
- );
- const htmlData = buildPreviewUI( filteredItems );
- previewWrapper.style.display = 'block';
- previewWrapper.innerHTML = htmlData;
-
- e.target
- .closest( '.button' )
- .classList.remove(
- 'get-classifier-preview-data-btn--loading'
- );
- } );
- }
+ } );
+ }
+
+ /**
+ * Filters response data depending on the threshold value.
+ *
+ * @param {Object} data Response data from NLU.
+ * @param {Object} thresholds Object containing threshold values for various taxnomy types.
+ * @return {Array} Sorted data.
+ */
+ function filterByScoreOrRelevance( data = {}, thresholds ) {
+ const filteredItems = Object.keys( data ).map( ( key ) => ( {
+ [ key ]: data[ key ].filter( ( item ) => {
+ if ( item?.score && item.score * 100 > thresholds[ key ] ) {
+ return item;
+ } else if (
+ item?.relevance &&
+ item.relevance * 100 > thresholds[ key ]
+ ) {
+ return item;
+ }
- /**
- * Filters response data depending on the threshold value.
- *
- * @param {Object} data Response data from NLU.
- * @param {Object} thresholds Object containing threshold values for various taxnomy types.
- * @return {Array} Sorted data.
- */
- function filterByScoreOrRelevance( data = {}, thresholds ) {
- const filteredItems = Object.keys( data ).map( ( key ) => ( {
- [ key ]: data[ key ].filter( ( item ) => {
- if ( item?.score && item.score * 100 > thresholds[ key ] ) {
- return item;
- } else if (
- item?.relevance &&
- item.relevance * 100 > thresholds[ key ]
- ) {
return item;
- }
+ } ),
+ } ) );
- return item;
- } ),
- } ) );
+ return filteredItems;
+ }
+ };
+ previewWatson();
- return filteredItems;
- }
+ const previewEmbeddings = () => {
+ if ( ! nonceElementEmbeddings ) {
+ return;
+ }
+
+ const getClassifierDataBtn = document.getElementById(
+ 'get-classifier-preview-data-btn'
+ );
+ getClassifierDataBtn.addEventListener( 'click', showPreviewEmeddings );
+
+ /** Previewer nonce. */
+ const previewerNonce = nonceElementEmbeddings.value;
+
+ /**
+ * Live preview features.
+ *
+ * @param {Object} e The event object.
+ */
+ function showPreviewEmeddings( e ) {
+ const postId = document.getElementById(
+ 'classifai-preview-post-selector'
+ ).value;
+
+ const previewWrapper = document.getElementById(
+ 'classifai-post-preview-wrapper'
+ );
+
+ // clear previewWrapper.
+ previewWrapper.innerHTML = '';
+
+ e.target
+ .closest( '.button' )
+ .classList.add( 'get-classifier-preview-data-btn--loading' );
+
+ const formData = new FormData();
+ formData.append(
+ 'action',
+ 'get_post_classifier_embeddings_preview_data'
+ );
+ formData.append( 'post_id', postId );
+ formData.append( 'nonce', previewerNonce );
+
+ fetch( `${ ajaxurl }`, {
+ method: 'POST',
+ body: formData,
+ } )
+ .then( ( response ) => {
+ return response.json();
+ } )
+ .then( ( data ) => {
+ if ( ! data.success ) {
+ previewWrapper.style.display = 'block';
+ previewWrapper.innerHTML = data.data;
+ e.target
+ .closest( '.button' )
+ .classList.remove(
+ 'get-classifier-preview-data-btn--loading'
+ );
+ return;
+ }
+
+ const htmlData = buildPreviewUI( data.data );
+ previewWrapper.style.display = 'block';
+ previewWrapper.innerHTML = htmlData;
+
+ // remove all .tax-row--hide
+ document
+ .querySelectorAll( '.tax-row--hide' )
+ .forEach( ( item ) => {
+ item.classList.remove( 'tax-row--hide' );
+ } );
+
+ e.target
+ .closest( '.button' )
+ .classList.remove(
+ 'get-classifier-preview-data-btn--loading'
+ );
+ } );
+ }
+ };
+ previewEmbeddings();
/**
* Builds user readable HTML data from the response by NLU.
@@ -198,6 +311,11 @@ import '../scss/language-processing.scss';
*/
function buildPreviewUI( filteredItems = [] ) {
let htmlData = '';
+ // check if filteredItems.forEach type is function
+ if ( ! filteredItems.forEach ) {
+ return '';
+ }
+
filteredItems.forEach( ( obj ) => {
Object.keys( obj ).forEach( ( prop ) => {
htmlData += `