# 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.6/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.6/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.6/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.6/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, {brackets: true})}`);
};
```

{% 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 :

```javascript
zoteroRoam.formatting.getItemCollections(item).map(collection => collection.data.name);
// 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.6/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 %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://alix-lahuec.gitbook.io/zotero-roam/v0.6/customization/formatting/code-snippets.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
