Display an image who is in bytes of array in my View - javascript

My goal is:
I load a person's data into a template that I send to my view.
The user can modify his data and click on 'Submit'.
Almost everything works as it should except for the image.
Indeed, in the method of my controller, I receive the image in bytes of array and I do not find how to display it in my view.
I found several explanations on the net where it is written that I have to use the URI scheme.
But I do not know how since my controller send this data in my code js.
I'd like to say that the input with #Model.Member_Picture contains exactly "value="System.Byte[]".
Could you enlighten me?
(I just post the code related to the image).
Model
[ContainerDataFor("Picture_gr")] // use the name in your C# model
public byte[] Member_Picture { get; set; }
View
<div class="form-check-inline col-xs-6" style="margin-top:2%">
<img id="ItemPreview" src="" accept="image/png, image/jpeg"/>
</div>
<div class="form-check-inline col-xs-6" style="margin-top:2%">
#Html.LabelFor(model => model.Member_Picture, "Upload Picture", new { htmlAttributes = new { #id = "test" } })
<input type="file" class="form-control-file" name="file" id="file" value="#Model.Member_Picture">
<small id="fileHelp" class="form-text text-muted">Maximum 1024kb</small>
</div>
$(document).ready(function () {
var img = $('#test').val();
$('#ItemPreview').attr('src', `data:image/png;base64,${img}`);
});

You cannot use value attribute inside <input type="file" /> to render the image. You need to render the image as Base64 string with this setting:
#{
var base64Image = Convert.ToBase64String(Model.Member_Picture);
var source = String.Format("data:image/png;base64,{0}", base64Image);
}
<img src="#source" width="100%" height="100%" />
Or create another string property like this:
public byte[] Member_Picture { get; set; }
public string Image
{
get
{
return String.Format("data:image/png;base64,{0}", Convert.ToBase64String(Member_Picture));
}
}
And show it in the view:
<img src="#Model.Image" width="100%" height="100%" />

change your src to src="data:image/png;base64, +data" where the data is the byte array of the image
<img id="ItemPreview" src="data:image/png;base64, #Model.Member_Picture">
example
<div>
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsBAMAAACLU5NGAAAAG1BMVEUAmf////8fpf9fv/8/sv+f2P/f8v9/zP+/5f8U6SNkAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAC1UlEQVR4nO3Xz0/aYBzH8doW8biOH3IsoswjDJd4bBnuTBez7GjN5nbUxOxMXaL+2fs+T4t92g6ywyO7vF8J7Ye0ha/fp34BxwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMD/5c5mYRE/njVT02j2vkj+bN5IllwEQdC/1fUlQTAIq6nJl2PBDx1PJD3UkiWtwX3ong9UjA/n7q9ONTUlT3NnlK1UgcF9+C6dVJIt7Uht75bSon4omywy01/c66t6shley+a0W0l2HbyRx1uVxldm2ihTj1A27k0l2dWWQuKlTh0zbXQXOn4vTxMjWeZJWUmo48BMG01Dp6V76uytjGRZSxaxWIPMTEo7L9KLzAsSx9m/0knWv0yWDWXZ8pVQy1Mmfexa76Yr43xXTtnTS63Wv0x2uTdyqxzmeTopk9756v/S8SpLOpZSYz3sHK9jJKv85KF80XhSpmKv2lVp1oWateuqO0ay6OtjcCk7rxg7w9syFVVLu4xmtZ7TXqQKjfRT99BINst6Tr9H28pS7TKa1Xp+7F++flnyeue9rWX5/VF1WIyy5Q7KkrdYbivLidNV9Xyvt5Oy2t2XGzae+LVbXg4HYe2Cu2h9WM6OX+OWV2QOvYyFqEzrw9PsunaBDLr1WOgaybbB5nGqZlY+uwzyQfP649Rxfm7+8NEzK661a3+1iw8f1aC8OW4laWpm1dslDSqaMzSTZV5nyxcbPbNq7ZpOdvHFZl/+3gN9xw5XZtIl65lVa1cqz/TXV1cdLZMtJ+rd3GyiPq/lxd00MpNRXpzv3M9qe6o6Of6mNt1KsmU8ODsaJXokfOrNj5NuNQkv3/lhXlb6e370IVAjwU8vjy6CqJKsOZffUj39lu6XZmryMrlAt8w5SZvJGn/x8svzeNFMTYtFWCR3ETUSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4N38AsX524K81XMUAAAAASUVORK5CYII=" alt="Red dot" />
</div>

You can do it like this with vanilla Javascript:
var fileInput = document.getElementById('img-input');
var demo = document.getElementById('demo');
fileInput.addEventListener('change', function(e) {
var file = fileInput.files[0];
var imageType = /image.*/;
if (file.type.match(imageType)) {
var reader = new FileReader();
reader.onload = function(e) {
demo.src= e.target.result;
}
reader.readAsDataURL(file);
} else {
demo.src = '';
console.log('file not supported');
}
});
#demo {
margin-top: 20px;
}
#img-input {
background-color: #4CAF50;
border: none;
color: white;
text-align: center;
padding: 12px 20px;
display: inline-block;
font-size: 18px;
margin: 20px auto;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<input type="file" id="img-input" name="file"></div>
<!-- this is where we display the choosen image //-->
<img id="demo" src="#" alt="your image here" />

Related

iframe pdf preview at change event

$(document).ready(function(){
$("#id_quotationFile").change(function(){
url=$("#id_quotationFile").attr(value);
$("#pdf-view").attr("src",url);
});
});
Trying to load pdf file browsed in input field using above jquery code but not working
<input type=file id="id_quotationFile">
<iframe src="" id="pdf-view" frameborder="0px" title="Preview"></iframe>
How to preview pdf file in iframe.
Consider the following.
$(function() {
function readURL(input, target) {
input = $(input).get(0);
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
$(target).attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
$(".details").html("Preview: '" + input.files[0].name + "' Type: " + input.files[0].type);
}
}
$("#fileUpload").click(function() {
$("#id_quotationFile").trigger("click");
});
$("#id_quotationFile").change(function() {
readURL(this, $("#pdf-view"));
});
});
input {
display: none;
}
button {
padding: 0.2em 0.4em;
margin: 0.2em;
}
iframe {
width: 95%;
height: 840px;
}
.details {
font-family: Arial, Monospace;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="fileUpload">Browse...</button>
<input type=file id="id_quotationFile">
<span class="details"> </span>
<iframe src="" id="pdf-view" frameborder="0px" title="Preview"></iframe>
Reference: Preview an image before it is uploaded
You need to convert the local file into a new FileReader. So we read the File, convert it into a Base64, and when it's ready, load it into the Target.
As mentioned in this question, try to force your iframe to reload:
document.getElementById('some_frame_id').contentWindow.location.reload();
What’s the best way to reload / refresh an iframe?
In your case it will be:
$("#pdf-view").contentWindow.location.reload();

How to add a close button to base64 image preview?

In a form I'd like to let users to upload multiple images, one by one, and also let them to remove each image by clicking on a x button:
Here is my javascript/jQuery which adds the Base64 image previews to the DOM:
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
let image = new Image();
image.src = `${e.target.result}`;
image.className = "img-thumbnail add-image-thumb";
let closeBtn = `<button type="button" class="close" aria-label="Close">
<span aria-hidden="true">×</span> </button>`;
$('#images-to-upload').append(image);
$('#images-to-upload').append(closeBtn);
}
reader.readAsDataURL(input.files[0]);
}
}
$("#imgInp").change(function () {
readURL(this);
});
And the html part:
<div id="images-to-upload" class="mb-3"> </div>
<div class="input-group mb-3">
<div class="custom-file">
<input type="file" class="custom-file-input" id="imgInp" >
<label class="custom-file-label" for="image-input"></label>
</div>
</div>
<small class="form-text text-muted">Upload one or more images</small>
<br>
CSS:
.add-image-thumb{
max-height: 64px;
margin: 10px;
padding: 5px;
}
This works fine for one image, but for multiple images, all x go to the right side of the page.
The problem is how can I put the x button at the top left corner of each image.
I could not construct a div out of Base64 images, otherwise I could mangage to acheive that through CSS.
I could not construct a div out of Base64 images, otherwise I could mangage to acheive that through CSS.
Wrap a div around the image
let wrapper = $('<div class="image-wrapper" />');
$('#images-to-upload').append(wrapper)
$(wrapper).append(image);
$(wrapper).append(closeBtn);
Use the below CSS
.image-wrapper {
display: inline-block;
position: relative;
}
.image-wrapper button {
position: absolute;
top: 0;
right: 0;
}

How can i select multiple pictures using change event?

How can i select multiple pictures. right now i'm able to get pictures single-single but i want to get multiple. how can i do that?
What i tried:-
$(function() {
$(document).on('change', '.caFileBtn', function() {
console.log(imagePath);
var files = this.files
for (var i = 0; i < files.length; i++) {
var file = files[i];
var imagePath = URL.createObjectURL(file);
}
var imageElement = `
<div>
<img src="${imagePath}" />
</div>
`;
$('.ca-photos-area').show();
$('.ca-photos-area').prepend(imageElement);
$(this).val('');
});
});
.ca-photos-area {
display: flex;
}
.ca-photos-area img {
width: 90px;
height: 90px;
margin: 5px;
border: 1px solid #ccc;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<div class="btn btn-primary btn-block">
<input type="file" Multiple="Multiple" class="caFileBtn" />
<i class="fas fa-image"></i>
</div>
<div class="col-md-12">
<div class="ca-photos-area mt-3">
</div>
</div>
Answer will be appreciated
While html tags and attributes are case-insensitive it's best practice to use lowercase. Though, attribute values are case-sensitive. So, you must define multiple in lowercase:
<input type="file" multiple="multiple" class="caFileBtn" />
<!-- Must be in lowercase --- ^^ -->
<!-- Otherwise, it's still single type -->
Or, you could also have defined attribute only:
<input type="file" multiple class="caFileBtn" />
The above answer seems to be wrong in the case of multiple attribute as it accepts boolean and using it case-insensitive also meant to be multiple.
Here's the working javascript code that will select all selected images:
$(function() {
$(document).on('change', '.caFileBtn', function() {
console.log(imagePath);
var files = this.files
var imageElement = [];
for (var i = 0; i < files.length; i++) {
var file = files[i];
var imagePath = URL.createObjectURL(file);
imageElement.push( `
<div>
<img src="${imagePath}" />
</div>`);
}
for (var i=0; i<imageElement.length; i++){
$('.ca-photos-area').prepend(imageElement[i]);
}
$('.ca-photos-area').show();
$(this).val('');
});
});
Issue
The majority of the statements within handler were outside the for loop. BTW I removed the reset statement: $(this).val('') because the tag would always display: "no files selected..." whether there were files selected or not. Without the reset you get a list of the file names selected.
Demo
$('.caFileBtn').on('change', function() {
const self = $(this)[0];
const files = self.files;
let imagePath, imageTag;
for (let i = 0; i < files.length; i++) {
let file = files[i];
imagePath = URL.createObjectURL(file);
imageTag = `<figure><img src="${imagePath}"></figure>`;
$('.ca-photos-area').prepend(imageTag);
}
});
.ca-photos-area {
display: flex;
}
.ca-photos-area img {
width: 90px;
height: 90px;
margin: 5px;
border: 1px solid #ccc;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
<link href="https://use.fontawesome.com/releases/v5.13.0/css/all.css" rel="stylesheet" crossorigin="anonymous">
<label class="btn btn-primary btn-block">
<input class="caFileBtn" type="file" multiple="multiple">
<i class="fas fa-image"></i>
</label>
<section class="col-md-12">
<article class="ca-photos-area mt-3"></article>
</section>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js'></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/js/bootstrap.min.js"></script>

jquery preview image not working

So I'm trying to implement a preview button so that when my users clicks on the upload button image they could have a preview but the thing is that it is not working, I wonder why ?? A brief description : I have a js function that creates new elements and append it to a p tag date. It is in this function that is going to create the preview image code
// code for creating new elements
function createElements(){
const userQuestions = document.querySelector('#userQuestions');
userQuestions.insertAdjacentHTML(
'beforeend', '<div class="uploader" onclick="$(\'#filePhoto\').click()"><p id="bg-text">No image</p></div><input type="file" name="userprofile_picture" id="filePhoto" style="display:block;width:185px;" /></center><div class="grid-container">'
);
}
///Code to preview image
function handleImage(e) {
var imageLoader = document.getElementById('filePhoto');
imageLoader.addEventListener('change', handleImage, false);
var reader = new FileReader();
reader.onload = function (event) {
$('.uploader').html( '<img width="300px" height="350px" src="'+event.target.result+'"/>' );
}
reader.readAsDataURL(e.target.files[0]);
}
.uploader {width:50%;height:35%;background:#f3f3f3;border:2px dashed #0091ea;}
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div id="userQuestions"></div>
<button type="button" onclick="createElements()">add elements</button>
</body>
</html>
If you run the snippet above you can see that the button is woeking but the preview is not showing. Could someone help me?
HTML:
<div class="row">
<div class="col-xs-4">
<div class="form-group">
<label>Company Logo</label>
<input type="file" class="form-control" value="" name="companyLogo" id="companyLogo" accept="image/*" />
</div>
</div>
<div id="displayImage">
<img id="imgData" src="#" alt="your image" height="150px" width="150px" />
</div>
</div>
JavaScript:
$("#companyLogo").change(function(e) {
if(e.target.value === "") {
$("#displayImage").hide();
} else {
$("#displayImage").show();
}
readURL(this);
});
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
$("#imgData").attr("src", e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
Short n simple
No need to create an element on click.
Just add an image tag and set a default image like no image selected or something like that.
The following code will help you
<input type="file" name="myCutomfile" id="myCutomfile"/>
<img id="customTargetImg" src="default.jpg" width="400" height="250">
$("#myCutomfile").change(function() {
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#customTargetImg').attr('src', e.target.result);
}
reader.readAsDataURL(this.files[0]);
}
});
Take advantage of jQuery -- particularly using
event handlers
delegated event handlers for dynamically-created elements
tree traversal methods.
$(function() {
var userQuestions = $('#userQuestions');
// create onclick event handler for your button
$('#addElements').click(function() {
// IDs must be unique - since you can have an arbitrary number of filePhoto, use a class instead
userQuestions.append(
'<div class="uploader"><p id="bg-text">No image</p></div><input type="file" name="userprofile_picture" class="filePhoto" /><div class="grid-container"></div>'
);
});
// create delegated onclick event handler for your .uploader
userQuestions.on('click', '.uploader', function() {
// you only want to target the file input immediately after it
$(this).next('[type=file]').click();
});
// create delegated onchange event handler for your .filePhoto
userQuestions.on('change', '.filePhoto', function() {
// find related uploader
var uploader = $(this).prev('.uploader');
// check file was given
if (this.files && this.files.length) {
var reader = new FileReader();
reader.onload = function(event) {
uploader.html('<img width="300px" height="350px" src="' + event.target.result + '"/>');
}
reader.readAsDataURL(this.files[0]);
}
});
});
.uploader {
width: 50%;
height: 35%;
background: #f3f3f3;
border: 2px dashed #0091ea;
}
.filePhoto {
display: block;
width: 185px;
}
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div id="userQuestions"></div>
<!-- added ID attribute -->
<button type="button" id="addElements">add elements</button>
</body>
</html>
Edit
This answer is a non-jQuery solution based off your comment.
// code for creating new elements
function createElements() {
// no need to document.querySelector if the selector is an ID
const userQuestions = document.getElementById('userQuestions');
// you want to use onclick/onchange attributes here as they are dynamically created
userQuestions.insertAdjacentHTML(
'beforeend', '<div class="uploader" onclick="selectFile(this)"><p id="bg-text">No image</p></div><input type="file" name="userprofile_picture" onchange="handleImage(this)" />'
);
}
// trigger click on file input that follows the uploader
function selectFile(uploader) {
uploader.nextSibling.click();
}
///Code to preview image
function handleImage(input) {
if (input.files.length) {
var reader = new FileReader();
reader.onload = function(e) {
input.previousSibling.innerHTML =
'<img width="300px" height="350px" src="' + e.target.result + '"/>';
}
reader.readAsDataURL(input.files[0]);
}
}
.uploader {
width: 50%;
height: 35%;
background: #f3f3f3;
border: 2px dashed #0091ea;
}
.filePhoto {
display: block;
width: 185px;
}
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div id="userQuestions"></div>
<button type="button" onclick="createElements()">add elements</button>
</body>
</html>

upload with multiple image preview

I am using this source: http://opoloo.github.io/jquery_upload_preview/
until now, I can upload one image with preview.
<style type="text/css">
.image-preview {
width: 200px;
height: 200px;
position: relative;
overflow: hidden;
background-color: #000000;
color: #ecf0f1;
}
input[type="file"] {
line-height: 200px;
font-size: 200px;
position: absolute;
opacity: 0;
z-index: 10;
}
label {
position: absolute;
z-index: 5;
opacity: 0.7;
cursor: pointer;
background-color: #bdc3c7;
width: 200px;
height: 50px;
font-size: 20px;
line-height: 50px;
text-transform: uppercase;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
text-align: center;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$("image-preview").each(
function(){
$.uploadPreview({
input_field: $(this).find(".image-upload"),
preview_box: this,
label_field: $(this).find(".image-label")
});
}
);
});
</script>
<!--| catatan penting: yang penting action="" & input type="file" name="image" |-->
<form action="upload.php" method="POST" enctype="multipart/form-data">
<div class="image-preview">
<label for="image-upload" class="image-label">+ GAMBAR</label>
<input type="file" name="my_field[]" class="image-upload" />
</div>
<div class="image-preview">
<label for="image-upload" class="image-label">+ GAMBAR</label>
<input type="file" name="my_field[]" class="image-upload" />
</div>
<input type="submit"/>
</form>
then try to add more div class image preview, i want add another button with image preview. i don't want multiple upload with one button.
$(document).ready(function() {$.uploadPreview => use id, of course when change to class and add more div, when upload a button, another button will change. i am confused with the logic. Anyone can help? maybe using array but, i don't know how..
Since upload button is dependent on state of uploadPreview you need to initialize for each div separately to get separate upload buttons.
Change your html like this give each container a class say imgBox
<div class="imgBox">
<label for="image-upload" class="image-label">Choose File</label>
<input type="file" name="image" class="image-upload" />
</div>
.....
....
...
<div class="imgBox">
<label for="image-upload" class="image-label">Choose File</label>
<input type="file" name="image" class="image-upload" />
</div>
..
Now initialize each one using jquery each()
$(".imgBox").each(
function(){
$.uploadPreview({
input_field: $(this).find(".image-upload"),
preview_box: this,
label_field: $(this).find(".image-label")
});
});
I created a simple image uploading index.html file for image uploading and preview.
Needs j-query.No need of extra plugins.
If you have any questions ask me ;)
//to preview image you need only these lines of code
var imageId=idOfClicked;
var output = document.getElementById(imageId);
output.src = URL.createObjectURL(event.target.files[0]);
Check it here:
https://jsfiddle.net/chs3s0jk/6/
I have one better option for the file upload it's easy to use and you can try it.
window.onload = function(){
if(window.File && window.FileList && window.FileReader){
$(document).on("change",'.file', function(event) {
var files = event.target.files; //FileList object
var output = document.getElementById("upload-preview");
$("#upload-preview").html("");
if(files.length>5){
$(".file").after("<div class='alert alert-error'><span class='close'></span>Maximum 5 files can be uploaded.</div>");
$(this).val("");
return false;
}
else{
$(".file").next(".alert").remove();
}
for(var i = 0; i< files.length; i++)
{
var file = files[i];
//Only pics
// if(!file.type.match('image'))
if(file.type.match('image.*')){
if(this.files[0].size < 2097152){
// continue;
var picReader = new FileReader();
picReader.addEventListener("load",function(event){
var picFile = event.target;
var div = document.createElement("div");
div.className = "upload-preview-thumb";
div.style.backgroundImage = 'url('+picFile.result+')';
output.insertBefore(div,null);
});
//Read the image
$('#clear, #upload-preview').show();
picReader.readAsDataURL(file);
}else{
alert("Image Size is too big. Minimum size is 1MB.");
$(this).val("");
}
}else{
alert("You can only upload image file.");
$(this).val("");
}
}
});
$(".file2").change(function(event){
var err=0;
var input = $(event.currentTarget);
var ele = $(this);
var file = input[0].files[0];
var u = URL.createObjectURL(this.files[0]);
var w = ele.attr("data-width");
var h = ele.attr("data-height");
var img = new Image;
img.onload = function(){
if(w){
if(img.width!=w || img.height!=h){
ele.parent().find(".alert").remove();
ele.parent().find(".upload-preview").before("<div class='alert alert-error'>Please upload a image with specified dimensions.</div>");
ele.val("");
}
else{
ele.parent().find(".alert").remove();
}
}
};
img.src = u;
var nh;
if($(this).attr('data-preview')=='full')
nh = (h/w*150)
else
nh=150
var preview = ele.parent().find(".upload-preview");
var reader = new FileReader();
preview.show();
reader.onload = function(e){
image_base64 = e.target.result;
preview.html("<div class='upload-preview-thumb' style='height:"+nh+"px;background-image:url("+image_base64+")'/><div>");
};
reader.readAsDataURL(file);
});
}
else
{
console.log("Your browser does not support File API");
}
}
above code save as one js file like file-upload.js
then link it to your file where you want perview.
i.e.
<script src="js/file-upload.js" type="text/javascript"></script>
use this kind of example for the input type
<input type="file" class="file2" name="page-image" id="page-image"/>
that works on the class that name is "file2" that class you given to the input field that able to create preview.
full structure something like below.
HTML Code you can try
<input type="file" class="file2" name="page-image[]" id="page-image"/>
<div class="clearfix"></div>
<div class="upload-preview" style="display: block;">
<div class="upload-preview-thumb">
// perview genereate here
// you can display image also here if uploaded throw the php condition in edit image part
</div>
</div>
<input type="file" class="file2" name="page-image[]" id="page-image"/>
<div class="clearfix"></div>
<div class="upload-preview" style="display: block;">
<div class="upload-preview-thumb">
// perview genereate here
// you can display image also here if uploaded throw the php condition in edit image part
</div>
</div>
CSS
.upload-preview {
border: 1px dashed #ccc;
display: block;
float: left;
margin-top: 10px;
padding: 5px;
}
.upload-preview-thumb {
background-position: 50% 25%;
background-size: cover;
float: left;
margin: 5px;
position: relative;
width: 139px;
}
Hope this works and in future it's helpful for you.
Thanks.

Categories