npm package: @mappingfactory/roadsheet
Description: The perfect solution to HTML render Michelin roadsheet within your Javascript project
Author: Michelin
Latest version: 1.4.8

# Michelin Roadsheet Javascript HTML Rendering
### The perfect solution to HTML render Michelin roadsheet within your Javascript project

## ✨ Features

- Thin & **lightweight** library to render Michelin roadsheet
- Works both on the **browser** and **node.js**
- **UMD compatible**, you can use it with any module loader
- Built with Vanilla Javascript

## 💡 Getting Started

First, install Michelin Roadsheet via the [npm](https://www.npmjs.com/get-npm) package manager:

```bash
npm install @mappingfactory/roadsheet
```

### Initialization

Then, initialise your client and use it.

```js
// ES-5 require
require('@mappingfactory/roadsheet/dist/michelin-roadsheet.min.css');
const michelinRoadsheet = require("@mappingfactory/roadsheet");
const michelinRoadsheetIcons = require("@mappingfactory/roadsheet/icons");
const mountMichelinRoadsheet = require("@mappingfactory/roadsheet/mount");

// or ES-6 import
import '@mappingfactory/roadsheet/dist/michelin-roadsheet.min.css';
import michelinRoadsheet from '@mappingfactory/roadsheet';
import michelinRoadsheetIcons from '@mappingfactory/roadsheet/icons';
import mountMichelinRoadsheet from '@mappingfactory/roadsheet/mount';

// client initialization
const clientMr = michelinRoadsheet.client(michelinRoadsheetIcons); 

// Route points collection (ie: returned by @mappingfactory/direction)
const routePoints = {routes: [{...}], waypoints: [{...}, {...}, {...}],"language": "en-GB"};

const html = clientMr.getHtml(routePoints.routes[0], routePoints.waypoints, routePoints.language);

// Then you can display the result where you want:
document.querySelector('#yourTargetDiv').innerHTML = html;
// Or ie, with jQuery:
$('#yourTargetDiv').html(html);
```

You can use use the dist version if you want to use @mappingfactory/roadsheet with html for a browser usage: 

```html
<link rel="stylesheet" href="./dist/michelin-roadsheet.min.css">
<script type="text/javascript" src="./dist/michelin-roadsheet.js"></script>
<script type="text/javascript" src="./dist/icons/index.js"></script>
<script>
  const clientMr = michelinRoadsheet.client(michelinRoadsheetIcons);
  ...
</script>
```

## 🔧 Importable Methods and tools

- `michelinRoadsheet.client()`: Returns a new michelinRoadsheet client. From `@mappingfactory/roadsheet`
- `michelinRoadsheetIcons`: An object of default icons for the roadsheet. From `@mappingfactory/roadsheet/icons`
- `mountMichelinRoadsheetIcons(roadsheet, { onDetailsClick, onMapIconClick })`: Mounts event listeners on an existing roadsheet (useful when rendering roadsheets server-side). From `@mappingfactory/roadsheet/mount`
- `@mappingfactory/roadsheet/michelin-roadsheet.min.css` is the default css style file which should be imported

## 🪄 Client Methods

Common methods you may want to use include:

### getHtml()

**Description:**

Return a new Michelin roadsheet HTML DOM.

```js
/**
 * Get HTML
 * @param {json} route
 * @param {array} waypoints
 * @returns {HTMLElement}
 */
getHtml (route, waypoints, language) {
  // ...
}
```

**Example usage:**
```js
// Route points collection (ie: returned by @mappingfactory/direction)
const routePoints = {routes: [{...}], waypoints: [{...}, {...}, {...}], "language": "en-GB"};

const roadsheet = clientMr.getHtml(routePoints.routes[0], routePoints.waypoints, routePoints.language);
document.getElementById('my-roadsheet-container').appendChild(roadsheet)
```

#### 💡 Server-side usage

This method can be used to render roadsheets on the server side. In this case, it will return an HTML string instead of an `HTMLElement`. However, roadsheets use interactive elements (mention details and map icon), so they need an extra "mounting" step on the client side.

The SDK exposes the events-binding logic in `@mappingfactory/roadsheet/mount` (or as `mountMichelinRoadsheet` in `dist/mount/index.js` if you inject it directly into your page), so you don't have to load the whole library when doing server-side rendering.

Example:

```js
// Client-side
// We assume there is an element with the id "my-roadsheet-container", and that
// element contains a server-generated roadsheet

import mountMichelinRoadsheet from '@mappingfactory/roadsheet/mount'

const roadsheet = document.getElementById('my-roadsheet-container')
mountMichelinRoadsheet(roadsheet, {
  onDetailsClick(stepEl, step, mentions, isDetailsSectionOpen) {
    // Do something; e.g. show/hide the mentions on the map
  },
  onMapIconClick(stepEl, step, mentions) {
    // Do something; e.g. zoom the map to the step location
  }
})
```

## ⚙️ Options & Parameters


### icons

**Description:**

Update icons for every maneuvers/mentions.

**updateIcons()**

```js
/**
 * Update icons
 * @param {object} icons
 */
updateIcons (icons) {
  // ...
}
```
`updateIcons()` will merge current icons by type with your custom icons.

***Icons format:***
```json
{
  mapIcon: {
    content: '...'
  },
  maneuver: {
    depart: {
      innerText: true,
      content: '...'
    },
    step: {
      innerText: true,
      content: '...'
    },
    arrive: {
      innerText: true,
      content: '...'
    },
    ...
  },
  mention: {
    neighbourhood: {
      content: '...'
    },
    toll_ticket: {
      content: '...'
    },
    toll_collection: {
      content: '...'
    },
  },
  ...
}
```
***Note:*** You will find more informations on [🎨 Customization](#customization) section below

**Getter:**

```js
/**
 * Get icons.
 * @returns {object}
 */
getIcons () {
  // ...
}
```

**Example usage:**
```js
// I update depart maneuver icon
const newIcons = {
  maneuver: {
    depart: {
      content: '<img alt="" class="custom-class" src="/path/to/my/custom/icon.png" />'
    }
  }
}
clientMr.updateIcons(newIcons);
```
---

### unit

**Default value:** `"metric"`

**Description:**

The unit to be used for result rendering.

**Setter:**
```js
/**
 * Set unit
 * @param  {UNIT_ENUM} unit
 */
setUnit (unit) {
  //...
}
```

***Enum:***
```js
/**
 * @readonly
 * @enum {UNIT_ENUM}
 */
UNIT_ENUM = {
  metric: 'metric',
  imperial: 'imperial'
}
```

**Example usage:**
```js
clientMr.setUnit(michelinRoadsheet.utils.UNIT_ENUM.imperial);
```
---
### custom_class

**Description:**

Custom html class added to HTML parent

**Setter:**
```js
/**
 * Set custom_class
 * @param  {string} customClass
 */
setCustomClass (customClass) {
  //...
}
```

**Example usage:**
```js
clientMr.setCustomClass('my-custom-class');
```
---
### language

**Default value:** `"en-GB"`

**Description:**

The language to be used for result rendering.

**Setter:**
```js
/**
 * Set language
 * @param {string|LANG_ENUM} lang
 */
setLanguage (lang) {
  //...
}
```

***Enum:***
```js
/**
 * @readonly
 * @enum {LANG_ENUM}
 */
LANG_ENUM = {
    'en-GB': 'en-GB',
    'fr-FR': 'fr-FR',
    'de-DE': 'de-DE',
    'it-IT': 'it-IT',
    'es-ES': 'es-ES',
    'en-US': 'en-US',
    'nl-NL': 'nl-NL',
    'de-CH': 'de-CH',
    'fr-CH': 'fr-CH',
    'fr-BE': 'fr-BE',
    'it-CH': 'it-CH',
    'nl-BE': 'nl-BE',
    com: 'com',
    'en-IE': 'en-IE',
    'pt-PT': 'pt-PT',
    'pl-PL': 'pl-PL',
    'de-AT': 'de-AT'
}
```

**Example usage:**
```js
clientMr.setLanguage(michelinRoadsheet.utils.LANG_ENUM['fr-FR']);
```

**Note:** you can also specify another custom language.<br>If you do it, you'll have to add it translations using `languageMessages` option. You will find more informations below.

---

### languageMessages

**Default value:** 

```js
{
  'en-GB': {
    details: 'Details',
    close: 'Close',
    departure: 'Departure',
    arrive: 'Arrival',
    step: 'Step',
    proximity: 'Passing close to',
    entry: 'Arrive in',
    exit: 'Leave',
    crossing: 'Pass by',
    slope: 'Steep hill downwards',
    turn_right: 'Bend to right',
    turn_left: 'Bend to left',
    turns_with_distance: 'Series of bends for',
    turns: 'Series of bends',
    remarkable_speed_limit_distance: '$V $U/h speed limit for $L',
    remarkable_speed_limit: '$V $U/h speed limit',
    break: 'Break',
    motorway: 'Motorway',
    limited_access_road: 'Limited access road'
  },
  'fr-FR': {
    //...
  },
  'de-DE': {
    //...
  },
  'it-IT': {
    //...
  },
  'es-ES': {
    //...
  },
  'en-US': {
    //...
  },
  'nl-NL': {
    //...
  },
  'de-CH': {
    //...
  },
  'fr-CH': {
    //...
  },
  'fr-BE': {
    //...
  },
  'it-CH': {
    //...
  },
  'nl-BE': {
    //...
  },
  com: {
    //...
  },
  'en-IE': {
    //...
  },
  'pt-PT': {
    //...
  },
  'pl-PL': {
    //...
  },
  'de-AT': {
    //...
  }
}
```

**Description:**

A key value object with:
`key`: Language code
`value`: translation object.

Your `languageMessages` value will be merged by language with default translation.

**Setter:**
```js
/**
 * Update language messages
 * @param {object} messages
 */
updateLanguageMessages (messages) {
  //...
}
```

**Getter:**

```js
/**
 * Get language messages
 * @returns {object}
 */
getLanguageMessages () {
  // ...
}
```

**Example usage:**
```js
// I update english message for arrival :
const newMessage = {
  'en-GB': {
    arrive: 'Finish'
  }
}
clientMr.updateLanguageMessages(newMessage);
```

**New language:**
You can also add new languages, simply using: 
```js
// I add korean new language
const newMessage = {
  'ko-KR': {
    details: '...',
    departure: '...',
    arrive: '...',
    step: '...',
    proximity: '...',
    ...
  }
}
clientMr.updateLanguageMessages(newMessage);
// Then use it
clientMr.setLanguage('ko-KR');
```
---
### startDate

**Default value:** `Date.now()`

**Description:**

The start date of the roadsheet.

**Setter:**
```js
/**
 * Set start date
 * @param {Date} date
 */
setStartDate (date) {
  //...
}
```

**Example usage:**
```js
clientMr.setStartDate(new Date('September 22, 2050 15:00:00'));
```

---

### disabledManeuvers

**Default value:** 
`['continue', 'end of road', 'new name', 'merge', 'use lane', 'rotary', 'roundabout turn', 'exit roundabout', 'exit rotary', 'notification']`

**Description:**

An array of disabled maneuver types.
Every disabled maneuver will not be displayed.
When the `continue` maneuver is disabled and the maneuver contains mention(s), only the mention(s) will be displayed.

**Setter:**
```js
/**
 * Set disabled maneuvers
 * @param  {array} maneuverTypes
 */
setDisabledManeuvers (maneuverTypes) {
  //...
}
```

**Maneuver type values:**
- `"depart"`
- `"step"`
- `"arrive"`
- `"turn"`
- `"continue"`
- `"fork"`
- `"end of road"`
- `"off ramp"`
- `"on ramp"`
- `"roundabout"`
- `"new name"`
- `"merge"`
- `"use lane"`
- `"rotary"`
- `"roundabout turn"`
- `"exit roundabout"`
- `"exit rotary"`
- `"notification"`

**Example usage:**
```js
// we enable continue maneuver type, removing it from disabledManeuvers
clientMr.setDisabledManeuvers(['end of road', 'new name', 'merge', 'use lane', 'rotary', 'roundabout turn', 'exit roundabout', 'exit rotary', 'notification']);

// display all maneuvers
clientMr.setDisabledManeuvers([]) 
```

---

### disabledMentions

**Default value:** 
`['deviation', 'traffic_event', 'touristic_road', 'scenic_road', 'remarkable_speed_limit']`

**Description:**

An array of disabled mention types.
Every disabled mention will not be displayed.

**Setter:**
```js
/**
 * Set disabled mentions
 * @param  {array|string} mentionTypes
 */
setDisabledMentions (mentionTypes) {
  //...
}
```

**Mention type values:**
- `"deviation"`
- `"traffic_event"`
- `"remarkable_speed_limit"`
- `"neighbourhood"`
- `"toll_ticket"`
- `"toll_collection"`
- `"bridge"`
- `"danger"`
- `"limitations"`
- `"tunnel"`
- `"speed_traffic_event"`
- `"town_entry"`
- `"town_exit"`
- `"town_crossing"`
- `"restricted_zone"`
- `"vignette_zone"`
- `"border"`
- `"slope"`
- `"turn"`
- `"turns"`
- `"poi"`
- `"road_sign"`
- `"touristic_road"`
- `"scenic_road"`
- `"motorway_or_limited_access_road"`

**Example usage:**
```js
// we disable default values + slope mentions type
clientMr.setDisabledMentions(['deviation', 'traffic_event', 'touristic_road', 'scenic_road', 'remarkable_speed_limit', 'slope']);

// display all mentions
clientMr.setDisabledMentions([]) 

// disable all mentions
clientMr.setDisabledMentions('all') 
```

---

### primaryMentions

**Default value:** 
`['road_sign', 'deviation', 'restricted_zone', 'vignette_zone', 'neighbourhood', 'town_entry', 'town_exit', 'town_crossing', 'border', 'toll_ticket', 'toll_collection']`

**Description:**

An array of primary (level 1)  mention types.
A primary mention will always be displayed first.

**Setter:**
```js
/**
 * Set primary mentions
 * @param  {array} mentionTypes
 */
setPrimaryMentions (mentionTypes) {
  //...
}
```

**Example usage:**
```js
// we remove deviation from primary mentions.
clientMr.setPrimaryMentions(['road_sign', 'restricted_zone', 'vignette_zone', 'neighbourhood', 'town_entry', 'town_exit', 'town_crossing', 'border', 'toll_ticket', 'toll_collection']);
```

---

### maxSecondaryMentions

**Default value:** `0`

**Description:**

Number of secondary mentions (not primary mentions) to display.
If there is more secondary mentions than `maxSecondaryMentions`, they will be grouped and a `Detail` button to expand them will appear.


**Setter:**
```js
/**
 * Set max secondary mentions
 * @param {integer} nb
 */
setMaxSecondaryMentions (nb) {
  //...
}
```
***Note:*** If you want to display all mentions (disable grouping), you need to set `maxSecondaryMentions` to `-1`

**Example usage:**
```js
clientMr.setMaxSecondaryMentions(8);
```

---

### onDetailsClick

**Description:**

A `onClick` event handler function for the details button.

**Setter:**
```js
/**
 * Set details onClick event handler
 * @param {function} handler
 */
setOnDetailsClick (handler) {
  //...
}
```

**Example usage:**
```js
clientMr.setOnDetailsClick((maneuverHtmlElement, step, mentions) => {
  // console.log('onDetailsClick', maneuverHtmlElement, step, mentions)
});
```
***Note:*** Use it if you want to hide/compact custom mentions (typically traffic information, town_entry, town_exit, scenic road, ...).

---

### updateManeuverTemplate

**Description:**

Customize maneuver template rendering by `maneuver.type`.

A key value object with:
`key`: a `maneuver.type`
`value`: a function rendering string or DOM HTML.

**Note:** Updating `updateManeuverTemplate` will not erase previous customized types. Old and new types will be merged.
- You can return `null` to disable a specific type.
- You can return `"default"` to return the default template

**Setter:**
```js
/**
 * Update maneuver template
 * @param {object} obj
 */
updateManeuverTemplate (obj) {
  //...
}
```

**Example usage:**
```js
clientMr.updateManeuverTemplate(
  {
    depart: (step) => { // function can return a string
      return `<div class="example-depart">This is a custom depart to ${step.name}</div>`
    },
    continue:  (step) => {
      return null // this maneuver will be disabled
    },
    turn:  (step) => { // function can return a dom element
      if (step.maneuver.modifier === 'right') { 
        const div = document.createElement('div')
        div.classList += 'my-custom-class'
        div.innerHTML = 'This is a custom template to turn right'
        return div
      }
      return 'default' // return default template
    }
  }
);
```

**Advanced usage:**

You can use default template rendering methods to help you to create your templates.
**Learn more about default template rendering** method in [🎨 Customization](#customization) section below.>

```js
clientMr.updateManeuverTemplate(
  {
    depart: (step, allMentions) => { // this is the default template configuration
      const fragment = new DocumentFragment()
      fragment.append(michelinRoadsheet.utils.createDomEl(clientMr.TEMPLATES.maneuver.icon(step)))
      fragment.append(michelinRoadsheet.utils.createDomEl(clientMr.TEMPLATES.maneuver.content(step, allMentions)))
      return fragment
    },
    arrive: (step, allMentions) => { // A more advanced template configuration
      const fragment = new DocumentFragment()
      fragment.append(michelinRoadsheet.utils.createDomEl(clientMr.TEMPLATES.maneuver.icon(step)))
      // custom content
      const div = document.createElement('div')
      div.classList += 'mr-ct'
      div.append(michelinRoadsheet.utils.createDomEl(clientMr.TEMPLATES.maneuver.distanceAndtime(step.odometer_distance, step.odometer_time)))
      div.append(michelinRoadsheet.utils.createDomEl(clientMr.TEMPLATES.maneuver.title(step)))
      div.append(michelinRoadsheet.utils.createDomEl(clientMr.TEMPLATES.maneuver.instruction(step)))
      if (allMentions && allMentions.length > 0 && step.mention_indices && step.mention_indices.length > 0) {
        div.append(michelinRoadsheet.utils.createDomEl(clientMr.TEMPLATES.maneuver.mentions(step, allMentions)))
      }
      fragment.append(div)
      return fragment
    },
  }
);
```

---

### updateMentionTemplate

**Description:**

Customize mention template rendering by `mention.type`.

A key value object with:
`key`: a `mention.type`
`value`: a function rendering string or DOM HTML.

**Note:** Updating `updateMentionTemplate` will not erase previous customized types. Old and new types will be merged.
- You can return `null` to disable a specific type.
- You can return `"default"` to return the default template

**Setter:**
```js
/**
 * Update mention template
 * @param {object} obj
 */
updateMentionTemplate (obj) {
  //...
}
```

**Example usage:**
```js
clientMr.updateMentionTemplate(
  {
    deviation: (step, mention) => { // function can return a string
      return `<div class="example-depart">This is a custom depart to ${mention.type}</div>`
    },
    remarkable_speed_limit:  (step, mention) => {
      return null // this mention will be disabled
    },
    traffic_event:  (step, mention) => { // function can return a dom element
      if (mention.code === 103) { 
        const div = document.createElement('div')
        div.classList += 'my-custom-class'
        div.innerHTML = 'This is a custom template for a specific case'
        return div
      }
      return 'default' // return default template
    }
  }
);
```

**Advanced usage:**

You can use default template rendering methods to help you to create your templates.
**Learn more about default template rendering** method in [🎨 Customization](#customization) section below.>

```js
clientMr.updateMentionTemplate(
  {
    toll_ticket: (step, mention) => { // this is the default template configuration
      const fragment = new DocumentFragment()
      fragment.append(michelinRoadsheet.utils.createDomEl(clientMr.TEMPLATES.mention.icon(mention)))
      fragment.append(michelinRoadsheet.utils.createDomEl(clientMr.TEMPLATES.mention.text(mention)))
      fragment.append(michelinRoadsheet.utils.createDomEl(clientMr.TEMPLATES.mention.road_sign(mention)))
      return fragment
    }
  }
);
```

---

## 🎨 Customization

#### Icons

You can customize icons using a key value objects depending on item family level.

***All icons format:***

```js
const michelinRoadsheetIcons = {
  mapIcon: { content: '...' },
  step: {
    mode: {  // step.mode
      ferry: { content: '...' },
      train: { content: '...' },
      walking: { content: '...' }
    }
  },
  maneuver: { // all maneuver icons
    depart: { 
      innerText: true,
      content: '...'
    },
    step: {
      innerText: true,
      content: '...'
    },
    arrive: {
      innerText: true,
      content: '...'
    },
    turn: { // for this maneuver.type, we check maneuver.modifier
      straight: { content: '...' },
      uturn: { // for maneuver.modifier == uturn, we check maneuver.driving_side
        right: { content: '...' },
        left: { content: '...' }
      },
      right: { content: '...' },
      left: { content: '...' },
      'sharp right': { content: '...' },
      'sharp left': { content: '...' },
      'slight right': { content: '...' },
      'slight left': { content: '...' },
    },
    continue: { content: '...' },
    fork: { // for this maneuver.type, we check maneuver.modifier
      right: { content: '...' },
      left: { content: '...' },
      'slight right': { content: '...' },
      'slight left': { content: '...' }
    },
    'end of road': { content: '...' },
    'off ramp': { // for this maneuver.type, we check maneuver.modifier
      right: { content: '...' },
      left: { content: '...' },
      'sharp right': { content: '...' },
      'sharp left': { content: '...' },
      'slight right': { content: '...' },
      'slight left': { content: '...' }
    },
    'on ramp': { content: '...' }
    roundabout: { content: '...' }
  },
  mention: { // all mention icons
    deviation: { content: '...' },
    traffic_event: { // for this mention.type, we check mention.group
      102: { content: '...' },
      103: { content: '...' },
      104: { content: '...' },
      105: { content: '...' },
      106: { content: '...' },
      107: { content: '...' },
      108: { content: '...' },
      109: { content: '...' },
      110: { content: '...' },
      111: { content: '...' },
      112: { content: '...' },
      114: { content: '...' },
      115: { content: '...' },
      116: { content: '...' },
      117: { content: '...' },
      118: { content: '...' },
      119: { content: '...' },
      120: { content: '...' },
      121: { content: '...' },
      122: { content: '...' },
      123: { content: '...' },
      124: { content: '...' },
      125: { content: '...' },
      126: { content: '...' },
      127: { content: '...' }
    },
    remarkable_speed_limit: {
      innerText: true,
      content: '...'
    },
    neighbourhood: { content: '...' },
    toll_ticket: { content: '...' },
    toll_collection: { content: '...' },
    bridge: { content: '...' },
    danger: { content: '...' },
    limitations: { content: '...' },
    tunnel: { content: '...' },
    town_exit: { content: '...' }, // the diagonal line crossing the exit road sign
    motorway_or_limited_access_road: { // for this mention.type, we check mention.category
      motorway: { content: '...' },
      limited_access_road: { content: '...' }
    },
    rest_stop: { content: '...' },
    restricted_zone: { content: '...' },
    vignette_zone: { content: '...' },
    border: { content: '...' },
    slope: { content: '...' },
    turn: { // for this mention.type, we check mention.direction
      right: { content: '...' },
      left: { content: '...' }
    },
    turns: { // for this mention.type, we check mention.direction
      right: { content: '...' },
      left: { content: '...' }
    },
    poi: { 
      default: { content: '...' },  // default poi icon
      accommodation: { content: '...' }, 
      restaurant: { content: '...' }, 
      tourism: { content: '...' },  
      servicestation: { content: '...' }, 
      reststop: { content: '...' }, 
      evstation: { content: '...' }
    },
    road_sign: { // this are icons to display in road_sign
      airport: { content: '...' },
      bus_station: { content: '...' },
      fair: { content: '...' },
      ferry_connection: { content: '...' },
      first_aid_post: { content: '...' },
      harbor: { content: '...' },
      hospital: { content: '...' },
      hotel: { content: '...' },
      industrial_area: { content: '...' },
      information_center: { content: '...' },
      parking_facility: { content: '...' },
      petrol_station: { content: '...' },
      railway_station: { content: '...' },
      rest_area: { content: '...' },
      restaurant: { content: '...' },
      toilet: { content: '...' }
    }
  }
}
```

***Single icon format:***
```js
{ 
  innerText: true, // optional, if true, a special wording will be added in the icon (like the step letter, or speed limit)
  text: '', // optional, if setted with innerText: true, the text will be added in the icon.
  content: '...' // the icon content, as inline <svg></svg> or <img/> or a string html
}
```

#### Templating

Michelin Roadsheet client use it's own methods to render maneuvers that can be used to custom templating.

##### Usefull global methods

| <div style="width: 250px;">Method</div> | Description |
| --- | --- |
| `michelinRoadsheet.utils.decodeHtml(string)` | Decode html encoded string. |
| `michelinRoadsheet.utils.createDomEl(any)` | Return a html dom element from a string html. |
| `michelinRoadsheet.utils.capitalizeFirstLetter(string)` | Capitalize first letter of a string. |
| `michelinRoadsheet.utils.getRoadSignColor(country, type)` | Return a color class for a road sign, by country and road number type. |
| `michelinRoadsheet.utils.getDistance(distance, unit)` | Return a string distance by a distance (in meters) and a unit (see `UNIT_ENUM`). |
| `michelinRoadsheet.utils.getDuration(date)` | Return a string duration like `hh:mm` from a javascript Date. |
| `michelinRoadsheet.utils.getManeuverMentions(step, allMentions)` | Return a `mentions` array from a step and a list of  mentions (`rougetHtmlte.mentions`). |
| `clientMr.TEMPLATES.timeline(step)` | Return a timeline html dom element. |
| `clientMr.TEMPLATES.mapIcon(step, mentions)` | Return a map icon html dom element. |

##### Maneuver

| <div style="width: 250px;">Method</div> | <div style="width: 80px;">CSS class</div> | Description |
| --- | --- | --- |
| `clientMr.getManeuverIcon(step)` | N/A | Return a maneuver icon object (from all icons). |
| `clientMr.TEMPLATES.maneuver.distanceAndtime(step.odometer_distance, step.odometer_time)` | `mr-h` | Return a maneuver distance and time html dom element. |
| `clientMr.TEMPLATES.maneuver.title(step)` | `mr-t` | Return a maneuver title html dom element (only used by `depart`, `step` and `arrive` types). |
| `clientMr.TEMPLATES.maneuver.instruction(step)` | `mr-c` | Return a maneuver instruction html dom element. |
| `clientMr.TEMPLATES.maneuver.icon(step)` | `mr-icon` | Return a maneuver icon html dom element. |
| `clientMr.TEMPLATES.maneuver.content(step, mentions)` | `mr-ct` | Return a maneuver all content html dom element. It actually return a maneuver with `distanceAndtime`, `title`, `icon`, `instruction` and `mentions` lists |
| `clientMr.TEMPLATES.maneuver.mentions(step, mentions)` | `mr-mentions` | Return all mentions from a maneuver as html dom element. |

##### Mention

| <div style="width: 250px;">Method</div> | <div style="width: 140px;">CSS class</div> | Description |
| --- | --- | --- |
| `clientMr.getMentionIcon(mention)` | N/A | Return a mention icon object (from all icons). |
| `clientMr.TEMPLATES.mention.icon(mention)` | `mr-m-icon` | Return a mention icon html dom element. |
| `clientMr.TEMPLATES.mention.text(mention)` | `mr-m-text` | Return a mention text description html dom element. |
| `clientMr.TEMPLATES.mention.road_sign(mention)` | `mr-ct-road_sign` <br>or<br> `m-rs town` | Return a mention road sign html dom element. (only used by `town_entry`, `town_exit`, `town_crossing` and `road_sign` types). |
| `clientMr.TEMPLATES.mention.content(step, mention)` | `mr-m` | Return a mention all content html dom element. It actually return a mention with `icon`, `text`, and `road_sign` |


## 🥷 Developers

#### Project initialization

First you need to install dev dependencies running:

```bash
npm install
```

Then you need to initialize some devs dependencies running:

```bash
npm run dev:init
```

Finally, you can launch `dev:watch` script:

```bash
npm run dev:watch
```

***Mock data for tests***

You will find a `mock/` folder at the project's root with a `mock.js` file containing:
**This configuration will be used in sample.html and all tests**
```js
var mockJson = {"routes":[...], "waypoints": [...], ... }
if(typeof module !== 'undefined'){
  module.exports = mockJson
}
// mockJson mush be equal to @mappingfactory/direction result
```

Project use [editorconfig](https://editorconfig.org/) to helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs.
Please install the [plugin](https://editorconfig.org/#download) for your IDE if needed.
**Tips:**: `editorconfig` currently doesn't allow to specify a rule on to insert space pefore function parenthesis. But `lint` will need it to respect Standards.
If you're using Visual Studio Code, you can easily check `JavaScript › Format: Insert Space Before Function Parenthesis` in IDE settings.

#### Project informations

`lint`actually use [Standard](https://standardjs.com/) configuration with ECMAScript 12.

#### Publishing informations

Project use `semantic-release` to manage versioning and package publishing.

**semantic-release** uses the commit messages to determine the type of changes in the codebase. Following formalized conventions for commit messages, **semantic-release** automatically determines the next [semantic version](https://semver.org) number, generates a changelog and publishes the release.

By default **semantic-release** uses [Angular Commit Message Conventions](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-format). The commit message format can be changed with the [`preset` or `config` options](docs/usage/configuration.md#options) of the [@semantic-release/commit-analyzer](https://github.com/semantic-release/commit-analyzer#options) and [@semantic-release/release-notes-generator](https://github.com/semantic-release/release-notes-generator#options) plugins.

Tools such as [commitizen](https://github.com/commitizen/cz-cli) or [commitlint](https://github.com/conventional-changelog/commitlint) can be used to help contributors and enforce valid commit messages.

The table below shows which commit message gets you which release type when `semantic-release` runs (using the default configuration):

| Commit message                                                                                                                                                                                   | Release type               |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------- |
| `fix(pencil): stop graphite breaking when too much pressure applied`                                                                                                                             | Patch Release              |
| `feat(pencil): add 'graphiteWidth' option`                                                                                                                                                       | ~~Minor~~ Feature Release  |
| `perf(pencil): remove graphiteWidth option`<br><br>`BREAKING CHANGE: The graphiteWidth option has been removed.`<br>`The default graphite width of 10mm is always used for performance reasons.` | ~~Major~~ Breaking Release |

**Example**
```bash
git commit -m "fix: updating readme for clarity"
```
Will increment `<PATCH>` version value. If version was: `1.0.0`, it becomes `1.0.1`.

#### ⚠️ First release

Currently, the release on `NPM` is done as dry-run mode. To make the first release, you will need to:
- Update `package.json` scripts `"semantic-release": "semantic-release --dry-run"` removing `--dry-run` option
- Remove this `First release` section from `README.md`
- Commit your changes to master with the message `"fix: initial release"`
- Push your changes and merge it to branch `master`

#### Helpfull scripts

Then you will find the helpfull scripts below: 

| <div style="width: 190px;">Script</div> | Description | 
| --- | --- | 
| `npm run dev:init` | Initialize some devs dependencies (install husky). | 
| `npm run dev:watch` | Launch build everytime you update and save a file. Very uselly during development. | 
| `npm run test:lint` | Launch lint tests. | 
| `npm run test:jest` | Launch jest tests. | 
| `npm run test` | Launch lint and jest tests. | 
| `npm run build:webpack` | Launch webpack build. | 
| `npm run build` | Launch webpack build. | 
| `npm run pre-commit` | **Not to use directly**<br>Used by Husky as a pre-commit hook.<br>Launch `npm run test`. | 
| `npm run semantic-release` | **Not to use directly**<br>Used by Gitlab CI to publish module on npm. | 

## 🏷️ Versioning

Michelin Roadsheet use the [Semantic Versioning](https://semver.org/) specification, given a version number as `<MAJOR>.<MINOR>.<PATCH>`

## 📄 License

**Michelin Roadsheet** is an open-sourced software licensed under the [MIT license](LICENCE.md).