UNPKG

gcfixer

Version:

Normalize Geocaching GPX file for Garmin devices

154 lines (129 loc) 3.81 kB
import * as I from './interfaces'; import cleaner from 'clean-html'; import he from 'he'; import unidecode from 'unidecode-plus'; import htmlToText from 'html-to-text'; const HTML_CLEAN = { 'add-break-around-tags': ['b', 'i', 'strong', 'em'], 'add-remove-attributes': ['style', 'class'], 'add-remove-tags': ['img', 'picture', 'a'], 'remove-comments': true, 'remove-empty-tags': ['p', 'div'] }; // chars < and > outside of tags can screw Garmin html parser, so we replace them with // visually similar from ASCII range. Possible are also unicode triangles &#9665; &#9655; const FAKE_LT = '&#171;'; // « const FAKE_GT = '&#187;'; // » /** * Helps he.decode to process some oddities */ export function unescape(txt: string): string { let out = txt; out = out.replace(/&amp;lt;/g, '#@AMP@LT@#'); out = out.replace(/&amp;gt;/g, '#@AMP@GT@#'); out = out.replace(/&amp;amp;/g, '#@AMP@#'); out = out.replace(/&amp;/g, '&'); out = out.replace(/ ,,/g, ' "'); out = out.replace(/„/g, '"'); out = out.replace(/“/g, '"'); return out; } export function escape(txt: string, escapeTags = false): string { let out = txt; out = out.replace(/&/g, '&amp;'); out = out.replace(/#@AMP@#/g, '&amp;amp;'); out = out.replace(/#@AMP@LT@#/g, FAKE_LT); out = out.replace(/#@AMP@GT@#/g, FAKE_GT); out = out.replace(/ ,,/g, '"'); if (escapeTags) { out = out.replace(/</g, '&lt;'); out = out.replace(/>/g, '&gt;'); out = out.replace(/°/g, '&#176;'); } return out; } export function untag(txt: string): string { let out = txt; out = out.replace(/</g, FAKE_LT); out = out.replace(/>/g, FAKE_GT); out = out.replace(/&amp;lt;/g, FAKE_LT); out = out.replace(/&amp;gt;/g, FAKE_GT); out = out.replace(/#@AMP@LT@#/g, FAKE_LT); out = out.replace(/#@AMP@GT@#/g, FAKE_GT); return out; } export function removeUrl(txt: string): string { return txt.replace(/\(https?:\/\/(.*)\)/gi, ''); } export function processText(text: string, flag: I.Flag) { let out = text; if (flag.desc) { // clean wrong html entities out = unescape(out); // replace html entities out = he.decode(out); // sanitize html cleaner.clean(out, HTML_CLEAN, (html) => { out = html; }); // remove html if needed if (flag.stripHtml) { out = htmlToText.fromString(out, { decodeOptions: { strict: false }, ignoreHref: true, ignoreImage: true, uppercaseHeadings: false }); } // utf8 to ascii out = unidecode(out, { skipRanges: [[0xb0, 0xb0]] }); // escape out = escape(out, !flag.stripHtml); if (flag.stripHtml) { out = untag(out); } } if (flag.text) { out = unidecode(out, { skipRanges: [[0xb0, 0xb0]] }); out = escape(out); out = untag(out); } if (flag.removeUrl) { out = removeUrl(out); } return out; } export function createTag(options: I.TagOptions) { const howManyTabs = options.closing && options.name === options.lastParent ? 0 : options.level; let levelMinus = false; let singleTag = false; let tabs = ''; let tag = ''; for (let i = 0; i < howManyTabs; i++) { tabs = `${tabs} `; } if (options.closing) { tag = options.name === options.lastParent ? '</' : `\n${tabs} </`; } else { tag = `\n${tabs}<`; } tag = tag + options.name; if (options.attrs !== undefined) { Object.keys(options.attrs).forEach((attr) => { let val: string = options.attrs![attr]; if (val.substr(-2) === ' /') { singleTag = true; val = val.substr(0, val.length - 2); } tag = `${tag} ${attr}="${val}"`; }); } if (options.name.includes('/') || singleTag) { levelMinus = true; } tag = `${tag}${singleTag ? ' />' : '>'}`; return { levelMinus, tag }; }