Dynamically Switching CSS Files - javascript

I get two error messages when trying to load the page with the code below. The error messages are:
"Uncaught TypeError: Cannot set property 'href' of null at HTMLAnchorElement.col.(anonymous function).onclick (http://www.jimbrink.org/:363:17)"
"Uncaught ReferenceError: bindEvents is not defined at (index):375"
I'm trying to create a link that, when clicked, would switch to another css file (with different fonts).
(BONUS QUESTION: Is there a way for the link to toggle between the two stylesheets?)
I've taken this from another StackOverflow question, but I can't seem to get the code to work my situation.
Here's the link:
<li>JB's Fav Font</li>
Here's the js function from the bottom of the page:
<!-- cssswitcher js -->
<script type='text/javascript'>
window.onload = function bindEvents(){
var css=document.getElementById('style');
var col=document.querySelectorAll('a.changeStyle');
/* iterate through collection and assign listener */
for( var n in col )if( col[n].nodeType==1 ) col[n].onclick=function(e){
e.preventDefault();/* prevent jumping to top of page etc */
var el=typeof(e.target)!='undefined' ? e.target : e.srcElement;
/* assign style attributes */
css.href=el.dataset.style;
css.rel=el.dataset.rel;
css.type=el.dataset.type;
/* store reference to style selected in localstorage */
localStorage.setItem( 'style', el.dataset.style );
};
/* if there is a reference to the user's css choice in storage, assign it */
if( localStorage.getItem( 'style' )!=null ) css.href=localStorage.getItem( 'style' );
}
document.addEventListener( 'DOMContentLoaded', bindEvents, false );
</script>

First, start by defining the original style like this:
HTML:
<link id="style" rel="stylesheet" type="text/css" href="style.css" />
Notice the id=style that we'll use to find the element. That might fix your first error.
For the second one, you have to decide whether to use document.addEventListener( 'DOMContentLoaded', bindEvents, false ); or window.onload because they're different functions. This might help you window.onload vs. body.onload vs. document.onready.
So, now we can do some Javascript:
HTML (the links):
<li><button onclick='setStyle("css/style.comicsans.css")'>JB's Fav Font</button></li>
<li><button onclick='setStyle("css/style.other.css")'>Other Font</button></li>
First, notice that I'm only using a parameter to call setStyle function on click, and btw it's better to work with <button>, this way we get a cleaner result.
Javascript:
var cssStyle = document.getElementById('style');
window.onload = function(){
if(localStorage && localStorage.getItem("style"))
cssStyle.href = localStorage.getItem("style");
};
function setStyle(newStyle){
cssStyle.href = newStyle;
if(localStorage)
localStorage.setItem("style", newStyle);
};
BONUS:
But if you are only be using two styles, then try to simplify, and do a shorthand method to do this:
HTML:
<li><button onclick='toggleStyle()'>Toggle</button></li>
<!-- cssswitcher js -->
<script type='text/javascript'>
var cssStyle = document.getElementById('style');
var listStyles = ["css/style.comicsans.css", "css/style.other.css"];
window.onload = function(){
if(localStorage && localStorage.getItem("style"))
cssStyle.href = localStorage.getItem("style");
};
function toggleStyle(){
var previousStyle = cssStyle.href;
if(previousStyle.endsWith(listStyles[0]))
newStyle = listStyles[1];
else
newStyle = listStyles[0];
cssStyle.href = newStyle;
if(localStorage)
localStorage.setItem("style", newStyle);
};
</script>

I did this in one of my projects using JQuery, I hope this will help you :
HTML :
<ul id="projectList">
<li class="project">Lorem Lorem</li>
<li class="project viewed">Lorem Lorem</li>
<li class="project viewed selected">Lorem Lorem</li>
<li class="project viewed selected">Lorem Lorem</li>
<li class="project viewed">Lorem Lorem</li>
</ul>
JavaScript with Dynamic CSS Code :
var data = {
"projectColors": {
"viewedColor": "#fffc20",
"selectedColor": "#ff7920"
}
};
var style = $(document.createElement("style")).attr("type", "text/css");
style.append("#projectList .project.viewed {color: " + data.projectColors.viewedColor + ";}");
style.append("#projectList .project.selected {color: " + data.projectColors.selectedColor + ";}");
style.appendTo("head");
JSFiddle :
https://jsfiddle.net/nikdtu/9gberp5o/

Related

Getting Uncaught TypeError but link works fine

Although there are some questions similar, ive read it and cant solve my problem.
Hope someone direct some light on this in order to help not only me but others out there.
I have added JS file to my child theme (wordpress) via functions.php and then added Event listener to ID.
The problem inhabits on the :Uncaught TypeError: Cannot read property 'addEventListener' of null
at custom.js?ver=5.4:1
Although when i click the object it goes correctly to the link, the error shows on console.
on functions.php ive added:
function my_customm_scripts() {
wp_enqueue_script( 'custom-js', get_stylesheet_directory_uri() . '/custom.js', array( 'jquery' ),'',true );
}
add_action( 'wp_enqueue_scripts', 'my_customm_scripts' );
and in my custom.js ive added:
document.getElementById('hercule').addEventListener('click', function() {
location.href = 'https://somedomain.com'
}, false);
im shure its straightforward but ive checked so many solutions without success
Thx for your time
J.
<div class="hoverfora wpb_animate_when_almost_visible wpb_slideInUp slideInUp wpb_column vc_column_container vc_col-sm-4 wpb_start_animation animated" id="hercule">
<div class="vc_column-inner vc_custom_1587337223984">
<div class="wpb_wrapper">
<div class="service_table_holder">
<ul class="service_table_inner">
<li class="service_table_title_holder background_color_type" style="">
<div class="service_table_title_inner">
<div class="service_table_title_inner2">
<h3 class="service_title" style="">Web design</h3>
<i class="qode_icon_font_awesome fa fa-desktop fa-3x" style="color: #efcd21 !important;"></i>
</div>
</div>
</li>
<li class="service_table_content" style="">Development de websites .
<p></p>
</li>
</ul>
</div>
</div>
</div>
</div>
I think that is
window.location.href = 'https://somedomain.com'
Try this instead,
If you are sure that your html source contains the element with id="hercule". Then you can try
window.onload = function () {
document.getElementById('hercule').addEventListener('click', function() {
location.href = 'https://somedomain.com'
// alert("clicked div");
}, false);
};
Make sure that all your onload processes are written in a single place. Otherwise the browser will keep and execute only last found window.onload function.
This code of yours:
document.getElementById('hercule')
is looking for a DOM element with id="hercule". Apparently, your page does not contain such an element at the time the custom.js is being run, so document.getElementById('hercule') is returning null, then a TypeError is thrown when addEventListener method is called on a null object.
Look for an element on your HTML page that contains id="hercule". If you can't find it, your problem has been identified.
Edit:
Subsequent information reveals that the custom.js JavaScript is trying to access a div with id="hercule" before it exists. The solution is to delay the start of the custom function until the div exists.
Normally, waiting for the window's load event is sufficient, but it is unclear in this case that will work, as "hercule" may be generated by another JavaScript function that won't complete by the time of the load event.
One solution is simply to continue to look for "hercule" until it's found:
// USE THIS SECTION IN YOUR CODE:
// keep looking for "hercule"
function lookForHercule() {
const hercule = document.getElementById('hercule');
if (hercule) {
// add the 'click' listener to 'hercule'
hercule.addEventListener('click', function() {
hercule.innerHTML = "You clicked hercule, redirecting to somedomain.com";
location.href = 'https://somedomain.com'
}, false);
return;
}
console.log('"hercule" not found');
setTimeout(lookForHercule, 2000);
}
// start looking
lookForHercule();
// END OF SECTION TO USE IN YOUR CODE
// THE CODE BELOW IS FOR THIS DEMO ONLY, DO NOT USE IN YOUR CODE:
// FOR DEMO ONLY:
// add 'hercule' after 10 seconds to simulate delay
setTimeout(() => {
const wrapper = document.getElementById('wrapper');
const hercule = document.createElement('div');
hercule.id = 'hercule';
hercule.innerHTML = "I am hercule. Clicking me will redirect to somedomain.com";
wrapper.appendChild(hercule);
}, 10000);
/* this will change cursor to finger when hovering over hercule */
#hercule:hover {
cursor: pointer;
}
<div id="wrapper">
<h4>Search for 'hercule'</h4>
</div>

Can't fire event "Uncaught ReferenceError: killMedia is not defined"

Im making WYSIWYG editor for web magazine with Riot.js.
I want to disply instagram image with delete icon when I click add button and delete instagram image when I click delete icon.
I can disply instagram image with delete icon but cant delete them because function killMedia doesnt work.
When I click delete icon, I get this error message.
Uncaught ReferenceError: killMedia is not defined
Im working on below code.
Does anyone know what problem is?
<div id='body-text' class='body-text' contenteditable=true data-placeholder='Body Contents'></div>
<label>
<input type='text' name='instagram' placeholder='Input Instagram embed code'>
<button onclick={ addInstagram }>add</button>
</label>
<script>
addInstagram(e) {
var embedCode = this.instagram.value
var instagram = document.createElement('div')
instagram.setAttribute('class', 'media-frame')
instagram.setAttribute('show', '{showMedia}')
instagram.innerHTML = embedCode
var i = document.createElement('i')
i.setAttribute('id', 'media-killer')
i.setAttribute('class', 'fa fa-times-circle-o media-killer')
i.setAttribute('aria-hidden', 'true')
i.setAttribute('show', '{showMedia}')
i.setAttribute('onclick', '{killMedia}')
var target = document.getElementById('body-text')
instagram.appendChild(i)
target.appendChild(instagram)
this.instagram.value = ''
this.update()
this.modal = false
}
this.showMedia = true
killMedia(e) {
this.showMedia = false
this.update()
}
</script>
The syntax you are using to define the function killMedia is incorrect. It seems like Riot.js has some alternative syntax for event handlers, which is why it works for addInstagram.
There are several ways to properly declare and define a function in JavaScript. One would be:
// declare variable killMedia and assign a function to it
var killMedia = function( e ){
this.showMedia = false;
this.update();
}
Also, I don't know anything about how Riot.js will call event functions, so, since you are using this inside your killMedia function, you may need to bind the proper this to the function as you declare it. One way to do this is with the bind function:
// declare variable killMedia and assign a function to it
var killMedia = function( e ){
this.showMedia = false;
this.update();
}.bind( this );
If the syntax is not the problem and Riot.js is handling the special declaration of killMedia, you may just need to declare killMedia before addInstagram, i.e. move that function above addInstagram. This is just a guess, since I don't know anything about Riot.js.
I added this as a comment but now I had time to implement this.
I think that is not working because you are trying to load the elements dynamically so is not compiled by Riot compiler.
A more Riot.js way to do what you want is to build another tag called instagram_image that you can remove, and have an array to iterate the images in the parent tag.
I simplified your code, but the idea is this:
(here is the plunker version http://plnkr.co/edit/EmkFhq0OyVtwSNIJqeJF?p=preview)
<my-tag>
<span each={img in instagram_images} >
<instagram_image embed_code={img.embed_code}></instagram_image>
</span>
<label>
<input type='text' name='instagram' placeholder='Input Instagram embed code'>
<button onclick={ addInstagram }>add</button>
</label>
<script>
this.instagram_images = []
addInstagram(e) {
var instagram_image = {embed_code: this.instagram.value}
this.instagram_images.push(instagram_image)
this.instagram.value = ''
}
</script>
</my-tag>
<instagram_image>
<div onclick={remove}>
{opts.embed_code}
</div>
<script>
this.remove = function() {
this.unmount()
this.update()
}
</script>
</instagram_image>

Calling a CSS within Javascript

I am creating a Safari extension that clears Outlook.com advertisements and other content. I have made two versions of the extension, one with CSS and one Javascript. However, there is a delay when removing the elements with Javascript. I was wondering is it possible to call a CSS file using Javascript so that it removes the elements quicker?
If anyone has made a Safari extension or is familiar with it, how can I make check box that will call a specific CSS file? For example, there is a CSS file called 'ads' and I have checkbox with the 'Key' ads and I want to be able to find a way so that I can call it when the checkbox has been checked.
I hope you understand what I am trying to say :) It is a bit difficult to write what I want to say.
Thanks.
This is the proxy.html file that calls the functions.
<!doctype html>
<html lang="en">
<head>
<script type="text/javascript">
var data = new Object();
safari.application.addEventListener( "message", function( e ) {
if( e.name === "getData" ) {
data.advertisements = safari.extension.settings.getItem( "advertisements" );
};
}, false );
</script>
</head>
<body>
</body>
</html>
Here is the script.js file.
$(function() {
safari.self.addEventListener( "message", function( e ) {
if( e.name === "setData" ) {
handleEvents( e.message );
}
}, false );
safari.self.tab.dispatchMessage( "getData" );
function handleEvents( e ){
if (e.advertisements !='show') {
var customStyles = document.createElement('style');
customStyles.appendChild(document.createTextNode('#RightRailContainer {display: none !important;} .WithRightRail {right: 0 !important;}'));
document.documentElement.insertBefore(customStyles);
}
Yes you can. In JavaScript you can use a function to create DOM elements:
document.createElement("link"); // Create CSS element.
Then you can use .setAttribute(attr, value) to give attributes to the created element. You can do something like this:
var file=document.createElement("link");
file.setAttribute("rel", "stylesheet");
file.setAttribute("type", "text/css");
file.setAttribute("href", "main.css");
Note: You can also set the property directly doing file.[attr] = [value]. For example, this does the same thing as the above code:
var file=document.createElement("link");
file.rel = "stylesheet";
file.type = "text/css";
file.href = "main.css";

Not able to change div display property using javascript

I am trying to change display property of my div using js but it is not showing any change.
In Js code if statements are not becoming true though in css all the properties are set properly.
Please help I am a novice and not understanding the issue. Below is the code I am trying
My HTML Code:
<!DOCTYPE html>
<html>
<head>
<script src="main.js" type="text/JavaScript"></script>
</head>
<body>
<nav id="nav">
<ul class="main_nav">
<li id="About_me">About Me</li>
<li id="home1">Home</li>
</ul>
</nav>
<div id="About">
<p>Hello</p>
</div>
<div id="home">
<p>hi</p>
</div>
</body>
My JS code:
function About_Me_Sel()
{
var Id;
Id =document.getElementById("About");
if(Id.style.display == "block")
{
Id.style.display = "none";
}
}
function Home_Sel()
{
var Id;
Id= document.getElementById("home");
if(Id.style.display == "none")
{Id.style.display = "block";
}
else
alert("hello");
}
That won't work the first time around. The style property is not connected to your stylesheet, so JavaScript doesn't know what rule's you've set there. The style property reflects a style attribute that gets written into your element's tag. (You can hard-code one into your HTML if you want.) So the value is always null until you set it.
Before
<div id="About">
After
<div id="About" style="display: block">
Try reversing the conditional
if (Id.style.display != "block")
{
Id.style.display = "block";
}
else
{
Id.style.display = "none";
}
I used Ajax to load in other content into my div, and i got the content from other php files. this way
function changecontent(name){
var htmlUrl = name;
$.ajax({
url: htmlUrl,
dataType: 'html',
success: function(data) {
console.log("Current page is: " + htmlUrl);
$("#maincontent").fadeOut(function() {
$(this).html(data).fadeIn("fast");
});
}
});
}
HTML:
<!DOCTYPE html>
<html>
<head>
<script src="main.js" type="text/JavaScript"></script>
</head>
<body>
<nav id="nav">
<ul class="main_nav">
<li id="About_me">About Me</li>
<li id="home">Home</li>
</ul>
</nav>
<div id="maincontent">
<p>Hello</p>
</div>
<div id="home">
<p>hi</p>
</div>
</body>
eks: about_me.php
<a>I'awesome</a>
You have a number of errors in your code; I'll try to go through them one-by-one to clarify those problems and offer solutions that should, hopefully, lead to better understanding (and practice) in future.
First, an id "...assigns a name to an element. This name must be unique in a document."1. Note the 'must,' which means that a document with a duplicated id (more than one element sharing the same id value makes the document invalid, and causes problems with JavaScript, since it will only ever look for one element with a given id, and recover from invalid documents with unpredictable results).
That being the case I've amended the ids of the div elements by, effectively, adding the Content string, and amended the ids of the li elements to be single word and lower-case so that they can be predictably made to reference each other. This gives the following HTML:
<nav id="nav">
<ul class="main_nav">
<li id="about">About Me</li>
<li id="home">Home</li>
</ul>
</nav>
<div id="aboutContent">
<p>Hello</p>
</div>
<div id="homeContent">
<p>hi</p>
</div>
JS Fiddle (this still doesn't work as you'd intend, because the other problems still exist; it's merely to show the corrected/amended HTML).
Now, the JavaScript.
The reason it can't work, as noted by #Jeffman in his answer is because element.style references only the in-line styles of an element (those set with the style attribute), not the styles set with either a stylesheet or in the head of the document. This means you're comparing an undefined variable with a string, which will always be false.
You're also using two functions to do the same thing, which is wasteful and unnecessary. This is the second reason I've modified the ids of the various elements. A modified (single) function to do what you want to do:
function sel(e, self) {
// e is the click event,
// self is the 'this' DOM node
var id = self.parentNode.id,
toggle = document.getElementById(id + 'Content'),
display = toggle.style.display;
toggle.style.display = display == 'block' ? 'none' : 'block';
}
The above requires the following HTML for the a elements:
About Me
JS Fiddle demo.
Now, this still requires obtrusive JavaScript (using in-line event-handling within the HTML itself, which requires updates every time you may want to add further event-handling or change the function to call upon those events). Therefore I'd suggest moving to a more unobtrusive version, such as:
function sel(e, self) {
// e is the click event,
// self is the 'this' DOM node
var id = self.parentNode.id,
toggle = document.getElementById(id + 'Content'),
display = toggle.style.display;
toggle.style.display = display == 'block' ? 'none' : 'block';
}
var links = document.getElementById('nav').getElementsByTagName('a');
for (var i = 0, len = links.length; i < len; i++) {
links[i].onclick = function (e){
sel(e, this);
};
}
JS Fiddle demo.
Now, while this works, this still requires assigning event-handlers to multiple elements (despite being more easily done using a loop within JavaScript itself).
The easier way is to delegate the event-handling to the parent element, and assess, in the function itself, where the event originated. Which would give the following JavaScript:
function sel(e) {
// e is the click event,
// self is the 'this' DOM node
var self = e.target,
id = self.parentNode.id,
toggle = document.getElementById(id + 'Content'),
display = toggle.style.display;
toggle.style.display = display == 'block' ? 'none' : 'block';
}
var nav = document.getElementById('nav');
nav.addEventListener('click', sel);
JS Fiddle demo.
Notes:
http://www.w3.org/TR/html401/struct/global.html#h-7.5.2.
References:
addEventListener().
document.getElementById().
getElementsByTagName().
Node.parentNode.

Changing background image once using either CSS or JavaScript [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I've been asked by a friend to set up a web page whose background changes only once when any of the links are clicked. I've tried a few examples given on this site and looked at a few on Google, but they are pretty much all gallery-style cyclical changes. Perhaps I'm just frustrated and not seeing the trees for the forest...
The page is # mysite/julie
The images are # mysite/julie/images/blogbka.png and /images/blogbk.png
I used the solution found below ( https://stackoverflow.com/a/11362465/1506620 )
Thank you all for your help.
var is=true;
document.body.onclick = function( e ) {
if ( e.target.tagName === 'A' ) {
if(is){
document.body.style.background='url(http://adultdave.co.uk/julie/images/blogbka.png)';is=false;}
}
};
I hope this is what you want, i.e change image only once. If not, do tell me.
var ischange=true;
document.onclick=function(e){
if( e.target.tagName === 'A' &&ischange)
{
document.body.style.background='url(/*Image url*/)';
ischange=false;
}
}
DEMO
Here on each page load a variable ischange is initialized true, and when any link is clicked, the background is changed and ischange is set to false, so that no more background changes are allowed.
One approach would be to use a function that wraps another function, only performing the inner function the first time its called. This is a fairly generic version of such:
var once = function(fn, thisObj) {
var called = false;
var val;
return function() {
if (called) {return val;}
called = true;
val = fn.apply(thisObj || null, arguments);
return val;
};
};
Then you can wrap your MooTools event listener inside a call to once() and know that it will only be called one time. This can be a rather elegant solution to such problems.
I have done bins for changing page background randomly on click of each link using jQuery.
So, please follow the steps:
1) Include Latest Jquery java script file on header.
2) HTML:
<div>
<p>
When you click on following paragraph link, every time it will be changed random background of the page
</p>
<p>
You have a present that was really memorable. It could have been given for an
<a href="#">
important occasion
</a>
or just for no reason at all. Tell us about the present and why it was memorable. Include the reason it was given, a
<a href="#">
description
</a>
of it, and how you felt when you got it.
The objective is to write a
<a href="#">
narrative essay
</a>
about this present you were given
The subject is a
<a href="#">
memorable present
</a>
The three main subtopics are:
<ul>
<li>
<a href="#">
the reason it was given
</a>
</li>
<li>
<a href="#">
a description of it and
</a>
</li>
<li>
<a href="#">
how you felt when you got it
</a>
</li>
</ul>
</p>
</div>
CSS:
body{
background:url("http://www.freeppt.net/background/modern-pink-floral-backgrounds-for-powerpoint.jpg");
}
JQuery:
var items = new Array();
items[0] = "http://www.publicdomainpictures.net/pictures/10000/nahled/abstract-orange-background-29541280675430Trj9.jpg";
items[1] = "http://www.psdgraphics.com/wp-content/uploads/2011/03/red-flowing-background.jpg";
items[2] = "http://www.dvd-ppt-slideshow.com/images/ppt-background/background-3.jpg";
items[3] = "http://www.purplebackgrounds.net/wp-content/uploads/2011/10/purple-heart-background-680x544.jpg";
items[4] = "http://www.freeppt.net/background/beautiful-on-green-backgrounds-for-powerpoint.jpg";
$(function() {
var bg_img = '',
last_bg = '';
$("a").click(function() {
bg_img = items[Math.floor(Math.random() * items.length)];
if (last_bg == bg_img) bg_img = items[Math.floor(Math.random() * items.length)];
last_bg = bg_img;
$("body").css('backgroundImage', 'url(' + bg_img + ')');
});
});
Try DEMO on http://codebins.com/codes/home/4ldqpbc
As I see it, you'll want to provide the user's browser a cookie to chew on and track the click event(s) for all anchor tags on the page. You could do something like:
Mock Html
<ul id="ActionLinks">
<li>Click Me!</li>
<li>Clear Cookie!</li>
</ul>
jQuery Code
<script type="text/javascript">
$(document).ready(function () {
// Array to store options
var settings = {
getRadomImage: function () {
//Update this with your different images.
var items = [
"http://adultdave.co.uk/julie/images/blogbka.png",
"http://adultdave.co.uk/julie/images/blogbka.png",
"http://adultdave.co.uk/julie/images/blogbka.png",
];
var img = items[Math.floor(Math.random() * items.length)];
return img;
},
cookieName: "tracker",
cookieOpts: { expires: 1} //cookie plug-in options
}
//Capture all tracked clicks
$(".tracked-click").click(function (e) {
//Check to see if there is a cookie for the user.
if ($.cookie(settings.cookieName) != "true") {
var img = document.createElement('img');
img.src = settings.getRadomImage();
//Load up the new image and show it once it's loaded.
img.onload = function () {
//Fade out the body and fade back
/*
$("body").fadeOut(function() {
$(this).css({ 'background-image': 'url(' + img.src + ')' }).fadeIn(4000);
});
*/
$("body").css({ 'background-image': 'url(' + img.src + ')' });
};
//Set the cookie for the client for tracking.
$.cookie(settings.cookieName, true);
}
});
//Action for clearing cookie.
$(".action-clear-cookie").click(function () {
$.cookie(settings.cookieName, null)
});
});
</script>
Code References
https://github.com/carhartl/jquery-cookie
Set up a jQuery script :
$("a").click(function(evt) {
$('body').css("background-image", "url(/myimage.jpg)");
}
If you want it to change between the pages, use a session cookie (i used "simpler" functions, see (this)[http://jquery-howto.blogspot.fr/2010/09/jquery-cookies-getsetdelete-plugin.html] for instance) :
if (getCookie('isAlreadyVisited') == 1) {
$('body').css("background-image", "url(/myimage.jpg)");
}
setCookie('isAlreadyVisited', 1);
You can also handle this by setting the cookie on the server side and add a class to your body, which changes the background (you won't have Flash of Unstyles Content this way).
body {
background-image: url(img1.jpg);
}
body.visited {
background-image: url(img2.jpg);
}

Categories

Resources