Lightweight GIS toolbar developed for OpenLayers

Overview

OpenLayers Toolbar - OLTB

Lightweight GIS toolbar developed for OpenLayers 6.13.0. The toolbar can be filled with any number of tools and can be used in both horizontal and vertical mode and is available in both light and dark theme.

Current version 1.0.0-beta1 - Demo

Screenshot

Screenshot of the toolbar in action

Table of contents

  1. Branches
  2. Report a bug
  3. Request a new feature
  4. Get started
  5. Browser support
  6. Architecture model
  7. About the code
    1. HTML
    2. SCSS
    3. Colors
    4. JavaScript
    5. Callback functions and constructor parameters
    6. Store data locally instead of via API
    7. Hidden tools
    8. Shortcut keys
    9. Custom projections
    10. Dialogs
    11. Modal
    12. Toast
    13. Context menu
    14. State Management
    15. Debug tool
    16. OLTB namespace
  8. External GitHub projects
  9. Maps used in the demo
  10. License
  11. Author

Branches

There are two branches in the repository. The main branch which contains the releases and the develop branch which also contains all upcomming commits for the next release. So if you want to test the latest features go checkout the develop branch. The gh-pages branch is just for the latest build for the demo-page.

Report a bug

If you find a bug in the latest release on the main branch start by:

  1. Check if the bug exists on the develop branch.
  2. Check if there is an open issue.
  3. If no issue is open, please create one and tag @qulle.

Use the existing labels to tag your issue.

Request a new feature

If you are missing a feature in the latest release on the main branch start by:

  1. Check if the feature already has been added on the develop branch.
  2. Check if there is an open request in the issue tracker.
  3. If no issue is open, please create one and tag @qulle.

Use the existing labels to tag your issue.

Get started

The dev-environment uses npm so you need to have Node.js installed.

Clone the repo.

$ git clone https://github.com/qulle/oltb.git

Install all dependencies from package.json.

$ npm install

Start the Parcel server.

$ npm start

Make build for distribution.

$ npm run build

Use the following command to remove dist directory. Uses rm -rf dist/

$ npm run clean

Check for dependency updates.

$ npm outdated

Install dependency updates.

$ npm update --save

Note that from npm version 7.0.0 the command $ npm update does not longer update the package.json file. From npm version 8.3.2 the command to run is $ npm update --save or to always apply the save option add save=true to the .npmrc file.

Browser support

Desktop browser Version Mobile browser Version
Mozilla Firefox 97.0.1 Android Google Chrome 98.0.4758.87
Google Chrome 98.0.4758.102 Apple Safari 13.3
Microsoft Edge 98.0.1108.56 Apple Google Chrome 90.0.4430.216

IE is not supported, it's time to move on.

Architecture model

Architecture model Architecture model showing my intentions of how an application could be built to keep the responsibility of each part separated and not end up with application specific code inside the toolbar.

About the code

Below is the basic HTML and JavaScript structure used in the project. For a complete example of how to set up the code go to map.js in the src/js directory.

HTML

OLTB - Toolbar for OpenLayers
">
>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=0" />
    <link rel="stylesheet" href="/scss/map.scss">
    <link rel="icon" type="image/x-icon" href="/favicon.ico" />
    <title>OLTB - Toolbar for OpenLayerstitle>
head>
<body>
    <div id="oltb">div>

    <div id="map" tabindex="0">div>

    <script src="/js/map.js">script>
body> html>

The toolbar is vertical by default, add class row to change direction. The user can change the direction using the tool DirectionToggle

">
<div id="oltb" class="row">div>

The toolbar theme is light by default, add class dark to change theme. The user can change the theme using the tool ThemeToggle

">
<div id="oltb" class="dark">div>

SCSS

SCSS and HTML is written with BEM (ish) naming convention.

.block {}
.block__element {}
.block__element--modifier {}

Colors

Blue #D7E3FA - $color-blue-light #4A86B8 - $color-blue-mid #0166A5 - $color-blue-dark
Green #ABFAF2 - $color-green-light #67C7BD - $color-green-mid #3CAEA3 - $color-green-dark
Yellow #FFEBAE - $color-yellow-light #FBDD83 - $color-yellow-mid #FFCC33 - $color-yellow-dark
Orange #FFD5AC - $color-orange-light #FCBE80 - $color-orange-mid #F79A5A - $color-orange-dark
Red #FDB5B4 - $color-red-light #F38C8A - $color-red-mid #ED6663 - $color-red-dark
Gray #D3D9E6 - $color-gray-light #8C94A4 - $color-gray-mid #3B4352 - $color-gray-dark

JavaScript

The JavaScript tool-buttons are located in the src/js/modules/tools directory. Every tool has its own class and extend the Control-class from OpenLayers.

class Coordinates extends Control {}

When using the custom tools, all that is needed is to import the module(s) you want to have in your toolbar

import HiddenMarker from './modules/tools/HiddenTools/Marker';
import HiddenMapNavigation from './modules/tools/HiddenTools/MapNavigation';
import Home from './modules/tools/Home';
import ZoomIn from './modules/tools/ZoomIn';
import ZoomOut from './modules/tools/ZoomOut';
import FullScreen from './modules/tools/FullScreen';
import ExportPNG from './modules/tools/ExportPNG';
import DrawTool from './modules/tools/DrawTool';
import MeasureTool from './modules/tools/MeasureTool';
import Edit from './modules/tools/Edit';
import Bookmark from './modules/tools/Bookmark';
import Layers from './modules/tools/Layers';
import SplitView from './modules/tools/SplitView';
import Magnify from './modules/tools/Magnify';
import ResetNorth from './modules/tools/ResetNorth';
import Coordinates from './modules/tools/Coordinates';
import MyLocation from './modules/tools/MyLocation';
import ImportGeoJSON from './modules/tools/ImportGeoJSON';
import ScaleLineTool from './modules/tools/ScaleLineTool';
import Refresh from './modules/tools/Refresh';
import ThemeToggle from './modules/tools/ThemeToggle';
import DirectionToggle from './modules/tools/DirectionToggle';
import Info from './modules/tools/Info';
import Help from './modules/tools/Help';
import Settings from './modules/tools/Settings';
import DebugInfo from './modules/tools/DebugInfo';
import HiddenAbout from './modules/tools/HiddenTools/About';

and then calling the constructor in the array inside the extend method. The tools are added to the toolbar in the order you include them in this array.

controls: defaultControls({
    zoom: false, 
    rotate: false, 
    attribution: SettingsManager.getSetting('showAttributions')
}).extend([
    new HiddenMarker(),
    new HiddenMapNavigation(),
    new Home(),
    new ZoomIn(),
    new ZoomOut(),
    new FullScreen(),
    new ExportPNG(),
    new DrawTool(),
    new MeasureTool(),
    new Edit(),
    new Bookmark(),
    new Layers(),
    new SplitView(),
    new Magnify(),
    new ResetNorth(),
    new Coordinates(),
    new MyLocation(),
    new ImportGeoJSON(),
    new ScaleLineTool(),
    new Refresh(),
    new ThemeToggle(),
    new DirectionToggle(),
    new Info(),
    new Help(),
    new Settings(),
    new DebugInfo()
    new HiddenAbout()
])

Callback functions and constructor parameters

Tools that in any way change the map, create, modify or delete objects have several different callback functions that return data to you.

controls: defaultControls({
    zoom: false, 
    rotate: false, 
    attribution: SettingsManager.getSetting('showAttributions')
}).extend([
    new HiddenMarker({
        added: function(marker) {
            console.log('Marker added', marker);
        },
        removed: function(marker) {
            console.log('Marker removed', marker);
        },
        edited: function(marker) {
            console.log('Marker edited', marker);
        }
    }),
    new HiddenMapNavigation({
        focusZoom: 10
    }),
    new Home({
        lon: 18.6435, 
        lat: 60.1282, 
        zoom: 4,
        home: function() {
            console.log('Map zoomed home');
        }
    }),
    new ZoomIn({
        zoomed: function() {
            console.log('Zoomed in');
        }
    }),
    new ZoomOut({
        zoomed: function() {
            console.log('Zoomed out');
        }
    }),
    new FullScreen({
        enter: function(event) {
            console.log('Enter fullscreen mode', event);
        },
        leave: function(event) {
            console.log('Leave fullscreen mode', event);
        }
    }),
    new ExportPNG({
        exported: function() {
            console.log('Map exported as png');
        }
    }),
    new DrawTool({
        start: function(event) {
            console.log('Draw Start');
        },
        end: function(event) {
            console.log('Draw end', event.feature);
        },
        abort: function(event) {
            console.log('Draw abort');
        },
        error: function(event) {
            console.log('Draw error');
        }
    }),
    new MeasureTool({
        start: function(event) {
            console.log('Measure Start');
        },
        end: function(event) {
            console.log('Measure end');
        },
        abort: function(event) {
            console.log('Measure abort');
        },
        error: function(event) {
            console.log('Measure error');
        }
    }),
    new Edit({
        selectadd: function(event) {
             console.log('Selected feature');
        },
        selectremove: function(event) {
            console.log('Deselected feature');
        },
        modifystart: function(event) {
            console.log('Modify start');
        },
        modifyend: function(event) {
            console.log('Modify end');
        },
        translatestart: function(event) {
            console.log('Translate start');
        },
        translateend: function(event) {
            console.log('Translate end');
        },
        removedfeature: function(feature) {
            console.log('Removed feature', feature);
        }
    }),
    new Bookmark({
        storeDataInLocalStorage: true,
        added: function(bookmark) {
            console.log('Bookmark added', bookmark);
        },
        removed: function(bookmark) {
            console.log('Bookmark removed', bookmark);
        },
        zoomedTo: function(bookmark) {
            console.log('Zoomed to bookmark', bookmark);
        },
        cleared: function() {
            console.log('Bookmarks cleared');
        }
    }),
    new Layers({
        mapLayerAdded: function(layerObject) {
            console.log('Map layer added', layerObject);
        },
        mapLayerRemoved: function(layerObject) {
            console.log('Map layer removed', layerObject);
        },
        mapLayerRenamed: function(layerObject) {
            console.log('Map layer renamed', layerObject);
        },
        mapLayerVisibilityChanged: function(layerObject) {
            console.log('Map layer visibility change', layerObject);
        },
        featureLayerAdded: function(layerObject) {
            console.log('Feature layer added', layerObject);
        },
        featureLayerRemoved: function(layerObject) {
            console.log('Feature layer added', layerObject);
        },
        featureLayerRenamed: function(layerObject) {
            console.log('Feature layer renamed', layerObject);
        },
        featureLayerVisibilityChanged: function(layerObject) {
            console.log('Feature layer visibility change', layerObject);
        },
        featureLayerDownloaded: function(layerObject) {
            console.log('Feature layer downloaded', layerObject);
        }
    }),
    new SplitView(),
    new Magnify(),
    new ResetNorth({
        reset: function() {
            console.log('Map north reset');
        }
    }),
    new Coordinates({
        clicked: function(coordinates) {
            console.log('You clicked', coordinates);
        }
    }),
    new MyLocation({
        location: function(location) {
            console.log('Location', location);
        },
        error: function(error) {
            console.log('Location error', error);
        }
    }),
    new ImportGeoJSON({
        imported: function(features) {
            console.log('Imported', features);
        },
        error: function(filename, error) {
            console.log('Error when importing geojson file:', filename, error);
        }
    }),
    new ScaleLineTool({
        units: 'metric'
    }),
    new Refresh(),
    new ThemeToggle({
        changed: function(theme) {
            console.log('Theme changed to', theme);
        }
    }),
    new DirectionToggle({
        changed: function(direction) {
            console.log('Direction changed to', direction);
        }
    }),
    new Info({
        title: 'Hey!', 
        content: '

This is a modal window, here you can place some text about your application or links to external resources.

'
}), new Help({ url: 'https://github.com/qulle/oltb', target: '_blank' }), new Settings({ cleared: function() { console.log('Settings cleared'); } }), new DebugInfo({ showWhenGetParameter: true }), new HiddenAbout() ])

Store data locally instead of via API

Tools that create objects at runtime, for example the BookmarkTool, LayerTool etc. returns data via the callback functions. There is also the possibility for these tools to store the created objects in localStorage instead. This is done by setting the constructor parameter storeDataInLocalStorage: true. This can be useful if you want to create a map-viewer that can persists data between page load but have no need for an additionall long-term storage via API.

Note At the moment only the BookmarkTool has this feature fully implemented.

Hidden tools

Tools refered to as hidden tools are tools that only add functionality via the context menu. The hidden tools are used to enable the same type of callback functions in the constructor as every other tool.

Shortcut keys

All tools have a shortcut key for ease of use and speeds up the handling of the toolbar and map. The shortcut key is displayed in the tooltip on the corresponding tool.

Custom projections

You can define custom projections in the file ./modules/epsg/Projections. This file is imported in the main map.js file and your projection can be used throughout the project. If you want to change the default proejction used, there is a general config file ./modules/core/Config.js where you can change that.

Dialogs

To use the custom dialogs in the map, include the following module. All the dialogs uses trap focus and circles the tab-key to always stay in the opened dialog.

import Dialog from './modules/common/Dialog';

To change the text in the confirm button, add the property confirmText: 'Your text'

Dialog.alert({text: 'This is a custom alert message'});

To have a 'success' confirm dialog, add the property confirmClass: Dialog.Success. To change the text of the confirm button, add the property confirmText: 'Your text'.

Dialog.confirm({
    text: 'Do you want to delete this layer?',
    onConfirm: function() {
        console.log('Confirm button clicked');
    },
    onCancel: function() {
        console.log('Cancel button clicked');
    }
});

To have a 'danger' prompt dialog, add the property confirmClass: Dialog.Danger. To change the text of the confirm button, add the property confirmText: 'Your text'.

Dialog.prompt({
    text: 'Change name of layer',
    value: 'Current name',
    onConfirm: function(result) {
        console.log(result);
    },
    onCancel: function() {
        console.log('Cancel button clicked');
    }
});

The dialogs could be extended with more options, but i want to keep the configuration as simple as possible.

Modal

To use the custom modal in the map, include the following module.

import Modal from './modules/common/Modal';

The modal uses the trap focus to circle the tab-key.

Modal.create({
    title: 'Title', 
    content: 'Text/HTML content'
});

Toast

To use the custom toasts in the map, include the following module.

import Toast from './modules/common/Toast';

There are four types of toast messages.

Toast.info({text: 'Item were removed from collection'});
Toast.warning({text: 'Request took longer than expected'});
Toast.success({text: 'Changes were saved to database'});
Toast.error({text: 'Failed to contact database'});

To remove the toast after a specific time (ms), add the autoremove property.

Toast.success({text: 'Changes were saved to database', autoremove: 3000});

To close the toast from the code, store a reference to the toast and then call the remove method. The attribute clickToClose is set to false, this means that the user can't click on the toast to close it.

const loadingToast = Toast.info({text: 'Trying to find your location...', clickToClose: false});

loadingToast.remove();

To add a loading spinner in the toast. Add the spinner property.

const loadingToast = Toast.info({text: 'Trying to find your location...', clickToClose: false, spinner: true});

Context menu

To use the context menu start by importing the following module.

import ContextMenu from './modules/common/ContextMenu';

To create a context menu call the constructor and give a unique name as the first argument and a selector to trigger the menu. The context menu class extends the Control class from OpenLayers.

map.addControl(new ContextMenu({
    name: 'main.map.context.menu', 
    selector: '#map canvas'
});

To add items to the context menu use the function addContextMenuItem and give the name that matches the context menu aswell as the name/label of the item, the icon and a function to call when the item is clicked.

addContextMenuItem('main.map.context.menu', {icon: '
   
    ...
   ', name: 'Zoom home', fn: this.handleResetToHome.bind(this)});

The callback function recieves a references to the map, the clicked coordinates and the target element (the canvas).

addContextMenuItem('main.map.context.menu', {icon: '
   
    ...
   ', name: 'Clear settings', fn: function(map, coordinates, target) {
    Dialog.confirm({
        text: 'Do you want to clear all settings?',
        onConfirm: function() {
            localStorage.clear();
        }
    });
}});

It is not important in what order the menu or its items are created. If no menu exist with the given name all menu items will be stored and added once the menu is created.

To insert a separator in the menu add an empty object.

addContextMenuItem('main.map.context.menu', {});

State Management

To use state management start by importing the following module.

import StateManager from './modules/core/Managers/StateManager';

State management is done through localStorage. First add a node name and an object to store default values.

const LOCAL_STORAGE_NODE_NAME = 'drawTool';
const LOCAL_STORAGE_PROPS = {
    collapsed: false,
    toolTypeIndex: 5,
    strokeColor: '#4A86B8',
    strokeWidth: 2,
    fillColor: '#FFFFFFFF'
};

These two nextcomming lines merges potential stored data into a runtime copy of the default properties located in LOCAL_STORAGE_PROPS. The spread operator is a really nice feature for this operation.

// Load potential stored data from localStorage
const loadedPropertiesFromLocalStorage = JSON.parse(StateManager.getStateObject(LOCAL_STORAGE_NODE_NAME)) || {};

// Merge the potential data replacing the default values
this.localStorage = {...LOCAL_STORAGE_PROPS, ...loadedPropertiesFromLocalStorage};

To update the state in localStorage, call the updateStateObject method and pass in the node name along with the updated state object.

StateManager.updateStateObject(LOCAL_STORAGE_NODE_NAME, JSON.stringify(this.localStorage));

Debug tool

To make debugging and diagnosting errors easier there is a tool named DebugInfo. This tool will gather information about the map such as zoomlevel, location, layers, rotation, projection etc and displays the information in a modal window. To hide the debug tool as default, add the parameter showWhenGetParameter: true and add the get parameter to the url /?debug=true to show the tool.

OLTB namespace

For some tools and features data is stored on the global window object. The name that is reserved for the toolbar is window.oltb. Data is stored in local storage under the key oltb-state.

All classes and id:s in the project are also prefixed with the namespace oltb.

External GitHub projects

  1. OpenLayers 6.13.0
  2. Tippy.js 6.3.7
  3. Bootstrap Icons
  4. A Color Picker 1.2.1
  5. Plain JS Slidetoggle 2.0.0

Maps used in the demo

  1. OpenStreetMap
  2. Maps.Stamen.Com
  3. ArcGIS World Topo

License

BSD-2-Clause License

Author

Qulle

Comments
  • Import vector layer

    Import vector layer

    Is the import vector layer fully baked at the moment ?

    It imports the file I have without errors but does not display anything. (The file then shows up in the layers). My suspicion is that it is there but the lines are not visible. File is from https://www.naturalearthdata.com/

    ne_50m_land.zip

    bug 
    opened by sl0burn 3
  • License

    License

    Hi @qulle the project looks very useful and nicely programmed. Currently the project does not have a license, which makes it hard to reuse it in other projects. Adding a license might attract more people to reuse your code.

    opened by JakobMiksch 2
  • New Tool - Toggle Graticule layer

    New Tool - Toggle Graticule layer

    This could be a layer that the user can toggle from the LayerTool. But to not make a tight dependency to the LayerTool i think this function deserves its own Toolbutton in the menu. Icon: https://icons.getbootstrap.com/icons/globe2/

    enhancement 
    opened by qulle 0
  • Beta3

    Beta3

    Small features:

    • [X] Stamen Terrain map
    • [X] Windbarb layer
    • [X] Highlight vector shape when hover
    • [X] Pan-and-zoom to MyLocation and open InfoWindow
    • [X] Support for thin scrollbars using the class .oltb-thin-scrollbars
    • [X] Subtracting intersections from drawn objects
    • [X] Shape functionality, Union, Difference, Intersection, SymDifference
    • [X] Updating color in the Edit Tool
    • [X] Re-activate activated tool after application was reloaded

    Refactoring:

    • [X] Comments
    • [X] Overall code quality
    • [X] MomentaryActivation function for non-persistant tools
    • [X] Module for creating feature tooltips
    • [X] Reduce dependency between DirectionTool and general Tooltips
    • [X] Directory naming, structure and hierarchy
    • [X] Create URLManager
    • [X] Create ToolManager, to remove global window.oltb
    • [X] Collapsed toolboxes, class method
    • [X] Consistent tool naming
    • [X] Add click callback to all tools that is fired when the main tool-button is clicked
    • [X] Consistent element ID-naming
    • [X] Module for shortcut keys, as it is now, it is difficult to get an overview of who is already busy
      • modules/helpers/constants/ShortcutKeys.js
    • [X] Module for ContextMenu instances
      • modules/helpers/constants/ContextMenus.js
    • [X] Module for feature properties
      • modules/helpers/constants/FeatureProperties.js
    • [X] Namespac (oltb) custom properties
    • [X] Module for custom event-names, minimize errors coming from text literals
      • modules/helpers/constants/Events.js
    • [X] Module for settings, minimize errors coming from text literals
      • modules/helpers/constants/Settings.js
    • [X] Store references directly on the object and not in a const variable first
    • [X] Consistent Fetch syntax
    • [X] Consistent arrow-function/then/catch syntax when one argument
    • [X] Create static Assert-class
    • [X] Convert all anonymous functions in constructor to class methods
    • [X] Make Uppercase CONST export/imports
    • [X] Make Uppercase CONST variables outside classes
    • [X] Use 4000ms as autoremove delay for Toast
    • [X] Copy epsg files from ProjectionTool branch
    • [X] Add pre defined projections to debug info window
    • [X] Solution for not creating two features for FeatureMarkers
    • [X] ScalieLine null declaration
    • [X] ModalWindow last element margin-button, remove classes from Notification modal
    • [X] Add accuracy and timeout properties to MyLocation constructor
    • [X] Make DrawingTool default white bg-color with alpha same as MeasureTool

    Bugs:

    • [X] Measure tooltips is not removed when deleting a selected feature using the Edit-tool
    • [X] MyLocation tool is triggered many times on button click and shortcut key
    • [X] Notification tool is triggered many times when shortcut key is used
    • [X] Info modal can be triggered many times when shortcut key is used
    • [X] Debug tool is triggered many times when shortcut key is used
    • [X] Unknown this.options in Contextmenu for Focus item
    • [X] Space between spinner and text in Toasts
    • [X] Close InfoWindow when a layer is removed
    • [X] Measure tooltips are not exported in PNG (not a bug actually). Items that are not draw in the map canvas (normal HTML elements, known as Overlays) will not be picked up in the export. A merge between the map Canvas and the Canvas generated by html2canvas lib will fix this.
    bug code improvement doing 
    opened by qulle 0
  • PR - General Refactoring

    PR - General Refactoring

    Refactored copy to clipboard to use navigator API instead of execCommand. Also updated settings name to follow the same naming convention as custom events, dots separating each word instead of camelcase.

    opened by qulle 0
  • Improve usability on touch devices

    Improve usability on touch devices

    On some clickable items a blue highlighted background/selection is show. This is annoying. This has nothing to do with accessibility so it should be safe to disable.

    enhancement 
    opened by qulle 0
  • Change branching strategy

    Change branching strategy

    Make the branching flow more simple. Don't stack finished features on hold in a develop branch. Use only one main branch and then use many feature branches. Make a PR from feature to main and then remove the feature branch. This is more in the line of CI/CD.

    documentation 
    opened by qulle 0
  • Bug in edit marker modal

    Bug in edit marker modal

    Due to a previous renaming of the result/response parameter from the marker modal, markers fails when they are edited.

    Simple fix, replace incorrect variable names.

    bug 
    opened by qulle 0
  • Refactor Tooltips

    Refactor Tooltips

    When using both the measure tool and coordinate tool the tooltips are not shown in a good way. Possible solution is to make a module responsible for tooltips. Tools can then register (push, pop) content to be displayed in the same tooltip.

    enhancement 
    opened by qulle 0
  • Create packages for use in third-party applications

    Create packages for use in third-party applications

    (1). Create Class to create new instance of the toolbar

    const toolbar = new OLTB({.....}); toolbar.setMap(map);

    (2). Create package (minified) or NPM package that can be included in other projects

    enhancement doing 
    opened by qulle 0
Releases(v1.0.0-beta2)
Owner
Daniel
Daniel
Lightweight Node.js isochrone map server

Galton Lightweight Node.js isochrone server. Build isochrones using OSRM, Turf and concaveman. Francis Galton is the author of the first known isochro

Urbica 266 Dec 17, 2022
OpenLayers

OpenLayers OpenLayers is a high-performance, feature-packed library for creating interactive maps on the web. It can display map tiles, vector data an

OpenLayers 9.7k Jan 7, 2023
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
A tooltip style toolbar jQuery plugin

Toolbar.js A jQuery plugin that creates tooltip style toolbars. Update Toolbar.js has been overhauled with new functionality, a completely new design

Paul Kinzett 2.3k Dec 18, 2022
Popline is an HTML5 Rich-Text-Editor Toolbar

popline Popline is a non-intrusive WYSIWYG editor that shows up only after selecting a piece of text on the page, inspired by popclip. Usage Load jQue

kenshin 1k Nov 4, 2022
An obsidian toolbar plugin, modified from the Cmenu plugin

obsidian-editing-toolbar Plugin Thanks to the cmenu plugin, which gave me a lot of inspiration, but this plugin has not been maintained for more than

null 321 Dec 29, 2022
This plugin allows you to stick toolbar for summernote editors.

Summernote Sticky Toolbar This is a simple plugin for Summernote that makes the toolbar sticky. preview.mp4 Installation Classic Include the plugin sc

İlyas Özkurt 6 Oct 5, 2022
EggyJS is a Javascript micro Library for simple, lightweight toast popups focused on being dependency-less, lightweight, quick and efficient.

EggyJS EggyJS is a Javascript micro Library for simple, lightweight toast popups. The goal of this library was to create something that meets the foll

Sam 10 Jan 8, 2023
Gmail-like client-side drafts and bit more. Plugin developed to save html forms data to LocalStorage to restore them after browser crashes, tabs closings and other disasters.

Sisyphus Plugin developed to save html forms data to LocalStorage to restore them after browser crashes, tabs closings and other disasters. Descriptio

Alexander Kaupanin 2k Dec 8, 2022
🏎 Open source racing game developed by everyone willing

?? Open source racing game developed by everyone willing

Poimandres 2k Dec 26, 2022
E-Commerce Application developed with nodejs and stored to mongodb.

E-Commerce Application This Application has been developed with nodejs and mongodb. Environment Variables Create a file named config.env in config dir

Abdullah Öztürk 13 Dec 23, 2021
A helper to send whatsapp without scheduling a contact, the project developed using TDD with Jest, Javascript classes, BotstrapVue and SweetAlert.

Project setup npm install Compiles and hot-reloads for development npm run serve Compiles and minifies for production npm run build Lints and fixes

Magascript 7 Sep 13, 2022
A game inspired by Go, developed using Phaser JS

Influence A game inspired by Go, developed using Phaser How to play Players take turns to select and color a tile. At the end of a turn, each tile wil

null 11 Aug 28, 2022
Pulsar Flex is a modern Apache Pulsar client for Node.js, developed to be independent of C++.

PulsarFlex Apache Pulsar® client for Node.js Report Bug · Request Feature About the project Features Usage Contributing About PulsarFlex is a modern A

null 43 Aug 19, 2022
HITB SECCONF EDU CTF 2021. Developed with ❤️ by Hackerdom team and HITB.

HITB SECCCONF EDU CTF 2021 SECCONF EDU CTF is an online international challenge in information security. Developed by Hackerdom team for HITB SECCONF

HITB+ CyberWeek 18 Sep 3, 2022
It is a discord (/) commands bot developed with Discord.js v13.

#Discord.js v13 Slash Bot #Discord Server ?? Setup Run the setup.bat file. Wait until the process is finished. Go to discord.dev page. Create a bot. C

clqu 30 Dec 23, 2022
This project was developed to practice Front-end and Back-end comunication, data formatting, http requests GET, POST, DELETE, form validation, it also consumes a rest API

React Application ?? Demonstration of the application | Features | Technologies used | Application installation ?? Demonstration of the application Ap

Victor Lira 36 May 17, 2022