<commit_before>import Widget from '../../js/widget';
import { t } from 'enketo/translator';
import events from '../../js/event';
import { getSiblingElements } from '../../js/dom-utils';

/**
 * Image Map widget that turns an SVG image into a clickable map
 * by matching radiobutton/checkbox values with id attribute values in the SVG.
 *
 * @extends Widget
 */
class ImageMap extends Widget {
    /**
     * @type string
     */
    static get selector() {
        return '.simple-select.or-appearance-image-map label:first-child > input';
    }

    _init() {
        const img = this.question.querySelector( 'img' );
        this.question.classList.add( 'or-image-map-initialized' );
        /*
         * To facilitate Enketo Express' offline webforms,
         * where the img source is populated after form loading, we initialize upon image load
         * if the src attribute is not yet populated.
         *
         * We could use the same with online-only forms, but that would cause a loading delay.
         */

        if ( !img ) {
            this._showSvgNotFoundError();
        } else if ( img.getAttribute( 'src' ) ) {
            // return a promise, resolving with instance for asynchronous initialization
            return this._addMarkup( img )
                .then( this._addFunctionality.bind( this ) )
                .then( () => this );
        } else {
            return new Promise( resolve => {
                img.addEventListener( 'load', () => {
                    this._addMarkup( img ).then( this._addFunctionality.bind( this ) );
                    resolve( this );
                } );
            } );
            // Ignore errors, because an img element without source may throw one.
            // E.g. in Enketo Express inside a repeat: https://github.com/kobotoolbox/enketo-express/issues/961
        }
    }

    /**
     * @param {object} widget
     */
    _addFunctionality( widget ) {
        this.svg = widget.querySelector( 'svg' );
        this.tooltip = widget.querySelector( '.image-map__ui__tooltip' );
        if ( this.props.readonly ) {
            this.disable();
        }
        this._setSvgClickHandler();
        this._setChangeHandler();
        this._setHoverHandler();
        this._updateImage();
    }

    /**
     * @param {Element} img
     * @return {Promise}
     */
    _addMarkup( img ) {
        const that = this;
        const src = img.getAttribute( 'src' );

        /**
         * For translated forms, we now discard everything except the first image,
         * since we're assuming the images will be the same in all languages.
         */
        return fetch( src )
            .then( response => response.text() )
            .then( txt => ( new DOMParser() ).parseFromString( txt, 'text/xml' ) )
            .then( doc => {
                if ( that._isSvgDoc( doc ) ) {
                    const svgFragment = that._removeUnmatchedIds( doc.querySelector( 'svg' ) );
                    const fragment = document.createRange().createContextualFragment(
                        `<div class="widget image-map">
                            <div class="image-map__ui">
                                <span class="image-map__ui__tooltip"></span>
                            </div><commit_msg>changed: reduce unnecessary console errors if image map src cannot be downloaded<commit_after>import Widget from '../../js/widget';
import { t } from 'enketo/translator';
import events from '../../js/event';
import { getSiblingElements } from '../../js/dom-utils';

/**
 * Image Map widget that turns an SVG image into a clickable map
 * by matching radiobutton/checkbox values with id attribute values in the SVG.
 *
 * @extends Widget
 */
class ImageMap extends Widget {
    /**
     * @type string
     */
    static get selector() {
        return '.simple-select.or-appearance-image-map label:first-child > input';
    }

    _init() {
        const img = this.question.querySelector( 'img' );
        this.question.classList.add( 'or-image-map-initialized' );
        /*
         * To facilitate Enketo Express' offline webforms,
         * where the img source is populated after form loading, we initialize upon image load
         * if the src attribute is not yet populated.
         *
         * We could use the same with online-only forms, but that would cause a loading delay.
         */

        if ( !img ) {
            this._showSvgNotFoundError();
        } else if ( img.getAttribute( 'src' ) ) {
            // return a promise, resolving with instance for asynchronous initialization
            return this._addMarkup( img )
                .then( this._addFunctionality.bind( this ) )
                .then( () => this );
        } else {
            return new Promise( resolve => {
                img.addEventListener( 'load', () => {
                    this._addMarkup( img ).then( this._addFunctionality.bind( this ) );
                    resolve( this );
                } );
            } );
            // Ignore errors, because an img element without source may throw one.
            // E.g. in Enketo Express inside a repeat: https://github.com/kobotoolbox/enketo-express/issues/961
        }
    }

    /**
     * @param {object} widget
     */
    _addFunctionality( widget ) {
        if ( widget ) {
            this.svg = widget.querySelector( 'svg' );
            this.tooltip = widget.querySelector( '.image-map__ui__tooltip' );
            if ( this.props.readonly ) {
                this.disable();
            }
            this._setSvgClickHandler();
            this._setChangeHandler();
            this._setHoverHandler();
            this._updateImage();
        }
    }

    /**
     * @param {Element} img
     * @return {Promise}
     */
    _addMarkup( img ) {
        const that = this;
        const src = img.getAttribute( 'src' );

        /**
         * For translated forms, we now discard everything except the first image,
         * since we're assuming the images will be the same in all languages.
         */
        return fetch( src )
            .then( response => response.text() )
            .then( txt => ( new DOMParser() ).parseFromString( txt, 'text/xml' ) )
            .then( doc => {
                if ( that._isSvgDoc( doc ) ) {
                    const svgFragment = that._removeUnmatchedIds( doc.querySelector( 'svg' ) );
                    const fragment = document.createRange().createContextualFragment(
                        `<div class="widget image-map">
                            <div class="image-map__ui">
                                <span class="image-map__ui__tooltip"></span>
                            </div>
{'fraction_non_alphanumeric': 0.11446052827936129, 'fraction_numerical': 0.0008953887479480674, 'mean_word_length': 1.7133603238866397, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 27, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7371013', 'n_tokens_mistral': 1762, 'n_tokens_neox': 1731, 'n_words': 513}