mirror of
https://github.com/ThePhaseless/Byparr.git
synced 2025-03-15 17:50:21 +08:00
856 lines
18 KiB
JavaScript
856 lines
18 KiB
JavaScript
/**
|
|
*
|
|
* Source below is based on Mozilla source code:
|
|
* https://searchfox.org/mozilla-central/rev/d317e93d9a59c9e4c06ada85fbff9f6a1ceaaad1/browser/extensions/webcompat/shims/google-ima.js
|
|
*
|
|
* Modifications to the original code below this comment:
|
|
* - Avoid JS syntax not supported by older browser versions
|
|
* - Add missing shim event
|
|
* - Modified to avoid jshint warnings as per uBO's config
|
|
* - Added `OmidVerificationVendor` to `ima`
|
|
* - Have `AdError.getInnerError()` return `null`
|
|
* - Have `AdDisplayContainer` constructor add DIV element to container
|
|
* - Added missing event dispatcher functionality
|
|
* - Corrected return type of `Ad.getUniversalAdIds()`
|
|
* - Corrected typo in `UniversalAdIdInfo.getAdIdValue()` method name
|
|
* - Corrected dispatch of LOAD event when preloading is enabled
|
|
* - Corrected dispatch of CONTENT_PAUSE/RESUME_REQUESTED events
|
|
*
|
|
* Related issue:
|
|
* - https://github.com/uBlockOrigin/uBlock-issues/issues/2158
|
|
*
|
|
**/
|
|
|
|
'use strict';
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/**
|
|
* Bug 1713690 - Shim Google Interactive Media Ads ima3.js
|
|
*
|
|
* Many sites use ima3.js for ad bidding and placement, often in conjunction
|
|
* with Google Publisher Tags, Prebid.js and/or other scripts. This shim
|
|
* provides a stubbed-out version of the API which helps work around related
|
|
* site breakage, such as black bxoes where videos ought to be placed.
|
|
*/
|
|
|
|
if (!window.google || !window.google.ima || !window.google.ima.VERSION) {
|
|
const VERSION = "3.517.2";
|
|
|
|
const CheckCanAutoplay = (function() {
|
|
// Sourced from: https://searchfox.org/mozilla-central/source/dom/media/gtest/negative_duration.mp4
|
|
const TEST_VIDEO = new Blob(
|
|
[
|
|
new Uint32Array([
|
|
469762048,
|
|
1887007846,
|
|
1752392036,
|
|
0,
|
|
913273705,
|
|
1717987696,
|
|
828601953,
|
|
-1878917120,
|
|
1987014509,
|
|
1811939328,
|
|
1684567661,
|
|
0,
|
|
0,
|
|
0,
|
|
-402456576,
|
|
0,
|
|
256,
|
|
1,
|
|
0,
|
|
0,
|
|
256,
|
|
0,
|
|
0,
|
|
0,
|
|
256,
|
|
0,
|
|
0,
|
|
0,
|
|
64,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
33554432,
|
|
-201261056,
|
|
1801548404,
|
|
1744830464,
|
|
1684564852,
|
|
251658241,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
16777216,
|
|
0,
|
|
-1,
|
|
-1,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
256,
|
|
0,
|
|
0,
|
|
0,
|
|
256,
|
|
0,
|
|
0,
|
|
0,
|
|
64,
|
|
5,
|
|
53250,
|
|
-2080309248,
|
|
1634296941,
|
|
738197504,
|
|
1684563053,
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
-2137614336,
|
|
-1,
|
|
-1,
|
|
50261,
|
|
754974720,
|
|
1919706216,
|
|
0,
|
|
0,
|
|
1701079414,
|
|
0,
|
|
0,
|
|
0,
|
|
1701079382,
|
|
1851869295,
|
|
1919249508,
|
|
16777216,
|
|
1852402979,
|
|
102,
|
|
1752004116,
|
|
100,
|
|
1,
|
|
0,
|
|
0,
|
|
1852400676,
|
|
102,
|
|
1701995548,
|
|
102,
|
|
0,
|
|
1,
|
|
1819440396,
|
|
32,
|
|
1,
|
|
1651799011,
|
|
108,
|
|
1937011607,
|
|
100,
|
|
0,
|
|
1,
|
|
1668702599,
|
|
49,
|
|
0,
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
33555712,
|
|
4718800,
|
|
4718592,
|
|
0,
|
|
65536,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
16776984,
|
|
1630601216,
|
|
21193590,
|
|
-14745500,
|
|
1729626337,
|
|
-1407254428,
|
|
89161945,
|
|
1049019,
|
|
9453056,
|
|
-251611125,
|
|
27269507,
|
|
-379058688,
|
|
-1329024392,
|
|
268435456,
|
|
1937011827,
|
|
0,
|
|
0,
|
|
268435456,
|
|
1668510835,
|
|
0,
|
|
0,
|
|
335544320,
|
|
2054386803,
|
|
0,
|
|
0,
|
|
0,
|
|
268435456,
|
|
1868788851,
|
|
0,
|
|
0,
|
|
671088640,
|
|
2019915373,
|
|
536870912,
|
|
2019914356,
|
|
0,
|
|
16777216,
|
|
16777216,
|
|
0,
|
|
0,
|
|
0,
|
|
]),
|
|
],
|
|
{ type: "video/mp4" }
|
|
);
|
|
|
|
let testVideo;
|
|
|
|
return function() {
|
|
if (!testVideo) {
|
|
testVideo = document.createElement("video");
|
|
testVideo.style =
|
|
"position:absolute; width:0; height:0; left:0; right:0; z-index:-1; border:0";
|
|
testVideo.setAttribute("muted", "muted");
|
|
testVideo.setAttribute("playsinline", "playsinline");
|
|
testVideo.src = URL.createObjectURL(TEST_VIDEO);
|
|
document.body.appendChild(testVideo);
|
|
}
|
|
return testVideo.play();
|
|
};
|
|
})();
|
|
|
|
const ima = {};
|
|
|
|
class AdDisplayContainer {
|
|
constructor(containerElement) {
|
|
const divElement = document.createElement("div");
|
|
divElement.style.setProperty("display", "none", "important");
|
|
divElement.style.setProperty("visibility", "collapse", "important");
|
|
containerElement.appendChild(divElement);
|
|
}
|
|
destroy() {}
|
|
initialize() {}
|
|
}
|
|
|
|
class ImaSdkSettings {
|
|
constructor() {
|
|
this.c = true;
|
|
this.f = {};
|
|
this.i = false;
|
|
this.l = "";
|
|
this.p = "";
|
|
this.r = 0;
|
|
this.t = "";
|
|
this.v = "";
|
|
}
|
|
getCompanionBackfill() {}
|
|
getDisableCustomPlaybackForIOS10Plus() {
|
|
return this.i;
|
|
}
|
|
getFeatureFlags() {
|
|
return this.f;
|
|
}
|
|
getLocale() {
|
|
return this.l;
|
|
}
|
|
getNumRedirects() {
|
|
return this.r;
|
|
}
|
|
getPlayerType() {
|
|
return this.t;
|
|
}
|
|
getPlayerVersion() {
|
|
return this.v;
|
|
}
|
|
getPpid() {
|
|
return this.p;
|
|
}
|
|
isCookiesEnabled() {
|
|
return this.c;
|
|
}
|
|
setAutoPlayAdBreaks() {}
|
|
setCompanionBackfill() {}
|
|
setCookiesEnabled(c) {
|
|
this.c = !!c;
|
|
}
|
|
setDisableCustomPlaybackForIOS10Plus(i) {
|
|
this.i = !!i;
|
|
}
|
|
setFeatureFlags(f) {
|
|
this.f = f;
|
|
}
|
|
setLocale(l) {
|
|
this.l = l;
|
|
}
|
|
setNumRedirects(r) {
|
|
this.r = r;
|
|
}
|
|
setPlayerType(t) {
|
|
this.t = t;
|
|
}
|
|
setPlayerVersion(v) {
|
|
this.v = v;
|
|
}
|
|
setPpid(p) {
|
|
this.p = p;
|
|
}
|
|
setSessionId(/*s*/) {}
|
|
setVpaidAllowed(/*a*/) {}
|
|
setVpaidMode(/*m*/) {}
|
|
|
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/2265#issuecomment-1637094149
|
|
getDisableFlashAds() {
|
|
}
|
|
setDisableFlashAds() {
|
|
}
|
|
}
|
|
ImaSdkSettings.CompanionBackfillMode = {
|
|
ALWAYS: "always",
|
|
ON_MASTER_AD: "on_master_ad",
|
|
};
|
|
ImaSdkSettings.VpaidMode = {
|
|
DISABLED: 0,
|
|
ENABLED: 1,
|
|
INSECURE: 2,
|
|
};
|
|
|
|
class EventHandler {
|
|
constructor() {
|
|
this.listeners = new Map();
|
|
}
|
|
|
|
_dispatch(e) {
|
|
let listeners = this.listeners.get(e.type);
|
|
listeners = listeners ? Array.from(listeners.values()) : [];
|
|
for (const listener of listeners) {
|
|
try {
|
|
listener(e);
|
|
} catch (r) {
|
|
console.error(r);
|
|
}
|
|
}
|
|
}
|
|
|
|
addEventListener(types, c, options, context) {
|
|
if (!Array.isArray(types)) {
|
|
types = [types];
|
|
}
|
|
|
|
for (const t of types) {
|
|
if (!this.listeners.has(t)) {
|
|
this.listeners.set(t, new Map());
|
|
}
|
|
this.listeners.get(t).set(c, c.bind(context || this));
|
|
}
|
|
}
|
|
|
|
removeEventListener(types, c) {
|
|
if (!Array.isArray(types)) {
|
|
types = [types];
|
|
}
|
|
|
|
for (const t of types) {
|
|
const typeSet = this.listeners.get(t);
|
|
if (typeSet) {
|
|
typeSet.delete(c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class AdsLoader extends EventHandler {
|
|
constructor() {
|
|
super();
|
|
this.settings = new ImaSdkSettings();
|
|
}
|
|
contentComplete() {}
|
|
destroy() {}
|
|
getSettings() {
|
|
return this.settings;
|
|
}
|
|
getVersion() {
|
|
return VERSION;
|
|
}
|
|
requestAds(/*r, c*/) {
|
|
// If autoplay is disabled and the page is trying to autoplay a tracking
|
|
// ad, then IMA fails with an error, and the page is expected to request
|
|
// ads again later when the user clicks to play.
|
|
CheckCanAutoplay().then(
|
|
() => {
|
|
const { ADS_MANAGER_LOADED } = AdsManagerLoadedEvent.Type;
|
|
this._dispatch(new ima.AdsManagerLoadedEvent(ADS_MANAGER_LOADED));
|
|
},
|
|
() => {
|
|
const e = new ima.AdError(
|
|
"adPlayError",
|
|
1205,
|
|
1205,
|
|
"The browser prevented playback initiated without user interaction."
|
|
);
|
|
this._dispatch(new ima.AdErrorEvent(e));
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
class AdsManager extends EventHandler {
|
|
constructor() {
|
|
super();
|
|
this.volume = 1;
|
|
this._enablePreloading = false;
|
|
}
|
|
collapse() {}
|
|
configureAdsManager() {}
|
|
destroy() {}
|
|
discardAdBreak() {}
|
|
expand() {}
|
|
focus() {}
|
|
getAdSkippableState() {
|
|
return false;
|
|
}
|
|
getCuePoints() {
|
|
return [0];
|
|
}
|
|
getCurrentAd() {
|
|
return currentAd;
|
|
}
|
|
getCurrentAdCuePoints() {
|
|
return [];
|
|
}
|
|
getRemainingTime() {
|
|
return 0;
|
|
}
|
|
getVolume() {
|
|
return this.volume;
|
|
}
|
|
init(/*w, h, m, e*/) {
|
|
if (this._enablePreloading) {
|
|
this._dispatch(new ima.AdEvent(AdEvent.Type.LOADED));
|
|
}
|
|
}
|
|
isCustomClickTrackingUsed() {
|
|
return false;
|
|
}
|
|
isCustomPlaybackUsed() {
|
|
return false;
|
|
}
|
|
pause() {}
|
|
requestNextAdBreak() {}
|
|
resize(/*w, h, m*/) {}
|
|
resume() {}
|
|
setVolume(v) {
|
|
this.volume = v;
|
|
}
|
|
skip() {}
|
|
start() {
|
|
requestAnimationFrame(() => {
|
|
for (const type of [
|
|
AdEvent.Type.LOADED,
|
|
AdEvent.Type.STARTED,
|
|
AdEvent.Type.CONTENT_PAUSE_REQUESTED,
|
|
AdEvent.Type.AD_BUFFERING,
|
|
AdEvent.Type.FIRST_QUARTILE,
|
|
AdEvent.Type.MIDPOINT,
|
|
AdEvent.Type.THIRD_QUARTILE,
|
|
AdEvent.Type.COMPLETE,
|
|
AdEvent.Type.ALL_ADS_COMPLETED,
|
|
AdEvent.Type.CONTENT_RESUME_REQUESTED,
|
|
]) {
|
|
try {
|
|
this._dispatch(new ima.AdEvent(type));
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
stop() {}
|
|
updateAdsRenderingSettings(/*s*/) {}
|
|
}
|
|
|
|
class AdsRenderingSettings {}
|
|
|
|
class AdsRequest {
|
|
setAdWillAutoPlay() {}
|
|
setAdWillPlayMuted() {}
|
|
setContinuousPlayback() {}
|
|
}
|
|
|
|
class AdPodInfo {
|
|
getAdPosition() {
|
|
return 1;
|
|
}
|
|
getIsBumper() {
|
|
return false;
|
|
}
|
|
getMaxDuration() {
|
|
return -1;
|
|
}
|
|
getPodIndex() {
|
|
return 1;
|
|
}
|
|
getTimeOffset() {
|
|
return 0;
|
|
}
|
|
getTotalAds() {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
class Ad {
|
|
constructor() {
|
|
this._pi = new AdPodInfo();
|
|
}
|
|
getAdId() {
|
|
return "";
|
|
}
|
|
getAdPodInfo() {
|
|
return this._pi;
|
|
}
|
|
getAdSystem() {
|
|
return "";
|
|
}
|
|
getAdvertiserName() {
|
|
return "";
|
|
}
|
|
getApiFramework() {
|
|
return null;
|
|
}
|
|
getCompanionAds() {
|
|
return [];
|
|
}
|
|
getContentType() {
|
|
return "";
|
|
}
|
|
getCreativeAdId() {
|
|
return "";
|
|
}
|
|
getCreativeId() {
|
|
return "";
|
|
}
|
|
getDealId() {
|
|
return "";
|
|
}
|
|
getDescription() {
|
|
return "";
|
|
}
|
|
getDuration() {
|
|
return 8.5;
|
|
}
|
|
getHeight() {
|
|
return 0;
|
|
}
|
|
getMediaUrl() {
|
|
return null;
|
|
}
|
|
getMinSuggestedDuration() {
|
|
return -2;
|
|
}
|
|
getSkipTimeOffset() {
|
|
return -1;
|
|
}
|
|
getSurveyUrl() {
|
|
return null;
|
|
}
|
|
getTitle() {
|
|
return "";
|
|
}
|
|
getTraffickingParameters() {
|
|
return {};
|
|
}
|
|
getTraffickingParametersString() {
|
|
return "";
|
|
}
|
|
getUiElements() {
|
|
return [""];
|
|
}
|
|
getUniversalAdIdRegistry() {
|
|
return "unknown";
|
|
}
|
|
getUniversalAdIds() {
|
|
return [new UniversalAdIdInfo()];
|
|
}
|
|
getUniversalAdIdValue() {
|
|
return "unknown";
|
|
}
|
|
getVastMediaBitrate() {
|
|
return 0;
|
|
}
|
|
getVastMediaHeight() {
|
|
return 0;
|
|
}
|
|
getVastMediaWidth() {
|
|
return 0;
|
|
}
|
|
getWidth() {
|
|
return 0;
|
|
}
|
|
getWrapperAdIds() {
|
|
return [""];
|
|
}
|
|
getWrapperAdSystems() {
|
|
return [""];
|
|
}
|
|
getWrapperCreativeIds() {
|
|
return [""];
|
|
}
|
|
isLinear() {
|
|
return true;
|
|
}
|
|
isSkippable() {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class CompanionAd {
|
|
getAdSlotId() {
|
|
return "";
|
|
}
|
|
getContent() {
|
|
return "";
|
|
}
|
|
getContentType() {
|
|
return "";
|
|
}
|
|
getHeight() {
|
|
return 1;
|
|
}
|
|
getWidth() {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
class AdError {
|
|
constructor(type, code, vast, message) {
|
|
this.errorCode = code;
|
|
this.message = message;
|
|
this.type = type;
|
|
this.vastErrorCode = vast;
|
|
}
|
|
getErrorCode() {
|
|
return this.errorCode;
|
|
}
|
|
getInnerError() {
|
|
return null;
|
|
}
|
|
getMessage() {
|
|
return this.message;
|
|
}
|
|
getType() {
|
|
return this.type;
|
|
}
|
|
getVastErrorCode() {
|
|
return this.vastErrorCode;
|
|
}
|
|
toString() {
|
|
return `AdError ${this.errorCode}: ${this.message}`;
|
|
}
|
|
}
|
|
AdError.ErrorCode = {};
|
|
AdError.Type = {};
|
|
|
|
const isEngadget = () => {
|
|
try {
|
|
for (const ctx of Object.values(window.vidible._getContexts())) {
|
|
const player = ctx.getPlayer();
|
|
if (!player) { continue;}
|
|
const div = player.div;
|
|
if (!div) { continue; }
|
|
if (div.innerHTML.includes("www.engadget.com")) {
|
|
return true;
|
|
}
|
|
}
|
|
} catch (_) {}
|
|
return false;
|
|
};
|
|
|
|
const currentAd = isEngadget() ? undefined : new Ad();
|
|
|
|
class AdEvent {
|
|
constructor(type) {
|
|
this.type = type;
|
|
}
|
|
getAd() {
|
|
return currentAd;
|
|
}
|
|
getAdData() {
|
|
return {};
|
|
}
|
|
}
|
|
AdEvent.Type = {
|
|
AD_BREAK_READY: "adBreakReady",
|
|
AD_BUFFERING: "adBuffering",
|
|
AD_CAN_PLAY: "adCanPlay",
|
|
AD_METADATA: "adMetadata",
|
|
AD_PROGRESS: "adProgress",
|
|
ALL_ADS_COMPLETED: "allAdsCompleted",
|
|
CLICK: "click",
|
|
COMPLETE: "complete",
|
|
CONTENT_PAUSE_REQUESTED: "contentPauseRequested",
|
|
CONTENT_RESUME_REQUESTED: "contentResumeRequested",
|
|
DURATION_CHANGE: "durationChange",
|
|
EXPANDED_CHANGED: "expandedChanged",
|
|
FIRST_QUARTILE: "firstQuartile",
|
|
IMPRESSION: "impression",
|
|
INTERACTION: "interaction",
|
|
LINEAR_CHANGE: "linearChange",
|
|
LINEAR_CHANGED: "linearChanged",
|
|
LOADED: "loaded",
|
|
LOG: "log",
|
|
MIDPOINT: "midpoint",
|
|
PAUSED: "pause",
|
|
RESUMED: "resume",
|
|
SKIPPABLE_STATE_CHANGED: "skippableStateChanged",
|
|
SKIPPED: "skip",
|
|
STARTED: "start",
|
|
THIRD_QUARTILE: "thirdQuartile",
|
|
USER_CLOSE: "userClose",
|
|
VIDEO_CLICKED: "videoClicked",
|
|
VIDEO_ICON_CLICKED: "videoIconClicked",
|
|
VIEWABLE_IMPRESSION: "viewable_impression",
|
|
VOLUME_CHANGED: "volumeChange",
|
|
VOLUME_MUTED: "mute",
|
|
};
|
|
|
|
class AdErrorEvent {
|
|
constructor(error) {
|
|
this.type = "adError";
|
|
this.error = error;
|
|
}
|
|
getError() {
|
|
return this.error;
|
|
}
|
|
getUserRequestContext() {
|
|
return {};
|
|
}
|
|
}
|
|
AdErrorEvent.Type = {
|
|
AD_ERROR: "adError",
|
|
};
|
|
|
|
const manager = new AdsManager();
|
|
|
|
class AdsManagerLoadedEvent {
|
|
constructor(type) {
|
|
this.type = type;
|
|
}
|
|
getAdsManager(c, settings) {
|
|
if (settings && settings.enablePreloading) {
|
|
manager._enablePreloading = true;
|
|
}
|
|
return manager;
|
|
}
|
|
getUserRequestContext() {
|
|
return {};
|
|
}
|
|
}
|
|
AdsManagerLoadedEvent.Type = {
|
|
ADS_MANAGER_LOADED: "adsManagerLoaded",
|
|
};
|
|
|
|
class CustomContentLoadedEvent {}
|
|
CustomContentLoadedEvent.Type = {
|
|
CUSTOM_CONTENT_LOADED: "deprecated-event",
|
|
};
|
|
|
|
class CompanionAdSelectionSettings {}
|
|
CompanionAdSelectionSettings.CreativeType = {
|
|
ALL: "All",
|
|
FLASH: "Flash",
|
|
IMAGE: "Image",
|
|
};
|
|
CompanionAdSelectionSettings.ResourceType = {
|
|
ALL: "All",
|
|
HTML: "Html",
|
|
IFRAME: "IFrame",
|
|
STATIC: "Static",
|
|
};
|
|
CompanionAdSelectionSettings.SizeCriteria = {
|
|
IGNORE: "IgnoreSize",
|
|
SELECT_EXACT_MATCH: "SelectExactMatch",
|
|
SELECT_NEAR_MATCH: "SelectNearMatch",
|
|
};
|
|
|
|
class AdCuePoints {
|
|
getCuePoints() {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
class AdProgressData {}
|
|
|
|
class UniversalAdIdInfo {
|
|
getAdIdRegistry() {
|
|
return "";
|
|
}
|
|
getAdIdValue() {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
Object.assign(ima, {
|
|
AdCuePoints,
|
|
AdDisplayContainer,
|
|
AdError,
|
|
AdErrorEvent,
|
|
AdEvent,
|
|
AdPodInfo,
|
|
AdProgressData,
|
|
AdsLoader,
|
|
AdsManager: manager,
|
|
AdsManagerLoadedEvent,
|
|
AdsRenderingSettings,
|
|
AdsRequest,
|
|
CompanionAd,
|
|
CompanionAdSelectionSettings,
|
|
CustomContentLoadedEvent,
|
|
gptProxyInstance: {},
|
|
ImaSdkSettings,
|
|
OmidAccessMode: {
|
|
DOMAIN: "domain",
|
|
FULL: "full",
|
|
LIMITED: "limited",
|
|
},
|
|
OmidVerificationVendor: {
|
|
1: "OTHER",
|
|
2: "GOOGLE",
|
|
GOOGLE: 2,
|
|
OTHER: 1
|
|
},
|
|
settings: new ImaSdkSettings(),
|
|
UiElements: {
|
|
AD_ATTRIBUTION: "adAttribution",
|
|
COUNTDOWN: "countdown",
|
|
},
|
|
UniversalAdIdInfo,
|
|
VERSION,
|
|
ViewMode: {
|
|
FULLSCREEN: "fullscreen",
|
|
NORMAL: "normal",
|
|
},
|
|
});
|
|
|
|
if (!window.google) {
|
|
window.google = {};
|
|
}
|
|
|
|
window.google.ima = ima;
|
|
}
|