mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 982322 - Part 1: Use a resolver object to only descend once. r=ochameau
This commit is contained in:
parent
380a642bcc
commit
e9e8df3ffd
@ -57,6 +57,7 @@ const NOT_FOUND_STRING = "n/a";
|
||||
*/
|
||||
function Template(root, store, l10nResolver) {
|
||||
this._store = store;
|
||||
this._rootResolver = new Resolver(this._store.object);
|
||||
this._l10n = l10nResolver;
|
||||
|
||||
// Listeners are stored in Maps.
|
||||
@ -84,39 +85,6 @@ Template.prototype = {
|
||||
this._doc = null;
|
||||
},
|
||||
|
||||
_resolvePath: function(path, defaultValue=null) {
|
||||
|
||||
// From the store, get the value of an object located
|
||||
// at @path.
|
||||
//
|
||||
// For example, if the store is designed as:
|
||||
//
|
||||
// {
|
||||
// foo: {
|
||||
// bar: [
|
||||
// {},
|
||||
// {},
|
||||
// {a: 2}
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// _resolvePath("foo.bar.2.a") will return "2".
|
||||
//
|
||||
// Array indexes are not surrounded by brackets.
|
||||
|
||||
let chunks = path.split(".");
|
||||
let obj = this._store.object;
|
||||
for (let word of chunks) {
|
||||
if ((typeof obj) == "object" &&
|
||||
(word in obj)) {
|
||||
obj = obj[word];
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
_storeChanged: function(event, path, value) {
|
||||
|
||||
// The store has changed (a "set" event has been emitted).
|
||||
@ -200,22 +168,18 @@ Template.prototype = {
|
||||
set.add(element);
|
||||
},
|
||||
|
||||
_processNode: function(element, rootPath="") {
|
||||
_processNode: function(element, resolver=this._rootResolver) {
|
||||
// The actual magic.
|
||||
// The element has a template attribute.
|
||||
// The value is supposed to be a JSON string.
|
||||
// rootPath is the prefex to the path used by
|
||||
// these elements (if children of template-loop);
|
||||
// resolver is a helper object that is used to retrieve data
|
||||
// from the template's data store, give the current path into
|
||||
// the data store, or descend down another level of the store.
|
||||
// See the Resolver object below.
|
||||
|
||||
let e = element;
|
||||
let str = e.getAttribute("template");
|
||||
|
||||
if (rootPath) {
|
||||
// We will prefix paths with this rootPath.
|
||||
// It needs to end with a dot.
|
||||
rootPath = rootPath + ".";
|
||||
}
|
||||
|
||||
try {
|
||||
let json = JSON.parse(str);
|
||||
// Sanity check
|
||||
@ -225,7 +189,7 @@ Template.prototype = {
|
||||
if (json.rootPath) {
|
||||
// If node has been generated through a loop, we stored
|
||||
// previously its rootPath.
|
||||
rootPath = json.rootPath;
|
||||
resolver = this._rootResolver.descend(json.rootPath);
|
||||
}
|
||||
|
||||
// paths is an array that will store all the paths we needed
|
||||
@ -240,16 +204,16 @@ Template.prototype = {
|
||||
!("path" in json)) {
|
||||
throw new Error("missing property");
|
||||
}
|
||||
e.setAttribute(json.name, this._resolvePath(rootPath + json.path, NOT_FOUND_STRING));
|
||||
paths.push(rootPath + json.path);
|
||||
e.setAttribute(json.name, resolver.get(json.path, NOT_FOUND_STRING));
|
||||
paths.push(resolver.rootPathTo(json.path));
|
||||
break;
|
||||
}
|
||||
case "textContent": {
|
||||
if (!("path" in json)) {
|
||||
throw new Error("missing property");
|
||||
}
|
||||
e.textContent = this._resolvePath(rootPath + json.path, NOT_FOUND_STRING);
|
||||
paths.push(rootPath + json.path);
|
||||
e.textContent = resolver.get(json.path, NOT_FOUND_STRING);
|
||||
paths.push(resolver.rootPathTo(json.path));
|
||||
break;
|
||||
}
|
||||
case "localizedContent": {
|
||||
@ -258,17 +222,17 @@ Template.prototype = {
|
||||
throw new Error("missing property");
|
||||
}
|
||||
let params = json.paths.map((p) => {
|
||||
paths.push(rootPath + p);
|
||||
let str = this._resolvePath(rootPath + p, NOT_FOUND_STRING);
|
||||
paths.push(resolver.rootPathTo(p));
|
||||
let str = resolver.get(p, NOT_FOUND_STRING);
|
||||
return str;
|
||||
});
|
||||
e.textContent = this._l10n(json.property, params);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rootPath) {
|
||||
if (resolver !== this._rootResolver) {
|
||||
// We save the rootPath if any.
|
||||
json.rootPath = rootPath;
|
||||
json.rootPath = resolver.path;
|
||||
e.setAttribute("template", JSON.stringify(json));
|
||||
}
|
||||
if (paths.length > 0) {
|
||||
@ -281,7 +245,7 @@ Template.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_processLoop: function(element, rootPath="") {
|
||||
_processLoop: function(element, resolver=this._rootResolver) {
|
||||
// The element has a template-loop attribute.
|
||||
// The related path must be an array. We go
|
||||
// through the array, and build one child per
|
||||
@ -296,9 +260,7 @@ Template.prototype = {
|
||||
!("childSelector" in json)) {
|
||||
throw new Error("missing property");
|
||||
}
|
||||
if (rootPath) {
|
||||
json.arrayPath = rootPath + "." + json.arrayPath;
|
||||
}
|
||||
let descendedResolver = resolver.descend(json.arrayPath);
|
||||
let templateParent = this._doc.querySelector(json.childSelector);
|
||||
if (!templateParent) {
|
||||
throw new Error("can't find child");
|
||||
@ -306,7 +268,7 @@ Template.prototype = {
|
||||
template = this._doc.createElement("div");
|
||||
template.innerHTML = templateParent.innerHTML;
|
||||
template = template.firstElementChild;
|
||||
let array = this._resolvePath(json.arrayPath, []);
|
||||
let array = descendedResolver.get("", []);
|
||||
if (!Array.isArray(array)) {
|
||||
console.error("referenced array is not an array");
|
||||
}
|
||||
@ -315,11 +277,11 @@ Template.prototype = {
|
||||
let fragment = this._doc.createDocumentFragment();
|
||||
for (let i = 0; i < count; i++) {
|
||||
let node = template.cloneNode(true);
|
||||
this._processTree(node, json.arrayPath + "." + i);
|
||||
this._processTree(node, descendedResolver.descend(i));
|
||||
fragment.appendChild(node);
|
||||
}
|
||||
this._registerLoop(json.arrayPath, e);
|
||||
this._registerLoop(json.arrayPath + ".length", e);
|
||||
this._registerLoop(descendedResolver.path, e);
|
||||
this._registerLoop(descendedResolver.rootPathTo("length"), e);
|
||||
this._unregisterNodes(e.querySelectorAll("[template]"));
|
||||
e.innerHTML = "";
|
||||
e.appendChild(fragment);
|
||||
@ -328,7 +290,7 @@ Template.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_processFor: function(element, rootPath="") {
|
||||
_processFor: function(element, resolver=this._rootResolver) {
|
||||
let e = element;
|
||||
try {
|
||||
let template;
|
||||
@ -339,10 +301,6 @@ Template.prototype = {
|
||||
throw new Error("missing property");
|
||||
}
|
||||
|
||||
if (rootPath) {
|
||||
json.path = rootPath + "." + json.path;
|
||||
}
|
||||
|
||||
if (!json.path) {
|
||||
// Nothing to show.
|
||||
this._unregisterNodes(e.querySelectorAll("[template]"));
|
||||
@ -350,6 +308,7 @@ Template.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
let descendedResolver = resolver.descend(json.path);
|
||||
let templateParent = this._doc.querySelector(json.childSelector);
|
||||
if (!templateParent) {
|
||||
throw new Error("can't find child");
|
||||
@ -358,10 +317,10 @@ Template.prototype = {
|
||||
content.innerHTML = templateParent.innerHTML;
|
||||
content = content.firstElementChild;
|
||||
|
||||
this._processTree(content, json.path);
|
||||
this._processTree(content, descendedResolver);
|
||||
|
||||
this._unregisterNodes(e.querySelectorAll("[template]"));
|
||||
this._registerFor(json.path, e);
|
||||
this._registerFor(descendedResolver.path, e);
|
||||
|
||||
e.innerHTML = "";
|
||||
e.appendChild(content);
|
||||
@ -371,21 +330,55 @@ Template.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_processTree: function(parent, rootPath="") {
|
||||
_processTree: function(parent, resolver=this._rootResolver) {
|
||||
let loops = parent.querySelectorAll(":not(template) [template-loop]");
|
||||
let fors = parent.querySelectorAll(":not(template) [template-for]");
|
||||
let nodes = parent.querySelectorAll(":not(template) [template]");
|
||||
for (let e of loops) {
|
||||
this._processLoop(e, rootPath);
|
||||
this._processLoop(e, resolver);
|
||||
}
|
||||
for (let e of fors) {
|
||||
this._processFor(e, rootPath);
|
||||
this._processFor(e, resolver);
|
||||
}
|
||||
for (let e of nodes) {
|
||||
this._processNode(e, rootPath);
|
||||
this._processNode(e, resolver);
|
||||
}
|
||||
if (parent.hasAttribute("template")) {
|
||||
this._processNode(parent, rootPath);
|
||||
this._processNode(parent, resolver);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function Resolver(object, path = "") {
|
||||
this._object = object;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
Resolver.prototype = {
|
||||
|
||||
get: function(path, defaultValue = null) {
|
||||
let obj = this._object;
|
||||
if (path === "") {
|
||||
return obj;
|
||||
}
|
||||
let chunks = path.toString().split(".");
|
||||
for (let word of chunks) {
|
||||
if ((typeof obj) == "object" &&
|
||||
(word in obj)) {
|
||||
obj = obj[word];
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
rootPathTo: function(path) {
|
||||
return this.path ? this.path + "." + path : path;
|
||||
},
|
||||
|
||||
descend: function(path) {
|
||||
return new Resolver(this.get(path), this.rootPathTo(path));
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -22,17 +22,17 @@
|
||||
<span template='{"type":"textContent","path":"title"}'>ttt</span>
|
||||
<span title="ttt" template='{"type":"attribute","name":"title","path":"title"}'></span>
|
||||
<span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:foo_l10n/bar_l10n</span>
|
||||
<div template-for='{"path":"mop","childSelector":"#template-for"}'><span template='{"type":"textContent","path":"name","rootPath":"mop."}'>meh</span></div>
|
||||
<div template-for='{"path":"mop","childSelector":"#template-for"}'><span template='{"type":"textContent","path":"name","rootPath":"mop"}'>meh</span></div>
|
||||
<div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0."}'>xx0</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span>
|
||||
</div>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1."}'>xx1</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -42,17 +42,17 @@
|
||||
<span template='{"type":"textContent","path":"title"}'>xxx</span>
|
||||
<span title="xxx" template='{"type":"attribute","name":"title","path":"title"}'></span>
|
||||
<span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:foo2_l10n/bar_l10n</span>
|
||||
<div template-for='{"path":"mop","childSelector":"#template-for"}'><span template='{"type":"textContent","path":"name","rootPath":"mop."}'>meh2</span></div>
|
||||
<div template-for='{"path":"mop","childSelector":"#template-for"}'><span template='{"type":"textContent","path":"name","rootPath":"mop"}'>meh2</span></div>
|
||||
<div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0."}'>xx0</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span>
|
||||
</div>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1."}'>xx1</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -64,14 +64,14 @@
|
||||
<div template-for='{"path":"","childSelector":"#template-for"}'></div>
|
||||
<div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0."}'>xx0</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span>
|
||||
</div>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1."}'>xx1</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -83,29 +83,29 @@
|
||||
<div template-for='{"path":"","childSelector":"#template-for"}'></div>
|
||||
<div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0."}'>xx0</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span>
|
||||
</div>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1."}'>xx1</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span>
|
||||
</div>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.2."}'>xx2</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.2."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.2."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.2"}'>xx2</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.2"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.2"}'>b</span>
|
||||
</div>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.3."}'>xx3</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.3."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.3."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.3"}'>xx3</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.3"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.3"}'>b</span>
|
||||
</div>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.4."}'>xx4</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.4."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.4."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.4"}'>xx4</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.4"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.4"}'>b</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -117,24 +117,24 @@
|
||||
<div template-for='{"path":"","childSelector":"#template-for"}'></div>
|
||||
<div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0."}'>xx0</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span>
|
||||
</div>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1."}'>xx1</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span>
|
||||
</div>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.2."}'>xx2</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.2."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.2."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.2"}'>xx2</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.2"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.2"}'>b</span>
|
||||
</div>
|
||||
<div>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.3."}'>xx3</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.3."}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.3."}'>b</span>
|
||||
<span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.3"}'>xx3</span>
|
||||
<span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.3"}'>a</span>
|
||||
<span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.3"}'>b</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user