> For the complete documentation index, see [llms.txt](https://alix-lahuec.gitbook.io/zotero-roam/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://alix-lahuec.gitbook.io/zotero-roam/v0.5/customization/formatting/code-snippets.md).

# Code snippets

This is a list of JavaScript snippets which can be used as building blocks when writing custom formatting functions. They correspond to common operations, like [converting a full date into Roam's Daily Notes format](/zotero-roam/v0.5/customization/formatting/code-snippets.md#converting-dateadded-into-roam-format) or [processing an array of author names into a comma-separated list](/zotero-roam/v0.5/customization/formatting/code-snippets.md#data-creators). Most of them are taken straight from the extension's built-in `getItemMetadata` function.

{% hint style="warning" %}
In all examples below, **metadata** is an array to which the element gets added. If you're creating a function for nested output, this might not always suit your purposes - so adjust accordingly. See [Nesting metadata](/zotero-roam/v0.5/customization/formatting/nesting-metadata.md) for examples of nested JavaScript output.
{% endhint %}

## Generic snippets

#### Basic function structure

```javascript
window.myFuncName = function(item){
    let metadata = [];
    
    // Code snippets should be here
    
    return metadata = [];
}
```

* If you're writing a function that **creates a flat output**, you can simply add blocks by using `metadata.push("String of block")`.
* If you're writing a function that **creates a nested output**, you'll have to create intermediate [Object blocks](/zotero-roam/v0.5/customization/formatting/nesting-metadata.md#object-blocks) during the processing/formatting of the item's data.

#### Adding an element as block if it exists

```javascript
if(item.<element_path>{
    metadata.push(item.<element_path>);
}

// Example :

// Item title, located under the item's `data` property
if(item.data.title){
    metadata.push(item.data.title);
}
```

## Attribute-specific snippets

### data.creators

*Goes through the item's list of creators, makes each creator's name into a page reference with format \[\[firstName lastName]], and adds the creator's role between parentheses after if the creator is not an author.*\
Returns : `Author(s):: [[First Author]], [[Second Author]], [[An Editor]] (editor)`

{% tabs %}
{% tab title="Full JavaScript" %}

```javascript
if(item.data.creators.length > 0){
    let creatorsList = item.data.creators.map(function (creator) {
        let nameTag = "[[" + [creator.firstName, creator.lastName].filter(Boolean).join(" ") + "]]";
        if (creator.creatorType != "author") {
            nameTag = nameTag + " (" + creator.creatorType + ")"
        }
        return nameTag;
    })
    metadata.push("Author(s):: " + creatorsList.join(", "));
}
```

{% endtab %}

{% tab title="Utility function" %}

```javascript
if (item.data.creators.length > 0){ 
    metadata.push(`Author(s):: ${zoteroRoam.formatting.getCreators(item)}`);
};
```

{% endtab %}
{% endtabs %}

### data.dateAdded

*Converts the date on which the item was added to Zotero into Roam's Daily Notes format.*\
Returns : `Date Added:: [[March 10th, 2021]]`

{% tabs %}
{% tab title="Full JavaScript" %}

```javascript
if(item.data.dateAdded){
    let date = new Date(item.data.dateAdded);
    let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
    let roamDateAdded = months[date.getMonth()] + " " + zoteroRoam.utils.makeOrdinal(date.getDate()) + ", " + date.getFullYear();
    metadata.push("Date Added:: [[" + roamDateAdded + "]]");
}
```

{% endtab %}

{% tab title="Utility function" %}

```javascript
if (item.data.dateAdded){ 
    metadata.push(`Date Added:: ${zoteroRoam.utils.makeDNP(item.data.dateAdded)}`);
};
```

{% endtab %}
{% endtabs %}

### data.itemType

*Maps an item's type to its non-machine name, as specified in the user's or extension's* `typemap`*.*\
Returns : `Type:: [[Book]]`

{% tabs %}
{% tab title="Full JavaScript" %}

```javascript
// Use mapping specified in user-defined typemap, 
// otherwise fall back on typemap_default
if (item.data.itemType) {
    let mapping = zoteroRoam.typemap[item.data.itemType] || item.data.itemType;
    if(zoteroRoam.config.userSettings.typemap){
        mapping = zoteroRoam.config.userSettings.typemap[item.data.itemType] || mapping;
    }
    metadata.push("Type:: " + "[[" + mapping + "]]");
}
```

{% endtab %}

{% tab title="Utility function" %}

```javascript
if (item.data.itemType){
    metadata.push(`Type:: [[${zoteroRoam.formatting.getItemType(item)}]]`);
};
```

{% endtab %}
{% endtabs %}

### data.tags

*Goes through the list of the item's tags, and make every tag into a Roam tag with format #\[\[tag]] to support multi-word tags.*\
Returns : `Tags:: #[[tag1]], #[[tag2]], #[[a multi-word tag]]`

{% tabs %}
{% tab title="Full JavaScript" %}

```javascript
if(item.data.tags.length > 0){
    let tagsList = item.data.tags.map(i => '#[[' + i.tag + ']]');
    metadata.push("Tags:: " + tagsList.join(", "))
}
```

{% endtab %}

{% tab title="Utility function" %}

```javascript
if (item.data.tags.length > 0){
    metadata.push(`Tags:: ${zoteroRoam.formatting.getTags(item)}`);
};
```

{% endtab %}
{% endtabs %}

## Additional data (collections, PDFs, notes...)

### Getting an item's collections

Collections data is requested along with items data, and is stored in the `zoteroRoam.data.collections` object. You can inspect its contents using the console if you'd like.

This allows the extension to identify which collections an item belongs to - and that's what the `zoteroRoam.formatting.getItemCollections` function does :

{% tabs %}
{% tab title="Function definition" %}

```javascript
// This is taken directly from the extension's source code :
zoteroRoam.formatting.getItemCollections(item){
    if(item.data.collections.length > 0){
        return item.data.collections.map(collecID => zoteroRoam.data.collections.find(collec => collec.key == collecID && collec.library.id == item.library.id));
    } else {
        return false;
    }
}
```

{% endtab %}

{% tab title="What the function returns" %}
The function returns an Array containing all the collections the item belongs to. The collections will be raw data objects, with all the data associated with a given collection.

You can use that output to obtain the collections' names, or any other information about them.

To obtain an Array with the collections' names, separated by a comma :

```javascript
zoteroRoam.formatting.getItemCollections(item).map(collection => collection.data.name).join(", ");
// Returns :
// ['Collection name', 'Other collection name']
```

{% endtab %}
{% endtabs %}

### Getting an item's children (PDF + notes)

If you want to get data about PDF attachments + notes, it's recommended to include them in your data request. That requires asking for `items` (all of them) rather than `items/top` (top-level items) in your data URI, and (for notes) giving your API key access to notes (see [Step 1.1](/zotero-roam/v0.5/getting-started/prereqs.md)).

The utility function `zoteroRoam.formatting.getItemChildren` can search for an item's children within the loaded dataset. If they're present, it will return them (as raw data or formatted output) ; if there aren't any, it will return 'false'. Lastly, in the case that the item has children but none are found in the dataset, the function will also return a `remoteChildren: true` property - which you can use to request them if you want to.

#### **Basic syntax**

```javascript
// At the minimum, all you have to do is this :
let children = zoteroRoam.formatting.getItemChildren(item);
// It will call the getItemChildren function with default parameters :
// pdf_as: "links"
// notes_as: "formatted"
// split_char: "\n"

// You can also set those arguments explicitly, as a reminder :
let children = zoteroRoam.formatting.getItemChildren(item, {pdf_as: "links", notes_as: "formatted", split_char: "\n"});

```

#### Function arguments

| Argument     | default     | Possible values      | What it does                                                                                                                                                                                                                                                         |
| ------------ | ----------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pdf_as`     | "links"     | "links" or "raw"     | <p>Determines the output format of PDF items :</p><ul><li>"links" -> an Array of Markdown-style links to the PDFs</li><li>"raw" -> an Array containing the raw data for the PDF items, as returned by the Zotero API</li></ul>                                       |
| `notes_as`   | "formatted" | "formatted" or "raw" | <p>Determines the output format of note items :</p><ul><li>"formatted" -> an Array of Arrays, where each child Array contains one note item's contents</li><li>"raw" -> an Array containing the raw data for the note items, as returned by the Zotero API</li></ul> |
| `split_char` | "\n"        | Anything             | <p>Determines how to split notes into blocks :</p><ul><li>"\n" will split by newline ;</li><li>"\</p>" will split by paragraph ;</li><li>...</li></ul>                                                                                                               |

#### What the function returns

{% tabs %}
{% tab title="No remote children" %}

```javascript
// If the item has no PDFs, no notes :
{
    pdfItems: false,
    notes: false
}

// If the item has PDFs + notes :
{
    pdfItems: [...], // Each element = one PDF item
    notes: [[...], [...]] // Each element = one note item
}

// PDFs and notes are searched & processed separately
// If there are no results, the value returned is 'false'
// If there are 1+ results, the value returned is an Array
// => Structure & value will depend on the function's arguments,
// but pdfItems will be an Array and notes will be an Array of Arrays
```

{% endtab %}

{% tab title="There are remote children" %}

```javascript
// In the case where the item is known to have children,
// but none are found in the loaded dataset,
// this is what the function will return :
{
    pdfItems: false,
    notes: false,
    remoteChildren: true
}

```

{% endtab %}
{% endtabs %}

#### How to use the function in your code

To make use of this function, there are 2 important things :\
1\) know what output format you're asking for (cf. function arguments above),\
2\) begin by checking if there is any data on the children.

This is how the built-in function `zoteroRoam.formatting.getItemMetadata` handles this :

```javascript
// Let's say we're creating a custom function called customPaperFormat :
window.customPaperFormat = function(item){
    let metadata = [];
    // ...
    
    let children = zoteroRoam.formatting.getItemChildren(item, {pdf_as: "links", notes_as: "formatted", split_char: "\n"});
    if(children.pdfItems){ // This checks that pdfItems is not 'false'
        // We asked for Markdown links, so children.pdfItems will be an Array of string elements
        // Here we're putting them together in one comma-separated String
        // If there is only one, this will simply return the one link
        metadata.push(`PDF links : ${children.pdfItems.join(", ")}`);
    }
    if(children.notes){ // This checks that notes is not 'false'
        // Let's make an intermediate object, in order to nest the notes under [[Notes]] :
        let notesBlock = {string: `[[Notes]]`, children: []};
        // We asked for formatted notes, so 'notes' will be an Array of Arrays
        // Here we're making a flat Array (because we don't care that the annotations come from different note items)
        // And then we push the notes Array to the 'children' property of our notes Block
        notesBlock.children.push(...children.notes.flat(1));
        // Finally we add notesBlock to the metadata array
        metadata.push(notesBlock);
    }
    
    // ...
    return metadata;
}
```

This is what the code above will output as Roam blocks :

{% tabs %}
{% tab title="If there are both PDF items & notes" %}

* PDF links : \[First PDF attachment.pdf]\(link to this PDF), \[Second PDF attachment.pdf]\(link to that PDF)
* \[\[Notes]]
  * Block from note 1
  * Another block from note 1
  * Block from note 2
  * Another block from note 2
    {% endtab %}

{% tab title="If there's only either PDFs or notes" %}
If there are only PDFs :

* PDF links : \[First PDF attachment.pdf]\(link to this PDF), \[Second PDF attachment.pdf]\(link to that PDF)

If there are only notes :

* \[\[Notes]]
  * Block from note 1
  * Another block from note 1
  * Block from note 2
  * Another block from note 2
    {% endtab %}

{% tab title="If neither exists" %}
Nothing will get added to the `metadata` array, and so no Roam blocks will be created.
{% endtab %}
{% endtabs %}
