Implementing Session Bookmark Feature in Open Event Webapp

The event websites generated by Open Event Webapp can be large in size with a massive number of sessions in it. As such, it becomes a little difficult for the user to keep track of the sessions he is interested in without any bookmark facility.  With the help of it, he/she can easily mark a session as his favorite and then see them all at once at the click of the button. This feature is available and synchronized across all the pages (track, rooms, and schedule) of the event. That is, if you bookmark a session on the tracks page, it would automatically appear on the other pages too. Likewise, if you unmark a session on any of the page, it would be reflected on rest of the pages. The event websites generated by the webapp are static in nature. It means that there is no backend server and database where the data about the bookmarked sessions and the user can be stored. So how are we storing information about the marked sessions and persisting it across different browser sessions? There must be a place where we are storing the data! Yes. It’s the LocalStorage Object. HTML5 introduced a localStorage object for storing information which persists even when the site or the browser is closed and opened again. Also, the max size of the localStorage object is quite sufficient (around 5 - 10 MB) for storing textual data and should not be a bottleneck in the majority of the cases. One very important thing to remember about the localStorage object is that it only supports strings in key and value pairs. Hence, we can’t store any other data type like Boolean, Number or Date directly in it. We would have to convert the concerned object into a string using the JSON.stringify() method and then store it. Okay, let’s see how we implement the bookmark facility of sessions. We initialize the localStorage object. We create a key in it which is the name of the event and corresponding to its value, we store an object which will contain the information about the bookmarked sessions of that particular event. It’s key will be the id of the session and the value (0 or 1) will define whether it is bookmarked or not. Initially, when no sessions have been bookmarked, it will be empty. function initPage() { //Check whether the localStorage object is already instantiated if(localStorage.hasOwnProperty(eventName) === false) { localStorage[eventName] = '{}'; // This object will store the bookmarked sessions info } } Now, we need to actually provide a button to the user for marking the sessions and then link it to the localStorage object. This is the session element with the star-shaped bookmark button Here is the related code <a class="bookmark" id="b{{session_id}}"> <i class="fa fa-star" aria-hidden="true"> </i> </a> Then we need to handle the click event which occurs when the user clicks this button. We will extract the id of the session and then store this info in the event object inside the localStorage object.…

Continue ReadingImplementing Session Bookmark Feature in Open Event Webapp

Implementing PNG Export of Schedule in Open Event Webapp

Recently, we implemented a cool feature in the Open Event Webapp. We provided the facility to download the png image of the schedule of an event at the click of a button. The user can download the schedule of an event in both the list and the calendar view. There are situations when the speaker or the organizer of an event want to get a hard-copy of the schedule so that they can paste it on notice boards for the convenience of the visitors. With the help of PNG Export functionality, they can download the png image of the schedule and print it easily. The whole code can be seen here. On the schedule page, the user can view the sessions in two modes: list and the calendar mode. Capturing a portion of the document and then converting it to an image is no easy task. It is because we can’t directly convert an HTML element to an image. We have to first somehow render that HTML element on the canvas and then convert it to an image.It is a two-step process. Rendering an HTML element on the canvas is the most important part and challenging part. The better we will be able to render an element on the canvas, the better will be the output quality of the image. Fortunately for us, we don’t have to implement it from scratch (which would have been extremely difficult and time-consuming). Enter html2canvas library. It renders an element onto the canvas after which we can convert it into an image. I will now explain how we implemented png export in the calendar mode. You can view the whole schedule template file here Here is a screenshot of calendar or grid view of the schedule. Currently selected date is 18th Mar, Saturday. The PNG Export button is on the top-right corner beside the ‘Calendar View’ button. Here is a little excerpt of the basic structure of the calendar mode of the sessions. I have given an overview of it in the comments. <div class="{{slug}} calendar"> <!-- slug represents the currently selected date --> <!-- This div contains all the sessions scheduled on the selected date --> <div class="col-md-12 paddingzero"> <!-- Contain content related to current date and time --> </div> <div class="calendar-content"> <div class="times"> <!-- This div contains the list of all the session times on the current day --> <!-- It is the left most column of the grid view which contains all the times → <div class="time"> <!-- This div contains information about the particular time --> </div> </div> <div class="rooms"> <!-- This div contains all the rooms of an event --> <!-- Each particular room has a set of sessions associated with it on that particular date --> <div class="room"> <!-- This div contains the list of session happening in a particular room --> <!-- Session Details --> </div> </div> </div> </div> Now, let us see how we will actually capture an image of the HTML element shown above. Here is the code related to it: $(".export-png").click(function() {…

Continue ReadingImplementing PNG Export of Schedule in Open Event Webapp

Integrating Selenium Testing in the Open Event Webapp

Open Event Webapp generates static website of events from JSON data fed to it in the form of a zip or an API endpoint. Over the course of time, the features included in the generated sites have grown. We have the bookmark option, search bar, calendar view and many other facilities. Every once in awhile, it happens that when we fix an issue, another issue which was solved previously in the past resurges. This is extremely frustrating as a developer to solve the old bugs again. In software engineering field, we call these issue/bugs as regressions. Detecting them is challenging as the reviewer has to manually go through all the pages of the site, and check each and every function. It is not uncommon that a part may be left out during the review process introducing regressions in the app. We already have proper testing for the generator part of the project with help of libraries like mocha and chai. But, up until now, we didn’t have the client-side testing aka the frontend testing in the project.  We had to introduce a way to test the functionality of the site. To check if a link is dead, the search bar is working or not, bookmark function works properly or not and so on. Enter Selenium. Selenium is a suite of tools to automate web browsers across many platforms. Using Selenium, we can control the browser and instruct it to do an ‘action’ programmatically. We can then check whether that action had the appropriate reaction and make our test cases based on this concept. There are various implementations of Selenium available in many different languages: Java, Ruby, Javascript, Python etc. As the main language used in the project is Javascript, we decided to use it. https://www.npmjs.com/package/selenium-webdriver https://seleniumhq.github.io/selenium/docs/api/javascript/index.html After deciding on the framework to be used in the project, we had to find a way to integrate it in the project. We wanted to run the tests on every PR made to the repo. If it failed, the build would be stopped and it would be shown to the user. Now, the problem was that the Travis doesn’t natively support running Selenium on their virtual machine. Fortunately, we have a company called Sauce Labs which provides automated testing for the web and mobile applications. And the best part, it is totally free for open source projects. And Travis supports Sauce Labs. The details of how to connect to Sauce Labs is described in detail on this page: https://docs.travis-ci.com/user/gui-and-headless-browsers/ Basically, we have to create an account on Sauce Labs and get a sauce_username and sauce_access_key which will be used to connect to the sauce cloud. Travis provides a sauce_connect addon which creates a tunnel which allows the Sauce browsers to easily access our application. Once the tunnel is established, the browser in the Sauce cloud can use it to access the localhost where we serve the pages of the generated sites. A little code would make it more clear at this stage: Here is a short excerpt from the travis.yml file :- addons:  sauce_connect:…

Continue ReadingIntegrating Selenium Testing in the Open Event Webapp

Cross-Browser Issue: Dealing With an UI Problem Arising Due to Floating HTML Elements in Open Event Webapp

A few days back, I came across a rather interesting but hard to debug bug. The weird part was that it presented itself only on particular browsers like Firefox and didn’t exist on other browsers like Chrome. In Open Event Webapp, we have collapsible session elements on track, room and schedule pages. The uncollapsed element would show you the major details like the title of the session, its type, name and a small thumbnail picture of the speaker. If the user clicks on it, then the element would collapse and more detail would pop up which would include the detailed description of the session and the speaker(s) presenting it. Now, the problem which was occurring was that the collapsible sessions on the rooms page were behaving erratically. They were collapsing predictably but left behind a huge white space after they wound up on clicking. I have attached some screenshots to better demonstrate the problem. At first, I was confused. Because the same thing worked perfectly on Chrome. Collapsed State On Clicking Again The actual solution for this problem turned out to be quite short (a one-liner in fact) but it took me a little while to solve it and actually understand what was going on. The solution:- .room-filter { overflow: hidden; } Here, room-filter is the class applied to the session element. Wait? But how did that solved the problem? Read on!! As I mentioned in the title, the problem arises due to the floating HTML elements. I am just going to give a quick introduction to the float property. In case you want to read more about it, I have provided links at the end of the article which you can go through for a detailed explanation. Float is a CSS positioning property. It tells whether an element would shift to the right or left of its parent container or another floated element while other HTML elements like images, text and inline elements would wrap around it. One popular analogy which is often given is that of a textbook where the text wraps around the images. We can say that the image is floating to the left (or right) while the text wraps around it. But how is this problem related to the floating elements still remain unclear? Hold on. This is the structure of the session div on the rooms page: <div class="room-filter" id="{{session_id}}"> <div class="eventtime col-xs-2 col-sm-2 col-md-2"> <!-- Some Content --> </div> <div class="room-container"> <div class="left-border col-xs-10 col-sm-10 col-md-10"> <!-- Some Content --> </div> </div> </div>   As we can see from the code, we have a session div with two divs inside it. We have applied bootstrap grid classes to them. These bootstrap layout classes actually make the elements float with a specified width (You can check it on the developer console). So, we have two floating div elements inside the parent div. Remember that although the floating elements remain the part of the document, they are taken outside of the normal flow of the page. Since the parent div here contain nothing but the floating elements, its height collapses…

Continue ReadingCross-Browser Issue: Dealing With an UI Problem Arising Due to Floating HTML Elements in Open Event Webapp

Using Flexbox for Responsive Layout in Open Event Webapp

Recently, I tackled the issue of alignment of different buttons and input bar in the Open Event Webapp. The major challenge was to create a responsive design which adapts well on all the platforms: desktop, tablets and mobile. Doing it using grid layout provided by Bootstrap was rather tough and complicated and I solved the problem using the flexbox. What is Flexbox? Flexible Boxes, also called as Flexbox, is a new layout mode introduced in CSS3. The best part of using flexbox is that it ensures that the elements behave in a predictable manner when the page layout accommodates different screen sizes and display devices. In other words, it helps us in making a responsive design in a simpler manner. It is an alternative to block elements being floated and manipulated using media queries. Using Flexbox, a container and its children can be arranged in any direction: up, down or left and right. Size is flexible and elements inside the flexbox can grow or shrink to occupy unused space or prevent overflow respectively. The difference with grid layouts? There is a slight difference between flexbox and grid layouts which makes one suitable for creating a fully complete layout and the other not so much. Ideally, grids (provided by Bootstrap for example) are used for creating an entire layout. Flexbox is suited for styling separate containers such as navbars for example.                                  Structure of Flexbox Container How did I solve it? First I defined a container which acted as a wrapper for all the buttons and the search bar. Now, to convert this container into an actual flexbox, we have to add display: flex property to it. .container {  display: flex; } Did the issue got solved? It doesn’t look so. The container enclosed in the red border is the flexbox. As we can see from the picture itself, the elements are quite close to each other and a lot of space is wasted. Fortunately, flexbox provides us a handy property called flex-grow which deals with this type of problem. It defines the ability for a flex item to grow if necessary. That it, it tells how much amount of the available space inside the flex container the item is allowed to take. If all the items have flex-grow set to 1, then the remaining space in the container would be equally distributed to all the items. In a similar way, if an item has flex-grow set to 2, then it would occupy twice the amount of available space when compared to the other items. So, I applied the flex-grow:1 on the items. .list-btn {    flex-grow: 1; } .search-filter {    flex-grow: 1; } .starred-btn {   flex-grow: 1; } .calendar-btn {    flex-grow: 1; } How does it look now? Much better. But is the problem solved now? No. There is one more thing that we haven’t checked yet. Yes. It’s the responsiveness. We haven’t yet checked how it displays on tablets and mobiles yet? Let’s test and check and what we see. Ok. Something…

Continue ReadingUsing Flexbox for Responsive Layout in Open Event Webapp

Handling Zip Upload in Open Event Webapp

In Open Event Webapp, we use Socket.IO library for real-time communication between client and the server. To be able to generate a site for an event, the user has to first upload the file to the generator. There are a lot of node libraries like multer and formidable which exists for this purpose. But they don’t support display of real-time stats regarding the progress of the uploaded file. To solve this issue, the project used socket-io-file-upload library which uploads the file to the Node.js server using Socket.IO and show live percentage denoting how much of the zip has been uploaded to the server. It was working quite well until we discovered a major problem with the library. It didn’t support canceling the upload. If we clicked on the cancel button, it stopped showing the progress on the front end but actually continued to upload the file to the server on the back end. We had only two choices: Either to refresh the page or just wait for the existing zip file to upload completely. The former is a really bad solution and the latter result in wastage of time and bandwidth. On investigating the issue, the problem was with how the library is designed. This was not a fault in our code or the library either. It was just that the library didn’t support this functionality.  After thoroughly searching for the solutions, we came across this library named socket-io-stream which met our requirements. It supports bidirectional binary data transfer with Stream API through Socket.IO. We can also cancel the upload in between. For streaming between server and client, we will send stream instances first. To receive streams, we just wrap socket with socket-io-stream and then listen for any events as usual. Client Side: function uploadFile(file) { // Calculating size of the file and creating a stream to be sent to the server var size = (file.size/(1024*1024)).toString().substring(0, 3); var stream = ss.createStream(); /* Creating a read stream and initializing variable to keep track of the size of the file uploaded so far */ var blobStream = ss.createBlobReadStream(file); var fileUploadSize = 0; // Tracking upload progress and updating the variables blobStream.on('data', function(chunk) { fileUploadSize += chunk.length; var percent = (fileUploadSize / file.size * 100); /* isCancelling is a boolean which stores the status of the zip upload.That is, whether it is live or canceled */ if (isCancelling) { var msg = 'File upload was cancelled by user'; stream.destroy(); socket.emit('cancelled', msg); isCancelling = false; console.log(msg); } updateUploadProgress(Math.floor(percent)); if (percent === 100) { uploadFinished = true; enableGenerateButton(true); } }); // Sending the stream to the server and transferring read data to it ss(socket).emit('file', stream, {size: file.size, name: file.name}); blobStream.pipe(stream); } Server Side: // Importing the library const ss = require('socket.io-stream'); // Listening to the stream sent from the client ss(socket).on('file', function(stream, file) { generator.startZipUpload(socket.connId); // Declare a filename and write the incoming data to it var filename = path.join(__dirname, '..', 'uploads/connection-' + id.toString()) + '/upload.zip'; stream.pipe(fs.createWriteStream(filename)); }); That’s all we need to do to…

Continue ReadingHandling Zip Upload in Open Event Webapp