yoastseo-dep
Version:
Yoast clientside page analysis
373 lines (322 loc) • 11.1 kB
JavaScript
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}
*/
AssessorPresenter.prototype.setKeyword = function( keyword ) {
this.keyword = keyword;
};
/**
* 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 ),
};
};
/**
* 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 "";
}
return this.presenterConfig[ rating ].className;
};
/**
* 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 "";
}
return this.presenterConfig[ rating ].screenReaderText;
};
/**
* 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 ].screenReaderReadabilityText;
};
/**
* 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 ].fullText;
};
/**
* 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 "";
}
result.rating = scoreToRating( result.score );
return result;
};
/**
* 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 );
forEach( mappedResults, function( item, key ) {
ratings[ key ] = this.addRating( item );
}.bind( this ) );
return ratings;
};
/**
* 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 );
};
/**
* 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 );
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;
} );
};
/**
* 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();
if ( item.hasMarker() ) {
indicator.marker = item.getMarker();
}
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.
*/
AssessorPresenter.prototype.getOverallRating = function( overallScore ) {
var rating = 0;
if ( this.keyword === "" ) {
return this.resultToRating( { score: rating } );
}
if ( isNumber( overallScore ) ) {
rating = ( overallScore / 10 );
}
return this.resultToRating( { score: rating } );
};
/**
* 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();
this._activeMarker = false;
} else {
marker();
this._activeMarker = identifier;
}
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();
};
/**
* 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 ];
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();
};
/**
* 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 ) );
};
/**
* Removes all marks currently on the text
*
* @returns {void}
*/
AssessorPresenter.prototype.removeAllMarks = function() {
var marker = this.assessor.getSpecificMarker();
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 overall rating.
*
* @returns {void}
*/
AssessorPresenter.prototype.renderOverallRating = function() {
var overallRating = this.getOverallRating( this.assessor.calculateOverallScore() );
var overallRatingElement = document.getElementById( this.overall );
if ( ! overallRatingElement ) {
return;
}
overallRatingElement.className = "overallScore " + this.getIndicatorColorClass( overallRating.rating );
};
export default AssessorPresenter;