Comments Track Implementation - 2 : Components

by 8:21 AM 0 comments

Hey, this post is about a very interesting part of my project. That’s about the model details of the components associated with the comments track that I implemented. I will explain how these components are wired together and how they behave in my next blog post.
We can identify several components of this specific feature. They are as follows.
  1. CommentFeature Track
    1. Comment Box
    2. Click behavior
    3. Feature Context Menu
    4. Track Context Menu
  2. TrackData Model
  3. DISQUS Controller
  4. Express Backend
  5. Sails Hook
  6. Side Bar
Let me explain them one by one. We shall move to the first component then.
  1. CommentFeature Track
The ‘CommentFeature’ track is implemented based on the ‘HTMLFeatures’ Track. The reason for that is simple. ‘HTMLFeatures’ track has already implemented the basic requirement of the comments feature which is displaying box like html div elements on the window and then rendering them by lazily fetching required data when the user scrolls through. I implemented some customizations to extend that to CommentTrack.
The first thing that I added is a `Comment Box`. It is a html div element constructed by first sending data to the `_decorate_feature` function of the `NCList` store to decorate with `getter` and `setter` functions and then adding it to the respective block by sending to the `addFeatureToBlock` function. This div element has an attribute called `feature`, which holds all the information about the track. These data are defined in the `TrackData.json` file associated with the CommentTrack.
Then I added the click behavior. Clicking on a div element should reload the comments sidebar with the associated thread identifier. Additionally, if the div is marked as having unread comments, the div should be reverted back as read when clicked. This behavior is defined in the `_defaultConfig -> events -> click` function.
events: {
   click: function(){
       var name = this.feature.get('name');
       reload(this.feature.get('thread_id'));
       query('#wrapper').removeClass('toggled');
       query('.sidebar-brand')[0].textContent = name;
       query('.feature.'+this.feature.get('thread_id')).removeClass('hasComments');
       this.feature.set('class_css','');
   }
}
Another option that I added is Feature Context Menu. The right click context menu items are defined under `_defaultConfig -> menuTemlate`. This accepts an array in which each array element should describe a menu option. In the CommentsTrack, each array element has three attributes called label, action and icon class. The label is the text showing in the context menu option. The action is what it does when selected. The icon class is a dijit icon and it is shown along the label. The action in each context menu option takes the form of sending a call to a method with the changed attributes of the feature along with a callback function. This callback function will update the feature associated with each comment box and redraw the track when changes happen to the feature. Here is a the code snippet for `UpdateRange` context menu option.

{ 
    label: 'Update Range',
    action: function() {
        var start = prompt("Enter starting point",500);
        var end = prompt("Enter Ending point",1000);
        var thread = this;
        _this.updateComment({
            feature: this.feature,
            start: start,
            end: end,
            action:"update"
        },function() {
            thread.feature.set('start',start);
            thread.feature.set('end',end);
            thread.track.redraw();
        });
    },
    iconClass: 'dijitIconTask'
}
The next significant feature with regard to CommentFeature track is a `Track context menu`. Track context menu is similar to the Feature Context Menu except that actions defined under this menu are effective to the track as a whole, rather than for the individual features. In the first release of Comments track, three options are available. They are `Pin to top` option, `Delete Track` option and the `Start new thread` option. Shown below is the code snippet used to add a new option to the track context menu.

var o = this.inherited(arguments);
var track = this;
o.push.apply(o, [{
    type: 'dijit/MenuSeparator'
}, {
    label: 'Start New Thread',
    type: 'dijit/MenuItem',
    onClick: function () {
        var name = prompt("Enter title for thread");
        var start = prompt("Enter start position");
        var end = prompt("Enter End position");
        track.updateComment({
            start: start,
            end: end,
            name: name,
            action: "insert"
        }, function (res) {
            var thread_id = res.id;
            startThread('http://jbrowse.org/v2/' + thread_id, thread_id, name);
            track.newFeats.push([0, start, end, "ctgA", thread_id, name]);
            track.redraw();
        });
    }
}]);


2. TrackData Model
The next important component model that I used in CommentFeature track is called `Nested Containment List`, aka `NCList`. This is the default store associated with HTMLFeature track and therefore CommentsTrack inherits that. NCList provides mechanisms to allow nested elements for optimizing fast retrieval. However CommentFeature track does not make much use of the speed of retrieval factor in the current implementation.
NCList uses an array representation to store data and defines multiple classes to decorate these arrays later with attributes. I have defined a single class for the requirement of CommentTrack. Thus each comment array element metadata takes the following form,
[NCList Class, Start, End, Sequence Id, DISQUS Thread Identifier, Title,Class_css]
We have also added two custom attributes to the NCList model, `host` and `absCount`. The host is used to uniquely identify the host server of the JBrowse instance. The absCount is total number of created by the host in DISQUS. These two attributes are used when generating DISQUS Identifiers as postfixes to avoid identifier conflicts as described by DISQUS documentation. The rest of the attributes used have their general behavior.


3) DISQUS Controller
Talking about the next important component comes the DISQUS controller. All the DISQUS related functionalities are implemented in the controller located at `plugins/SocialFeatures/disqus_actions.js`. Following are the list of actions defined in the current implementation of comments track.
  1. disqus_config : Loads the initial configurations (Loads the identifier_1 thread by default)
  2. reload : Reloads the comment section displayed on the sidebar using the given configurations. The function uses thread identifier in the current implementation as the configuration parameter.
  3. startThread : Starts a new thread and reloads the DISQUS comments section. Uses the url, title and identifier parameters to send the call to DISQUS API.
The following code snippet shows how the startThread is implemented. I will not quote the rest here as all three functions have a similar structure.

/**
* Start a new thread and refresh disqus comment section
*/
function startThread(url,identifier,title){
  DISQUS.reset({
    reload: true,
    config: function () {  
      this.page.identifier = identifier;
      this.page.url = url;
      this.page.title = title;
    }
  });
}

4) Express Backend
JBrowse can be hosted in any webserver like apache. However for the comment feature to work properly, that is, to make the changes permanent, we need to have a server component to write data back to the trackData.json file. The express server component implemented at https://github.com/pupudu/jBrowse-comment-server does that.
In the express backend, a single endpoint serves the update comment function. The rest endpoint accepts a post request with the old and new feature data. The generic File Stream module provided by NodeJs is used for reading and writing data to files in JSON format.

This code snippet shows the most important few lines of the endpoint which structures the data properly based on the request parameters before writing back to the file.

if(action === 'insert'){
    newId = host+dataObj.absCount;
    newThread[defaultClass.indexOf('thread_id')+1] = newId;
    list.splice(index,0,newThread);
    dataObj.featureCount++;
    dataObj.absCount++;
}
else if(action === "remove"){
    list.splice(index,1);
    dataObj.featureCount--;
}
else if(action === "update"){
    list[index] = newThread;
}
list = list.sort(Comparator);
4) Sails Hook
This component is still under construction. Details will be updated here when the implementation is finished.


5) Side Bar
Last but not least, the Sidebar can be described as the next important component. The side bar used to display DISQUS threads is a simple html structure. The styles and also the slide in and out behavior are based on CSS classes, by toggling classes to switch between the two states. This html div component is defined inside the main index.html file in which JBrowse is also instantiated.
I hope that this post would have clearly explained you about the model details of the components.

0 comments:

Post a Comment