Skip to content
A general purpose, JS/Bootstrap UI framework for rich text posting. An author-friendly, minimal, mobile/desktop interface for creating blog-like content, designed for PublicLab.org
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github Replace 'rake test' with 'grunt jasmine' in PR template (#226) Jan 16, 2019
dist Fixed nested <a> tags in wk-commands buttons. Mar 6, 2019
examples identify error in map module; options: true (#300) Mar 4, 2019
spec/javascripts identify error in map module; options: true (#300) Mar 4, 2019
src Fixed nested <a> tags in wk-commands buttons. Mar 6, 2019
.gitignore
.travis.yml
CODE_OF_CONDUCT.md Minor markdown fix in CoC (#266) Jan 31, 2019
CONTRIBUTING.md
Gruntfile.js added debug option by generating source map (#212) Jan 17, 2019
LICENSE
README.md
package-lock.json Bump jquery from 3.3.1 to 3.4.0 Apr 11, 2019
package.json
test.html

README.md

PublicLab.Editor

Build Status

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:

  • titling
  • main image uploading
  • text body editing using Woofmark (markdown/WYSIWYG)
  • tagging
  • edit history

We may include an extensible API for adding custom content modules, for example:

  • coauthoring: "add co-authors"
  • "post a response" buttons
  • event data (calendar selector, RSVP)
  • related content selection: "this work is a response to post X"
  • geotagging: "Use the map to show where this happened"
  • data embedding: "Paste data here and choose a chart style" (via Chart.js?)

Some, or many of the above may be optionally based on Public Lab Powertags. We may also include:

  • client-side validation

Design process

Design updates are viewable on PublicLab.org.

You can try a very early, rough prototype here:

https://publiclab.github.io/PublicLab.Editor/examples/

Screenshot

Modules

The editor is built from different modules like:

  • TitleModule
  • MainImageModule
  • EditorModule
  • TagsModule
  • HistoryModule
  • RichTextModule
  • MapModule

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.

Default modules

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.

Module content

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.

Installation

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. You may have to use sudo for root privileges.

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 in /examples/index.html. 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.

Setup

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 differnt formats. You can use a smaller version by using a size property in the constructor. Refer to example given in /examples/comment.html

Dependencies

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="../node_modules/jquery/dist/jquery.min.js"></script>

<!-- required for TagsModule -->
<script src="../node_modules/typeahead.js/dist/typeahead.jquery.js"></script>
<script src="../node_modules/typeahead.js/dist/bloodhound.js"></script>
<script src="../node_modules/bootstrap-tokenfield/dist/bootstrap-tokenfield.js"></script>

<!-- required for MainImageModule -->
<script src="../node_modules/blueimp-file-upload/js/vendor/jquery.ui.widget.js"></script>
<script src="../node_modules/blueimp-file-upload/js/jquery.iframe-transport.js"></script>
<script src="../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.

Server

PublicLab.Editor expects a response from the server upon sending a request to destination that is a URL which it will follow.

Creating a mock server

  • 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.

  • Clone plots2 and follow the Standard Installation instructions to run it on your local server.

  • Now in plots2/package.json# at line 62, replace this line with "publiclab-editor": "file:..<path>" where <path> is path of your cloned PublicLab.Editor repo folder

  • Now with passenger 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.

  • For reflecting HTML changes use plots2/app/views/editor/rich.html.erb instead of example.html. They both have same sturcture.

  • For reflecting the changes on the local server need to run yarn install --force and refresh your page.


Modules

Various modules have different configurable options to be added to the options object, as seen in the Integration section.

Rich Text Module

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']
  }
});

Atwho.js Data Module

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:

  • Set up multiple listeners for different characters with different behavior and data.
  • Format returned data using custom templates, that support keyboard and mouse imput.
  • Custom data handlers and template renderers using a group of configurable callbacks.

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.

atwho_js

Usage and code snippets

  • Inorder to setup the autocompletion library, please follow these steps.
  1. Assuming that you have an installed copy of At.js in your node modules, firstly, you need to include the minified CSS and JS builds and the emojis source file, emoji.js to your parent HTML.
<link
      href="../node_modules/at.js/dist/css/jquery.atwho.min.css"
      rel="stylesheet"
    />
    <script src="../node_modules/at.js/dist/js/jquery.atwho.min.js"></script>
    <script src="data/emoji.js"></script>
  1. Include the At.js data module script from ./examples/data/atwho.PLE.jsinto your parent HTML.
<script src="data/atwho.PLE.js"></script>
  • Callout watcher: Triggered at the "@" character, the callout watcher recommends four most likely authors for the user's query. Upon selection, the profile of that particular user is rendered in an anchor tag.
  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
  • Hastag watcher: Triggered at the "#" character, the hashtag watcher recommends four most likely wikis for the user's query. Upon selection, the wiki page of that particular topic is rendered in an anchor tag.
  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 watcher: Triggered at the ":" character, the emoji watcher recommends three most likely emojis for the user's query. Upon selection, the value of the particular emoji is rendered from the emoji source file 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!

Tags Module

The Tags module uses Bootstrap Tokenfield. To add tags after initialization, use:

editor.tagsModule.el.find('input').tokenfield('createToken', 'purple');


Developers

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

Testing

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 Jasmine for testing: https://jasmine.github.io/2.4/introduction.html

To run tests, open /test.html in a browser. If you have phantomjs installed, you can run grunt jasmine to run tests on the commandline.

You can find the installation instructions for phantomjs in its official build documentation. For Ubuntu/debian based system you can follow these instructions or use the script mentioned there.

To add new tests, edit the *_spec.js files in /spec/javascripts/.


Integration

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:

  • publishing by POST (CREATE) to /notes/create (will go to plots2's notes_controller.rb#create)
  • updating by UPDATE to /notes/update (will go to plots2's notes_controller.rb#update)
  • uploading images by POST to /images/create (will go to plots2's images_controller.rb#create)

Tags integration

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.

RichText integration

Similarly the RichText module (which wraps the Woofmark adaptor) may make GET requests to:

  • fetch relevant tags from /tags/related.json with whatever relevant content to base "relatedness" on
  • fetch relevant authors from /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'
  }
});

Title integration

The TitleModule can make requests to find "related" content and suggest it be attached. Documentation on this can be found at:

https://github.com/publiclab/PublicLab.Editor/blob/master/src/modules/PublicLab.TitleModule.Related.js


Compatibility

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.

You can’t perform that action at this time.