1627309500
In this video we create a feature that will allow you to preview your image before uploading your file!
Patreon: https://www.patreon.com/johnkomarnicki
TimeStamps:
0:00 Introduction
1:19 Project Setup & HTML
6:43 Styling (CSS)
16:02 JavaScript
#HTML #CSS #JavaScript
#html #css #javascript
1678870808
CodeMirror component for React. Demo Preview: @uiwjs.github.io/react-codemirror
Features:
š Quickly and easily configure the API.
š± Versions after @uiw/react-codemirror@v4
use codemirror 6. #88.
āļø Support the features of React Hook(requires React 16.8+).
š Use Typescript to write, better code hints.
š The bundled version supports use directly in the browser #267.
š There are better sample previews.
šØ Support theme customization, provide theme editor.
Not dependent on uiw.
npm install @uiw/react-codemirror --save
import React from 'react';
import CodeMirror from '@uiw/react-codemirror';
import { javascript } from '@codemirror/lang-javascript';
function App() {
const onChange = React.useCallback((value, viewUpdate) => {
console.log('value:', value);
}, []);
return (
<CodeMirror
value="console.log('hello world!');"
height="200px"
extensions={[javascript({ jsx: true })]}
onChange={onChange}
/>
);
}
export default App;
import CodeMirror from '@uiw/react-codemirror';
import { StreamLanguage } from '@codemirror/language';
import { go } from '@codemirror/legacy-modes/mode/go';
const goLang = `package main
import "fmt"
func main() {
fmt.Println("Hello, äøē")
}`;
export default function App() {
return <CodeMirror value={goLang} height="200px" extensions={[StreamLanguage.define(go)]} />;
}
@codemirror/legacy-modes/mode/cpp
=> @codemirror/lang-cpp
@codemirror/legacy-modes/mode/css
=> @codemirror/lang-css
@codemirror/legacy-modes/mode/html
=> @codemirror/lang-html
@codemirror/legacy-modes/mode/java
=> @codemirror/lang-java
@codemirror/legacy-modes/mode/javascript
=> @codemirror/lang-javascript
@codemirror/legacy-modes/mode/json
=> @codemirror/lang-json
@codemirror/legacy-modes/mode/lezer
=> @codemirror/lang-lezer
@codemirror/legacy-modes/mode/markdown
=> @codemirror/lang-markdown
@codemirror/legacy-modes/mode/php
=> @codemirror/lang-php
@codemirror/legacy-modes/mode/python
=> @codemirror/lang-python
@codemirror/legacy-modes/mode/rust
=> @codemirror/lang-rust
@codemirror/legacy-modes/mode/sql
=> @codemirror/lang-sql
@codemirror/legacy-modes/mode/xml
=> @codemirror/lang-xml
@codemirror/legacy-modes/mode/wast
=> @codemirror/lang-wast
Markdown language code is automatically highlighted.
import CodeMirror from '@uiw/react-codemirror';
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
import { languages } from '@codemirror/language-data';
const code = `## Title
\`\`\`jsx
function Demo() {
return <div>demo</div>
}
\`\`\`
\`\`\`bash
# Not dependent on uiw.
npm install @codemirror/lang-markdown --save
npm install @codemirror/language-data --save
\`\`\`
[weisit ulr](https://uiwjs.github.io/react-codemirror/)
\`\`\`go
package main
import "fmt"
func main() {
fmt.Println("Hello, äøē")
}
\`\`\`
`;
export default function App() {
return <CodeMirror value={code} extensions={[markdown({ base: markdownLanguage, codeLanguages: languages })]} />;
}
import { useEffect, useMemo, useRef } from 'react';
import { useCodeMirror } from '@uiw/react-codemirror';
import { javascript } from '@codemirror/lang-javascript';
const code = "console.log('hello world!');\n\n\n";
// Define the extensions outside the component for the best performance.
// If you need dynamic extensions, use React.useMemo to minimize reference changes
// which cause costly re-renders.
const extensions = [javascript()];
export default function App() {
const editor = useRef();
const { setContainer } = useCodeMirror({
container: editor.current,
extensions,
value: code,
});
useEffect(() => {
if (editor.current) {
setContainer(editor.current);
}
}, [editor.current]);
return <div ref={editor} />;
}
We have created a theme editor
where you can define your own theme. We have also defined some themes ourselves, which can be installed and used directly. Below is a usage example:
import CodeMirror from '@uiw/react-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { okaidia } from '@uiw/codemirror-theme-okaidia';
const extensions = [javascript({ jsx: true })];
export default function App() {
return (
<CodeMirror
value="console.log('hello world!');"
height="200px"
theme={okaidia}
extensions={[javascript({ jsx: true })]}
/>
);
}
import CodeMirror from '@uiw/react-codemirror';
import { createTheme } from '@uiw/codemirror-themes';
import { javascript } from '@codemirror/lang-javascript';
import { tags as t } from '@lezer/highlight';
const myTheme = createTheme({
theme: 'light',
settings: {
background: '#ffffff',
foreground: '#75baff',
caret: '#5d00ff',
selection: '#036dd626',
selectionMatch: '#036dd626',
lineHighlight: '#8a91991a',
gutterBackground: '#fff',
gutterForeground: '#8a919966',
},
styles: [
{ tag: t.comment, color: '#787b8099' },
{ tag: t.variableName, color: '#0080ff' },
{ tag: [t.string, t.special(t.brace)], color: '#5c6166' },
{ tag: t.number, color: '#5c6166' },
{ tag: t.bool, color: '#5c6166' },
{ tag: t.null, color: '#5c6166' },
{ tag: t.keyword, color: '#5c6166' },
{ tag: t.operator, color: '#5c6166' },
{ tag: t.className, color: '#5c6166' },
{ tag: t.definition(t.typeName), color: '#5c6166' },
{ tag: t.typeName, color: '#5c6166' },
{ tag: t.angleBracket, color: '#5c6166' },
{ tag: t.tagName, color: '#5c6166' },
{ tag: t.attributeName, color: '#5c6166' },
],
});
const extensions = [javascript({ jsx: true })];
export default function App() {
const onChange = React.useCallback((value, viewUpdate) => {
console.log('value:', value);
}, []);
return (
<CodeMirror
value="console.log('hello world!');"
height="200px"
theme={myTheme}
extensions={extensions}
onChange={onChange}
/>
);
}
initialState
to restore state from JSON-serialized representationCodeMirror allows to serialize editor state to JSON representation with toJSON function for persistency or other needs. This JSON representation can be later used to recreate ReactCodeMirror component with the same internal state.
For example, this is how undo history can be saved in the local storage, so that it remains after the page reloads
import CodeMirror from '@uiw/react-codemirror';
import { historyField } from '@codemirror/commands';
// When custom fields should be serialized, you can pass them in as an object mapping property names to fields.
// See [toJSON](https://codemirror.net/docs/ref/#state.EditorState.toJSON) documentation for more details
const stateFields = { history: historyField };
export function EditorWithInitialState() {
const serializedState = localStorage.getItem('myEditorState');
const value = localStorage.getItem('myValue') || '';
return (
<CodeMirror
value={value}
initialState={
serializedState
? {
json: JSON.parse(serializedState || ''),
fields: stateFields,
}
: undefined
}
onChange={(value, viewUpdate) => {
localStorage.setItem('myValue', value);
const state = viewUpdate.state.toJSON(stateFields);
localStorage.setItem('myEditorState', JSON.stringify(state));
}}
/>
);
}
value?: string
value of the auto created model in the editor.width?: string
width of editor. Defaults to auto
.height?: string
height of editor. Defaults to auto
.theme?
: 'light'
/ 'dark'
/ Extension
Defaults to 'light'
.import React from 'react';
import { EditorState, EditorStateConfig, Extension } from '@codemirror/state';
import { EditorView, ViewUpdate } from '@codemirror/view';
export * from '@codemirror/view';
export * from '@codemirror/basic-setup';
export * from '@codemirror/state';
export interface UseCodeMirror extends ReactCodeMirrorProps {
container?: HTMLDivElement | null;
}
export declare function useCodeMirror(props: UseCodeMirror): {
state: EditorState | undefined;
setState: import('react').Dispatch<import('react').SetStateAction<EditorState | undefined>>;
view: EditorView | undefined;
setView: import('react').Dispatch<import('react').SetStateAction<EditorView | undefined>>;
container: HTMLDivElement | null | undefined;
setContainer: import('react').Dispatch<import('react').SetStateAction<HTMLDivElement | null | undefined>>;
};
export interface ReactCodeMirrorProps
extends Omit<EditorStateConfig, 'doc' | 'extensions'>,
Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'placeholder'> {
/** value of the auto created model in the editor. */
value?: string;
height?: string;
minHeight?: string;
maxHeight?: string;
width?: string;
minWidth?: string;
maxWidth?: string;
/** focus on the editor. */
autoFocus?: boolean;
/** Enables a placeholderāa piece of example content to show when the editor is empty. */
placeholder?: string | HTMLElement;
/**
* `light` / `dark` / `Extension` Defaults to `light`.
* @default light
*/
theme?: 'light' | 'dark' | Extension;
/**
* Whether to optional basicSetup by default
* @default true
*/
basicSetup?: boolean | BasicSetupOptions;
/**
* This disables editing of the editor content by the user.
* @default true
*/
editable?: boolean;
/**
* This disables editing of the editor content by the user.
* @default false
*/
readOnly?: boolean;
/**
* Whether to optional basicSetup by default
* @default true
*/
indentWithTab?: boolean;
/** Fired whenever a change occurs to the document. */
onChange?(value: string, viewUpdate: ViewUpdate): void;
/** Some data on the statistics editor. */
onStatistics?(data: Statistics): void;
/** The first time the editor executes the event. */
onCreateEditor?(view: EditorView, state: EditorState): void;
/** Fired whenever any state change occurs within the editor, including non-document changes like lint results. */
onUpdate?(viewUpdate: ViewUpdate): void;
/**
* Extension values can be [provided](https://codemirror.net/6/docs/ref/#state.EditorStateConfig.extensions) when creating a state to attach various kinds of configuration and behavior information.
* They can either be built-in extension-providing objects,
* such as [state fields](https://codemirror.net/6/docs/ref/#state.StateField) or [facet providers](https://codemirror.net/6/docs/ref/#state.Facet.of),
* or objects with an extension in its `extension` property. Extensions can be nested in arrays arbitrarily deepāthey will be flattened when processed.
*/
extensions?: Extension[];
/**
* If the view is going to be mounted in a shadow root or document other than the one held by the global variable document (the default), you should pass it here.
* Originally from the [config of EditorView](https://codemirror.net/6/docs/ref/#view.EditorView.constructor%5Econfig.root)
*/
root?: ShadowRoot | Document;
/**
* Create a state from its JSON representation serialized with [toJSON](https://codemirror.net/docs/ref/#state.EditorState.toJSON) function
*/
initialState?: {
json: any;
fields?: Record<'string', StateField<any>>;
};
}
export interface ReactCodeMirrorRef {
editor?: HTMLDivElement | null;
state?: EditorState;
view?: EditorView;
}
declare const ReactCodeMirror: React.ForwardRefExoticComponent<
ReactCodeMirrorProps & React.RefAttributes<ReactCodeMirrorRef>
>;
export default ReactCodeMirror;
export interface BasicSetupOptions {
lineNumbers?: boolean;
highlightActiveLineGutter?: boolean;
highlightSpecialChars?: boolean;
history?: boolean;
foldGutter?: boolean;
drawSelection?: boolean;
dropCursor?: boolean;
allowMultipleSelections?: boolean;
indentOnInput?: boolean;
syntaxHighlighting?: boolean;
bracketMatching?: boolean;
closeBrackets?: boolean;
autocompletion?: boolean;
rectangularSelection?: boolean;
crosshairCursor?: boolean;
highlightActiveLine?: boolean;
highlightSelectionMatches?: boolean;
closeBracketsKeymap?: boolean;
defaultKeymap?: boolean;
searchKeymap?: boolean;
historyKeymap?: boolean;
foldKeymap?: boolean;
completionKeymap?: boolean;
lintKeymap?: boolean;
}
import { EditorSelection, SelectionRange } from '@codemirror/state';
import { ViewUpdate } from '@codemirror/view';
export interface Statistics {
/** Get the number of lines in the editor. */
lineCount: number;
/** total length of the document */
length: number;
/** Get the proper [line-break](https://codemirror.net/docs/ref/#state.EditorState^lineSeparator) string for this state. */
lineBreak: string;
/** Returns true when the editor is [configured](https://codemirror.net/6/docs/ref/#state.EditorState^readOnly) to be read-only. */
readOnly: boolean;
/** The size (in columns) of a tab in the document, determined by the [`tabSize`](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) facet. */
tabSize: number;
/** Cursor Position */
selection: EditorSelection;
/** Make sure the selection only has one range. */
selectionAsSingle: SelectionRange;
/** Retrieves a list of all current selections. */
ranges: readonly SelectionRange[];
/** Get the currently selected code. */
selectionCode: string;
/**
* The length of the given array should be the same as the number of active selections.
* Replaces the content of the selections with the strings in the array.
*/
selections: string[];
/** Return true if any text is selected. */
selectedText: boolean;
}
export declare const getStatistics: (view: ViewUpdate) => Statistics;
All Packages
Name | NPM Version | Website |
---|---|---|
@uiw/react-codemirror | #preview | |
@uiw/codemirror-extensions-basic-setup | #preview | |
@uiw/codemirror-extensions-color | #preview | |
@uiw/codemirror-extensions-classname | #preview | |
@uiw/codemirror-extensions-events | #preview | |
@uiw/codemirror-extensions-hyper-link | #preview | |
@uiw/codemirror-extensions-langs | #preview | |
@uiw/codemirror-extensions-line-numbers-relative | #preview | |
@uiw/codemirror-extensions-mentions | #preview | |
@uiw/codemirror-extensions-zebra-stripes | #preview | |
@uiw/codemirror-themes | #preview |
Name | NPM Version | Website |
---|---|---|
@uiw/codemirror-themes-all | #preview | |
@uiw/codemirror-theme-abcdef | #preview | |
@uiw/codemirror-theme-androidstudio | #preview | |
@uiw/codemirror-theme-atomone | #preview | |
@uiw/codemirror-theme-aura | #preview | |
@uiw/codemirror-theme-bbedit | #preview | |
@uiw/codemirror-theme-bespin | #preview | |
@uiw/codemirror-theme-duotone | #preview | |
@uiw/codemirror-theme-dracula | #preview | |
@uiw/codemirror-theme-darcula | #preview | |
@uiw/codemirror-theme-eclipse | #preview | |
@uiw/codemirror-theme-github | #preview | |
@uiw/codemirror-theme-gruvbox-dark | #preview | |
@uiw/codemirror-theme-material | #preview | |
@uiw/codemirror-theme-noctis-lilac | #preview | |
@uiw/codemirror-theme-nord | #preview | |
@uiw/codemirror-theme-okaidia | #preview | |
@uiw/codemirror-theme-solarized | #preview | |
@uiw/codemirror-theme-sublime | #preview | |
@uiw/codemirror-theme-tokyo-night | #preview | |
@uiw/codemirror-theme-tokyo-night-storm | #preview | |
@uiw/codemirror-theme-tokyo-night-day | #preview | |
@uiw/codemirror-theme-vscode | #preview | |
@uiw/codemirror-theme-xcode | #preview |
Author: uiwjs
Source Code: https://github.com/uiwjs/react-codemirror
License: MIT license
1653123600
This repository is a fork of SimpleMDE, made by Sparksuite. Go to the dedicated section for more information.
A drop-in JavaScript text area replacement for writing beautiful and understandable Markdown. EasyMDE allows users who may be less experienced with Markdown to use familiar toolbar buttons and shortcuts.
In addition, the syntax is rendered while editing to clearly show the expected result. Headings are larger, emphasized words are italicized, links are underlined, etc.
EasyMDE also features both built-in auto saving and spell checking. The editor is entirely customizable, from theming to toolbar buttons and javascript hooks.
Via npm:
npm install easymde
Via the UNPKG CDN:
<link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css">
<script src="https://unpkg.com/easymde/dist/easymde.min.js"></script>
Or jsDelivr:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.css">
<script src="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.js"></script>
After installing and/or importing the module, you can load EasyMDE onto the first textarea
element on the web page:
<textarea></textarea>
<script>
const easyMDE = new EasyMDE();
</script>
Alternatively you can select a specific textarea
, via JavaScript:
<textarea id="my-text-area"></textarea>
<script>
const easyMDE = new EasyMDE({element: document.getElementById('my-text-area')});
</script>
Use easyMDE.value()
to get the content of the editor:
<script>
easyMDE.value();
</script>
Use easyMDE.value(val)
to set the content of the editor:
<script>
easyMDE.value('New input for **EasyMDE**');
</script>
true
, force downloads Font Awesome (used for icons). If set to false
, prevents downloading. Defaults to undefined
, which will intelligently check whether Font Awesome has already been included, then download accordingly.true
, focuses the editor automatically. Defaults to false
.true
, saves the text automatically. Defaults to false
.10000
(10 seconds).autosave.delay
or 10000
(10 seconds).locale: en-US, format: hour:minute
.{ delay: 300 }
, it will check every 300 ms if the editor is visible and if positive, call CodeMirror's refresh()
.**
or __
. Defaults to **
.```
or ~~~
. Defaults to ```
.*
or _
. Defaults to *
.*
, -
or +
. Defaults to *
.textarea
element to use. Defaults to the first textarea
element on the page.true
, force text changes made in EasyMDE to be immediately stored in original text area. Defaults to false
.false
, indent using spaces instead of tabs. Defaults to true
.false
by default, preview for images will appear only for images on separate lines.
as argument and returns a string that serves as the src
attribute of the <img>
tag in the preview. Enables dynamic previewing of images in the frontend without having to upload them to a server, allows copy-pasting of images to the editor with preview.["[", "](http://)"]
.true
, enables line numbers in the editor.false
, disable line wrapping. Defaults to true
."500px"
. Defaults to "300px"
.minHeight
option will be ignored. Should be a string containing a valid CSS value like "500px"
. Defaults to undefined
.true
when the editor is currently going into full screen mode, or false
.true
, will render headers without a space after the #
. Defaults to false
.false
, will not process GFM strikethrough syntax. Defaults to true
.true
, let underscores be a delimiter for separating words. Defaults to false
.false
, will replace CSS classes returned by the default Markdown mode. Otherwise the classes returned by the custom mode will be combined with the classes returned by the default mode. Defaults to true
."editor-preview"
.true
, a JS alert window appears asking for the link or image URL. Defaults to false
.URL of the image:
.URL for the link:
.true
, enables the image upload functionality, which can be triggered by drag and drop, copy-paste and through the browse-file window (opened when the user click on the upload-image icon). Defaults to false
.1024 * 1024 * 2
(2 MB).image/png, image/jpeg
.imageMaxSize
, imageAccept
, imageUploadEndpoint
and imageCSRFToken
ineffective.onSuccess
and onError
callback functions as parameters. onSuccess(imageUrl: string)
and onError(errorMessage: string)
{"data": {"filePath": "<filePath>"}}
where filePath is the path of the image (absolute if imagePathAbsolute
is set to true, relative if otherwise);{"error": "<errorCode>"}
, where errorCode can be noFileGiven
(HTTP 400 Bad Request), typeNotAllowed
(HTTP 415 Unsupported Media Type), fileTooLarge
(HTTP 413 Payload Too Large) or importError
(see errorMessages below). If errorCode is not one of the errorMessages, it is alerted unchanged to the user. This allows for server-side error messages. No default value.true
, will treat imageUrl
from imageUploadFunction
and filePath returned from imageUploadEndpoint
as an absolute rather than relative path, i.e. not prepend window.location.origin
to it.imageCSRFToken
has value, defaults to csrfmiddlewaretoken
.true
, passing CSRF token via header. Defaults to false
, which pass CSRF through request body.#image_name#
, #image_size#
and #image_max_size#
will replaced by their respective values, that can be used for customization or internationalization:uploadImage
is set to true
. Defaults to Attach files by drag and dropping or pasting from clipboard.
.Drop image to upload it.
.Uploading images #images_names#
.Uploading #file_name#: #progress#%
.Uploaded #image_name#
.B, KB, MB
(example: 218 KB
). You can use B,KB,MB
instead if you prefer without whitespaces (218KB
).errorCallback
option, where #image_name#
, #image_size#
and #image_max_size#
will replaced by their respective values, that can be used for customization or internationalization:You must select a file.
.imageAccept
list, or the server returned this error code. Defaults to This image type is not allowed.
.imageMaxSize
, or if the server returned this error code. Defaults to Image #image_name# is too big (#image_size#).\nMaximum file size is #image_max_size#.
.Something went wrong when uploading the image #image_name#.
.(errorMessage) => alert(errorMessage)
.true
, will highlight using highlight.js. Defaults to false
. To use this feature you must include highlight.js on your page or pass in using the hljs
option. For example, include the script and the CSS files like:<script src="https://cdn.jsdelivr.net/highlight.js/latest/highlight.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/highlight.js/latest/styles/github.min.css">
window.hljs
), you can provide an instance here. Defaults to undefined
.renderingConfig
options will take precedence.false
, disable parsing GitHub Flavored Markdown (GFM) single line breaks. Defaults to true
.false
, disable the spell checker. Defaults to true
. Optionally pass a CodeMirrorSpellChecker-compliant function.textarea
or contenteditable
. Defaults to textarea
for desktop and contenteditable
for mobile. contenteditable
option is necessary to enable nativeSpellcheck.false
, disable native spell checker. Defaults to true
.false
, allows side-by-side editing without going into fullscreen. Defaults to true
.false
, hide the status bar. Defaults to the array of built-in status bar items.false
, remove the CodeMirror-selectedtext
class from selected lines. Defaults to true
.false
, disable syncing scroll in side by side mode. Defaults to true
.2
.easymde
.false
, hide the toolbar. Defaults to the array of icons.false
, disable toolbar button tips. Defaults to true
.rtl
or ltr
. Changes text direction to support right-to-left languages. Defaults to ltr
.Most options demonstrate the non-default behavior:
const editor = new EasyMDE({
autofocus: true,
autosave: {
enabled: true,
uniqueId: "MyUniqueID",
delay: 1000,
submit_delay: 5000,
timeFormat: {
locale: 'en-US',
format: {
year: 'numeric',
month: 'long',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
},
},
text: "Autosaved: "
},
blockStyles: {
bold: "__",
italic: "_",
},
unorderedListStyle: "-",
element: document.getElementById("MyID"),
forceSync: true,
hideIcons: ["guide", "heading"],
indentWithTabs: false,
initialValue: "Hello world!",
insertTexts: {
horizontalRule: ["", "\n\n-----\n\n"],
image: [""],
link: ["[", "](https://)"],
table: ["", "\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Text | Text | Text |\n\n"],
},
lineWrapping: false,
minHeight: "500px",
parsingConfig: {
allowAtxHeaderWithoutSpace: true,
strikethrough: false,
underscoresBreakWords: true,
},
placeholder: "Type here...",
previewClass: "my-custom-styling",
previewClass: ["my-custom-styling", "more-custom-styling"],
previewRender: (plainText) => customMarkdownParser(plainText), // Returns HTML from a custom parser
previewRender: (plainText, preview) => { // Async method
setTimeout(() => {
preview.innerHTML = customMarkdownParser(plainText);
}, 250);
return "Loading...";
},
promptURLs: true,
promptTexts: {
image: "Custom prompt for URL:",
link: "Custom prompt for URL:",
},
renderingConfig: {
singleLineBreaks: false,
codeSyntaxHighlighting: true,
sanitizerFunction: (renderedHTML) => {
// Using DOMPurify and only allowing <b> tags
return DOMPurify.sanitize(renderedHTML, {ALLOWED_TAGS: ['b']})
},
},
shortcuts: {
drawTable: "Cmd-Alt-T"
},
showIcons: ["code", "table"],
spellChecker: false,
status: false,
status: ["autosave", "lines", "words", "cursor"], // Optional usage
status: ["autosave", "lines", "words", "cursor", {
className: "keystrokes",
defaultValue: (el) => {
el.setAttribute('data-keystrokes', 0);
},
onUpdate: (el) => {
const keystrokes = Number(el.getAttribute('data-keystrokes')) + 1;
el.innerHTML = `${keystrokes} Keystrokes`;
el.setAttribute('data-keystrokes', keystrokes);
},
}], // Another optional usage, with a custom status bar item that counts keystrokes
styleSelectedText: false,
sideBySideFullscreen: false,
syncSideBySidePreviewScroll: false,
tabSize: 4,
toolbar: false,
toolbarTips: false,
});
Below are the built-in toolbar icons (only some of which are enabled by default), which can be reorganized however you like. "Name" is the name of the icon, referenced in the JavaScript. "Action" is either a function or a URL to open. "Class" is the class given to the icon. "Tooltip" is the small tooltip that appears via the title=""
attribute. Note that shortcut hints are added automatically and reflect the specified action if it has a key bind assigned to it (i.e. with the value of action
set to bold
and that of tooltip
set to Bold
, the final text the user will see would be "Bold (Ctrl-B)").
Additionally, you can add a separator between any icons by adding "|"
to the toolbar array.
Name | Action | Tooltip Class |
---|---|---|
bold | toggleBold | Bold fa fa-bold |
italic | toggleItalic | Italic fa fa-italic |
strikethrough | toggleStrikethrough | Strikethrough fa fa-strikethrough |
heading | toggleHeadingSmaller | Heading fa fa-header |
heading-smaller | toggleHeadingSmaller | Smaller Heading fa fa-header |
heading-bigger | toggleHeadingBigger | Bigger Heading fa fa-lg fa-header |
heading-1 | toggleHeading1 | Big Heading fa fa-header header-1 |
heading-2 | toggleHeading2 | Medium Heading fa fa-header header-2 |
heading-3 | toggleHeading3 | Small Heading fa fa-header header-3 |
code | toggleCodeBlock | Code fa fa-code |
quote | toggleBlockquote | Quote fa fa-quote-left |
unordered-list | toggleUnorderedList | Generic List fa fa-list-ul |
ordered-list | toggleOrderedList | Numbered List fa fa-list-ol |
clean-block | cleanBlock | Clean block fa fa-eraser |
link | drawLink | Create Link fa fa-link |
image | drawImage | Insert Image fa fa-picture-o |
table | drawTable | Insert Table fa fa-table |
horizontal-rule | drawHorizontalRule | Insert Horizontal Line fa fa-minus |
preview | togglePreview | Toggle Preview fa fa-eye no-disable |
side-by-side | toggleSideBySide | Toggle Side by Side fa fa-columns no-disable no-mobile |
fullscreen | toggleFullScreen | Toggle Fullscreen fa fa-arrows-alt no-disable no-mobile |
guide | This link | Markdown Guide fa fa-question-circle |
undo | undo | Undo fa fa-undo |
redo | redo | Redo fa fa-redo |
Customize the toolbar using the toolbar
option.
Only the order of existing buttons:
const easyMDE = new EasyMDE({
toolbar: ["bold", "italic", "heading", "|", "quote"]
});
All information and/or add your own icons
const easyMDE = new EasyMDE({
toolbar: [
{
name: "bold",
action: EasyMDE.toggleBold,
className: "fa fa-bold",
title: "Bold",
},
"italics", // shortcut to pre-made button
{
name: "custom",
action: (editor) => {
// Add your own code
},
className: "fa fa-star",
title: "Custom Button",
attributes: { // for custom attributes
id: "custom-id",
"data-value": "custom value" // HTML5 data-* attributes need to be enclosed in quotation marks ("") because of the dash (-) in its name.
}
},
"|" // Separator
// [, ...]
]
});
Put some buttons on dropdown menu
const easyMDE = new EasyMDE({
toolbar: [{
name: "heading",
action: EasyMDE.toggleHeadingSmaller,
className: "fa fa-header",
title: "Headers",
},
"|",
{
name: "others",
className: "fa fa-blind",
title: "others buttons",
children: [
{
name: "image",
action: EasyMDE.drawImage,
className: "fa fa-picture-o",
title: "Image",
},
{
name: "quote",
action: EasyMDE.toggleBlockquote,
className: "fa fa-percent",
title: "Quote",
},
{
name: "link",
action: EasyMDE.drawLink,
className: "fa fa-link",
title: "Link",
}
]
},
// [, ...]
]
});
EasyMDE comes with an array of predefined keyboard shortcuts, but they can be altered with a configuration option. The list of default ones is as follows:
Shortcut (Windows / Linux) | Shortcut (macOS) | Action |
---|---|---|
Ctrl-' | Cmd-' | "toggleBlockquote" |
Ctrl-B | Cmd-B | "toggleBold" |
Ctrl-E | Cmd-E | "cleanBlock" |
Ctrl-H | Cmd-H | "toggleHeadingSmaller" |
Ctrl-I | Cmd-I | "toggleItalic" |
Ctrl-K | Cmd-K | "drawLink" |
Ctrl-L | Cmd-L | "toggleUnorderedList" |
Ctrl-P | Cmd-P | "togglePreview" |
Ctrl-Alt-C | Cmd-Alt-C | "toggleCodeBlock" |
Ctrl-Alt-I | Cmd-Alt-I | "drawImage" |
Ctrl-Alt-L | Cmd-Alt-L | "toggleOrderedList" |
Shift-Ctrl-H | Shift-Cmd-H | "toggleHeadingBigger" |
F9 | F9 | "toggleSideBySide" |
F11 | F11 | "toggleFullScreen" |
Here is how you can change a few, while leaving others untouched:
const editor = new EasyMDE({
shortcuts: {
"toggleOrderedList": "Ctrl-Alt-K", // alter the shortcut for toggleOrderedList
"toggleCodeBlock": null, // unbind Ctrl-Alt-C
"drawTable": "Cmd-Alt-T", // bind Cmd-Alt-T to drawTable action, which doesn't come with a default shortcut
}
});
Shortcuts are automatically converted between platforms. If you define a shortcut as "Cmd-B", on PC that shortcut will be changed to "Ctrl-B". Conversely, a shortcut defined as "Ctrl-B" will become "Cmd-B" for Mac users.
The list of actions that can be bound is the same as the list of built-in actions available for toolbar buttons.
You can catch the following list of events: https://codemirror.net/doc/manual.html#events
const easyMDE = new EasyMDE();
easyMDE.codemirror.on("change", () => {
console.log(easyMDE.value());
});
You can revert to the initial text area by calling the toTextArea
method. Note that this clears up the autosave (if enabled) associated with it. The text area will retain any text from the destroyed EasyMDE instance.
const easyMDE = new EasyMDE();
// ...
easyMDE.toTextArea();
easyMDE = null;
If you need to remove registered event listeners (when the editor is not needed anymore), call easyMDE.cleanup()
.
The following self-explanatory methods may be of use while developing with EasyMDE.
const easyMDE = new EasyMDE();
easyMDE.isPreviewActive(); // returns boolean
easyMDE.isSideBySideActive(); // returns boolean
easyMDE.isFullscreenActive(); // returns boolean
easyMDE.clearAutosavedValue(); // no returned value
EasyMDE is a continuation of SimpleMDE.
SimpleMDE began as an improvement of lepture's Editor project, but has now taken on an identity of its own. It is bundled with CodeMirror and depends on Font Awesome.
CodeMirror is the backbone of the project and parses much of the Markdown syntax as it's being written. This allows us to add styles to the Markdown that's being written. Additionally, a toolbar and status bar have been added to the top and bottom, respectively. Previews are rendered by Marked using GitHub Flavored Markdown (GFM).
I originally made this fork to implement FontAwesome 5 compatibility into SimpleMDE. When that was done I submitted a pull request, which has not been accepted yet. This, and the project being inactive since May 2017, triggered me to make more changes and try to put new life into the project.
Changes include:
https://
by defaultMy intention is to continue development on this project, improving it and keeping it alive.
You may want to edit this library to adapt its behavior to your needs. This can be done in some quick steps:
gulp
command, which will generate files: dist/easymde.min.css
and dist/easymde.min.js
;Want to contribute to EasyMDE? Thank you! We have a contribution guide just for you!
Author: Ionaru
Source Code: https://github.com/Ionaru/easy-markdown-editor
License: MIT license
1675716120
In this article you will know how to create Drag and Drop File Upload using HTML CSS and JavaScript. Earlier I have shared many more types of Drag and Drop elements and File Upload and Preview section. This is basically a drag and drop multiple file upload that I made with javascript.
Now itās time to create JavaScript Drag and Drop File Upload. Here I have created the basic structure by html. I designed it with css and activated this Drag & Drop or Browse ā File upload with javascript.
Drag and drop file upload in JavaScript refers to the ability to select one or more files using the drag and drop gesture, and then upload those files to a server.
This feature is typically implemented using JavaScript event listeners, such as ādragoverā and ādrop,ā that detect when a file is being dragged over a specific element on the page and when the file is dropped onto that element. Within this project () you can upload the image by selecting it and you can also upload it by drag and drop. Also here you can upload multiple images.
As you can see above this is a simple javascript drag and drop file upload project. Here I have created a small area or box. Inside that box is a button. You can select the image by clicking on that button or you can drag and drop the image into the box. Then the uploaded image can be seen in the preview box below.
Now if you want to build(How To Make A Drag-and-Drop File Uploader With javascript) this project then follow the step by step tutorial below. Here I have given you the necessary explanation and source code. A bit more JavaScript is used here. But there is no reason to worry. I have explained each code step by step and tried to explain you completely why I used that line of code.
Besides, I have given a preview after each step. Which will help you to know what kind of result will be seen after using that code.
I have created the basic structure of this Drag and drop multiple file upload using the following html and css. All the information of that box can be seen. I used white for the background color of the box and some shadows around it.
<div class="container">
</div>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: "Poppins", sans-serif;
}
body {
background-color: #a4b7e3;
}
.container {
background-color: #ffffff;
width: 60%;
min-width: 27.5em;
padding: 3.12em 1.87em;
position: absolute;
transform: translate(-50%, -50%);
left: 50%;
top: 50%;
box-shadow: 0 1.25em 3.43em rgba(0, 0, 0, 0.08);
border-radius: 0.5em;
}
Now an input button is created to upload the file. I have created this button using HTMLās input function. For uploading image only I used accept=āimage/*ā which basically will help to upload image file only.
<input type="file" id="upload-button" multiple accept="image/*" />
<label for="upload-button">
<i class="fa-solid fa-upload"></i> Choose Or Drop Photos
</label>
input[type="file"] {
display: none;
}
label {
display: block;
position: relative;
background-color: #025bee;
color: #ffffff;
font-size: 1.1em;
text-align: center;
width: 16em;
padding: 1em 0;
border-radius: 0.3em;
margin: 0 auto 1em auto;
cursor: pointer;
}
Now an area is created, in which the images can be viewed i.e. image preview area is created. How images are displayed is determined by css.
<div id="error"></div>
<div id="image-display"></div>
#image-display {
position: relative;
width: 90%;
margin: 0 auto;
display: flex;
justify-content: space-evenly;
gap: 1.25em;
flex-wrap: wrap;
}
#image-display figure {
width: 45%;
}
#image-display img {
width: 100%;
}
#image-display figcaption {
font-size: 0.8em;
text-align: center;
color: #5a5861;
}
.active {
border: 0.2em dashed #025bee;
}
#error {
text-align: center;
color: #ff3030;
}
Now itās time to implement this drag and drop multiple file uploader using javascript. Here I have given the necessary explanation, hope you will not have any difficulty in understanding.
These lines of code are assigning variables to different elements on a webpage by their ID to active Drag And Drop File Uploader in javascript. The variables are:
uploadButton
is assigned to the element with the ID āupload-buttonāchosenImage
is assigned to the element with the ID āchosen-imageāfileName
is assigned to the element with the ID āfile-nameācontainer
is assigned to the first element with the class ācontainerāerror
is assigned to the element with the ID āerrorāimageDisplay
is assigned to the element with the ID āimage-displayāIt appears that these variables will be used to manipulate these elements and their properties in later JavaScript code.
let uploadButton = document.getElementById("upload-button");
let chosenImage = document.getElementById("chosen-image");
let fileName = document.getElementById("file-name");
let container = document.querySelector(".container");
let error = document.getElementById("error");
let imageDisplay = document.getElementById("image-display");
This code defines a function fileHandler()
that takes in three parameters: file
, name
, and type
. The function checks if the file is of type image by checking the first part of the type string before ā/ā if it is not of type image it sets the innerText of the error element to āPlease upload an image fileā and returns false.
If the file is an image, it sets the innerText of the error element to an empty string and creates a new FileReader object, which reads the contents of the file as a Data URL.
When the file has been read, the onloadend
event is triggered, at this point it creates an imageContainer
element and an img
element, sets the source of the img
element to the result of the file reader, appends the img
element to the imageContainer
element, and then appends a figcaption
element containing the file name to the imageContainer
element. Finally, it appends the imageContainer
element to the imageDisplay
element.
const fileHandler = (file, name, type) => {
if (type.split("/")[0] !== "image") {
//File Type Error
error.innerText = "Please upload an image file";
return false;
}
error.innerText = "";
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
//image and file name
let imageContainer = document.createElement("figure");
let img = document.createElement("img");
img.src = reader.result;
imageContainer.appendChild(img);
imageContainer.innerHTML += `<figcaption>${name}</figcaption>`;
imageDisplay.appendChild(imageContainer);
};
};
Now Drag and Drop File Upload project upload button should be activated. This code attaches an event listener to the uploadButton
element that listens for a āchangeā event. When the event is fired, the function inside the event listener is executed. The function clears the content of the imageDisplay
element by setting its innerHTML to an empty string.
Then it creates an array of the files from the uploadButton
element, and for each file in that array, it calls the fileHandler()
function and passes in the file
, the file.name
, and the file.type
as the arguments.
This code is used to handle the event of a user selecting and uploading one or more image files, displaying each image file and its name on the page by calling the fileHandler
function for each file.
//Upload Button
uploadButton.addEventListener("change", () => {
imageDisplay.innerHTML = "";
Array.from(uploadButton.files).forEach((file) => {
fileHandler(file, file.name, file.type);
});
});
Now you need to define what happens when you drag the file into the Simple javascript Drag and Drop File Upload box. This code attaches an event listener to the container
element that listens for a ādragenterā event. When the event is fired, the function inside the event listener is executed. The function calls preventDefault()
and stopPropagation()
on the event object (e
) to prevent the default browser behavior and stop the event from propagating.
Then it adds a class āactiveā to the container
elementās classList. This code is probably used to handle the event of a user dragging an item over the container
element, indicating that the container is a valid drop target.
The class āactiveā is added to the container which can be used to style it and give visual feedback that the container is active and ready to accept the dragged item.
container.addEventListener(
"dragenter",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.add("active");
},
false
);
Now you need to determine what happens if you drag leave within the HTML drag drop upload area. This code attaches an event listener to the container
element that listens for a ādragleaveā event. When the event is fired, the function inside the event listener is executed. The function calls preventDefault()
and stopPropagation()
on the event object (e
) to prevent the default browser behavior and stop the event from propagating.
Then it removes the class āactiveā from the container
elementās classList. This code is probably used to handle the event of a user dragging an item out of the container
element, indicating that the container is no longer a valid drop target.
The class āactiveā is removed from the container, it can be used to style it and give visual feedback that the container is no longer active and no longer ready to accept the dragged item.
container.addEventListener(
"dragleave",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.remove("active");
},
false
);
Upload files with Drag and Drop to determine what happens when dragover. This code attaches an event listener to the container
element that listens for a ādragoverā event. When the event is fired, the function inside the event listener is executed. The function calls preventDefault()
and stopPropagation()
on the event object (e
) to prevent the default browser behavior and stop the event from propagating.
container.addEventListener(
"dragover",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.add("active");
},
false
);
This code attaches an event listener to the container
element that listens for a ādropā event. When the event is fired, the function inside the event listener is executed. The function calls preventDefault()
and stopPropagation()
on the event object (e
) to prevent the default browser behavior and stop the event from propagating.
Then it removes the class āactiveā from the container
elementās classList. It then creates a variable draggedData
which holds the dataTransfer object of the event, which contains the data that was dropped onto the element. It then creates a variable files
which holds the file object from the draggedData
object. It clears the content of the imageDisplay
element by setting its innerHTML to an empty string.
Then it creates an array of the files and for each file in that array, it calls the fileHandler()
function and passes in the file
, the file.name
, and the file.type
as the arguments. This code is used to handle the event of a user dropping one or more files on to the container
element, which then calls the fileHandler
function to display the image and its name on the page.
container.addEventListener(
"drop",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.remove("active");
let draggedData = e.dataTransfer;
let files = draggedData.files;
imageDisplay.innerHTML = "";
Array.from(files).forEach((file) => {
fileHandler(file, file.name, file.type);
});
},
false
);
Now it is determined that every time the page is loaded all the information contained in the Drag and Drop File Upload with HTML CSS JavaScript will be removed. This code assigns an anonymous function to the onload
property of the window
object, which is fired when the page has finished loading. The function sets the innerText
of the error
element to an empty string, effectively clearing any pre-existing text.
window.onload = () => {
error.innerText = "";
};
Hope you got to know from above tutorial how I created this project(Drag and Drop File Upload Using HTML,CSS & JavaScript). I have already shared many more JavaScript tutorials with you.
Be sure to comment how you liked this Drag and Drop File Upload JavaScript tutorial.
Original article source at: https://foolishdeveloper.com/
1667425440
Perl script converts PDF files to Gerber format
Pdf2Gerb generates Gerber 274X photoplotting and Excellon drill files from PDFs of a PCB. Up to three PDFs are used: the top copper layer, the bottom copper layer (for 2-sided PCBs), and an optional silk screen layer. The PDFs can be created directly from any PDF drawing software, or a PDF print driver can be used to capture the Print output if the drawing software does not directly support output to PDF.
The general workflow is as follows:
Please note that Pdf2Gerb does NOT perform DRC (Design Rule Checks), as these will vary according to individual PCB manufacturer conventions and capabilities. Also note that Pdf2Gerb is not perfect, so the output files must always be checked before submitting them. As of version 1.6, Pdf2Gerb supports most PCB elements, such as round and square pads, round holes, traces, SMD pads, ground planes, no-fill areas, and panelization. However, because it interprets the graphical output of a Print function, there are limitations in what it can recognize (or there may be bugs).
See docs/Pdf2Gerb.pdf for install/setup, config, usage, and other info.
#Pdf2Gerb config settings:
#Put this file in same folder/directory as pdf2gerb.pl itself (global settings),
#or copy to another folder/directory with PDFs if you want PCB-specific settings.
#There is only one user of this file, so we don't need a custom package or namespace.
#NOTE: all constants defined in here will be added to main namespace.
#package pdf2gerb_cfg;
use strict; #trap undef vars (easier debug)
use warnings; #other useful info (easier debug)
##############################################################################################
#configurable settings:
#change values here instead of in main pfg2gerb.pl file
use constant WANT_COLORS => ($^O !~ m/Win/); #ANSI colors no worky on Windows? this must be set < first DebugPrint() call
#just a little warning; set realistic expectations:
#DebugPrint("${\(CYAN)}Pdf2Gerb.pl ${\(VERSION)}, $^O O/S\n${\(YELLOW)}${\(BOLD)}${\(ITALIC)}This is EXPERIMENTAL software. \nGerber files MAY CONTAIN ERRORS. Please CHECK them before fabrication!${\(RESET)}", 0); #if WANT_DEBUG
use constant METRIC => FALSE; #set to TRUE for metric units (only affect final numbers in output files, not internal arithmetic)
use constant APERTURE_LIMIT => 0; #34; #max #apertures to use; generate warnings if too many apertures are used (0 to not check)
use constant DRILL_FMT => '2.4'; #'2.3'; #'2.4' is the default for PCB fab; change to '2.3' for CNC
use constant WANT_DEBUG => 0; #10; #level of debug wanted; higher == more, lower == less, 0 == none
use constant GERBER_DEBUG => 0; #level of debug to include in Gerber file; DON'T USE FOR FABRICATION
use constant WANT_STREAMS => FALSE; #TRUE; #save decompressed streams to files (for debug)
use constant WANT_ALLINPUT => FALSE; #TRUE; #save entire input stream (for debug ONLY)
#DebugPrint(sprintf("${\(CYAN)}DEBUG: stdout %d, gerber %d, want streams? %d, all input? %d, O/S: $^O, Perl: $]${\(RESET)}\n", WANT_DEBUG, GERBER_DEBUG, WANT_STREAMS, WANT_ALLINPUT), 1);
#DebugPrint(sprintf("max int = %d, min int = %d\n", MAXINT, MININT), 1);
#define standard trace and pad sizes to reduce scaling or PDF rendering errors:
#This avoids weird aperture settings and replaces them with more standardized values.
#(I'm not sure how photoplotters handle strange sizes).
#Fewer choices here gives more accurate mapping in the final Gerber files.
#units are in inches
use constant TOOL_SIZES => #add more as desired
(
#round or square pads (> 0) and drills (< 0):
.010, -.001, #tiny pads for SMD; dummy drill size (too small for practical use, but needed so StandardTool will use this entry)
.031, -.014, #used for vias
.041, -.020, #smallest non-filled plated hole
.051, -.025,
.056, -.029, #useful for IC pins
.070, -.033,
.075, -.040, #heavier leads
# .090, -.043, #NOTE: 600 dpi is not high enough resolution to reliably distinguish between .043" and .046", so choose 1 of the 2 here
.100, -.046,
.115, -.052,
.130, -.061,
.140, -.067,
.150, -.079,
.175, -.088,
.190, -.093,
.200, -.100,
.220, -.110,
.160, -.125, #useful for mounting holes
#some additional pad sizes without holes (repeat a previous hole size if you just want the pad size):
.090, -.040, #want a .090 pad option, but use dummy hole size
.065, -.040, #.065 x .065 rect pad
.035, -.040, #.035 x .065 rect pad
#traces:
.001, #too thin for real traces; use only for board outlines
.006, #minimum real trace width; mainly used for text
.008, #mainly used for mid-sized text, not traces
.010, #minimum recommended trace width for low-current signals
.012,
.015, #moderate low-voltage current
.020, #heavier trace for power, ground (even if a lighter one is adequate)
.025,
.030, #heavy-current traces; be careful with these ones!
.040,
.050,
.060,
.080,
.100,
.120,
);
#Areas larger than the values below will be filled with parallel lines:
#This cuts down on the number of aperture sizes used.
#Set to 0 to always use an aperture or drill, regardless of size.
use constant { MAX_APERTURE => max((TOOL_SIZES)) + .004, MAX_DRILL => -min((TOOL_SIZES)) + .004 }; #max aperture and drill sizes (plus a little tolerance)
#DebugPrint(sprintf("using %d standard tool sizes: %s, max aper %.3f, max drill %.3f\n", scalar((TOOL_SIZES)), join(", ", (TOOL_SIZES)), MAX_APERTURE, MAX_DRILL), 1);
#NOTE: Compare the PDF to the original CAD file to check the accuracy of the PDF rendering and parsing!
#for example, the CAD software I used generated the following circles for holes:
#CAD hole size: parsed PDF diameter: error:
# .014 .016 +.002
# .020 .02267 +.00267
# .025 .026 +.001
# .029 .03167 +.00267
# .033 .036 +.003
# .040 .04267 +.00267
#This was usually ~ .002" - .003" too big compared to the hole as displayed in the CAD software.
#To compensate for PDF rendering errors (either during CAD Print function or PDF parsing logic), adjust the values below as needed.
#units are pixels; for example, a value of 2.4 at 600 dpi = .0004 inch, 2 at 600 dpi = .0033"
use constant
{
HOLE_ADJUST => -0.004 * 600, #-2.6, #holes seemed to be slightly oversized (by .002" - .004"), so shrink them a little
RNDPAD_ADJUST => -0.003 * 600, #-2, #-2.4, #round pads seemed to be slightly oversized, so shrink them a little
SQRPAD_ADJUST => +0.001 * 600, #+.5, #square pads are sometimes too small by .00067, so bump them up a little
RECTPAD_ADJUST => 0, #(pixels) rectangular pads seem to be okay? (not tested much)
TRACE_ADJUST => 0, #(pixels) traces seemed to be okay?
REDUCE_TOLERANCE => .001, #(inches) allow this much variation when reducing circles and rects
};
#Also, my CAD's Print function or the PDF print driver I used was a little off for circles, so define some additional adjustment values here:
#Values are added to X/Y coordinates; units are pixels; for example, a value of 1 at 600 dpi would be ~= .002 inch
use constant
{
CIRCLE_ADJUST_MINX => 0,
CIRCLE_ADJUST_MINY => -0.001 * 600, #-1, #circles were a little too high, so nudge them a little lower
CIRCLE_ADJUST_MAXX => +0.001 * 600, #+1, #circles were a little too far to the left, so nudge them a little to the right
CIRCLE_ADJUST_MAXY => 0,
SUBST_CIRCLE_CLIPRECT => FALSE, #generate circle and substitute for clip rects (to compensate for the way some CAD software draws circles)
WANT_CLIPRECT => TRUE, #FALSE, #AI doesn't need clip rect at all? should be on normally?
RECT_COMPLETION => FALSE, #TRUE, #fill in 4th side of rect when 3 sides found
};
#allow .012 clearance around pads for solder mask:
#This value effectively adjusts pad sizes in the TOOL_SIZES list above (only for solder mask layers).
use constant SOLDER_MARGIN => +.012; #units are inches
#line join/cap styles:
use constant
{
CAP_NONE => 0, #butt (none); line is exact length
CAP_ROUND => 1, #round cap/join; line overhangs by a semi-circle at either end
CAP_SQUARE => 2, #square cap/join; line overhangs by a half square on either end
CAP_OVERRIDE => FALSE, #cap style overrides drawing logic
};
#number of elements in each shape type:
use constant
{
RECT_SHAPELEN => 6, #x0, y0, x1, y1, count, "rect" (start, end corners)
LINE_SHAPELEN => 6, #x0, y0, x1, y1, count, "line" (line seg)
CURVE_SHAPELEN => 10, #xstart, ystart, x0, y0, x1, y1, xend, yend, count, "curve" (bezier 2 points)
CIRCLE_SHAPELEN => 5, #x, y, 5, count, "circle" (center + radius)
};
#const my %SHAPELEN =
#Readonly my %SHAPELEN =>
our %SHAPELEN =
(
rect => RECT_SHAPELEN,
line => LINE_SHAPELEN,
curve => CURVE_SHAPELEN,
circle => CIRCLE_SHAPELEN,
);
#panelization:
#This will repeat the entire body the number of times indicated along the X or Y axes (files grow accordingly).
#Display elements that overhang PCB boundary can be squashed or left as-is (typically text or other silk screen markings).
#Set "overhangs" TRUE to allow overhangs, FALSE to truncate them.
#xpad and ypad allow margins to be added around outer edge of panelized PCB.
use constant PANELIZE => {'x' => 1, 'y' => 1, 'xpad' => 0, 'ypad' => 0, 'overhangs' => TRUE}; #number of times to repeat in X and Y directions
# Set this to 1 if you need TurboCAD support.
#$turboCAD = FALSE; #is this still needed as an option?
#CIRCAD pad generation uses an appropriate aperture, then moves it (stroke) "a little" - we use this to find pads and distinguish them from PCB holes.
use constant PAD_STROKE => 0.3; #0.0005 * 600; #units are pixels
#convert very short traces to pads or holes:
use constant TRACE_MINLEN => .001; #units are inches
#use constant ALWAYS_XY => TRUE; #FALSE; #force XY even if X or Y doesn't change; NOTE: needs to be TRUE for all pads to show in FlatCAM and ViewPlot
use constant REMOVE_POLARITY => FALSE; #TRUE; #set to remove subtractive (negative) polarity; NOTE: must be FALSE for ground planes
#PDF uses "points", each point = 1/72 inch
#combined with a PDF scale factor of .12, this gives 600 dpi resolution (1/72 * .12 = 600 dpi)
use constant INCHES_PER_POINT => 1/72; #0.0138888889; #multiply point-size by this to get inches
# The precision used when computing a bezier curve. Higher numbers are more precise but slower (and generate larger files).
#$bezierPrecision = 100;
use constant BEZIER_PRECISION => 36; #100; #use const; reduced for faster rendering (mainly used for silk screen and thermal pads)
# Ground planes and silk screen or larger copper rectangles or circles are filled line-by-line using this resolution.
use constant FILL_WIDTH => .01; #fill at most 0.01 inch at a time
# The max number of characters to read into memory
use constant MAX_BYTES => 10 * M; #bumped up to 10 MB, use const
use constant DUP_DRILL1 => TRUE; #FALSE; #kludge: ViewPlot doesn't load drill files that are too small so duplicate first tool
my $runtime = time(); #Time::HiRes::gettimeofday(); #measure my execution time
print STDERR "Loaded config settings from '${\(__FILE__)}'.\n";
1; #last value must be truthful to indicate successful load
#############################################################################################
#junk/experiment:
#use Package::Constants;
#use Exporter qw(import); #https://perldoc.perl.org/Exporter.html
#my $caller = "pdf2gerb::";
#sub cfg
#{
# my $proto = shift;
# my $class = ref($proto) || $proto;
# my $settings =
# {
# $WANT_DEBUG => 990, #10; #level of debug wanted; higher == more, lower == less, 0 == none
# };
# bless($settings, $class);
# return $settings;
#}
#use constant HELLO => "hi there2"; #"main::HELLO" => "hi there";
#use constant GOODBYE => 14; #"main::GOODBYE" => 12;
#print STDERR "read cfg file\n";
#our @EXPORT_OK = Package::Constants->list(__PACKAGE__); #https://www.perlmonks.org/?node_id=1072691; NOTE: "_OK" skips short/common names
#print STDERR scalar(@EXPORT_OK) . " consts exported:\n";
#foreach(@EXPORT_OK) { print STDERR "$_\n"; }
#my $val = main::thing("xyz");
#print STDERR "caller gave me $val\n";
#foreach my $arg (@ARGV) { print STDERR "arg $arg\n"; }
Author: swannman
Source Code: https://github.com/swannman/pdf2gerb
License: GPL-3.0 license
1665991784
In this tutorial, you'll learn how to create a drag-and-drop file upload with HTML, CSS and JavaScript.
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Drag & Drop File Upload</title>
<!-- Font Awesome Icons -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
/>
<!-- Google Font -->
<link
href="https://fonts.googleapis.com/css2?family=Poppins&display=swap"
rel="stylesheet"
/>
<!-- Stylesheet -->
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="container">
<input type="file" id="upload-button" multiple accept="image/*" />
<label for="upload-button"
><i class="fa-solid fa-upload"></i> Choose Or Drop Photos
</label>
<div id="error"></div>
<div id="image-display"></div>
</div>
<!-- Script -->
<script src="script.js"></script>
</body>
</html>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: "Poppins", sans-serif;
}
body {
background-color: #f5f8ff;
}
.container {
background-color: #ffffff;
width: 60%;
min-width: 37.5em;
padding: 3.12em 1.87em;
position: absolute;
transform: translateX(-50%);
left: 50%;
top: 1em;
box-shadow: 0 1.25em 3.43em rgba(0, 0, 0, 0.08);
border-radius: 0.5em;
}
input[type="file"] {
display: none;
}
label {
display: block;
position: relative;
background-color: #025bee;
color: #ffffff;
font-size: 1.1em;
text-align: center;
width: 16em;
padding: 1em 0;
border-radius: 0.3em;
margin: 0 auto 1em auto;
cursor: pointer;
}
#image-display {
position: relative;
width: 90%;
margin: 0 auto;
display: flex;
justify-content: space-evenly;
gap: 1.25em;
flex-wrap: wrap;
}
#image-display figure {
width: 45%;
}
#image-display img {
width: 100%;
}
#image-display figcaption {
font-size: 0.8em;
text-align: center;
color: #5a5861;
}
.active {
border: 0.2em dashed #025bee;
}
#error {
text-align: center;
color: #ff3030;
}
let uploadButton = document.getElementById("upload-button");
let chosenImage = document.getElementById("chosen-image");
let fileName = document.getElementById("file-name");
let container = document.querySelector(".container");
let error = document.getElementById("error");
let imageDisplay = document.getElementById("image-display");
const fileHandler = (file, name, type) => {
if (type.split("/")[0] !== "image") {
//File Type Error
error.innerText = "Please upload an image file";
return false;
}
error.innerText = "";
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
//image and file name
let imageContainer = document.createElement("figure");
let img = document.createElement("img");
img.src = reader.result;
imageContainer.appendChild(img);
imageContainer.innerHTML += `<figcaption>${name}</figcaption>`;
imageDisplay.appendChild(imageContainer);
};
};
//Upload Button
uploadButton.addEventListener("change", () => {
imageDisplay.innerHTML = "";
Array.from(uploadButton.files).forEach((file) => {
fileHandler(file, file.name, file.type);
});
});
container.addEventListener(
"dragenter",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.add("active");
},
false
);
container.addEventListener(
"dragleave",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.remove("active");
},
false
);
container.addEventListener(
"dragover",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.add("active");
},
false
);
container.addEventListener(
"drop",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.remove("active");
let draggedData = e.dataTransfer;
let files = draggedData.files;
imageDisplay.innerHTML = "";
Array.from(files).forEach((file) => {
fileHandler(file, file.name, file.type);
});
},
false
);
window.onload = () => {
error.innerText = "";
};
#html #css #javascript