@callback YoastSEO.App~updatedKeywordsResults

@param {Object[]} result The updated keywords analysis results.
@param {number} result[].score The SEO score.
@param {string} result[].rating String representation of the SEO score.
@param {string} result[].text Textual explanation of the score.
@param {number} overallContentScore The overall keywords SEO score.
 
Loader for the analyzer, loads the eventbinder and the elementdefiner

@param {Object} args The arguments passed to the loader.
@param {Object} args.translations Jed compatible translations.
@param {Object} args.targets Targets to retrieve or set on.
@param {String} args.targets.snippet ID for the snippet preview element.
@param {String} args.targets.output ID for the element to put the output of the analyzer in.
@param {int} args.typeDelay Number of milliseconds to wait between typing to refresh the analyzer output.
@param {boolean} args.dynamicDelay   Whether to enable dynamic delay, will ignore type delay if the analyzer takes a long time.
Applicable on slow devices.
@param {int} args.maxTypeDelay The maximum amount of type delay even if dynamic delay is on.
@param {int} args.typeDelayStep The amount with which to increase the typeDelay on each step when dynamic delay is enabled.
@param {Object} args.callbacks The callbacks that the app requires.
@param {Object} args.assessor The Assessor to use instead of the default assessor.
@param {YoastSEO.App~getData} args.callbacks.getData Called to retrieve input data
@param {YoastSEO.App~getAnalyzerInput} args.callbacks.getAnalyzerInput Called to retrieve input for the analyzer.
@param {YoastSEO.App~bindElementEvents} args.callbacks.bindElementEvents Called to bind events to the DOM elements.
@param {YoastSEO.App~updateSnippetValues} args.callbacks.updateSnippetValues Called when the snippet values need to be updated.
@param {YoastSEO.App~saveScores} args.callbacks.saveScores Called when the score has been determined by the analyzer.
@param {YoastSEO.App~saveContentScore} args.callback.saveContentScore Called when the content score has been
determined by the assessor.
@param {YoastSEO.App~updatedContentResults} args.callbacks.updatedContentResults Called when the score has been determined
by the analyzer.
@param {YoastSEO.App~updatedKeywordsResults} args.callback.updatedKeywordsResults Called when the content score has been
determined by the assessor.
@param {Function} args.callbacks.saveSnippetData Function called when the snippet data is changed.
@param {Function} args.marker The marker to use to apply the list of marks retrieved from an assessment.

@param {SnippetPreview} args.snippetPreview The SnippetPreview object to be used.
@param {boolean} [args.debouncedRefresh] Whether or not to debounce the
refresh function. Defaults to true.

@constructor
function( args ) {
	if ( ! isObject( args ) ) {
		args = {};
	}

	defaultsDeep( args, defaults );

	verifyArguments( args );

	this.config = args;

	if ( args.debouncedRefresh === true ) {
		this.refresh = debounce( this.refresh.bind( this ), inputDebounceDelay );
	}
	this._pureRefresh = throttle( this._pureRefresh.bind( this ), this.config.typeDelay );

	this.callbacks = this.config.callbacks;
	this.i18n = this.constructI18n( this.config.translations );

	this.initializeAssessors( args );

	this.pluggable = new Pluggable( this );

	this.getData();

	this.defaultOutputElement = this.getDefaultOutputElement( args );

	if ( this.defaultOutputElement !== "" ) {
		this.showLoadingDialog();
	}

	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;
			this.snippetPreview._i18n = this.i18n;
		}
	} else if ( args.hasSnippetPreview ) {
		this.snippetPreview = createDefaultSnippetPreview.call( this );
	}

	this._assessorOptions = {
		useCornerStone: false,
		useKeywordDistribution: false,
	};

	this.initSnippetPreview();
	this.initAssessorPresenters();
}