Please contact plots-dev@googlegroups.com to get involved! We'd love to make this editor compatible with other platforms.
PublicLab.Editor is a general purpose, modular JavaScript/Bootstrap UI library for rich text posting, which provides an author-friendly, minimal, mobile/desktop (fluid) interface for creating blog-like content, designed for PublicLab.org (itself an open source project).
PublicLab.Editor provides author-friendly interfaces for:
We may include an extensible API for adding custom content modules, for example:
Some, or many of the above may be optionally based on Public Lab Powertags. We may also include:
Design updates are viewable on PublicLab.org.
You can try a very early, rough prototype here:
https://publiclab.github.io/PublicLab.Editor/examples/
The editor is built from different modules like:
Each manages its own UI and validation, and which report their contents via a module.value()
method. The EditorModule encapsulates all the modules. It contains a WYSIWYG textarea, managed (by default) by Woofmark.
The Title, MainImage, Tags, History, and RichText modules are on by default. To disable them for a more minimal editor, you can set them to false in the constructor options:
var editor = new PL.Editor({
mainImageModule: false // disable the MainImageModule
});
Note: The MapModule is NOT a default module, i.e., you will need to explicitly set mapModule: true
in order to properly enable it in the parent HTML file.
To input content into a module, the convention is to use that module's value()
method, like this:
editor.richTextModule.value('hello there'); // sets the richTextModule's content
You can also use module.value()
as a getter, like this:
var content = editor.richTextModule.value(); // get the richTextModule's content
editor.richTextModule.value(content + ' and then some'); // sets the richTextModule's content
To add a new field, or new behavior, extend PublicLab.Module
or customize an existing module by extending it -- for example:
PublicLab.Module.extend({
init: function(_editor) {
this._super(_editor);
}
});
Module output is collected (by editor.collectData()
) in the editor.data
object -- a collection of values based on each module.key
, assigning the value of module.value()
to that key. So a module with a key
of nid
, for example, which returned 6
from module.value()
, would result in editor.data
being:
{
nid: 6
}
Because of this, each module must have a key
property and a value()
method. Some modules, like the TagsModule, will return their own value added to the existing value of key
, so that multiple modules may add to the tags
property of editor.data
.
To install PublicLab.Editor for development, you'll need NodeJS. You can get the detailed instruction on installing node and npm in its official documentation.
After installing node and npm run npm install
from the root directory.
PublicLab.Editor uses grunt - the javascript task runner for compilation of the modules. To install grunt run npm install -g grunt-cli
. If you get permission errors, refer this.
If you simply open the examples/index.html
file in your browser, you will probably experience the CORS offline issue. To avoid this, you need to access the Editor using a HTTP/HTTPS domain in your localhost, through a server. One option is to install npm live server with the command npm install -g live-server
.
Make changes to the files in the /src/
directory, then run grunt build
to compile into /dist/PublicLab.Editor.js
. This will use grunt-browserify
to concatenate and include any node modules named in require()
statements. You'll then be able to try it out by opening the project's directory in your terminal and running the command live-server
. When the project opens in your default browser, open the folder /examples
to access the editor. Run grunt
and leave it running to build as you go.
You can also run grunt debug
to have grunt-browserify to include Source Maps for easy debugging. This way you can locate the module from where the error is generating. This is for use in development only.
To use PublicLab.Editor, you'll need to follow the template provided here, and use the following constructor:
var editor = new PL.Editor({
textarea: document.getElementById('my-textarea'),
destination: "/notes/create", // content will Submit to this URL upon clicking "Publish"
data: { // prepopulate fields:
title: "Your post title",
body: "Your post content",
tags: "nice,cool"
}
});
To customize the @author and #tag autocompletes with your own suggestions, or with AJAX calls to your server, see the autocomplete example in /examples/autocomplete.html
.
The editor toolbar comes in two different formats. You can use a smaller version by using a size
property in the constructor. Refer to example given in /examples/comment.html
As of v1
, PublicLab.Editor
requires jQuery
to be included on the page, and some external jQuery
plugins for the TagsModule
and MainImageModule
:
<script src="https://github.com/publiclab/PublicLab.Editor/raw/main/./node_modules/jquery/dist/jquery.min.js"></script>
<!-- required for TagsModule -->
<script src="https://github.com/publiclab/PublicLab.Editor/raw/main/./node_modules/typeahead.js/dist/typeahead.jquery.js"></script>
<script src="https://github.com/publiclab/PublicLab.Editor/raw/main/./node_modules/typeahead.js/dist/bloodhound.js"></script>
<script src="https://github.com/publiclab/PublicLab.Editor/raw/main/./node_modules/bootstrap-tokenfield/dist/bootstrap-tokenfield.js"></script>
<!-- required for MainImageModule -->
<script src="https://github.com/publiclab/PublicLab.Editor/raw/main/./node_modules/blueimp-file-upload/js/vendor/jquery.ui.widget.js"></script>
<script src="https://github.com/publiclab/PublicLab.Editor/raw/main/./node_modules/blueimp-file-upload/js/jquery.iframe-transport.js"></script>
<script src="https://github.com/publiclab/PublicLab.Editor/raw/main/./node_modules/blueimp-file-upload/js/jquery.fileupload.js"></script>
These used to be compiled into PublicLab.Editor
but are now external so that jQuery
does not get included twice when using the editor in a page which already has `jQuery.
PublicLab.Editor expects a response from the server upon sending a request to destination
that is a URL which it will follow.
Testing image upload and other features that depend on an interactive server is difficult with just a basic one-line webserver. Instead, you can set up the plots2
project as explained here to see the Editor working interactively while you test out those features.
plots2
and follow the Standard Installation instructions to run it on your local server.plots2/package.json#
at line 62
, replace this line with "publiclab-editor": "file:..<path>"
where <path>
is path of your cloned PublicLab.Editor
repo folderpassenger start
you can access the Editor at localhost:3000/post
. Make changes in Editor's source code and run grunt build
or grunt debug
to bundle all files. Then run yarn install --force
in plots2 repo to view changes on server.plots2/app/views/editor/rich.html.erb
instead of example.html. They both have same structure.yarn install --force
and refresh your page.Various modules have different configurable options to be added to the options object, as seen in the Integration section.
The Rich Text module is built around a custom fork of the Woofmark editor.
formats -- provide an array of strings specifying allowed file extensions that may be uploaded inline in the rich text input area:
var editor = new PL.Editor({
textarea: document.getElementById('my-textarea'),
richTextModule: {
formats: ['xml', 'pdf', 'csv', 'stl']
}
});
The Public Lab website makes use of a “short-code” syntax, similar to WordPress’s short-codes convention, in which text in the format [notes:water-quality]
(for example) generates a dynamically-populated table of content from the website, in this case under the topic "water-quality". The Editor allows for these types of inserts and deals with them in different ways, either displaying them as editable blocks, or inserting them via special buttons.
Read more about the different types of inserts, how they work, and what they look like at https://publiclab.org/power-tags#Inline+power+tags https://publiclab.org/advanced-grids, and https://publiclab.org/notes/liz/08-30-2016/check-out-these-activity-grids.
At.js
is essentially an autocompletion library to autocomplete mentions, smileys etc. just like you see on Github. It can be used to implement the following functionalities:
We currently employ the At.js
library to autocomplete authors, wiki pages, and emojis, by wiring them to the "@", "#", and ":" symbols respectively. Refer to the demonstration below for a better insight as to how this works.
Usage and code snippets
emoji.js
to your parent HTML.
<link
href="https://github.com/publiclab/PublicLab.Editor/blob/main/./node_modules/at.js/dist/css/jquery.atwho.min.css"
rel="stylesheet"
/>
<script src="https://github.com/publiclab/PublicLab.Editor/raw/main/./node_modules/at.js/dist/js/jquery.atwho.min.js"></script>
<script src="https://github.com/publiclab/PublicLab.Editor/raw/main/data/emoji.js"></script>
At.js
data module script from ./examples/data/atwho.PLE.js
into your parent HTML.
<script src="https://github.com/publiclab/PublicLab.Editor/raw/main/data/atwho.PLE.js"></script>
at: "@",
callbacks: {
beforeInsert: function(value, obj) {
username = value.slice(1); // remove ambiguous first character
value = "<a href='https://publiclab.org/profile/" + username + "' target='_blank'>" + value + "</a>"; // render value as a link
return value;
},
remoteFilter: function(query, callback) {
$.getJSON(
"https://publiclab.org/api/srch/profiles?query=" + query, {}, // send user query to PL servers
function(data) {
if (data.hasOwnProperty("items") && data.items.length > 0) {
callback(
data.items.map(function(i) {
return i.doc_title; // for every "item" return the author's name
}));}});}},
highlightFirst: true, // highlight the first suggestion
limit: 4 // limiter
at: "#",
callbacks: {
beforeInsert: function(value, obj) {
value = value.slice(1); // remove ambiguous first character
tag = value.slice(value.lastIndexOf("/") + 1); // retrieve tag name
value = "<a href='https://publiclab.org" + value + "' target='_blank'>#" + tag + "</a>"; // render value as a link
return value;
},
remoteFilter: function(query, callback) {
$.getJSON(
"https://publiclab.org/api/srch/tags?query=" + query, {}, // send user query to PL servers
function(data) {
if (data.hasOwnProperty("items") && data.items.length > 0) {
callback(
data.items.map(function(i) {
return i.doc_url; // for every "item" return the wiki's url
}));}});} },
highlightFirst: true, // highlight the first suggestion
limit: 4
emoji.js
.
if (e.key === ":") {
var x = emoji;
$(this).atwho({
at: e.key,
limit: 3,
highlightFirst: true, // highlight the first suggestion
data: keys,
callbacks: {
beforeInsert: function(value, obj) {
value = value.slice(1); // remove ambiguous first character
value = x[value]; // retrieve respective emoji object's value from source
return value;
Detailed documentation can be referred to at the At.js wiki pages. Checkout this link for a live demo!
The Tags module uses Bootstrap Tokenfield. To add tags after initialization, use:
editor.tagsModule.el.find('input').tokenfield('createToken', 'purple');
To add Map module, pass mapModule: true
in options. Also if you pass lat: XX
and lon: YY
in options it will show the map at coordinates [XX, YY]. You can optionally include a zoom parameter, by default the value of "5" will be used.
editor = new PL.Editor({
mapModule: true,
lat: 23,
lon: 77,
zoom: 5
});
Help improve Public Lab software!
To report bugs and request features, please use the GitHub issue tracker provided at https://github.com/publiclab/PublicLab.Editor/issues
For additional support, join the Public Lab website and mailing list at http://publiclab.org/lists or for urgent requests, email web@publiclab.org
Automated tests are an essential way to ensure that new changes don't break existing functionality, and can help you be confident that your code is ready to be merged in. We use Jest for testing: https://github.com/facebook/jest. The tests are written using jest and puppeteer.
To add new tests, edit the *test.js
files in /test/ui-testing/
.
To run the tests you can run npm run test-ui
.
If you face any error while running the tests:
sudo apt-get install gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
Connect this editor to a parent server-side app, such as PublicLab.org or other servers.
The API we'll be working from will include several server URLs, which we'll be building into the file at src/adapters/PublicLab.Adaptors.js
:
POST
(CREATE
) to /notes/create
(will go to plots2's notes_controller.rb#create
)UPDATE
to /notes/update
(will go to plots2's notes_controller.rb#update
)POST
to /images/create
(will go to plots2's images_controller.rb#create
)The TagsModule uses Bloodhound for tag suggestions. It can make GET
requests to the server to fetch recent tag suggestion, which returns data in json format like /tags/recent.json
. You can also give your own suggestions in an array. Refer to the example given in /examples/autocomplete.html
.
Similarly the RichText module (which wraps the Woofmark adaptor) may make GET
requests to:
/tags/related.json
with whatever relevant content to base "relatedness" on/authors/<foo>.json
with <foo>
being the typeahead stub, like @jyw
for @jywarren
These can be overridden within the options in a richTextModule
object, like:
var editor = new PL.Editor({
textarea: document.getElementById('my-textarea'),
richTextModule: {
tagsUrl: '/tags.json',
authorsUrl: '/authors.json'
}
});
The TitleModule can make requests to find "related" content and suggest it be attached. Documentation on this can be found at:
PublicLab.Editor uses jQuery 1.7+ or 2, and tests run on Node v5+. Other versions depended on are noted in the package.json file.