mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
* Fixed: Popout Chat from the dashboard and mod view not working correctly. Please note there is still a race condition on the dashboard popout chat. It may require several refreshes or not work at all depending on your Internet connection. * Fixed: Only load the chat types from Twitch once. Ignore any future module loads. * Fixed: Hide the empty bar at the bottom of Twitch pages due to incorrect styles being applied to the new snackbar container element. * Fixed: Apollo should only clear the query cache if it makes changes to a query. Likewise, Apollo should only fetch the `gql-printer` module upon demand. * Fixed: Remove debug logging from `utilities/dom::createElement` * Changed: Slightly delay tool-tip repositioning when rich content is loaded, hopefully reducing flicker events. * Changed: Refactor WebMunch, adding compatibility for a future webpack update and reducing the number of modules checked when scanning for modules. * Changed: Allow Switchboard to keep trying to load routes if the one it tries fails to actually populate `require()`. * API Added: `EventEmitter::hasListeners(event)` method for determining if there are any listeners for a specific event. * API Added: `localStorage.ffzLogLevel` can be set to override the global log level. * API Added: `log.verbose(...)` as an even weaker logging level than `debug(...)` * API Changed: Allow Tooltip instances to add tool-tips to the DOM under a different element than the parent element used for events.
130 lines
No EOL
2.1 KiB
JavaScript
130 lines
No EOL
2.1 KiB
JavaScript
'use strict';
|
|
|
|
export function parse(path) {
|
|
return parseAST({
|
|
path,
|
|
i: 0
|
|
});
|
|
}
|
|
|
|
function parseAST(ctx) {
|
|
const path = ctx.path,
|
|
length = path.length,
|
|
out = [];
|
|
|
|
let token, raw;
|
|
let old_tab = false,
|
|
old_page = false;
|
|
|
|
while ( ctx.i < length ) {
|
|
const start = ctx.i,
|
|
char = path[start],
|
|
next = path[start + 1];
|
|
|
|
if ( ! token ) {
|
|
raw = [];
|
|
token = {};
|
|
}
|
|
|
|
// JSON
|
|
if ( char === '@' && next === '{') {
|
|
ctx.i++;
|
|
const tag = parseJSON(ctx);
|
|
if ( tag )
|
|
Object.assign(token, tag);
|
|
|
|
continue;
|
|
}
|
|
|
|
// Segment End?
|
|
const tab = char === `~` && next === '>',
|
|
page = char === '>' && next === '>',
|
|
segment = ! page && char === '>';
|
|
|
|
if ( ! segment && ! page && ! tab ) {
|
|
raw.push(char);
|
|
ctx.i++;
|
|
continue;
|
|
}
|
|
|
|
// We're at the end of a segment, so push
|
|
// the token out.
|
|
if ( tab || page )
|
|
ctx.i++;
|
|
|
|
token.title = raw.join('').trim();
|
|
token.key = token.title.toSnakeCase();
|
|
|
|
token.page = old_page;
|
|
token.tab = old_tab;
|
|
|
|
old_page = page;
|
|
old_tab = tab;
|
|
|
|
out.push(token);
|
|
token = raw = null;
|
|
ctx.i++;
|
|
}
|
|
|
|
if ( token ) {
|
|
token.title = raw.join('').trim();
|
|
token.key = token.title.toSnakeCase();
|
|
token.page = old_page;
|
|
token.tab = old_tab;
|
|
out.push(token);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
function parseJSON(ctx) {
|
|
const path = ctx.path,
|
|
length = path.length,
|
|
|
|
start = ctx.i;
|
|
|
|
ctx.i++;
|
|
|
|
const stack = ['{'];
|
|
let string = false;
|
|
|
|
while ( ctx.i < length && stack.length ) {
|
|
const start = ctx.i,
|
|
char = path[start];
|
|
|
|
if ( string ) {
|
|
if ( char === '\\' ) {
|
|
ctx.i++;
|
|
continue;
|
|
}
|
|
|
|
if ( (char === '"' || char === "'") && char === string ) {
|
|
stack.pop();
|
|
string = false;
|
|
}
|
|
|
|
} else {
|
|
if ( char === '"' || char === "'" ) {
|
|
string = char;
|
|
stack.push(char);
|
|
}
|
|
|
|
if ( char === '{' || char === '[' )
|
|
stack.push(char);
|
|
|
|
if ( char === ']' ) {
|
|
if ( stack.pop() !== '[' )
|
|
throw new SyntaxError('Invalid JSON');
|
|
}
|
|
|
|
if ( char === '}' ) {
|
|
if ( stack.pop() !== '{' )
|
|
throw new SyntaxError('Invalid JSON');
|
|
}
|
|
}
|
|
|
|
ctx.i++;
|
|
}
|
|
|
|
return JSON.parse(path.slice(start, ctx.i));
|
|
} |