Lawliet18's picture
Add application file
6342ac4
<!DOCTYPE html>
<html><script type="text/javascript">try {
(function injectPageScriptAPI(scriptName, shouldOverrideWebSocket, shouldOverrideWebRTC, isInjected) {
'use strict';
/**
* If script have been injected into a frame via contentWindow then we can simply take the copy of messageChannel left for us by parent document
* Otherwise creates new message channel that sends a message to the content-script to check if request should be allowed or not.
*/
var messageChannel = isInjected ? window[scriptName] : (function () {
// Save original postMessage and addEventListener functions to prevent webpage from tampering both.
var postMessage = window.postMessage;
var addEventListener = window.addEventListener;
// Current request ID (incremented every time we send a new message)
var currentRequestId = 0;
var requestsMap = {};
/**
* Handles messages sent from the content script back to the page script.
*
* @param event Event with necessary data
*/
var onMessageReceived = function (event) {
if (!event.data || !event.data.direction || event.data.direction !== "to-page-script@abu") {
return;
}
var requestData = requestsMap[event.data.requestId];
if (requestData) {
var wrapper = requestData.wrapper;
requestData.onResponseReceived(wrapper, event.data.block);
delete requestsMap[event.data.requestId];
}
};
/**
* @param url The URL to which wrapped object is willing to connect
* @param requestType Request type ( WEBSOCKET or WEBRTC)
* @param wrapper WebSocket wrapper instance
* @param onResponseReceived Called when response is received
*/
var sendMessage = function (url, requestType, wrapper, onResponseReceived) {
if (currentRequestId === 0) {
// Subscribe to response when this method is called for the first time
addEventListener.call(window, "message", onMessageReceived, false);
}
var requestId = ++currentRequestId;
requestsMap[requestId] = {
wrapper: wrapper,
onResponseReceived: onResponseReceived
};
var message = {
requestId: requestId,
direction: 'from-page-script@abu',
elementUrl: url,
documentUrl: document.URL,
requestType: requestType
};
// Send a message to the background page to check if the request should be blocked
postMessage.call(window, message, "*");
};
return {
sendMessage: sendMessage
};
})();
/*
* In some case Chrome won't run content scripts inside frames.
* So we have to intercept access to contentWindow/contentDocument and manually inject wrapper script into this context
*
* Based on: https://github.com/adblockplus/adblockpluschrome/commit/1aabfb3346dc0821c52dd9e97f7d61b8c99cd707
*/
var injectedToString = Function.prototype.toString.bind(injectPageScriptAPI);
var injectedFramesAdd;
var injectedFramesHas;
if (window.WeakSet instanceof Function) {
var injectedFrames = new WeakSet();
injectedFramesAdd = WeakSet.prototype.add.bind(injectedFrames);
injectedFramesHas = WeakSet.prototype.has.bind(injectedFrames);
} else {
var frames = [];
injectedFramesAdd = function (el) {
if (frames.indexOf(el) < 0) {
frames.push(el);
}
};
injectedFramesHas = function (el) {
return frames.indexOf(el) >= 0;
};
}
/**
* Injects wrapper's script into passed window
* @param contentWindow Frame's content window
*/
function injectPageScriptAPIInWindow(contentWindow) {
try {
if (contentWindow && !injectedFramesHas(contentWindow)) {
injectedFramesAdd(contentWindow);
contentWindow[scriptName] = messageChannel; // Left message channel for the injected script
var args = "'" + scriptName + "', " + shouldOverrideWebSocket + ", " + shouldOverrideWebRTC + ", true";
contentWindow.eval("(" + injectedToString() + ")(" + args + ");");
delete contentWindow[scriptName];
}
} catch (e) {
}
}
/**
* Overrides access to contentWindow/contentDocument for the passed HTML element's interface (iframe, frame, object)
* If the content of one of these objects is requested we will inject our wrapper script.
* @param iface HTML element's interface
*/
function overrideContentAccess(iface) {
var contentWindowDescriptor = Object.getOwnPropertyDescriptor(iface.prototype, "contentWindow");
var contentDocumentDescriptor = Object.getOwnPropertyDescriptor(iface.prototype, "contentDocument");
// Apparently in HTMLObjectElement.prototype.contentWindow does not exist
// in older versions of Chrome such as 42.
if (!contentWindowDescriptor) {
return;
}
var getContentWindow = Function.prototype.call.bind(contentWindowDescriptor.get);
var getContentDocument = Function.prototype.call.bind(contentDocumentDescriptor.get);
contentWindowDescriptor.get = function () {
var contentWindow = getContentWindow(this);
injectPageScriptAPIInWindow(contentWindow);
return contentWindow;
};
contentDocumentDescriptor.get = function () {
injectPageScriptAPIInWindow(getContentWindow(this));
return getContentDocument(this);
};
Object.defineProperty(iface.prototype, "contentWindow", contentWindowDescriptor);
Object.defineProperty(iface.prototype, "contentDocument", contentDocumentDescriptor);
}
var interfaces = [HTMLFrameElement, HTMLIFrameElement, HTMLObjectElement];
for (var i = 0; i < interfaces.length; i++) {
overrideContentAccess(interfaces[i]);
}
/**
* Defines properties in destination object
* @param src Source object
* @param dest Destination object
* @param properties Properties to copy
*/
var copyProperties = function (src, dest, properties) {
for (var i = 0; i < properties.length; i++) {
var prop = properties[i];
var descriptor = Object.getOwnPropertyDescriptor(src, prop);
// Passed property may be undefined
if (descriptor) {
Object.defineProperty(dest, prop, descriptor);
}
}
};
/**
* Check request by sending message to content script
* @param url URL to block
* @param type Request type
* @param callback Result callback
*/
var checkRequest = function (url, type, callback) {
messageChannel.sendMessage(url, type, this, function (wrapper, blockConnection) {
callback(blockConnection);
});
};
/**
* The function overrides window.WebSocket with our wrapper, that will check url with filters through messaging with content-script.
*
* IMPORTANT NOTE:
* This function is first loaded as a content script. The only purpose of it is to call
* the "toString" method and use resulting string as a text content for injected script.
*/
var overrideWebSocket = function () {
if (!(window.WebSocket instanceof Function)) {
return;
}
/**
* WebSocket wrapper implementation.
* https://github.com/AdguardTeam/AdguardBrowserExtension/issues/349
*
* Based on:
* https://github.com/adblockplus/adblockpluschrome/commit/457a336ee55a433217c3ffe5d363e5c6980f26f4
*/
/**
* As far as possible we must track everything we use that could be sabotaged by the website later in order to circumvent us.
*/
var RealWebSocket = WebSocket;
var closeWebSocket = Function.prototype.call.bind(RealWebSocket.prototype.close);
function WrappedWebSocket(url, protocols) {
// Throw correct exceptions if the constructor is used improperly.
if (!(this instanceof WrappedWebSocket)) {
return RealWebSocket();
}
if (arguments.length < 1) {
return new RealWebSocket();
}
var websocket = new RealWebSocket(url, protocols);
// This is the key point: checking if this WS should be blocked or not
// Don't forget that the type of 'websocket.url' is String, but 'url 'parameter might have another type.
checkRequest(websocket.url, 'WEBSOCKET', function (blocked) {
if (blocked) {
closeWebSocket(websocket);
}
});
return websocket;
}
// https://github.com/AdguardTeam/AdguardBrowserExtension/issues/488
WrappedWebSocket.prototype = RealWebSocket.prototype;
window.WebSocket = WrappedWebSocket.bind();
copyProperties(RealWebSocket, WebSocket, ["CONNECTING", "OPEN", "CLOSING", "CLOSED", "name", "prototype"]);
RealWebSocket.prototype.constructor = WebSocket;
};
/**
* The function overrides window.RTCPeerConnection with our wrapper, that will check ice servers URLs with filters through messaging with content-script.
*
* IMPORTANT NOTE:
* This function is first loaded as a content script. The only purpose of it is to call
* the "toString" method and use resulting string as a text content for injected script.
*/
var overrideWebRTC = function () {
if (!(window.RTCPeerConnection instanceof Function) &&
!(window.webkitRTCPeerConnection instanceof Function)) {
return;
}
/**
* RTCPeerConnection wrapper implementation.
* https://github.com/AdguardTeam/AdguardBrowserExtension/issues/588
*
* Based on:
* https://github.com/adblockplus/adblockpluschrome/commit/af0585137be19011eace1cf68bf61eed2e6db974
*
* Chromium webRequest API doesn't allow the blocking of WebRTC connections
* https://bugs.chromium.org/p/chromium/issues/detail?id=707683
*/
var RealRTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection;
var closeRTCPeerConnection = Function.prototype.call.bind(RealRTCPeerConnection.prototype.close);
var RealArray = Array;
var RealString = String;
var createObject = Object.create;
var defineProperty = Object.defineProperty;
/**
* Convert passed url to string
* @param url URL
* @returns {string}
*/
function urlToString(url) {
if (typeof url !== "undefined") {
return RealString(url);
}
}
/**
* Creates new immutable array from original with some transform function
* @param original
* @param transform
* @returns {*}
*/
function safeCopyArray(original, transform) {
if (original === null || typeof original !== "object") {
return original;
}
var immutable = RealArray(original.length);
for (var i = 0; i < immutable.length; i++) {
defineProperty(immutable, i, {
configurable: false, enumerable: false, writable: false,
value: transform(original[i])
});
}
defineProperty(immutable, "length", {
configurable: false, enumerable: false, writable: false,
value: immutable.length
});
return immutable;
}
/**
* Protect configuration from mutations
* @param configuration RTCPeerConnection configuration object
* @returns {*}
*/
function protectConfiguration(configuration) {
if (configuration === null || typeof configuration !== "object") {
return configuration;
}
var iceServers = safeCopyArray(
configuration.iceServers,
function (iceServer) {
var url = iceServer.url;
var urls = iceServer.urls;
// RTCPeerConnection doesn't iterate through pseudo Arrays of urls.
if (typeof urls !== "undefined" && !(urls instanceof RealArray)) {
urls = [urls];
}
return createObject(iceServer, {
url: {
configurable: false, enumerable: false, writable: false,
value: urlToString(url)
},
urls: {
configurable: false, enumerable: false, writable: false,
value: safeCopyArray(urls, urlToString)
}
});
}
);
return createObject(configuration, {
iceServers: {
configurable: false, enumerable: false, writable: false,
value: iceServers
}
});
}
/**
* Check WebRTC connection's URL and close if it's blocked by rule
* @param connection Connection
* @param url URL to check
*/
function checkWebRTCRequest(connection, url) {
checkRequest(url, 'WEBRTC', function (blocked) {
if (blocked) {
try {
closeRTCPeerConnection(connection);
} catch (e) {
// Ignore exceptions
}
}
});
}
/**
* Check each URL of ice server in configuration for blocking.
*
* @param connection RTCPeerConnection
* @param configuration Configuration for RTCPeerConnection
* https://developer.mozilla.org/en-US/docs/Web/API/RTCConfiguration
*/
function checkConfiguration(connection, configuration) {
if (!configuration || !configuration.iceServers) {
return;
}
var iceServers = configuration.iceServers;
for (var i = 0; i < iceServers.length; i++) {
var iceServer = iceServers[i];
if (!iceServer) {
continue;
}
if (iceServer.url) {
checkWebRTCRequest(connection, iceServer.url);
}
if (iceServer.urls) {
for (var j = 0; j < iceServer.urls.length; j++) {
checkWebRTCRequest(connection, iceServer.urls[j]);
}
}
}
}
/**
* Overrides setConfiguration method
* https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/setConfiguration
*/
if (RealRTCPeerConnection.prototype.setConfiguration) {
var realSetConfiguration = Function.prototype.call.bind(RealRTCPeerConnection.prototype.setConfiguration);
RealRTCPeerConnection.prototype.setConfiguration = function (configuration) {
configuration = protectConfiguration(configuration);
// Call the real method first, so that validates the configuration
realSetConfiguration(this, configuration);
checkConfiguration(this, configuration);
};
}
function WrappedRTCPeerConnection(configuration, arg) {
if (!(this instanceof WrappedRTCPeerConnection)) {
return RealRTCPeerConnection();
}
configuration = protectConfiguration(configuration);
/**
* The old webkitRTCPeerConnection constructor takes an optional second argument and we must pass it.
*/
var connection = new RealRTCPeerConnection(configuration, arg);
checkConfiguration(connection, configuration);
return connection;
}
WrappedRTCPeerConnection.prototype = RealRTCPeerConnection.prototype;
var boundWrappedRTCPeerConnection = WrappedRTCPeerConnection.bind();
copyProperties(RealRTCPeerConnection, boundWrappedRTCPeerConnection, ["caller", "generateCertificate", "name", "prototype"]);
RealRTCPeerConnection.prototype.constructor = boundWrappedRTCPeerConnection;
if ("RTCPeerConnection" in window) {
window.RTCPeerConnection = boundWrappedRTCPeerConnection;
}
if ("webkitRTCPeerConnection" in window) {
window.webkitRTCPeerConnection = boundWrappedRTCPeerConnection;
}
};
if (shouldOverrideWebSocket) {
overrideWebSocket();
}
if (shouldOverrideWebRTC) {
overrideWebRTC();
}
})('wrapper-script-3215676477346979', false, true);
} catch (ex) { console.error('Error executing AG js: ' + ex); }
(function () {
var current = document.currentScript;
var parent = current && current.parentNode;
if (parent) {
parent.removeChild(current);
}
})();</script><head>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; connect-src 'self'">
<title>Page not found · GitHub Pages</title>
<style type="text/css" media="screen">
body {
background-color: #f1f1f1;
margin: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.container { margin: 50px auto 40px auto; width: 600px; text-align: center; }
a { color: #4183c4; text-decoration: none; }
a:hover { text-decoration: underline; }
h1 { width: 800px; position:relative; left: -100px; letter-spacing: -1px; line-height: 60px; font-size: 60px; font-weight: 100; margin: 0px 0 50px 0; text-shadow: 0 1px 0 #fff; }
p { color: rgba(0, 0, 0, 0.5); margin: 20px 0; line-height: 1.6; }
ul { list-style: none; margin: 25px 0; padding: 0; }
li { display: table-cell; font-weight: bold; width: 1%; }
.logo { display: inline-block; margin-top: 35px; }
.logo-img-2x { display: none; }
@media
only screen and (-webkit-min-device-pixel-ratio: 2),
only screen and ( min--moz-device-pixel-ratio: 2),
only screen and ( -o-min-device-pixel-ratio: 2/1),
only screen and ( min-device-pixel-ratio: 2),
only screen and ( min-resolution: 192dpi),
only screen and ( min-resolution: 2dppx) {
.logo-img-1x { display: none; }
.logo-img-2x { display: inline-block; }
}
#suggestions {
margin-top: 35px;
color: #ccc;
}
#suggestions a {
color: #666666;
font-weight: 200;
font-size: 14px;
margin: 0 10px;
}
</style>
</head>
<body>
<div class="container">
<h1>404</h1>
<p><strong>File not found</strong></p>
<p>
The site configured at this address does not
contain the requested file.
</p>
<p>
If this is your site, make sure that the filename case matches the URL
as well as any file permissions.<br>
For root URLs (like <code>http://example.com/</code>) you must provide an
<code>index.html</code> file.
</p>
<p>
<a href="https://help.github.com/pages/">Read the full documentation</a>
for more information about using <strong>GitHub Pages</strong>.
</p>
<div id="suggestions">
<a href="https://githubstatus.com/">GitHub Status</a>
<a href="https://twitter.com/githubstatus">@githubstatus</a>
</div>
<a href="https://llava-vl.github.io/" class="logo logo-img-1x">
<img width="32" height="32" title="" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFMTZCRDY3REIzRjAxMUUyQUQzREIxQzRENUFFNUM5NiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFMTZCRDY3RUIzRjAxMUUyQUQzREIxQzRENUFFNUM5NiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkUxNkJENjdCQjNGMDExRTJBRDNEQjFDNEQ1QUU1Qzk2IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkUxNkJENjdDQjNGMDExRTJBRDNEQjFDNEQ1QUU1Qzk2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+SM9MCAAAA+5JREFUeNrEV11Ik1EY3s4+ddOp29Q5b0opCgKFsoKoi5Kg6CIhuwi6zLJLoYLopq4qsKKgi4i6CYIoU/q5iDAKs6syoS76IRWtyJ+p7cdt7sf1PGOD+e0c3dygAx/67ZzzPM95/877GYdHRg3ZjMXFxepQKNS6sLCwJxqNNuFpiMfjVs4ZjUa/pmmjeD6VlJS8NpvNT4QQ7mxwjSsJiEQim/1+/9lgMHgIr5ohuxG1WCw9Vqv1clFR0dCqBODElV6v90ogEDjGdYbVjXhpaendioqK07CIR7ZAqE49PT09BPL2PMgTByQGsYiZlQD4uMXtdr+JxWINhgINYhGT2MsKgMrm2dnZXgRXhaHAg5jEJodUAHxux4LudHJE9RdEdA+i3Juz7bGHe4mhE9FNrgwBCLirMFV9Okh5eflFh8PR5nK5nDabrR2BNJlKO0T35+Li4n4+/J+/JQCxhmu5h3uJoXNHPbmWZAHMshWB8l5/ipqammaAf0zPDDx1ONV3vurdidqwAQL+pEc8sLcAe1CCvQ3YHxIW8Pl85xSWNC1hADDIv0rIE/o4J0k3kww4xSlwIhcq3EFFOm7KN/hUGOQkt0CFa5WpNJlMvxBEz/IVQAxg/ZRZl9wiHA63yDYieM7DnLP5CiAGsC7I5sgtYKJGWe2A8seFqgFJrJjEPY1Cn3pJ8/9W1e5VWsFDTEmFrBcoDhZJEQkXuhICMyKpjhahqN21hRYATKfUOlDmkygrR4o4C0VOLGJKrOITKB4jijzdXygBKixyC5TDQdnk/Pz8qRw6oOWGlsTKGOQW6OH6FBWsyePxdOXLTgxiyebILZCjz+GLgMIKnXNzc49YMlcRdHXcSwxFVgTInQhC9G33UhNoJLuqq6t345p9y3eUy8OTk5PjAHuI9uo4b07FBaOhsu0A4Unc+T1TU1Nj3KsSSE5yJ65jqF2DDd8QqWYmAZrIM2VlZTdnZmb6AbpdV9V6ec9znf5Q7HjYumdRE0JOp3MjitO4SFa+cZz8Umqe3TCbSLvdfkR/kWDdNQl5InuTcysOcpFT35ZrbBxx4p3JAHlZVVW1D/634VRt+FvLBgK/v5LV9WS+10xMTEwtRw7XvqOL+e2Q8V3AYIOIAXQ26/heWVnZCVfcyKHg2CBgTpmPmjYM8l24GyaUHyaIh7XwfR9ErE8qHoDfn2LTNAVC0HX6MFcBIP8Bi+6F6cdW/DICkANRfx99fEYFQ7Nph5i/uQiA214gno7K+guhaiKg9gC62+M8eR7XsBsYJ4ilam60Fb7r7uAj8wFyuwM1oIOWgfmDy6RXEEQzJMPe23DXrVS7rtyD3Df8z/FPgAEAzWU5Ku59ZAUAAAAASUVORK5CYII=">
</a>
<a href="https://llava-vl.github.io/" class="logo logo-img-2x">
<img width="32" height="32" title="" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpEQUM1QkUxRUI0MUMxMUUyQUQzREIxQzRENUFFNUM5NiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpEQUM1QkUxRkI0MUMxMUUyQUQzREIxQzRENUFFNUM5NiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkUxNkJENjdGQjNGMDExRTJBRDNEQjFDNEQ1QUU1Qzk2IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkUxNkJENjgwQjNGMDExRTJBRDNEQjFDNEQ1QUU1Qzk2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+hfPRaQAAB6lJREFUeNrsW2mM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">
</a>
</div>
</body></html>