From 138b0b7e271ec417d13559cbb3f4398023021af4 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Thu, 11 Jan 2024 12:13:45 +0100 Subject: [PATCH 01/11] Refactor classes to use newer syntax with `class` keyword --- packages/js/src/insights/initializer.js | 2 +- .../values/ProminentWord.js | 163 ++--- .../yoastseo/src/scoring/taxonomyAssessor.js | 61 +- .../yoastseo/src/values/AssessmentResult.js | 529 ++++++++-------- packages/yoastseo/src/values/Mark.js | 463 +++++++------- packages/yoastseo/src/values/Paper.js | 595 +++++++++--------- .../yoastseo/src/worker/AnalysisWebWorker.js | 2 +- .../yoastseo/src/worker/transporter/parse.js | 4 +- 8 files changed, 917 insertions(+), 902 deletions(-) diff --git a/packages/js/src/insights/initializer.js b/packages/js/src/insights/initializer.js index 9c8b21169aa..d07463b6e7d 100644 --- a/packages/js/src/insights/initializer.js +++ b/packages/js/src/insights/initializer.js @@ -23,7 +23,7 @@ const createUpdater = () => { * @returns {void} */ return () => { - const paper = Paper.parse( collectData() ); + const paper = Paper.prototype.parse( collectData() ); runResearch( "readingTime", paper ).then( response => setEstimatedReadingTime( response.result ) ); runResearch( "getFleschReadingScore", paper ).then( response => { diff --git a/packages/yoastseo/src/languageProcessing/values/ProminentWord.js b/packages/yoastseo/src/languageProcessing/values/ProminentWord.js index 49a994eb188..d190083b187 100644 --- a/packages/yoastseo/src/languageProcessing/values/ProminentWord.js +++ b/packages/yoastseo/src/languageProcessing/values/ProminentWord.js @@ -1,90 +1,95 @@ /** * Represents a prominent word in the context of relevant words. - * - * @constructor - * - * @param {string} word The word. - * @param {string} [stem] The stem / base form of the word, defaults to the word. - * @param {number} [occurrences] The number of occurrences, defaults to 0. - */ -function ProminentWord( word, stem, occurrences ) { - this._word = word; - this._stem = stem ? stem : word; - this._occurrences = occurrences || 0; -} +*/ +class ProminentWord { + /** + * Constructs Prominent word object. + * + * @constructor + * + * @param {string} word The word. + * @param {string} [stem] The stem / base form of the word, defaults to the word. + * @param {number} [occurrences] The number of occurrences, defaults to 0. + */ + constructor( word, stem, occurrences ) { + this._word = word; + this._stem = stem ? stem : word; + this._occurrences = occurrences || 0; + } -/** - * Sets the word. - * - * @param {string} word The word to set. - * - * @returns {void}. - */ -ProminentWord.prototype.setWord = function( word ) { - this._word = word; -}; + /** + * Sets the word. + * + * @param {string} word The word to set. + * + * @returns {void}. + */ + setWord( word ) { + this._word = word; + } -/** - * Returns the word. - * - * @returns {string} The word. - */ -ProminentWord.prototype.getWord = function() { - return this._word; -}; + /** + * Returns the word. + * + * @returns {string} The word. + */ + getWord() { + return this._word; + } -/** - * Returns the stem of the word. - * - * @returns {string} The stem. - */ -ProminentWord.prototype.getStem = function() { - return this._stem; -}; + /** + * Returns the stem of the word. + * + * @returns {string} The stem. + */ + getStem() { + return this._stem; + } -/** - * Sets the number of occurrences to the word. - * - * @param {int} numberOfOccurrences The number of occurrences to set. - * - * @returns {void}. - */ -ProminentWord.prototype.setOccurrences = function( numberOfOccurrences ) { - this._occurrences = numberOfOccurrences; -}; + /** + * Sets the number of occurrences to the word. + * + * @param {int} numberOfOccurrences The number of occurrences to set. + * + * @returns {void}. + */ + setOccurrences( numberOfOccurrences ) { + this._occurrences = numberOfOccurrences; + } -/** - * Returns the amount of occurrences of this word. - * - * @returns {number} The number of occurrences. - */ -ProminentWord.prototype.getOccurrences = function() { - return this._occurrences; -}; + /** + * Returns the amount of occurrences of this word. + * + * @returns {number} The number of occurrences. + */ + getOccurrences() { + return this._occurrences; + } -/** - * Serializes the ProminentWord instance to an object. - * - * @returns {Object} The serialized ProminentWord. - */ -ProminentWord.prototype.serialize = function() { - return { - _parseClass: "ProminentWord", - word: this._word, - stem: this._stem, - occurrences: this._occurrences, - }; -}; + /** + * Serializes the ProminentWord instance to an object. + * + * @returns {Object} The serialized ProminentWord. + */ + serialize() { + return { + _parseClass: "ProminentWord", + word: this._word, + stem: this._stem, + occurrences: this._occurrences, + }; + } -/** - * Parses the object to a ProminentWord. - * - * @param {Object} serialized The serialized object. - * - * @returns {ProminentWord} The parsed ProminentWord. - */ -ProminentWord.parse = function( serialized ) { - return new ProminentWord( serialized.word, serialized.stem, serialized.occurrences ); -}; + /** + * Parses the object to a ProminentWord. + * + * @param {Object} serialized The serialized object. + * + * @returns {ProminentWord} The parsed ProminentWord. + */ + parse( serialized ) { + return new ProminentWord( serialized.word, serialized.stem, serialized.occurrences ); + } +} export default ProminentWord; diff --git a/packages/yoastseo/src/scoring/taxonomyAssessor.js b/packages/yoastseo/src/scoring/taxonomyAssessor.js index 8801a98728e..d682e36a487 100644 --- a/packages/yoastseo/src/scoring/taxonomyAssessor.js +++ b/packages/yoastseo/src/scoring/taxonomyAssessor.js @@ -1,5 +1,3 @@ -import { inherits } from "util"; - import IntroductionKeywordAssessment from "./assessments/seo/IntroductionKeywordAssessment"; import KeyphraseLengthAssessment from "./assessments/seo/KeyphraseLengthAssessment"; import KeyphraseDensityAssessment from "./assessments/seo/KeywordDensityAssessment"; @@ -33,36 +31,39 @@ export const getTextLengthAssessment = function() { /** * Creates the Assessor used for taxonomy pages. - * - * @param {Researcher} researcher The researcher used for the analysis. - * @param {Object?} options The options for this assessor. - * @constructor */ -const TaxonomyAssessor = function( researcher, options ) { - Assessor.call( this, researcher, options ); - this.type = "taxonomyAssessor"; +class TaxonomyAssessor extends Assessor { + /** + * Creates a new taxonomy assessor. + * + * @param {Researcher} researcher The researcher to use. + * @param {Object} options The assessor options. + */ + constructor( researcher, options ) { + super( researcher, options ); - this._assessments = [ - new IntroductionKeywordAssessment(), - new KeyphraseLengthAssessment(), - new KeyphraseDensityAssessment(), - new MetaDescriptionKeywordAssessment(), - new MetaDescriptionLengthAssessment(), - getTextLengthAssessment(), - new KeyphraseInSEOTitleAssessment(), - new PageTitleWidthAssessment( - { - scores: { - widthTooShort: 9, - }, - }, true - ), - new SlugKeywordAssessment(), - new FunctionWordsInKeyphrase(), - new SingleH1Assessment(), - ]; -}; + this.type = "taxonomyAssessor"; -inherits( TaxonomyAssessor, Assessor ); + this._assessments = [ + new IntroductionKeywordAssessment(), + new KeyphraseLengthAssessment(), + new KeyphraseDensityAssessment(), + new MetaDescriptionKeywordAssessment(), + new MetaDescriptionLengthAssessment(), + getTextLengthAssessment(), + new KeyphraseInSEOTitleAssessment(), + new PageTitleWidthAssessment( + { + scores: { + widthTooShort: 9, + }, + }, true + ), + new SlugKeywordAssessment(), + new FunctionWordsInKeyphrase(), + new SingleH1Assessment(), + ]; + } +} export default TaxonomyAssessor; diff --git a/packages/yoastseo/src/values/AssessmentResult.js b/packages/yoastseo/src/values/AssessmentResult.js index 6b6533993e9..489ea640a8d 100644 --- a/packages/yoastseo/src/values/AssessmentResult.js +++ b/packages/yoastseo/src/values/AssessmentResult.js @@ -7,308 +7,311 @@ import Mark from "./Mark"; * * @returns {Array} A list of empty marks. */ -var emptyMarker = function() { +const emptyMarker = function() { return []; }; /** - * Construct the AssessmentResult value object. - * - * @param {Object} [values] The values for this assessment result. - * - * @constructor + * Represents the assessment result. */ -var AssessmentResult = function( values ) { - this._hasScore = false; - this._identifier = ""; - this._hasMarks = false; - this._hasJumps = false; - this._hasEditFieldName = false; - this._marker = emptyMarker; - this._hasBetaBadge = false; - this.score = 0; - this.text = ""; - this.marks = []; - this.editFieldName = ""; - - if ( isUndefined( values ) ) { - values = {}; +class AssessmentResult { + /** + * Constructs the AssessmentResult value object. + * + * @param {Object} [values] The values for this assessment result. + */ + constructor( values ) { + this._hasScore = false; + this._identifier = ""; + this._hasMarks = false; + this._hasJumps = false; + this._hasEditFieldName = false; + this._marker = emptyMarker; + this._hasBetaBadge = false; + this.score = 0; + this.text = ""; + this.marks = []; + this.editFieldName = ""; + + if ( isUndefined( values ) ) { + values = {}; + } + + if ( ! isUndefined( values.score ) ) { + this.setScore( values.score ); + } + + if ( ! isUndefined( values.text ) ) { + this.setText( values.text ); + } + + if ( ! isUndefined( values.marks ) ) { + this.setMarks( values.marks ); + } + + if ( ! isUndefined( values._hasBetaBadge ) ) { + this.setHasBetaBadge( values._hasBetaBadge ); + } + + if ( ! isUndefined( values._hasJumps ) ) { + this.setHasJumps( values._hasJumps ); + } + + if ( ! isUndefined( values.editFieldName ) ) { + this.setEditFieldName( values.editFieldName ); + } } - if ( ! isUndefined( values.score ) ) { - this.setScore( values.score ); + /** + * Checks if a score is available. + * @returns {boolean} Whether or not a score is available. + */ + hasScore() { + return this._hasScore; } - if ( ! isUndefined( values.text ) ) { - this.setText( values.text ); + /** + * Gets the available score + * @returns {number} The score associated with the AssessmentResult. + */ + getScore() { + return this.score; } - if ( ! isUndefined( values.marks ) ) { - this.setMarks( values.marks ); + /** + * Sets the score for the assessment. + * @param {number} score The score to be used for the score property + * @returns {void} + */ + setScore( score ) { + if ( isNumber( score ) ) { + this.score = score; + this._hasScore = true; + } } - if ( ! isUndefined( values._hasBetaBadge ) ) { - this.setHasBetaBadge( values._hasBetaBadge ); + /** + * Checks if a text is available. + * @returns {boolean} Whether or not a text is available. + */ + hasText() { + return this.text !== ""; } - if ( ! isUndefined( values._hasJumps ) ) { - this.setHasJumps( values._hasJumps ); + /** + * Gets the available text + * @returns {string} The text associated with the AssessmentResult. + */ + getText() { + return this.text; } - if ( ! isUndefined( values.editFieldName ) ) { - this.setEditFieldName( values.editFieldName ); + /** + * Sets the text for the assessment. + * @param {string} text The text to be used for the text property + * @returns {void} + */ + setText( text ) { + if ( isUndefined( text ) ) { + text = ""; + } + + this.text = text; } -}; - -/** - * Check if a score is available. - * @returns {boolean} Whether or not a score is available. - */ -AssessmentResult.prototype.hasScore = function() { - return this._hasScore; -}; -/** - * Get the available score - * @returns {number} The score associated with the AssessmentResult. - */ -AssessmentResult.prototype.getScore = function() { - return this.score; -}; - -/** - * Set the score for the assessment. - * @param {number} score The score to be used for the score property - * @returns {void} - */ -AssessmentResult.prototype.setScore = function( score ) { - if ( isNumber( score ) ) { - this.score = score; - this._hasScore = true; + /** + * Gets the available marks. + * + * @returns {array} The marks associated with the AssessmentResult. + */ + getMarks() { + return this.marks; } -}; -/** - * Check if a text is available. - * @returns {boolean} Whether or not a text is available. - */ -AssessmentResult.prototype.hasText = function() { - return this.text !== ""; -}; - -/** - * Get the available text - * @returns {string} The text associated with the AssessmentResult. - */ -AssessmentResult.prototype.getText = function() { - return this.text; -}; - -/** - * Set the text for the assessment. - * @param {string} text The text to be used for the text property - * @returns {void} - */ -AssessmentResult.prototype.setText = function( text ) { - if ( isUndefined( text ) ) { - text = ""; + /** + * Sets the marks for the assessment. + * + * @param {array} marks The marks to be used for the marks property + * + * @returns {void} + */ + setMarks( marks ) { + if ( isArray( marks ) ) { + this.marks = marks; + this._hasMarks = marks.length > 0; + } } - this.text = text; -}; - -/** - * Gets the available marks. - * - * @returns {array} The marks associated with the AssessmentResult. - */ -AssessmentResult.prototype.getMarks = function() { - return this.marks; -}; - -/** - * Sets the marks for the assessment. - * - * @param {array} marks The marks to be used for the marks property - * - * @returns {void} - */ -AssessmentResult.prototype.setMarks = function( marks ) { - if ( isArray( marks ) ) { - this.marks = marks; - this._hasMarks = marks.length > 0; + /** + * Sets the identifier + * + * @param {string} identifier An alphanumeric identifier for this result. + * @returns {void} + */ + setIdentifier( identifier ) { + this._identifier = identifier; } -}; -/** - * Sets the identifier - * - * @param {string} identifier An alphanumeric identifier for this result. - * @returns {void} - */ -AssessmentResult.prototype.setIdentifier = function( identifier ) { - this._identifier = identifier; -}; - -/** - * Gets the identifier - * - * @returns {string} An alphanumeric identifier for this result. - */ -AssessmentResult.prototype.getIdentifier = function() { - return this._identifier; -}; + /** + * Gets the identifier + * + * @returns {string} An alphanumeric identifier for this result. + */ + getIdentifier() { + return this._identifier; + } -/** - * Sets the marker, a pure function that can return the marks for a given Paper - * - * @param {Function} marker The marker to set. - * @returns {void} - */ -AssessmentResult.prototype.setMarker = function( marker ) { - this._marker = marker; -}; + /** + * Sets the marker, a pure function that can return the marks for a given Paper + * + * @param {Function} marker The marker to set. + * @returns {void} + */ + setMarker( marker ) { + this._marker = marker; + } -/** - * Returns whether or not this result has a marker that can be used to mark for a given Paper - * - * @returns {boolean} Whether or this result has a marker. - */ -AssessmentResult.prototype.hasMarker = function() { - return this._hasMarks && this._marker !== this.emptyMarker; -}; + /** + * Returns whether or not this result has a marker that can be used to mark for a given Paper + * + * @returns {boolean} Whether or this result has a marker. + */ + hasMarker() { + return this._hasMarks && this._marker !== this.emptyMarker; + } -/** - * Gets the marker, a pure function that can return the marks for a given Paper - * - * @returns {Function} The marker. - */ -AssessmentResult.prototype.getMarker = function() { - return this._marker; -}; + /** + * Gets the marker, a pure function that can return the marks for a given Paper + * + * @returns {Function} The marker. + */ + getMarker() { + return this._marker; + } -/** - * Sets the value of _hasMarks to determine if there is something to mark. - * - * @param {boolean} hasMarks Is there something to mark. - * @returns {void} - */ -AssessmentResult.prototype.setHasMarks = function( hasMarks ) { - this._hasMarks = hasMarks; -}; + /** + * Sets the value of _hasMarks to determine if there is something to mark. + * + * @param {boolean} hasMarks Is there something to mark. + * @returns {void} + */ + setHasMarks( hasMarks ) { + this._hasMarks = hasMarks; + } -/** - * Returns the value of _hasMarks to determine if there is something to mark. - * - * @returns {boolean} Is there something to mark. - */ -AssessmentResult.prototype.hasMarks = function() { - return this._hasMarks; -}; + /** + * Returns the value of _hasMarks to determine if there is something to mark. + * + * @returns {boolean} Is there something to mark. + */ + hasMarks() { + return this._hasMarks; + } -/** - * Sets the value of _hasBetaBadge to determine if the result has a beta badge. - * - * @param {boolean} hasBetaBadge Whether this result has a beta badge. - * @returns {void} - */ -AssessmentResult.prototype.setHasBetaBadge = function( hasBetaBadge ) { - this._hasBetaBadge = hasBetaBadge; -}; + /** + * Sets the value of _hasBetaBadge to determine if the result has a beta badge. + * + * @param {boolean} hasBetaBadge Whether this result has a beta badge. + * @returns {void} + */ + setHasBetaBadge( hasBetaBadge ) { + this._hasBetaBadge = hasBetaBadge; + } -/** - * Returns the value of _hasBetaBadge to determine if the result has a beta badge. - * - * @returns {bool} Whether this result has a beta badge. - */ -AssessmentResult.prototype.hasBetaBadge = function() { - return this._hasBetaBadge; -}; + /** + * Returns the value of _hasBetaBadge to determine if the result has a beta badge. + * + * @returns {bool} Whether this result has a beta badge. + */ + hasBetaBadge() { + return this._hasBetaBadge; + } -/** - * Sets the value of _hasJumps to determine whether it's needed to jump to a different field. - * - * @param {boolean} hasJumps Whether this result causes a jump to a different field. - * @returns {void} - */ -AssessmentResult.prototype.setHasJumps = function( hasJumps ) { - this._hasJumps = hasJumps; -}; + /** + * Sets the value of _hasJumps to determine whether it's needed to jump to a different field. + * + * @param {boolean} hasJumps Whether this result causes a jump to a different field. + * @returns {void} + */ + setHasJumps( hasJumps ) { + this._hasJumps = hasJumps; + } -/** - * Returns the value of _hasJumps to determine whether it's needed to jump to a different field. - * - * @returns {bool} Whether this result causes a jump to a different field. - */ -AssessmentResult.prototype.hasJumps = function() { - return this._hasJumps; -}; + /** + * Returns the value of _hasJumps to determine whether it's needed to jump to a different field. + * + * @returns {bool} Whether this result causes a jump to a different field. + */ + hasJumps() { + return this._hasJumps; + } -/** - * Check if an edit field name is available. - * @returns {boolean} Whether or not an edit field name is available. - */ -AssessmentResult.prototype.hasEditFieldName = function() { - return this._hasEditFieldName; -}; + /** + * Check if an edit field name is available. + * @returns {boolean} Whether or not an edit field name is available. + */ + hasEditFieldName() { + return this._hasEditFieldName; + } -/** - * Get the edit field name. - * @returns {string} The edit field name associated with the AssessmentResult. - */ -AssessmentResult.prototype.getEditFieldName = function() { - return this.editFieldName; -}; + /** + * Gets the edit field name. + * @returns {string} The edit field name associated with the AssessmentResult. + */ + getEditFieldName() { + return this.editFieldName; + } -/** - * Set the edit field name to be used to create the aria label for an edit button. - * @param {string} editFieldName The string to be used for the string property - * @returns {void} - */ -AssessmentResult.prototype.setEditFieldName = function( editFieldName ) { - if ( editFieldName !== "" ) { - this.editFieldName = editFieldName; - this._hasEditFieldName = true; + /** + * Sets the edit field name to be used to create the aria label for an edit button. + * @param {string} editFieldName The string to be used for the string property + * @returns {void} + */ + setEditFieldName( editFieldName ) { + if ( editFieldName !== "" ) { + this.editFieldName = editFieldName; + this._hasEditFieldName = true; + } } -}; -/** - * Serializes the AssessmentResult instance to an object. - * - * @returns {Object} The serialized AssessmentResult. - */ -AssessmentResult.prototype.serialize = function() { - return { - _parseClass: "AssessmentResult", - identifier: this._identifier, - score: this.score, - text: this.text, - marks: this.marks.map( mark => mark.serialize() ), - _hasBetaBadge: this._hasBetaBadge, - _hasJumps: this._hasJumps, - editFieldName: this.editFieldName, - }; -}; + /** + * Serializes the AssessmentResult instance to an object. + * + * @returns {Object} The serialized AssessmentResult. + */ + serialize() { + return { + _parseClass: "AssessmentResult", + identifier: this._identifier, + score: this.score, + text: this.text, + marks: this.marks.map( mark => mark.serialize() ), + _hasBetaBadge: this._hasBetaBadge, + _hasJumps: this._hasJumps, + editFieldName: this.editFieldName, + }; + } -/** - * Parses the object to an AssessmentResult. - * - * @param {Object} serialized The serialized object. - * - * @returns {AssessmentResult} The parsed AssessmentResult. - */ -AssessmentResult.parse = function( serialized ) { - const result = new AssessmentResult( { - text: serialized.text, - score: serialized.score, - marks: serialized.marks.map( mark => Mark.parse( mark ) ), - _hasBetaBadge: serialized._hasBetaBadge, - _hasJumps: serialized._hasJumps, - editFieldName: serialized.editFieldName, - } ); - result.setIdentifier( serialized.identifier ); - - return result; -}; + /** + * Parses the object to an AssessmentResult. + * + * @param {Object} serialized The serialized object. + * + * @returns {AssessmentResult} The parsed AssessmentResult. + */ + parse( serialized ) { + const result = new AssessmentResult( { + text: serialized.text, + score: serialized.score, + marks: serialized.marks.map( mark => Mark.parse( mark ) ), + _hasBetaBadge: serialized._hasBetaBadge, + _hasJumps: serialized._hasJumps, + editFieldName: serialized.editFieldName, + } ); + result.setIdentifier( serialized.identifier ); + + return result; + } +} export default AssessmentResult; diff --git a/packages/yoastseo/src/values/Mark.js b/packages/yoastseo/src/values/Mark.js index bcbbd71f350..7b87570a384 100644 --- a/packages/yoastseo/src/values/Mark.js +++ b/packages/yoastseo/src/values/Mark.js @@ -1,264 +1,269 @@ import { defaults, isUndefined } from "lodash-es"; -/** - * Represents a place where highlighting should be applied. - * We allow both replacement-based highlighting (through providing `original`, `marked`, and potentially `fieldsToMark`) and - * position-based highlighting (through providing a `position`). - * - * @param {Object} properties The properties of this Mark. - * - * @param {string?} properties.original The original text that should be marked. - * @param {string?} properties.marked The new text including marks. - * @param {array?} properties.fieldsToMark The array that specifies which text section(s) to mark. - * - * @param {SourceCodeRange?} properties.position The position object: a range in the source code. - * - * @constructor - */ -function Mark( properties ) { - defaults( properties, { original: "", marked: "", fieldsToMark: [] } ); - this._properties = properties; - this.isValid(); -} +const defaultProperties = { original: "", marked: "", fieldsToMark: [] }; /** - * Returns the original text. - * - * @returns {string} The original text. - */ -Mark.prototype.getOriginal = function() { - return this._properties.original; -}; - -/** - * Returns the marked text. - * - * @returns {string} The replaced text. + * Represents a place where highlighting should be applied. */ -Mark.prototype.getMarked = function() { - return this._properties.marked; -}; +class Mark { + /** + * Represents a place where highlighting should be applied. + * We allow both replacement-based highlighting (through providing `original`, `marked`, and potentially `fieldsToMark`) and + * position-based highlighting (through providing a `position`). + * + * @param {Object} [properties] The properties of this Mark. + * + * @param {string?} properties.original The original text that should be marked. + * @param {string?} properties.marked The new text including marks. + * @param {array?} properties.fieldsToMark The array that specifies which text section(s) to mark. + * + * @param {SourceCodeRange?} properties.position The position object: a range in the source code. + */ + constructor( properties ) { + properties = properties || {}; + defaults( properties, defaultProperties ); + this._properties = properties; + this.isValid(); + } -/** - * Returns the fields to mark. - * - * @returns {array} The fields to mark. - */ -Mark.prototype.getFieldsToMark = function() { - return this._properties.fieldsToMark; -}; + /** + * Returns the original text. + * + * @returns {string} The original text. + */ + getOriginal() { + return this._properties.original; + } -/** - * Returns the position information. - * - * @returns {number} The position information. - */ -Mark.prototype.getPosition = function() { - return this._properties.position; -}; + /** + * Returns the marked text. + * + * @returns {string} The replaced text. + */ + getMarked() { + return this._properties.marked; + } -/** - * Returns the start position. - * - * @returns {number} The start position. - */ -Mark.prototype.getPositionStart = function() { - return this._properties.position && this._properties.position.startOffset; -}; + /** + * Returns the fields to mark. + * + * @returns {array} The fields to mark. + */ + getFieldsToMark() { + return this._properties.fieldsToMark; + } -/** - * Returns the end position. - * - * @returns {number} The end position. - */ -Mark.prototype.getPositionEnd = function() { - return this._properties.position && this._properties.position.endOffset; -}; + /** + * Returns the position information. + * + * @returns {number} The position information. + */ + getPosition() { + return this._properties.position; + } -/** - * Sets the start position. - * - * @param {number} positionStart The new start position. - * - * @returns {void} - */ -Mark.prototype.setPositionStart = function( positionStart ) { - this._properties.position.startOffset = positionStart; -}; + /** + * Returns the start position. + * + * @returns {number} The start position. + */ + getPositionStart() { + return this._properties.position && this._properties.position.startOffset; + } -/** - * Sets the end position. - * - * @param {number} positionEnd The new end position. - * - * @returns {void} - */ -Mark.prototype.setPositionEnd = function( positionEnd ) { - this._properties.position.endOffset = positionEnd; -}; + /** + * Returns the end position. + * + * @returns {number} The end position. + */ + getPositionEnd() { + return this._properties.position && this._properties.position.endOffset; + } -/** - * Returns the start position of a block. - * - * @param {number} startOffsetBlock The block start offset. - * - * @returns {number} The start position of a block. - */ -Mark.prototype.setBlockPositionStart = function( startOffsetBlock ) { - this._properties.position.startOffsetBlock = startOffsetBlock; -}; + /** + * Sets the start position. + * + * @param {number} positionStart The new start position. + * + * @returns {void} + */ + setPositionStart( positionStart ) { + this._properties.position.startOffset = positionStart; + } -/** - * Returns the end position of a block. - * - * @param {number} endOffsetBlock The block end offset. - * - * @returns {number} The end position of a block. - */ -Mark.prototype.setBlockPositionEnd = function( endOffsetBlock ) { - this._properties.position.endOffsetBlock = endOffsetBlock; -}; + /** + * Sets the end position. + * + * @param {number} positionEnd The new end position. + * + * @returns {void} + */ + setPositionEnd( positionEnd ) { + this._properties.position.endOffset = positionEnd; + } -/** - * Gets the block client id. - * - * @returns {string} The block client id. - */ -Mark.prototype.getBlockClientId = function() { - return this._properties.position && this._properties.position.clientId; -}; + /** + * Returns the start position of a block. + * + * @param {number} startOffsetBlock The block start offset. + * + * @returns {number} The start position of a block. + */ + setBlockPositionStart( startOffsetBlock ) { + this._properties.position.startOffsetBlock = startOffsetBlock; + } -/** - * Gets the block attribute id. - * - * @returns {string} The block attribute id. - */ -Mark.prototype.getBlockAttributeId = function() { - return this._properties.position && this._properties.position.attributeId; -}; + /** + * Returns the end position of a block. + * + * @param {number} endOffsetBlock The block end offset. + * + * @returns {number} The end position of a block. + */ + setBlockPositionEnd( endOffsetBlock ) { + this._properties.position.endOffsetBlock = endOffsetBlock; + } + /** + * Gets the block client id. + * + * @returns {string} The block client id. + */ + getBlockClientId() { + return this._properties.position && this._properties.position.clientId; + } -/** - * Checks if the mark object is intended for the first section of a Yoast sub-block. - * This method will be used only for Yoast blocks where each block consists of sub-blocks - * with two sections. - * - * @returns {boolean} Whether the mark object is intended for the first section of a Yoast sub-block. - */ -Mark.prototype.isMarkForFirstBlockSection = function() { - return this._properties.position && this._properties.position.isFirstSection; -}; + /** + * Gets the block attribute id. + * + * @returns {string} The block attribute id. + */ + getBlockAttributeId() { + return this._properties.position && this._properties.position.attributeId; + } -/** - * Returns the start position inside block. - * - * @returns {number} The start position inside block if the mark position information, undefined otherwise. - */ -Mark.prototype.getBlockPositionStart = function() { - return this._properties.position && this._properties.position.startOffsetBlock; -}; -/** - * Returns the end position inside block if the mark has position information, undefined otherwise. - * - * @returns {number} The end position inside block. - */ -Mark.prototype.getBlockPositionEnd = function() { - return this._properties.position && this._properties.position.endOffsetBlock; -}; + /** + * Checks if the mark object is intended for the first section of a Yoast sub-block. + * This method will be used only for Yoast blocks where each block consists of sub-blocks + * with two sections. + * + * @returns {boolean} Whether the mark object is intended for the first section of a Yoast sub-block. + */ + isMarkForFirstBlockSection() { + return this._properties.position && this._properties.position.isFirstSection; + } -/** - * Applies this mark to the given text with replacement-based highlighting. - * - * @param {string} text The original text without the mark applied. - * @returns {string} A new text with the mark applied to it. - */ -Mark.prototype.applyWithReplace = function( text ) { - // (=^ ◡ ^=) Cute method to replace everything in a string without using regex. - return text.split( this._properties.original ).join( this._properties.marked ); -}; + /** + * Returns the start position inside block. + * + * @returns {number} The start position inside block if the mark position information, undefined otherwise. + */ + getBlockPositionStart() { + return this._properties.position && this._properties.position.startOffsetBlock; + } -/** - * Applies this mark to the given text with position-based highlighting. - * - * @param {string} text The original text without the mark applied. - * @returns {string} A new text with the mark applied to it. - */ -Mark.prototype.applyWithPosition = function( text ) { - const markStart = ""; - const markEnd = ""; + /** + * Returns the end position inside block if the mark has position information, undefined otherwise. + * + * @returns {number} The end position inside block. + */ + getBlockPositionEnd() { + return this._properties.position && this._properties.position.endOffsetBlock; + } - const newPositionEnd = this.getPositionEnd() + markStart.length; + /** + * Applies this mark to the given text with replacement-based highlighting. + * + * @param {string} text The original text without the mark applied. + * @returns {string} A new text with the mark applied to it. + */ + applyWithReplace( text ) { + // (=^ ◡ ^=) Cute method to replace everything in a string without using regex. + return text.split( this._properties.original ).join( this._properties.marked ); + } - text = text.substring( 0, this.getPositionStart() ) + markStart + text.substring( this.getPositionStart() ); - text = text.substring( 0, newPositionEnd ) + markEnd + text.substring( newPositionEnd ); + /** + * Applies this mark to the given text with position-based highlighting. + * + * @param {string} text The original text without the mark applied. + * @returns {string} A new text with the mark applied to it. + */ + applyWithPosition( text ) { + const markStart = ""; + const markEnd = ""; - return text; -}; + const newPositionEnd = this.getPositionEnd() + markStart.length; -/** - * Serializes the Mark instance to an object. - * - * @returns {Object} The serialized Mark. - */ -Mark.prototype.serialize = function() { - return { - _parseClass: "Mark", - ...this._properties, - }; -}; + text = text.substring( 0, this.getPositionStart() ) + markStart + text.substring( this.getPositionStart() ); + text = text.substring( 0, newPositionEnd ) + markEnd + text.substring( newPositionEnd ); -/** - * Checks if the mark object is valid for position-based highlighting. - * @returns {void} - */ -// eslint-disable-next-line complexity -Mark.prototype.isValid = function() { - if ( ! isUndefined( this.getPositionStart() ) && this.getPositionStart() < 0 ) { - throw new RangeError( "positionStart should be larger or equal than 0." ); + return text; } - if ( ! isUndefined( this.getPositionEnd() ) && this.getPositionEnd() <= 0 ) { - throw new RangeError( "positionEnd should be larger than 0." ); - } - if ( ! isUndefined( this.getPositionStart() ) && ! isUndefined( this.getPositionEnd() ) && - this.getPositionStart() >= this.getPositionEnd() ) { - throw new RangeError( "The positionStart should be smaller than the positionEnd." ); + + /** + * Serializes the Mark instance to an object. + * + * @returns {Object} The serialized Mark. + */ + serialize() { + return { + _parseClass: "Mark", + ...this._properties, + }; } - if ( isUndefined( this.getPositionStart() ) && ! isUndefined( this.getPositionEnd() ) || - isUndefined( this.getPositionEnd() ) && ! isUndefined( this.getPositionStart() ) ) { - throw new Error( "A mark object should either have start and end defined or start and end undefined." ); + + /** + * Checks if the mark object is valid for position-based highlighting. + * @returns {void} + */ + isValid() { + if ( ! isUndefined( this.getPositionStart() ) && this.getPositionStart() < 0 ) { + throw new RangeError( "positionStart should be larger or equal than 0." ); + } + if ( ! isUndefined( this.getPositionEnd() ) && this.getPositionEnd() <= 0 ) { + throw new RangeError( "positionEnd should be larger than 0." ); + } + if ( ! isUndefined( this.getPositionStart() ) && ! isUndefined( this.getPositionEnd() ) && + this.getPositionStart() >= this.getPositionEnd() ) { + throw new RangeError( "The positionStart should be smaller than the positionEnd." ); + } + if ( isUndefined( this.getPositionStart() ) && ! isUndefined( this.getPositionEnd() ) || + isUndefined( this.getPositionEnd() ) && ! isUndefined( this.getPositionStart() ) ) { + throw new Error( "A mark object should either have start and end defined or start and end undefined." ); + } } -}; -/** - * Checks if a mark has position information available. - * @returns {boolean} Returns true if the Mark object has position information, false otherwise. - */ -Mark.prototype.hasPosition = function() { - return ! isUndefined( this.getPositionStart() ); -}; + /** + * Checks if a mark has position information available. + * @returns {boolean} Returns true if the Mark object has position information, false otherwise. + */ + hasPosition() { + return ! isUndefined( this.getPositionStart() ); + } -/** - * Checks if a mark has block position information available. - * A block has position information if the block start offset is available. - * - * @returns {boolean} Returns true if the Mark object has block position information, false otherwise. - */ -Mark.prototype.hasBlockPosition = function() { - return ! isUndefined( this.getBlockPositionStart() ); -}; + /** + * Checks if a mark has block position information available. + * A block has position information if the block start offset is available. + * + * @returns {boolean} Returns true if the Mark object has block position information, false otherwise. + */ + hasBlockPosition() { + return ! isUndefined( this.getBlockPositionStart() ); + } -/** - * Parses the object to a Mark. - * - * @param {Object} serialized The serialized object. - * - * @returns {Mark} The parsed Mark. - */ -Mark.parse = function( serialized ) { - delete serialized._parseClass; - return new Mark( serialized ); -}; + /** + * Parses the object to a Mark. + * + * @param {Object} serialized The serialized object. + * + * @returns {Mark} The parsed Mark. + */ + parse( serialized ) { + delete serialized._parseClass; + return new Mark( serialized ); + } +} export default Mark; diff --git a/packages/yoastseo/src/values/Paper.js b/packages/yoastseo/src/values/Paper.js index 46756c7da71..0a19edaa6a2 100644 --- a/packages/yoastseo/src/values/Paper.js +++ b/packages/yoastseo/src/values/Paper.js @@ -21,338 +21,341 @@ const defaultAttributes = { }; /** - * Constructs the Paper object and sets the keyword property. - * - * @param {string} text The text to use in the analysis. - * @param {object} [attributes] The object containing all attributes. - * @param {string} [attributes.keyword] The main keyword. - * @param {string} [attributes.synonyms] The main keyword's synonyms. - * @param {string} [attributes.description] The SEO description. - * @param {string} [attributes.title] The SEO title. - * @param {number} [attributes.titleWidth] The width of the title in pixels. - * @param {string} [attributes.slug] The slug. - * @param {string} [attributes.locale] The locale. - * @param {string} [attributes.permalink] The base url + slug. - * @param {string} [attributes.date] The date. - * @param {Object} [attributes.wpBlocks] The text, encoded in WordPress block editor blocks. - * @param {Object} [attributes.customData] Custom data. - * @param {string} [attributes.textTitle] The title of the text. - * @param {string} [attributes.writingDirection] The writing direction of the paper. Defaults to left to right (LTR). - * - * @constructor - */ -function Paper( text, attributes ) { - this._text = text || ""; - - this._tree = null; + * Represents an object where the analysis data is stored. + */ +class Paper { + /** + * Constructs the Paper object and sets the keyword property. + * + * @param {string} text The text to use in the analysis. + * @param {object} [attributes] The object containing all attributes. + * @param {string} [attributes.keyword] The main keyword. + * @param {string} [attributes.synonyms] The main keyword's synonyms. + * @param {string} [attributes.description] The SEO description. + * @param {string} [attributes.title] The SEO title. + * @param {number} [attributes.titleWidth] The width of the title in pixels. + * @param {string} [attributes.slug] The slug. + * @param {string} [attributes.locale] The locale. + * @param {string} [attributes.permalink] The base url + slug. + * @param {string} [attributes.date] The date. + * @param {Object} [attributes.wpBlocks] The text, encoded in WordPress block editor blocks. + * @param {Object} [attributes.customData] Custom data. + * @param {string} [attributes.textTitle] The title of the text. + * @param {string} [attributes.writingDirection] The writing direction of the paper. Defaults to left to right (LTR). + */ + constructor( text, attributes ) { + this._text = text || ""; + + this._tree = null; + + attributes = attributes || {}; + defaults( attributes, defaultAttributes ); + + if ( attributes.locale === "" ) { + attributes.locale = defaultAttributes.locale; + } + + if ( attributes.hasOwnProperty( "url" ) ) { + // The 'url' attribute has been deprecated since version 18.8, refer to hasUrl and getUrl below. + console.warn( "The 'url' attribute is deprecated, use 'slug' instead." ); + attributes.slug = attributes.url || attributes.slug; + } + + const onlyLetters = attributes.keyword.replace( /[‘’“”"'.?!:;,¿¡«»&*@#±^%|~`[\](){}⟨⟩<>/\\–\-\u2014\u00d7\u002b\u0026\s]/g, "" ); + + if ( isEmpty( onlyLetters ) ) { + attributes.keyword = defaultAttributes.keyword; + } + + this._attributes = attributes; + } - attributes = attributes || {}; - defaults( attributes, defaultAttributes ); - if ( attributes.locale === "" ) { - attributes.locale = defaultAttributes.locale; + /** + * Checks whether a keyword is available. + * @returns {boolean} Returns true if the Paper has a keyword. + */ + hasKeyword() { + return this._attributes.keyword !== ""; } - if ( attributes.hasOwnProperty( "url" ) ) { - // The 'url' attribute has been deprecated since version 18.8, refer to hasUrl and getUrl below. - console.warn( "The 'url' attribute is deprecated, use 'slug' instead." ); - attributes.slug = attributes.url || attributes.slug; + /** + * Returns the associated keyword or an empty string if no keyword is available. + * @returns {string} Returns Keyword + */ + getKeyword() { + return this._attributes.keyword; } - const onlyLetters = attributes.keyword.replace( /[‘’“”"'.?!:;,¿¡«»&*@#±^%|~`[\](){}⟨⟩<>/\\–\-\u2014\u00d7\u002b\u0026\s]/g, "" ); - - if ( isEmpty( onlyLetters ) ) { - attributes.keyword = defaultAttributes.keyword; + /** + * Checks whether synonyms are available. + * @returns {boolean} Returns true if the Paper has synonyms. + */ + hasSynonyms() { + return this._attributes.synonyms !== ""; } - this._attributes = attributes; -} - - -/** - * Checks whether a keyword is available. - * @returns {boolean} Returns true if the Paper has a keyword. - */ -Paper.prototype.hasKeyword = function() { - return this._attributes.keyword !== ""; -}; - -/** - * Returns the associated keyword or an empty string if no keyword is available. - * @returns {string} Returns Keyword - */ -Paper.prototype.getKeyword = function() { - return this._attributes.keyword; -}; - -/** - * Checks whether synonyms are available. - * @returns {boolean} Returns true if the Paper has synonyms. - */ -Paper.prototype.hasSynonyms = function() { - return this._attributes.synonyms !== ""; -}; - -/** - * Returns the associated synonyms or an empty string if no synonyms is available. - * @returns {string} Returns synonyms. - */ -Paper.prototype.getSynonyms = function() { - return this._attributes.synonyms; -}; - -/** - * Checks whether the text is available. - * @returns {boolean} Returns true if the paper has a text. - */ -Paper.prototype.hasText = function() { - return this._text !== ""; -}; - -/** - * Returns the associated text or am empty string if no text is available. - * @returns {string} Returns text - */ -Paper.prototype.getText = function() { - return this._text; -}; + /** + * Returns the associated synonyms or an empty string if no synonyms is available. + * @returns {string} Returns synonyms. + */ + getSynonyms() { + return this._attributes.synonyms; + } -/** - * Sets the tree. - * - * @param {Node} tree The tree to set. - * - * @returns {void} - */ -Paper.prototype.setTree = function( tree ) { - this._tree = tree; -}; + /** + * Checks whether the text is available. + * @returns {boolean} Returns true if the paper has a text. + */ + hasText() { + return this._text !== ""; + } -/** - * Returns the tree. - * - * @returns {Node} The tree. - */ -Paper.prototype.getTree = function() { - return this._tree; -}; + /** + * Returns the associated text or am empty string if no text is available. + * @returns {string} Returns text + */ + getText() { + return this._text; + } -/** - * Checks whether a description is available. - * @returns {boolean} Returns true if the paper has a description. - */ -Paper.prototype.hasDescription = function() { - return this._attributes.description !== ""; -}; + /** + * Sets the tree. + * + * @param {Node} tree The tree to set. + * + * @returns {void} + */ + setTree( tree ) { + this._tree = tree; + } -/** - * Returns the description or an empty string if no description is available. - * @returns {string} Returns the description. - */ -Paper.prototype.getDescription = function() { - return this._attributes.description; -}; + /** + * Returns the tree. + * + * @returns {Node} The tree. + */ + getTree() { + return this._tree; + } -/** - * Checks whether a title is available - * @returns {boolean} Returns true if the Paper has a title. - */ -Paper.prototype.hasTitle = function() { - return this._attributes.title !== ""; -}; + /** + * Checks whether a description is available. + * @returns {boolean} Returns true if the paper has a description. + */ + hasDescription() { + return this._attributes.description !== ""; + } -/** - * Returns the title, or an empty string of no title is available. - * @returns {string} Returns the title - */ -Paper.prototype.getTitle = function() { - return this._attributes.title; -}; + /** + * Returns the description or an empty string if no description is available. + * @returns {string} Returns the description. + */ + getDescription() { + return this._attributes.description; + } -/** - * Checks whether a title width in pixels is available - * @returns {boolean} Returns true if the Paper has a title. - */ -Paper.prototype.hasTitleWidth = function() { - return this._attributes.titleWidth !== 0; -}; + /** + * Checks whether a title is available + * @returns {boolean} Returns true if the Paper has a title. + */ + hasTitle() { + return this._attributes.title !== ""; + } -/** - * Returns the title width in pixels, or an empty string of no title width in pixels is available. - * @returns {string} Returns the title - */ -Paper.prototype.getTitleWidth = function() { - return this._attributes.titleWidth; -}; + /** + * Returns the title, or an empty string of no title is available. + * @returns {string} Returns the title + */ + getTitle() { + return this._attributes.title; + } -/** - * Checks whether a slug is available - * @returns {boolean} Returns true if the Paper has a slug. - */ -Paper.prototype.hasSlug = function() { - return this._attributes.slug !== ""; -}; + /** + * Checks whether a title width in pixels is available + * @returns {boolean} Returns true if the Paper has a title. + */ + hasTitleWidth() { + return this._attributes.titleWidth !== 0; + } -/** - * Returns the slug, or an empty string of no slug is available. - * @returns {string} Returns the url - */ -Paper.prototype.getSlug = function() { - return this._attributes.slug; -}; + /** + * Returns the title width in pixels, or an empty string of no title width in pixels is available. + * @returns {string} Returns the title + */ + getTitleWidth() { + return this._attributes.titleWidth; + } -/** - * Checks whether an url is available - * @deprecated Since version 18.7. Use hasSlug instead. - * @returns {boolean} Returns true if the Paper has a slug. - */ -Paper.prototype.hasUrl = function() { - console.warn( "This function is deprecated, use hasSlug instead" ); - return this.hasSlug(); -}; + /** + * Checks whether a slug is available + * @returns {boolean} Returns true if the Paper has a slug. + */ + hasSlug() { + return this._attributes.slug !== ""; + } -/** - * Returns the url, or an empty string if no url is available. - * @deprecated Since version 18.8. Use getSlug instead. - * @returns {string} Returns the url - */ -Paper.prototype.getUrl = function() { - console.warn( "This function is deprecated, use getSlug instead" ); - return this.getSlug(); -}; + /** + * Returns the slug, or an empty string of no slug is available. + * @returns {string} Returns the url + */ + getSlug() { + return this._attributes.slug; + } -/** - * Checks whether a locale is available - * @returns {boolean} Returns true if the paper has a locale - */ -Paper.prototype.hasLocale = function() { - return this._attributes.locale !== ""; -}; + /** + * Checks whether an url is available + * @deprecated Since version 18.7. Use hasSlug instead. + * @returns {boolean} Returns true if the Paper has a slug. + */ + hasUrl() { + console.warn( "This function is deprecated, use hasSlug instead" ); + return this.hasSlug(); + } -/** - * Returns the locale or an empty string if no locale is available - * @returns {string} Returns the locale - */ -Paper.prototype.getLocale = function() { - return this._attributes.locale; -}; + /** + * Returns the url, or an empty string if no url is available. + * @deprecated Since version 18.8. Use getSlug instead. + * @returns {string} Returns the url + */ + getUrl() { + console.warn( "This function is deprecated, use getSlug instead" ); + return this.getSlug(); + } -/** - * Gets the information of the writing direction of the paper. - * It returns "LTR" (left to right) if this attribute is not provided. - * - * @returns {string} Returns the information of the writing direction of the paper. - */ -Paper.prototype.getWritingDirection = function() { - return this._attributes.writingDirection; -}; + /** + * Checks whether a locale is available + * @returns {boolean} Returns true if the paper has a locale + */ + hasLocale() { + return this._attributes.locale !== ""; + } -/** - * Checks whether a permalink is available - * @returns {boolean} Returns true if the Paper has a permalink. - */ -Paper.prototype.hasPermalink = function() { - return this._attributes.permalink !== ""; -}; + /** + * Returns the locale or an empty string if no locale is available + * @returns {string} Returns the locale + */ + getLocale() { + return this._attributes.locale; + } -/** - * Returns the permalink, or an empty string if no permalink is available. - * @returns {string} Returns the permalink. - */ -Paper.prototype.getPermalink = function() { - return this._attributes.permalink; -}; + /** + * Gets the information of the writing direction of the paper. + * It returns "LTR" (left to right) if this attribute is not provided. + * + * @returns {string} Returns the information of the writing direction of the paper. + */ + getWritingDirection() { + return this._attributes.writingDirection; + } -/** - * Checks whether a date is available. - * @returns {boolean} Returns true if the Paper has a date. - */ -Paper.prototype.hasDate = function() { - return this._attributes.date !== ""; -}; + /** + * Checks whether a permalink is available + * @returns {boolean} Returns true if the Paper has a permalink. + */ + hasPermalink() { + return this._attributes.permalink !== ""; + } -/** - * Returns the date, or an empty string if no date is available. - * @returns {string} Returns the date. - */ -Paper.prototype.getDate = function() { - return this._attributes.date; -}; + /** + * Returns the permalink, or an empty string if no permalink is available. + * @returns {string} Returns the permalink. + */ + getPermalink() { + return this._attributes.permalink; + } -/** - * Checks whether custom data is available. - * @returns {boolean} Returns true if the Paper has custom data. - */ -Paper.prototype.hasCustomData = function() { - return ! isEmpty( this._attributes.customData ); -}; + /** + * Checks whether a date is available. + * @returns {boolean} Returns true if the Paper has a date. + */ + hasDate() { + return this._attributes.date !== ""; + } -/** - * Returns the custom data, or an empty object if no data is available. - * @returns {Object} Returns the custom data. - */ -Paper.prototype.getCustomData = function() { - return this._attributes.customData; -}; + /** + * Returns the date, or an empty string if no date is available. + * @returns {string} Returns the date. + */ + getDate() { + return this._attributes.date; + } -/** - * Checks whether a text title is available. - * @returns {boolean} Returns true if the Paper has a text title. - */ -Paper.prototype.hasTextTitle = function() { - return this._attributes.textTitle !== "" && ! isNil( this._attributes.textTitle ); -}; + /** + * Checks whether custom data is available. + * @returns {boolean} Returns true if the Paper has custom data. + */ + hasCustomData() { + return ! isEmpty( this._attributes.customData ); + } -/** - * Returns the text title, or an empty string if no data is available. - * @returns {string} Returns the text title. - */ -Paper.prototype.getTextTitle = function() { - return this._attributes.textTitle; -}; + /** + * Returns the custom data, or an empty object if no data is available. + * @returns {Object} Returns the custom data. + */ + getCustomData() { + return this._attributes.customData; + } -/** - * Serializes the Paper instance to an object. - * - * @returns {Object} The serialized Paper. - */ -Paper.prototype.serialize = function() { - return { - _parseClass: "Paper", - text: this._text, - ...this._attributes, - }; -}; + /** + * Checks whether a text title is available. + * @returns {boolean} Returns true if the Paper has a text title. + */ + hasTextTitle() { + return this._attributes.textTitle !== "" && ! isNil( this._attributes.textTitle ); + } -/** - * Checks whether the given paper has the same properties as this instance. - * - * @param {Paper} paper The paper to compare to. - * - * @returns {boolean} Whether the given paper is identical or not. - */ -Paper.prototype.equals = function( paper ) { - return this._text === paper.getText() && isEqual( this._attributes, paper._attributes ); -}; + /** + * Returns the text title, or an empty string if no data is available. + * @returns {string} Returns the text title. + */ + getTextTitle() { + return this._attributes.textTitle; + } -/** - * Parses the object to a Paper. - * - * @param {Object|Paper} serialized The serialized object or Paper instance. - * - * @returns {Paper} The parsed Paper. - */ -Paper.parse = function( serialized ) { - // For ease of use, check if it is not already a Paper instance. - if ( serialized instanceof Paper ) { - return serialized; + /** + * Serializes the Paper instance to an object. + * + * @returns {Object} The serialized Paper. + */ + serialize() { + return { + _parseClass: "Paper", + text: this._text, + ...this._attributes, + }; } - // _parseClass is taken here, so it doesn't end up in the attributes. - // eslint-disable-next-line no-unused-vars - const { text, _parseClass, ...attributes } = serialized; + /** + * Checks whether the given paper has the same properties as this instance. + * + * @param {Paper} paper The paper to compare to. + * + * @returns {boolean} Whether the given paper is identical or not. + */ + equals( paper ) { + return this._text === paper.getText() && isEqual( this._attributes, paper._attributes ); + } - return new Paper( text, attributes ); -}; + /** + * Parses the object to a Paper. + * + * @param {Object|Paper} serialized The serialized object or Paper instance. + * + * @returns {Paper} The parsed Paper. + */ + parse( serialized ) { + // For ease of use, check if it is not already a Paper instance. + if ( serialized instanceof Paper ) { + return serialized; + } + + // _parseClass is taken here, so it doesn't end up in the attributes. + // eslint-disable-next-line no-unused-vars + const { text, _parseClass, ...attributes } = serialized; + + return new Paper( text, attributes ); + } +} export default Paper; diff --git a/packages/yoastseo/src/worker/AnalysisWebWorker.js b/packages/yoastseo/src/worker/AnalysisWebWorker.js index 4ae39dc7cdb..8a00bbc7a96 100644 --- a/packages/yoastseo/src/worker/AnalysisWebWorker.js +++ b/packages/yoastseo/src/worker/AnalysisWebWorker.js @@ -1229,7 +1229,7 @@ export default class AnalysisWebWorker { return await Promise.all( keywordKeys.map( key => { this._relatedKeywords[ key ] = relatedKeywords[ key ]; - const relatedPaper = Paper.parse( { + const relatedPaper = Paper.prototype.parse( { ...paper.serialize(), keyword: this._relatedKeywords[ key ].keyword, synonyms: this._relatedKeywords[ key ].synonyms, diff --git a/packages/yoastseo/src/worker/transporter/parse.js b/packages/yoastseo/src/worker/transporter/parse.js index 76a63a79c1f..6f8ce291820 100644 --- a/packages/yoastseo/src/worker/transporter/parse.js +++ b/packages/yoastseo/src/worker/transporter/parse.js @@ -31,9 +31,7 @@ export default function parse( thing ) { const thingIsObject = isObject( thing ); if ( thingIsObject && thing._parseClass && PARSE_CLASSES[ thing._parseClass ] ) { - return thing._parseClass === "Sentence" || thing._parseClass === "Clause" - ? PARSE_CLASSES[ thing._parseClass ].prototype.parse( thing ) - : PARSE_CLASSES[ thing._parseClass ].parse( thing ); + return PARSE_CLASSES[ thing._parseClass ].prototype.parse( thing ); } if ( thingIsObject ) { From 781a62fdfdd6873de1160f010ef2e56ee0d8cdcb Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Thu, 11 Jan 2024 13:50:05 +0100 Subject: [PATCH 02/11] use `import` statement instead of `require` --- packages/yoastseo/src/scoring/contentAssessor.js | 4 +++- packages/yoastseo/src/scoring/cornerstone/contentAssessor.js | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/yoastseo/src/scoring/contentAssessor.js b/packages/yoastseo/src/scoring/contentAssessor.js index f5a161c171f..3c93b86266d 100644 --- a/packages/yoastseo/src/scoring/contentAssessor.js +++ b/packages/yoastseo/src/scoring/contentAssessor.js @@ -1,3 +1,5 @@ +import { inherits } from "util"; + import Assessor from "./assessor.js"; import ParagraphTooLong from "./assessments/readability/ParagraphTooLongAssessment.js"; import SentenceLengthInText from "./assessments/readability/SentenceLengthInTextAssessment.js"; @@ -40,7 +42,7 @@ const ContentAssessor = function( researcher, options = {} ) { ]; }; -require( "util" ).inherits( ContentAssessor, Assessor ); +inherits( ContentAssessor, Assessor ); /** * Calculates the weighted rating for languages that have all assessments based on a given rating. diff --git a/packages/yoastseo/src/scoring/cornerstone/contentAssessor.js b/packages/yoastseo/src/scoring/cornerstone/contentAssessor.js index cb920a0f9c3..c090ecb05d3 100644 --- a/packages/yoastseo/src/scoring/cornerstone/contentAssessor.js +++ b/packages/yoastseo/src/scoring/cornerstone/contentAssessor.js @@ -1,3 +1,4 @@ +import { inherits } from "util"; import Assessor from "../assessor.js"; import ContentAssessor from "../contentAssessor"; import ParagraphTooLong from "../assessments/readability/ParagraphTooLongAssessment.js"; @@ -50,7 +51,7 @@ const CornerStoneContentAssessor = function( researcher, options = {} ) { ]; }; -require( "util" ).inherits( CornerStoneContentAssessor, ContentAssessor ); +inherits( CornerStoneContentAssessor, ContentAssessor ); export default CornerStoneContentAssessor; From e63d1d727a397e6dc0089493fac3c80a6457522c Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Mon, 15 Jan 2024 11:40:34 +0100 Subject: [PATCH 03/11] Refactor the class --- .../scoring/renderers/AssessorPresenter.js | 633 +++++++++--------- 1 file changed, 320 insertions(+), 313 deletions(-) diff --git a/packages/yoastseo/src/scoring/renderers/AssessorPresenter.js b/packages/yoastseo/src/scoring/renderers/AssessorPresenter.js index 00a64bac13a..c79652732d4 100644 --- a/packages/yoastseo/src/scoring/renderers/AssessorPresenter.js +++ b/packages/yoastseo/src/scoring/renderers/AssessorPresenter.js @@ -1,372 +1,379 @@ +/* eslint-disable tree-shaking/no-side-effects-in-initialization */ import { __ } from "@wordpress/i18n"; import { difference, forEach, isNumber, isObject, isUndefined } from "lodash-es"; import { assessmentPresenterResult as template } from "../../snippetPreview/templates.js"; import scoreToRating from "../interpreters/scoreToRating.js"; import createConfig from "../../config/presenter.js"; -/** - * Constructs the AssessorPresenter. - * - * @param {Object} args A list of arguments to use in the presenter. - * @param {object} args.targets The HTML elements to render the output to. - * @param {string} args.targets.output The HTML element to render the individual ratings out to. - * @param {string} args.targets.overall The HTML element to render the overall rating out to. - * @param {string} args.keyword The keyword to use for checking, when calculating the overall rating. - * @param {SEOAssessor} args.assessor The Assessor object to retrieve assessment results from. - * - * @constructor - */ -var AssessorPresenter = function( args ) { - this.keyword = args.keyword; - this.assessor = args.assessor; - this.output = args.targets.output; - this.overall = args.targets.overall || "overallScore"; - this.presenterConfig = createConfig(); - - this._disableMarkerButtons = false; - - this._activeMarker = false; -}; /** - * Sets the keyword. - * - * @param {string} keyword The keyword to use. - * @returns {void} + * Represents the AssessorPresenter. */ -AssessorPresenter.prototype.setKeyword = function( keyword ) { - this.keyword = keyword; -}; +class AssessorPresenter { + /** + * Constructs the AssessorPresenter. + * + * @param {Object} args A list of arguments to use in the presenter. + * @param {object} args.targets The HTML elements to render the output to. + * @param {string} args.targets.output The HTML element to render the individual ratings out to. + * @param {string} args.targets.overall The HTML element to render the overall rating out to. + * @param {string} args.keyword The keyword to use for checking, when calculating the overall rating. + * @param {SEOAssessor} args.assessor The Assessor object to retrieve assessment results from. + * + * @constructor + */ + construct( args ) { + this.keyword = args.keyword; + this.assessor = args.assessor; + this.output = args.targets.output; + this.overall = args.targets.overall || "overallScore"; + this.presenterConfig = createConfig(); + + this._disableMarkerButtons = false; -/** - * Checks whether or not a specific property exists in the presenter configuration. - * - * @param {string} property The property name to search for. - * @returns {boolean} Whether or not the property exists. - */ -AssessorPresenter.prototype.configHasProperty = function( property ) { - return this.presenterConfig.hasOwnProperty( property ); -}; - -/** - * Gets a fully formatted indicator object that can be used. - * - * @param {string} rating The rating to use. - * @returns {Object} An object containing the class, the screen reader text, and the full text. - */ -AssessorPresenter.prototype.getIndicator = function( rating ) { - return { - className: this.getIndicatorColorClass( rating ), - screenReaderText: this.getIndicatorScreenReaderText( rating ), - fullText: this.getIndicatorFullText( rating ), - screenReaderReadabilityText: this.getIndicatorScreenReaderReadabilityText( rating ), - }; -}; + this._activeMarker = false; + } -/** - * Gets the indicator color class from the presenter configuration, if it exists. - * - * @param {string} rating The rating to check against the config. - * @returns {string} String containing the CSS class to be used. - */ -AssessorPresenter.prototype.getIndicatorColorClass = function( rating ) { - if ( ! this.configHasProperty( rating ) ) { - return ""; + /** + * Sets the keyword. + * + * @param {string} keyword The keyword to use. + * @returns {void} + */ + setKeyword( keyword ) { + this.keyword = keyword; } - return this.presenterConfig[ rating ].className; -}; + /** + * Checks whether a specific property exists in the presenter configuration. + * + * @param {string} property The property name to search for. + * @returns {boolean} Whether or not the property exists. + */ + configHasProperty( property ) { + return this.presenterConfig.hasOwnProperty( property ); + } -/** - * Get the indicator screen reader text from the presenter configuration, if it exists. - * - * @param {string} rating The rating to check against the config. - * @returns {string} Translated string containing the screen reader text to be used. - */ -AssessorPresenter.prototype.getIndicatorScreenReaderText = function( rating ) { - if ( ! this.configHasProperty( rating ) ) { - return ""; + /** + * Gets a fully formatted indicator object that can be used. + * + * @param {string} rating The rating to use. + * @returns {Object} An object containing the class, the screen reader text, and the full text. + */ + getIndicator( rating ) { + return { + className: this.getIndicatorColorClass( rating ), + screenReaderText: this.getIndicatorScreenReaderText( rating ), + fullText: this.getIndicatorFullText( rating ), + screenReaderReadabilityText: this.getIndicatorScreenReaderReadabilityText( rating ), + }; } - return this.presenterConfig[ rating ].screenReaderText; -}; + /** + * Gets the indicator color class from the presenter configuration, if it exists. + * + * @param {string} rating The rating to check against the config. + * @returns {string} String containing the CSS class to be used. + */ + getIndicatorColorClass( rating ) { + if ( ! this.configHasProperty( rating ) ) { + return ""; + } -/** - * Get the indicator screen reader readability text from the presenter configuration, if it exists. - * - * @param {string} rating The rating to check against the config. - * @returns {string} Translated string containing the screen reader readability text to be used. - */ -AssessorPresenter.prototype.getIndicatorScreenReaderReadabilityText = function( rating ) { - if ( ! this.configHasProperty( rating ) ) { - return ""; + return this.presenterConfig[ rating ].className; } - return this.presenterConfig[ rating ].screenReaderReadabilityText; -}; + /** + * Get the indicator screen reader text from the presenter configuration, if it exists. + * + * @param {string} rating The rating to check against the config. + * @returns {string} Translated string containing the screen reader text to be used. + */ + getIndicatorScreenReaderText( rating ) { + if ( ! this.configHasProperty( rating ) ) { + return ""; + } -/** - * Get the indicator full text from the presenter configuration, if it exists. - * - * @param {string} rating The rating to check against the config. - * @returns {string} Translated string containing the full text to be used. - */ -AssessorPresenter.prototype.getIndicatorFullText = function( rating ) { - if ( ! this.configHasProperty( rating ) ) { - return ""; + return this.presenterConfig[ rating ].screenReaderText; } - return this.presenterConfig[ rating ].fullText; -}; + /** + * Get the indicator screen reader readability text from the presenter configuration, if it exists. + * + * @param {string} rating The rating to check against the config. + * @returns {string} Translated string containing the screen reader readability text to be used. + */ + getIndicatorScreenReaderReadabilityText( rating ) { + if ( ! this.configHasProperty( rating ) ) { + return ""; + } -/** - * Adds a rating based on the numeric score. - * - * @param {Object} result Object based on the Assessment result. Requires a score property to work. - * @returns {Object} The Assessment result object with the rating added. - */ -AssessorPresenter.prototype.resultToRating = function( result ) { - if ( ! isObject( result ) ) { - return ""; + return this.presenterConfig[ rating ].screenReaderReadabilityText; } - result.rating = scoreToRating( result.score ); - - return result; -}; + /** + * Get the indicator full text from the presenter configuration, if it exists. + * + * @param {string} rating The rating to check against the config. + * @returns {string} Translated string containing the full text to be used. + */ + getIndicatorFullText( rating ) { + if ( ! this.configHasProperty( rating ) ) { + return ""; + } -/** - * Takes the individual assessment results, sorts and rates them. - * - * @returns {Object} Object containing all the individual ratings. - */ -AssessorPresenter.prototype.getIndividualRatings = function() { - var ratings = {}; - var validResults = this.sort( this.assessor.getValidResults() ); - var mappedResults = validResults.map( this.resultToRating ); + return this.presenterConfig[ rating ].fullText; + } - forEach( mappedResults, function( item, key ) { - ratings[ key ] = this.addRating( item ); - }.bind( this ) ); + /** + * Adds a rating based on the numeric score. + * + * @param {Object} result Object based on the Assessment result. Requires a score property to work. + * @returns {Object} The Assessment result object with the rating added. + */ + resultToRating( result ) { + if ( ! isObject( result ) ) { + return ""; + } - return ratings; -}; + result.rating = scoreToRating( result.score ); -/** - * Excludes items from the results that are present in the exclude array. - * - * @param {Array} results Array containing the items to filter through. - * @param {Array} exclude Array of results to exclude. - * @returns {Array} Array containing items that remain after exclusion. - */ -AssessorPresenter.prototype.excludeFromResults = function( results, exclude ) { - return difference( results, exclude ); -}; + return result; + } -/** - * Sorts results based on their score property and always places items considered to be unsortable, at the top. - * - * @param {Array} results Array containing the results that need to be sorted. - * @returns {Array} Array containing the sorted results. - */ -AssessorPresenter.prototype.sort = function( results ) { - var unsortables = this.getUndefinedScores( results ); - var sortables = this.excludeFromResults( results, unsortables ); + /** + * Takes the individual assessment results, sorts and rates them. + * + * @returns {Object} Object containing all the individual ratings. + */ + getIndividualRatings() { + const ratings = {}; + const validResults = this.sort( this.assessor.getValidResults() ); + const mappedResults = validResults.map( this.resultToRating ); + + forEach( mappedResults, function( item, key ) { + ratings[ key ] = this.addRating( item ); + }.bind( this ) ); + + return ratings; + } - sortables.sort( function( a, b ) { - return a.score - b.score; - } ); + /** + * Excludes items from the results that are present in the exclude array. + * + * @param {Array} results Array containing the items to filter through. + * @param {Array} exclude Array of results to exclude. + * @returns {Array} Array containing items that remain after exclusion. + */ + excludeFromResults( results, exclude ) { + return difference( results, exclude ); + } - return unsortables.concat( sortables ); -}; + /** + * Sorts results based on their score property and always places items considered to be unsortable, at the top. + * + * @param {Array} results Array containing the results that need to be sorted. + * @returns {Array} Array containing the sorted results. + */ + sort( results ) { + const unsortables = this.getUndefinedScores( results ); + const sortables = this.excludeFromResults( results, unsortables ); + + sortables.sort( function( a, b ) { + return a.score - b.score; + } ); + + return unsortables.concat( sortables ); + } -/** - * Returns a subset of results that have an undefined score or a score set to zero. - * - * @param {Array} results The results to filter through. - * @returns {Array} A subset of results containing items with an undefined score or where the score is zero. - */ -AssessorPresenter.prototype.getUndefinedScores = function( results ) { - return results.filter( function( result ) { - return isUndefined( result.score ) || result.score === 0; - } ); -}; + /** + * Returns a subset of results that have an undefined score or a score set to zero. + * + * @param {Array} results The results to filter through. + * @returns {Array} A subset of results containing items with an undefined score or where the score is zero. + */ + getUndefinedScores( results ) { + return results.filter( function( result ) { + return isUndefined( result.score ) || result.score === 0; + } ); + } -/** - * Creates a rating object based on the item that is being passed. - * - * @param {AssessmentResult} item The item to check and create a rating object from. - * @returns {Object} Object containing a parsed item, including a colored indicator. - */ -AssessorPresenter.prototype.addRating = function( item ) { - var indicator = this.getIndicator( item.rating ); - indicator.text = item.text; - indicator.identifier = item.getIdentifier(); + /** + * Creates a rating object based on the item that is being passed. + * + * @param {AssessmentResult} item The item to check and create a rating object from. + * @returns {Object} Object containing a parsed item, including a colored indicator. + */ + addRating( item ) { + const indicator = this.getIndicator( item.rating ); + indicator.text = item.text; + indicator.identifier = item.getIdentifier(); + + if ( item.hasMarker() ) { + indicator.marker = item.getMarker(); + } - if ( item.hasMarker() ) { - indicator.marker = item.getMarker(); + return indicator; } - return indicator; -}; + /** + * Calculates the overall rating score based on the overall score. + * + * @param {Number} overallScore The overall score to use in the calculation. + * @returns {Object} The rating based on the score. + */ + getOverallRating( overallScore ) { + let rating = 0; + + if ( this.keyword === "" ) { + return this.resultToRating( { score: rating } ); + } -/** - * Calculates the overall rating score based on the overall score. - * - * @param {Number} overallScore The overall score to use in the calculation. - * @returns {Object} The rating based on the score. - */ -AssessorPresenter.prototype.getOverallRating = function( overallScore ) { - var rating = 0; + if ( isNumber( overallScore ) ) { + rating = ( overallScore / 10 ); + } - if ( this.keyword === "" ) { return this.resultToRating( { score: rating } ); } - if ( isNumber( overallScore ) ) { - rating = ( overallScore / 10 ); - } + /** + * Mark with a given marker. This will set the active marker to the correct value. + * + * @param {string} identifier The identifier for the assessment/marker. + * @param {Function} marker The marker function. + * @returns {void} + */ + markAssessment( identifier, marker ) { + if ( this._activeMarker === identifier ) { + this.removeAllMarks(); + this._activeMarker = false; + } else { + marker(); + this._activeMarker = identifier; + } - return this.resultToRating( { score: rating } ); -}; + this.render(); + } -/** - * Mark with a given marker. This will set the active marker to the correct value. - * - * @param {string} identifier The identifier for the assessment/marker. - * @param {Function} marker The marker function. - * @returns {void} - */ -AssessorPresenter.prototype.markAssessment = function( identifier, marker ) { - if ( this._activeMarker === identifier ) { - this.removeAllMarks(); + /** + * Disables the currently active marker in the UI. + * + * @returns {void} + */ + disableMarker() { this._activeMarker = false; - } else { - marker(); - this._activeMarker = identifier; + this.render(); } - this.render(); -}; - -/** - * Disables the currently active marker in the UI. - * - * @returns {void} - */ -AssessorPresenter.prototype.disableMarker = function() { - this._activeMarker = false; - this.render(); -}; - -/** - * Disables the marker buttons. - * - * @returns {void} - */ -AssessorPresenter.prototype.disableMarkerButtons = function() { - this._disableMarkerButtons = true; - this.render(); -}; - -/** - * Enables the marker buttons. - * - * @returns {void} - */ -AssessorPresenter.prototype.enableMarkerButtons = function() { - this._disableMarkerButtons = false; - this.render(); -}; + /** + * Disables the marker buttons. + * + * @returns {void} + */ + disableMarkerButtons() { + this._disableMarkerButtons = true; + this.render(); + } -/** - * Adds an event listener for the marker button - * - * @param {string} identifier The identifier for the assessment the marker belongs to. - * @param {Function} marker The marker function that can mark the assessment in the text. - * @returns {void} - */ -AssessorPresenter.prototype.addMarkerEventHandler = function( identifier, marker ) { - var container = document.getElementById( this.output ); - var markButton = container.getElementsByClassName( "js-assessment-results__mark-" + identifier )[ 0 ]; + /** + * Enables the marker buttons. + * + * @returns {void} + */ + enableMarkerButtons() { + this._disableMarkerButtons = false; + this.render(); + } - markButton.addEventListener( "click", this.markAssessment.bind( this, identifier, marker ) ); -}; + /** + * Adds an event listener for the marker button + * + * @param {string} identifier The identifier for the assessment the marker belongs to. + * @param {Function} marker The marker function that can mark the assessment in the text. + * @returns {void} + */ + addMarkerEventHandler( identifier, marker ) { + const container = document.getElementById( this.output ); + const markButton = container.getElementsByClassName( "js-assessment-results__mark-" + identifier )[ 0 ]; + + markButton.addEventListener( "click", this.markAssessment.bind( this, identifier, marker ) ); + } -/** - * Renders out both the individual and the overall ratings. - * - * @returns {void} - */ -AssessorPresenter.prototype.render = function() { - this.renderIndividualRatings(); - this.renderOverallRating(); -}; + /** + * Renders out both the individual and the overall ratings. + * + * @returns {void} + */ + render() { + this.renderIndividualRatings(); + this.renderOverallRating(); + } -/** - * Adds event handlers to the mark buttons - * - * @param {Array} scores The list of rendered scores. - * - * @returns {void} - */ -AssessorPresenter.prototype.bindMarkButtons = function( scores ) { - // Make sure the button works for every score with a marker. - forEach( scores, function( score ) { - if ( score.hasOwnProperty( "marker" ) ) { - this.addMarkerEventHandler( score.identifier, score.marker ); - } - }.bind( this ) ); -}; + /** + * Adds event handlers to the mark buttons + * + * @param {Array} scores The list of rendered scores. + * + * @returns {void} + */ + bindMarkButtons( scores ) { + // Make sure the button works for every score with a marker. + forEach( scores, function( score ) { + if ( score.hasOwnProperty( "marker" ) ) { + this.addMarkerEventHandler( score.identifier, score.marker ); + } + }.bind( this ) ); + } -/** - * Removes all marks currently on the text - * - * @returns {void} - */ -AssessorPresenter.prototype.removeAllMarks = function() { - var marker = this.assessor.getSpecificMarker(); + /** + * Removes all marks currently on the text + * + * @returns {void} + */ + removeAllMarks() { + const marker = this.assessor.getSpecificMarker(); - marker( this.assessor.getPaper(), [] ); -}; + marker( this.assessor.getPaper(), [] ); + } -/** - * Renders out the individual ratings. - * - * @returns {void} - */ -AssessorPresenter.prototype.renderIndividualRatings = function() { - var outputTarget = document.getElementById( this.output ); - var scores = this.getIndividualRatings(); - - outputTarget.innerHTML = template( { - scores: scores, - i18n: { - disabledMarkText: __( "Marks are disabled in current view", "wordpress-seo" ), - markInText: __( "Mark this result in the text", "wordpress-seo" ), - removeMarksInText: __( "Remove marks in the text", "wordpress-seo" ), - }, - activeMarker: this._activeMarker, - markerButtonsDisabled: this._disableMarkerButtons, - } ); - - this.bindMarkButtons( scores ); -}; + /** + * Renders out the individual ratings. + * + * @returns {void} + */ + renderIndividualRatings() { + const outputTarget = document.getElementById( this.output ); + const scores = this.getIndividualRatings(); + + outputTarget.innerHTML = template( { + scores: scores, + i18n: { + disabledMarkText: __( "Marks are disabled in current view", "wordpress-seo" ), + markInText: __( "Mark this result in the text", "wordpress-seo" ), + removeMarksInText: __( "Remove marks in the text", "wordpress-seo" ), + }, + activeMarker: this._activeMarker, + markerButtonsDisabled: this._disableMarkerButtons, + } ); + + this.bindMarkButtons( scores ); + } -/** - * Renders out the overall rating. - * - * @returns {void} - */ -AssessorPresenter.prototype.renderOverallRating = function() { - var overallRating = this.getOverallRating( this.assessor.calculateOverallScore() ); - var overallRatingElement = document.getElementById( this.overall ); + /** + * Renders out the overall rating. + * + * @returns {void} + */ + renderOverallRating() { + const overallRating = this.getOverallRating( this.assessor.calculateOverallScore() ); + const overallRatingElement = document.getElementById( this.overall ); + + if ( ! overallRatingElement ) { + return; + } - if ( ! overallRatingElement ) { - return; + overallRatingElement.className = "overallScore " + this.getIndicatorColorClass( overallRating.rating ); } - - overallRatingElement.className = "overallScore " + this.getIndicatorColorClass( overallRating.rating ); -}; +} export default AssessorPresenter; From 386cc38ccf9ae2289043aaff64079585540f8674 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Tue, 16 Jan 2024 16:15:39 +0100 Subject: [PATCH 04/11] Explicitly make parse method static. This way this method can only be accessed from the Class itself and not from the object instance of the class. --- packages/yoastseo/src/languageProcessing/values/Clause.js | 2 +- .../yoastseo/src/languageProcessing/values/ProminentWord.js | 2 +- packages/yoastseo/src/languageProcessing/values/Sentence.js | 2 +- packages/yoastseo/src/values/AssessmentResult.js | 2 +- packages/yoastseo/src/values/Mark.js | 2 +- packages/yoastseo/src/values/Paper.js | 2 +- packages/yoastseo/src/worker/AnalysisWebWorker.js | 2 +- packages/yoastseo/src/worker/transporter/parse.js | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/yoastseo/src/languageProcessing/values/Clause.js b/packages/yoastseo/src/languageProcessing/values/Clause.js index b25fbc4ba90..42510f37ade 100644 --- a/packages/yoastseo/src/languageProcessing/values/Clause.js +++ b/packages/yoastseo/src/languageProcessing/values/Clause.js @@ -97,7 +97,7 @@ class Clause { * * @returns {Clause} The parsed Clause. */ - parse( serialized ) { + static parse( serialized ) { const clause = new Clause( serialized.clauseText, serialized.auxiliaries ); clause.setPassive( serialized.isPassive ); diff --git a/packages/yoastseo/src/languageProcessing/values/ProminentWord.js b/packages/yoastseo/src/languageProcessing/values/ProminentWord.js index d190083b187..fc3cb373129 100644 --- a/packages/yoastseo/src/languageProcessing/values/ProminentWord.js +++ b/packages/yoastseo/src/languageProcessing/values/ProminentWord.js @@ -87,7 +87,7 @@ class ProminentWord { * * @returns {ProminentWord} The parsed ProminentWord. */ - parse( serialized ) { + static parse( serialized ) { return new ProminentWord( serialized.word, serialized.stem, serialized.occurrences ); } } diff --git a/packages/yoastseo/src/languageProcessing/values/Sentence.js b/packages/yoastseo/src/languageProcessing/values/Sentence.js index af164af4399..948fc5354f7 100644 --- a/packages/yoastseo/src/languageProcessing/values/Sentence.js +++ b/packages/yoastseo/src/languageProcessing/values/Sentence.js @@ -99,7 +99,7 @@ class Sentence { * * @returns {Sentence} The parsed Sentence. */ - parse( serialized ) { + static parse( serialized ) { const sentence = new Sentence( serialized.sentenceText ); sentence.setClauses( serialized.clauses ); sentence.setPassive( serialized.isPassive ); diff --git a/packages/yoastseo/src/values/AssessmentResult.js b/packages/yoastseo/src/values/AssessmentResult.js index 489ea640a8d..a8ab557ad31 100644 --- a/packages/yoastseo/src/values/AssessmentResult.js +++ b/packages/yoastseo/src/values/AssessmentResult.js @@ -299,7 +299,7 @@ class AssessmentResult { * * @returns {AssessmentResult} The parsed AssessmentResult. */ - parse( serialized ) { + static parse( serialized ) { const result = new AssessmentResult( { text: serialized.text, score: serialized.score, diff --git a/packages/yoastseo/src/values/Mark.js b/packages/yoastseo/src/values/Mark.js index 7b87570a384..1f5383f52f9 100644 --- a/packages/yoastseo/src/values/Mark.js +++ b/packages/yoastseo/src/values/Mark.js @@ -260,7 +260,7 @@ class Mark { * * @returns {Mark} The parsed Mark. */ - parse( serialized ) { + static parse( serialized ) { delete serialized._parseClass; return new Mark( serialized ); } diff --git a/packages/yoastseo/src/values/Paper.js b/packages/yoastseo/src/values/Paper.js index 0a19edaa6a2..e9e385f8800 100644 --- a/packages/yoastseo/src/values/Paper.js +++ b/packages/yoastseo/src/values/Paper.js @@ -344,7 +344,7 @@ class Paper { * * @returns {Paper} The parsed Paper. */ - parse( serialized ) { + static parse( serialized ) { // For ease of use, check if it is not already a Paper instance. if ( serialized instanceof Paper ) { return serialized; diff --git a/packages/yoastseo/src/worker/AnalysisWebWorker.js b/packages/yoastseo/src/worker/AnalysisWebWorker.js index 8a00bbc7a96..4ae39dc7cdb 100644 --- a/packages/yoastseo/src/worker/AnalysisWebWorker.js +++ b/packages/yoastseo/src/worker/AnalysisWebWorker.js @@ -1229,7 +1229,7 @@ export default class AnalysisWebWorker { return await Promise.all( keywordKeys.map( key => { this._relatedKeywords[ key ] = relatedKeywords[ key ]; - const relatedPaper = Paper.prototype.parse( { + const relatedPaper = Paper.parse( { ...paper.serialize(), keyword: this._relatedKeywords[ key ].keyword, synonyms: this._relatedKeywords[ key ].synonyms, diff --git a/packages/yoastseo/src/worker/transporter/parse.js b/packages/yoastseo/src/worker/transporter/parse.js index 6f8ce291820..e779ac8d795 100644 --- a/packages/yoastseo/src/worker/transporter/parse.js +++ b/packages/yoastseo/src/worker/transporter/parse.js @@ -31,7 +31,7 @@ export default function parse( thing ) { const thingIsObject = isObject( thing ); if ( thingIsObject && thing._parseClass && PARSE_CLASSES[ thing._parseClass ] ) { - return PARSE_CLASSES[ thing._parseClass ].prototype.parse( thing ); + return PARSE_CLASSES[ thing._parseClass ].parse( thing ); } if ( thingIsObject ) { From 4215e2c63ebb65f4700e412179dd8a7e155b64bf Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Tue, 16 Jan 2024 16:17:00 +0100 Subject: [PATCH 05/11] Remove irrelevant tests. Now parse method is a static method of the class, which means that it cannot be accessed from an object instance of that class. --- .../spec/languageProcessing/values/ClauseSpec.js | 6 ------ .../spec/languageProcessing/values/SentenceSpec.js | 10 ---------- 2 files changed, 16 deletions(-) diff --git a/packages/yoastseo/spec/languageProcessing/values/ClauseSpec.js b/packages/yoastseo/spec/languageProcessing/values/ClauseSpec.js index 96487b4dc36..436e286a7f5 100644 --- a/packages/yoastseo/spec/languageProcessing/values/ClauseSpec.js +++ b/packages/yoastseo/spec/languageProcessing/values/ClauseSpec.js @@ -40,11 +40,5 @@ describe( "a test for serializing and parsing a Clause class instance", function isPassive: false, participles: [], } ); - expect( mockClause.parse( mockClause.serialize() ) ).toEqual( { - _clauseText: "The cat is loved.", - _auxiliaries: [ "is" ], - _isPassive: false, - _participles: [], - } ); } ); } ); diff --git a/packages/yoastseo/spec/languageProcessing/values/SentenceSpec.js b/packages/yoastseo/spec/languageProcessing/values/SentenceSpec.js index 80bf8ad6d62..20689cd8bf4 100644 --- a/packages/yoastseo/spec/languageProcessing/values/SentenceSpec.js +++ b/packages/yoastseo/spec/languageProcessing/values/SentenceSpec.js @@ -64,15 +64,5 @@ describe( "Creates a sentence object", function() { isPassive: true, sentenceText: "Cats are adored.", } ); - expect( sentence.parse( sentence.serialize() ) ).toEqual( { - _clauses: [ - { _auxiliaries: [ "are" ], - _clauseText: "Cats are adored", - _isPassive: true, - _participles: [], - } ], - _isPassive: true, - _sentenceText: "Cats are adored.", - } ); } ); } ); From 9d83ec165a3f397fa27970491a5f19a439795916 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Tue, 16 Jan 2024 16:24:37 +0100 Subject: [PATCH 06/11] refactor AssessorPresenter to use class --- packages/yoastseo/src/scoring/renderers/AssessorPresenter.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/yoastseo/src/scoring/renderers/AssessorPresenter.js b/packages/yoastseo/src/scoring/renderers/AssessorPresenter.js index c79652732d4..9089ebda211 100644 --- a/packages/yoastseo/src/scoring/renderers/AssessorPresenter.js +++ b/packages/yoastseo/src/scoring/renderers/AssessorPresenter.js @@ -5,7 +5,6 @@ import { assessmentPresenterResult as template } from "../../snippetPreview/temp import scoreToRating from "../interpreters/scoreToRating.js"; import createConfig from "../../config/presenter.js"; - /** * Represents the AssessorPresenter. */ @@ -22,7 +21,7 @@ class AssessorPresenter { * * @constructor */ - construct( args ) { + constructor( args ) { this.keyword = args.keyword; this.assessor = args.assessor; this.output = args.targets.output; From 0bc4453899cb3053a37e55f1735075236d9b2e8f Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Tue, 16 Jan 2024 16:43:13 +0100 Subject: [PATCH 07/11] Fix the way we access the `parse` method --- packages/js/src/insights/initializer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js/src/insights/initializer.js b/packages/js/src/insights/initializer.js index d07463b6e7d..9c8b21169aa 100644 --- a/packages/js/src/insights/initializer.js +++ b/packages/js/src/insights/initializer.js @@ -23,7 +23,7 @@ const createUpdater = () => { * @returns {void} */ return () => { - const paper = Paper.prototype.parse( collectData() ); + const paper = Paper.parse( collectData() ); runResearch( "readingTime", paper ).then( response => setEstimatedReadingTime( response.result ) ); runResearch( "getFleschReadingScore", paper ).then( response => { From 65616ad81e7572c989832b95699cd70a772b874f Mon Sep 17 00:00:00 2001 From: Martijn van der Klis Date: Wed, 17 Apr 2024 13:45:20 +0200 Subject: [PATCH 08/11] Code scouting fixes --- .../src/scoring/renderers/AssessorPresenter.js | 15 +++++++-------- packages/yoastseo/src/values/AssessmentResult.js | 10 ++++------ packages/yoastseo/src/values/Mark.js | 2 +- packages/yoastseo/src/values/Paper.js | 4 ++-- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/packages/yoastseo/src/scoring/renderers/AssessorPresenter.js b/packages/yoastseo/src/scoring/renderers/AssessorPresenter.js index 9089ebda211..e0c48d43297 100644 --- a/packages/yoastseo/src/scoring/renderers/AssessorPresenter.js +++ b/packages/yoastseo/src/scoring/renderers/AssessorPresenter.js @@ -1,4 +1,3 @@ -/* eslint-disable tree-shaking/no-side-effects-in-initialization */ import { __ } from "@wordpress/i18n"; import { difference, forEach, isNumber, isObject, isUndefined } from "lodash-es"; import { assessmentPresenterResult as template } from "../../snippetPreview/templates.js"; @@ -158,7 +157,7 @@ class AssessorPresenter { } /** - * Excludes items from the results that are present in the exclude array. + * Excludes items from the results that are present in the `exclude` array. * * @param {Array} results Array containing the items to filter through. * @param {Array} exclude Array of results to exclude. @@ -169,20 +168,20 @@ class AssessorPresenter { } /** - * Sorts results based on their score property and always places items considered to be unsortable, at the top. + * Sorts results based on their score property and always places items considered to be non-sortable, at the top. * * @param {Array} results Array containing the results that need to be sorted. * @returns {Array} Array containing the sorted results. */ sort( results ) { - const unsortables = this.getUndefinedScores( results ); - const sortables = this.excludeFromResults( results, unsortables ); + const nonSortables = this.getUndefinedScores( results ); + const sortables = this.excludeFromResults( results, nonSortables ); sortables.sort( function( a, b ) { return a.score - b.score; } ); - return unsortables.concat( sortables ); + return nonSortables.concat( sortables ); } /** @@ -200,7 +199,7 @@ class AssessorPresenter { /** * Creates a rating object based on the item that is being passed. * - * @param {AssessmentResult} item The item to check and create a rating object from. + * @param {Object} item The item to check and create a rating object from. * @returns {Object} Object containing a parsed item, including a colored indicator. */ addRating( item ) { @@ -311,7 +310,7 @@ class AssessorPresenter { /** * Adds event handlers to the mark buttons * - * @param {Array} scores The list of rendered scores. + * @param {Object} scores The list of rendered scores. * * @returns {void} */ diff --git a/packages/yoastseo/src/values/AssessmentResult.js b/packages/yoastseo/src/values/AssessmentResult.js index a8ab557ad31..10ae3f01cfd 100644 --- a/packages/yoastseo/src/values/AssessmentResult.js +++ b/packages/yoastseo/src/values/AssessmentResult.js @@ -7,9 +7,7 @@ import Mark from "./Mark"; * * @returns {Array} A list of empty marks. */ -const emptyMarker = function() { - return []; -}; +const emptyMarker = () => []; /** * Represents the assessment result. @@ -172,12 +170,12 @@ class AssessmentResult { } /** - * Returns whether or not this result has a marker that can be used to mark for a given Paper + * Returns whether this result has a marker that can be used to mark for a given Paper. * - * @returns {boolean} Whether or this result has a marker. + * @returns {boolean} Whether this result has a marker. */ hasMarker() { - return this._hasMarks && this._marker !== this.emptyMarker; + return this._hasMarks && this._marker !== emptyMarker; } /** diff --git a/packages/yoastseo/src/values/Mark.js b/packages/yoastseo/src/values/Mark.js index 1f5383f52f9..629f5e0dad0 100644 --- a/packages/yoastseo/src/values/Mark.js +++ b/packages/yoastseo/src/values/Mark.js @@ -56,7 +56,7 @@ class Mark { /** * Returns the position information. * - * @returns {number} The position information. + * @returns {Object} The position information. */ getPosition() { return this._properties.position; diff --git a/packages/yoastseo/src/values/Paper.js b/packages/yoastseo/src/values/Paper.js index e9e385f8800..0f5fac7246f 100644 --- a/packages/yoastseo/src/values/Paper.js +++ b/packages/yoastseo/src/values/Paper.js @@ -61,7 +61,7 @@ class Paper { attributes.slug = attributes.url || attributes.slug; } - const onlyLetters = attributes.keyword.replace( /[‘’“”"'.?!:;,¿¡«»&*@#±^%|~`[\](){}⟨⟩<>/\\–\-\u2014\u00d7\u002b\u0026\s]/g, "" ); + const onlyLetters = attributes.keyword.replace( /[‘’“”"'.?!:;,¿¡«»&*@#±^%|~`[\](){}⟨⟩<>/\\–\-\u2014\u00d7\u002b\s]/g, "" ); if ( isEmpty( onlyLetters ) ) { attributes.keyword = defaultAttributes.keyword; @@ -181,7 +181,7 @@ class Paper { /** * Returns the title width in pixels, or an empty string of no title width in pixels is available. - * @returns {string} Returns the title + * @returns {number} Returns the title */ getTitleWidth() { return this._attributes.titleWidth; From bf0e33de98694e66765582ea64ec075cf613c380 Mon Sep 17 00:00:00 2001 From: Martijn van der Klis Date: Thu, 18 Apr 2024 09:47:53 +0200 Subject: [PATCH 09/11] Uses class syntax for App --- packages/yoastseo/src/app.js | 1235 +++++++++++++++++----------------- 1 file changed, 619 insertions(+), 616 deletions(-) diff --git a/packages/yoastseo/src/app.js b/packages/yoastseo/src/app.js index fece3aa9037..4460b24bfbd 100644 --- a/packages/yoastseo/src/app.js +++ b/packages/yoastseo/src/app.js @@ -1,7 +1,7 @@ import { setLocaleData } from "@wordpress/i18n"; import { debounce, defaultsDeep, forEach, isArray, isEmpty, isFunction, isObject, isString, isUndefined, merge, noop, throttle } from "lodash-es"; import MissingArgument from "./errors/missingArgument"; -import { measureTextWidth } from "./helpers/createMeasurementElement.js"; +import { measureTextWidth } from "./helpers"; import removeHtmlBlocks from "./languageProcessing/helpers/html/htmlParser.js"; import Pluggable from "./pluggable.js"; @@ -234,726 +234,729 @@ function verifyArguments( args ) { * * @constructor */ -var App = function( args ) { - if ( ! isObject( args ) ) { - args = {}; - } - - defaultsDeep( args, defaults ); - - verifyArguments( args ); +class App { + /** + * Creates a new App instance. + * @param {Object} args The arguments. + */ + constructor( args ) { + if ( ! isObject( args ) ) { + args = {}; + } - this.config = args; + defaultsDeep( args, defaults ); - if ( args.debouncedRefresh === true ) { - this.refresh = debounce( this.refresh.bind( this ), inputDebounceDelay ); - } - this._pureRefresh = throttle( this._pureRefresh.bind( this ), this.config.typeDelay ); + verifyArguments( args ); - this.callbacks = this.config.callbacks; + this.config = args; - setLocaleData( this.config.translations.locale_data[ "wordpress-seo" ], "wordpress-seo" ); + if ( args.debouncedRefresh === true ) { + this.refresh = debounce( this.refresh.bind( this ), inputDebounceDelay ); + } + this._pureRefresh = throttle( this._pureRefresh.bind( this ), this.config.typeDelay ); - this.initializeAssessors( args ); + this.callbacks = this.config.callbacks; - this.pluggable = new Pluggable( this ); + setLocaleData( this.config.translations.locale_data[ "wordpress-seo" ], "wordpress-seo" ); - this.getData(); + this.initializeAssessors( args ); - this.defaultOutputElement = this.getDefaultOutputElement( args ); + this.pluggable = new Pluggable( this ); - if ( this.defaultOutputElement !== "" ) { - this.showLoadingDialog(); - } + this.getData(); - if ( isValidSnippetPreview( args.snippetPreview ) ) { - this.snippetPreview = args.snippetPreview; + this.defaultOutputElement = this.getDefaultOutputElement( args ); - /* Hack to make sure the snippet preview always has a reference to this App. This way we solve the circular - dependency issue. In the future this should be solved by the snippet preview not having a reference to the - app.*/ - if ( this.snippetPreview.refObj !== this ) { - this.snippetPreview.refObj = this; + if ( this.defaultOutputElement !== "" ) { + this.showLoadingDialog(); } - } else if ( args.hasSnippetPreview ) { - this.snippetPreview = createDefaultSnippetPreview.call( this ); - } - - this._assessorOptions = { - useCornerStone: false, - }; - this.initSnippetPreview(); - this.initAssessorPresenters(); -}; + if ( isValidSnippetPreview( args.snippetPreview ) ) { + this.snippetPreview = args.snippetPreview; + + /* Hack to make sure the snippet preview always has a reference to this App. This way we solve the circular + dependency issue. In the future this should be solved by the snippet preview not having a reference to the + app.*/ + if ( this.snippetPreview.refObj !== this ) { + this.snippetPreview.refObj = this; + } + } else if ( args.hasSnippetPreview ) { + this.snippetPreview = createDefaultSnippetPreview.call( this ); + } -/** - * Returns the default output element based on which analyses are active. - * - * @param {Object} args The arguments passed to the App. - * @returns {string} The ID of the target that is active. - */ -App.prototype.getDefaultOutputElement = function( args ) { - if ( args.keywordAnalysisActive ) { - return args.targets.output; - } + this._assessorOptions = { + useCornerStone: false, + }; - if ( args.contentAnalysisActive ) { - return args.targets.contentOutput; + this.initSnippetPreview(); + this.initAssessorPresenters(); } - return ""; -}; - -/** - * Sets the assessors based on the assessor options and refreshes them. - * - * @param {Object} assessorOptions The specific options. - * @returns {void} - */ -App.prototype.changeAssessorOptions = function( assessorOptions ) { - this._assessorOptions = merge( this._assessorOptions, assessorOptions ); - - // Set the assessors based on the new assessor options. - this.seoAssessor = this.getSeoAssessor(); - this.contentAssessor = this.getContentAssessor(); - - // Refresh everything so the user sees the changes. - this.initAssessorPresenters(); - this.refresh(); -}; - -/** - * Returns an instance of the seo assessor to use. - * - * @returns {Assessor} The assessor instance. - */ -App.prototype.getSeoAssessor = function() { - const { useCornerStone } = this._assessorOptions; - - const assessor = useCornerStone ? this.cornerStoneSeoAssessor : this.defaultSeoAssessor; + /** + * Returns the default output element based on which analyses are active. + * + * @param {Object} args The arguments passed to the App. + * @returns {string} The ID of the target that is active. + */ + getDefaultOutputElement( args ) { + if ( args.keywordAnalysisActive ) { + return args.targets.output; + } - return assessor; -}; + if ( args.contentAnalysisActive ) { + return args.targets.contentOutput; + } -/** - * Returns an instance of the content assessor to use. - * - * @returns {Assessor} The assessor instance. - */ -App.prototype.getContentAssessor = function() { - const { useCornerStone } = this._assessorOptions; - return useCornerStone ? this.cornerStoneContentAssessor : this.defaultContentAssessor; -}; + return ""; + } + + /** + * Sets the assessors based on the assessor options and refreshes them. + * + * @param {Object} assessorOptions The specific options. + * @returns {void} + */ + changeAssessorOptions( assessorOptions ) { + this._assessorOptions = merge( this._assessorOptions, assessorOptions ); + + // Set the assessors based on the new assessor options. + this.seoAssessor = this.getSeoAssessor(); + this.contentAssessor = this.getContentAssessor(); + + // Refresh everything so the user sees the changes. + this.initAssessorPresenters(); + this.refresh(); + } + + /** + * Returns an instance of the seo assessor to use. + * + * @returns {Assessor} The assessor instance. + */ + getSeoAssessor() { + const { useCornerStone } = this._assessorOptions; + return useCornerStone ? this.cornerStoneSeoAssessor : this.defaultSeoAssessor; + } + + /** + * Returns an instance of the content assessor to use. + * + * @returns {Assessor} The assessor instance. + */ + getContentAssessor() { + const { useCornerStone } = this._assessorOptions; + return useCornerStone ? this.cornerStoneContentAssessor : this.defaultContentAssessor; + } + + /** + * Initializes assessors based on if the respective analysis is active. + * + * @param {Object} args The arguments passed to the App. + * @returns {void} + */ + initializeAssessors( args ) { + this.initializeSEOAssessor( args ); + this.initializeContentAssessor( args ); + } + + /** + * Initializes the SEO assessor. + * + * @param {Object} args The arguments passed to the App. + * @returns {void} + */ + initializeSEOAssessor( args ) { + if ( ! args.keywordAnalysisActive ) { + return; + } -/** - * Initializes assessors based on if the respective analysis is active. - * - * @param {Object} args The arguments passed to the App. - * @returns {void} - */ -App.prototype.initializeAssessors = function( args ) { - this.initializeSEOAssessor( args ); - this.initializeContentAssessor( args ); -}; + this.defaultSeoAssessor = new SEOAssessor( { marker: this.config.marker } ); + this.cornerStoneSeoAssessor = new CornerstoneSEOAssessor( { marker: this.config.marker } ); -/** - * Initializes the SEO assessor. - * - * @param {Object} args The arguments passed to the App. - * @returns {void} - */ -App.prototype.initializeSEOAssessor = function( args ) { - if ( ! args.keywordAnalysisActive ) { - return; + // Set the assessor + if ( isUndefined( args.seoAssessor ) ) { + this.seoAssessor = this.defaultSeoAssessor; + } else { + this.seoAssessor = args.seoAssessor; + } } - this.defaultSeoAssessor = new SEOAssessor( { marker: this.config.marker } ); - this.cornerStoneSeoAssessor = new CornerstoneSEOAssessor( { marker: this.config.marker } ); + /** + * Initializes the content assessor. + * + * @param {Object} args The arguments passed to the App. + * @returns {void} + */ + initializeContentAssessor( args ) { + if ( ! args.contentAnalysisActive ) { + return; + } - // Set the assessor - if ( isUndefined( args.seoAssessor ) ) { - this.seoAssessor = this.defaultSeoAssessor; - } else { - this.seoAssessor = args.seoAssessor; - } -}; + this.defaultContentAssessor = new ContentAssessor( { marker: this.config.marker, locale: this.config.locale } ); + this.cornerStoneContentAssessor = new CornerstoneContentAssessor( { marker: this.config.marker, locale: this.config.locale } ); -/** - * Initializes the content assessor. - * - * @param {Object} args The arguments passed to the App. - * @returns {void} - */ -App.prototype.initializeContentAssessor = function( args ) { - if ( ! args.contentAnalysisActive ) { - return; + // Set the content assessor + if ( isUndefined( args._contentAssessor ) ) { + this.contentAssessor = this.defaultContentAssessor; + } else { + this.contentAssessor = args._contentAssessor; + } } - this.defaultContentAssessor = new ContentAssessor( { marker: this.config.marker, locale: this.config.locale } ); - this.cornerStoneContentAssessor = new CornerstoneContentAssessor( { marker: this.config.marker, locale: this.config.locale } ); + /** + * Extend the config with defaults. + * + * @param {Object} args The arguments to be extended. + * @returns {Object} args The extended arguments. + */ + extendConfig( args ) { + args.sampleText = this.extendSampleText( args.sampleText ); + args.locale = args.locale || "en_US"; - // Set the content assessor - if ( isUndefined( args._contentAssessor ) ) { - this.contentAssessor = this.defaultContentAssessor; - } else { - this.contentAssessor = args._contentAssessor; + return args; } -}; -/** - * Extend the config with defaults. - * - * @param {Object} args The arguments to be extended. - * @returns {Object} args The extended arguments. - */ -App.prototype.extendConfig = function( args ) { - args.sampleText = this.extendSampleText( args.sampleText ); - args.locale = args.locale || "en_US"; + /** + * Extend sample text config with defaults. + * + * @param {Object} sampleText The sample text to be extended. + * @returns {Object} sampleText The extended sample text. + */ + extendSampleText( sampleText ) { + var defaultSampleText = defaults.sampleText; - return args; -}; + if ( isUndefined( sampleText ) ) { + return defaultSampleText; + } -/** - * Extend sample text config with defaults. - * - * @param {Object} sampleText The sample text to be extended. - * @returns {Object} sampleText The extended sample text. - */ -App.prototype.extendSampleText = function( sampleText ) { - var defaultSampleText = defaults.sampleText; + for ( var key in sampleText ) { + if ( isUndefined( sampleText[ key ] ) ) { + sampleText[ key ] = defaultSampleText[ key ]; + } + } - if ( isUndefined( sampleText ) ) { - return defaultSampleText; + return sampleText; } - for ( var key in sampleText ) { - if ( isUndefined( sampleText[ key ] ) ) { - sampleText[ key ] = defaultSampleText[ key ]; + /** + * Registers a custom data callback. + * + * @param {Function} callback The callback to register. + * + * @returns {void} + */ + registerCustomDataCallback( callback ) { + if ( ! this.callbacks.custom ) { + this.callbacks.custom = []; } - } - return sampleText; -}; - -/** - * Registers a custom data callback. - * - * @param {Function} callback The callback to register. - * - * @returns {void} - */ -App.prototype.registerCustomDataCallback = function( callback ) { - if ( ! this.callbacks.custom ) { - this.callbacks.custom = []; + if ( isFunction( callback ) ) { + this.callbacks.custom.push( callback ); + } } - if ( isFunction( callback ) ) { - this.callbacks.custom.push( callback ); - } -}; - -/** - * Retrieves data from the callbacks.getData and applies modification to store these in this.rawData. - * - * @returns {void} - */ -App.prototype.getData = function() { - this.rawData = this.callbacks.getData(); + /** + * Retrieves data from the callbacks.getData and applies modification to store these in this.rawData. + * + * @returns {void} + */ + getData() { + this.rawData = this.callbacks.getData(); - // Add the custom data to the raw data. - if ( isArray( this.callbacks.custom ) ) { - this.callbacks.custom.forEach( ( customCallback ) => { - const customData = customCallback(); + // Add the custom data to the raw data. + if ( isArray( this.callbacks.custom ) ) { + this.callbacks.custom.forEach( ( customCallback ) => { + const customData = customCallback(); - this.rawData = merge( this.rawData, customData ); - } ); - } - - if ( this.hasSnippetPreview() ) { - // Gets the data FOR the analyzer - var data = this.snippetPreview.getAnalyzerData(); + this.rawData = merge( this.rawData, customData ); + } ); + } - this.rawData.metaTitle = data.title; - this.rawData.url = data.url; - this.rawData.meta = data.metaDesc; - } + if ( this.hasSnippetPreview() ) { + // Gets the data FOR the analyzer + var data = this.snippetPreview.getAnalyzerData(); - if ( this.pluggable.loaded ) { - this.rawData.metaTitle = this.pluggable._applyModifications( "data_page_title", this.rawData.metaTitle ); - this.rawData.meta = this.pluggable._applyModifications( "data_meta_desc", this.rawData.meta ); - } + this.rawData.metaTitle = data.title; + this.rawData.url = data.url; + this.rawData.meta = data.metaDesc; + } - this.rawData.titleWidth = measureTextWidth( this.rawData.metaTitle ); + if ( this.pluggable.loaded ) { + this.rawData.metaTitle = this.pluggable._applyModifications( "data_page_title", this.rawData.metaTitle ); + this.rawData.meta = this.pluggable._applyModifications( "data_meta_desc", this.rawData.meta ); + } - this.rawData.locale = this.config.locale; -}; + this.rawData.titleWidth = measureTextWidth( this.rawData.metaTitle ); -/** - * Refreshes the analyzer and output of the analyzer, is debounced for a better experience. - * - * @returns {void} - */ -App.prototype.refresh = function() { - // Until all plugins are loaded, do not trigger a refresh. - if ( ! this.pluggable.loaded ) { - return; + this.rawData.locale = this.config.locale; } - this._pureRefresh(); -}; - -/** - * Refreshes the analyzer and output of the analyzer, is throttled to prevent performance issues. - * - * @returns {void} - * - * @private - */ -App.prototype._pureRefresh = function() { - this.getData(); - this.runAnalyzer(); -}; - -/** - * Determines whether or not this app has a snippet preview. - * - * @returns {boolean} Whether or not this app has a snippet preview. - */ -App.prototype.hasSnippetPreview = function() { - return this.snippetPreview !== null && ! isUndefined( this.snippetPreview ); -}; - -/** - * Initializes the snippet preview for this App. - * - * @returns {void} - */ -App.prototype.initSnippetPreview = function() { - if ( this.hasSnippetPreview() ) { - this.snippetPreview.renderTemplate(); - this.snippetPreview.callRegisteredEventBinder(); - this.snippetPreview.bindEvents(); - this.snippetPreview.init(); - } -}; + /** + * Refreshes the analyzer and output of the analyzer, is debounced for a better experience. + * + * @returns {void} + */ + refresh() { + // Until all plugins are loaded, do not trigger a refresh. + if ( ! this.pluggable.loaded ) { + return; + } -/** - * Initializes the assessor presenters for content and SEO. - * - * @returns {void} - */ -App.prototype.initAssessorPresenters = function() { - // Pass the assessor result through to the formatter - if ( ! isUndefined( this.config.targets.output ) ) { - this.seoAssessorPresenter = new AssessorPresenter( { - targets: { - output: this.config.targets.output, - }, - assessor: this.seoAssessor, - } ); + this._pureRefresh(); + } + + /** + * Refreshes the analyzer and output of the analyzer, is throttled to prevent performance issues. + * + * @returns {void} + * + * @private + */ + _pureRefresh() { + this.getData(); + this.runAnalyzer(); + } + + /** + * Determines whether or not this app has a snippet preview. + * + * @returns {boolean} Whether or not this app has a snippet preview. + */ + hasSnippetPreview() { + return this.snippetPreview !== null && ! isUndefined( this.snippetPreview ); + } + + /** + * Initializes the snippet preview for this App. + * + * @returns {void} + */ + initSnippetPreview() { + if ( this.hasSnippetPreview() ) { + this.snippetPreview.renderTemplate(); + this.snippetPreview.callRegisteredEventBinder(); + this.snippetPreview.bindEvents(); + this.snippetPreview.init(); + } } - if ( ! isUndefined( this.config.targets.contentOutput ) ) { + /** + * Initializes the assessor presenters for content and SEO. + * + * @returns {void} + */ + initAssessorPresenters() { // Pass the assessor result through to the formatter - this.contentAssessorPresenter = new AssessorPresenter( { - targets: { - output: this.config.targets.contentOutput, - }, - assessor: this.contentAssessor, - } ); - } -}; - -/** - * Binds the refresh function to the input of the targetElement on the page. - * - * @returns {void} - */ -App.prototype.bindInputEvent = function() { - for ( var i = 0; i < this.config.elementTarget.length; i++ ) { - var elem = document.getElementById( this.config.elementTarget[ i ] ); - elem.addEventListener( "input", this.refresh.bind( this ) ); - } -}; + if ( ! isUndefined( this.config.targets.output ) ) { + this.seoAssessorPresenter = new AssessorPresenter( { + targets: { + output: this.config.targets.output, + }, + assessor: this.seoAssessor, + } ); + } -/** - * Runs the rerender function of the snippetPreview if that object is defined. - * - * @returns {void} - */ -App.prototype.reloadSnippetText = function() { - if ( this.hasSnippetPreview() ) { - this.snippetPreview.reRender(); + if ( ! isUndefined( this.config.targets.contentOutput ) ) { + // Pass the assessor result through to the formatter + this.contentAssessorPresenter = new AssessorPresenter( { + targets: { + output: this.config.targets.contentOutput, + }, + assessor: this.contentAssessor, + } ); + } } -}; - -/** - * Sets the startTime timestamp. - * - * @returns {void} - */ -App.prototype.startTime = function() { - this.startTimestamp = new Date().getTime(); -}; -/** - * Sets the endTime timestamp and compares with startTime to determine typeDelayincrease. - * - * @returns {void} - */ -App.prototype.endTime = function() { - this.endTimestamp = new Date().getTime(); - if ( this.endTimestamp - this.startTimestamp > this.config.typeDelay ) { - if ( this.config.typeDelay < ( this.config.maxTypeDelay - this.config.typeDelayStep ) ) { - this.config.typeDelay += this.config.typeDelayStep; + /** + * Binds the refresh function to the input of the targetElement on the page. + * + * @returns {void} + */ + bindInputEvent() { + for ( var i = 0; i < this.config.elementTarget.length; i++ ) { + var elem = document.getElementById( this.config.elementTarget[ i ] ); + elem.addEventListener( "input", this.refresh.bind( this ) ); } } -}; -/** - * Inits a new pageAnalyzer with the inputs from the getInput function and calls the scoreFormatter - * to format outputs. - * - * @returns {void} - */ -App.prototype.runAnalyzer = function() { - if ( this.pluggable.loaded === false ) { - return; + /** + * Runs the rerender function of the snippetPreview if that object is defined. + * + * @returns {void} + */ + reloadSnippetText() { + if ( this.hasSnippetPreview() ) { + this.snippetPreview.reRender(); + } } - if ( this.config.dynamicDelay ) { - this.startTime(); + /** + * Sets the startTime timestamp. + * + * @returns {void} + */ + startTime() { + this.startTimestamp = new Date().getTime(); + } + + /** + * Sets the endTime timestamp and compares with startTime to determine typeDelayincrease. + * + * @returns {void} + */ + endTime() { + this.endTimestamp = new Date().getTime(); + if ( this.endTimestamp - this.startTimestamp > this.config.typeDelay ) { + if ( this.config.typeDelay < ( this.config.maxTypeDelay - this.config.typeDelayStep ) ) { + this.config.typeDelay += this.config.typeDelayStep; + } + } } - this.analyzerData = this.modifyData( this.rawData ); - - if ( this.hasSnippetPreview() ) { - this.snippetPreview.refresh(); - } + /** + * Inits a new pageAnalyzer with the inputs from the getInput function and calls the scoreFormatter + * to format outputs. + * + * @returns {void} + */ + runAnalyzer() { + if ( this.pluggable.loaded === false ) { + return; + } - let text = this.analyzerData.text; + if ( this.config.dynamicDelay ) { + this.startTime(); + } - // Insert HTML stripping code - text = removeHtmlBlocks( text ); + this.analyzerData = this.modifyData( this.rawData ); - let titleWidth = this.analyzerData.titleWidth; - if ( this.hasSnippetPreview() ) { - titleWidth = this.snippetPreview.getTitleWidth(); - } + if ( this.hasSnippetPreview() ) { + this.snippetPreview.refresh(); + } - // Create a paper object for the Researcher - this.paper = new Paper( text, { - keyword: this.analyzerData.keyword, - synonyms: this.analyzerData.synonyms, - description: this.analyzerData.meta, - slug: this.analyzerData.slug, - title: this.analyzerData.metaTitle, - titleWidth: titleWidth, - locale: this.config.locale, - permalink: this.analyzerData.permalink, - } ); + let text = this.analyzerData.text; - this.config.researcher.setPaper( this.paper ); + // Insert HTML stripping code + text = removeHtmlBlocks( text ); - this.runKeywordAnalysis(); + let titleWidth = this.analyzerData.titleWidth; + if ( this.hasSnippetPreview() ) { + titleWidth = this.snippetPreview.getTitleWidth(); + } - this.runContentAnalysis(); + // Create a paper object for the Researcher + this.paper = new Paper( text, { + keyword: this.analyzerData.keyword, + synonyms: this.analyzerData.synonyms, + description: this.analyzerData.meta, + slug: this.analyzerData.slug, + title: this.analyzerData.metaTitle, + titleWidth: titleWidth, + locale: this.config.locale, + permalink: this.analyzerData.permalink, + } ); - this._renderAnalysisResults(); + this.config.researcher.setPaper( this.paper ); - if ( this.config.dynamicDelay ) { - this.endTime(); - } + this.runKeywordAnalysis(); - if ( this.hasSnippetPreview() ) { - this.snippetPreview.reRender(); - } -}; + this.runContentAnalysis(); -/** - * Runs the keyword analysis and calls the appropriate callbacks. - * - * @returns {void} - */ -App.prototype.runKeywordAnalysis = function() { - if ( this.config.keywordAnalysisActive ) { - this.seoAssessor.assess( this.paper ); - const overallSeoScore = this.seoAssessor.calculateOverallScore(); + this._renderAnalysisResults(); - if ( ! isUndefined( this.callbacks.updatedKeywordsResults ) ) { - this.callbacks.updatedKeywordsResults( this.seoAssessor.results, overallSeoScore ); + if ( this.config.dynamicDelay ) { + this.endTime(); } - if ( ! isUndefined( this.callbacks.saveScores ) ) { - this.callbacks.saveScores( overallSeoScore, this.seoAssessorPresenter ); + if ( this.hasSnippetPreview() ) { + this.snippetPreview.reRender(); } } -}; -/** - * Runs the content analysis and calls the appropriate callbacks. - * - * @returns {void} - */ -App.prototype.runContentAnalysis = function() { - if ( this.config.contentAnalysisActive ) { - this.contentAssessor.assess( this.paper ); - const overallContentScore = this.contentAssessor.calculateOverallScore(); + /** + * Runs the keyword analysis and calls the appropriate callbacks. + * + * @returns {void} + */ + runKeywordAnalysis() { + if ( this.config.keywordAnalysisActive ) { + this.seoAssessor.assess( this.paper ); + const overallSeoScore = this.seoAssessor.calculateOverallScore(); - if ( ! isUndefined( this.callbacks.updatedContentResults ) ) { - this.callbacks.updatedContentResults( this.contentAssessor.results, overallContentScore ); - } + if ( ! isUndefined( this.callbacks.updatedKeywordsResults ) ) { + this.callbacks.updatedKeywordsResults( this.seoAssessor.results, overallSeoScore ); + } - if ( ! isUndefined( this.callbacks.saveContentScore ) ) { - this.callbacks.saveContentScore( overallContentScore, this.contentAssessorPresenter ); + if ( ! isUndefined( this.callbacks.saveScores ) ) { + this.callbacks.saveScores( overallSeoScore, this.seoAssessorPresenter ); + } } } -}; - -/** - * Modifies the data with plugins before it is sent to the analyzer. - * - * @param {Object} data The data to be modified. - * @returns {Object} The data with the applied modifications. - */ -App.prototype.modifyData = function( data ) { - // Copy rawdata to lose object reference. - data = JSON.parse( JSON.stringify( data ) ); - data.text = this.pluggable._applyModifications( "content", data.text ); - data.metaTitle = this.pluggable._applyModifications( "title", data.metaTitle ); + /** + * Runs the content analysis and calls the appropriate callbacks. + * + * @returns {void} + */ + runContentAnalysis() { + if ( this.config.contentAnalysisActive ) { + this.contentAssessor.assess( this.paper ); + const overallContentScore = this.contentAssessor.calculateOverallScore(); - return data; -}; - -/** - * Function to fire the analyzer when all plugins are loaded, removes the loading dialog. - * - * @returns {void} - */ -App.prototype.pluginsLoaded = function() { - this.removeLoadingDialog(); - this.refresh(); -}; + if ( ! isUndefined( this.callbacks.updatedContentResults ) ) { + this.callbacks.updatedContentResults( this.contentAssessor.results, overallContentScore ); + } -/** - * Shows the loading dialog which shows the loading of the plugins. - * - * @returns {void} - */ -App.prototype.showLoadingDialog = function() { - var outputElement = document.getElementById( this.defaultOutputElement ); - - if ( this.defaultOutputElement !== "" && ! isEmpty( outputElement ) ) { - var dialogDiv = document.createElement( "div" ); - dialogDiv.className = "YoastSEO_msg"; - dialogDiv.id = "YoastSEO-plugin-loading"; - document.getElementById( this.defaultOutputElement ).appendChild( dialogDiv ); + if ( ! isUndefined( this.callbacks.saveContentScore ) ) { + this.callbacks.saveContentScore( overallContentScore, this.contentAssessorPresenter ); + } + } } -}; -/** - * Updates the loading plugins. Uses the plugins as arguments to show which plugins are loading. - * - * @param {Object} plugins The plugins to be parsed into the dialog. - * @returns {void} - */ -App.prototype.updateLoadingDialog = function( plugins ) { - var outputElement = document.getElementById( this.defaultOutputElement ); - - if ( this.defaultOutputElement === "" || isEmpty( outputElement ) ) { - return; + /** + * Modifies the data with plugins before it is sent to the analyzer. + * + * @param {Object} data The data to be modified. + * @returns {Object} The data with the applied modifications. + */ + modifyData( data ) { + // Copy rawdata to lose object reference. + data = JSON.parse( JSON.stringify( data ) ); + + data.text = this.pluggable._applyModifications( "content", data.text ); + data.metaTitle = this.pluggable._applyModifications( "title", data.metaTitle ); + + return data; + } + + /** + * Function to fire the analyzer when all plugins are loaded, removes the loading dialog. + * + * @returns {void} + */ + pluginsLoaded() { + this.removeLoadingDialog(); + this.refresh(); + } + + /** + * Shows the loading dialog which shows the loading of the plugins. + * + * @returns {void} + */ + showLoadingDialog() { + var outputElement = document.getElementById( this.defaultOutputElement ); + + if ( this.defaultOutputElement !== "" && ! isEmpty( outputElement ) ) { + var dialogDiv = document.createElement( "div" ); + dialogDiv.className = "YoastSEO_msg"; + dialogDiv.id = "YoastSEO-plugin-loading"; + document.getElementById( this.defaultOutputElement ).appendChild( dialogDiv ); + } } - var dialog = document.getElementById( "YoastSEO-plugin-loading" ); - dialog.textContent = ""; + /** + * Updates the loading plugins. Uses the plugins as arguments to show which plugins are loading. + * + * @param {Object} plugins The plugins to be parsed into the dialog. + * @returns {void} + */ + updateLoadingDialog( plugins ) { + var outputElement = document.getElementById( this.defaultOutputElement ); - forEach( plugins, function( plugin, pluginName ) { - dialog.innerHTML += "" + pluginName + "" + plugin.status + "
"; - } ); + if ( this.defaultOutputElement === "" || isEmpty( outputElement ) ) { + return; + } - dialog.innerHTML += ""; -}; + var dialog = document.getElementById( "YoastSEO-plugin-loading" ); + dialog.textContent = ""; -/** - * Removes the pluging load dialog. - * - * @returns {void} - */ -App.prototype.removeLoadingDialog = function() { - var outputElement = document.getElementById( this.defaultOutputElement ); - var loadingDialog = document.getElementById( "YoastSEO-plugin-loading" ); + forEach( plugins, function( plugin, pluginName ) { + dialog.innerHTML += "" + pluginName + "" + plugin.status + "
"; + } ); - if ( ( this.defaultOutputElement !== "" && ! isEmpty( outputElement ) ) && ! isEmpty( loadingDialog ) ) { - document.getElementById( this.defaultOutputElement ).removeChild( document.getElementById( "YoastSEO-plugin-loading" ) ); + dialog.innerHTML += ""; } -}; -// ***** PLUGGABLE PUBLIC DSL ***** // + /** + * Removes the pluging load dialog. + * + * @returns {void} + */ + removeLoadingDialog() { + var outputElement = document.getElementById( this.defaultOutputElement ); + var loadingDialog = document.getElementById( "YoastSEO-plugin-loading" ); -/** - * Delegates to `YoastSEO.app.pluggable.registerPlugin` - * - * @param {string} pluginName The name of the plugin to be registered. - * @param {object} options The options object. - * @param {string} options.status The status of the plugin being registered. Can either be "loading" or "ready". - * @returns {boolean} Whether or not it was successfully registered. - */ -App.prototype.registerPlugin = function( pluginName, options ) { - return this.pluggable._registerPlugin( pluginName, options ); -}; - -/** - * Delegates to `YoastSEO.app.pluggable.ready` - * - * @param {string} pluginName The name of the plugin to check. - * @returns {boolean} Whether or not the plugin is ready. - */ -App.prototype.pluginReady = function( pluginName ) { - return this.pluggable._ready( pluginName ); -}; - -/** - * Delegates to `YoastSEO.app.pluggable.reloaded` - * - * @param {string} pluginName The name of the plugin to reload - * @returns {boolean} Whether or not the plugin was reloaded. - */ -App.prototype.pluginReloaded = function( pluginName ) { - return this.pluggable._reloaded( pluginName ); -}; - -/** - * Delegates to `YoastSEO.app.pluggable.registerModification`. - * - * @param {string} modification The name of the filter - * @param {function} callable The callable function - * @param {string} pluginName The plugin that is registering the modification. - * @param {number} [priority] Used to specify the order in which the callables associated with a particular filter are called. - * Lower numbers correspond with earlier execution. - * - * @returns {boolean} Whether or not the modification was successfully registered. - */ -App.prototype.registerModification = function( modification, callable, pluginName, priority ) { - return this.pluggable._registerModification( modification, callable, pluginName, priority ); -}; - -/** - * Registers a custom assessment for use in the analyzer, this will result in a new line in the analyzer results. - * The function needs to use the assessmentresult to return an result based on the contents of the page/posts. - * - * Score 0 results in a grey circle if it is not explicitly set by using setscore - * Scores 0, 1, 2, 3 and 4 result in a red circle - * Scores 6 and 7 result in a yellow circle - * Scores 8, 9 and 10 result in a green circle - * - * @param {string} name Name of the test. - * @param {function} assessment The assessment to run - * @param {string} pluginName The plugin that is registering the test. - * @returns {boolean} Whether or not the test was successfully registered. - */ -App.prototype.registerAssessment = function( name, assessment, pluginName ) { - if ( ! isUndefined( this.seoAssessor ) ) { - return this.pluggable._registerAssessment( this.defaultSeoAssessor, name, assessment, pluginName ) && - this.pluggable._registerAssessment( this.cornerStoneSeoAssessor, name, assessment, pluginName ); + if ( ( this.defaultOutputElement !== "" && ! isEmpty( outputElement ) ) && ! isEmpty( loadingDialog ) ) { + document.getElementById( this.defaultOutputElement ).removeChild( document.getElementById( "YoastSEO-plugin-loading" ) ); + } } -}; -/** - * Disables markers visually in the UI. - * - * @returns {void} - */ -App.prototype.disableMarkers = function() { - if ( ! isUndefined( this.seoAssessorPresenter ) ) { - this.seoAssessorPresenter.disableMarker(); + // ***** PLUGGABLE PUBLIC DSL ***** // + /** + * Delegates to `YoastSEO.app.pluggable.registerPlugin` + * + * @param {string} pluginName The name of the plugin to be registered. + * @param {object} options The options object. + * @param {string} options.status The status of the plugin being registered. Can either be "loading" or "ready". + * @returns {boolean} Whether or not it was successfully registered. + */ + registerPlugin( pluginName, options ) { + return this.pluggable._registerPlugin( pluginName, options ); + } + + /** + * Delegates to `YoastSEO.app.pluggable.ready` + * + * @param {string} pluginName The name of the plugin to check. + * @returns {boolean} Whether or not the plugin is ready. + */ + pluginReady( pluginName ) { + return this.pluggable._ready( pluginName ); + } + + /** + * Delegates to `YoastSEO.app.pluggable.reloaded` + * + * @param {string} pluginName The name of the plugin to reload + * @returns {boolean} Whether or not the plugin was reloaded. + */ + pluginReloaded( pluginName ) { + return this.pluggable._reloaded( pluginName ); + } + + /** + * Delegates to `YoastSEO.app.pluggable.registerModification`. + * + * @param {string} modification The name of the filter + * @param {function} callable The callable function + * @param {string} pluginName The plugin that is registering the modification. + * @param {number} [priority] Used to specify the order in which the callables associated with a particular filter are called. + * Lower numbers correspond with earlier execution. + * + * @returns {boolean} Whether or not the modification was successfully registered. + */ + registerModification( modification, callable, pluginName, priority ) { + return this.pluggable._registerModification( modification, callable, pluginName, priority ); + } + + /** + * Registers a custom assessment for use in the analyzer, this will result in a new line in the analyzer results. + * The function needs to use the assessmentresult to return an result based on the contents of the page/posts. + * + * Score 0 results in a grey circle if it is not explicitly set by using setscore + * Scores 0, 1, 2, 3 and 4 result in a red circle + * Scores 6 and 7 result in a yellow circle + * Scores 8, 9 and 10 result in a green circle + * + * @param {string} name Name of the test. + * @param {function} assessment The assessment to run + * @param {string} pluginName The plugin that is registering the test. + * @returns {boolean} Whether or not the test was successfully registered. + */ + registerAssessment( name, assessment, pluginName ) { + if ( ! isUndefined( this.seoAssessor ) ) { + return this.pluggable._registerAssessment( this.defaultSeoAssessor, name, assessment, pluginName ) && + this.pluggable._registerAssessment( this.cornerStoneSeoAssessor, name, assessment, pluginName ); + } } - if ( ! isUndefined( this.contentAssessorPresenter ) ) { - this.contentAssessorPresenter.disableMarker(); - } -}; + /** + * Disables markers visually in the UI. + * + * @returns {void} + */ + disableMarkers() { + if ( ! isUndefined( this.seoAssessorPresenter ) ) { + this.seoAssessorPresenter.disableMarker(); + } -/** - * Renders the content and keyword analysis results. - * - * @returns {void} - */ -App.prototype._renderAnalysisResults = function() { - if ( this.config.contentAnalysisActive && ! isUndefined( this.contentAssessorPresenter ) ) { - this.contentAssessorPresenter.renderIndividualRatings(); - } - if ( this.config.keywordAnalysisActive && ! isUndefined( this.seoAssessorPresenter ) ) { - this.seoAssessorPresenter.setKeyword( this.paper.getKeyword() ); - this.seoAssessorPresenter.render(); + if ( ! isUndefined( this.contentAssessorPresenter ) ) { + this.contentAssessorPresenter.disableMarker(); + } } -}; -// Deprecated functions -/** - * The analyzeTimer calls the checkInputs function with a delay, so the function won't be executed - * at every keystroke checks the reference object, so this function can be called from anywhere, - * without problems with different scopes. - * - * @deprecated: 1.3 - Use this.refresh() instead. - * - * @returns {void} - */ -App.prototype.analyzeTimer = function() { - this.refresh(); -}; - -/** - * Registers a custom test for use in the analyzer, this will result in a new line in the analyzer results. The function - * has to return a result based on the contents of the page/posts. - * - * The scoring object is a special object with definitions about how to translate a result from your analysis function - * to a SEO score. - * - * Negative scores result in a red circle - * Scores 1, 2, 3, 4 and 5 result in a orange circle - * Scores 6 and 7 result in a yellow circle - * Scores 8, 9 and 10 result in a red circle - * - * @returns {void} - * - * @deprecated since version 1.2 - */ -App.prototype.registerTest = function() { - console.error( "This function is deprecated, please use registerAssessment" ); -}; - -/** - * Creates the elements for the snippetPreview - * - * @deprecated Don't create a snippet preview using this method, create it directly using the prototype and pass it as - * an argument instead. - * - * @returns {void} - */ -App.prototype.createSnippetPreview = function() { - this.snippetPreview = createDefaultSnippetPreview.call( this ); - this.initSnippetPreview(); -}; + /** + * Renders the content and keyword analysis results. + * + * @returns {void} + */ + _renderAnalysisResults() { + if ( this.config.contentAnalysisActive && ! isUndefined( this.contentAssessorPresenter ) ) { + this.contentAssessorPresenter.renderIndividualRatings(); + } + if ( this.config.keywordAnalysisActive && ! isUndefined( this.seoAssessorPresenter ) ) { + this.seoAssessorPresenter.setKeyword( this.paper.getKeyword() ); + this.seoAssessorPresenter.render(); + } + } -/** - * Switches between the cornerstone and default assessors. - * - * @deprecated 1.35.0 - Use changeAssessorOption instead. - * - * @param {boolean} useCornerStone True when cornerstone should be used. - * - * @returns {void} - */ -App.prototype.switchAssessors = function( useCornerStone ) { - // eslint-disable-next-line no-console - console.warn( "Switch assessor is deprecated since YoastSEO.js version 1.35.0" ); + // Deprecated functions + /** + * The analyzeTimer calls the checkInputs function with a delay, so the function won't be executed + * at every keystroke checks the reference object, so this function can be called from anywhere, + * without problems with different scopes. + * + * @deprecated: 1.3 - Use this.refresh() instead. + * + * @returns {void} + */ + analyzeTimer() { + this.refresh(); + } + + /** + * Registers a custom test for use in the analyzer, this will result in a new line in the analyzer results. The function + * has to return a result based on the contents of the page/posts. + * + * The scoring object is a special object with definitions about how to translate a result from your analysis function + * to a SEO score. + * + * Negative scores result in a red circle + * Scores 1, 2, 3, 4 and 5 result in a orange circle + * Scores 6 and 7 result in a yellow circle + * Scores 8, 9 and 10 result in a red circle + * + * @returns {void} + * + * @deprecated since version 1.2 + */ + registerTest() { + console.error( "This function is deprecated, please use registerAssessment" ); + } + + /** + * Creates the elements for the snippetPreview + * + * @deprecated Don't create a snippet preview using this method, create it directly using the prototype and pass it as + * an argument instead. + * + * @returns {void} + */ + createSnippetPreview() { + this.snippetPreview = createDefaultSnippetPreview.call( this ); + this.initSnippetPreview(); + } + + /** + * Switches between the cornerstone and default assessors. + * + * @deprecated 1.35.0 - Use changeAssessorOption instead. + * + * @param {boolean} useCornerStone True when cornerstone should be used. + * + * @returns {void} + */ + switchAssessors( useCornerStone ) { + // eslint-disable-next-line no-console + console.warn( "Switch assessor is deprecated since YoastSEO.js version 1.35.0" ); + + this.changeAssessorOptions( { + useCornerStone, + } ); + } +} - this.changeAssessorOptions( { - useCornerStone, - } ); -}; export default App; From 8b8e18ef248daa8924c04c701d87177f671adbb1 Mon Sep 17 00:00:00 2001 From: Martijn van der Klis Date: Thu, 18 Apr 2024 10:15:28 +0200 Subject: [PATCH 10/11] Uses class syntax for assessors (first pass) --- packages/yoastseo/src/scoring/assessor.js | 505 +++++++++--------- .../yoastseo/src/scoring/contentAssessor.js | 271 +++++----- .../scoring/cornerstone/contentAssessor.js | 80 ++- .../cornerstone/relatedKeywordAssessor.js | 59 +- .../src/scoring/cornerstone/seoAssessor.js | 136 +++-- .../src/scoring/inclusiveLanguageAssessor.js | 13 +- .../src/scoring/relatedKeywordAssessor.js | 49 +- .../scoring/relatedKeywordTaxonomyAssessor.js | 46 +- packages/yoastseo/src/scoring/seoAssessor.js | 77 ++- .../cornerstone/contentAssessor.js | 98 ++-- .../yoastseo/src/scoring/taxonomyAssessor.js | 16 +- 11 files changed, 648 insertions(+), 702 deletions(-) diff --git a/packages/yoastseo/src/scoring/assessor.js b/packages/yoastseo/src/scoring/assessor.js index fedab386dce..a912d0c28ac 100644 --- a/packages/yoastseo/src/scoring/assessor.js +++ b/packages/yoastseo/src/scoring/assessor.js @@ -12,292 +12,293 @@ import { build } from "../parse/build"; const ScoreRating = 9; /** - * Creates the Assessor. - * - * @param {Researcher} researcher The researcher to use in the assessor. - * @param {Object?} options The options for this assessor. - * @param {Function} options.marker The marker to pass the list of marks to. - * - * @constructor + * The Assessor is a base class for all assessors. */ -const Assessor = function( researcher, options ) { - this.type = "assessor"; - this.setResearcher( researcher ); - this._assessments = []; - - this._options = options || {}; -}; - -/** - * Checks if the researcher is defined and sets it. - * - * @param {Researcher} researcher The researcher to use in the assessor. - * - * @throws {MissingArgument} Parameter needs to be a valid researcher object. - * @returns {void} - */ -Assessor.prototype.setResearcher = function( researcher ) { - if ( isUndefined( researcher ) ) { - throw new MissingArgument( "The assessor requires a researcher." ); +class Assessor { + /** + * Creates a new Assessor instance. + * @param {Researcher} researcher The researcher to use. + * @param {Object} [options] The assessor options. + */ + constructor( researcher, options ) { + this.type = "assessor"; + this.setResearcher( researcher ); + this._assessments = []; + + this._options = options || {}; } - this._researcher = researcher; -}; -/** - * Gets all available assessments. - * @returns {object} assessment - */ -Assessor.prototype.getAvailableAssessments = function() { - return this._assessments; -}; - -/** - * Checks whether the Assessment is applicable. - * - * @param {Object} assessment The Assessment object that needs to be checked. - * @param {Paper} paper The Paper object to check against. - * @param {Researcher} [researcher] The Researcher object containing additional information. - * @returns {boolean} Whether or not the Assessment is applicable. - */ -Assessor.prototype.isApplicable = function( assessment, paper, researcher ) { - if ( assessment.hasOwnProperty( "isApplicable" ) || typeof assessment.isApplicable === "function" ) { - return assessment.isApplicable( paper, researcher ); + /** + * Checks if the researcher is defined and sets it. + * + * @param {Researcher} researcher The researcher to use in the assessor. + * + * @throws {MissingArgument} Parameter needs to be a valid researcher object. + * @returns {void} + */ + setResearcher( researcher ) { + if ( isUndefined( researcher ) ) { + throw new MissingArgument( "The assessor requires a researcher." ); + } + this._researcher = researcher; } - return true; -}; - -/** - * Determines whether an assessment has a marker. - * - * @param {Object} assessment The assessment to check for. - * @returns {boolean} Whether or not the assessment has a marker. - */ -Assessor.prototype.hasMarker = function( assessment ) { - return isFunction( this._options.marker ) && ( assessment.hasOwnProperty( "getMarks" ) || typeof assessment.getMarks === "function" ); -}; - -/** - * Returns the specific marker for this assessor. - * - * @returns {Function} The specific marker for this assessor. - */ -Assessor.prototype.getSpecificMarker = function() { - return this._options.marker; -}; + /** + * Gets all available assessments. + * @returns {object} assessment + */ + getAvailableAssessments() { + return this._assessments; + } -/** - * Returns the paper that was most recently assessed. - * - * @returns {Paper} The paper that was most recently assessed. - */ -Assessor.prototype.getPaper = function() { - return this._lastPaper; -}; + /** + * Checks whether the Assessment is applicable. + * + * @param {Object} assessment The Assessment object that needs to be checked. + * @param {Paper} paper The Paper object to check against. + * @param {Researcher} [researcher] The Researcher object containing additional information. + * @returns {boolean} Whether or not the Assessment is applicable. + */ + isApplicable( assessment, paper, researcher ) { + if ( assessment.hasOwnProperty( "isApplicable" ) || typeof assessment.isApplicable === "function" ) { + return assessment.isApplicable( paper, researcher ); + } -/** - * Returns the marker for a given assessment, composes the specific marker with the assessment getMarks function. - * - * @param {Object} assessment The assessment for which we are retrieving the composed marker. - * @param {Paper} paper The paper to retrieve the marker for. - * @param {Researcher} researcher The researcher for the paper. - * @returns {Function} A function that can mark the given paper according to the given assessment. - */ -Assessor.prototype.getMarker = function( assessment, paper, researcher ) { - const specificMarker = this._options.marker; + return true; + } - return function() { - let marks = assessment.getMarks( paper, researcher ); - marks = removeDuplicateMarks( marks ); + /** + * Determines whether an assessment has a marker. + * + * @param {Object} assessment The assessment to check for. + * @returns {boolean} Whether or not the assessment has a marker. + */ + hasMarker( assessment ) { + return isFunction( this._options.marker ) && ( assessment.hasOwnProperty( "getMarks" ) || typeof assessment.getMarks === "function" ); + } - specificMarker( paper, marks ); - }; -}; + /** + * Returns the specific marker for this assessor. + * + * @returns {Function} The specific marker for this assessor. + */ + getSpecificMarker() { + return this._options.marker; + } -/** - * Runs the researches defined in the task list or the default researches. - * - * @param {Paper} paper The paper to run assessments on. - * @returns {void} - */ -Assessor.prototype.assess = function( paper ) { - this._researcher.setPaper( paper ); + /** + * Returns the paper that was most recently assessed. + * + * @returns {Paper} The paper that was most recently assessed. + */ + getPaper() { + return this._lastPaper; + } - const languageProcessor = new LanguageProcessor( this._researcher ); - const shortcodes = paper._attributes && paper._attributes.shortcodes; - paper.setTree( build( paper, languageProcessor, shortcodes ) ); + /** + * Returns the marker for a given assessment, composes the specific marker with the assessment getMarks function. + * + * @param {Object} assessment The assessment for which we are retrieving the composed marker. + * @param {Paper} paper The paper to retrieve the marker for. + * @param {Researcher} researcher The researcher for the paper. + * @returns {Function} A function that can mark the given paper according to the given assessment. + */ + getMarker( assessment, paper, researcher ) { + const specificMarker = this._options.marker; + + return function() { + let marks = assessment.getMarks( paper, researcher ); + marks = removeDuplicateMarks( marks ); + + specificMarker( paper, marks ); + }; + } - let assessments = this.getAvailableAssessments(); - this.results = []; + /** + * Runs the researches defined in the task list or the default researches. + * + * @param {Paper} paper The paper to run assessments on. + * @returns {void} + */ + assess( paper ) { + this._researcher.setPaper( paper ); - assessments = filter( assessments, function( assessment ) { - return this.isApplicable( assessment, paper, this._researcher ); - }.bind( this ) ); + const languageProcessor = new LanguageProcessor( this._researcher ); + const shortcodes = paper._attributes && paper._attributes.shortcodes; + paper.setTree( build( paper, languageProcessor, shortcodes ) ); - this.setHasMarkers( false ); - this.results = map( assessments, this.executeAssessment.bind( this, paper, this._researcher ) ); + let assessments = this.getAvailableAssessments(); + this.results = []; - this._lastPaper = paper; -}; + assessments = filter( assessments, function( assessment ) { + return this.isApplicable( assessment, paper, this._researcher ); + }.bind( this ) ); -/** - * Sets the value of has markers with a boolean to determine if there are markers. - * - * @param {boolean} hasMarkers True when there are markers, otherwise it is false. - * @returns {void} - */ -Assessor.prototype.setHasMarkers = function( hasMarkers ) { - this._hasMarkers = hasMarkers; -}; + this.setHasMarkers( false ); + this.results = map( assessments, this.executeAssessment.bind( this, paper, this._researcher ) ); -/** - * Returns true when there are markers. - * - * @returns {boolean} Are there markers - */ -Assessor.prototype.hasMarkers = function() { - return this._hasMarkers; -}; - -/** - * Executes an assessment and returns the AssessmentResult. - * - * @param {Paper} paper The paper to pass to the assessment. - * @param {Researcher} researcher The researcher to pass to the assessment. - * @param {Object} assessment The assessment to execute. - * @returns {AssessmentResult} The result of the assessment. - */ -Assessor.prototype.executeAssessment = function( paper, researcher, assessment ) { - let result; - - try { - result = assessment.getResult( paper, researcher ); - result.setIdentifier( assessment.identifier ); + this._lastPaper = paper; + } - if ( result.hasMarks() ) { - result.marks = assessment.getMarks( paper, researcher ); - result.marks = removeDuplicateMarks( result.marks ); - } + /** + * Sets the value of has markers with a boolean to determine if there are markers. + * + * @param {boolean} hasMarkers True when there are markers, otherwise it is false. + * @returns {void} + */ + setHasMarkers( hasMarkers ) { + this._hasMarkers = hasMarkers; + } - if ( result.hasMarks() && this.hasMarker( assessment ) ) { - this.setHasMarkers( true ); + /** + * Returns true when there are markers. + * + * @returns {boolean} Are there markers + */ + hasMarkers() { + return this._hasMarkers; + } - result.setMarker( this.getMarker( assessment, paper, researcher ) ); + /** + * Executes an assessment and returns the AssessmentResult. + * + * @param {Paper} paper The paper to pass to the assessment. + * @param {Researcher} researcher The researcher to pass to the assessment. + * @param {Object} assessment The assessment to execute. + * @returns {AssessmentResult} The result of the assessment. + */ + executeAssessment( paper, researcher, assessment ) { + let result; + + try { + result = assessment.getResult( paper, researcher ); + result.setIdentifier( assessment.identifier ); + + if ( result.hasMarks() ) { + result.marks = assessment.getMarks( paper, researcher ); + result.marks = removeDuplicateMarks( result.marks ); + } + + if ( result.hasMarks() && this.hasMarker( assessment ) ) { + this.setHasMarkers( true ); + + result.setMarker( this.getMarker( assessment, paper, researcher ) ); + } + } catch ( assessmentError ) { + showTrace( assessmentError ); + + result = new AssessmentResult(); + + result.setScore( -1 ); + result.setText( sprintf( + /* translators: %1$s expands to the name of the assessment. */ + __( "An error occurred in the '%1$s' assessment", "wordpress-seo" ), + assessment.identifier, + assessmentError + ) ); } - } catch ( assessmentError ) { - showTrace( assessmentError ); - - result = new AssessmentResult(); - - result.setScore( -1 ); - result.setText( sprintf( - /* translators: %1$s expands to the name of the assessment. */ - __( "An error occurred in the '%1$s' assessment", "wordpress-seo" ), - assessment.identifier, - assessmentError - ) ); + return result; } - return result; -}; -/** - * Filters out all assessment results that have no score and no text. - * - * @returns {Array} The array with all the valid assessments. - */ -Assessor.prototype.getValidResults = function() { - return filter( this.results, function( result ) { - return this.isValidResult( result ); - }.bind( this ) ); -}; - -/** - * Returns if an assessmentResult is valid. - * - * @param {object} assessmentResult The assessmentResult to validate. - * @returns {boolean} whether or not the result is valid. - */ -Assessor.prototype.isValidResult = function( assessmentResult ) { - return assessmentResult.hasScore() && assessmentResult.hasText(); -}; + /** + * Filters out all assessment results that have no score and no text. + * + * @returns {Array} The array with all the valid assessments. + */ + getValidResults() { + return filter( this.results, function( result ) { + return this.isValidResult( result ); + }.bind( this ) ); + } -/** - * Returns the overall score. Calculates the total score by adding all scores and dividing these - * by the number of results times the ScoreRating. - * - * @returns {number} The overall score. - */ -Assessor.prototype.calculateOverallScore = function() { - const results = this.getValidResults(); + /** + * Returns if an assessmentResult is valid. + * + * @param {object} assessmentResult The assessmentResult to validate. + * @returns {boolean} whether or not the result is valid. + */ + isValidResult( assessmentResult ) { + return assessmentResult.hasScore() && assessmentResult.hasText(); + } - const totalScore = results.reduce( ( total, assessmentResult ) => total + assessmentResult.getScore(), 0 ); + /** + * Returns the overall score. Calculates the total score by adding all scores and dividing these + * by the number of results times the ScoreRating. + * + * @returns {number} The overall score. + */ + calculateOverallScore() { + const results = this.getValidResults(); - return Math.round( totalScore / ( results.length * ScoreRating ) * 100 ) || 0; -}; + const totalScore = results.reduce( ( total, assessmentResult ) => total + assessmentResult.getScore(), 0 ); -/** - * Register an assessment to add it to the internal assessments object. - * - * @param {string} name The name of the assessment. - * @param {object} assessment The object containing function to run as an assessment and it's requirements. - * @returns {boolean} Whether registering the assessment was successful. - * @private - */ -Assessor.prototype.addAssessment = function( name, assessment ) { - if ( ! assessment.hasOwnProperty( "identifier" ) ) { - assessment.identifier = name; - } - // If the assessor already has the same assessment, remove it and replace it with the new assessment with the same identifier. - if ( this.getAssessment( assessment.identifier ) ) { - this.removeAssessment( assessment.identifier ); + return Math.round( totalScore / ( results.length * ScoreRating ) * 100 ) || 0; } - this._assessments.push( assessment ); - return true; -}; + /** + * Register an assessment to add it to the internal assessments object. + * + * @param {string} name The name of the assessment. + * @param {object} assessment The object containing function to run as an assessment and it's requirements. + * @returns {boolean} Whether registering the assessment was successful. + * @private + */ + addAssessment( name, assessment ) { + if ( ! assessment.hasOwnProperty( "identifier" ) ) { + assessment.identifier = name; + } + // If the assessor already has the same assessment, remove it and replace it with the new assessment with the same identifier. + if ( this.getAssessment( assessment.identifier ) ) { + this.removeAssessment( assessment.identifier ); + } -/** - * Remove a specific Assessment from the list of Assessments. - * - * @param {string} name The Assessment to remove from the list of assessments. - * @returns {void} - */ -Assessor.prototype.removeAssessment = function( name ) { - const toDelete = findIndex( this._assessments, function( assessment ) { - return assessment.hasOwnProperty( "identifier" ) && name === assessment.identifier; - } ); + this._assessments.push( assessment ); + return true; + } - if ( -1 !== toDelete ) { - this._assessments.splice( toDelete, 1 ); + /** + * Remove a specific Assessment from the list of Assessments. + * + * @param {string} name The Assessment to remove from the list of assessments. + * @returns {void} + */ + removeAssessment( name ) { + const toDelete = findIndex( this._assessments, function( assessment ) { + return assessment.hasOwnProperty( "identifier" ) && name === assessment.identifier; + } ); + + if ( -1 !== toDelete ) { + this._assessments.splice( toDelete, 1 ); + } } -}; -/** - * Returns an assessment by identifier - * - * @param {string} identifier The identifier of the assessment. - * @returns {undefined|Assessment} The object if found, otherwise undefined. - */ -Assessor.prototype.getAssessment = function( identifier ) { - return find( this._assessments, function( assessment ) { - return assessment.hasOwnProperty( "identifier" ) && identifier === assessment.identifier; - } ); -}; + /** + * Returns an assessment by identifier + * + * @param {string} identifier The identifier of the assessment. + * @returns {undefined|Assessment} The object if found, otherwise undefined. + */ + getAssessment( identifier ) { + return find( this._assessments, function( assessment ) { + return assessment.hasOwnProperty( "identifier" ) && identifier === assessment.identifier; + } ); + } -/** - * Checks which of the available assessments are applicable and returns an array with applicable assessments. - * - * @returns {Array} The array with applicable assessments. - */ -Assessor.prototype.getApplicableAssessments = function() { - const availableAssessments = this.getAvailableAssessments(); - return filter( - availableAssessments, - function( availableAssessment ) { - return this.isApplicable( availableAssessment, this.getPaper(), this._researcher ); - }.bind( this ) - ); -}; + /** + * Checks which of the available assessments are applicable and returns an array with applicable assessments. + * + * @returns {Array} The array with applicable assessments. + */ + getApplicableAssessments() { + const availableAssessments = this.getAvailableAssessments(); + return filter( + availableAssessments, + function( availableAssessment ) { + return this.isApplicable( availableAssessment, this.getPaper(), this._researcher ); + }.bind( this ) + ); + } +} export default Assessor; diff --git a/packages/yoastseo/src/scoring/contentAssessor.js b/packages/yoastseo/src/scoring/contentAssessor.js index 3c93b86266d..70925200cd8 100644 --- a/packages/yoastseo/src/scoring/contentAssessor.js +++ b/packages/yoastseo/src/scoring/contentAssessor.js @@ -1,5 +1,4 @@ -import { inherits } from "util"; - +import { map, sum } from "lodash-es"; import Assessor from "./assessor.js"; import ParagraphTooLong from "./assessments/readability/ParagraphTooLongAssessment.js"; import SentenceLengthInText from "./assessments/readability/SentenceLengthInTextAssessment.js"; @@ -8,168 +7,156 @@ import TransitionWords from "./assessments/readability/TransitionWordsAssessment import PassiveVoice from "./assessments/readability/PassiveVoiceAssessment.js"; import SentenceBeginnings from "./assessments/readability/SentenceBeginningsAssessment.js"; import TextPresence from "./assessments/readability/TextPresenceAssessment.js"; - -/* - Temporarily disabled: - var sentenceLengthInDescription = require( "./assessments/sentenceLengthInDescriptionAssessment.js" ); - */ - import scoreToRating from "./interpreters/scoreToRating"; -import { map, sum } from "lodash-es"; - -/** - * Creates the Assessor - * - * @param {object} researcher The researcher to use for the analysis. - * @param {Object} options The options for this assessor. - * @param {Object} options.marker The marker to pass the list of marks to. - * - * @constructor - */ -const ContentAssessor = function( researcher, options = {} ) { - Assessor.call( this, researcher, options ); - this.type = "contentAssessor"; - this._assessments = [ - new SubheadingDistributionTooLong(), - new ParagraphTooLong(), - new SentenceLengthInText(), - new TransitionWords(), - new PassiveVoice(), - new TextPresence(), - new SentenceBeginnings(), - // Temporarily disabled: wordComplexity, - ]; -}; - -inherits( ContentAssessor, Assessor ); - /** - * Calculates the weighted rating for languages that have all assessments based on a given rating. - * - * @param {number} rating The rating to be weighted. - * @returns {number} The weighted rating. + * The ContentAssessor class is used for the readability analysis. */ -ContentAssessor.prototype.calculatePenaltyPointsFullSupport = function( rating ) { - switch ( rating ) { - case "bad": - return 3; - case "ok": - return 2; - default: - case "good": - return 0; +export default class ContentAssessor extends Assessor { + /** + * Creates a new ContentAssessor instance. + * @param {Researcher} researcher The researcher to use. + * @param {Object} [options] The assessor options. + */ + constructor( researcher, options ) { + super( researcher, options ); + this.type = "contentAssessor"; + + this._assessments = [ + new SubheadingDistributionTooLong(), + new ParagraphTooLong(), + new SentenceLengthInText(), + new TransitionWords(), + new PassiveVoice(), + new TextPresence(), + new SentenceBeginnings(), + ]; } -}; -/** - * Calculates the weighted rating for languages that don't have all assessments based on a given rating. - * - * @param {number} rating The rating to be weighted. - * @returns {number} The weighted rating. - */ -ContentAssessor.prototype.calculatePenaltyPointsPartialSupport = function( rating ) { - switch ( rating ) { - case "bad": - return 4; - case "ok": - return 2; - default: - case "good": - return 0; + /** + * Calculates the weighted rating for languages that have all assessments based on a given rating. + * + * @param {string} rating The rating to be weighted. + * @returns {number} The weighted rating. + */ + calculatePenaltyPointsFullSupport( rating ) { + switch ( rating ) { + case "bad": + return 3; + case "ok": + return 2; + default: + case "good": + return 0; + } } -}; -/** - * Determines whether a language is fully supported. If a language supports 8 content assessments - * it is fully supported - * - * @returns {boolean} True if fully supported. - */ -ContentAssessor.prototype._allAssessmentsSupported = function() { - const numberOfAssessments = this._assessments.length; - const applicableAssessments = this.getApplicableAssessments(); - return applicableAssessments.length === numberOfAssessments; -}; + /** + * Calculates the weighted rating for languages that don't have all assessments based on a given rating. + * + * @param {string} rating The rating to be weighted. + * @returns {number} The weighted rating. + */ + calculatePenaltyPointsPartialSupport( rating ) { + switch ( rating ) { + case "bad": + return 4; + case "ok": + return 2; + default: + case "good": + return 0; + } + } -/** - * Calculates the penalty points based on the assessment results. - * - * @returns {number} The total penalty points for the results. - */ -ContentAssessor.prototype.calculatePenaltyPoints = function() { - const results = this.getValidResults(); + /** + * Determines whether a language is fully supported. If a language supports 8 content assessments + * it is fully supported + * + * @returns {boolean} True if fully supported. + */ + _allAssessmentsSupported() { + const numberOfAssessments = this._assessments.length; + const applicableAssessments = this.getApplicableAssessments(); + return applicableAssessments.length === numberOfAssessments; + } - const penaltyPoints = map( results, function( result ) { - const rating = scoreToRating( result.getScore() ); + /** + * Calculates the penalty points based on the assessment results. + * + * @returns {number} The total penalty points for the results. + */ + calculatePenaltyPoints() { + const results = this.getValidResults(); - if ( this._allAssessmentsSupported() ) { - return this.calculatePenaltyPointsFullSupport( rating ); - } + const penaltyPoints = map( results, function( result ) { + const rating = scoreToRating( result.getScore() ); - return this.calculatePenaltyPointsPartialSupport( rating ); - }.bind( this ) ); + if ( this._allAssessmentsSupported() ) { + return this.calculatePenaltyPointsFullSupport( rating ); + } - return sum( penaltyPoints ); -}; + return this.calculatePenaltyPointsPartialSupport( rating ); + }.bind( this ) ); -/** - * Rates the penalty points - * - * @param {number} totalPenaltyPoints The amount of penalty points. - * @returns {number} The score based on the amount of penalty points. - * - * @private - */ -ContentAssessor.prototype._ratePenaltyPoints = function( totalPenaltyPoints ) { - if ( this.getValidResults().length === 1 ) { - // If we have only 1 result, we only have a "no content" result - return 30; + return sum( penaltyPoints ); } - if ( this._allAssessmentsSupported() ) { - // Determine the total score based on the total penalty points. - if ( totalPenaltyPoints > 6 ) { - // A red indicator. - return 30; - } - - if ( totalPenaltyPoints > 4 ) { - // An orange indicator. - return 60; - } - } else { - if ( totalPenaltyPoints > 4 ) { - // A red indicator. + /** + * Rates the penalty points + * + * @param {number} totalPenaltyPoints The amount of penalty points. + * @returns {number} The score based on the amount of penalty points. + * + * @private + */ + _ratePenaltyPoints( totalPenaltyPoints ) { + if ( this.getValidResults().length === 1 ) { + // If we have only 1 result, we only have a "no content" result return 30; } - if ( totalPenaltyPoints > 2 ) { - // An orange indicator. - return 60; + if ( this._allAssessmentsSupported() ) { + // Determine the total score based on the total penalty points. + if ( totalPenaltyPoints > 6 ) { + // A red indicator. + return 30; + } + + if ( totalPenaltyPoints > 4 ) { + // An orange indicator. + return 60; + } + } else { + if ( totalPenaltyPoints > 4 ) { + // A red indicator. + return 30; + } + + if ( totalPenaltyPoints > 2 ) { + // An orange indicator. + return 60; + } } + // A green indicator. + return 90; } - // A green indicator. - return 90; -}; -/** - * Calculates the overall score based on the assessment results. - * - * @returns {number} The overall score. - */ -ContentAssessor.prototype.calculateOverallScore = function() { - const results = this.getValidResults(); - - // If you have no content, you have a red indicator. - if ( results.length === 0 ) { - return 30; - } + /** + * Calculates the overall score based on the assessment results. + * + * @returns {number} The overall score. + */ + calculateOverallScore() { + const results = this.getValidResults(); - const totalPenaltyPoints = this.calculatePenaltyPoints(); - - return this._ratePenaltyPoints( totalPenaltyPoints ); -}; + // If you have no content, you have a red indicator. + if ( results.length === 0 ) { + return 30; + } -export default ContentAssessor; + const totalPenaltyPoints = this.calculatePenaltyPoints(); + return this._ratePenaltyPoints( totalPenaltyPoints ); + } +} diff --git a/packages/yoastseo/src/scoring/cornerstone/contentAssessor.js b/packages/yoastseo/src/scoring/cornerstone/contentAssessor.js index c090ecb05d3..5911f39f77a 100644 --- a/packages/yoastseo/src/scoring/cornerstone/contentAssessor.js +++ b/packages/yoastseo/src/scoring/cornerstone/contentAssessor.js @@ -1,5 +1,3 @@ -import { inherits } from "util"; -import Assessor from "../assessor.js"; import ContentAssessor from "../contentAssessor"; import ParagraphTooLong from "../assessments/readability/ParagraphTooLongAssessment.js"; import SentenceLengthInText from "../assessments/readability/SentenceLengthInTextAssessment.js"; @@ -9,50 +7,38 @@ import PassiveVoice from "../assessments/readability/PassiveVoiceAssessment.js"; import SentenceBeginnings from "../assessments/readability/SentenceBeginningsAssessment.js"; import TextPresence from "../assessments/readability/TextPresenceAssessment.js"; -/* - Temporarily disabled: - - var sentenceLengthInDescription = require( "./assessments/readability/sentenceLengthInDescriptionAssessment.js" ); - */ - /** - * Creates the Assessor - * - * @param {object} researcher The researcher used for the analysis. - * @param {Object} options The options for this assessor. - * @param {Object} options.marker The marker to pass the list of marks to. - * - * @constructor + * The CornerStoneContentAssessor class is used for the readability analysis on cornerstone content. */ -const CornerStoneContentAssessor = function( researcher, options = {} ) { - Assessor.call( this, researcher, options ); - this.type = "cornerstoneContentAssessor"; - - this._assessments = [ - new SubheadingDistributionTooLong( { - parameters: { - slightlyTooMany: 250, - farTooMany: 300, - recommendedMaximumLength: 250, - }, - applicableIfTextLongerThan: 250, - cornerstoneContent: true, - } ), - new ParagraphTooLong(), - new SentenceLengthInText( { - slightlyTooMany: 20, - farTooMany: 25, - }, true ), - new TransitionWords(), - new PassiveVoice(), - new TextPresence(), - new SentenceBeginnings(), - // Temporarily disabled: wordComplexity, - ]; -}; - -inherits( CornerStoneContentAssessor, ContentAssessor ); - - -export default CornerStoneContentAssessor; - +export default class CornerStoneContentAssessor extends ContentAssessor { + /** + * Creates a new CornerStoneContentAssessor instance. + * @param {Researcher} researcher The researcher to use. + * @param {Object} [options] The assessor options. + */ + constructor( researcher, options ) { + super( researcher, options ); + this.type = "cornerstoneContentAssessor"; + + this._assessments = [ + new SubheadingDistributionTooLong( { + parameters: { + slightlyTooMany: 250, + farTooMany: 300, + recommendedMaximumLength: 250, + }, + applicableIfTextLongerThan: 250, + cornerstoneContent: true, + } ), + new ParagraphTooLong(), + new SentenceLengthInText( { + slightlyTooMany: 20, + farTooMany: 25, + }, true ), + new TransitionWords(), + new PassiveVoice(), + new TextPresence(), + new SentenceBeginnings(), + ]; + } +} diff --git a/packages/yoastseo/src/scoring/cornerstone/relatedKeywordAssessor.js b/packages/yoastseo/src/scoring/cornerstone/relatedKeywordAssessor.js index ca825e19bae..32010063806 100644 --- a/packages/yoastseo/src/scoring/cornerstone/relatedKeywordAssessor.js +++ b/packages/yoastseo/src/scoring/cornerstone/relatedKeywordAssessor.js @@ -1,6 +1,4 @@ -import { inherits } from "util"; import Assessor from "../assessor.js"; - import IntroductionKeyword from "../assessments/seo/IntroductionKeywordAssessment.js"; import KeyphraseLength from "../assessments/seo/KeyphraseLengthAssessment.js"; import KeyphraseDensityAssessment from "../assessments/seo/KeywordDensityAssessment.js"; @@ -10,35 +8,32 @@ import FunctionWordsInKeyphrase from "../assessments/seo/FunctionWordsInKeyphras import ImageKeyphrase from "../assessments/seo/KeyphraseInImageTextAssessment"; /** - * Creates the Assessor - * - * @param {Researcher} researcher The researcher used for the analysis. - * @param {Object?} options The options for this assessor. - * @param {Function} options.marker The marker to pass the list of marks to. - * - * @constructor + * The relatedKeywordAssessor class is used for the related keyword analysis for cornerstone content. */ -const relatedKeywordAssessor = function( researcher, options ) { - Assessor.call( this, researcher, options ); - this.type = "cornerstoneRelatedKeywordAssessor"; - - this._assessments = [ - new IntroductionKeyword(), - new KeyphraseLength( { isRelatedKeyphrase: true } ), - new KeyphraseDensityAssessment(), - new MetaDescriptionKeyword(), - new TextCompetingLinks(), - new FunctionWordsInKeyphrase(), - new ImageKeyphrase( { - scores: { - withAltNonKeyword: 3, - withAlt: 3, - noAlt: 3, - }, - } ), - ]; -}; - -inherits( relatedKeywordAssessor, Assessor ); +export default class relatedKeywordAssessor extends Assessor { + /** + * Creates a new relatedKeywordAssessor instance. + * @param {Researcher} researcher The researcher to use. + * @param {Object} [options] The assessor options. + */ + constructor( researcher, options ) { + super( researcher, options ); + this.type = "cornerstoneRelatedKeywordAssessor"; -export default relatedKeywordAssessor; + this._assessments = [ + new IntroductionKeyword(), + new KeyphraseLength( { isRelatedKeyphrase: true } ), + new KeyphraseDensityAssessment(), + new MetaDescriptionKeyword(), + new TextCompetingLinks(), + new FunctionWordsInKeyphrase(), + new ImageKeyphrase( { + scores: { + withAltNonKeyword: 3, + withAlt: 3, + noAlt: 3, + }, + } ), + ]; + } +} diff --git a/packages/yoastseo/src/scoring/cornerstone/seoAssessor.js b/packages/yoastseo/src/scoring/cornerstone/seoAssessor.js index d816dcd6c51..44392ed661a 100644 --- a/packages/yoastseo/src/scoring/cornerstone/seoAssessor.js +++ b/packages/yoastseo/src/scoring/cornerstone/seoAssessor.js @@ -1,5 +1,4 @@ -import { inherits } from "util"; - +import SEOAssessor from "../seoAssessor"; import IntroductionKeywordAssessment from "../assessments/seo/IntroductionKeywordAssessment"; import KeyphraseLengthAssessment from "../assessments/seo/KeyphraseLengthAssessment"; import KeyphraseDensityAssessment from "../assessments/seo/KeywordDensityAssessment"; @@ -8,8 +7,6 @@ import TextCompetingLinksAssessment from "../assessments/seo/TextCompetingLinksA import InternalLinksAssessment from "../assessments/seo/InternalLinksAssessment"; import KeyphraseInSEOTitleAssessment from "../assessments/seo/KeyphraseInSEOTitleAssessment"; import SlugKeywordAssessment from "../assessments/seo/UrlKeywordAssessment"; -import Assessor from "../assessor"; -import SEOAssessor from "../seoAssessor"; import MetaDescriptionLength from "../assessments/seo/MetaDescriptionLengthAssessment"; import SubheadingsKeyword from "../assessments/seo/SubHeadingsKeywordAssessment"; import ImageKeyphrase from "../assessments/seo/KeyphraseInImageTextAssessment"; @@ -21,78 +18,75 @@ import FunctionWordsInKeyphrase from "../assessments/seo/FunctionWordsInKeyphras import SingleH1Assessment from "../assessments/seo/SingleH1Assessment"; /** - * Creates the Assessor - * - * @param {Researcher} researcher The researcher used for the analysis. - * @param {Object?} options The options for this assessor. - * @param {Function} options.marker The marker to pass the list of marks to. - * - * @constructor + * The CornerstoneSEOAssessor class is used for the SEO analysis for cornerstone content. */ -const CornerstoneSEOAssessor = function( researcher, options ) { - Assessor.call( this, researcher, options ); - this.type = "cornerstoneSEOAssessor"; - - this._assessments = [ - new IntroductionKeywordAssessment(), - new KeyphraseLengthAssessment(), - new KeyphraseDensityAssessment(), - new MetaDescriptionKeywordAssessment(), - new MetaDescriptionLength( { - scores: { - tooLong: 3, - tooShort: 3, - }, - } ), - new SubheadingsKeyword(), - new TextCompetingLinksAssessment(), - new ImageKeyphrase( { - scores: { - withAltNonKeyword: 3, - withAlt: 3, - noAlt: 3, - }, - } ), - new ImageCount(), - new TextLength( { - recommendedMinimum: 900, - slightlyBelowMinimum: 400, - belowMinimum: 300, - - scores: { - belowMinimum: -20, - farBelowMinimum: -20, - }, +export default class CornerstoneSEOAssessor extends SEOAssessor { + /** + * Creates a new CornerstoneSEOAssessor instance. + * @param {Researcher} researcher The researcher to use. + * @param {Object} [options] The assessor options. + */ + constructor( researcher, options ) { + super( researcher, options ); + this.type = "cornerstoneSEOAssessor"; - cornerstoneContent: true, - } ), - new OutboundLinks( { - scores: { - noLinks: 3, - }, - } ), - new KeyphraseInSEOTitleAssessment(), - new InternalLinksAssessment(), - new TitleWidth( - { + this._assessments = [ + new IntroductionKeywordAssessment(), + new KeyphraseLengthAssessment(), + new KeyphraseDensityAssessment(), + new MetaDescriptionKeywordAssessment(), + new MetaDescriptionLength( { scores: { - widthTooShort: 9, + tooLong: 3, + tooShort: 3, }, - }, - true - ), - new SlugKeywordAssessment( - { + } ), + new SubheadingsKeyword(), + new TextCompetingLinksAssessment(), + new ImageKeyphrase( { scores: { - okay: 3, + withAltNonKeyword: 3, + withAlt: 3, + noAlt: 3, }, - } - ), - new FunctionWordsInKeyphrase(), - new SingleH1Assessment(), - ]; -}; + } ), + new ImageCount(), + new TextLength( { + recommendedMinimum: 900, + slightlyBelowMinimum: 400, + belowMinimum: 300, -inherits( CornerstoneSEOAssessor, SEOAssessor ); + scores: { + belowMinimum: -20, + farBelowMinimum: -20, + }, -export default CornerstoneSEOAssessor; + cornerstoneContent: true, + } ), + new OutboundLinks( { + scores: { + noLinks: 3, + }, + } ), + new KeyphraseInSEOTitleAssessment(), + new InternalLinksAssessment(), + new TitleWidth( + { + scores: { + widthTooShort: 9, + }, + }, + true + ), + new SlugKeywordAssessment( + { + scores: { + okay: 3, + }, + } + ), + new FunctionWordsInKeyphrase(), + new SingleH1Assessment(), + ]; + } +} diff --git a/packages/yoastseo/src/scoring/inclusiveLanguageAssessor.js b/packages/yoastseo/src/scoring/inclusiveLanguageAssessor.js index 5338b69eac8..aa37c643e1b 100644 --- a/packages/yoastseo/src/scoring/inclusiveLanguageAssessor.js +++ b/packages/yoastseo/src/scoring/inclusiveLanguageAssessor.js @@ -13,14 +13,13 @@ const defaultOptions = { }; /** - * An assessor that assesses a paper for potentially non-inclusive language. + * The InclusiveLanguageAssessor assesses a paper for potentially non-inclusive language. */ -class InclusiveLanguageAssessor extends Assessor { +export default class InclusiveLanguageAssessor extends Assessor { /** - * Creates a new inclusive language assessor. - * - * @param {Researcher} researcher The researcher to use. - * @param {Object} [options] The assessor options. + * Creates a new InclusiveLanguageAssessor instance. + * @param {Researcher} researcher The researcher to use. + * @param {Object} [options] The assessor options. */ constructor( researcher, options = {} ) { super( researcher, options ); @@ -63,5 +62,3 @@ class InclusiveLanguageAssessor extends Assessor { return 90; } } - -export default InclusiveLanguageAssessor; diff --git a/packages/yoastseo/src/scoring/relatedKeywordAssessor.js b/packages/yoastseo/src/scoring/relatedKeywordAssessor.js index ddf792d9abf..6f2c21647cf 100644 --- a/packages/yoastseo/src/scoring/relatedKeywordAssessor.js +++ b/packages/yoastseo/src/scoring/relatedKeywordAssessor.js @@ -1,38 +1,33 @@ -import { inherits } from "util"; - import Assessor from "./assessor.js"; import IntroductionKeyword from "./assessments/seo/IntroductionKeywordAssessment.js"; import KeyphraseLength from "./assessments/seo/KeyphraseLengthAssessment.js"; import KeyphraseDensityAssessment from "./assessments/seo/KeywordDensityAssessment.js"; import MetaDescriptionKeyword from "./assessments/seo/MetaDescriptionKeywordAssessment.js"; -import ImageKeyphrase from "./assessments/seo/KeyphraseInImageTextAssessment"; import TextCompetingLinks from "./assessments/seo/TextCompetingLinksAssessment.js"; import FunctionWordsInKeyphrase from "./assessments/seo/FunctionWordsInKeyphraseAssessment"; +import ImageKeyphrase from "./assessments/seo/KeyphraseInImageTextAssessment"; /** - * Creates the Assessor - * - * @param {Researcher} researcher The researcher to use for the analysis. - * @param {Object?} options The options for this assessor. - * @param {Function} options.marker The marker to pass the list of marks to. - * - * @constructor + * The relatedKeywordAssessor class is used for the related keyword analysis. */ -const relatedKeywordAssessor = function( researcher, options ) { - Assessor.call( this, researcher, options ); - this.type = "relatedKeywordAssessor"; - - this._assessments = [ - new IntroductionKeyword(), - new KeyphraseLength( { isRelatedKeyphrase: true } ), - new KeyphraseDensityAssessment(), - new MetaDescriptionKeyword(), - new TextCompetingLinks(), - new FunctionWordsInKeyphrase(), - new ImageKeyphrase(), - ]; -}; - -inherits( relatedKeywordAssessor, Assessor ); +export default class relatedKeywordAssessor extends Assessor { + /** + * Creates a new relatedKeywordAssessor instance. + * @param {Researcher} researcher The researcher to use. + * @param {Object} [options] The assessor options. + */ + constructor( researcher, options ) { + super( researcher, options ); + this.type = "relatedKeywordAssessor"; -export default relatedKeywordAssessor; + this._assessments = [ + new IntroductionKeyword(), + new KeyphraseLength( { isRelatedKeyphrase: true } ), + new KeyphraseDensityAssessment(), + new MetaDescriptionKeyword(), + new TextCompetingLinks(), + new FunctionWordsInKeyphrase(), + new ImageKeyphrase(), + ]; + } +} diff --git a/packages/yoastseo/src/scoring/relatedKeywordTaxonomyAssessor.js b/packages/yoastseo/src/scoring/relatedKeywordTaxonomyAssessor.js index bb75942b814..854d0d6e838 100644 --- a/packages/yoastseo/src/scoring/relatedKeywordTaxonomyAssessor.js +++ b/packages/yoastseo/src/scoring/relatedKeywordTaxonomyAssessor.js @@ -1,34 +1,30 @@ -import { inherits } from "util"; - +import Assessor from "./assessor"; import IntroductionKeywordAssessment from "./assessments/seo/IntroductionKeywordAssessment"; import KeyphraseLengthAssessment from "./assessments/seo/KeyphraseLengthAssessment"; import KeyphraseDensityAssessment from "./assessments/seo/KeywordDensityAssessment"; import MetaDescriptionKeywordAssessment from "./assessments/seo/MetaDescriptionKeywordAssessment"; -import Assessor from "./assessor"; import FunctionWordsInKeyphrase from "./assessments/seo/FunctionWordsInKeyphraseAssessment"; /** - * Creates the Assessor used for taxonomy pages. - * - * @param {Researcher} researcher The researcher to use for the analysis. - * @param {Object?} options The options for this assessor. - * - * @constructor + * The RelatedKeywordTaxonomyAssessor class is used for the related keyword analysis on terms. */ -const RelatedKeywordTaxonomyAssessor = function( researcher, options ) { - Assessor.call( this, researcher, options ); - this.type = "relatedKeywordsTaxonomyAssessor"; - - this._assessments = [ - new IntroductionKeywordAssessment(), - new KeyphraseLengthAssessment( { isRelatedKeyphrase: true } ), - new KeyphraseDensityAssessment(), - new MetaDescriptionKeywordAssessment(), - // Text Images assessment here. - new FunctionWordsInKeyphrase(), - ]; -}; - -inherits( RelatedKeywordTaxonomyAssessor, Assessor ); +export default class RelatedKeywordTaxonomyAssessor extends Assessor { + /** + * Creates a new RelatedKeywordTaxonomyAssessor instance. + * @param {Researcher} researcher The researcher to use. + * @param {Object} [options] The assessor options. + */ + constructor( researcher, options ) { + super( researcher, options ); + this.type = "relatedKeywordsTaxonomyAssessor"; -export default RelatedKeywordTaxonomyAssessor; + this._assessments = [ + new IntroductionKeywordAssessment(), + new KeyphraseLengthAssessment( { isRelatedKeyphrase: true } ), + new KeyphraseDensityAssessment(), + new MetaDescriptionKeywordAssessment(), + // Text Images assessment here. + new FunctionWordsInKeyphrase(), + ]; + } +} diff --git a/packages/yoastseo/src/scoring/seoAssessor.js b/packages/yoastseo/src/scoring/seoAssessor.js index ae228ea591d..aaa0a1bdb44 100644 --- a/packages/yoastseo/src/scoring/seoAssessor.js +++ b/packages/yoastseo/src/scoring/seoAssessor.js @@ -1,5 +1,4 @@ -import { inherits } from "util"; - +import Assessor from "./assessor"; import IntroductionKeywordAssessment from "./assessments/seo/IntroductionKeywordAssessment"; import KeyphraseLengthAssessment from "./assessments/seo/KeyphraseLengthAssessment"; import KeyphraseDensityAssessment from "./assessments/seo/KeywordDensityAssessment"; @@ -8,7 +7,6 @@ import TextCompetingLinksAssessment from "./assessments/seo/TextCompetingLinksAs import InternalLinksAssessment from "./assessments/seo/InternalLinksAssessment"; import KeyphraseInSEOTitleAssessment from "./assessments/seo/KeyphraseInSEOTitleAssessment"; import SlugKeywordAssessment from "./assessments/seo/UrlKeywordAssessment"; -import Assessor from "./assessor"; import MetaDescriptionLength from "./assessments/seo/MetaDescriptionLengthAssessment"; import SubheadingsKeyword from "./assessments/seo/SubHeadingsKeywordAssessment"; import ImageKeyphrase from "./assessments/seo/KeyphraseInImageTextAssessment"; @@ -20,43 +18,40 @@ import FunctionWordsInKeyphrase from "./assessments/seo/FunctionWordsInKeyphrase import SingleH1Assessment from "./assessments/seo/SingleH1Assessment"; /** - * Creates the Assessor - * - * @param {Researcher} researcher The researcher to use for the analysis. - * @param {Object?} options The options for this assessor. - * @param {Function} options.marker The marker to pass the list of marks to. - * - * @constructor + * The SEOAssessor class is used for the general SEO analysis. */ -const SEOAssessor = function( researcher, options ) { - Assessor.call( this, researcher, options ); - this.type = "SEOAssessor"; - - this._assessments = [ - new IntroductionKeywordAssessment(), - new KeyphraseLengthAssessment(), - new KeyphraseDensityAssessment(), - new MetaDescriptionKeywordAssessment(), - new MetaDescriptionLength(), - new SubheadingsKeyword(), - new TextCompetingLinksAssessment(), - new ImageKeyphrase(), - new ImageCount(), - new TextLength(), - new OutboundLinks(), - new KeyphraseInSEOTitleAssessment(), - new InternalLinksAssessment(), - new TitleWidth( { - scores: { - widthTooShort: 9, - }, - }, true ), - new SlugKeywordAssessment(), - new FunctionWordsInKeyphrase(), - new SingleH1Assessment(), - ]; -}; - -inherits( SEOAssessor, Assessor ); +export default class SEOAssessor extends Assessor { + /** + * Creates a new SEOAssessor instance. + * @param {Researcher} researcher The researcher to use. + * @param {Object} [options] The assessor options. + */ + constructor( researcher, options ) { + super( researcher, options ); + this.type = "SEOAssessor"; -export default SEOAssessor; + this._assessments = [ + new IntroductionKeywordAssessment(), + new KeyphraseLengthAssessment(), + new KeyphraseDensityAssessment(), + new MetaDescriptionKeywordAssessment(), + new MetaDescriptionLength(), + new SubheadingsKeyword(), + new TextCompetingLinksAssessment(), + new ImageKeyphrase(), + new ImageCount(), + new TextLength(), + new OutboundLinks(), + new KeyphraseInSEOTitleAssessment(), + new InternalLinksAssessment(), + new TitleWidth( { + scores: { + widthTooShort: 9, + }, + }, true ), + new SlugKeywordAssessment(), + new FunctionWordsInKeyphrase(), + new SingleH1Assessment(), + ]; + } +} diff --git a/packages/yoastseo/src/scoring/storePostsAndPages/cornerstone/contentAssessor.js b/packages/yoastseo/src/scoring/storePostsAndPages/cornerstone/contentAssessor.js index 38a746c4beb..5d7188f87cb 100644 --- a/packages/yoastseo/src/scoring/storePostsAndPages/cornerstone/contentAssessor.js +++ b/packages/yoastseo/src/scoring/storePostsAndPages/cornerstone/contentAssessor.js @@ -1,6 +1,4 @@ -import { inherits } from "util"; - -import { Assessor, ContentAssessor, assessments, helpers } from "yoastseo"; +import { ContentAssessor, assessments, helpers } from "yoastseo"; const { createAnchorOpeningTag } = helpers; const { @@ -27,53 +25,59 @@ const { * * @constructor */ -const StorePostsAndPagesCornerstoneContentAssessor = function( researcher, options = {} ) { - Assessor.call( this, researcher, options ); - this.type = "storePostsAndPagesCornerstoneContentAssessor"; - - this._assessments = [ +class StorePostsAndPagesCornerstoneContentAssessor extends ContentAssessor { + /** + * Creates a new assessor. + * + * @param {Researcher} researcher The researcher to use. + * @param {Object} options The assessor options. + */ + constructor( researcher, options ) { + super( researcher, options ); - new SubheadingDistributionTooLongAssessment( { - parameters: { - slightlyTooMany: 250, - farTooMany: 300, - recommendedMaximumLength: 250, - }, - urlTitle: createAnchorOpeningTag( "https://yoa.st/shopify68" ), - urlCallToAction: createAnchorOpeningTag( "https://yoa.st/shopify69" ), - cornerstoneContent: true, - } ), - new ParagraphTooLongAssessment( { - urlTitle: createAnchorOpeningTag( "https://yoa.st/shopify66" ), - urlCallToAction: createAnchorOpeningTag( "https://yoa.st/shopify67" ), - } ), - new SentenceLengthInTextAssessment( { - slightlyTooMany: 20, - farTooMany: 25, - urlTitle: createAnchorOpeningTag( "https://yoa.st/shopify48" ), - urlCallToAction: createAnchorOpeningTag( "https://yoa.st/shopify49" ), - }, true ), - new TransitionWordsAssessment( { - urlTitle: createAnchorOpeningTag( "https://yoa.st/shopify44" ), - urlCallToAction: createAnchorOpeningTag( "https://yoa.st/shopify45" ), - } ), - new PassiveVoiceAssessment( { - urlTitle: createAnchorOpeningTag( "https://yoa.st/shopify42" ), - urlCallToAction: createAnchorOpeningTag( "https://yoa.st/shopify43" ), - } ), - new TextPresenceAssessment( { - urlTitle: createAnchorOpeningTag( "https://yoa.st/shopify56" ), - urlCallToAction: createAnchorOpeningTag( "https://yoa.st/shopify57" ), - } ), - new SentenceBeginningsAssessment( { - urlTitle: createAnchorOpeningTag( "https://yoa.st/shopify5" ), - urlCallToAction: createAnchorOpeningTag( "https://yoa.st/shopify65" ), - } ), - ]; -}; + this.type = "storePostsAndPagesCornerstoneContentAssessor"; -inherits( StorePostsAndPagesCornerstoneContentAssessor, ContentAssessor ); + this._assessments = [ + new SubheadingDistributionTooLongAssessment( { + parameters: { + slightlyTooMany: 250, + farTooMany: 300, + recommendedMaximumLength: 250, + }, + urlTitle: createAnchorOpeningTag( "https://yoa.st/shopify68" ), + urlCallToAction: createAnchorOpeningTag( "https://yoa.st/shopify69" ), + cornerstoneContent: true, + } ), + new ParagraphTooLongAssessment( { + urlTitle: createAnchorOpeningTag( "https://yoa.st/shopify66" ), + urlCallToAction: createAnchorOpeningTag( "https://yoa.st/shopify67" ), + } ), + new SentenceLengthInTextAssessment( { + slightlyTooMany: 20, + farTooMany: 25, + urlTitle: createAnchorOpeningTag( "https://yoa.st/shopify48" ), + urlCallToAction: createAnchorOpeningTag( "https://yoa.st/shopify49" ), + }, true ), + new TransitionWordsAssessment( { + urlTitle: createAnchorOpeningTag( "https://yoa.st/shopify44" ), + urlCallToAction: createAnchorOpeningTag( "https://yoa.st/shopify45" ), + } ), + new PassiveVoiceAssessment( { + urlTitle: createAnchorOpeningTag( "https://yoa.st/shopify42" ), + urlCallToAction: createAnchorOpeningTag( "https://yoa.st/shopify43" ), + } ), + new TextPresenceAssessment( { + urlTitle: createAnchorOpeningTag( "https://yoa.st/shopify56" ), + urlCallToAction: createAnchorOpeningTag( "https://yoa.st/shopify57" ), + } ), + new SentenceBeginningsAssessment( { + urlTitle: createAnchorOpeningTag( "https://yoa.st/shopify5" ), + urlCallToAction: createAnchorOpeningTag( "https://yoa.st/shopify65" ), + } ), + ]; + } +} export default StorePostsAndPagesCornerstoneContentAssessor; diff --git a/packages/yoastseo/src/scoring/taxonomyAssessor.js b/packages/yoastseo/src/scoring/taxonomyAssessor.js index d682e36a487..ec184305850 100644 --- a/packages/yoastseo/src/scoring/taxonomyAssessor.js +++ b/packages/yoastseo/src/scoring/taxonomyAssessor.js @@ -1,10 +1,10 @@ +import Assessor from "./assessor"; import IntroductionKeywordAssessment from "./assessments/seo/IntroductionKeywordAssessment"; import KeyphraseLengthAssessment from "./assessments/seo/KeyphraseLengthAssessment"; import KeyphraseDensityAssessment from "./assessments/seo/KeywordDensityAssessment"; import MetaDescriptionKeywordAssessment from "./assessments/seo/MetaDescriptionKeywordAssessment"; import KeyphraseInSEOTitleAssessment from "./assessments/seo/KeyphraseInSEOTitleAssessment"; import SlugKeywordAssessment from "./assessments/seo/UrlKeywordAssessment"; -import Assessor from "./assessor"; import MetaDescriptionLengthAssessment from "./assessments/seo/MetaDescriptionLengthAssessment"; import TextLengthAssessment from "./assessments/seo/TextLengthAssessment"; import PageTitleWidthAssessment from "./assessments/seo/PageTitleWidthAssessment"; @@ -17,7 +17,7 @@ import { createAnchorOpeningTag } from "../helpers"; * * @returns {TextLengthAssessment} The text length assessment (with taxonomy configuration) to use. */ -export const getTextLengthAssessment = function() { +export const getTextLengthAssessment = () => { // Export so it can be used in tests. return new TextLengthAssessment( { recommendedMinimum: 30, @@ -30,18 +30,16 @@ export const getTextLengthAssessment = function() { }; /** - * Creates the Assessor used for taxonomy pages. + * The TaxonomyAssessor is used for the assessment of terms. */ -class TaxonomyAssessor extends Assessor { +export default class TaxonomyAssessor extends Assessor { /** - * Creates a new taxonomy assessor. - * + * Creates a new TaxonomyAssessor instance. * @param {Researcher} researcher The researcher to use. - * @param {Object} options The assessor options. + * @param {Object} [options] The assessor options. */ constructor( researcher, options ) { super( researcher, options ); - this.type = "taxonomyAssessor"; this._assessments = [ @@ -65,5 +63,3 @@ class TaxonomyAssessor extends Assessor { ]; } } - -export default TaxonomyAssessor; From 995dc7ac4639b2e734a499452d494915f69b2a71 Mon Sep 17 00:00:00 2001 From: Martijn van der Klis Date: Wed, 24 Apr 2024 10:45:14 +0200 Subject: [PATCH 11/11] Alternative way of providing cornerstone values --- packages/yoastseo/src/scoring/assessor.js | 1 - .../src/scoring/cornerstone/seoAssessor.js | 93 +++++-------------- 2 files changed, 23 insertions(+), 71 deletions(-) diff --git a/packages/yoastseo/src/scoring/assessor.js b/packages/yoastseo/src/scoring/assessor.js index a912d0c28ac..e4cbfcd4625 100644 --- a/packages/yoastseo/src/scoring/assessor.js +++ b/packages/yoastseo/src/scoring/assessor.js @@ -241,7 +241,6 @@ class Assessor { * @param {string} name The name of the assessment. * @param {object} assessment The object containing function to run as an assessment and it's requirements. * @returns {boolean} Whether registering the assessment was successful. - * @private */ addAssessment( name, assessment ) { if ( ! assessment.hasOwnProperty( "identifier" ) ) { diff --git a/packages/yoastseo/src/scoring/cornerstone/seoAssessor.js b/packages/yoastseo/src/scoring/cornerstone/seoAssessor.js index 44392ed661a..c7ef48ed42c 100644 --- a/packages/yoastseo/src/scoring/cornerstone/seoAssessor.js +++ b/packages/yoastseo/src/scoring/cornerstone/seoAssessor.js @@ -1,21 +1,10 @@ import SEOAssessor from "../seoAssessor"; -import IntroductionKeywordAssessment from "../assessments/seo/IntroductionKeywordAssessment"; -import KeyphraseLengthAssessment from "../assessments/seo/KeyphraseLengthAssessment"; -import KeyphraseDensityAssessment from "../assessments/seo/KeywordDensityAssessment"; -import MetaDescriptionKeywordAssessment from "../assessments/seo/MetaDescriptionKeywordAssessment"; -import TextCompetingLinksAssessment from "../assessments/seo/TextCompetingLinksAssessment"; -import InternalLinksAssessment from "../assessments/seo/InternalLinksAssessment"; -import KeyphraseInSEOTitleAssessment from "../assessments/seo/KeyphraseInSEOTitleAssessment"; -import SlugKeywordAssessment from "../assessments/seo/UrlKeywordAssessment"; import MetaDescriptionLength from "../assessments/seo/MetaDescriptionLengthAssessment"; -import SubheadingsKeyword from "../assessments/seo/SubHeadingsKeywordAssessment"; import ImageKeyphrase from "../assessments/seo/KeyphraseInImageTextAssessment"; -import ImageCount from "../assessments/seo/ImageCountAssessment"; import TextLength from "../assessments/seo/TextLengthAssessment"; import OutboundLinks from "../assessments/seo/OutboundLinksAssessment"; import TitleWidth from "../assessments/seo/PageTitleWidthAssessment"; -import FunctionWordsInKeyphrase from "../assessments/seo/FunctionWordsInKeyphraseAssessment"; -import SingleH1Assessment from "../assessments/seo/SingleH1Assessment"; +import SlugKeywordAssessment from "../assessments/seo/UrlKeywordAssessment"; /** * The CornerstoneSEOAssessor class is used for the SEO analysis for cornerstone content. @@ -30,63 +19,27 @@ export default class CornerstoneSEOAssessor extends SEOAssessor { super( researcher, options ); this.type = "cornerstoneSEOAssessor"; - this._assessments = [ - new IntroductionKeywordAssessment(), - new KeyphraseLengthAssessment(), - new KeyphraseDensityAssessment(), - new MetaDescriptionKeywordAssessment(), - new MetaDescriptionLength( { - scores: { - tooLong: 3, - tooShort: 3, - }, - } ), - new SubheadingsKeyword(), - new TextCompetingLinksAssessment(), - new ImageKeyphrase( { - scores: { - withAltNonKeyword: 3, - withAlt: 3, - noAlt: 3, - }, - } ), - new ImageCount(), - new TextLength( { - recommendedMinimum: 900, - slightlyBelowMinimum: 400, - belowMinimum: 300, - - scores: { - belowMinimum: -20, - farBelowMinimum: -20, - }, - - cornerstoneContent: true, - } ), - new OutboundLinks( { - scores: { - noLinks: 3, - }, - } ), - new KeyphraseInSEOTitleAssessment(), - new InternalLinksAssessment(), - new TitleWidth( - { - scores: { - widthTooShort: 9, - }, - }, - true - ), - new SlugKeywordAssessment( - { - scores: { - okay: 3, - }, - } - ), - new FunctionWordsInKeyphrase(), - new SingleH1Assessment(), - ]; + this.addAssessment( "metaDescriptionLength", new MetaDescriptionLength( { + scores: { tooLong: 3, tooShort: 3 }, + } ) ); + this.addAssessment( "imageKeyphrase", new ImageKeyphrase( { + scores: { withAltNonKeyword: 3, withAlt: 3, noAlt: 3 }, + } ) ); + this.addAssessment( "textLength", new TextLength( { + recommendedMinimum: 900, + slightlyBelowMinimum: 400, + belowMinimum: 300, + scores: { belowMinimum: -20, farBelowMinimum: -20 }, + cornerstoneContent: true, + } ) ); + this.addAssessment( "externalLinks", new OutboundLinks( { + scores: { noLinks: 3 }, + } ) ); + this.addAssessment( "titleWidth", new TitleWidth( { + scores: { widthTooShort: 9 }, + }, true ) ); + this.addAssessment( "slugKeyword", new SlugKeywordAssessment( { + scores: { okay: 3 }, + } ) ); } }