View on Github

Portal.js

A JS library to build portals ...

Tutorial - Writing a markdown widget

Let's write a complete widget. What do we want ?

Now the code

var MarkdownWidget = Portal.Widget({
  name: "Markdown",
  description: "Display markdown formated text"  
});

Portal.registerWidget('HfJ49Gceyh4PInNPr8', MarkdownWidget);

that's a basic Widget with nothing rendered and just name and description available in the widget catalog.

now let's add a stupid render function

var MarkdownWidget = Portal.Widget({
  name: "Markdown",
  description: "Display markdown formated text" ,
  render: function(container, prefs) {
    $('#' + container).html('<h1>Hello World!</h1>');
  }
});

Portal.registerWidget('HfJ49Gceyh4PInNPr8', MarkdownWidget);

The widget should render a big Hello World!, but that's not really what we need.

Showdown

Showdown is a JS lib that can render markdown formated text as html.

You can get the source here http://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js

let's declare it as dependency of the widget

var MarkdownWidget = Portal.Widget({
  name: "Markdown",
  description: "Display markdown formated text" ,
  jsAssets: ['http://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js'],
  render: function(container, prefs) {
    $('#' + container).html('<h1>Hello World!</h1>');
  }
});

Portal.registerWidget('HfJ49Gceyh4PInNPr8', MarkdownWidget);

and use it

var MarkdownWidget = Portal.Widget({
  name: "Markdown",
  description: "Display markdown formated text" ,
  jsAssets: ['http://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js'],
  init: function() {
    this.converter = new Showdown.converter();
  },
  render: function(container, prefs) {
    var mardowntext = prefs.markdown;
    var htmltext = this.converter.makeHtml(mardowntext);
    $('#' + container).html(htmltext);
  }
});

Portal.registerWidget('HfJ49Gceyh4PInNPr8', MarkdownWidget);

Here the only issue is that if there is no preference in the widget, then the converter will fail, so we will add default preference for markdown. Also we will add property to show preference edit button and always show it even is the current user is not in admin mode

var MarkdownWidget = Portal.Widget({
  name: "Markdown",
  description: "Display markdown formated text" ,
  hasPrefs: true,
  alwaysShowPrefs: true,
  defaultPrefs: { markdown: "# Hello World!" },
  jsAssets: ['http://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js'],
  init: function() {
    this.converter = new Showdown.converter();
  },
  render: function(container, prefs) {
    var mardowntext = prefs.markdown;
    var htmltext = this.converter.makeHtml(mardowntext);
    $('#' + container).html(htmltext);
  }
});

Portal.registerWidget('HfJ49Gceyh4PInNPr8', MarkdownWidget);

As we want to display title in gree, we also want to add the following css style to the widget

h1 {
  color: green;
}

now we can use it with

var MarkdownWidget = Portal.Widget({
  name: "Markdown",
  description: "Display markdown formated text" ,
  hasPrefs: true,
  alwaysShowPrefs: true,
  defaultPrefs: { markdown: "# Hello World!" },
  cssAssets: ['/url/to/the/nice/css/file.css'],
  jsAssets: ['http://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js'],
  init: function() {
    this.converter = new Showdown.converter();
  },
  render: function(container, prefs) {
    var mardowntext = prefs.markdown;
    var htmltext = this.converter.makeHtml(mardowntext);
    $('#' + container).html(htmltext);
  }
});

Portal.registerWidget('HfJ49Gceyh4PInNPr8', MarkdownWidget);

Now the last thing we want to do is to be able to edit Markdown in preferences. The rendering of the edit page is done in a renderEdit function. Let's write one

var MarkdownWidget = Portal.Widget({
  name: "Markdown",
  description: "Display markdown formated text" ,
  hasPrefs: true,
  alwaysShowPrefs: true,
  defaultPrefs: { markdown: "# Hello World!" },
  cssAssets: ['/url/to/the/nice/css/file.css'],
  jsAssets: ['http://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js'],
  init: function() {
    this.converter = new Showdown.converter();
  },
  render: function(container, prefs) {
    var mardowntext = prefs.markdown;
    var htmltext = this.converter.makeHtml(mardowntext);
    $('#' + container).html(htmltext);
  },
  renderEdit: function(container, prefs, saveCallback, cancelCallback) {
    $('#' + container).html('<div>' +
      '<div class="row-fluid">' +
        '<textarea id="' + (container + '_mdtext') 
            + '"class="largeText span12">' + (prefs.markdown || '') + '</textarea>' +
      '</div>' +
      '<div class="row-fluid" style="margin-top: 10px">' +
        '<div class="btn-group pull-right">' +
          '<button type="button" id="' + (container + '_mdcancel') 
              + '" class="btn btn-small btn-danger">Cancel</button>' +
          '<button type="button" id="' + (container + '_mdok') 
              + '" class="btn btn-small btn-primary">Ok</button>' +
        '</div>' +
      '</div></div>'
    );
    $('#' + container + '_mdcancel')
        .click(cancelCallback);  // add action when clicking on cancel
    $('#' + container + '_mdok').click(function(e) { // add action when clicking on save
      e.preventDefault();
      prefs.markdown = 
        $('#' + container + '_mdtext').val(); // change value of prefs.markdown
      saveCallback(prefs); // use the saveCallback to save prefs in the store
    });
  }
});

Portal.registerWidget('HfJ49Gceyh4PInNPr8', MarkdownWidget);

And we're done, we have written our first widget.

Enjoy ;-)