Tuesday, 5 February 2013

PresJS prototype


Yesterday I wrote about my ideal very-fast presentation workflow. As a quick recap the idea was to do no presentation at all. Making the research actually could be converted to slides. Let's see the prototype then.


Check out how it works on an iPhone:



I decided to use reusable and independent components. However the setup is not perfect yet it's a good start - no we only needed an MVP. The component structure was an ideal use case for RequireJS. The HTML wireframe is as simple as possible:

<!doctype html>
<html>
<head>
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen">
<link rel="stylesheet" type="text/css" href="style/style.css" />
</head>
<body>

<div id="page">

  <textarea name="mmxml" id="mmxml" rows="10"></textarea>

  <input type="submit" id="submit" class="btn btn-success" value="Start presentation" />

</div>

<div id="screen" class="theme_dark"></div>

<div id="pagers">
  <div id="prev"></div>
  <div id="next"></div>
</div>

</body>
</html>


I added Bootstrap for a minimal design (avoiding vomiting your screen) and my custom stylesheet. The textarea can receive the mindmap xml. #screen will show us the slide and #pagers will do the navigations part. All righty, let's add the heart of the system, the RequireJS loader:

<script data-main="scripts/main" src="scripts/require.js"></script>


Here we init the flow with main.js. There first we need to configure how RequireJS behaves:

requirejs.config({
  baseUrl: 'scripts/lib',
  paths: {
    app: '../app'
  },
  urlArgs: 'bust=' +  (new Date()).getTime()
});


This is pretty straightforward, maybe urlArgs worth a note - it adds an additional stamp to the request uri, so caching won't be a problem during development.

Our first component is the parser library - presjs.js:

define(function() {
  var slide = 0;
  var items = [];

  return {};
});


Here slide will contains the index of the current slide, and items are the slide objects. The parser's most important job is to parse the mindmap xml string. I've fond DOMParser being pretty simple and just perfect for us. We need to convert the string to XML object, traverse through the node list and save them to slides. Let's add it to the returned object of the component:

    start: function(rawXML) {
      var parser = new DOMParser();
      var xmldata = parser.parseFromString(rawXML, 'application/xml');

      var nodes = xmldata.getElementsByTagName('node');

      var length = nodes.length;
      for (var idx = 0; idx < length; idx++) {
        var node = nodes[idx];

        items.push({
          text: node.getAttribute('TEXT')
        });
      }
    },


Quite clear what it does. At this very moment we only care about the node text. Later we can use the formatting, images, links and descriptions.
Other important part of the parser is providing the slides and navigations, let's add them also:

    next: function() {
      var length = items.length;
      slide >= (length - 1) ? null : slide++;
      return items[slide];
    },

    prev: function() {
      slide > 0 ? slide-- : null;
      return items[slide];
    },

    current: function() {
      return items[slide];
    }


That makes the parser functionally ready to try. Next component is the screening component that is responsible for presenting slides on the screen:

define(function() {
  var screenArea = null;

  return {};
});


One setting is the screen area itself, I added a variable, so it's gonna be configurable by a setter in the returned object:

    setScreen: function(screen) {
      screenArea = screen;
    },


The presentation method expects the slide object and needs to put all the visual items on the screen. To make it nice I added a minimal animation (no proton-explosion yet, though):

    present: function(slide) {
      screenArea.html('<div class="slide_text">' + slide.text + '</div>');
      jQuery('.slide_text').hide()
      jQuery('.slide_text').slideDown('fast');
    }


Now that we have all the gears we need to press them together. In our main.js file let's load them:

requirejs(['jquery', 'app/presjs', 'app/screening'], function($, PresJS, Screening) {
});


The form submission starts the whole procedure - parse the xml and present the first slide:

  $('#submit').click(function() {
    PresJS.start($('#mmxml').val());
    $('#screen,#pagers').show();
    $('#page').hide();
    Screening.present(PresJS.current());
  });


Then we define the navigation:

  $('#next').click(function() {
    Screening.present(PresJS.next());
  });

  $('#prev').click(function() {
    Screening.present(PresJS.prev());
  });


Finally we set the screen for the screening component:

  Screening.setScreen($('#screen'));


And that's it. In my code I added a little extra - you can select a presentation template. The form select element offers some themes - by selecting one you add a class name to the container.

Try the online demo (here you are a sample mindmap xml: see sample) or check out the source code on GitHub.

---

Let me know what you think about the idea.

Peter

No comments:

Post a Comment