15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/services/microlink/screenshot.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class Screenshot extends MicrolinkBaseClass {
4 | name = "screenshot";
5 | icon = ``;
6 | color = "#e67e22";
7 | }
8 |
--------------------------------------------------------------------------------
/src/services/microlink/url.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class URL extends MicrolinkBaseClass {
4 | name = "url";
5 | icon = ``;
6 | color = "#8e44ad";
7 | }
8 |
--------------------------------------------------------------------------------
/tests/__mocks__/tabbable.ts:
--------------------------------------------------------------------------------
1 | const lib = jest.requireActual("tabbable");
2 |
3 | const tabbable = {
4 | ...lib,
5 | tabbable: (node: any, options: any) =>
6 | lib.tabbable(node, { ...options, displayCheck: "none" }),
7 | focusable: (node: any, options: any) =>
8 | lib.focusable(node, { ...options, displayCheck: "none" }),
9 | isFocusable: (node: any, options: any) =>
10 | lib.isFocusable(node, { ...options, displayCheck: "none" }),
11 | isTabbable: (node: any, options: any) =>
12 | lib.isTabbable(node, { ...options, displayCheck: "none" }),
13 | };
14 |
15 | module.exports = tabbable;
16 |
--------------------------------------------------------------------------------
/tests/helpers/elements.test.ts:
--------------------------------------------------------------------------------
1 | import { getElements, safeListen } from "../../src/helpers/elements";
2 |
3 | test("get elements", () =>
4 | expect(getElements(".no-elements-returned")).toEqual([]));
5 |
6 | test("safe listen", () =>
7 | expect(
8 | safeListen(document.createElement("div"), "click", () => {})
9 | ).toBeUndefined());
10 |
11 | test("safe listen adds listener", () => {
12 | let completed = false;
13 | const button = document.createElement("button");
14 | safeListen(button, "click", () => (completed = true));
15 | button.click();
16 | expect(completed).toBeTruthy();
17 | });
18 |
--------------------------------------------------------------------------------
/src/effects/filter/contrast.ts:
--------------------------------------------------------------------------------
1 | import UpploadFilterBaseClass from "../../helpers/filter";
2 |
3 | export default class Contrast extends UpploadFilterBaseClass {
4 | name = "contrast";
5 | icon = ``;
6 | cssFilter = "contrast";
7 | unit = "%";
8 | value = 100;
9 | max = 200;
10 | }
11 |
--------------------------------------------------------------------------------
/scripts/build-lang.js:
--------------------------------------------------------------------------------
1 | const { translateObject } = require("auto-i18n");
2 | const { writeFileSync, readFileSync } = require("fs");
3 | const { join } = require("path");
4 |
5 | let lang;
6 | eval(
7 | "lang = " +
8 | readFileSync(join("src", "i18n", "nl.ts"))
9 | .toString()
10 | .replace("export default ", "")
11 | );
12 |
13 | translateObject(lang, "de")
14 | .then(translation => {
15 | const content = `export default ${JSON.stringify(translation, null, 2)};\n`;
16 | writeFileSync(join("src", "i18n", "de.ts"), content);
17 | })
18 | .catch(error => {
19 | console.log("Got an error", error);
20 | });
21 |
--------------------------------------------------------------------------------
/src/effects/filter/sepia.ts:
--------------------------------------------------------------------------------
1 | import UpploadFilterBaseClass from "../../helpers/filter";
2 |
3 | export default class Sepia extends UpploadFilterBaseClass {
4 | name = "sepia";
5 | icon = ``;
6 | cssFilter = "sepia";
7 | unit = "%";
8 | value = 0;
9 | max = 100;
10 | }
11 |
--------------------------------------------------------------------------------
/content/multiple-files.md:
--------------------------------------------------------------------------------
1 | # Uploading multiple files
2 |
3 | Uppload is designed for single-image uploads, like profile and cover photos. However, you can use the `multiple` property in the constructor object, but this is not recommended because effects only work with single images.
4 |
5 | ```ts
6 | const uploader = new Uppload({
7 | multiple: true,
8 | });
9 | ```
10 |
11 | If users select only a single image, effects like crop and filters continue to work, but when users select more than one file, effects are skipped and the images are directly uploaded. You'll also have to use a custom uploader which supports uploading multiple files at once.
12 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | ---
5 |
6 | **Is your feature request related to a problem? Please describe.**
7 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
8 |
9 | **Describe the solution you'd like**
10 | A clear and concise description of what you want to happen.
11 |
12 | **Describe alternatives you've considered**
13 | A clear and concise description of any alternative solutions or features you've considered.
14 |
15 | **Additional context**
16 | Add any other context or screenshots about the feature request here.
17 |
--------------------------------------------------------------------------------
/scripts/build-demo.js:
--------------------------------------------------------------------------------
1 | const { join } = require("path");
2 | const { writeFileSync, readdirSync, readFileSync, mkdirSync } = require("fs");
3 |
4 | const siteFiles = readdirSync(join(__dirname, "..", "dist")).filter(
5 | file =>
6 | file.startsWith("demo.") && (file.endsWith(".css") || file.endsWith(".js"))
7 | );
8 |
9 | siteFiles.forEach(file => {
10 | const content = readFileSync(join(__dirname, "..", "dist", file)).toString();
11 | if (file.endsWith(".js")) {
12 | writeFileSync(join(__dirname, "..", "public", "uppload-demo.js"), content);
13 | } else {
14 | writeFileSync(join(__dirname, "..", "public", "uppload-demo.css"), content);
15 | }
16 | });
17 |
--------------------------------------------------------------------------------
/src/services/microlink/9gag.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class NineGag extends MicrolinkBaseClass {
4 | name = "ninegag";
5 | icon = ``;
6 | color = "#000";
7 | exampleURL = "https://9gag.com/gag/awoBXb8";
8 | validator = (input: string) =>
9 | /(https?:\/\/(.+?\.)?9gag\.com(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(
10 | input
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "rootDir": "./src",
4 | "target": "ESNext",
5 | "useDefineForClassFields": true,
6 | "module": "ESNext",
7 | "lib": ["ESNext", "DOM"],
8 | "moduleResolution": "Node",
9 | "strict": true,
10 | "sourceMap": true,
11 | "resolveJsonModule": true,
12 | "esModuleInterop": true,
13 | "noEmit": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitReturns": true,
17 | "forceConsistentCasingInFileNames": true,
18 | "types": ["vite/client", "node", "jest"]
19 | },
20 | "include": ["src"],
21 | "exclude": ["**/*.test.ts", "node_modules", "test/**", ".history/**"]
22 | }
23 |
--------------------------------------------------------------------------------
/src/effects/filter/hue-rotate.ts:
--------------------------------------------------------------------------------
1 | import UpploadFilterBaseClass from "../../helpers/filter";
2 |
3 | export default class HueRotate extends UpploadFilterBaseClass {
4 | name = "hue-rotate";
5 | icon = ``;
6 | cssFilter = "hue-rotate";
7 | unit = "deg";
8 | value = 0;
9 | max = 360;
10 | }
11 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | push:
4 | branches:
5 | - master
6 | jobs:
7 | release:
8 | name: Release
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout
12 | uses: actions/checkout@v3
13 | with:
14 | fetch-depth: 0
15 | - name: Setup Node.js
16 | uses: actions/setup-node@v3
17 | with:
18 | node-version: "lts/*"
19 | cache: npm
20 | - name: Install dependencies
21 | run: npm ci
22 | - name: Release
23 | env:
24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
25 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
26 | run: npx semantic-release
27 |
--------------------------------------------------------------------------------
/content/services/camera.md:
--------------------------------------------------------------------------------
1 | # Camera service
2 |
3 | Uppload's Camera service allows users to click a photo using their device's camera.
4 |
5 | 
6 |
7 | To add the Camera service to your build, simply import it, initialize the class to an object, and use the `Uppload.use()` function:
8 |
9 | ```ts
10 | import { Uppload, Camera } from "uppload";
11 |
12 | const picture = new Uppload();
13 | picture.use(new Camera());
14 | ```
15 |
16 | This service is built for non-mobile devices (like laptops with webcams) since the native file picker (for Uppload, that is the [Local](/services/local) service) allows users to click photos in all major mobile operating systems.
17 |
--------------------------------------------------------------------------------
/src/services/microlink/deviantart.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class DeviantArt extends MicrolinkBaseClass {
4 | name = "deviantart";
5 | icon = ``;
6 | color = "#00d159";
7 | exampleURL =
8 | "https://www.deviantart.com/artbycatherineradley/art/Despair-820869682";
9 | validator = (input: string) =>
10 | /(https?:\/\/(.+?\.)?(deviantart|fav)\.(com|me)(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(
11 | input
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | push:
4 | branches:
5 | - master
6 | pull_request:
7 | branches:
8 | - master
9 | jobs:
10 | test:
11 | runs-on: ubuntu-latest
12 | if: "!contains(github.event.head_commit.message, '[skip ci]')"
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v3
16 | with:
17 | fetch-depth: 0
18 | - name: Setup Node.js
19 | uses: actions/setup-node@v3
20 | with:
21 | node-version: "lts/*"
22 | cache: npm
23 | - name: Install dependencies
24 | run: npm ci
25 | - name: Build
26 | run: npm run build
27 | - name: Run tests
28 | run: npm run test
29 |
--------------------------------------------------------------------------------
/src/services/microlink/artstation.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class ArtStation extends MicrolinkBaseClass {
4 | name = "artstation";
5 | icon = ``;
6 | color = "#3ea2cf";
7 | exampleURL = "https://www.artstation.com/artwork/VdGOkZ";
8 | validator = (input: string) =>
9 | /(https?:\/\/(.+?\.)?artstation\.com(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(
10 | input
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import {defineConfig} from "vite";
3 |
4 | module.exports = defineConfig({
5 | base: "./",
6 | build: {
7 | lib: {
8 | entry: path.resolve(__dirname, "src/index.ts"),
9 | name: 'Uppload',
10 | formats: ['es', 'cjs', 'umd', 'iife'],
11 | fileName: 'uppload',
12 | },
13 | rollupOptions: {
14 | external: ["focus-trap", "mitt", "cropperjs"],
15 | output: {
16 | globals: {
17 | "focus-trap": "createFocusTrap",
18 | mitt: "mitt",
19 | cropperjs: "Cropper",
20 | },
21 | },
22 | },
23 | },
24 | });
25 |
--------------------------------------------------------------------------------
/.staartrc:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Uppload",
3 | "baseUrl": "https://uppload.js.org",
4 | "themeColor": "#f05d5e",
5 | "textColor": "#1b0000",
6 | "linkColor": "#dc3545",
7 | "googleAnalytics": "UA-15148382-2",
8 | "agastyaApiKey": "uppload",
9 | "algoliaApiKey": "a6a7714b2b52dbd3313ebef7075746d5",
10 | "algoliaIndex": "uppload",
11 | "navbar": [
12 | "[Docs](/)",
13 | "[Getting started](/getting-started)",
14 | "[Blog](/blog)",
15 | "[GitHub](https://github.com/elninotech/uppload)"
16 | ],
17 | "twitterPublisher": "elninoict",
18 | "scripts": ["/uppload-demo.js"],
19 | "stylesheets": ["/uppload-demo.css", "/assets/demo.css"],
20 | "footerNavbar": ["[Hosted by Netlify](https://netlify.com)"],
21 | "fancyLinks": true
22 | }
23 |
--------------------------------------------------------------------------------
/src/services/microlink/flickr.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class Flickr extends MicrolinkBaseClass {
4 | name = "flickr";
5 | icon = ``;
6 | noRecolor = true;
7 | color = "#ff0084";
8 | exampleURL = "https://www.flickr.com/photos/renewolf/26111951000/";
9 | validator = (input: string) =>
10 | /(https?:\/\/(.+?\.)?(flickr|flic)\.(com|kr)(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(
11 | input
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/services/microlink/flipboard.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class Flipboard extends MicrolinkBaseClass {
4 | name = "flipboard";
5 | icon = ``;
6 | color = "#e12828";
7 | exampleURL =
8 | "https://flipboard.com/@bbcfuture/how-climate-change-could-kill-the-red-apple/f-c8d499b4ca%2Fbbc.com";
9 | validator = (input: string) =>
10 | /(https?:\/\/(.+?\.)?(flipboard|flip)\.(com|it)(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(
11 | input
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/services/microlink/tumblr.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class Tumblr extends MicrolinkBaseClass {
4 | name = "tumblr";
5 | icon = ``;
6 | color = "#34526f";
7 | exampleURL =
8 | "https://germanpostwarmodern.tumblr.com/post/186653088149/cubicus-building-of-twente-university-1969-73-in";
9 | validator = (input: string) =>
10 | /(https?:\/\/(.+?\.)?tumblr\.com(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(
11 | input
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/services/microlink/weheartit.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class WeHeartIt extends MicrolinkBaseClass {
4 | name = "weheartit";
5 | icon = ``;
6 | color = "#ff5464";
7 | exampleURL = "https://weheartit.com/entry/221671573";
8 | validator = (input: string) =>
9 | /(https?:\/\/(.+?\.)?weheartit\.com(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(
10 | input
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/content/bundle-size.md:
--------------------------------------------------------------------------------
1 | # Bundle size
2 |
3 | ## Compared to v1
4 |
5 | _This section is currently in development._
6 |
7 | | Bundle type | Uppload v1 | Uppload v2 |
8 | | --------------- | ---------- | ------------ |
9 | | Lean build | 39.8 kB | some |
10 | | True build | 59.1 kB | some plugins |
11 | | Full build | 134 kB | 137 kB |
12 | | True full build | 154 kB | 137 kB |
13 |
14 | - In Uppload v1, Lean bundle includes no polyfills; in Uppload v2, it includes no plugins
15 | - Uppload v1 included lazy-loaded components, True builds includes them too
16 | - In Uppload v2, True build includes 6 services and 5 effects, the recommended
17 | - True full builds include all polyfills and plugins
18 | - Uppload v1 included CSS styles while v2 requires loading them separately
19 |
--------------------------------------------------------------------------------
/src/services/microlink/facebook.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class Facebook extends MicrolinkBaseClass {
4 | name = "facebook";
5 | icon = ``;
6 | color = "#1b69f6";
7 | exampleURL =
8 | "https://www.facebook.com/elninotech/photos/a.2066268863489861/2066268886823192/?type=3&theater";
9 | validator = (input: string) =>
10 | /(https?:\/\/(.+?\.)?(facebook|fb)\.(com|me)(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(
11 | input
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/scripts/build-scss.js:
--------------------------------------------------------------------------------
1 | const sass = require("sass");
2 | const { join } = require("path");
3 | const { writeFileSync, readdirSync, mkdirSync } = require("fs");
4 |
5 | const result = sass.renderSync({
6 | file: join(__dirname, "..", "src", "styles", "uppload.scss"),
7 | });
8 | writeFileSync(join(__dirname, "..", "dist", "uppload.css"), result.css);
9 |
10 | const themes = readdirSync(join(__dirname, "..", "src", "themes")).filter(
11 | theme => theme !== "theme.scss"
12 | );
13 | mkdirSync(join(__dirname, "..", "dist", "themes"), {
14 | recursive: true,
15 | });
16 | themes.forEach(theme => {
17 | const result = sass.renderSync({
18 | file: join(__dirname, "..", "src", "themes", theme),
19 | });
20 | writeFileSync(
21 | join(__dirname, "..", "dist", "themes", theme.replace(".scss", ".css")),
22 | result.css
23 | );
24 | });
25 |
--------------------------------------------------------------------------------
/src/services/microlink/twitter.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class Twitter extends MicrolinkBaseClass {
4 | name = "twitter";
5 | icon = ``;
6 | color = "#1da1f2";
7 | exampleURL = "https://twitter.com/elninoict/status/1106176415622418433";
8 | validator = (input: string) =>
9 | /(https?:\/\/(.+?\.)?(twitter|t)\.(co|com)(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(
10 | input
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/content/treeshaking.md:
--------------------------------------------------------------------------------
1 | # A-la-carte (treeshaking) plugins
2 |
3 | It's very easy to create your own custom build of Uppload, so you only load the feature you want without bloating up your build size.
4 |
5 | For example, you can add the following services:
6 |
7 | - Local (select file from computer)
8 | - Direct URL
9 | - Search on Unsplash and Pexels
10 |
11 | You can import the required services from the `uppload` package and create your package:
12 |
13 | ```ts
14 | import { Uppload, Local, URL, Unsplash, Pexels, en } from "uppload";
15 |
16 | const uploader = new Uppload({ lang: en });
17 | uploader.use([
18 | new Local(),
19 | new URL(),
20 | new Unsplash("unsplash-api-key"),
21 | new Pexels("pexels-api-key"),
22 | ]);
23 | ```
24 |
25 | Similarly, you can import effects and uploaders and use the `Uppload.use()` function to build your package.
26 |
--------------------------------------------------------------------------------
/content/index.md:
--------------------------------------------------------------------------------
1 | # Uppload
2 |
3 | Uppload is a better JavaScript image uploader. It's highly customizable with 30+ plugins, completely free and open-source, and can be used with any file uploading backend.
4 |
5 | - [Getting started](/getting-started)
6 | - [Configuration](/configuration)
7 | - [Examples](/examples)
8 | - [A-la-carte (treeshaking) plugins](/treeshaking)
9 | - [Uppload API](/api)
10 | - [Listening to events](/listening-to-events)
11 | - [Services](/services) (20+ ways to select a file)
12 | - [Effects](/effects) (10+ ways to edit a file)
13 | - [Uploaders](/uploaders) (ways to send a file to the server)
14 | - [Themes](/themes)
15 | - [Backends](/backends)
16 | - [Frontend frameworks](/wrappers)
17 | - [Blog](/blog)
18 | - [Image compression](/compression)
19 | - [Internationalization](/i18n)
20 | - [Accessibility](/a11y)
21 | - [Compare Uppload](/compare)
22 | - [FAQs](/faq)
23 |
--------------------------------------------------------------------------------
/src/services/microlink/pinterest.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class Pinterest extends MicrolinkBaseClass {
4 | name = "pinterest";
5 | icon = ``;
6 | color = "#e60023";
7 | exampleURL = "https://pinterest.com/pin/437201076327078006/";
8 | validator = (input: string) =>
9 | /(https?:\/\/(.+?\.)?(pinterest|pin)\.(com|it)(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(
10 | input
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/assets/icon-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/content/help/services/import-from-web-service/9gag.md:
--------------------------------------------------------------------------------
1 | # Import image from 9GAG
2 |
3 | ## How do I upload an image using 9GAG?
4 |
5 | You can import an image using its 9GAG URL and then perform manipulations (like cropping or filters). 9GAG URLs start with "9gag.com".
6 |
7 | 1. Find the 9GAG URL of your photo and copy it
8 | 2. Click on the "9GAG" button in the widget
9 | 3. Paste the 9GAG URL in the textbox
10 | 4. Click on the "Import from 9GAG" button
11 |
12 | ## How do I find the 9GAG URL of an image?
13 |
14 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
15 |
16 | ## Why don't I see the 9GAG icon?
17 |
18 | If you don't see the 9GAG icon, it could be because:
19 |
20 | - Your browser does not support importing an image from 9GAG; if this is the case, you should update your browser
21 | - The website has not enabled the 9GAG import feature
22 |
23 | 
24 |
--------------------------------------------------------------------------------
/content/help/services/url.md:
--------------------------------------------------------------------------------
1 | # Import image from URL
2 |
3 | ## How do I upload an image from a URL?
4 |
5 | You can import an image using its URL and then perform manipulations (like cropping or filters). URL start with "http://" or "https://".
6 |
7 | 1. Find the URL of your photo and copy it
8 | 2. Click on the "Direct URL" button in the widget
9 | 3. Paste the URL in the textbox
10 | 4. Click on the "Import from URL" button
11 |
12 | ## How do I find the URL of an image?
13 |
14 | ### Web
15 |
16 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
17 |
18 | ## Why don't I see the Direct URL icon?
19 |
20 | If you don't see the Direct URL icon, it could be because:
21 |
22 | - Your browser does not support importing an image from URL; if this is the case, you should update your browser
23 | - The website has not enabled the Direct URL import feature
24 |
25 | 
26 |
--------------------------------------------------------------------------------
/src/services/microlink/linkedin.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class LinkedIn extends MicrolinkBaseClass {
4 | name = "linkedin";
5 | icon = ``;
6 | color = "#0e76a8";
7 | exampleURL =
8 | "https://www.linkedin.com/posts/explorius-vastgoedontwikkeling-b-v-_el-nino-huurt-kantoor-in-enschede-activity-6480386878641180672-7DC_";
9 | validator = (input: string) =>
10 | /(https?:\/\/(.+?\.)?linkedin\.com(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(
11 | input
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/effects/filter/grayscale.ts:
--------------------------------------------------------------------------------
1 | import UpploadFilterBaseClass from "../../helpers/filter";
2 |
3 | export default class Grayscale extends UpploadFilterBaseClass {
4 | name = "grayscale";
5 | icon = ``;
6 | cssFilter = "grayscale";
7 | unit = "%";
8 | value = 0;
9 | max = 100;
10 | }
11 |
--------------------------------------------------------------------------------
/src/services/microlink/fotki.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class Fotki extends MicrolinkBaseClass {
4 | name = "fotki";
5 | icon = ``;
6 | color = "#5471B9";
7 | exampleURL =
8 | "https://public.fotki.com/EricAnke/holland/molens/20170928-162510.html";
9 | validator = (input: string) =>
10 | /(https?:\/\/(.+?\.)?fotki\.com(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(
11 | input
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/content/help/services/import-from-web-service/fotki.md:
--------------------------------------------------------------------------------
1 | # Import image from Fotki
2 |
3 | ## How do I upload an image using Fotki?
4 |
5 | You can import an image using its Fotki URL and then perform manipulations (like cropping or filters). Fotki URLs start with "fotki.com".
6 |
7 | 1. Find the Fotki URL of your photo and copy it
8 | 2. Click on the "Fotki" button in the widget
9 | 3. Paste the Fotki URL in the textbox
10 | 4. Click on the "Import from Fotki" button
11 |
12 | ## How do I find the Fotki URL of an image?
13 |
14 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
15 |
16 | ## Why don't I see the Fotki icon?
17 |
18 | If you don't see the Fotki icon, it could be because:
19 |
20 | - Your browser does not support importing an image from Fotki; if this is the case, you should update your browser
21 | - The website has not enabled the Fotki import feature
22 |
23 | 
24 |
--------------------------------------------------------------------------------
/content/help/services/import-from-web-service/reddit.md:
--------------------------------------------------------------------------------
1 | # Import image from Reddit
2 |
3 | ## How do I upload an image using Reddit?
4 |
5 | You can import an image using its Reddit URL and then perform manipulations (like cropping or filters). Reddit URLs start with "reddit.com".
6 |
7 | 1. Find the Reddit URL of your photo and copy it
8 | 2. Click on the "Reddit" button in the widget
9 | 3. Paste the Reddit URL in the textbox
10 | 4. Click on the "Import from Reddit" button
11 |
12 | ## How do I find the Reddit URL of an image?
13 |
14 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
15 |
16 | ## Why don't I see the Reddit icon?
17 |
18 | If you don't see the Reddit icon, it could be because:
19 |
20 | - Your browser does not support importing an image from Reddit; if this is the case, you should update your browser
21 | - The website has not enabled the Reddit import feature
22 |
23 | 
24 |
--------------------------------------------------------------------------------
/content/help/services/import-from-web-service/tumblr.md:
--------------------------------------------------------------------------------
1 | # Import image from Tumblr
2 |
3 | ## How do I upload an image using Tumblr?
4 |
5 | You can import an image using its Tumblr URL and then perform manipulations (like cropping or filters). Tumblr URLs start with "tumblr.com".
6 |
7 | 1. Find the Tumblr URL of your photo and copy it
8 | 2. Click on the "Tumblr" button in the widget
9 | 3. Paste the Tumblr URL in the textbox
10 | 4. Click on the "Import from Tumblr" button
11 |
12 | ## How do I find the Tumblr URL of an image?
13 |
14 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
15 |
16 | ## Why don't I see the Tumblr icon?
17 |
18 | If you don't see the Tumblr icon, it could be because:
19 |
20 | - Your browser does not support importing an image from Tumblr; if this is the case, you should update your browser
21 | - The website has not enabled the Tumblr import feature
22 |
23 | 
24 |
--------------------------------------------------------------------------------
/content/services/local.md:
--------------------------------------------------------------------------------
1 | # Local service
2 |
3 | Uppload's Local service allows users to upload images from their computer. Users can drag and drop files or click on the "Select a file" button to open a native file browser.
4 |
5 | 
6 |
7 | To add the Local service to your build, simply import it, initialize the class to an object, and use the `Uppload.use()` function:
8 |
9 | ```ts
10 | import { Uppload, Local } from "uppload";
11 |
12 | const picture = new Uppload();
13 | picture.use(new Local());
14 | ```
15 |
16 | In the constructor parameter, you can specify the mime types you want to support and the maximum file size. By default, JPG, PNG, and GIF images are allowed, since Uppload is primarily an image uploader (but you can easily extend it to be a general-purpose file uploader).
17 |
18 | ```ts
19 | const localServiceWithVideo = new Local({
20 | maxFileSize: 25000,
21 | mimeTypes: ["image/png", "image/jpeg", "video/mp4"],
22 | });
23 | ```
24 |
--------------------------------------------------------------------------------
/content/help/services/import-from-web-service/flickr.md:
--------------------------------------------------------------------------------
1 | # Import image from Flickr
2 |
3 | ## How do I upload an image using Flickr?
4 |
5 | You can import an image using its Flickr URL and then perform manipulations (like cropping or filters). Flickr URLs start with "flickr.com" or "flic.kr".
6 |
7 | 1. Find the Flickr URL of your photo and copy it
8 | 2. Click on the "Flickr" button in the widget
9 | 3. Paste the Flickr URL in the textbox
10 | 4. Click on the "Import from Flickr" button
11 |
12 | ## How do I find the Flickr URL of an image?
13 |
14 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
15 |
16 | ## Why don't I see the Flickr icon?
17 |
18 | If you don't see the Flickr icon, it could be because:
19 |
20 | - Your browser does not support importing an image from Flickr; if this is the case, you should update your browser
21 | - The website has not enabled the Flickr import feature
22 |
23 | 
24 |
--------------------------------------------------------------------------------
/content/help/services/import-from-web-service/twitter.md:
--------------------------------------------------------------------------------
1 | # Import image from Twitter
2 |
3 | ## How do I upload an image using Twitter?
4 |
5 | You can import an image using its Twitter URL and then perform manipulations (like cropping or filters). Twitter URLs start with "twitter.com" or "t.co".
6 |
7 | 1. Find the Twitter URL of your photo and copy it
8 | 2. Click on the "Twitter" button in the widget
9 | 3. Paste the Twitter URL in the textbox
10 | 4. Click on the "Import from Twitter" button
11 |
12 | ## How do I find the Twitter URL of an image?
13 |
14 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
15 |
16 | ## Why don't I see the Twitter icon?
17 |
18 | If you don't see the Twitter icon, it could be because:
19 |
20 | - Your browser does not support importing an image from Twitter; if this is the case, you should update your browser
21 | - The website has not enabled the Twitter import feature
22 |
23 | 
24 |
--------------------------------------------------------------------------------
/content/help/services/screenshot.md:
--------------------------------------------------------------------------------
1 | # Take a webpage screenshot
2 |
3 | ## How do I upload an image from a screenshot?
4 |
5 | You can take a webpage's screenshot and then perform manipulations (like cropping or filters). Webpage URLs start with "http://" or "https://".
6 |
7 | 1. Find the URL of the webpage you want to screenshot and copy it
8 | 2. Click on the "Screenshot" button in the widget
9 | 3. Paste the webpage URL in the textbox
10 | 4. Click on the "Take screenshot" button
11 |
12 | ## How do I find the URL of a webpage?
13 |
14 | ### Web
15 |
16 | On the web, open webpage you want to screenshot and copy the URL from your browser's address bar.
17 |
18 | ## Why don't I see the screenshot icon?
19 |
20 | If you don't see the Screenshot icon, it could be because:
21 |
22 | - Your browser does not support take a URL's screenshot; if this is the case, you should update your browser
23 | - The website has not enabled the Screenshot feature
24 |
25 | 
26 |
--------------------------------------------------------------------------------
/content/help/services/import-from-web-service/linkedin.md:
--------------------------------------------------------------------------------
1 | # Import image from LinkedIn
2 |
3 | ## How do I upload an image using LinkedIn?
4 |
5 | You can import an image using its LinkedIn URL and then perform manipulations (like cropping or filters). LinkedIn URLs start with "linkedin.com".
6 |
7 | 1. Find the LinkedIn URL of your photo and copy it
8 | 2. Click on the "LinkedIn" button in the widget
9 | 3. Paste the LinkedIn URL in the textbox
10 | 4. Click on the "Import from LinkedIn" button
11 |
12 | ## How do I find the LinkedIn URL of an image?
13 |
14 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
15 |
16 | ## Why don't I see the LinkedIn icon?
17 |
18 | If you don't see the LinkedIn icon, it could be because:
19 |
20 | - Your browser does not support importing an image from LinkedIn; if this is the case, you should update your browser
21 | - The website has not enabled the LinkedIn import feature
22 |
23 | 
24 |
--------------------------------------------------------------------------------
/src/helpers/utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Minified a string of HTML
3 | * @param html - HTML string to minify
4 | * @returns Minified string
5 | */
6 | export const minifyHTML = (html: string) =>
7 | html.replace(/\n/g, "").replace(/ /g, "");
8 |
9 | /**
10 | * Returns a formatted version of the bytes
11 | * @param bytes - The bytes to format
12 | * @param decimals - The number of decimals to show
13 | * @returns The formatted string
14 | */
15 | export const formatBytes = (bytes: number, decimals: number = 2) => {
16 | if (bytes === 0) {
17 | return "0 Bytes";
18 | }
19 | const base: number = 1024;
20 | const dm: number = decimals < 0 ? 0 : decimals;
21 | const sizes: string[] = [
22 | "Bytes",
23 | "KB",
24 | "MB",
25 | "GB",
26 | "TB",
27 | "PB",
28 | "EB",
29 | "ZB",
30 | "YB",
31 | ];
32 | const index: number = Math.floor(Math.log(bytes) / Math.log(base));
33 | const size: string = sizes[index];
34 | return parseFloat((bytes / Math.pow(base, index)).toFixed(dm)) + " " + size;
35 | };
36 |
--------------------------------------------------------------------------------
/src/services/microlink/reddit.ts:
--------------------------------------------------------------------------------
1 | import { MicrolinkBaseClass } from "../../helpers/microlink";
2 |
3 | export default class Reddit extends MicrolinkBaseClass {
4 | name = "reddit";
5 | icon = ``;
6 | color = "#ff4301";
7 | exampleURL =
8 | "https://www.reddit.com/r/thenetherlands/comments/dz1myk/a_beautiful_morning_in_ermelo/";
9 | validator = (input: string) =>
10 | /(https?:\/\/(.+?\.)?reddit\.com(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(
11 | input
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/content/help/services/import-from-web-service/facebook.md:
--------------------------------------------------------------------------------
1 | # Import image from Facebook
2 |
3 | ## How do I upload an image using Facebook?
4 |
5 | You can import an image using its Facebook URL and then perform manipulations (like cropping or filters). Facebook URLs start with "facebook.com", "fb.com", or "fb.me".
6 |
7 | 1. Find the Facebook URL of your photo and copy it
8 | 2. Click on the "Facebook" button in the widget
9 | 3. Paste the Facebook URL in the textbox
10 | 4. Click on the "Import from Facebook" button
11 |
12 | ## How do I find the Facebook URL of an image?
13 |
14 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
15 |
16 | ## Why don't I see the Facebook icon?
17 |
18 | If you don't see the Facebook icon, it could be because:
19 |
20 | - Your browser does not support importing an image from Facebook; if this is the case, you should update your browser
21 | - The website has not enabled the Facebook import feature
22 |
23 | 
24 |
--------------------------------------------------------------------------------
/content/services/search-for-images.md:
--------------------------------------------------------------------------------
1 | # Search for images
2 |
3 | Users can search for photos from web services which have a free API. This is particulatly useful for letting users upload cover photos or photos in blog posts. Currently supported services are:
4 |
5 | | Service name | Class name |
6 | | ------------ | ---------- |
7 | | Unsplash | `Unsplash` |
8 | | Pexels | `Pexels` |
9 | | Pixabay | `Pixabay` |
10 | | GIPHY | `GIPHY` |
11 |
12 | You can sign up for a free API key for all these services, and they are also CORS-friendly. Under the hood, Uppload caches API responses in local storage.
13 |
14 | 
15 |
16 | All search services work the same way. For example, to add the Unsplash service to your build, simply import it, initialize the class with an API key, and use the `Uppload.use()` function:
17 |
18 | ```ts
19 | import { Uppload, Unsplash } from "uppload";
20 |
21 | const picture = new Uppload();
22 | picture.use(new Unsplash("your-api-key"));
23 | ```
24 |
--------------------------------------------------------------------------------
/src/effects/filter/saturate.ts:
--------------------------------------------------------------------------------
1 | import UpploadFilterBaseClass from "../../helpers/filter";
2 |
3 | export default class Saturate extends UpploadFilterBaseClass {
4 | name = "saturate";
5 | icon = ``;
6 | cssFilter = "saturate";
7 | unit = "%";
8 | value = 100;
9 | max = 200;
10 | }
11 |
--------------------------------------------------------------------------------
/content/help/services/import-from-web-service/flipboard.md:
--------------------------------------------------------------------------------
1 | # Import image from Flipboard
2 |
3 | ## How do I upload an image using Flipboard?
4 |
5 | You can import an image using its Flipboard URL and then perform manipulations (like cropping or filters). Flipboard URLs start with "flipboard.com" or "flip.it".
6 |
7 | 1. Find the Flipboard URL of your photo and copy it
8 | 2. Click on the "Flipboard" button in the widget
9 | 3. Paste the Flipboard URL in the textbox
10 | 4. Click on the "Import from Flipboard" button
11 |
12 | ## How do I find the Flipboard URL of an image?
13 |
14 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
15 |
16 | ## Why don't I see the Flipboard icon?
17 |
18 | If you don't see the Flipboard icon, it could be because:
19 |
20 | - Your browser does not support importing an image from Flipboard; if this is the case, you should update your browser
21 | - The website has not enabled the Flipboard import feature
22 |
23 | 
24 |
--------------------------------------------------------------------------------
/content/help/services/import-from-web-service/pinterest.md:
--------------------------------------------------------------------------------
1 | # Import image from Pinterest
2 |
3 | ## How do I upload an image using Pinterest?
4 |
5 | You can import an image using its Pinterest URL and then perform manipulations (like cropping or filters). Pinterest URLs start with "pinterest.com" or "pin.it".
6 |
7 | 1. Find the Pinterest URL of your photo and copy it
8 | 2. Click on the "Pinterest" button in the widget
9 | 3. Paste the Pinterest URL in the textbox
10 | 4. Click on the "Import from Pinterest" button
11 |
12 | ## How do I find the Pinterest URL of an image?
13 |
14 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
15 |
16 | ## Why don't I see the Pinterest icon?
17 |
18 | If you don't see the Pinterest icon, it could be because:
19 |
20 | - Your browser does not support importing an image from Pinterest; if this is the case, you should update your browser
21 | - The website has not enabled the Pinterest import feature
22 |
23 | 
24 |
--------------------------------------------------------------------------------
/content/help/services/import-from-web-service/artstation.md:
--------------------------------------------------------------------------------
1 | # Import image from ArtStation
2 |
3 | ## How do I upload an image using ArtStation?
4 |
5 | You can import an image using its ArtStation URL and then perform manipulations (like cropping or filters). ArtStation URLs start with "artstation.com".
6 |
7 | 1. Find the ArtStation URL of your photo and copy it
8 | 2. Click on the "ArtStation" button in the widget
9 | 3. Paste the ArtStation URL in the textbox
10 | 4. Click on the "Import from ArtStation" button
11 |
12 | ## How do I find the ArtStation URL of an image?
13 |
14 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
15 |
16 | ## Why don't I see the ArtStation icon?
17 |
18 | If you don't see the ArtStation icon, it could be because:
19 |
20 | - Your browser does not support importing an image from ArtStation; if this is the case, you should update your browser
21 | - The website has not enabled the ArtStation import feature
22 |
23 | 
24 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | ---
5 |
6 | **Describe the bug**
7 | A clear and concise description of what the bug is. Include whether you're using Uppload version 1.x or 2.x.
8 |
9 | **To Reproduce**
10 | Steps to reproduce the behavior:
11 |
12 | 1. Go to '...'
13 | 2. Click on '....'
14 | 3. Scroll down to '....'
15 | 4. See error
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Desktop (please complete the following information):**
24 |
25 | - OS: \[e.g. iOS]
26 | - Browser \[e.g. chrome, safari]
27 | - Version \[e.g. 22]
28 |
29 | **Smartphone (please complete the following information):**
30 |
31 | - Device: \[e.g. iPhone6]
32 | - OS: \[e.g. iOS8.1]
33 | - Browser \[e.g. stock browser, safari]
34 | - Version \[e.g. 22]
35 |
36 | **Additional context**
37 | Add any other context about the problem here.
38 |
--------------------------------------------------------------------------------
/src/effects/filter/invert.ts:
--------------------------------------------------------------------------------
1 | import UpploadFilterBaseClass from "../../helpers/filter";
2 |
3 | export default class Invert extends UpploadFilterBaseClass {
4 | name = "invert";
5 | icon = ``;
6 | cssFilter = "invert";
7 | unit = "%";
8 | value = 0;
9 | max = 100;
10 | }
11 |
--------------------------------------------------------------------------------
/tests/mocks.ts:
--------------------------------------------------------------------------------
1 | import { GlobalWithFetchMock } from "jest-fetch-mock";
2 | import {
3 | IServiceTemplateParams,
4 | IHandlersParams,
5 | ITemplateParams,
6 | translate,
7 | Uppload,
8 | } from "../src";
9 | const uppload = new Uppload();
10 | import xhr from "xhr-mock";
11 |
12 | const customGlobal: GlobalWithFetchMock =
13 | global as unknown as GlobalWithFetchMock;
14 | customGlobal.fetch = require("jest-fetch-mock");
15 | customGlobal.fetchMock = customGlobal.fetch;
16 | export const fetch = customGlobal.fetch;
17 |
18 | const serviceTemplateParams: IServiceTemplateParams = {
19 | uppload,
20 | translate,
21 | };
22 | const handlersParams: IHandlersParams = {
23 | upload: () => new Promise(() => {}),
24 | uploadMultiple: () => new Promise(() => {}),
25 | next: () => {},
26 | handle: () => {},
27 | showHelp: () => {},
28 | uppload,
29 | translate,
30 | };
31 | const effectTemplateParams: ITemplateParams = {
32 | file: { blob: new Blob() },
33 | translate,
34 | };
35 |
36 | export { xhr, serviceTemplateParams, handlersParams, effectTemplateParams };
37 |
--------------------------------------------------------------------------------
/content/help/services/import-from-web-service/deviantart.md:
--------------------------------------------------------------------------------
1 | # Import image from DeviantArt
2 |
3 | ## How do I upload an image using DeviantArt?
4 |
5 | You can import an image using its DeviantArt URL and then perform manipulations (like cropping or filters). DeviantArt URLs start with "deviantart.com" or "fav.me".
6 |
7 | 1. Find the DeviantArt URL of your photo and copy it
8 | 2. Click on the "DeviantArt" button in the widget
9 | 3. Paste the DeviantArt URL in the textbox
10 | 4. Click on the "Import from DeviantArt" button
11 |
12 | ## How do I find the DeviantArt URL of an image?
13 |
14 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
15 |
16 | ## Why don't I see the DeviantArt icon?
17 |
18 | If you don't see the DeviantArt icon, it could be because:
19 |
20 | - Your browser does not support importing an image from DeviantArt; if this is the case, you should update your browser
21 | - The website has not enabled the DeviantArt import feature
22 |
23 | 
24 |
--------------------------------------------------------------------------------
/content/help/services/import-from-web-service/weheartit.md:
--------------------------------------------------------------------------------
1 | # Import image from We Heart It
2 |
3 | ## How do I upload an image using We Heart It?
4 |
5 | You can import an image using its We Heart It URL and then perform manipulations (like cropping or filters). We Heart It URLs start with "weheartit.com".
6 |
7 | 1. Find the We Heart It URL of your photo and copy it
8 | 2. Click on the "We Heart It" button in the widget
9 | 3. Paste the We Heart It URL in the textbox
10 | 4. Click on the "Import from We Heart It" button
11 |
12 | ## How do I find the We Heart It URL of an image?
13 |
14 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
15 |
16 | ## Why don't I see the We Heart It icon?
17 |
18 | If you don't see the We Heart It icon, it could be because:
19 |
20 | - Your browser does not support importing an image from We Heart It; if this is the case, you should update your browser
21 | - The website has not enabled the We Heart It import feature
22 |
23 | 
24 |
--------------------------------------------------------------------------------
/src/helpers/files.ts:
--------------------------------------------------------------------------------
1 | import { IUpploadFile } from "./interfaces";
2 |
3 | /**
4 | * Convert a blob to a native File
5 | * @param blob - Blob to convert to file
6 | * @param fileName - Name of the file
7 | * @param lastModified - Date modified
8 | */
9 | const safeBlobToFile = (blob: Blob, fileName?: string, lastModified?: Date) => {
10 | try {
11 | return new File([blob], fileName || "file_name", {
12 | lastModified: (lastModified || new Date()).getTime(),
13 | type: blob.type,
14 | });
15 | } catch (error) {
16 | return blob;
17 | }
18 | };
19 |
20 | export const blobToUpploadFile = (
21 | blob: Blob,
22 | name?: string,
23 | type?: string,
24 | lastModified?: Date
25 | ) => {
26 | const result: IUpploadFile = {
27 | name,
28 | blob,
29 | lastModified,
30 | type,
31 | };
32 | return result;
33 | };
34 |
35 | export const safeUpploadFileToFile = (file: IUpploadFile) => {
36 | const blob = file.blob;
37 | file.lastModified = file.lastModified || new Date();
38 | return safeBlobToFile(blob, file.name, file.lastModified);
39 | };
40 |
--------------------------------------------------------------------------------
/content/help/services/search/giphy.md:
--------------------------------------------------------------------------------
1 | # Search GIPHY for photos
2 |
3 | ## How do I upload an image using GIPHY?
4 |
5 | You can search for images on GIPHY and then edit and upload the image.
6 |
7 | 1. Click on the "GIPHY" button in the widget
8 | 2. Type your search query in the text box
9 | 3. Click on the "Search on GIPHY" button
10 | 4. Click on the image you want to import from the results
11 |
12 | ## Why don't I see the GIPHY icon?
13 |
14 | If you don't see the GIPHY icon, it could be because:
15 |
16 | - Your browser does not support importing an image from GIPHY; if this is the case, you should update your browser
17 | - The website has not enabled the GIPHY import feature
18 |
19 | ## Why don't I see any search results?
20 |
21 | If you've typed your query and click on the search button but you don't see any search results, it could be because:
22 |
23 | - Your browser does not support searching an image on GIPHY; if this is the case, you should update your browser
24 | - The website has not enabled the searching GIPHY feature
25 |
26 | 
27 |
--------------------------------------------------------------------------------
/content/help/services/search/pexels.md:
--------------------------------------------------------------------------------
1 | # Search Pexels for photos
2 |
3 | ## How do I upload an image using Pexels?
4 |
5 | You can search for images on Pexels and then edit and upload the image.
6 |
7 | 1. Click on the "Pexels" button in the widget
8 | 2. Type your search query in the text box
9 | 3. Click on the "Search on Pexels" button
10 | 4. Click on the image you want to import from the results
11 |
12 | ## Why don't I see the Pexels icon?
13 |
14 | If you don't see the Pexels icon, it could be because:
15 |
16 | - Your browser does not support importing an image from Pexels; if this is the case, you should update your browser
17 | - The website has not enabled the Pexels import feature
18 |
19 | ## Why don't I see any search results?
20 |
21 | If you've typed your query and click on the search button but you don't see any search results, it could be because:
22 |
23 | - Your browser does not support searching an image on Pexels; if this is the case, you should update your browser
24 | - The website has not enabled the searching Pexels feature
25 |
26 | 
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 El Niño BV
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/content/help/services/search/pixabay.md:
--------------------------------------------------------------------------------
1 | # Search Pixabay for photos
2 |
3 | ## How do I upload an image using Pixabay?
4 |
5 | You can search for images on Pixabay and then edit and upload the image.
6 |
7 | 1. Click on the "Pixabay" button in the widget
8 | 2. Type your search query in the text box
9 | 3. Click on the "Search on Pixabay" button
10 | 4. Click on the image you want to import from the results
11 |
12 | ## Why don't I see the Pixabay icon?
13 |
14 | If you don't see the Pixabay icon, it could be because:
15 |
16 | - Your browser does not support importing an image from Pixabay; if this is the case, you should update your browser
17 | - The website has not enabled the Pixabay import feature
18 |
19 | ## Why don't I see any search results?
20 |
21 | If you've typed your query and click on the search button but you don't see any search results, it could be because:
22 |
23 | - Your browser does not support searching an image on Pixabay; if this is the case, you should update your browser
24 | - The website has not enabled the searching Pixabay feature
25 |
26 | 
27 |
--------------------------------------------------------------------------------
/content/help/services/search/unsplash.md:
--------------------------------------------------------------------------------
1 | # Search Unsplash for photos
2 |
3 | ## How do I upload an image using Unsplash?
4 |
5 | You can search for images on Unsplash and then edit and upload the image.
6 |
7 | 1. Click on the "Unsplash" button in the widget
8 | 2. Type your search query in the text box
9 | 3. Click on the "Search on Unsplash" button
10 | 4. Click on the image you want to import from the results
11 |
12 | ## Why don't I see the Unsplash icon?
13 |
14 | If you don't see the Unsplash icon, it could be because:
15 |
16 | - Your browser does not support importing an image from Unsplash; if this is the case, you should update your browser
17 | - The website has not enabled the Unsplash import feature
18 |
19 | ## Why don't I see any search results?
20 |
21 | If you've typed your query and click on the search button but you don't see any search results, it could be because:
22 |
23 | - Your browser does not support searching an image on Unsplash; if this is the case, you should update your browser
24 | - The website has not enabled the searching Unsplash feature
25 |
26 | 
27 |
--------------------------------------------------------------------------------
/content/effects/crop.md:
--------------------------------------------------------------------------------
1 | # Crop
2 |
3 | The Crop effect lets users crop their images before uploading them. Users can also choose which aspect ratio they want to crop by -- free, square, or 16:9.
4 |
5 | ```ts
6 | import { Uppload, Crop } from "uppload";
7 |
8 | const profilePicture = new Uppload();
9 | profilePicture.use(new Crop());
10 | ```
11 |
12 | You can force the aspect ratio. For example, only allow squares (1:1):
13 |
14 | ```ts
15 | profilePicture.use(
16 | new Crop({
17 | aspectRatio: 1,
18 | })
19 | );
20 | ```
21 |
22 | You can also customize the aspect ratio options given to users. By default, the free aspect ratio has the value `NaN`:
23 |
24 | ```ts
25 | profilePicture.use(
26 | new Crop({
27 | aspectRatioOptions: {
28 | free: NaN,
29 | square: 1,
30 | "16:9": 16 / 9,
31 | },
32 | })
33 | );
34 | ```
35 |
36 | If you don't want users to edit the aspect ratio, you can use the `hideAspectRatioSettings` property. This is the default case if you specify an aspect ratio:
37 |
38 | ```ts
39 | profilePicture.use(
40 | new Crop({
41 | hideAspectRatioSettings: true
42 | );
43 | ```
44 |
45 | 
46 |
--------------------------------------------------------------------------------
/content/@/anand.md:
--------------------------------------------------------------------------------
1 | ---
2 | linkedin: AnandChowdhary
3 | twitter: AnandChowdhary
4 | github: AnandChowdhary
5 | website: https://anandchowdhary.com
6 | ---
7 |
8 | # Anand Chowdhary
9 |
10 | Anand Chowdhary built Uppload as a consultant to El Niño. He is a creative technologist and entrepreneur, and the co-founder and CEO of Oswald Labs, an award-winning accessibility technology company that builds products for people with disabilities.
11 |
12 |
13 |
14 | He is best known for his work on web accessibility and is also an active open-source contributor. As a developer advocate, he has spoken at 20+ events around the world about startups, technology, and design, and is also a consultant to several startups and studios like El Niño, the developers of Uppload. At El Niño, he was a consultant specializing in frontend engineering and accessibility.
15 |
16 | In April 2018, he made the first commit to Uppload ([56bd930](https://github.com/elninotech/uppload/commit/56bd9307a020c692ed1383f0f4731690c00d90c9)), and in September 2019, to Uppload v2 ([073a091](https://github.com/elninotech/uppload/commit/073a0914ef9026036a2e52ce9c6795404ddefcf5)). Since then, he has been actively managing issues and merging pull requests.
17 |
--------------------------------------------------------------------------------
/content/migrating-from-1x.md:
--------------------------------------------------------------------------------
1 | # Migrating from Uppload 1.x
2 |
3 | Uppload is rewritten from the ground up in TypeScript, and it's not backwards-compatible with Uppload 1.x.
4 |
5 | To make sure there are no collisions, even the import style is different. For Uppload 1.x, you would use the default import as the class:
6 |
7 | ```js
8 | import Uppload from "uppload";
9 | ```
10 |
11 | But Uppload 2.x requires you to import the Uppload class like this:
12 |
13 | ```ts
14 | import { Uppload } from "uppload";
15 | ```
16 |
17 | Uppload 1.x had all packages built-in, so you could initialize it like this:
18 |
19 | ```js
20 | const profilePicture = new Uppload({
21 | value: "https://randomuser.me/api/portraits/women/17.jpg",
22 | });
23 | ```
24 |
25 | In Uppload 2.x, however, you have to import the features you require.
26 |
27 | ```ts
28 | import { Uppload, Instagram, Pexels } from "uppload";
29 | const profilePicture = new Uppload({
30 | value: "https://randomuser.me/api/portraits/women/17.jpg",
31 | });
32 | profilePicture.use(new Instagram(), new Pexels());
33 | ```
34 |
35 | Finally, Uppload 1.x didn't support TypeScript, but Uppload 2.x is written in TypeScript so it works with both JavaScript (ES6) and TypeScript.
36 |
--------------------------------------------------------------------------------
/src/styles/ui.scss:
--------------------------------------------------------------------------------
1 | .uppload-modal {
2 | p {
3 | margin: 0;
4 | margin-bottom: 1rem;
5 | &:last-child {
6 | margin-bottom: 0;
7 | }
8 | }
9 |
10 | .uppload-error {
11 | position: absolute;
12 | top: 0;
13 | left: 0;
14 | right: 0;
15 | padding: 1rem;
16 | text-align: center;
17 | }
18 |
19 | form {
20 | text-align: center;
21 | margin: 2rem 0;
22 | input {
23 | width: 75%;
24 | border: 0.1rem solid;
25 | }
26 | }
27 |
28 | form input,
29 | form button,
30 | button.uppload-button,
31 | .effects-continue button {
32 | -webkit-appearance: none;
33 | appearance: none;
34 | font: inherit;
35 | padding: 0.75rem 1rem;
36 | border-radius: 0.2rem;
37 | font-size: 135%;
38 | display: block;
39 | margin: 1rem auto;
40 | transition: 0.2s;
41 | }
42 | form button,
43 | button.uppload-button,
44 | .effects-continue button {
45 | border: none;
46 | }
47 | .effects-continue button {
48 | margin: 0 1rem;
49 | }
50 | form button[type="submit"]::after,
51 | .uppload-button--cta::after,
52 | .effects-continue--upload::after {
53 | content: "→";
54 | margin-left: 0.5rem;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/content/help/services/import-from-web-service/instagram.md:
--------------------------------------------------------------------------------
1 | # Import image from Instagram
2 |
3 | ## How do I upload an image using Instagram?
4 |
5 | You can import an image using its Instagram URL and then perform manipulations (like cropping or filters). Instagram URLs start with "instagram.com" or "instagr.am".
6 |
7 | 1. Find the Instagram URL of your photo and copy it
8 | 2. Click on the "Instagram" button in the widget
9 | 3. Paste the Instagram URL in the textbox
10 | 4. Click on the "Import from Instagram" button
11 |
12 | ## How do I find the Instagram URL of an image?
13 |
14 | ### Web
15 |
16 | On the web, open the picture you want to select and copy the URL from your browser's address bar.
17 |
18 | ### Mobile
19 |
20 | Using the Instagram app, click on the "More" (three dot) icon on the top-right of an image and select "Copy Link".
21 |
22 | ## Why don't I see the Instagram icon?
23 |
24 | If you don't see the Instagram icon, it could be because:
25 |
26 | - Your browser does not support importing an image from Instagram; if this is the case, you should update your browser
27 | - The website has not enabled the Instagram import feature
28 |
29 | 
30 |
--------------------------------------------------------------------------------
/content/backends.md:
--------------------------------------------------------------------------------
1 | # Backends
2 |
3 | By default, Uppload works with any file uploading backend because it sends `FormData`. Uppload does **not** provide the server side implementation of handling the files, but the way files are uploaded is identical to simple file upload forms like this:
4 |
5 | ```html
6 |
9 | ```
10 |
11 | Here are some common backend templates to get your started:
12 |
13 | - [Ruby on Rails](http://guides.rubyonrails.org/form_helpers.html#uploading-files)
14 | - [PHP (Basic)](http://www.startutorial.com/articles/view/how-to-build-a-file-upload-form-using-dropzonejs-and-php)
15 | - [Laravel](http://maxoffsky.com/code-blog/howto-ajax-multiple-file-upload-in-laravel/)
16 | - [Symfony 2 and AWS S3](http://www.jesuisundev.fr/upload-drag-drop-via-dropzonejs-symfony2-on-cloud-amazon-s3/)
17 | - [ASP.NET](https://www.codeproject.com/Articles/874215/File-upload-in-ASP-NET-MVC-using-Dropzone-JS-and-H)
18 | - [ServiceStack](http://www.buildclassifieds.com/2016/01/08/uploading-images-servicestack-and-dropzone/)
19 | - [Golang](https://hackernoon.com/how-to-build-a-file-upload-form-using-dropzonejs-and-go-8fb9f258a991)
20 |
--------------------------------------------------------------------------------
/content/listening-to-events.md:
--------------------------------------------------------------------------------
1 | # Listening to events
2 |
3 | You can listen to events by using the `on` function:
4 |
5 | ```ts
6 | import { Uppload, en } from "uppload";
7 | const uploader = new Uppload({ lang: en });
8 |
9 | uploader.on("upload", (url: string) => {
10 | console.log(`The URL of the uploaded file is: ${url}`);
11 | });
12 | ```
13 |
14 | If you want to stop listening to the event, you can use the `off` function:
15 |
16 | ```ts
17 | const errorLogger = (error: string) => {
18 | console.log("The error message is", error);
19 | };
20 | uploader.on("error", errorLogger); // Start listening
21 | /* . . . */
22 | uploader.off("error", errorLogger); // Stop listening
23 | ```
24 |
25 | You can listen to the following event:
26 |
27 | | Event | Description |
28 | | --------------- | --------------------------------- |
29 | | `ready` | Plugin is ready and initialized |
30 | | `bind` | The value of a new URL is applied |
31 | | `open` | Plugin is opened |
32 | | `close` | Plugin is closed |
33 | | `before-upload` | File upload has started |
34 | | `upload` | File upload has completed |
35 | | `error` | File upload got an error |
36 |
--------------------------------------------------------------------------------
/src/effects/preview/index.ts:
--------------------------------------------------------------------------------
1 | import { UpploadEffect } from "../../effect";
2 | import { fitImageToContainer } from "../../helpers/elements";
3 | import { IHandlersParams, ITemplateParams } from "../../helpers/interfaces";
4 |
5 | export default class Preview extends UpploadEffect {
6 | name = "preview";
7 | icon = ``;
8 |
9 | template = ({ file }: ITemplateParams) => {
10 | const image = URL.createObjectURL(file.blob);
11 | return `
12 |
13 |
14 |
15 | `;
16 | };
17 |
18 | handlers = (params: IHandlersParams) => {
19 | const image = params.uppload.container.querySelector(
20 | ".uppload-preview-element img"
21 | ) as HTMLImageElement | null;
22 | if (image) fitImageToContainer(params, image);
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/tests/helpers/assets.test.ts:
--------------------------------------------------------------------------------
1 | import { colorSVG } from "../../src/helpers/assets";
2 | import { UpploadService } from "../../src/service";
3 | import { UpploadEffect } from "../../src/effect";
4 |
5 | const sampleService = new UpploadService();
6 | sampleService.icon = ``;
7 | sampleService.color = "#9d2e2c";
8 |
9 | test("colors an svg string from a service", () => {
10 | const SVG = sampleService.icon;
11 | expect(colorSVG(SVG, sampleService)).toContain("#9d2e2c");
12 | });
13 |
14 | test("remove original color from an svg string from a service", () => {
15 | const SVG = sampleService.icon;
16 | expect(colorSVG(SVG, sampleService)).not.toContain("#000");
17 | });
18 |
19 | const sampleEffect = new UpploadEffect();
20 | sampleEffect.icon = ``;
21 | sampleEffect.color = "#1abc9c";
22 |
23 | test("colors an svg string from a effect", () => {
24 | const SVG = sampleEffect.icon;
25 | expect(colorSVG(SVG, sampleEffect)).toContain("#1abc9c");
26 | });
27 |
28 | test("remove original color from an svg string from a effect", () => {
29 | const SVG = sampleEffect.icon;
30 | expect(colorSVG(SVG, sampleEffect)).not.toContain("#000");
31 | });
32 |
--------------------------------------------------------------------------------
/src/styles/mobile.scss:
--------------------------------------------------------------------------------
1 | @media (max-height: 500px) {
2 | .uppload-modal {
3 | height: 90%;
4 | }
5 | }
6 |
7 | @media (max-width: 850px) {
8 | .uppload-modal {
9 | transform: none;
10 | left: 0;
11 | right: 0;
12 | width: 100%;
13 | border-radius: 0;
14 | bottom: 0;
15 | height: auto;
16 | top: 10%;
17 | flex-direction: column;
18 | .uppload-service--default .uppload-services .uppload-service-name {
19 | width: 47.5%;
20 | }
21 | aside {
22 | height: auto;
23 | width: 100%;
24 | .uppload-services {
25 | display: flex;
26 | }
27 | nav .uppload-service-name label {
28 | white-space: nowrap;
29 | }
30 | }
31 | footer.effects-nav {
32 | flex-direction: column;
33 | padding: 1rem 0;
34 | .effects-tabs {
35 | width: 100% !important;
36 | margin: 1rem 0 !important;
37 | }
38 | }
39 | .effects-continue {
40 | width: 90%;
41 | }
42 | .effects-continue button {
43 | margin: 0 !important;
44 | width: 100%;
45 | box-sizing: border-box;
46 | }
47 | section .uppload-active-container footer button {
48 | display: block !important;
49 | margin: 0.5rem 0 0 0 !important;
50 | width: 100%;
51 | box-sizing: border-box;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/helpers/i18n.ts:
--------------------------------------------------------------------------------
1 | let i18n: any = {};
2 |
3 | export const flattenObject = (ob: any) => {
4 | const toReturn: any = {};
5 | for (const i in ob) {
6 | if (!ob.hasOwnProperty(i)) continue;
7 | if (typeof ob[i] == "object") {
8 | const flatObject = flattenObject(ob[i]);
9 | for (const x in flatObject) {
10 | if (!flatObject.hasOwnProperty(x)) continue;
11 | toReturn[i + "." + x] = flatObject[x];
12 | }
13 | } else {
14 | toReturn[i] = ob[i];
15 | }
16 | }
17 | return toReturn;
18 | };
19 |
20 | /**
21 | *
22 | * @param translations
23 | */
24 | export const setI18N = (translations: any) => {
25 | i18n = flattenObject(translations);
26 | };
27 |
28 | /**
29 | * Get a translation from i18n setting
30 | * @param key - Translation key
31 | * @param params - Single term or array of variables
32 | */
33 | export const translate = (key: string, params?: string | string[]) => {
34 | try {
35 | let term = i18n[key] as string;
36 | if (typeof params === "string") params = [params];
37 | if (params)
38 | params.forEach((param, index) => {
39 | term = term.replace(`$${index + 1}$`, param);
40 | });
41 | if (i18n.helper && typeof i18n.helper === "function")
42 | term = i18n.helper(term);
43 | return term;
44 | } catch (error) {
45 | return "";
46 | }
47 | };
48 |
--------------------------------------------------------------------------------
/content/themes.md:
--------------------------------------------------------------------------------
1 | # Themes
2 |
3 | Uppload can be themed based on your liking. You can choose from the `dark` or `light` theme, or write your own theme.
4 |
5 | You can import the CSS in your JavaScript using a bundler like Webpack:
6 |
7 | ```ts
8 | import { Uppload } from "uppload";
9 | import "uppload/dist/uppload.css"; // Basic styles
10 | import "uppload/dist/themes/light.css"; // Light theme
11 |
12 | const uppload = new Uppload();
13 | ```
14 |
15 | You can also use the Sass source code instead:
16 |
17 | ```css
18 | @import "uppload/src/styles/uppload.scss";
19 | @import "uppload/src/themes/light.scss";
20 | ```
21 |
22 | Or, use a CDN for styling:
23 |
24 | ```html
25 |
26 |
27 |
31 |
32 | ```
33 |
34 | ## Custom theme
35 |
36 | Instead of importing a theme, you can also design a custom theme. The best way is to look at the source code of [light.scss](https://github.com/elninotech/uppload/blob/master/src/themes/light.scss) and use the same CSS classes. You'll only have to change the color variables.
37 |
38 | Alternately, you can overwrite some rules instead of building your own theme. The [theme.scss](https://github.com/elninotech/uppload/blob/master/src/themes/theme.scss) file applies the color variables to the widget, so that's a good starting point for that.
39 |
--------------------------------------------------------------------------------
/tests/effect.test.ts:
--------------------------------------------------------------------------------
1 | import { UpploadEffect, Uppload } from "../src";
2 | import {
3 | Crop,
4 | Preview,
5 | Flip,
6 | Blur,
7 | Brightness,
8 | Contrast,
9 | Grayscale,
10 | HueRotate,
11 | Invert,
12 | Saturate,
13 | Sepia,
14 | } from "../src";
15 | import { effectTemplateParams } from "./mocks";
16 | import "jest-canvas-mock";
17 |
18 | const effect = new UpploadEffect();
19 | const effects = [
20 | new Crop(),
21 | new Preview(),
22 | new Flip(),
23 | new Blur(),
24 | new Brightness(),
25 | new Contrast(),
26 | new Grayscale(),
27 | new HueRotate(),
28 | new Invert(),
29 | new Saturate(),
30 | new Sepia(),
31 | ];
32 |
33 | describe("effect template", () => {
34 | it("is a function", () => expect(typeof effect.template).toBe("function"));
35 | it("gives a string", () =>
36 | expect(typeof effect.template(effectTemplateParams)).toBe("string"));
37 | });
38 |
39 | const uppload = new Uppload();
40 | uppload.use(effects);
41 |
42 | window.URL.createObjectURL = jest.fn();
43 | window.URL.createObjectURL = jest.fn(() => "fake-url");
44 |
45 | for (const currentEffect of effects) {
46 | describe(`${currentEffect.name} effect`, () => {
47 | it("template is a function", () =>
48 | expect(typeof currentEffect.template).toBe("function"));
49 | it("template gives a string", () =>
50 | expect(typeof currentEffect.template(effectTemplateParams)).toBe(
51 | "string"
52 | ));
53 | });
54 | }
55 |
--------------------------------------------------------------------------------
/content/effects/filter.md:
--------------------------------------------------------------------------------
1 | # Filter effects
2 |
3 | Filter effects use CSS filters on SVG and allow users to drag an input range to add a filter. Available filter effects are:
4 |
5 | | Service name | Class name |
6 | | ------------ | ------------ |
7 | | Blur | `Blur` |
8 | | Brightness | `Brightness` |
9 | | Contrast | `Contrast` |
10 | | Grayscale | `Grayscale` |
11 | | HueRotate | `HueRotate` |
12 | | Invert | `Invert` |
13 | | Saturate | `Saturate` |
14 | | Sepia | `Sepia` |
15 |
16 | In the following example, we're using the Blur effect is used, but all filter effects have the same usage:
17 |
18 | ```ts
19 | import { Uppload, Blur } from "uppload";
20 |
21 | const profilePicture = new Uppload();
22 | profilePicture.use(new Blur());
23 | ```
24 |
25 | ## Development
26 |
27 | All filter effects are inherited from the `UpploadFilterBaseClass` class, and you can create your own filter effects too:
28 |
29 | ```ts
30 | import { UpploadFilterBaseClass } from "uppload";
31 |
32 | class Brightness extends UpploadFilterBaseClass {
33 | name = "brightness";
34 | icon = "your-svg-icon-string";
35 | cssFilter = "brightness";
36 | unit = "%";
37 | value = 0;
38 | min = 0;
39 | max = 100;
40 | }
41 | ```
42 |
43 | In the above example, the CSS filter applied to the SVG will be `filter: brightness(10%)` if the value is `10`.
44 |
45 | 
46 |
--------------------------------------------------------------------------------
/content/help/services/camera.md:
--------------------------------------------------------------------------------
1 | # Camera
2 |
3 | ## How do I take a photo?
4 |
5 | - If your browser supports the camera feature, you will see the icon when you open the widget
6 | - Click on that icon and grant the required permission
7 | - Press the "Click photo" button to take a photo
8 |
9 | ## Why don't I see the camera icon?
10 |
11 | If you don't see the camera icon, it could be because:
12 |
13 | - Your browser does not support reading your camera's video; if this is the case, you should update your browser
14 | - The website has not enabled the camera feature
15 |
16 | ## Why can't I see my camera image?
17 |
18 | If you've clicked on the camera icon, but you don't see your picture, this could be because:
19 |
20 | - You have not granted the sufficient permission to access your camera
21 | - Your browser does not support reading your camera's video
22 | - Your camera does not work
23 |
24 | ### Grant permission
25 |
26 | Click on the information icon (an "i" or _lock_ icon on the top left of your web browser), and choose "Allow" under "Camera". This option may be in a different place depending on your browser.
27 |
28 | ### Update your browser
29 |
30 | If you're not using a modern web browser like Chrome, Firefox, Safari, Opera, or Edge, you should download the most recent version of any of those browser. If you're already using one of them, update them by going to your browser settings.
31 |
32 | 
33 |
--------------------------------------------------------------------------------
/tests/services/search/pexels.test.ts:
--------------------------------------------------------------------------------
1 | import Pexels, { PexelsResult } from "../../../src/services/search/pexels";
2 | import { minifyHTML } from "../../../src/helpers/utils";
3 |
4 | const service = new Pexels("API_KEY");
5 |
6 | const pexelsResult: PexelsResult = {
7 | url: "https://pexels.com/image",
8 | photographer: "Anand Chowdhary",
9 | src: {
10 | original: "https://pexels.com/original.jpg",
11 | large2x: "https://pexels.com/large.jpg",
12 | tiny: "https://pexels.com/tiny.jpg",
13 | },
14 | };
15 |
16 | test("popular endpoint", () =>
17 | expect(service.popularEndpoint).toBe(
18 | "https://api.pexels.com/v1/curated?per_page=9&page=1"
19 | ));
20 |
21 | test("search endpoint", () =>
22 | expect(service.searchEndpoint("API_KEY", "QUERY")).toBe(
23 | "https://api.pexels.com/v1/search?query=QUERY&per_page=12&page=1"
24 | ));
25 |
26 | test("gets search results", () =>
27 | expect(service.getSearchResults({ photos: [pexelsResult] })).toEqual([
28 | pexelsResult,
29 | ]));
30 |
31 | test("gets popular items", () =>
32 | expect(service.getPopularResults({ photos: [pexelsResult] })).toEqual([
33 | pexelsResult,
34 | ]));
35 |
36 | test("gets button HTML", () =>
37 | expect(minifyHTML(service.getButton(pexelsResult))).toBe(
38 | `
`
39 | ));
40 |
--------------------------------------------------------------------------------
/tests/services/search/giphy.test.ts:
--------------------------------------------------------------------------------
1 | import GIPHY, { GIPHYResult } from "../../../src/services/search/giphy";
2 | import { minifyHTML } from "../../../src/helpers/utils";
3 |
4 | const service = new GIPHY("API_KEY");
5 |
6 | const giphyResult: GIPHYResult = {
7 | id: "1",
8 | title: "An image",
9 | url: "https://giphy.com/image.gif",
10 | images: {
11 | downsized_large: { url: "https://giphy.com/large.gif" },
12 | preview_gif: { url: "https://giphy.com/preview.gif" },
13 | },
14 | user: {
15 | avatar_url: "https://giphy.com/avatar.jpg",
16 | display_name: "Anand Chowdhary",
17 | profile_url: "https://giphy.com/anand",
18 | },
19 | };
20 |
21 | test("popular endpoint", () =>
22 | expect(service.popularEndpoint).toBe(
23 | "https://api.giphy.com/v1/gifs/trending?api_key=API_KEY&limit=18&rating=G"
24 | ));
25 |
26 | test("search endpoint", () =>
27 | expect(service.searchEndpoint("API_KEY", "QUERY")).toBe(
28 | "https://api.giphy.com/v1/gifs/search?api_key=API_KEY&q=QUERY&limit=18&offset=0&rating=G&lang=en"
29 | ));
30 |
31 | test("gets search results", () =>
32 | expect(service.getSearchResults({ data: [giphyResult] })).toEqual([
33 | giphyResult,
34 | ]));
35 |
36 | test("gets popular items", () =>
37 | expect(service.getPopularResults({ data: [giphyResult] })).toEqual([
38 | giphyResult,
39 | ]));
40 |
41 | test("gets button HTML", () =>
42 | expect(minifyHTML(service.getButton(giphyResult))).toBe(
43 | ``
44 | ));
45 |
--------------------------------------------------------------------------------
/content/help/services/local.md:
--------------------------------------------------------------------------------
1 | # Select a local file
2 |
3 | ## How do I upload a file from my device?
4 |
5 | You can upload a file from your computer by dragging and dropping the file in the "Drop files here" area, or clicking the "Select a file" button.
6 |
7 | 1. Click on the "Select a file" button
8 | 2. Select the file from your device's file manager
9 | 3. Click on the "Upload" button
10 |
11 | ## How do I find a file?
12 |
13 | To learn how to select a file from the file manager on your device, follow the guidelines for your operating system below.
14 |
15 | - Finder on macOS: [Get to know the Finder on your Mac](https://support.apple.com/en-ca/HT201732) on apple.com
16 | - Files on Android: [Find & delete files on Android](https://support.google.com/android/answer/9110661?hl=en) on google.com
17 | - File Explorer on Windows 10: [Find your documents in Windows 10](https://support.microsoft.com/en-us/help/4026289/windows-10-find-your-documents) on microsoft.com
18 | - Files on iOS: [View files and folders in Files on iPhone](https://support.apple.com/guide/iphone/view-files-and-folders-iphc61044c11/ios) on apple.com
19 | - Files on iPadOS: [View files and folders in Files on iPad](https://support.apple.com/guide/ipad/view-files-and-folders-ipad49b77901/ipados) on apple.com
20 |
21 | ## Why don't I see the "Choose file" icon?
22 |
23 | If you don't see the "Choose file" icon, it could be because:
24 |
25 | - Your browser does not support selecting an image from a device; if this is the case, you should update your browser
26 | - The website has not enabled the "Choose file" feature
27 |
28 | 
29 |
--------------------------------------------------------------------------------
/tests/services/local.test.ts:
--------------------------------------------------------------------------------
1 | import { Local } from "../../src";
2 | import { handlersParams } from "../mocks";
3 |
4 | const service = new Local();
5 |
6 | describe("template", () => {
7 | it("is a function", () => expect(typeof service.template).toBe("function"));
8 | it("returns a string", () =>
9 | expect(typeof service.template(handlersParams)).toBe("string"));
10 | it("has a drop area", () =>
11 | expect(service.template(handlersParams)).toContain("drop-area"));
12 | it("has an input", () =>
13 | expect(service.template(handlersParams)).toContain(" new Blob(),
23 | },
24 | ],
25 | },
26 | dataTransfer: {
27 | items: [
28 | {
29 | kind: "file",
30 | type: "image/png",
31 | getAsFile: () => new Blob(),
32 | },
33 | ],
34 | },
35 | preventDefault: () => {},
36 | };
37 |
38 | test("drop handler", () => {
39 | let file = null;
40 | service.dropHandler(handlersParams, fakeEvent as any);
41 | expect(file).toBeDefined();
42 | });
43 |
44 | test("get file", () => {
45 | let file = null;
46 | service.getFile(handlersParams, fakeEvent as any);
47 | expect(file).toBeDefined();
48 | });
49 |
50 | test("drag handler", () =>
51 | expect(
52 | service.dragHandler(handlersParams, fakeEvent as any)
53 | ).toBeUndefined());
54 |
55 | test("file select", () =>
56 | expect(service.fileSelect(handlersParams, fakeEvent as any)).toBeUndefined());
57 |
58 | test("handlers", () =>
59 | expect(service.handlers(handlersParams)).toBeUndefined());
60 |
--------------------------------------------------------------------------------
/tests/services/search/unsplash.test.ts:
--------------------------------------------------------------------------------
1 | import Unsplash, {
2 | UnsplashResult,
3 | } from "../../../src/services/search/unsplash";
4 | import { minifyHTML } from "../../../src/helpers/utils";
5 |
6 | const service = new Unsplash("API_KEY");
7 |
8 | const unsplashResult: UnsplashResult = {
9 | id: "1",
10 | description: "Tree",
11 | alt_description: "An image of a tree",
12 | urls: {
13 | regular: "https://unsplash.com/regular.jpg",
14 | thumb: "https://unsplash.com/thumb.jpg",
15 | },
16 | user: {
17 | name: "Anand Chowdhary",
18 | profile_image: {
19 | small: "https://unsplash.com/user.jpg",
20 | },
21 | },
22 | };
23 |
24 | test("popular endpoint", () =>
25 | expect(service.popularEndpoint).toBe(
26 | "https://api.unsplash.com/photos?client_id=API_KEY"
27 | ));
28 |
29 | test("search endpoint", () =>
30 | expect(service.searchEndpoint("API_KEY", "QUERY")).toBe(
31 | "https://api.unsplash.com/search/photos?client_id=API_KEY&page=1&query=QUERY"
32 | ));
33 |
34 | test("gets search results", () =>
35 | expect(service.getSearchResults({ results: [unsplashResult] })).toEqual([
36 | unsplashResult,
37 | ]));
38 |
39 | test("gets popular items", () =>
40 | expect(service.getPopularResults([unsplashResult])).toEqual([
41 | unsplashResult,
42 | ]));
43 |
44 | test("gets button HTML", () =>
45 | expect(minifyHTML(service.getButton(unsplashResult))).toBe(
46 | `
Anand Chowdhary
`
47 | ));
48 |
--------------------------------------------------------------------------------
/content/compression.md:
--------------------------------------------------------------------------------
1 | # Image compression
2 |
3 | To save on bandwidth and storage costs, you may want to upload resized and compressed images by default. If this is the case, you can [configure](/configuration) Uppload to do so:
4 |
5 | ```ts
6 | import { Uppload } from "uppload";
7 |
8 | const uploader = new Uppload({
9 | maxSize: [800, 600],
10 | });
11 | ```
12 |
13 | In the above example, images will be resized to a maximum of 800px in width and 600px in height. You can also specify only one of the two constraints using `maxWidth` or `maxHeight`.
14 |
15 | If you want to apply compression, you can specify the `compression` factor (from 0 to 1), and Uppload will apply a lossy compression before uploading the image. This means that even if users upload a PNG, we will convert it to `image/jpeg` and upload it. Alternately, by specifying the `compressionToMime`, you can use WebP instead of JPEG.
16 |
17 | ```ts
18 | const uploader = new Uppload({
19 | compression: 0.8,
20 | compressionToMime: "image/webp",
21 | });
22 | ```
23 |
24 | If you only want specific file types to be compress, for example only JPEG and WEBP images, but not PNGs, you can use the `compressionFromMimes` property:
25 |
26 | ```ts
27 | const uploader = new Uppload({
28 | compression: 0.8,
29 | compressionFromMimes: ["image/jpeg", "image/webp"],
30 | compressionToMime: "image/webp",
31 | });
32 | ```
33 |
34 | If you want to perform your own image compression, perhaps by using an external library, you can specify a function instead:
35 |
36 | ```ts
37 | const uploader = new Uppload({
38 | compressor: (file: Blob) => {
39 | return new Promise((resolve, reject) => {
40 | // Perform your compression here
41 | resolve(file);
42 | });
43 | },
44 | });
45 | ```
46 |
--------------------------------------------------------------------------------
/src/services/search/pixabay.ts:
--------------------------------------------------------------------------------
1 | import { SearchBaseClass } from "../../helpers/search";
2 |
3 | export interface PixabayResult {
4 | id: number;
5 | largeImageURL: string;
6 | previewURL: string;
7 | user: string;
8 | userImageURL: string;
9 | pageURL: string;
10 | tags: string;
11 | }
12 |
13 | export default class Pixabay extends SearchBaseClass {
14 | constructor(apiKey: string) {
15 | super({
16 | apiKey,
17 | name: "pixabay",
18 | icon: ``,
19 | color: "#2ec66d",
20 | poweredByUrl: "https://pixabay.com",
21 | popularEndpoint: (apiKey: string) =>
22 | `https://pixabay.com/api/?key=${apiKey}&per_page=18&image_type=photo`,
23 | searchEndpoint: (apiKey: string, query: string) =>
24 | `https://pixabay.com/api/?key=${apiKey}&per_page=18&q=${encodeURIComponent(
25 | query
26 | )}&image_type=photo`,
27 | getButton: (image: PixabayResult) => `