/** * Slugger generates header id */ export class Slugger { constructor() { this.seen = {}; } serialize(value) { return value .toLowerCase() .trim() // remove html tags .replace(/<[!\/a-z].*?>/ig, '') // remove unwanted chars .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '') .replace(/\s/g, '-'); } /** * Finds the next safe (unique) slug to use */ getNextSafeSlug(originalSlug, isDryRun) { let slug = originalSlug; let occurenceAccumulator = 0; if (this.seen.hasOwnProperty(slug)) { occurenceAccumulator = this.seen[originalSlug]; do { occurenceAccumulator++; slug = originalSlug + '-' + occurenceAccumulator; } while (this.seen.hasOwnProperty(slug)); } if (!isDryRun) { this.seen[originalSlug] = occurenceAccumulator; this.seen[slug] = 0; } return slug; } /** * Convert string to unique id * @param {object} options * @param {boolean} options.dryrun Generates the next unique slug without updating the internal accumulator. */ slug(value, options = {}) { const slug = this.serialize(value); return this.getNextSafeSlug(slug, options.dryrun); } }