const hashReplacements = new Map([ ["%", "."], ["(", ".28"], [")", ".29"], [".", ".2E"], ]); // Some browsers zealously URI-decode the contents of // window.location.hash. So we hide our URI-encoding // by replacing % with . (like MediaWiki). export function encodeHashComponent(str) { return encodeURIComponent(str).replace(/[%().]/g, (matched) => hashReplacements.get(matched)); } export function decodeHashComponent(str) { // This fails for URLs containing // foo.foo or foo%foo due to our fault in special handling // of such characters when encoding. This can also, // fail independent of our fault. // Here we let the calling code handle the exception. return decodeURIComponent(str.replace(/\./g, "%")); } export function stream_id_to_slug(stream_id, maybe_get_stream_name) { let name = maybe_get_stream_name(stream_id) || "unknown"; // The name part of the URL doesn't really matter, so we try to // make it pretty. // TODO: Convert this to replaceAll once mobile no longer supports // browsers that don't have it. name = name.replace(/ /g, "-"); return stream_id + "-" + name; } export function encode_stream_id(stream_id, maybe_get_stream_name) { // stream_id_to_slug appends the stream name, but it does not do the // URI encoding piece. const slug = stream_id_to_slug(stream_id, maybe_get_stream_name); return encodeHashComponent(slug); } export function by_stream_url(stream_id, maybe_get_stream_name) { return "#narrow/stream/" + encode_stream_id(stream_id, maybe_get_stream_name); } export function by_stream_topic_url(stream_id, topic, maybe_get_stream_name) { return ( "#narrow/stream/" + encode_stream_id(stream_id, maybe_get_stream_name) + "/topic/" + encodeHashComponent(topic) ); }