@module snippetPreview
 
Defines the config and outputTarget for the SnippetPreview

@param {Object}         opts                           - Snippet preview options.
@param {App}            opts.analyzerApp               - The app object the Snippet Preview is part of.
@param {Object}         opts.placeholder               - The placeholder values for the fields, will be shown as
actual placeholders in the inputs and as a fallback for the preview.
@param {string}         opts.placeholder.title         - The placeholder title.
@param {string}         opts.placeholder.metaDesc      - The placeholder meta description.
@param {string}         opts.placeholder.urlPath       - The placeholder url.

@param {Object}         opts.defaultValue              - The default value for the fields, if the user has not
changed a field, this value will be used for the analyzer, preview and the progress bars.
@param {string}         opts.defaultValue.title        - The default title.
@param {string}         opts.defaultValue.metaDesc     - The default meta description.
it.

@param {string}         opts.baseURL                   - The basic URL as it will be displayed in google.
@param {HTMLElement}    opts.targetElement             - The target element that contains this snippet editor.

@param {Object}         opts.callbacks                 - Functions that are called on specific instances.
@param {Function}       opts.callbacks.saveSnippetData - Function called when the snippet data is changed.

@param {boolean}        opts.addTrailingSlash          - Whether or not to add a trailing slash to the URL.
@param {string}         opts.metaDescriptionDate       - The date to display before the meta description.

@param {Jed}            opts.i18n                      - The translation object.

@param {string}         opts.previewMode               - The current preview mode.

@property {App}         refObj                         - The connected app object.
@property {Jed}         i18n                           - The translation object.

@property {HTMLElement} targetElement                  - The target element that contains this snippet editor.

@property {Object}      element                        - The elements for this snippet editor.
@property {Object}      element.rendered               - The rendered elements.
@property {HTMLElement} element.rendered.title         - The rendered title element.
@property {HTMLElement} element.rendered.urlPath       - The rendered url path element.
@property {HTMLElement} element.rendered.urlBase       - The rendered url base element.
@property {HTMLElement} element.rendered.metaDesc      - The rendered meta description element.

@property {HTMLElement} element.measurers.titleWidth   - The rendered title width element.
@property {HTMLElement} element.measurers.metaHeight   - The rendered meta height element.

@property {Object}      element.input                  - The input elements.
@property {HTMLElement} element.input.title            - The title input element.
@property {HTMLElement} element.input.urlPath          - The url path input element.
@property {HTMLElement} element.input.metaDesc         - The meta description input element.

@property {HTMLElement} element.container              - The main container element.
@property {HTMLElement} element.formContainer          - The form container element.
@property {HTMLElement} element.editToggle             - The button that toggles the editor form.

@property {Object}      data                           - The data for this snippet editor.
@property {string}      data.title                     - The title.
@property {string}      data.urlPath                   - The url path.
@property {string}      data.metaDesc                  - The meta description.
@property {int}         data.titleWidth                - The width of the title in pixels.
@property {int}         data.metaHeight                - The height of the meta description in pixels.

@property {string}      baseURL                        - The basic URL as it will be displayed in google.

@property {boolean}     hasProgressSupport             - Whether this browser supports the <progress> element.

@constructor
function( opts ) {
	defaultsDeep( opts, defaults );

	this.data = opts.data;

	if ( ! isUndefined( opts.analyzerApp ) ) {
		this.refObj = opts.analyzerApp;
		this.i18n = this.refObj.i18n;

		this.data = {
			title: this.refObj.rawData.snippetTitle || "",
			urlPath: this.refObj.rawData.snippetCite || "",
			metaDesc: this.refObj.rawData.snippetMeta || "",
		};

		// For backwards compatibility set the metaTitle as placeholder.
		if ( ! isEmpty( this.refObj.rawData.metaTitle ) ) {
			opts.placeholder.title = this.refObj.rawData.metaTitle;
		}
	}

	if ( ! isUndefined( opts.i18n ) ) {
		this.i18n = opts.i18n;
	}

	if ( ! isElement( opts.targetElement ) ) {
		throw new Error( "The snippet preview requires a valid target element" );
	}

	this.opts = opts;
	this._currentFocus = null;
	this._currentHover = null;

	// For backwards compatibility monitor the unformatted text for changes and reflect them in the preview
	this.unformattedText = {};
	Object.defineProperty( this.unformattedText, "snippet_cite", {
		get: retrieveUnformattedText.bind( this, "urlPath" ),
		set: updateUnformattedText.bind( this, "urlPath" ),
	} );
	Object.defineProperty( this.unformattedText, "snippet_meta", {
		get: retrieveUnformattedText.bind( this, "metaDesc" ),
		set: updateUnformattedText.bind( this, "metaDesc" ),
	} );
	Object.defineProperty( this.unformattedText, "snippet_title", {
		get: retrieveUnformattedText.bind( this, "title" ),
		set: updateUnformattedText.bind( this, "title" ),
	} );
}