june-paulajs
v2.1.1
Published
JuNe PaulaJS (Portable Adaptable Utility for Lightweight Applications) JavaScript framework to build frontend, user interfaces, Single-page application or Progressive Web App
Downloads
74
Maintainers
Readme
JuNe PaulaJS (only 1 file: 27 Kb)
JavaScript framework to build frontend or any type of web project
● JuNe PaulaJS (Portable Adaptable Utility for Lightweight Applications) is a JavaScript framework similar to React, Vue or Angular, but with more and needed features, without dependencies an optimized in 1 file.
🏗 To build frontend, user interfaces, Single-page application (SPA) or Progressive Web App (PWA), also to create public pages with link compatibility for SEO. With a fully functionality and easy installation and use.
⌨ Developing without using any pseudo-language, just pure (Vanilla) JavaScript, without compiler/transpiler to edit your files directly (build optional for compact/uglify).
📳 Run on all devices (desktop, mobile, tablet).
🏎️ So, developments, deployments, executions and performance are faster.
🍃 And a lower impact on the carbon footprint, both in the server and in the client browser, by using files of a few 27 Kb, which represents less transfer and less execution times, in a difference between 400-800 times compared to other frameworks.
● Compatible and created with ECMAScript 2024 (ES15).
🏅 10 in 1
1. Reactive variables
2. Routing system (with SEO compatibility if public)
3. Languages support
4. Modal windows
5. Toast notifications
6. Backend requests (with captcha capability)
7. Upload files with progress
8. Rescaling images for faster uploads and preview
9. Voice commands, speech recognition and synthesis of human speech
10. JuNe CSS (responsive)
⏺ Includes the default features that any other framework has, such as reactive variables, for loops, or ifs.
⏺ But it also includes other features that in other frameworks you have to add a plugin, such as routing, language support, modal windows, toast notifications, captcha, or send requests to backend.
⏺ And also other important features such as a complete file upload system with custom preview, image rescaling, modules in background, automatic loader display, Open Graph, social share, or speech recognition/synthesis .
⏺ It includes everything you need to make your complete frontend, such as JuNe CSS, backend with JuNe BackServer, or a WebServer.
⏺ It´s easier and faster to directly develop, pure JavaScript, with no transpiler and including a single few Kbs file.
👉 Ideal to use with backend JuNe BackServer
| | JuNe PaulaJS | React | Create-React | Vue | Angular | Next | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | Files | 1 | 151 | 37.200 | 293 | 10.410 | 5.490 | | Folders | 0 | 23 | 4.909 | 56 | 943 | 497 | | Size | 27 Kb | 7.8 Mb | 225 Mb | 14 Mb | 36.7 Mb | 156 Mb |
All features you need
✔ Pure vanilla JavaScript without dependencies, with native Web Components.
✔ Reactive variables with automatic visual changes.
✔ If-conditions to show/hide, and for-loops to content repeat.
✔ Routing system integrated, with window.history and compatibility with open in new tab/window.
✔ SEO compatibility (for public projects).
✔ Support for languages.
✔ Support for modal windows.
✔ Support for toast notifications.
✔ Send requests to backend (GET, POST, PUT, PATCH, DELETE), recommended: JuNe BackServer.
✔ Compatibility with JuNe BackServer Captcha.
✔ Upload multiple small or large files and entire folders with progress, from inputs, Drag & Drop or clipboard.
✔ Possibility of rescaling images for faster uploads and preview.
✔ Background modules and restore again to foreground with the same status.
✔ Meta tags or Open Graph protocol compatibility (for public projects).
✔ Social network share (for public projects).
✔ File type icons according file extension.
✔ Manage your frontend with your voice, transcribes from speech to text and human speech synthesis.
✔ Detect DOM new elements.
✔ Includes JuNe CSS (responsive).
✔ Optional Node.js HTTP server for development with automatic update (HMR).
✔ Develop using only JavaScript without other pseudo-languages (like React syntax).
✔ Simple structure: data, functions and html, without complications (useEffect, useState...).
✔ No compilation/transpiler needed, neither Babel/Webpack/Snowpack.
✔ Ease installation, just include (minified) JuNe PaulaJS JavaScript file on your HTML.
Author
Eduardo Ruiz <[email protected]>
JuNe / JUst NEeded Philosophy
- Source code using less code as possible So you can understand code and find bugs easier.
- Few and optimized lines is better Elegant design.
- Avoid external dependencies abuse/bloated, and possible third-party bugs Less files size, better and faster to the interpreter.
- Clear and useful documentation with examples and without verbose Get to the point.
- Avoid showing unsolicited popups, notifications or messages in frontend For better User eXperience.
- Simple UI, without many menus/options and with few clicks to get to sites.
- Consequences of having a lot of code (and for simple things): Having to work and search through many files and folders with a lot of wasted time, successive errors due to missing unknown files, madness to move a code to another project, errors due to recursive dependencies difficult to locate, complexity or impossibility to migrate to new versions, unfeasibility to follow the trace with so much code, risk of new errors if the functionality is extended, problems not seen at the first sight, general slowness in the whole development due to excessive and unnecessary code.
Installation
All you need is JuNe PaulaJS file and add it to your HTML page:
<script src="june-paula.js"></script>
or minified (better):
<script src="june-paula.min.js"></script>
And you can specify root path in path
parameter <script src="june-paula.min.js" path="/mypath/"></script>
(default /) slash ended.
So you can download one of these files.
A whole solution for production and development
You can install the complete package if you want to run a production server, a development environment, view examples, or use utilities to create template projects and uglify/build projects.
npm install june-paulajs
or better option globally with npm install -g june-paulajs
JuNe BackServer and JuNe WebServer are integrated, so it´s not necessary to install them directly.
| Command | Definition | If globally | | --- | --- | --- | | npm run start file | Start JuNe WebServer in development mode in current folder to run projects and reload on changesDefault http://localhost:8080Sample npm run start index.html | - | | npm run example file | Start JuNe WebServer in development mode in JuNe PaulaJS folder to run examples and reload on changesDefault http://localhost:8080Sample npm run example "05-If-sample.html" | - | | npm run backend | Start Backend with JuNe BackServer for some examples, or create your own for production or developmentDefault http://localhost:8180 | - | | npm run create | Create a template project in current folder | paulajs create | | npm run build | Uglify/build a project to folder dist/ | paulajs build | | ~~npm install~~ | Not necessary, JuNe PaulaJS has no dependencies | - |
You may not need Nginx/Apache and launch your project directly with integrated JuNe WebServer:
webserver -url http://localhost:8180 -folder /var/www/myproject
Have a look to JuNe WebServer Readme for details.
Create template project
If you want to create templates easily with JuNe CSS and Font Awesome, you must have JuNe PaulaJS installed globally, so create a project folder and type inside: paulajs create
Visual Studio Code Extension available.
Using JuNe PaulaJS
The idea is to develop your entire project as an object with OOP (Object Oriented Programming) using this
where is integrated JuNe PaulaJS and your project functions.
Please remember that we are looking for a practical and lightweight functionality, so there are specific ways to do something, without creating many other variations and to achieve the usual functions in a frontend.
Global const created to access directly june_pau
General objects:
| Object | Type | From | Definition | Sample |
| --- | --- | --- | --- | --- |
| this.data | Array | Imported module | For reactive variables | this.data.myVariable = 1
|
| this.funcs | Array | Imported module | Module own functions | this.funcs.myFunc()
|
| this.main | Array | main.js | Optional main global data/functions | this.main.funcs.myFunc()
|
| this.outlet | HTMLElement | HTML Tag jpau-content | HTML route content | <span>My Page</span>
|
| this.params | Array | Autogenerated | Params from URL | /users/:uid
➤ this.params.uid |
| this.getparams | URLSearchParams | Autogenerated | Params from GET | /users/?var=1
➤ this.getparams.var |
Or you could use globally june_pau.data
june_pau.funcs
june_pau.main.data
...
Optional Main file
Global optional main file loaded at startup.
Export (all optional) constants for data variables and functions.
Save in root path main.js
and access with this.main.data
and this.main.funcs
If june_pau.main = -1
is previously defined then no attempt is made to load main.js
to avoid 404 and browser console error.
| Value | Type | Definition | | --- | --- | --- | | data | Array | Optional global data variables | | funcs | Array | Optional global functions |
Sample:
export const data = {
forceUpdate: false
};
export const funcs = {
onLoad: () => alert('First Load'),
onMount: () => alert('First Mount')
};
| Object | Const | Type | Definition | Default | | --- | --- | --- | --- | :---: | | forceUpdate | data | Bool | Optional if true then force entire updated on every this.data change | false | | beforeunload | data | Bool | Optional Window before unload notice | nothing | | uploadSmallMax | data | Integer | Optional maximum file size in Kb for small uploads, recommended 5120 Kb (5 Mb) | - | | uploadLargeMax | data | Integer | Optional maximum file size in Kb for large uploads, recommended 51200 Kb (50 Mb) | - | | onLoad | funcs | Function | Optional function called on first load | - | | onMount | funcs | Function | Optional function called on first mount | - |
- To update an element (document.getElementById) with
this.updElm(element)
orjune_pau.updElm(element)
- To update all
this.updHTML()
Standard Module Structure
Create a file for each section of your page.
Export (all optional) constants for data variables, functions and HTML shown.
Save in file mySection.js
(you can use paths, starts with relative path always).
| Object | Type | Definition | | --- | --- | --- | | data | Array | Optional module data reactive variables | | funcs | Array | Optional module functions | | html | String | Optional module HTML content |
Sample:
export const data = {
title: 'My Module Document Title',
myVariable: 'My value',
myArray: [{name: 'John'}, {name: 'Peter'}]
};
export const funcs = {
onLoad: () => alert('Module loaded'),
onMount: () => alert('Module mounted'),
myFunc: (value) => `${value}`,
myFunctionClassic: function() {},
myFuncStyle: () => 'color: red'
};
export const html = `<div><input type="text"></div>`;
| Object | Const | Type | Definition |
| --- | --- | --- | --- |
| title | data | String | Optional HTML document titleFor better performance: <title data-jpauattr="1" data-jpauihtml="{{ title }}">My APP title</title>
|
| onLoad | funcs | Function | Optional function called on module load |
| onMount | funcs | Function | Optional function called on module mounted |
| onVisible | funcs | Function | Optional function called when a background module is shown with first parameter true or false |
Prefixes for HTML tags attributes
| Prefix | Definition | Sample |
| :---: | --- | --- |
| : | Assign value using text and data variables | <img :title="Text and {{ variable }}">
<input readonly :value="{{ variable * 3 }}">
|
| * | Assign value using return value from function, or to reassign data variables from inputs | <img *title="this.funcs.myTitle()">
<input *value="variable">
|
| @ | For events | <input @click="this.funcs.myFunction()">
|
checked and selected (boolean) attributes using * may use object this.data.myvar[value]
or variable this.data.myvar == value
And with the other attributes may use object this.data.myvar[id | name]
or variable this.data.myvar
So you can directly program the variable assignment or use this method.
Syntax in HTML elements
| Tag | Definition | Sample |
| :---: | --- | --- |
| {{ variable }} | Replace variable by it´s value | <span>{{ username }}</span>
|
| {{ function() }} | Replace with function returned value | <span>{{ this.funcs.myFunction() }}</span>
|
| $this | Current object for events | <input @click="this.funcs.myFunction($this.tagName)">
|
Reactive variables
- For this.data and this.main.data do not use it for browser handlers (like a WebSocket), for that, create other object as this.handlers
- JuNe PaulaJS unlike other frameworks, does support all levels of nesting of objects/arrays, so it´s not very necessary to assign variables with redux-style systems.
- You can use this or june_pau prefixes to access data and functions:
{{ this.funcs.myFunc() }}
or{{ june_pau.funcs.myFunc() }}
or inside for-loops:{{ this.funcs.myFunc(x + this.data.myVar) }}
The process of repainting / generating HTML according to reactive variables is more efficient than other frameworks and is only performed when necessary (in a specific DOM node), and not several times.
Redux
JuNe PaulaJS reactive variables are only triggered at each variable or object change, so it is more efficient than standard Redux systems where you have to reassign the entire object with setState({...state, ...variables...})
however you can design your own Redux system by assigning the variables directly to their object without reassigning the entire object.
If-conditions
To show if true using data-jpau-if
<div data-jpau-if="this.data.mustShow && this.data.mustBeTwo === 2">
...
</div>
For-loops
To repeat content from variables or arrays using data-jpau-for
<div data-jpau-for="for(let i = 0; i < 5; ++i)">
<span>Number {{ i }}</span>
</div>
<table>
<tbody>
<tr data-jpau-for="for(let user of this.data.users)">
<td>Name {{ user.name }}</td>
</tr>
</tbody>
</table>
HTML pre
tag is skipped for processing, so it can be used for sample code.
Always use a previous parent container:
<div> <!-- parent -->
<div data-jpau-for="...">...</div>
</div>
<select> <!-- parent -->
<option data-jpau-for="...">
</select>
<table> <!-- parent -->
<tr data-jpau-for="...">
...
</tr>
</table>
HTML tags (for routing system)
| Tag | Definition | Sample |
| :---: | --- | --- |
| jpau-content | Block content and routes definition | <jpau-content><jpau-route path="/" module="index"></jpau-route></jpau-content>
|
| jpau-route | Route definition to load JS modules according path | <jpau-route path="/" module="index"></jpau-route>
<jpau-route path="/users/:id" module="users"></jpau-route>
<jpau-route path="(default)" module="page404"></jpau-route>
|
| jpau-link | Link to route using is with HTMLAnchorElementUse it as a normal a href tag | <a href="/" is="jpau-link">Home</a>
<a href="/users" is="jpau-link">Users</a>
|
jpau-route attributes: path: URL route path with variables. module: name of the module to load, must be unique name. background: Optional true to set a background module.
<jpau-route path="/" module="index"></jpau-route>
<jpau-route path="/users/:uid" module="modules/users" background="true"></jpau-route>
(default) For jpau-route if no routes found and load a module for 404 notice.
Navigate to a route using
this.link(URL)
ℹ️Best Practices
- Use unique names, with a certain length of characters, that are not repeated in the project (add a prefix of the module it is), and that cannot be confused with HTML tags or JavaScript code, by example if you can use for faster performance
user.user_name
,customer.customer_name
(not mandatory) - Be careful when programming because some events can be triggered several times or another repaint can be generated in loops, so do not make requests in those places and limit it only to normal display functions.
- Insert always the variables in a container
<span>{{ myvar }}</span>
Single file app or loading modules
this.importMod() is the function that loads the modules and is called internally on each link request. But if you look at the examples that are downloaded in the repository, they are individual files, outside of a routing system. So, you can make an app that is all in one file, without loading importing modules. JuNe PaulaJS structure can be in a file or in an object (with data, funcs and html).
| Call | Definition | Go | | --- | --- | --- | | importMod(filename) | Load ´filename´ JavaScript module | this.link(filename) | | importMod(filename, true) | Load ´filename´ JavaScript as backgroud module | this.link(filename) | | importMod(object) | Load object JavaScript module as index | this.importMod(object) | | importMod(object, name) | Load object JavaScript module as name | this.importMod(object, name) |
Sample single page:
<jpau-content></jpau-content>
<script>
const page1 = {
data: {},
funcs: {},
html: 'Page1 <input type="button" @click="this.importMod(page2, 'page2')" value="Go to Page2">'
};
const page2 = {
data: {},
funcs: {},
html: 'Page2 <input type="button" @click="this.importMod(page1, 'page1')" value="Go to Page1">'
};
june_pau.main = -1;
june_pau.importMod(page1, 'background');
</script>
Meta tags / Open Graph protocol (for public projects)
- Sample meta property
<meta property="og:image" content="{{ og:image }}">
export const data = {
title: 'My Module Document Title',
'og:image': 'https://mydomain.tld/myimage.jpg'
};
We can´t have control over whether a spider/crawler interprets JavaScript to change headers / DOM.
🏳Languages
Add to main.js
(for globally use).
| Object | Type | Definition | | --- | --- | --- | | langs | Array | Languages available | | texts | Array | Texts in each language |
- Languages array with:
- code: Language code as defined in RFC 5646.
- name: Language name.
- icon: Language icon (flag).
- default: Short first two-letters unique code for other similar language, by example if
en-US
anddefault: 'en'
then is selected if language isen-AU
oren-*
(and don´t exist).
First try window.navigator.language or first one of langs array.
Sample:
export const langs = [
{code: 'en-US', name: 'English', icon: '🇬🇧', default: 'en'},
{code: 'en-AU', name: 'English (Australia)', icon: '🇦🇺'},
{code: 'es-ES', name: 'Español', icon: '🇪🇸', default: 'es'}
];
export const texts = {
'en-US': { ok: 'Ok',
cancel: 'Cancel',
deleteAsk: 'Delete?',
uploadSmallMsg: 'Maximum file size is 5 Mb'
uploadLargeMsg: 'Maximum file size is 50 Mb'
},
'en-AU': { ok: 'Ok',
cancel: 'Cancel',
deleteAsk: 'Delete?',
uploadSmallMsg: 'Maximum file size is 5 Mb'
uploadLargeMsg: 'Maximum file size is 50 Mb'
},
'es-ES': { ok: 'Aceptar',
cancel: 'Cancelar',
deleteAsk: '¿Eliminar?',
uploadSmallMsg: 'El tamaño máximo es 5 Mb'
uploadLargeMsg: 'El tamaño máximo es 50 Mb'
}
};
- Change language with
this.setLang('code')
orjune_pau.setLang('code')
samplethis.setLang('en-US')
- The selected language is stored in localStorage to remember the decision when you enter the website again.
- Get current language ISO code with
this.lng
(as en-US, es-ES...) - 🗣 Get text according current language with
this.text(code)
- You can use the JavaScript internationalization Intl namespace object to get for example, a number in a language format (thousands and decimal separator) with:
const mynumberstr = new Intl.NumberFormat(this.lng, {maximumFractionDigits: 2}).format(mynumber)
For dates (year, month and day positions):const mydatestr = new Intl.DateTimeFormat(this.lng).format(new Date())
Internal language codes used:
| Code | Function | Default |
| --- | --- | --- |
| ok | If you want Ok button for this.windowShow | Ok
|
| cancel | If you want Cancel button for this.windowShow | Cancel
|
| deleteAsk | Ask for delete for this.windowDel | Delete sure?
|
| uploadSmallMsg | Notice maximum size small files upload | Maximum file size
|
| uploadLargeMsg | Notice maximum size large files upload | Maximum file size
|
Modal windows
A modal window disables the main window but is visible, users must interact before they can return to main window.
To show this.windowShow(text[, options])
or june_pau.windowShow(text[, options])
To hide last this.windowHide()
or june_pau.windowHide()
Responds to keyboard (Esc, Return, Space).
- text: HTML text for window.
- options: Optional array with all optional options:
| Object | Type | Definition |
| --- | --- | --- |
| okAction | Function | Function for ok button |
| okText | String | HTML text to show in ok button or default ok
language text |
| cancelAction | Function | Function for cancel button, set to 0 for just hide |
| cancelText | String | HTML text to show in cancel button or default cancel
language text |
| input | String | Input default value (or empty string) to request an input value passed to okAction |
| noHide | Bool | If true window not hidden on button click, windowHide implemented manually |
| winCSSbg | String | CSS class name for background instead internal stylesdefault: background: rgba(255,255,255,0.83); backdrop-filter: blur(2px); transition: .5s ease |
| winCSSfg | String | CSS class name for window instead internal stylesdefault: margin: auto; padding: 8px; text-align: center; border-radius: 6px; background: #FFF; box-shadow: 1px 1px 10px #444; transform: scale(1.4); transition: .3s ease |
- 👁️To show ok or cancel button, must exist okaction or cancelaction functions.
- 🏳 Be sure languages default texts for
ok
andcancel
exist inmain.js
- 🎨 Be careful if you replace CSS, copy internal and modify it.
- ℹ️ You can set globally
winCSSbg
andwinCSSfg
inthis.main.data
- ❌ Predefined ask for delete modal window
this.windowDel(function_for_delete, optional_text)
Toast notifications
Is a non-modal, unobtrusive element that displays a short message when an event occurs, and dissapears in a time. Also called passive pop-up, snackbar, bubble or notification.
To show: this.toastShow(text[, options])
or june_pau.toastShow(text[, options])
To hide last: this.toastHide()
or june_pau.toastHide()
- text: HTML text for toast.
- options: Optional array with all optional options:
| Object | Type | Definition | Default | | --- | --- | --- | :---: | | toastPos | String | Position [top or bottom] | top | | toastPY | Integer | End animation Y position (for top or bottom) | 50px | | toastTime | Integer | Millisecons to hide when Page Visibility (0 for no hide) | 3500 | | toastCSS | String | CSS class name for toast instead internal stylesdefault: width: 80%; left: 10%; right: 10%; padding: 7px 9px; text-align: center; border-radius: 6px; box-shadow: 1px 1px 10px #CCC; color: #FFF; background: rgba(0,0,0,0.83); backdrop-filter: blur(2px); transition: .3s ease' | - |
- ℹ️ You can set globally these options in
this.main.data
- Page Visibility API allows not to miss toasts when an event occurs until page is shown.
Default modal / toast CSS:
.wincssbg {
background: rgba(255, 255, 255, 0.83);
backdrop-filter: blur(2px);
transition: .5s ease;
}
.wincssfg {
margin: auto;
padding: 8px;
text-align: center;
border-radius: 6px;
background: var(--jcbg);
box-shadow: 1px 1px 10px #444;
transform: scale(1.4);
transition: .3s ease;
div {
color: var(--jcclr);
line-height: 1.7em;
}
}
.toastcss {
width: 80%;
left: 10%;
right: 10%;
padding: 7px 9px 20px;
text-align: center;
border-radius: 6px;
box-shadow: 1px 1px 10px #CCC;
color: #FFF;
background: rgba(0,0,0,0.83);
backdrop-filter: blur(2px);
transition: .3s ease;
}
Backend requests
- Integrated async function to send requests to backend.
- Header for x-access-token or authorization is sent for user login identification, and automatic session update.
- To create RESTful API ⯁ Try the routing/token backend JuNe BackServer
- Designed for JSON and multipart/form-data communication.
- Performs an event.preventDefault() to prevent e.g. form submit.
this.sendRequest(url[, method[, data[, pget]]])
| Param | Type | Definition |
| --- | --- | --- |
| url | String | URL to send request |
| method | String | Optional HTTP method: GET (default), POST, PUT, PATCH, DELETE |
| data | FormData / Array | Optional for POST/PUT/PATCH:FormData from this.fdata
or data array object {"myvar1": 1, "var2": 2}
|
| pget | Object | Optional GET parameters to add to the URL |
Common uses: GET for read, POST for create, PUT for update, PATCH for modify and DELETE for delete.
- You could store main backend URL in
main.data.backend_url
Returns an array object
| Object | Type | Definition | OnOk | OnError | | --- | --- | --- | :---: | :---: | | status | Integer | HTTP status | ✅ | ❌ | | headers | Array | HTTP headers | ✅ | ❌ | | response | JSON | HTTP response | ✅ | ❌ | | error | Exception | Error | ❌ | ✅ |
⏱ This function is asynchronous, but you may prefer to use a promise, in that case and if you want to use the token system as well, you can use the sendRequest breakdown with:
- this.sendReqIni(url, method, data, pget = {}) for the start of the call to prepare the parameters, returns object with url and options for the fetch.
- this.sendReqEnd(response) to update the token, returns headers object.
const f = this.sendReqIni(url, method, data, pget);
fetch(f.url, f.options).then(response => {
const headers = this.sendReqEnd(response);
if(response.ok)
return response.json();
else
this.toastShow(`Error: ${response.statusText}`);
}).then(json => {
// Process JSON here
}).catch(err => {
this.idle(false);
this.toastShow(`Error: ${err}`);
});
ℹ️ Show a loader
Loader in a HTMLElement with initial style.visibility: hidden
and store the id string in main.data.loader
Then it will be shown on request, and hidden when finished calling this.idle(bool)
💡 Tips for a cool design
- Add to body
style="opacity: 0; transition: all .5s"
to hide page while loading, and this function to main.jsexport const funcs = {onMount: () => document.body.style.opacity = 1}
then page will be shown with a fade on mount. - To temporarily avoid showing tags with {{}} you can add the attribute with the tag to be displayed and leave it empty or put something in the content:
<title data-jpauattr="1" data-jpauihtml="{{ title }}">My App title</title>
- Have a look to JuNeDNS Frontend to see the WebSocket system to automatically update the content displayed on screen if another user makes any changes in the showed section from another computer
🔑 To allow auth control
- Enable it by adding
auth: true
todata
inmain.js
- Automatically will check implementation auth header on every response:
- x-access-token and stored in
main.data._token
- authorization (bearer or basic) and stored in
main.data._auth
- x-access-token and stored in
- And will be resent in header on next request with updated expiration. To make this, JuNe BackServer expose headers X-Access-Token and Authorization.
🎓 Good practices
- Never trust on the data received, always check each field to avoid errors or security risks.
- Data may not be received due to a communication failure, be incomplete or manipulated from origin.
- Check everything even if it seems redundant, remember that a simple error can stop the execution of your program.
- Don´t forget to check user permissions.
- JuNe PaulaJS can´t check everything to prevent the code from growing, so keep an eye on everything you do and watch the error console.
- Use Optional chaining operator ?. the expression evaluates to undefined instead of throwing an error myobject?.mymethod?.mymethod2
- Use Nullish coalescing operator to set a (default) return value if null or undefined myvar = myvar ?? mydefault
- Use Short-circuits conditionals with logical operator isOk && isOkCall() or const lang = getLang() || 'en-US'
Create FormData from form inputs
To facilitate data submit converting form inputs into a FormData with this.fdata(formId, reportValidity)
- Requires id form value as parameter.
- Optional second parameter (default true) trigger form.reportValidity(), return false if validation constraints.
- Returns FormData or false if input field restrictions fail.
- So data would be FormData
let data = this->fdata('frm')
- Input name is required.
- Disabled elements (not readonly) are ignored.
- Input type checkbox or radio must have a value to send when checked or default 1 for true.
- You can add more variables using
data.append(name, value)
Captcha with JuNe BackServer (for public projects)
Using JuNe BackServer Captcha to prevent form spamming by bots. It´s in invisible for users.
- Create a GET endpoint to receive a key to add to the form:
app.get('/captcha', (req, res) => {
req.content.k = app.captcha();
});
- Function to create FormData (from
frm
form), add captcha key and send:
async function sendForm(id) {
let fd = june_pau.fdata('frm');
if(fd === false) return;
fd = june_pau.captcha(fd, 'http://localhost:8080/captcha');
let res = await june_pau.sendRequest('http://localhost:8080/post', 'POST', fd);
}
- On POST endpoint must check captcha input variable:
app.post('/post', (req, res) => {
if(!req.body.captcha || !app.captcha(req.body.captcha))
return;
// Ok
});
The operation is the following, before sending the form by the frontend, it sends a request to the backend that returns a key that is registered in the backend for one minute, the frontend forwards it to the backend and it is verified that it exists. Since a FormData is used, the captcha key request is done first and then the form submit. Therefore, indicate a different and useless URL in the form action so that the bots waste time there.
Files upload
Forget about the complication of a file upload system, which can be multiple and with large files. Just receive the file and nothing else.
The JuNe PaulaJS powerful integrated file uploading system is unique on Internet, you can do everything... Upload normal or large files, multiple selection, entire folders, rescale images, limit file types, events control, continue browsing while uploading, warning notice if window unload, show preview with progress, upload times, file type icons, Drag & Drop, paste image or files from clipboard.
- It´s possible to add file input attributes
webkitdirectory
(for directory upload) ormultiple
(for more than 1 file). - Support from inputs, Drag & Drop or clipboard image or files.
- Possibility of rescaling images for faster uploads.
- As well as
main.data.beforeunload
a notice window warning if unload and still files uploading. - If a file limit is set, it must also be checked at server reception for security reasons.
- Header for x-access-token or authorization is sent for user login identification.
- You can decide how to upload files depending on size, small for small files or large for large files sent by chunks.
⚠️Be careful and do not allow uploads without check user login in frontend and backend.
Upload small files
For small files (< 12 Mb) you could use this.fdata , and the submit will be like a normal form. To enable this:
- Add dataset
data-jpau-upload="small"
in file input<input type="file" name="myfile" data-jpau-upload="small">
- Set maximum Kb size in
this.main.data.uploadSmallMax
recomended5120
5 Mb (default0
for no limit). - Set
uploadSmallMsg
language code if different value toMaximum file size
- File(s) are sent as array whether it is one or multiple.
- Array fields for size if is image
myfile_WIDTH
andmyfile_HEIGHT
JuNe BackServer combines these fields into the same object{name: FileName, type: Content-Type, size: FileSize, width: ImageWidth, height: ImageHeight}
Upload large files
Files are sent in 2 Mb-blocks with PUT method.
Each block is sent to URL
data-jpau-action
oraction
from input form.Add dataset
data-jpau-upload="large"
in file input<input type="file" name="myfile" data-jpau-upload="large">
Set maximum Kb size in
this.main.data.uploadLargeMax
recommended51200
50 Mb (default0
for no limit).Set
uploadLargeMsg
language code if different value toMaximum file size
This input can´t be required.
You can add the dataset
data-jpau-upload-id
or if not a random number will be generated withthis.genId()
So the server can identify parts from the same request and section.upload Id name is the name of the input field concatenated with number file,
myvar
for first,myvar_1
for second,myvar_2
for third...Variables sent to server using upload Id name and sufixed with _ID, _RND, _NAME, _TYPE, _SIZE, _WIDTH, _HEIGHT, _PATH, _PART, _PARTS: myvar_ID: Id from
data-jpau-upload-id
maybe random or a section code, to avoid different concatenations from several requests. myvar_RND: Unique randon number. myvar_NAME: file name. myvar_TYPE: file content-type. myvar_SIZE: file size. myvar_WIDTH: image width (if file is image). myvar_HEIGHT: image height (if file is image). myvar_PATH: file path (webkitRelativePath: relative path to selected directory). myvar_PART: file part (0, 1, 2, 3...). myvar_PARTS: file total part (0, 1, 2, 3...). myvar: file content part (raw).Server must concatenate each file request, during the process must use _ID and _RND to avoid different sources.
Why 2 random codes? ID maybe used to identificate a section in your project (1 for user files, 2 for user images...), and RND is a unique Id to ensure differentiate the same file while uploading.
Try JuNe BackServer - Is designed to accept this kind of files, join the chunks, and return the file.
Drag & Drop or clipboard
To allow adding files via Drag & Drop or clipboard, just add data-jpau-upload-target
with the id
of the file input target, then the element can accept Drag & Drop, paste image from clipboard, or paste files (only Chromium browsers).
If <input type="file" id="myfile" name="myfile" data-jpau-upload="large">
Then this div accepts drag and paste: <div data-jpau-upload-target="myfile">Drop / Paste here</div>
Change drag over default (#cfc) background color in data.main.dragColor
Drop and paste it´s for large files due these events contain the files and must be sent at the same time.
Global control
Files added or removed in object this.main.files
for a whole global/general control, with:
- Key:
ID_RND_NAME
ID from myvar_ID, RND from myvar_RND and NAME from upload Id name - Value:
{name: FileName, size: FileSize, upload: UploadedSize}
A key for totals is updated in each file operation {size: TotalSize, upload; TotalUploaded, percent: PercentUploaded}
this.main.files.total.size
total pending files size.
this.main.files.total.upload
total already uploaded.
this.main.files.total.percent
percent total already uploaded.
You can use this.sizeUnit(integer)
to show sizes in byte metric unit (Kb, Mb...).
ℹ️ Show a loader
Loader in a HTMLElement with initial style.visibility: hidden
and store the id string in main.data.loader
Then it will be shown on first large upload file, and hidden when finished all upload files calling this.idle(bool)
Use accept
attribute to limit file type
This limitation will be use on files selection.
Only images:
<input type="file" name="myimage" accept="image/*" data-jpau-upload="large">
Only PDF:
<input type="file" name="mypdf" accept="application/pdf" data-jpau-upload="large">
Only Word:
<input type="file" name="mypdf" accept=".doc,.docx,application/msword" data-jpau-upload="large">
Use capture
attribute to read from device camera or microphone
user: user facing or front facing camera and or microphone. environment: outer facing or back facing camera and or microphone.
Image:
<input type="file" id="myimage" name="myimage" capture="user" accept="image/*" data-jpau-upload="small">
Audio:
<input type="file" name="myaudio" name="myaudio" capture="user" accept="audio/*" data-jpau-upload="small">
Video:
<input type="file" id="myvideo" name="myvideo" capture="environment" accept="video/*" data-jpau-upload="large"
And use capture attribute to show a button to activate camera/microphone (add ´display: none´ to each input above):
<button *disabled="!this.e('myimage').capture" @click="this.e('myimage').click()">📷</button>
<button *disabled="!this.e('myaudio').capture" @click="this.e('myaudio').click()">🎤</button>
<button *disabled="!this.e('myvideo').capture" @click="this.e('myvideo').click()">📹</button>
If you need more functionality
- Add function onUpload to funcs in module, is triggered on select files, receives event parameter and must return boolean, true to continue or false to cancel upload.
export const funcs = {
onUpload: (e) => {
if(e.target) {
alert('Files: ' + e.target.files.length);
alert('File size: ' + e.target.files[0].size);
return true;
}
if(e.dataTransfer) {
alert('Drag & Drop, files: ' + e.dataTransfer.files.length);
return false;
}
}
};
- Add function onPreview to funcs in module, is triggered on preview added to DOM, receives container Id HTMLElement created (from template cloned), and upload Id name.
export const funcs = {
onPreview: (container, idName) => {
// Preview container created: this.e(container);
}
};
- Add function onUploadEnd to funcs in module, is triggered on upload ends, receives upload Id name.
export const funcs = {
onUploadEnd: (idName) => {
alert(`Finished ${idName}`);
}
};
- Add function onUploading to funcs in module, is triggered on uploading, receives current File object, input name, upload Id, part and parts.
export const funcs = {
onUploading: (cfile, iname, id, part, parts) => {
}
};
Rescale images before upload
- You can send images as is or rescale size for faster upload.
- Add desired size with
data-jpau-resize
in format (width)x(height), sample1280x720
Also adddata-jpau-upload
for small or large andaccept
for images:
<input type="file" name="myimage" data-jpau-upload="small" data-jpau-resize="1280x720" accept="image/*">
Preview and progress loading
- You can show a progress or information while loading and a preview of the image when is selected.
- Due there may be several images in the same input a template is required, so you can custom the design you want.
Create a template with an
id
as explained below:<template id="mytemplate"></template>
Create a
<div>
with anid
at the place you want to show the preview / file information, use the datasetdata-jpau-template
to specify the id of the template:<div id="mypreview" data-jpau-template="mytemplate"></div>
Add to the file input or drop zone the dataset
data-jpau-preview
with the id of the preview:<input type="file" name="myfile" data-jpau-upload="small" data-jpau-preview="mypreview">
- Template content will be duplicated according each file loaded.
Creating the template
Create a
<template>
with an id with the same name as div previewdata-jpau-template
Add all optional HTMLElement with dataset
data-jpau-element
with the element definition (and you can see the attribute changed): progress* and time* only for large not for small.- img: for show the image (src)
- cancel: HTMLElement for cancel upload (click)
- name: file name (innerText).
- size: file size using
this.sizeUnit
(innerText). - icon: Font Awesome class for icon, it´s added so you could have
fa-2xl
(className). - error: if error occurs (innerText).
- progress: progress bar (value).
- progressWidth: From 0% to 100% if you prefer to create your custom progress bar (style.width).
- progressStr: progress number (innerText).
- timeElapsed: time elapsed in format mm:ss (innerText).
- timeEstimated: time estimated in format mm:ss (innerText).
And
data-jpau-element-show
for show or hide until load complete:- loading: show while loading, when finished hide.
- finish: hide while loading, when finished show.
- error: show if error or abort occurs.
<template id="mytemplate">
<img data-jpau-element="img"><br>
<i data-jpau-element="icon"></i>
Name: <span data-jpau-element="name"></span><br>
Size: <span data-jpau-element="size"></span><br>
<div data-jpau-element-show="loading">
<progress data-jpau-element="progress" max="100"></progress>
<span data-jpau-element="abort" class="cp">╳</span><br>
<span data-jpau-element="timeElapsed"></span> / <span data-jpau-element="timeEstimated"></span>
</div>
<div data-jpau-element-show="finish">Ok</div>
<div data-jpau-element-show="error" style="color: red"></div>
</template>
Background modules
It´s an exclusive feature of JuNe PaulaJS that allows the current page (module) to be sent to the background, and to keep running when a new page (module) is loaded, and if it is reloaded again (for example selecting the link by the user), then the page (module) will be shown again in the foreground and without reloading the file again.
You can load another module (page) and the current one stays in background, then if the route shows it, it returns to foreground keeping all the status. By example in files upload modules/sections.
Module name is the file name, so don´t use same file names in different folders.
Do not abuse background modules to avoid browser memory problems.
⚠️ Important If you decide to create modules to send to background, you can´t repeat any Id in the whole project, be sure to use different names so as not to interfere with other modules.
To define a module for background, set background="true"
in route <jpau-route path="/sample" module="sample" background="true"></jpau-route>
You must use this.bgmod[module].data
to access data variables in background instead this.data, and this.bgmod[module].funcs.FUNCTION
to call module functions.
Optional function this.funcs.onVisible(status)
is called with the status boolean parameter to indicate visible (true) or sent to background (false).
Social Network share links / icons (for public projects)
To create share buttons getting links with this.socialShare([title[, url]])
- title parameter for title or default
document.title
- url parameter for URL or default
window.location
Returns an object with icon object for Font Awesome class, and link for share link.
let share = this->socialShare();
`<a href="${share.facebook.link}" target="_blank" title="Share with Facebook"><i class="${share.facebook.icon}"></i></a>`
- Available Social Networks: Facebook, WhatsApp, Twitter, Telegram, LinkedIn, Pinterest.
Speech recognition
If the browser supports speech recognition (only HTTPS), you can:
- Create voice commands and activate options as if you were clicking on them.
- Transcribe from voice to a text box or variable.
this.voiceLang()
initializes the language to be used, if no parameter is specified it will use the language set if languages have been declared in main file, or it will get the browser default language.
Forces a language with its code this.voiceLang('en-US')
You must create a button and either disable it or make it visible depending on whether the browser supports recognition using the this.voiceOpts()
function.
<button *disabled="!this.voiceOpts().speech" @click="this.funcs.speechRecog()">🎤 Speak here</button>
this.voiceOpts()
returns an object with 2 values:
- speech boolean if the browser supports recognition.
- voices array with all the voices supported by the browser (for synthesis).
Starts speech recognition with this.speech(callAlways, function, noProcess)
all parameters are optional:
- callAlways boolean indicating that function should always be called, even if a voice command has been found.
- function function to be called if the voice command is not found, or if callAlways is true, receives a parameter with the text.
- noProcess boolean for do not process voice commands.
To set voice commands, add data-jpau-speech
attribute with the text to be recognized:
<a href="/mypage" data-jpau-speech="My page" is="jpau-link">My page</a>
<input type="reset" value="Cancel" data-jpau-speech="Cancel">
So when you speak into the microphone, you can navigate to a page or perform a button action.
export const funcs = {
toText: function(v) { // Set voice value to a HTMLInputElement
this.e('txtSpeech').value = v;
},
speechRecog1: function() { // Initializes speech with voice commands else call toText
this.voiceLang();
this.speech(false, this.funcs.toText);
},
speechRecog2: function() { // Initializes speech with voice commands and call toText
this.voiceLang();
this.speech(true, this.funcs.toText);
},
speechRecog3: function() { // Initializes speech without voice commands and call toText
this.voiceLang();
this.speech(true, this.funcs.toText, true);
}
};
Synthesis of human speech 🗣 Make your program speak
First, set the language with this.voiceLang()
(as explained in speech recognition).
Optionally you can set a voice, listing from this.voiceOpts().voices
that returns an array with name and lang, so you could make a HTML select.
Sample to show in browser console:
this.voiceOpts().voices.forEach(e => console.log(`Name: ${e.name} - Language: ${e.lang}`));
Set voice element object in this.voiceLang(false, voice)
second parameter, and language to false, to use set language or browser language.
Start a speech with speak(obj, text, speaker, play, pause)
- obj HTMLElement where user clicks to start speech
- text string text to speech
- speaker HTML to show on the object indicating that it can be played 🔊
- play HTML to show on the object when playing ▶️
- pause HTML to show on the object when paused ⏸️
export const funcs = {
speakText: function(obj, txt) {
this.voiceLang();
this.speak(this.e(obj), this.e(txt).value, '🔊', '▶️', '⏸️');
},
};
export const html = `
<textarea id="txtSpeak">This is a sample text</textarea>
<button id="bttSpeak" @click="this.funcs.speakText('bttSpeak', 'txtSpeak')">🔊</button>
`;
With FontAwesome
export const funcs = {
speakText: function(obj, txt) {
this.voiceLang();
this.speak(this.e(obj), this.e(txt).value, '<i class="fa-solid fa-volume-high fa-fw"></i>', '<i class="fa-solid fa-play fa-fw"></i>', '<i class="fa-solid fa-pause fa-fw"></i>');
},
};
export const html = `
<textarea id="txtSpeak">This is a sample text</textarea>
<button id="bttSpeak" @click="this.funcs.speakText('bttSpeak', 'txtSpeak')"><i class="fa-solid fa-volume-high fa-fw"></i></button>
`;
Attributes summary
| Name | Value | Definition | | --- | --- | --- | | data-jpau-if | JavaScript Code | If command | | data-jpau-for | JavaScript Code | For command | | data-jpau-speech | Text | For commands speech recognition | | data-jpau-upload | small/large | File input upload type | | data-jpau-action | URL | Action URL for upload large files with PUT (or form action) | | data-jpau-upload-id | Id code | Unique Id for upload same file | | data-jpau-upload-target | Id HTMLElement | Target HTMLElement from a Drag & Drop / clipboard paste HTMLElement | | data-jpau-resize | widthxheight | Resize input images | | data-jpau-template | Id HTMLElement | Template HTMLElement for uploads | | data-jpau-preview | Id HTMLElement | Preview HTMLElement for uploads | | data-jpau-element | Code | Preview template element (img, cancel, name, size, icon...) | | data-jpau-show | Code | Preview template allow show (loading, finish ,error) |
Functions summary
this or june_pau according scope. Have a look to functions to help you to develop your project as prepHTML, html2text, htmlEntities, sizeUnit, fileIcon, socialShare...
| Function | Param | Definition |
| --- | --- | --- |
| this.prepHTML(element) | parentNode | Prepare HTML if new added to DOM (document for all) |
| this.load(object) | main | To configure the object directly instead of importing module |
| this.importMod(string ∣ object[, bg]) | filename ∣ module, bg | Import module with filename or using object directly, and background module |
| this.link(string) | URL | Navigate to route (sample /users/23
) and assign params/getparams |
| this.setLang(string) | lang | Set language (as window.navigator.language) |
| this.genId() | - | Generate an unique Id |
| this.e(string) | id | Return document.getElementById (HTMLElement) |
| this.updElm(element) | HTMLElement | Update element |
| this.updHTML() | - | Update all |
| this.text(string) | key | Return text code in current language |
| this.html2text(string) | html | Remove HTML tags and returns only text |
| this.htmlEntities(string) | html | Convert applicable characters to HTML entities (& < > ") |
| this.sizeUnit(integer) | bytes | Returns string with format in byte metric unit 1.95 Kb
for 2000
|
| this.idle(bool) | show | Show or hide this.main.data.loader
if exists |
| this.shake(element) | HTMLElement | Shake effect to element (5 ms) |
| this.stopp() | - | Event prevent default and stop propagation |
| this.windowShow(string[, array]) | html, options | Show modal window |
| this.windowHide() | - | Hide last modal window |
| this.windowDel(function[, text]) | function if confirm, optional text | Predefined delete ask window |
| this.toastShow(string[, array]) | html, options | Show toast notification |
| this.toastHide() | - | Hide last toast notification |
| this.sendRequest(string[, string, array, object]) | URL, method, data, getparams | Async send requests (CRUD operations) |
| this.fdata(string[, bool]) | id, check | Create FormData from form inputs, check validation |
| this.captcha(formdata, string) | formdata, URL | Add captcha to formdata from URL endpoint |
| this.fileIcon(string) | filename | Returns file type Font Awesome icon |
| this.socialShare([string[, string]]) | title, URL | Returns array for Social Network share buttons |
| this.voiceLang([string[, voice]]) | lang, voice | Set optional language for speech/synthesis, and voice |
| this.voiceOpts() | - | Returns array if browser supports speech recognition (speech), and voices for synthesis |
| this.speech([bool, function, bool]) | callAlways, function, noProcess | Starts speech recognition |
| this.speak(element, string, string, string, string) | HTMLElement, text, speaker, play, pause | Speak speech |
Life Cycle
st=>start: Start
op1=>operation: main.funcs.onLoad
op2=>operation: module.funcs.onLoad
op3=>start: Performed HTML
op4=>operation: module.funcs.onVisible(true)
op5=>operation: main.funcs.onMount [once]
op6=>operation: module.funcs.onMount
st->op1->op2->op3->op4->op5->op6
To work with JuNe PaulaJS
You can run your project without a HTTP server (development), but to run a PaulaJS project you can use this HTTP server: JuNe WebServer that is integrated and reloads page on new changes, so you can launch it opening a terminal in your project folder and type npm run webserver
and it will open your default browser, if you want a specific file use index parameter npm run webserver -index myfile.html
Launch Backend for examples
If you see examples that send requests to a backend as upload files, open another terminal and run npm run backend
to see the information for each request.
Build project
Build project it´s not neccesary because you can copy your files and PaulaJS file for distribution.
If you want to offuscate your code or facilitate the installation copying all in dist/ folder.
First install Uglify-JS globally to reduce files size and make it difficult to view your source code.
npm install -g uglify-js
So if you have installed JuNe PaulaJS globally run paulajs build
Or if it´s local npm run build
Server production configuration
- Required to allow paths
/myparam1/myparam2/myparam3
- Considered
/index.html
as main file:
⬢ Nginx
server {
...
location / {
try_files $uri $uri/ /index.html?$args;
}
...
}
🪶 Apache .htaccess
# BEGIN JuNe PaulaJS
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
# END JuNe PaulaJS
Check out JuNeDNS Frontend made with JuNe PaulaJS
https://github.com/EduardoRuizM/junedns-frontend
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
Files
| File | Description | | --- | --- | | dev/ | Development or production programs | | ∟ backend.js | Backend for JuNe PaulaJS test purposes examples | | ∟ webserver.js | JuNe WebServer for run in development or production mode | | ∟ paulajs.js | Utility for create template or build project | | ∟ backserver.js | JuNe BackServer | | ∟⎽⎽⎽⎽ template/ | Files to create a PaulaJS project | | examples/ | JuNe PaulaJS examples and files (also used for project templates creation) | | june-paula.js | JuNe PaulaJS file | | june-paula.min.js | JuNe PaulaJS minified file | | logo.png | JuNe PaulaJS logo | | package.json | JuNe PaulaJS package.json | | README.md | Full and detailed JuNe PaulaJS documentation |
JuNe Development Ecosystem
Everything you need to develop your project:
Backend
- JuNe BackServer With request routing, tokens, file upload, send Emails, WebSockets, SSE and captcha.
- JuNe WebServer Web server with HMR.
Frontend
- JuNe PaulaJS Powerful JavaScript framework
- JuNe CSS Full responsive CSS library with icons.