<commit_before>/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {isJsonScriptTag} from '../../../src/dom';
import {assertHttpsUrl, appendEncodedParamStringToUrl} from '../../../src/url';
import {dev, rethrowAsync, user} from '../../../src/log';
import {expandTemplate} from '../../../src/string';
import {isArray, isObject} from '../../../src/types';
import {dict, hasOwn, map} from '../../../src/utils/object';
import {sendRequest, sendRequestUsingIframe} from './transport';
import {IframeTransport} from './iframe-transport';
import {getAmpAdResourceId} from '../../../src/ad-helper';
import {Services} from '../../../src/services';
import {toggle} from '../../../src/style';
import {isEnumValue} from '../../../src/types';
import {parseJson} from '../../../src/json';
import {getMode} from '../../../src/mode';
import {Activity} from './activity-impl';
import {
    InstrumentationService,
    instrumentationServicePromiseForDoc,
    AnalyticsEventType,
} from './instrumentation';
import {
  ExpansionOptions,
  installVariableService,
  variableServiceFor,
} from './variables';
import {ANALYTICS_CONFIG} from './vendors';
import {SANDBOX_AVAILABLE_VARS} from './sandbox-vars-whitelist';

const TAG = 'amp-analytics';

const MAX_REPLACES = 16; // The maximum number of entries in a extraUrlParamsReplaceMap

const WHITELIST_EVENT_IN_SANDBOX = [
  AnalyticsEventType.VISIBLE,
  AnalyticsEventType.HIDDEN,
];


export class AmpAnalytics extends AMP.BaseElement {

  /** @param {!AmpElement} element */
  constructor(element) {
    super(element);

    /**
     * @const {!JsonObject} Copied here for tests.
     * @private
     */
    this.predefinedConfig_ = ANALYTICS_CONFIG;

    /** @private {!Promise} */
    this.consentPromise_ = Promise.resolve();

    /**
     * The html id of the `amp-user-notification` element.
     * @private {?string}
     */
    this.consentNotificationId_ = null;

    /**
     * @private {?string} Predefined type associated with the tag. If specified,
     * the config from the predefined type is merged with the inline config
     */
    this.type_ = null;

    /** @private {!boolean} */
    this.isSandbox_ = false;

    /**
     * @private {Object<string, string>} A map of request names to the request
     * format string used by the tag to send data
     */
    this.requests_ = {};

    /**
     * @private {JsonObject}
     */
    this.config_ = dict();

    /**
     * @private {JsonObject}
     */
    this.remoteConfig_ = dict();

    /** @private {?./instrumentation.InstrumentationService} */
    this.instrumentation_ = null;

    /** @private {?./instrumentation.AnalyticsGroup} */
    this.analyticsGroup_ = null;

    /** @private {!./variables.VariableService} */
    this.variableService_ = variableServiceFor(this.win);

    /** @private {!../../../src/service/crypto-impl.Crypto} */
    this.cryptoService_ = Services.cryptoFor(this.win);

    /** @private {?Promise} */
    this.iniPromise_ = null;

    /** @private {?IframeTransport} */
    this.iframeTransport_ = null;
  }

  /** @override */
  getPriority() {
    // Load immediately if inabox, otherwise after other content.
    return getMode().runtime == 'inabox' ? 0 : 1;
  }

  /** @override */
  isAlwaysFixed() {
    return true;
  }

  /** @override */
  isLayoutSupported(unusedLayout) {
    return true;
  }

  /** @override */
  buildCallback() {
    this.isSandbox_ = this.element.hasAttribute('sandbox');

    this.element.setAttribute('aria-hidden', 'true');

    this.consentNotificationId_ = this.element
        .getAttribute('data-consent-notification-id');

    if (this.consentNotificationId_ != null) {
      this.consentPromise_ =
          Services.userNotificationManagerForDoc(this.element)
              .then(service => service.get(dev().assertString(
                  this.consentNotificationId_)));
    }

    if (this.element.getAttribute('trigger') == 'immediate') {
      this.ensureInitialized_();
    }
  }

  /** @override */
  layoutCallback() {
    // Now that we are rendered, stop rendering the element to reduce
    // resource consumption.
    return this.ensureInitialized_();
  }

  /** @override */
  detachedCallback() {
    if (this.analyticsGroup_) {
      this.analyticsGroup_.dispose();
      this.analyticsGroup_ = null;
    }
  }

  /** @override */
  resumeCallback() {
    if (this.config_['transport'] && this.config_['transport']['iframe']) {
      this.initIframeTransport_();
    }
  }

  /** @override */
  unlayoutCallback() {
    if (this.iframeTransport_) {
      this.iframeTransport_.detach();
      this.iframeTransport_ = null;
    }
    return super.unlayoutCallback();
  }

  /**
   * @return {!Promise}<commit_msg>Analytics: Differentiates between element unlayout and page unlayout (#11713)<commit_after>/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {isJsonScriptTag} from '../../../src/dom';
import {assertHttpsUrl, appendEncodedParamStringToUrl} from '../../../src/url';
import {dev, rethrowAsync, user} from '../../../src/log';
import {expandTemplate} from '../../../src/string';
import {isArray, isObject} from '../../../src/types';
import {dict, hasOwn, map} from '../../../src/utils/object';
import {sendRequest, sendRequestUsingIframe} from './transport';
import {IframeTransport} from './iframe-transport';
import {getAmpAdResourceId} from '../../../src/ad-helper';
import {Services} from '../../../src/services';
import {toggle} from '../../../src/style';
import {isEnumValue} from '../../../src/types';
import {parseJson} from '../../../src/json';
import {getMode} from '../../../src/mode';
import {Activity} from './activity-impl';
import {
    InstrumentationService,
    instrumentationServicePromiseForDoc,
    AnalyticsEventType,
} from './instrumentation';
import {
  ExpansionOptions,
  installVariableService,
  variableServiceFor,
} from './variables';
import {ANALYTICS_CONFIG} from './vendors';
import {SANDBOX_AVAILABLE_VARS} from './sandbox-vars-whitelist';

const TAG = 'amp-analytics';

const MAX_REPLACES = 16; // The maximum number of entries in a extraUrlParamsReplaceMap

const WHITELIST_EVENT_IN_SANDBOX = [
  AnalyticsEventType.VISIBLE,
  AnalyticsEventType.HIDDEN,
];


export class AmpAnalytics extends AMP.BaseElement {

  /** @param {!AmpElement} element */
  constructor(element) {
    super(element);

    /**
     * @const {!JsonObject} Copied here for tests.
     * @private
     */
    this.predefinedConfig_ = ANALYTICS_CONFIG;

    /** @private {!Promise} */
    this.consentPromise_ = Promise.resolve();

    /**
     * The html id of the `amp-user-notification` element.
     * @private {?string}
     */
    this.consentNotificationId_ = null;

    /**
     * @private {?string} Predefined type associated with the tag. If specified,
     * the config from the predefined type is merged with the inline config
     */
    this.type_ = null;

    /** @private {!boolean} */
    this.isSandbox_ = false;

    /**
     * @private {Object<string, string>} A map of request names to the request
     * format string used by the tag to send data
     */
    this.requests_ = {};

    /**
     * @private {JsonObject}
     */
    this.config_ = dict();

    /**
     * @private {JsonObject}
     */
    this.remoteConfig_ = dict();

    /** @private {?./instrumentation.InstrumentationService} */
    this.instrumentation_ = null;

    /** @private {?./instrumentation.AnalyticsGroup} */
    this.analyticsGroup_ = null;

    /** @private {!./variables.VariableService} */
    this.variableService_ = variableServiceFor(this.win);

    /** @private {!../../../src/service/crypto-impl.Crypto} */
    this.cryptoService_ = Services.cryptoFor(this.win);

    /** @private {?Promise} */
    this.iniPromise_ = null;

    /** @private {?IframeTransport} */
    this.iframeTransport_ = null;
  }

  /** @override */
  getPriority() {
    // Load immediately if inabox, otherwise after other content.
    return getMode().runtime == 'inabox' ? 0 : 1;
  }

  /** @override */
  isAlwaysFixed() {
    return true;
  }

  /** @override */
  isLayoutSupported(unusedLayout) {
    return true;
  }

  /** @override */
  buildCallback() {
    this.isSandbox_ = this.element.hasAttribute('sandbox');

    this.element.setAttribute('aria-hidden', 'true');

    this.consentNotificationId_ = this.element
        .getAttribute('data-consent-notification-id');

    if (this.consentNotificationId_ != null) {
      this.consentPromise_ =
          Services.userNotificationManagerForDoc(this.element)
              .then(service => service.get(dev().assertString(
                  this.consentNotificationId_)));
    }

    if (this.element.getAttribute('trigger') == 'immediate') {
      this.ensureInitialized_();
    }
  }

  /** @override */
  layoutCallback() {
    // Now that we are rendered, stop rendering the element to reduce
    // resource consumption.
    return this.ensureInitialized_();
  }

  /** @override */
  detachedCallback() {
    if (this.analyticsGroup_) {
      this.analyticsGroup_.dispose();
      this.analyticsGroup_ = null;
    }
  }

  /** @override */
  resumeCallback() {
    if (this.config_['transport'] && this.config_['transport']['iframe']) {
      this.initIframeTransport_();
    }
  }

  /** @override */
  unlayoutCallback() {
    if (Services.viewerForDoc(this.getAmpDoc()).isVisible()) {
      // amp-analytics tag was just set to display:none. Page is still loaded.
      return false;
    }

    // Page was unloaded - free up owned resources.
    if (this.iframeTransport_) {
      this.iframeTransport_.detach();
      this.iframeTransport_ = null;
    }
    return super.unlayoutCallback();
  }

  /**
   * @return {!Promise}
{'fraction_non_alphanumeric': 0.15020708697653015, 'fraction_numerical': 0.00266912103083295, 'mean_word_length': 2.952710076391415, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2131139', 'n_tokens_mistral': 3275, 'n_tokens_neox': 3182, 'n_words': 813}