Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
liamwhite committed Mar 10, 2024
1 parent 886539c commit a724644
Show file tree
Hide file tree
Showing 14 changed files with 788 additions and 888 deletions.
877 changes: 0 additions & 877 deletions assets/js/match_query.js

This file was deleted.

16 changes: 16 additions & 0 deletions assets/js/match_query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { generateLexArray } from 'query/lex';
import { parseTokens } from 'query/parse';
import { parseTerm } from 'query/term';

function parseSearch(query: string) {
const tokens = generateLexArray(query, parseTerm);
const matcher = parseTokens(tokens);

return {
hitsImage(image: HTMLElement) {
return matcher(image);
}
};
}

export default parseSearch;
35 changes: 35 additions & 0 deletions assets/js/query/boolean.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { AstMatcher } from './types';

export function matchAny(...matchers: AstMatcher[]): AstMatcher {
return (e: HTMLElement) => {
for (const matcher of matchers) {
if (matcher(e)) {
return true;
}
}
return false;
};
}

export function matchAll(...matchers: AstMatcher[]): AstMatcher {
return (e: HTMLElement) => {
for (const matcher of matchers) {
if (!matcher(e)) {
return false;
}
}
return true;
};
}

export function matchNot(matcher: AstMatcher): AstMatcher {
return (e: HTMLElement) => {
return !matcher(e);
};
}

export function matchNone(): AstMatcher {
return () => {
return false;
};
}
145 changes: 145 additions & 0 deletions assets/js/query/date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { FieldMatcher, RangeEqualQualifier } from './types';

type Year = number;
type Month = number;
type Day = number;
type Hours = number;
type Minutes = number;
type Seconds = number;
type AbsoluteDate = [Year, Month, Day, Hours, Minutes, Seconds];
type TimeZoneOffset = [Hours, Minutes];
type PosixTimeMs = number;

function makeMatcher(bottomDate: PosixTimeMs, topDate: PosixTimeMs, qual: RangeEqualQualifier): FieldMatcher {
// The open-left, closed-right date range specified by the
// date/time format limits the types of comparisons that are
// done compared to numeric ranges.
switch (qual) {
case 'lte':
return v => new Date(v).getTime() < topDate;
case 'gte':
return v => new Date(v).getTime() >= bottomDate;
case 'lt':
return v => new Date(v).getTime() < bottomDate;
case 'gt':
return v => new Date(v).getTime() >= topDate;
case 'eq':
default:
return v => {
const t = new Date(v).getTime();
return t >= bottomDate && t < topDate;
};
}
}

function makeRelativeDateMatcher(dateVal: string, qual: RangeEqualQualifier): FieldMatcher {
const match = /(\d+) (second|minute|hour|day|week|month|year)s? ago/.exec(dateVal);
const bounds: Record<string, number> = {
second: 1000,
minute: 60000,
hour: 3600000,
day: 86400000,
week: 604800000,
month: 2592000000,
year: 31536000000
};

if (match) {
const amount = parseInt(match[1], 10);
const scale = bounds[match[2]];

const now = new Date().getTime();
const bottomDate = new Date(now - amount * scale).getTime();
const topDate = new Date(now - (amount - 1) * scale).getTime();

return makeMatcher(bottomDate, topDate, qual);
}

throw new Error(`Cannot parse date string: ${dateVal}`);

}

function makeAbsoluteDateMatcher(dateVal: string, qual: RangeEqualQualifier): FieldMatcher {
const parseRes: RegExp[] = [
/^(\d{4})/,
/^-(\d{2})/,
/^-(\d{2})/,
/^(?:\s+|T|t)(\d{2})/,
/^:(\d{2})/,
/^:(\d{2})/
];
const timeZoneOffset: TimeZoneOffset = [0, 0];
const timeData: AbsoluteDate = [0, 0, 1, 0, 0, 0];

const origDateVal: string = dateVal;
let localDateVal = origDateVal;

const offsetMatch = /([+-])(\d{2}):(\d{2})$/.exec(localDateVal);
if (offsetMatch) {
timeZoneOffset[0] = parseInt(offsetMatch[2], 10);
timeZoneOffset[1] = parseInt(offsetMatch[3], 10);
if (offsetMatch[1] === '-') {
timeZoneOffset[0] *= -1;
timeZoneOffset[1] *= -1;
}
localDateVal = localDateVal.substr(0, localDateVal.length - 6);
}
else {
localDateVal = localDateVal.replace(/[Zz]$/, '');
}

let matchIndex = 0;
for (; matchIndex < parseRes.length; matchIndex += 1) {
if (localDateVal.length === 0) {
break;
}

const componentMatch = parseRes[matchIndex].exec(localDateVal);
if (componentMatch) {
if (matchIndex === 1) {
// Months are offset by 1.
timeData[matchIndex] = parseInt(componentMatch[1], 10) - 1;
}
else {
// All other components are not offset.
timeData[matchIndex] = parseInt(componentMatch[1], 10);
}

// Slice string.
localDateVal = localDateVal.substr(
componentMatch[0].length, localDateVal.length - componentMatch[0].length
);
}
else {
throw new Error(`Cannot parse date string: ${origDateVal}`);
}
}

if (localDateVal.length > 0) {
throw new Error(`Cannot parse date string: ${origDateVal}`);
}

// Apply the user-specified time zone offset. The JS Date constructor
// is very flexible here.
timeData[3] -= timeZoneOffset[0];
timeData[4] -= timeZoneOffset[1];

const asPosix = (data: AbsoluteDate) => {
return new Date(Date.UTC.apply(Date, data)).getTime();
};

const bottomDate = asPosix(timeData);
timeData[matchIndex - 1] += 1;
const topDate = asPosix(timeData);

return makeMatcher(bottomDate, topDate, qual);
}

export function makeDateMatcher(dateVal: string, qual: RangeEqualQualifier): FieldMatcher {
try {
return makeAbsoluteDateMatcher(dateVal, qual);
}
catch (_) {
return makeRelativeDateMatcher(dateVal, qual);
}
}
37 changes: 37 additions & 0 deletions assets/js/query/fields.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { FieldName } from './types';

type AttributeName = string;

export const numberFields: FieldName[] =
['id', 'width', 'height', 'aspect_ratio',
'comment_count', 'score', 'upvotes', 'downvotes',
'faves', 'tag_count'];

export const dateFields: FieldName[] = ['created_at'];

export const literalFields =
['tags', 'orig_sha512_hash', 'sha512_hash',
'score', 'uploader', 'source_url', 'description'];

export const termSpaceToImageField: Record<FieldName, AttributeName> = {
tags: 'data-image-tag-aliases',
score: 'data-score',
upvotes: 'data-upvotes',
downvotes: 'data-downvotes',
uploader: 'data-uploader',
// Yeah, I don't think this is reasonably supportable.
// faved_by: 'data-faved-by',
id: 'data-image-id',
width: 'data-width',
height: 'data-height',
/* eslint-disable camelcase */
aspect_ratio: 'data-aspect-ratio',
comment_count: 'data-comment-count',
tag_count: 'data-tag-count',
source_url: 'data-source-url',
faves: 'data-faves',
sha512_hash: 'data-sha512',
orig_sha512_hash: 'data-orig-sha512',
created_at: 'data-created-at'
/* eslint-enable camelcase */
};
Loading

0 comments on commit a724644

Please sign in to comment.