Why my Layout Position changes when i use appendChild() method? - javascript

I am changing the text content in <p> tags. While setting the new text content to <p> tags, the sequence of <p> tags is changing.
Is there any way to fix the issue ?
var userInput = "optin-monster";
var all_script = "Benedict_Cumberbatch ";
var all=$("p:contains(" + userInput + ")").attr('id', 'xyz');
var len_all=$('p').length;
var all_array=[];
for (var i=0; i < len_all; i++) {
all_array.push($(all[i]).text());
}
all_array = all_array.filter(item => item);
changed_array=[];
for (var i = 0; i < all_array.length; i++)
{
var indexEqu=all_array[i].indexOf("=");
var slicedVal=all_array[i].slice(indexEqu+2,indexEqu+22);
var result = all_array[i].replace(all_array[i],all_script);
var out=result+slicedVal+" Enrique_Iglesias";
changed_array.push(out);
}
for (j= 0, n = changed_array.length; j< n; j++) {
var line = document.getElementById("xyz");
line.innerHTML = changed_array[j];
document.body.appendChild(line);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>vmware</p>
<p>[optin-monster slug="wxjxpdi2nhbn7xlmmljo"]</p>
<p>product design</p>
<p>[optin-monster slug="i2nlmmljodhbn7wxjxpx"]</p>
<p>[optin-monster slug="mljodhbn7wxji2nlmxpx"]</p>
Purpose : The changed paragraph tags should be in there actual positions after getting new text content.
Expected :
vmware
Benedict_Cumberbatch wxjxpdi2nhbn7xlmmljo Enrique_Iglesias
product design
Benedict_Cumberbatch i2nlmmljodhbn7wxjxpx Enrique_Iglesias
Benedict_Cumberbatch mljodhbn7wxji2nlmxpx Enrique_Iglesias

From the description of the expected output in the question, your goal is to update the content of the elements in place. As such you don't need to use appendChild(). You can edit the element's content in-place.
In addition, as you've included jQuery in the page already you may as well use it to simplify the logic. You can provide a function to text() which accepts the existing text as an argument, retrieves the slug value from it and returns that with the added prefix/suffix.
To get the slug value you can use a regular expression which you simply concatenate the other string values to:
let userInput = "optin-monster";
let prefix = "Benedict_Cumberbatch";
let suffix = "Enrique_Iglesias"
$(`p:contains(${userInput})`).text((i, t) => {
let slug = t.match(/(?:"[^"]*"|^[^"]*$)/)[0].replace(/"/g, '');
return `${prefix} ${slug} ${suffix}`;
});
// non-es6 version
/*
$('p:contains(' + userInput + ')').text(function(i, t) {
let slug = t.match(/(?:"[^"]*"|^[^"]*$)/)[0].replace(/"/g, '');
return prefix + ' ' + slug + ' ' + suffix;
});
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>vmware</p>
<p>[optin-monster slug="wxjxpdi2nhbn7xlmmljo"]</p>
<p>product design</p>
<p>[optin-monster slug="i2nlmmljodhbn7wxjxpx"]</p>
<p>[optin-monster slug="mljodhbn7wxji2nlmxpx"]</p>
Also note that I removed the part of the logic where you set the same id on all the elements. This is invalid as id must be unique. If you need a method of identifying these elements as a group, use a class.

Related

How can I create unique IDs for each input field after click on "Add Row"? [duplicate]

I have a form where a user can add multiple select boxes for multiple cities. The problem is that each newly generated select box needs to have a unique id. Can this be done is JavaScript?
Here is the part of the form for selecting cities. Also note that I'm using some PHP to fill in the cities when a specific state is selected.
<form id="form" name="form" method="post" action="citySelect.php">
<select id="state" name="state" onchange="getCity()">
<option></option>
<option value="1">cali</option>
<option value="2">arizona</option>
<option value="3">texas</option>
</select>
<select id="city" name="city" style="width:100px">
</select>
<br/>
</form>
Here is the JavaScript:
$("#bt").click(function() {
$("#form").append(
"<select id='state' name='state' onchange='getCity()'>
<option></option>
<option value='1'>cali</option>
<option value='2'>arizona</option>
<option value='3'>texas</option>
</select>
<select id='city' name='city' style='width:100px'></select><br/>"
);
});
var id = "id" + Math.random().toString(16).slice(2)
another way it to use the millisecond timer:
var uniq = 'id' + (new Date()).getTime();
const uid = function(){
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
This Function generates very unique IDs that are sorted by its generated Date.
Also useable for IDs in Databases.
could you not just keep a running index?
var _selectIndex = 0;
...code...
var newSelectBox = document.createElement("select");
newSelectBox.setAttribute("id","select-"+_selectIndex++);
EDIT
Upon further consideration, you may actually prefer to use array-style names for your selects...
e.g.
<select name="city[]"><option ..../></select>
<select name="city[]"><option ..../></select>
<select name="city[]"><option ..../></select>
then, on the server side in php for example:
$cities = $_POST['city']; //array of option values from selects
EDIT 2 In response to OP comment
Dynamically creating options using DOM methods can be done as follows:
var newSelectBox = document.createElement("select");
newSelectBox.setAttribute("id","select-"+_selectIndex++);
var city = null,city_opt=null;
for (var i=0, len=cities.length; i< len; i++) {
city = cities[i];
var city_opt = document.createElement("option");
city_opt.setAttribute("value",city);
city_opt.appendChild(document.createTextNode(city));
newSelectBox.appendChild(city_opt);
}
document.getElementById("example_element").appendChild(newSelectBox);
assuming that the cities array already exists
Alternatively you could use the innerHTML method.....
var newSelectBox = document.createElement("select");
newSelectBox.setAttribute("id","select-"+_selectIndex++);
document.getElementById("example_element").appendChild(newSelectBox);
var city = null,htmlStr="";
for (var i=0, len=cities.length; i< len; i++) {
city = cities[i];
htmlStr += "<option value='" + city + "'>" + city + "</option>";
}
newSelectBox.innerHTML = htmlStr;
function uniqueid(){
// always start with a letter (for DOM friendlyness)
var idstr=String.fromCharCode(Math.floor((Math.random()*25)+65));
do {
// between numbers and characters (48 is 0 and 90 is Z (42-48 = 90)
var ascicode=Math.floor((Math.random()*42)+48);
if (ascicode<58 || ascicode>64){
// exclude all chars between : (58) and # (64)
idstr+=String.fromCharCode(ascicode);
}
} while (idstr.length<32);
return (idstr);
}
No external libraries needed. Uniqueness proved.
You could do something like this.
// Function to generate unique id
const uniqueId = (length=16) => {
return parseInt(Math.ceil(Math.random() * Date.now()).toPrecision(length).toString().replace(".", ""))
}
// ----------------------------
document.querySelector("#butt01").onclick = () => {
document.querySelector("#span01").innerHTML = uniqueId()
}
ids = []
count = 0
document.querySelector("#butt02").onclick = () => {
for (let i = 0; i< 1000; i++){
ids.push(uniqueId())
}
for (el of ids){
for (ell of ids){
if(el == ell && ids.indexOf(el) != ids.indexOf(ell)){
count += 1
}
}
}
document.querySelector("#span02").innerHTML = `Found ${count} duplicated random values.`
}
<button id="butt01">Generate</button>
<br>
<span id="span01"></span>
<br>
<hr>
<br>
<button id="butt02">Check collision potentiality in 1000 cases</button>
<br>
<span id="span02"></span>
Multiply time in milliseconds since epoch with a random value to fixed size.
Run this to see possible collisions.
You would see there are no collisions whether it is 1000, 10000 or 1000000000.
It would have a very small chance if two users generate ids at the same time and gets the rame random number.
To increase the uniqueness you could multiply date more Math.random()s.
The shortest and without libraries, also works in nodejs
crypto.randomUUID();
// 'a63ae209-ec69-4867-af8a-6f4d1efe15c6'
https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID
btn.onclick = () => myID.textContent = crypto.randomUUID()
<button id="btn">Generate ID</button>
<myID id="myID"></myID>
Very short function will give you unique ID:
var uid = (function(){var id=0;return function(){if(arguments[0]===0)id=0;return id++;}})();
alert ( uid() );
In reply to #scott :
Sometime JS go very fast... so...
var uniqueId = null,
getUniqueName = function(prefix) {
if (!uniqueId) uniqueId = (new Date()).getTime();
return (prefix || 'id') + (uniqueId++);
};
I'm working on a similar problem to the OP, and found that elements of the solutions from #Guy and #Scott can be combined to create a solution that's more solid IMO. The resulting unique id here has three sections separated by underscores:
A leading letter;
A timestamp displayed in base 36;
And a final, random section.
This solution should work really well, even for very large sets:
function uniqueId () {
// desired length of Id
var idStrLen = 32;
// always start with a letter -- base 36 makes for a nice shortcut
var idStr = (Math.floor((Math.random() * 25)) + 10).toString(36) + "_";
// add a timestamp in milliseconds (base 36 again) as the base
idStr += (new Date()).getTime().toString(36) + "_";
// similar to above, complete the Id using random, alphanumeric characters
do {
idStr += (Math.floor((Math.random() * 35))).toString(36);
} while (idStr.length < idStrLen);
return (idStr);
}
You could generate an ID using a timer and avoiding duplicates using performance.now():
id = 'id' + performance.now()
dup = 'id' + performance.now()
console.log(id)
console.log(id.replace('.','')) // sexy id
console.log(id === dup) // false!
.as-console-wrapper{border-top: none !important;overflow-y: auto !important;top: 0;}
Note that the High resolution time API is available in all recent browsers.
EDIT(per Ciprian's comment): That is unfortunately not enough, performance.now() is only precise to the millisecond. Consider using it in conjuction with Math.random():
const generateId = () => `${performance.now()}${Math.random().toString().slice(5)}`.replace('.','')
let id = generateId()
let dup = generateId()
console.log(id)
console.log(id === dup) // false!
let ary = [...Array(1000)].map(_ => generateId())
console.log((new Set(ary)).size === 1000) // no dups!
.as-console-wrapper{border-top: none !important;overflow-y: auto !important;top: 0;}
put in your namespace an instance similar to the following one
var myns = {/*.....*/};
myns.uid = new function () {
var u = 0;
this.toString = function () {
return 'myID_' + u++;
};
};
console.dir([myns.uid, myns.uid, myns.uid]);
There are two packages available for this.
For short unique id generation nanoid link
import { nanoid } from 'nanoid'
const id = nanoid() // "Uakgb_J5m9g-0JDMbcJqLJ"
const id = nanoid(10) // "jcNqc0UAWK"
For universally unique id generation uuid link
import { v4 as uuidv4 } from 'uuid';
const id= uuidv4(); // quite big id
You can use the Generator function, was introduced in ES6 (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*)
const idCreator = function* () {
let i = 0;
while (true) yield i++;
};
const idsGenerator = idCreator();
const generateId = () => idsGenerator.next().value;
console.log(generateId()) // 0
console.log(generateId()) // 1
console.log(generateId()) // 2
...
To avoid creating any counters and be sure that the id is unique even if there are some other components that create elements with ids on the page, you can use a random number and than correct it if it's not good enough (but you also have to set the id immediately to avoid conflicts):
var id = "item"+(new Date()).getMilliseconds()+Math.floor(Math.random()*1000);
// or use any random number generator
// whatever prefix can be used instead of "item"
while(document.getElementById(id))
id += 1;
//# set id right here so that no element can get that id between the check and setting it
Random is not unique. Times values are not unique. The concepts are quite different and the difference rears its ugly head when your application scales and is distributed. Many of the answers above are potentially dangerous.
A safer approach to the poster's question is UUIDs: Create GUID / UUID in JavaScript?
Like others said you can use a running index, or if you don't like the idea of using a variable just pull the id of the last city in the list and add 1 to its id.
Here is a function (function genID() below) that recursively checks the DOM for uniqueness based on whatever id prefex/ID you want.
In your case you'd might use it as such
var seedNum = 1;
newSelectBox.setAttribute("id",genID('state-',seedNum));
function genID(myKey, seedNum){
var key = myKey + seedNum;
if (document.getElementById(key) != null){
return genID(myKey, ++seedNum);
}
else{
return key;
}
}
Warning: This answer may not be good for the general intent of this question, but I post it here nevertheless, because it solves a partial version of this issue.
You can use lodash's uniqueId (documentation here). This is not a good uniqueId generator for say, db records, or things that will persist a session in a browser or something like that. But the reason I came here looking for this was solved by using it. If you need a unique id for something transient enough, this will do.
I needed it because I was creating a reusable react component that features a label and a form control. The label needs to have a for="controlId" attribute, corresponding to the id="controlId" that the actual form control has (the input or select element). This id is not necessary out of this context, but I need to generate one id for both attributes to share, and make sure this id is unique in the context of the page being rendered. So lodash's function worked just fine. Just in case is useful for someone else.
Simple Solution :)
const ID = (_length=13) => {
// Math.random to base 36 (numbers, letters),
// grab the first 9 characters
// after the decimal.
return '_' + Math.random().toString(36).substr(2, _length); // max _length should be less then 13
};
console.log("Example ID()::", ID())
function generateId() {
return Math.random().toString(36).substring(2) +
(new Date()).getTime().toString(36);
}
console.log(generateId())
Look at this functions, it will get ur job done.
If u want uppercase and lowercase chars in ur string:
function (length) {
var id = '';
while (id.length < length) {
var ch = Math.random()
.toString(36)
.substr(2, 1);
if (Math.random() < 0.5) {
ch = ch.toUpperCase();
}
id += ch;
}
return id;
}
Only lowercase chars:
function (length) {
var id = '';
while (id.length < length) {
id += Math.random()
.toString(36)
.substr(2, 1);
}
return id;
}
Just two cents
function makeId(tokenLen) {
if (tokenLen == null) {
tokenLen = 16;
}
var text = "";
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < tokenLen; ++i)
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
Here's my own take at it based on the xpath of the element created :
/** Returns the XPATH of an element **/
var getPathTo = function(element) {
if (element===document.body)
return element.tagName;
var ix= 0;
var siblings= element.parentNode.childNodes;
for (var i= 0; i<siblings.length; i++) {
var sibling= siblings[i];
if (sibling===element)
// stripped xpath (parent xpath + tagname + index)
return getPathTo(element.parentNode)+ element.tagName + ix+1;
if (sibling.nodeType===1 && sibling.tagName===element.tagName)
ix++;
}
}
/** hashcode function (credit http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery **/
var hashCode = function(str) {
var hash = 0, i, chr, len;
if (str.length === 0) return hash;
for (i = 0, len = str.length; i < len; i++) {
chr = str.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
};
/** Genaretes according to xpath + timestamp **/
var generateUID = function(ele)
{
return hashCode(getPathTo(ele)) + new Date().getTime();
}
First the xpath of the element is fetched.
The hashcode of the xpath is then computed. We therefore have a unique id per xpath.
The problem here is that xpath are not necesseraly unique if unique elements are generated on the fly. Thus we add the timestamp at the end.
Maybe we could also garantee more unique elements by adding a final Math.Random().
You could take advantage of closure.
var i = 0;
function generateId() {
return i++;
}
If you want to enclose it:
function generator() {
var i = 0;
return function() {
return i++;
};
}
var generateId = generator();
generateId(); //1
generateId(); //2
generator could accept a default prefix; generateId coud accept an optional suffix:
function generator(prefix) {
var i = 0;
return function(suffix) {
return prefix + (i++) + (suffix || '');
};
}
var generateId = generator('_');
generateId('_'); //_1_
generateId('#'); //_2#
This comes in handy if you want your id to indicate a sequence, very much like new Date().getTime(), but easier to read.
Combining random & date in ms should do the trick with almost no change of collision :
function uniqid(){
return Math.random().toString(16).slice(2)+(new Date()).getTime()+Math.random().toString(16).slice(2);
}
alert(uniqid()+"\r"+uniqid());
const generateUniqueId = () => 'id_' + Date.now() + String(Math.random()).substr(2);
// if u want to check for collision
const arr = [];
const checkForCollision = () => {
for (let i = 0; i < 10000; i++) {
const el = generateUniqueId();
if (arr.indexOf(el) > -1) {
alert('COLLISION FOUND');
}
arr.push(el);
}
};
I think if you really want to have a unique ID then the best approach is to use a library like: uuid or uniqueid
Note: Unique ID is not the same as Random ID
To use only date time milliseconds approach is wrong.
Nowadays computers are fast enough and able to run more than one iteration of a loop in a single millisecond.
npm install uuid
Importing the library:
If you are using ES modules
import { v4 as uuidv4 } from 'uuid';
And for CommonJS:
const { v4: uuidv4 } = require('uuid');
Usage:
uuidv4();
// This will output something like: 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d
For generate unique id's:
const uid = () =>
String(
Date.now().toString(32) +
Math.random().toString(32) +
Math.random().toString(32)
).replace(/\./g, '')
For check that is works:
var size = 500000
var arr = new Array(size)
.fill(0)
.map(() => uid())
var b = new Set(arr)
console.log(
size === b.size ? 'all ids are unique' : `not unique records ${size - b.size}`
)
I managed to have a non digit unique id with this function, so i'll like to share :)
const id = btoa((Math.random(0,(new Date()).getTime())).toString()).slice(0, -10);
I sliced it for DB varchar limit reasons, but you're free to do without it.

I want to add inputted numbers into an array, then add them together for now but I keep getting undefined array ? javascript html

This is my code and I am trying to get it to be put into an array and added together but keeps becoming an undefined array.
var myHTML = '';
let numa = [];
for (var i = 0; i < input; i++) {
myHTML += '<span class="test">Mark #' + (i + 1) + ' Name: ' + '</span> <input type="text" id="markname" <br/> <span class="markn">Mark: '
+ '</span> <input type="text" class="marknum" <br/><br/>'
;
wrapper.innerHTML = myHTML
var newinput = document.getElementsByClassName('marknum').value;
numa.push(newinput);
console.log(numa);
}
}
Everything other than the code that adds new HTML to the variable should be outside the loop.
getElementsByClassName returns a live nodelist which is an array-like list of elements. Even if there's only one element you can't grab the value like that.
It's not clear how you intend to check when the values have been changed, so in this example I'm going to use a button that calls a function to calculate the sum of the values in the inputs.
// Cache your elements, and add an event listener to
// the button
const wrapper = document.querySelector('#wrapper');
const button = document.querySelector('button');
button.addEventListener('click', handleClick, false);
let myHTML = '';
// Create your HTML
for (var i = 0; i < 3; i++) {
myHTML += `<label>Mark</label><input type="text" /><br />`;
}
wrapper.innerHTML = myHTML;
function handleClick() {
// Grab all the inputs in the `wrapper` container
const inputs = wrapper.querySelectorAll('input');
// `querySelectorAll` also returns an array-like list
// so we need to create an array from it before we
// can use array methods on it like: `reduce`
// Basically `reduce` takes an initial value, and then iterates
// over the array adding to that value
// and then passing that value back into the callback.
// Here: we're coercing the
// input value (a string) to a number, and then summing it with
// the sum from the previous iteration
const numa = Array.from(inputs).reduce((acc, input) => {
return acc + Number(input.value);
}, 0);
console.log(numa);
}
<div id="wrapper"></div>
<button>Calculate</button>
Additional documentation
reduce
querySelectorAll

jQuery - Find and remove multiple attributes of divs

I am trying to find all divs with class "comment-like" that also has "data-id" attributes equals to 118603,1234,1234,118601,118597 and if some div contains one of these data value, then remove that data attribute.
So far I created this, but it is not working currently.
remove_comments = 118603,1234,1234,118601,118597;
$('.comment-like').find('[data-id="' + remove_comments + '"]').removeAttr('data-id');
You can dynamically create a query string to capture all the divs you are looking for. The query string would look like this:
.comment-like[data-id="118603"], .comment-like[data-id="1234"], etc...
var ids = [118603, 1234, 1234, 118601, 118597];
var queryString = ids
.map(function(id) {
return '.comment-like[data-id="' + id + '"]';
})
.join(', ');
$(queryString).each(function(i, el) {
$(el).removeAttr('data-id');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="comment-like" data-id="118603"></div>
<div class="comment-like" data-id="1234"></div>
<div class="comment-like" data-id="0"></div>
Easy solution is just loop through your ids
remove_comments = 118603,1234,1234,118601,118597;
rc = remove_comments.split(",");
for (var i = 0; i < rc.length; i++) {
$('.comment-like').find('[data-id="' + rc[i] + '"]').removeAttr('data-id');
}
If you open the developer console in Chrome and typ this in:
remove_comments = 118603,1234,1234,118601,118597;
remove_comments
You can see what is actually happening.
One approach that should work is to put it in an array, and then loop over it:
var remove_comments = [118603,1234,1234,118601,118597];
for (var i = 0; i < remove_comments.length; i++) {
$('.comment-like').find('[data-id="' + remove_comments[i] + '"]').removeAttr('data-id');
}

function to change argument to another sign

I dynamically create this list element and information a user has typed in shows up in it when a button is clicked 'info' is text and shuld show as it is but 'grade' is a number that i want to convert to another sign with the function changeNumber() but I am new to javascript and cant figure out how to make this function, can anyone give a suggestion or point me in the right direction?
var list = $("#filmlista");
var list_array = new Array();
function updateFilmList()
{
document.getElementById("name").value = '';
document.getElementById("star").value = 0;
var listan = list_array[0][0];
var grade = list_array[0][1];
var element = '<li class="lista">' + list + '<span class="grade">'+ changeNumber(grade) +'</span></li>';
list.append(element);
}
should I use innerHTML? not shure I understand how it works? and how do I use the replace method if I have to replace many different numbers to the amount of signs the number is?
for example if the number is 5 it should show up as: *****, if number is 3 show up as: *** and so on
Here's some code that should do the trick:
Add this function into your script.
function changeNumber(number) {
var finalProduct = "";
for (var i = 0; i < number; i++) {
finalProduct += "*";
}
return finalProduct;
}
Replace the updateFilmsList with this code.
document.getElementById("name").value = '';
document.getElementById("star").value = 0;
var listan = list_array[0][0];
var grade = changeNumber(list_array[0][1]);
var element = '<li class="lista">' + list + '<span class="grade">'+ grade +'</span></li>';
list.append(element);
It looks like you're trying to do something like PHP's str_repeat. In that case, take a look at str_repeat from PHPJS
There are options other than a loop:
function charString(n, c) {
n = n? ++n : 0;
return new Array(n).join(c);
}
charString(3, '*'); // ***
You can use innerHTML to set the text content of an element provided none of the text might be mistaken for markup. Otherwise, set the textContent (W3C compliant) or innerText (IE proprietary but widely implemented) property as appropriate.

Get array of ids from e.g xx_numbers

How can I retrieve an array of ids with only a prefix in common?
E.g.
I've got a list of say 50 divs and they all got and ID looking like: aa_0000. Where 'a' is a prefix and '0' represents random numbers.
You want all elements of which their id starts with something common?
Assuming they are all div elements, this should work....
// Just so we can stay DRY :)
var prefix = 'aa_',
matchElement = 'div';
// Do we have an awesome browser?
if ('querySelectorAll' in document) {
var matchedDivs = document.querySelectorAll(matchElement + '[id^="' + prefix + '"]');
} else {
var allDivs = document.getElementsByTagName(matchElement),
matchedDivs = [],
regex = new RegExp('^' + prefix);
for (var i = 0, allDivsLength = allDivs.length; i < allDivsLength; i++) {
var element = allDivs[i];
if (element.id.match(regex)) {
matchedDivs.push(element);
}
}
}
console.log(matchedDivs.length); // Expect 3
jsFiddle.
If you want to explicitly match ones with numbers, try the regex /^aa_\d+$/.
If you have jQuery floating around, you can use $('div[id^="aa__"]').
For people using jQuery:
$('div[id^="aa_"]')

Categories

Resources