Making Expandable divs on Rooms page

The Open-Event Webapp provides the information about different Session Venues inside Rooms page. The idea was to provide information related to each Venue in a better way. For that, the expandable sessions are the best way.

 

68

To make a div expandable using bootstrap, we have written the following code:

 <div class="room-container"
 data-toggle="collapse"
 data-target="#desc-{{session_id}} .collapse"
 aria-expanded="false"
 aria-controls="desc-{{session_id}}">

 <h4 style="background-color:{{{color}}}" class="sizeevent event">
 {{title}}
 </h4>
// Write the element that should be clicked for expanding
<div class="session-speakers-list collapse">
  {{#speakers_list}}
  <a href="speakers.html#{{id}}">
  <p class="session-speakers-less">
  {{#if photo}}
    <p style="margin-right:20px" class="session-speakers">
     <img onError="this.onerror=null;this.src='./dependencies/avatar.png';" class="card-img-top" src="{{photo}}" style="width:10rem; height:10rem; border-radius:50%;"/>
     </p>
    {{/if}}
    </p>
   </a>
  </div>
</div>

The class ‘.collapse’  will be added to the DOM Elements that will be expanded on click. That’s the easiest way to make a div expandable on click.

Using Partial in Handlebars and Reusing Code

Open Event Webapp uses handlebar partials for optimizing code. We can reuse a template using Handlebars partial.

How to use Handlebars partial ?

To use Handlebars partial, we have to follow some easy steps:

Step 1: In the .hbs file containing code, register your partial by using function Handlebars.registerPartial 

Handlebars.registerPartial('myPartial', '{{name}}')

Step 2: Calling the partial

{{> myPartial }}

In Open-Event Webapp we have made partials for common templates like navbar and footer.

1. // Navbar template (navbar.hbs)

  
 <!-- Fixed navbar -->
 <nav class="navbar navbar-default navbar-fixed-top">
  <div class="container">
   <div class="navbar-header navbar-left pull-left">
    <a class="navbar-brand" href="{{ eventurls.main_page_url }}">
    {{#if eventurls.logo_url}}
    <img alt="{{eventurls.name}}" class="logo logo-dark" src="{{  eventurls.logo_url }}">
    {{else}}
    {{ eventurls.name }}
    {{/if}}
    </a>
   </div>
 <div class="navbar-header navbar-right pull-right">
   <ul style="margin-left:20px" class="nav navbar-nav pull-left">
   {{#sociallinks}}
   {{#if show}}
    <li class="pull-left"><a href="{{link}}" style="padding-right:0; padding-left:0;margin-left:15px"><i class="fa fa-lg fa-{{icon}}" aria-hidden="true" title="{{{icon}}}"></i></a></li>
   {{/if}}
   {{/sociallinks}}
   </ul>
 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse" style="margin-left:1em;margin-top:1em;">
   <span class="sr-only">Toggle navigation</span>
   <span class="icon-bar"></span>
   <span class="icon-bar"></span>
   <span class="icon-bar"></span>
 </button>
 </div>

 <div class="hidden-lg hidden-md hidden-sm clearfix"></div>
   <div class="collapse navbar-collapse">
    <ul class="nav navbar-nav navbar-right">
     <li class="navlink"><a id="homelink" href="index.html">Home</a>
     {{#if timeList}}
     <li class="navlink">
     <a id="schedulelink"href="schedule.html">
     Schedule</a>
     </li>
     {{/if}}
     {{#if tracks}}
     <li class="navlink">
      <a id="trackslink" href="tracks.html">Tracks</a>
    </li>
  {{/if}}
     {{#if roomsinfo}}
     <li class="navlink">
      <a id="roomslink" href="rooms.html">Rooms</a>   
     </li>
    {{/if}}
    {{#if speakerslist}}
    <li class="navlink">
      <a id="speakerslink" href="speakers.html">Speakers</a>
     </li>
    {{/if}}
   </ul>
     </div>
   </div>
 </nav>
//Compiling Template by providing path

2. const navbar = handlebars.compile(fs.readFileSync(__dirname + '/templates/partials/navbar.hbs').toString('utf-8'));
// Register Partial

3. handlebars.registerPartial('navbar', navbar);

Templating with Handlebars and Nodejs

There are many frameworks present today that does DOM manipulation. But, DOM manipulation is useful only with simpler Javascript apps. If we want to deal with huge amount of data, we will need to take the support of templating.

One of the templating libraries is Handlebar.js. Handlebars.js is the lightest and one of the fastest templating libraries I have worked with.

How to work with Handlebars ?

Handlebars can be said as the superset of Mustache templating language. In Open-event-web app, Handlebars is used along with Node.js.

1. To install Handlebars
npm install handlebars
2. To include handlebars
const handlebars = require('handlebars');
const fs         = require('fs-e'xtra);
3. To compile a file ( Extension .tpl or hbs)
const tpl = handlebars.compile(fs.readFileSync(__dirname +   '/templates/schedule.hbs').toString('utf-8'));
fs.writeFileSync(distHelper.distPath + '/' + appFolder + '/tracks.html', tpl(jsonData));

Here jsonData is the JSON Array of objects provided to the template.

An example of a .hbs templating file can be :

 <div class="track-names col-md-2">
 {{# tracks}}
 {{#if title}}
 
 <ul class="title-inline">
 <li style="background-color:{{{color}}}" class="titlecolor"></li>
 <li>{{title}}</li>
 </ul>
 
 {{/if}}
 {{/tracks}} 
 </div>

 

 

Twitter Section Using loklak webtweets

In Open event web app, the user can provide URL of social links such as Twitter, Facebook etc in the event.json file inside the ZIP. The previous functionality was to use Twitter API and to generate a timeline showing the tweets of the twitter URL mentioned in event.json by user. But, it can be done by following another approach which reduces the third party dependency i.e Loklak-webtweets.

I have implemented the twitter section using loklak webtweets which can be done very easily.

Step 1:  Including necessary files from loklakwebtweets repository inside index.html. You can find them in js/ folder of this repository.

<script src="./dependencies/jquery.min.js"></script>
<script src="./dependencies/bootstrap.min.js" type="text/javascript"></script>
<script src="./dependencies/loklak-fetcher.js" type="text/javascript"></script>
 <script src="./dependencies/tweets.js" type="text/javascript"></script>

 

Step 2:  Specify the data source in HTML from which twitter data will be fetched. Here I have extracted the last word from the twitter URL provided by the user and passed it to HTML.

const sociallinks = Array.from(event.social_links);
 var twitter ="";
 sociallinks.forEach((link) => {
  if(link.name.toLowerCase() === "twitter") {
   twitter = link.link;
  }
 }) 
 const arrayTwitterLink = sociallink.split('/');
 const twitterLink = arrayTwitterLink[arrayTwitterLink.length - 1];
 
 const urls= {
   twitterLink: twitterLink,
   tweetUrl: twitter,
 };

This code will search twitter link in social links array present in event.json and get its last character which will be provided to data-from and data-query attribute of HTML.

 <section class="sponsorscont">
  <div class="tweet-row">
   <div class="col-sm-12 col-md-12 col-xs-12">
    <i class ="social_twitter fa fa-twitter"></i>
     <div class="tweets-feed" id="tweets" data-count=50 data-query="    {{{eventurls.twitterLink}}}" data-from="{{{eventurls.twitterLink}}}">
     <div class="arrow-up"></div>
      <p id="tweet" class="tweet">
       Loading...
     </p>
   <span style="margin-bottom: 20px;" id="dateTweeted"></span>
    <p>Follow<u>
    <b><a href="{{eventurls.tweetUrl}}"/>
     @{{eventurls.twitterLink}}</a>
    </b></u> for more updates</p> 
     </div> 
    </div>
  </div>
</section>

Step 3 : Now we just need to add styling so that it looks decent. For that, I have written some SASS.

.tweets-feed {
   color: $black;
   line-height: 30px;
   font-size: 20px;
   transition: opacity 0.2s linear;
   margin-bottom: 20px;
   height: 100px;
 
  a {
   color: $black;
   text-decoration: underline;
   font-weight: 700;
  } 

  #dateTweeted {
   font-size: 15px;
   display: block;
  }

}

.tweet-row {
   padding: 0 80px;
   margin-bottom: 80px;
   .social_twitter {
     font-size: 60px;
     margin-bottom: 12px;
  }
}

The output from the above code is a well designed Twitter section fetching tweets from the URL provided as a string in event.json by user.

tweet

 

Responsive Image Overlay

Image overlay is a very common concept in front-end development. It is easy to implement but difficult when we deal it with different screen sizes, where we need to cover the image with the overlay each time the screen size is changed. I have gone through various blog posts when I need to implement the same for Open-event webapp and researched a solution that works for all screen sizes without any media query.

1234

How to add an overlay to an image ?

If we need four images in a single row nearly 300*300px.  The code below shows the markup.

image-holder : The parent class to take the image and overlay inside it.

background-image: This class takes image source.

responsive-overlay: This is the key point to make it responsive. Responsive-overlay contains a class hover-state to add overlay absolutely and a class social-links.

social-links: It adds content to hover-state.

 

<div class="image-holder">
  <img class="background-image" alt="" src="">
   <div class="responsive-overlay">
     <div class="hover-state text-center preserve3d">
       <div class="social-links vertical-align">

       </div>
     </div>
   </div>
 </div>

The styling is written with SASS in .scss file as shown below.

//overlayimage and backgroundshade can be set in config.scss

 .image-holder {
   position: relative;
   overflow: hidden;
   margin-bottom: 12px;

   .background-image {
     height: 300px;
     width: 300px;
     display: block;
     margin: 0 auto;
     background-color: $background-shade;
    }
 
   .responsive-overlay {
     @include responsiveoverlay;

    .preserve3d {
       height: 300px;
      }

    .hover-state {
     @include hoverstate;
     height: 300px;
     width: 300px;
    }

  @mixin responsiveoverlay {
     height: 100%;
     position: absolute;
     top: 0;
     width: 100%;
}

   @mixin hoverstate {
     background: $overlayimage;
     display: block;
     height: 300px;
     left: 0;
     margin: 0 auto;
     opacity: 0;
     position: relative;
     top: 0;
     -moz-transition: all 0.3s ease-out;
     -webkit-transition: all 0.3s ease-out;
     transition: all 0.3s ease-out;
     width: 300px;
     z-index: 2;
   }

This code will work for responsiveness as well. The main catch here is the responsive-overlay class which is made 100% in width but set to position absolute. The images which are 300 * 300 px in size will take an overlay of the same size because of hover-state class. Instead, if we adjust sizes of images in small screens the above code will adjust overlay on the image automatically.

Like, on tablets we can have an overlay like this.

345

And on mobile screen output is like that :

23

Conclusion

Responsiveness is easy if we follow correct concepts. Here, the concepts of absolute and relative positioning in CSS have done the magic. Now we can play by adding different contents and effect on hover following the same basics.

Handling images inside ZIP or with URL’s

The Open-event webapp generator now provides the user a facility to upload the speakers and sponsors images inside the ZIP as images/speakers and images/sponsors.  After that the user will just have to specify the path of the images inside JSON such as /images/speakers/photoname.jpg  instead of URL’s.

How It Works ?

Whenever a user uploads a ZIP containing different files, it is extracted in dist/ folder according to the filename inside ZIP.

//fold.js... function to extract files according to folder name

var admZip = require('adm-zip');
const distPath = __dirname + '/../../../dist';
const uploadsPath = __dirname + '/../../../uploads';
const mockPath = __dirname + '/../../../mockjson'

copyUploads: function(appFolder, uploadedFile) {
 const appPath = distPath + '/' + appFolder;
 fs.mkdirpSync(appPath + '/json');
 var zip = new admZip(uploadedFile);
 var zipEntries = zip.getEntries(); 

 zipEntries.forEach(function(zipEntry) {
 
 switch(zipEntry.entryName){
 case 'images/speakers/':
 zip.extractEntryTo("images/speakers/", appPath ); 
 break;
 case 'images/sponsors/':
 zip.extractEntryTo("images/sponsors/", appPath ); 
 break;
 case 'audio/':
 zip.extractEntryTo("audio/", appPath);
 break;
 case 'sessions':
 zip.extractEntryTo("sessions", appPath +'/json/');
 break;
 case 'speakers':
 zip.extractEntryTo("speakers", appPath +'/json/');
 break;
 case 'microlocations' :
 zip.extractEntryTo("microlocations", appPath+'/json/');
 break;
 case 'event' :
 zip.extractEntryTo("event", appPath +'/json/');
 break;
 case 'sponsors' :
 zip.extractEntryTo("sponsors", appPath +'/json/');
 break;
 case 'tracks':
 zip.extractEntryTo("tracks", appPath +'/json/');
 break;
 default:
 }

This will send all the speaker images to images/speakers in dist and all sponsors images to images/sponsors in dist ( dist is the folder served to the user).

 

const appFolder = reqOpts.email + '/' + slugify(reqOpts.name);
 speakers.forEach((speaker) => {
  if ((speaker.photo !== null) && (speaker.photo.substring(0, 4) ===     'http')) {
 speaker.photo = urlencode(distHelper.downloadSpeakerPhoto(appFolder,    speaker.photo));
 }
 else {
 var reg = speaker.photo.split('');
 if(reg[0] =='/'){
 speaker.photo =    urlencode(speaker.photo.substring(1,speaker.photo.length));
 }
 
 }

 });
//dist.js

downloadSpeakerPhoto: function(appFolder, photoUrl) {
 const appPath = distPath + '/' +appFolder;
 const photoFileName = photoUrl.split('/').pop();
 const photoFilePath = 'images/speakers/' + photoFileName;

 console.log('Downloading photo : ' + photoFileName);
 downloadFile(photoUrl, appPath + '/' + photoFilePath);
 return photoFilePath;
 },

The code above shows how the image URL’s are split on the basis of ‘/’ to get the last element of the URL that is photoFileName. After that the image can be written inside the folder.

images/speakers/' + photoFileName

This code also handles the case when the images are downloaded by taking URL paths from JSON. If the photo in JSON has a string matched with ‘HTTP’ .Then it is downloaded in images/speakers/ . Finally, the image is downloaded with the same path as specified in JSON which can then be easily related in HTML image tag.

//dist.js Function to download files

const downloadFile = function(url, filePath) {
 const fileStream = fs.createWriteStream(filePath);

 fileStream.on('error', function(err) {
 console.log(err);
 });
 try {
 request(url).pipe(fileStream);
 } catch (err) {
 console.log(err);
 }
};

That’s how the images inside the ZIP and the image URL’s inside JSON files are handled in Open-event webapp generator.

Creating Dynamic Footer with Popover

In Open-Event Webapp generator, the track page height varies according to the popover that appears on hovering the tracks. The problem with this design was the footer of the page that always remains static and produce a bad UI to user.

12

So, I have decided to make footer dynamic so that it varies it’s position according to the popover appeared on hover. The approach was a bit tricky but the diagram below will make it easy to understand.

Dynamic footer

The following code will work on hovering the track.

//popover.js 

var outerContheight= $('.main').offset().top + $('.main').outerHeight();
var tracknext= $(track).next();
var tracktocheck= track.offset().top + track.outerHeight() + 
 tracknext.outerHeight() + 15;
 var shift= tracktocheck - outerContheight;
 if(shift > 0){
 
 $('.footer').css({
 'position':'absolute',
 'top': outerContheight + shift,
 'width':'100%',
 'z-index': '999'
 })
 }

If shift > 0 which is calculated as shown in the above code it means that the footer needs to be shifted and hence we shift the footer by setting absolute position in CSS. Else we set position: static for footer.

 $('.footer').css({
 'position':'static'
 })

After following the above approach the footer position changes according to the popover. Here is the screencast for the approach.

 

Optimizing page load time

The average size of a web page has been growing at an accelerating rate over the last few years. Last week, when the open-event webapp was tested, it was very slow to load because of loading 67.2 MB data ( which contained audio and image files ) on the web page.I have taken following steps which are good to read to make any web application load faster.

1. Stop preloading of audio files

The HTML5 audio player loads all audio files by default on the page. To stop this we have to change the code as

<audio controls preload="none">
 <source src="{{audio}}" type="audio/mpeg">
 </audio>

Setting preload option to none help us to load audio only when it is clicked. Hence it decreases the HTTP calls and decreases the page loading time.

Previously

Network tab with Audio files takes 68.0MB  load and 13.6 minute loading time.

fail

Now

29

Network tab with option <audio controls preloaded=”none”> takes 1.1 MB load and  1 minute loading time. This was a huge improvement but more optimizations can be done.

2. Using Compression Middleware

Express compression middleware works like a charm by compressing the response coming to the web page. It is too simple to add.

$ npm install compression
var compression = require('compression')
var express = require('express')

var app = express()

// compress all requests
app.use(compression())

// add all routes

This has compressed the responses and decreased the page load time.

3.  Reduce the number of HTTP requests

Another great way of speeding up your web pages is to simply reduce the number of files that need to be loaded.

Combine files

Combining multiple stylesheets into one file is a really useful way of eliminating extra HTTP requests. This strategy can also be adopted for your JavaScript files. A single larger CSS or JavaScript file will often load quicker because more time can be spent downloading the data rather than establishing multiple connections to a server.

After such optimizations, the webapp loads now in 8-15 seconds with DOM loaded in 2 seconds.

fulloptimize

The result of these optimizations are awesome. We can test the page speed by using various tools like pagespeed, speedtracer etc.

Optimization with SASS

The problem with using CSS is that it can become repetitive when used in large projects. Like using same values of margins, paddings, radius. SASS provides a way to store these values in ONE variable and to use that variable instead.

SASS provides various concepts that optimize CSS. Like discussed above using variables for all the color names and fonts will remove the repetition. A piece of code from Open-Event-Webapp is shown below for declaring variable names.

//config.scss

@charset "UTF-8";
 
// Colors
$black: #000;
$white: #fff;
$red: #e2061c;
$gray-light: #c9c8c8;
$gray: #838282;
$gray-dark: #777;
$gray-extra-dark: #757575;
$gray-extra-light:#e7e7e7 !default;
$gray-perfect :#ddd;
$blue: #253652;
$orange: #e12b00;
$vivid-blue: #2196F3;
$pure-orange: #ff8700;
$light-purple: #ebccd1;
$red-dark: #e52d27;
$light-black: #333333;
$light-skyblue : #b7cdff !default;
$gray-trackshade: #999;
$blue-shade: #2482d3;
$dark-black: rgba(22, 22, 22, 0.99) !default;
$black-main:#232323 !default;
$timeroom-color:rgba(0,0,0,.10) !default;
$session-color: $gray-trackshade !default;
$header-color: #f8f8f8 !default;
$main-background: #fff !default;

// Corp-Colors
$corp-color: $white !default;
$corp-color-dark: darken($corp-color, 15%) !default;
$corp-color-second: $red !default;
$corp-color-second-dark: darken($corp-color-second, 15%) !default;

Nesting

SASS supports nesting concept. The nested SCSS changes to CSS when compiled. A good approach follows nesting elements to three-degree maximum.

// Nesting in application.scss session-list (Open-Event-Webapp)

.session{

  &-list {

    li{
     cursor: pointer;
      }
   .label {
     background: #ff8700;
     border-color: $light-purple;
     color: #FFFFFF;
     font-weight: 500;
     font-size: 80%;
     margin-left: -8px;
    }
   .session-title {
      margin-top: 0.1em;
       .session-link {
         font-size: 12px;
        }
    }
 }
 &-location{
 text-align: left;
 }

}

The output generated after compilation will be

//Code from schedule.css in Open-event-webapp

.session-list li {
 cursor: pointer; 
}

.session-list .label {
 background: #ff8700;
 border-color: #ebccd1;
 color: #FFFFFF;
 font-weight: 500;
 font-size: 80%;
 margin-left: -8px; 
}

.session-list .session-title {
 margin-top: 0.1em; 
}

 .session-list .session-title .session-link {
 font-size: 12px;
 }

.session-location {
 text-align: left; 
}

Mixins

Mixins are another way to optimize the SASS code. Mixins are just like functions that let us pass the values as parameters to make the code more flexible.

// Simple mixin for button
@mixin btn($color, $width, $height) {
 display: block;
 font: 16px “Open sans”, arial;
 color: $color;
 width: $width;
 height: $height;
}

Using these type of simple approaches make CSS more effective and efficient. It will enhance the workflow and help us to write cleaner code.

Debugging with Node

Nodejs is a powerful Event-driven I/O server-side JavaScript environment based on V8. Debugging of Node applications is not as easy as the browser code. But the node wonderful debugger can make it easy if it is used effectively.

How to start the debugger

For debugging node applications, we have to only run a debug command. Here for a quick demonstration, I have used Open-Event-Webapp fold.js code.

Suppose there is a problem in the script file and we have to debug it. All we have to do is to run the debug command from the directory containing the file.

node debug ./fold.js

The output screen will show something like this:

25

This brings us into the debug mode. When we are in debug mode, we can try various commands like ‘n’ for next, ‘s’ for the step into a function, ‘list(k)’ where k is the number of lines of the code you want on the screen.

26

The ‘n’ command always takes us to next instruction, hence in long codebases, it is always recommended to use ‘c’ or Continue Execution command for going to the next breakpoint.

To set the breakpoint, we can use the command:

setBreakpoint() or sb()

The snapshot shows the output once the breakpoint is set. We can check the values at the breakpoint by using command repl.

4

Debugging the code correctly can save a lot of time and effort. These techniques provided by the debugger are necessary to learn.

Alternates