npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

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

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.

27 Kb NPM Downloads

Author

Eduardo Ruiz <[email protected]>

JuNe / JUst NEeded Philosophy

  1. Source code using less code as possible So you can understand code and find bugs easier.
  2. Few and optimized lines is better Elegant design.
  3. Avoid external dependencies abuse/bloated, and possible third-party bugs Less files size, better and faster to the interpreter.
  4. Clear and useful documentation with examples and without verbose Get to the point.
  5. Avoid showing unsolicited popups, notifications or messages in frontend For better User eXperience.
  6. Simple UI, without many menus/options and with few clicks to get to sites.
  7. 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) or june_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 and default: 'en' then is selected if language is en-AU or en-* (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') or june_pau.setLang('code') sample this.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 and cancel exist in main.js
  • 🎨 Be careful if you replace CSS, copy internal and modify it.
  • ℹ️ You can set globally winCSSbg and winCSSfg in this.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.js export 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 to data in main.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
  • 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.

  1. Create a GET endpoint to receive a key to add to the form:
app.get('/captcha', (req, res) => {
  req.content.k = app.captcha();
});
  1. 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);
}
  1. 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) or multiple (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 recomended 5120 5 Mb (default 0 for no limit).
  • Set uploadSmallMsg language code if different value to Maximum file size
  • File(s) are sent as array whether it is one or multiple.
  • Array fields for size if is image myfile_WIDTH and myfile_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 or action 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 recommended 51200 50 Mb (default 0 for no limit).

  • Set uploadLargeMsg language code if different value to Maximum 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 with this.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), sample 1280x720 Also add data-jpau-upload for small or large and accept 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.
  1. Create a template with an id as explained below: <template id="mytemplate"></template>

  2. Create a <div> with an id at the place you want to show the preview / file information, use the dataset data-jpau-template to specify the id of the template: <div id="mypreview" data-jpau-template="mytemplate"></div>

  3. 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 preview data-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">&#9587;</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

Frontend