parent
10d55dd3e9
commit
deeeafe274
Binary file not shown.
Before Width: | Height: | Size: 31 KiB |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-google" viewBox="0 0 16 16">
|
||||
<path d="M15.545 6.558a9.42 9.42 0 0 1 .139 1.626c0 2.434-.87 4.492-2.384 5.885h.002C11.978 15.292 10.158 16 8 16A8 8 0 1 1 8 0a7.689 7.689 0 0 1 5.352 2.082l-2.284 2.284A4.347 4.347 0 0 0 8 3.166c-2.087 0-3.86 1.408-4.492 3.304a4.792 4.792 0 0 0 0 3.063h.003c.635 1.893 2.405 3.301 4.492 3.301 1.078 0 2.004-.276 2.722-.764h-.003a3.702 3.702 0 0 0 1.599-2.431H8v-3.08h7.545z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 513 B |
10
index.css
10
index.css
@ -1,10 +0,0 @@
|
||||
#cursor {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#message {
|
||||
transition-property: opacity;
|
||||
transition-duration: 0.3s;
|
||||
}
|
117
index.html
117
index.html
@ -1,117 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<link rel="icon" href="images/google.svg" />
|
||||
|
||||
<meta name="author" content="Nato Boram" />
|
||||
<meta name="keywords" content="Google, Lmgtfy" />
|
||||
|
||||
<meta property="og:image" content="images/Lmgtfy.png" />
|
||||
<meta property="og:title" content="Let me Google that for you" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.10.2/dist/umd/popper.min.js"
|
||||
integrity="sha384-7+zCNj/IqJ95wo16oMtfsKbZ9ccEh31eOz1HGyDuCQ6wgnyJNSYdrPa03rtR1zdB"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"
|
||||
integrity="sha384-QJHtvGhmr9XOIpI6YVutG+2QOK9T+ZnN4kzFN1RtK3zEFEIsxhlmWl5/YESvpZ13"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.6.1/font/bootstrap-icons.css"
|
||||
/>
|
||||
|
||||
<link rel="stylesheet" href="index.css" />
|
||||
<script src="index.js"></script>
|
||||
|
||||
<title>Let me Google that for you</title>
|
||||
</head>
|
||||
|
||||
<body class="bg-light">
|
||||
<!-- Navbar -->
|
||||
<nav class="navbar navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<div></div>
|
||||
<button
|
||||
class="btn bi-brightness-high btn-light"
|
||||
onclick="toggleBrightness()"
|
||||
></button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="container">
|
||||
<form class="row g-3 text-center mt-5">
|
||||
<!-- Google -->
|
||||
<label class="display-1 form-label fw-normal mb-0" for="input">
|
||||
<span class="text-primary">G</span><span class="text-danger">o</span
|
||||
><span class="text-warning">o</span><span class="text-primary">g</span
|
||||
><span class="text-success">l</span><span class="text-danger">e</span>
|
||||
</label>
|
||||
<small class="text-muted mt-0">Let me Google that for you</small>
|
||||
|
||||
<!-- Input -->
|
||||
<div
|
||||
class="
|
||||
bg-light
|
||||
border border-1 border-light
|
||||
input-group
|
||||
rounded-pill
|
||||
shadow-sm
|
||||
"
|
||||
>
|
||||
<label
|
||||
class="bg-light bi-search border-0 input-group-text text-dark"
|
||||
for="input"
|
||||
id="searchicon"
|
||||
></label>
|
||||
<input
|
||||
class="form-control border-0 bg-light text-dark"
|
||||
id="input"
|
||||
maxlength="2048"
|
||||
name="search"
|
||||
placeholder="Search"
|
||||
required
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div>
|
||||
<button class="btn btn-light" id="search" type="submit">
|
||||
Google Search
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-light"
|
||||
id="lucky"
|
||||
name="lucky"
|
||||
title="Open the first search result"
|
||||
type="submit"
|
||||
value="true"
|
||||
>
|
||||
I'm feeling lucky
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Alert -->
|
||||
<div id="message" class="alert text-start opacity-0">
|
||||
<h5 id="message-heading" class="alert-heading"></h5>
|
||||
<div id="message-content"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
160
index.js
160
index.js
@ -1,160 +0,0 @@
|
||||
"use strict"
|
||||
|
||||
/** @type {{search: string; lucky?: boolean;}} */
|
||||
const query = window.location.search
|
||||
.substr(1)
|
||||
.split("&")
|
||||
.map(keyValue => keyValue.split("="))
|
||||
.map(([key, value]) => ({
|
||||
[decodeURIComponent(key)]: decodeURIComponent(
|
||||
value?.replaceAll("+", "%20")
|
||||
),
|
||||
}))
|
||||
.reduce((previous, current) => ({ ...previous, ...current }), {})
|
||||
|
||||
/** @type {HTMLInputElement} */
|
||||
let input
|
||||
|
||||
window.addEventListener("load", async () => {
|
||||
input = document.getElementById("input")
|
||||
input.value = ""
|
||||
|
||||
setBrightness(JSON.parse(localStorage.getItem("dark") ?? "false"))
|
||||
|
||||
if (!query.search) return
|
||||
|
||||
await setMessage("Step 1", "Type in your question")
|
||||
const cursor = makeCursor()
|
||||
await move(cursor, input)
|
||||
input.focus()
|
||||
await write()
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
input.blur()
|
||||
|
||||
await setMessage("Step 2", "Click on the search button")
|
||||
const button = query.lucky
|
||||
? document.getElementById("lucky")
|
||||
: document.getElementById("search")
|
||||
await move(cursor, button)
|
||||
button.focus()
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
|
||||
await setMessage("Come on", "Was it really that hard?", "alert-success")
|
||||
await new Promise(resolve => setTimeout(resolve, 3000))
|
||||
|
||||
window.location.href = `https://www.google.com/search?${
|
||||
query.lucky ? "btnI&" : ""
|
||||
}q=${query.search}`
|
||||
})
|
||||
|
||||
function makeCursor() {
|
||||
const dark = JSON.parse(localStorage.getItem("dark") ?? "false")
|
||||
|
||||
const cursor = document.createElement("span")
|
||||
cursor.className = `bi-cursor-fill text-${dark ? "light" : "dark"}`
|
||||
cursor.id = "cursor"
|
||||
document.body.appendChild(cursor)
|
||||
return cursor
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the cursor to the targeted element
|
||||
* @param {HTMLSpanElement} cursor
|
||||
* @param {HTMLButtonElement} target
|
||||
*/
|
||||
async function move(cursor, target) {
|
||||
return new Promise(resolve => {
|
||||
const diffX =
|
||||
target.getBoundingClientRect().left +
|
||||
target.clientWidth / 2 -
|
||||
cursor.getBoundingClientRect().left
|
||||
const diffY =
|
||||
target.getBoundingClientRect().top +
|
||||
target.clientHeight / 2 -
|
||||
cursor.getBoundingClientRect().top
|
||||
|
||||
const steps = 60
|
||||
const stepX = diffX / steps
|
||||
const stepY = diffY / steps
|
||||
|
||||
let step = 0
|
||||
const interval = setInterval(frame, 1000 / 60)
|
||||
|
||||
function frame() {
|
||||
if (step >= steps) {
|
||||
clearInterval(interval)
|
||||
resolve()
|
||||
} else {
|
||||
step++
|
||||
cursor.style.top = (parseFloat(cursor.style.top) || 0) + stepY + "px"
|
||||
cursor.style.left = (parseFloat(cursor.style.left) || 0) + stepX + "px"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function write() {
|
||||
for (const letter of query.search) {
|
||||
await new Promise(resolve => setTimeout(resolve, Math.random() * 200 + 100))
|
||||
input.value += letter
|
||||
input.scrollLeft = input.scrollWidth
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message box under the search buttons.
|
||||
* @param {string} heading
|
||||
* @param {string} content
|
||||
* @param {string} type
|
||||
*/
|
||||
async function setMessage(heading, content, type = "alert-primary") {
|
||||
const message = document.getElementById("message")
|
||||
|
||||
message.classList.add("opacity-0")
|
||||
await new Promise(resolve => setTimeout(resolve, 300))
|
||||
|
||||
message.classList.remove("alert-primary")
|
||||
message.classList.remove("alert-success")
|
||||
message.classList.add(type)
|
||||
document.getElementById("message-heading").innerText = heading
|
||||
document.getElementById("message-content").innerText = content
|
||||
|
||||
message.classList.remove("opacity-0")
|
||||
await new Promise(resolve => setTimeout(resolve, 300))
|
||||
}
|
||||
|
||||
function toggleBrightness() {
|
||||
const dark = JSON.parse(localStorage.getItem("dark") ?? "false")
|
||||
localStorage.setItem("dark", !dark)
|
||||
setBrightness(!dark)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply brightness on the page.
|
||||
* @param {boolean} dark
|
||||
*/
|
||||
function setBrightness(dark) {
|
||||
const newbrightness = dark ? "dark" : "light"
|
||||
const oldBrightness = dark ? "light" : "dark"
|
||||
|
||||
for (const oldClass of [
|
||||
`bg-${oldBrightness}`,
|
||||
`navbar-${oldBrightness}`,
|
||||
`btn-${oldBrightness}`,
|
||||
`border-${oldBrightness}`,
|
||||
]) {
|
||||
const newClass = oldClass.replace(oldBrightness, newbrightness)
|
||||
for (const element of document.querySelectorAll(`.${oldClass}`)) {
|
||||
element.classList.remove(oldClass)
|
||||
element.classList.add(newClass)
|
||||
}
|
||||
}
|
||||
|
||||
for (const oldClass of [`text-${newbrightness}`]) {
|
||||
const newClass = oldClass.replace(newbrightness, oldBrightness)
|
||||
for (const element of document.querySelectorAll(`.${oldClass}`)) {
|
||||
element.classList.remove(oldClass)
|
||||
element.classList.add(newClass)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
/* Projects */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
/* Language and Environment */
|
||||
"target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
|
||||
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
/* Modules */
|
||||
"module": "ESNext" /* Specify what module code is generated. */,
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files */
|
||||
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
/* Type Checking */
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
|
||||
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
1588
pnpm-lock.yaml
1588
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user