Code page using SCSS


Today me and jaspreet was given the task of building a web page using sass. The page was simple and challenge is to use the newly learned features of the scss to build a web page.

Our instructor, Inderpreet Singh has given us the requirements that there should be a header. Inside of a header, there should be a logo and navigation bar. In main content we have to build the articles.

Using the scss was easy to get the solution up and running in 1 day

/// Enforcing utf - 8 encoding in the main stylesheets;
@charset 'utf-8';

 /************************
  VARIABLES DECLARATION
 ************************/
$main-font : ubuntu;
$background-color: rgba(251,244,244,.34);
$sass-pink-color: rgba(242,90,90,1);
$sass-green-color : rgba(23,198,201,1);
$theme-color: $sass-pink-color;
$link-color: $theme-color;
/// This color basically refers to the main navigation element font color
$nav-li-ft-pink-color : $theme-color;

/// This refers to the main navigation element background-colors ( as there will be 2 backgrounds so 2 colors)
$nav-li-bg-pink-color : $theme-color;
$nav-li-bg-white-color : rgba(255,255,255,.9);

/// navigation elements border color
$nav-li-border-color : $theme-color;

$card-bg-color: rgba(255,255,255,1);
$card-shadow-color: rgba(242, 91, 95, 0.75);
$card-border-color: $theme-color;

/// List of vendor prefixes
$prefixes: ('webkit','moz','ms');

/*****************
FUNCTIONS
****************/

/***************
MIXINS
***************/
/// Mixin for adding common flex properties
/// $dir - flex direction
/// $wrap - want to wrap or not;
@mixin add-flexbox($dir,$wrap,$justify,$align) {
  display : flex;
  flex-direction : $dir;
  flex-wrap : $wrap;
  justify-content : $justify;
  align-content : $align;
}

/// Mixin to prefix a property
/// $property - Property name
/// $value - Property value
@mixin prefix-property($property, $value) {
  @each $prefix in $prefixes {
    #{'-' + $prefix + '-' + $property}: $value;
  }
  // Below is the non prefixed property.
  #{$property}: $value;
}

// Give the no of columns and margin without any units
@mixin get-card-dimensions($cols,$margin) {
  margin : $margin * 1%;
  width : (100 - ($margin*$cols*2))/$cols * 1%;
}

/*
  General styles........
*/
html, body {
  width: 100%;
  margin: 0;
  padding: 0;
  font-family: $main-font;
  background-color: $background-color;
}

main , nav , header, footer {
  @include add-flexbox(row,wrap,space-around,center);
  @include prefix-property(box-sizing ,border-box);
}
a {
  text-decoration: none;
  color : $link-color;
}

.wd-col{
  width : 25%;
}

/*
header styles
*/
header {
  justify-content : flex-start;
  padding : 2rem;
}

.logo img {
  @include prefix-property(border-radius,50%);

  width : 8rem;
  height : 8rem;
  margin-right: 1rem;
}

/*
  Navigation styles
*/
nav {
  width: 80%;
}

li {
  @include prefix-property(box-sizing ,border-box);

  padding : 1rem;
  list-style: none;
  color : white;
  text-align : center;
}

.main-nav-1 , .main-nav-3 , .main-nav-6 , .main-nav-8 {
  background : $nav-li-bg-pink-color;
}

.main-nav-2 , .main-nav-4, .main-nav-5, .main-nav-7 {
  color : $nav-li-ft-pink-color;
  background : $nav-li-bg-white-color;
  border : .3rem solid $nav-li-border-color;
}

/*Articles styles*/

.card {
  $border-radius : 1;
  @include prefix-property(box-sizing ,border-box);
  @include get-card-dimensions(4,2);
  @include prefix-property(border-radius,$border-radius * 1rem);
  @include prefix-property(box-shadow, 0 0 14px 0 $card-shadow-color);

  /*margin : .5rem;*/
  background : $card-bg-color;
  border : .3rem solid $card-border-color;

  img {
    width : 100%;
    height : 15rem;
    display : block;
    margin : 0 auto;
  }
}

.ar-content-wrapper {
  padding : 2rem;
  padding-top : 1rem;
}

.title{
  margin-bottom : .5rem;
}

.author {
  $card-author-font :'Source Sans Pro', sans-serif;
  $card-author-ft-color : #999;

  margin-bottom : .5rem;
  font-family : $card-author-font;
  font-size : 0.8rem;
  text-transform : uppercase;
  color : $card-author-ft-color;
}

.social-icons {
  $social-bg-color: rgba(0,0,0,.9);
  @include prefix-property(box-sizing ,border-box);
  @include add-flexbox(row,wrap,center,center);
  
  font-style: italic;
  background: $social-bg-color;

  border-bottom-left-radius: .5rem;
  border-bottom-right-radius: .5rem;

  cursor: pointer;

  .icon {
    padding : .5rem;
  }
}

/*
  MEDIA QUERIES
*/

@media (max-width : 1024px) {
  .wd-col{
    width : 50%;
    margin : 0;
  }

  .card {
    @include get-card-dimensions(3,1);
  }
}

@media (max-width : 768px) {
  .wd-col{
    width : 50%;
    margin : 0;
  }

  .card {
    @include get-card-dimensions(2,1);
  }
}

@media (max-width : 510px) {
  header {
    justify-content: center;
    padding : 0.2rem;
  }
  nav {
    width : 100%;
  }
  .card ,.wd-col{
    @include get-card-dimensions(1,1);
  }

}

Here is my solution for that

Implementing Drag And Drop


Today we will be implementing the Drag and Drop API. so these are the functions below that will be adding the event listeners

// drop event Handler
var handleDrop = function(e) {
if(e.preventDefault) { e.preventDefault(); }
if(e.stopPropagation) { e.stopPropagation(); }
console.log(‘handling drop,’,e.pageX,e.pageY,e);
const elemId = e.dataTransfer.getData(‘textId’);
const domElem = document.getElementById(elemId);
domElem.style.top = addPixels(pageY) || addPixels(e.pageY);
domElem.style.left = addPixels(pageX) || addPixels(e.pageX)
return false;
}

// drag Start Handler…
var handleDragStart = function(event){
event.stopPropagation();
event.dataTransfer.setData(‘textId’, event.currentTarget.id);
}
var handleDragEnter = function(e) {
e.preventDefault();
e.stopPropagation();
console.log(“dragEnter”);
return false;
};

var handleDragOver = function(e) {
e.preventDefault();
e.stopPropagation();
console.log(“dragOver”,e.pageX,e.pageY);
pageX = e.pageX;
pageY = e.pageY;
return false;
};

var handleDragLeave = function(e) {
e.preventDefault();
e.stopPropagation();
console.log(“dragLeave”);
return false;
};
//handle drag end….
var handleDragEnd = function(e) {
e.stopPropagation();
e.preventDefault();
console.log(e,e.pageX,e.pageY,””)
e.currentTarget.style.opacity = 1;
}

The above methods as explained in the previous post as well, will be the event listeners for the various drag and drop event listeners.

var addingDragDropEventListeners = function(elem) {
elem.setAttribute(‘draggable’,true);
elem.addEventListener(‘dragstart’,handleDragStart,false);
elem.addEventListener(‘dragenter’, handleDragEnter);
elem.addEventListener(‘dragleave’, handleDragLeave);
elem.addEventListener(‘dragend’,handleDragEnd);
return elem;
}

This method as it’s name specifies about adding the drag and drop event listeners to the specified DOM element.

addingDragDropEventListeners(textControlDiv);
This will add the drag and drop to the current element :). This is all about how I add the drag and drop API to the text box.

Drag And Drop API


Drag and drop (DnD) is a first class citizen in HTML5! The spec defines an event-based mechanism, JavaScript API, and additional markup for declaring that just about any type of element be draggable on a page

Creating draggable content

Making an object draggable is simple. Set the draggable=true attribute on the element you want to make moveable. Just about anything can be drag-enabled, including images, links, files, or other DOM nodes.

The basic markup may look something like this:

id="columns">
class="column" draggable="true">
A
class="column" draggable="true">
B
class="column" draggable="true">
C
</div>

For example, dragging the logo on google.com produces a ghost image:

Dragging an image in the browser

Listening for Dragging Events

There are a number of different events to attach to for monitoring the entire drag and drop process:

  • dragstart
  • drag
  • dragenter
  • dragleave
  • dragover
  • drop
  • dragend

1. Starting a Drag

Once you have draggable="true" attributes defined on your content, attachdragstart event handlers to kick off the DnD sequence for each column.

This code will set the column’s opacity to 40% when the user begins dragging it:

function handleDragStart(e) {
  this.style.opacity = '0.4';  // this / e.target is the source node.
}

var cols = document.querySelectorAll('#columns .column');
[].forEach.call(cols, function(col) {
  col.addEventListener('dragstart', handleDragStart, false);
});

2. dragenter, dragover, and dragleave

dragenter, dragover, and dragleave event handlers can be used to provide additional visual cues during the drag process. For example, when a column is hovered over during a drag, its border could become dashed. This will let users know the columns are also drop targets.

<style>
.column.over {
  border: 2px dashed #000;
}
</style>
function handleDragStart(e) {
  this.style.opacity = '0.4';  // this / e.target is the source node.
}

function handleDragOver(e) {
  if (e.preventDefault) {
    e.preventDefault(); // Necessary. Allows us to drop.
  }

  e.dataTransfer.dropEffect = 'move';  // See the section on the DataTransfer object.

  return false;
}

function handleDragEnter(e) {
  // this / e.target is the current hover target.
  this.classList.add('over');
}

function handleDragLeave(e) {
  this.classList.remove('over');  // this / e.target is previous target element.
}

var cols = document.querySelectorAll('#columns .column');
[].forEach.call(cols, function(col) {
  col.addEventListener('dragstart', handleDragStart, false);
  col.addEventListener('dragenter', handleDragEnter, false);
  col.addEventListener('dragover', handleDragOver, false);
  col.addEventListener('dragleave', handleDragLeave, false);
});

There are a couple of points worth covering in this code:

  • The this/e.target changes for each type of event, depending on where we are in the DnD event model.
  • In the case of dragging something like a link, we need to prevent the browser’s default behavior, which is to navigate to that link. To do this, call e.preventDefault() in the dragover event. Another good practice is to return false in that same handler. Browsers are somewhat inconsistent about needing these, but they don’t hurt to add.
  • dragenter is used to toggle the ‘over’ class instead of the dragover. If we were to use dragover, our CSS class would be toggled many times as the event dragover continued to fire on a column hover. Ultimately, that would cause the browser’s renderer to do a large amount of unnecessary work. Keeping redraws to a minimum is always a good idea.

3. Completing a Drag

To process the actual drop, add an event listener for the drop and dragend events. In this handler, you’ll need to prevent the browser’s default behavior for drops, which is typically some sort of annoying redirect. You can prevent the event from bubbling up the DOM by calling e.stopPropagation().

The DataTransfer object

The dataTransfer property is where all the DnD magic happens. It holds the piece of data sent in a drag action. dataTransfer is set in the dragstart event and read/handled in the drop event. Calling e.dataTransfer.setData(format, data) will set the object’s content to the mimetype and data payload passed as arguments.

Dragging properties

The dataTransfer object exposes properties to provide visual feedback to the user during the drag process. These properties can also be used to control how each drop target responds to a particular data type.

dataTransfer.effectAllowed
Restricts what ‘type of drag’ the user can perform on the element. It is used in the drag-and-drop processing model to initialize the dropEffect during thedragenter and dragover events. The property can be set to the following values: none, copy, copyLink, copyMove, link, linkMove, move, all, and uninitialized.
dataTransfer.dropEffect
Controls the feedback that the user is given during the dragenter anddragover events. When the user hovers over a target element, the browser’s cursor will indicate what type of operation is going to take place (e.g. a copy, a move, etc.). The effect can take on one of the following values: none, copy, link, move.
    e.dataTransfer.setDragImage(imgElement, x, y)
Instead of using the browser’s default ‘ghost image’ feedback, you can optionally set a drag icon

var dragIcon = document.createElement('img');
dragIcon.src = 'logo.png';
dragIcon.width = 100;
e.dataTransfer.setDragImage(dragIcon, -10, -10);

Conclusion

No one will argue that HTML5’s DnD model is complicated compared to other solutions like JQuery UI. However, any time you can take advantage of the browser’s native APIs, do so! After all, that’s the whole point of HTML5…which is to standardize and make available a rich set of APIs that are native to the browser.

Integrating Barcharts in React application


Today I will integrate the BarCharts into my application. It’s will be really easy but have to see that bar charts will be updating. So let’s start by first adding the bar-charts and then checking what needs to be done to overcome any issue faced.

Following is the charts component that will add the charts to the application.

export default class MyChart extends Component {
constructor(props) {
super(props);
this.state = {
data : getDefaultData()
}
}
componentDidMount() {
let site = this.props.site;
let siteData = convertRdDataToAnnotations(site.rdData);
this.setState({
data : newChartData(siteData)
})
}
componentWillReceiveProps(nextProps) {
let site = nextProps.site;
let siteData = convertRdDataToAnnotations(site.rdData);
this.setState({
data : newChartData(siteData)
})
}
shouldComponentUpdate(nextProps,nextState) {
debugger;
let currentData = this.state.data.datasets[0].data;
let newData = nextState.data.datasets[0].data;
for(let i = 0; i < currentData.length; ++i) {
if(currentData[i] !== newData[i] ) {
return true;
}
}
return false;
}
render() {
let {data} = this.state
return (

);
}
}

The only new function I am adding is the componentWillReceiveProps. I will come to it for sure later on this post. First, let’s see the function that will create the data for the charts.

const convertRdDataToAnnotations = function(rdData, siteUrl){

let pdfDataAnnotations = [],
pdfgeneralAnnotations = [],
pdfImageAnnotations = [],
pdfTextAnnotations = []
console.log(rdData,”rdData that is being passed”);
if(typeof rdData == ‘undefined’){
console.log(“undefined”);
return {
data: pdfDataAnnotations.length,
image: pdfImageAnnotations.length,
general: pdfgeneralAnnotations.length,
text : pdfTextAnnotations.length
}
}
else if(typeof rdData !== ‘object’){
console.log(“not object”);
return {
data: pdfDataAnnotations.length,
image: pdfImageAnnotations.length,
general: pdfgeneralAnnotations.length,
text : pdfTextAnnotations.length
}
}
else if(rdData.commentBoxes || rdData.totalElements) {
console.log(“object”);
//console.log(‘rdData’,rdData)
// Pdf Data Anntotions Data
pdfDataAnnotations = rdData.highlighters.filter(function( obj ) {
return obj.type == “normal”;
});
// Pdf Image Anntotions Data
pdfImageAnnotations = rdData.highlighters.filter(function( obj ) {
return obj.type == “image”;
});
// pdfImageAnnotations.highlighters = highlighterArr;

//pdf general Annotation
pdfgeneralAnnotations = rdData.circles.filter(function( obj ) {
return obj.type == “normal”;
});

pdfTextAnnotations = rdData.textHighlighters;
}

return {
data: pdfDataAnnotations.length,
image: pdfImageAnnotations.length,
general: pdfgeneralAnnotations.length,
text : pdfTextAnnotations.length
}
//let status = generatePdf(pdfDataAnnotations, pdfImageAnnotations, pdfgeneralAnnotations, siteUrl);
// console.log(status,”pdf generated status”);
//return status;
}

This function will create a data for the charts and then we will be changing this data to the format in which chart.js will accept the data.

const newChartData = ({general,data,image,text}) => {
return {
labels: [“General”, “Data”, “Images”,”Text”],
datasets: [{
label: ‘# of Anotations’,
data: [general, data, image,text],
backgroundColor: [
‘rgba(22, 159, 12, 0.2)’,
‘rgba(54, 162, 235, 0.2)’,
‘rgba(255, 206, 86, 0.2)’,
‘rgba(106, 106, 86, 0.2)’
],
borderColor: [
‘rgba(22, 159, 12, 1)’,
‘rgba(54, 162, 235, 1)’,
‘rgba(255, 206, 86, 1)’,
‘rgba(106, 106, 86, 1)’
],
borderWidth: 1
}],
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
}
}

The above function will be creating the data in the format in which barcharts will be accepting it. Now everything is done, but how will the charts.js will update it based on the new data. So this life cycle method of React come to our rescue, what it will do is update the charts based on the component’s data.

componentWillReceiveProps(nextProps) {
let site = nextProps.site;
let siteData = convertRdDataToAnnotations(site.rdData);
this.setState({
data : newChartData(siteData)
})
}

This will update the state based on new Data.

Charts.js Basics


I chose Chart.js because it can be learned and leveraged quickly. It’s designed with simplicity in mind, yet is extremely customizable. In my experience, charting libraries fall onto a spectrum of complexity, where more complex libraries offer deeper customization.

Installing Chart.js

Again, Chart.js is focused on being easy. Easy to learn, easy to leverage, and easy to install. If you’d like to dive into the actual code, check out the GitHub project.

You only need two things to use Chart.js.

1) The library – for this guide, I recommend using a CDN because it’s the easiest way to get up and running fast.

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.4/Chart.min.js"></script>

2) A <canvas> element, as Chart.js leverages HTML5 canvas.

</canvas><canvas id="myChart"></canvas>

Alternatively, you can use a package manager to download the library.

Line Chart

This is all you need to create a minimum line chart in Chart.js. Just put it inside of a <script></script> somewhere in your <body> after you declare the HTML5 canvas.

var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
  type: 'line',
  data: {
    labels: ['M', 'T', 'W', 'T', 'F', 'S', 'S'],
    datasets: [{
      label: 'apples',
      data: [12, 19, 3, 17, 6, 3, 7],
      backgroundColor: "rgba(153,255,51,0.4)"
    }, {
      label: 'oranges',
      data: [2, 29, 5, 5, 2, 3, 10],
      backgroundColor: "rgba(255,153,0,0.4)"
    }]
  }
});

If this code looks intense, don’t worry! All Chart.js examples follow the above format for the most part, so you only have to learn it once. Lets go line by line to understand what’s happening.

var ctx = document.getElementById("myChart").getContext('2d');

This line gets a reference to the <canvas> element we created earlier, then calls the getContext method on it. The getContext method returns an object that provides methods and properties for drawing on the canvas. We store this in a variable named ctx.

var myChart = new Chart(ctx, {
  type: 'line',
  data: // array of line data goes here
});

Here we are creating the chart object. I’ve excluded the data for a moment to focus on the type property, which determines the type of chart we want. Chart.js’ new Chart() constructor takes two parameters:

  1. Either a reference to a </canvas><canvas> element that the chart will be rendered on, or a reference to its 2d drawing context (here we are using the 2d context). Regardless of which you use, the Chart.js convention is to call it ctx.
  2. An object literal containing the data and the configuration options that Chart.js will use to build your chart. The required properties are type and data. In our example type is ‘line’ because we want a line chart. data is the data you used to populate the chart.

Bar Chart

Bar charts are (mostly) just line charts that look a bit different. By changing one line of our previous example, we can create a bar chart.

type: 'line'

to:

type: 'bar'

Yes, it’s really that easy.

The full documentation on bar charts can be found here.

Here’s the full code for this example:

var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
  type: 'bar',
  data: {
    labels: ["M", "T", "W", "R", "F", "S", "S"],
    datasets: [{
      label: 'apples',
      data: [12, 19, 3, 17, 28, 24, 7]
    }, {
      label: 'oranges',
      data: [30, 29, 5, 5, 20, 3, 10]
    }]
  }
});

Polar Charts

Polar charts give each data point an equal amount of radial space. Segments with larger values extend further from the center of the graph. Here’s the polar chart for our apples data set.

As usual, specifying that this is a polar chart can be done with a single line. Change:

type: 'radar'

to:

type: 'polarArea'

But, the polar area is the first chart I’ve covered that can’t be used to compare two data sets. The previous examples were different ways of contrasting two arrays of equal length, whereas the polar chart (and pie chart, which will be covered next) only visualize a single group of numbers.

Here’s the full code for this example:

var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
  type: 'polarArea',
  data: {
    labels: ["M", "T", "W", "T", "F", "S", "S"],
    datasets: [{
      backgroundColor: [
        "#2ecc71",
        "#3498db",
        "#95a5a6",
        "#9b59b6",
        "#f1c40f",
        "#e74c3c",
        "#34495e"
      ],
      data: [12, 19, 3, 17, 28, 24, 7]
    }]
  }
});

Today I took my time to learn the charts.js basics and tomorrow I will integrate the same into my React application.

Loading and Re-rendering Data


Now the major step is to do in my project is to do loading and re-rendering of the data. These 2 areas are the major things which are equally important and are related sections.
First I will try to explain the 2 things:-

Loading

The Loading of the Data refers to getting and creating the basic DOM elements which we have stored in the database.

Re-Rendering

Re Rendering of the data refers to the fitting of the DOM elements when browser window or screen size changes. This is the major component for our tool.

Ironically, first I will start with the Re-rendering of the data despite the title of the post contains the Loading first in the name.
So for the Re-rendering of the data we need to an event window.onResize.

// Re Render the Data….
var reRenderTheData = function() {
console.log(rdData, ‘rdData’);
sliderChange();
rdData.highlighters.forEach((highLighterObj, index) => {
renderElementFromUniqueString(highLighterObj);
})
rdData.circles.forEach((dotObj, index) => {
let dotElement = document.getElementById(dotObj.id);
let highlighterElement = document.getElementById(dotObj.highlighterID);
let commentElement = document.getElementById(rdData.commentBoxes[index].id);
renderElementFromDOM(dotObj, dotElement, highlighterElement);
renderElementFromDOM(rdData.commentBoxes[index], commentElement, dotElement);
})
rdData.textHighlighters.forEach((textObj,index) => {
renderElementFromUniqueString(textObj)
})
}

// This function will re render the component based on UniqueString elements.
var renderElementFromUniqueString = function(elementObj) {
let originalElement = loadElementFromUnqiueSelector(elementObj.element);
let element = document.getElementById(elementObj.id);
console.log(element, “highlighter element from dom”)
renderElementFromDOM(elementObj, element, originalElement);
}

// This function will re render the component based on the DOM elements..
// dot(type = highlighter) requires highlighter(DOM elements) to get there styles.
var renderElementFromDOM = function(elementObj, rdElement, domElement) {
if(elementObj.elemType == ‘dot’ && elementObj.type == ‘normal’) {
rdElement.style.top = elementObj.top;
rdElement.style.left = elementObj.left;
}else {
var getAllElementstyles = window.getComputedStyle(domElement, null);
if (getAllElementstyles.display == ‘none’) {
rdElement.style.display = ‘none’;
}
else {
rdElement.style.display = ‘block’;
if (elementObj.elemType == ‘highlighter’ || elementObj.elemType == ‘text’) {
// Here the rdElement is the dom element
let elemBoundingRect = domElement.getBoundingClientRect();
let dataObj = {
width: elemBoundingRect.width,
height: elemBoundingRect.height,
top: getTop(domElement),
left: elemBoundingRect.left
}
renderHighlighterCommonPart(rdElement, dataObj);
} else if (elementObj.elemType == ‘dot’ && elementObj.type == ‘highlighter’) {
// this is highligher dot..
let elemBoundingRect = domElement.getBoundingClientRect();
let dataObj = {
top: getTop(domElement),
left: elemBoundingRect.left
}
// Render the comment correctly as well

rdElement.style.top = addPixels(dataObj.top);
rdElement.style.left = addPixels(dataObj.left);
}
else {
// this is comment

let elemBoundingRect = domElement.getBoundingClientRect();
let dataObj = {
top: getTop(domElement),
left: elemBoundingRect.left
}

if (elementObj.type == ‘highlighter’) {
rdElement.style.top = dataObj.top + COMMENTBOX_HEIGHT / 2 + ‘px’;
rdElement.style.left = addPixels(dataObj.left);
} else {
rdElement.style.top = dataObj.top + HALF_CIRCLE + ‘px’;
rdElement.style.left = dataObj.left + HALF_CIRCLE + ‘px’;
}
}
}
}
}

These are basically 3 functions
reRenderTheData, renderElementFromUniqueString, renderElementFromDOM.

reRenderTheData is the main function which encloses the 2 other functions renderElementFromUniqueString –> which is a function which generates a DOM element from the unique string.

renderElementFromDOM —> To this function we basically pass the actual DOM element and then , using it’s present styles re rendering the style of the elements.
Now since the elements are being rendered properly, now it’s time to make an API call and get the actual data.

Selection_225

loadBasicComponents: This is the function which is going to load and create the new elements

var loadBasicComponents = function(rdData) {
console.info(rdData, “rdData”)
rdData.highlighters.forEach((highObj, index) => {
let dataObj = giveStylesToHighlighterObj(highObj);
let highLight = createHighLighter(dataObj);

// when you store your colors
highLight.style.borderColor = highObj.backgroundColor;
highLight.children[0].style.backgroundColor = highObj.backgroundColor;

if(highObj.type == ‘image’) {
let imageElem = loadElementFromUnqiueSelector(highObj.element);
if(highObj.newSource) {
setTheImageUrl(imageElem,highObj.newSource);
}
imageElem.addEventListener(‘click’, () => predictImageTool(imageElem, highObj.originalSource, dataObj, highObj), false)
}
document.body.appendChild(highLight);
})

rdData.textHighlighters.forEach((textHighObj, index) => {
let dataObj = giveStylesToHighlighterObj(textHighObj)
let highLight = createTextHighLighter(dataObj);
// when you store your colors
highLight.style.borderColor = textHighObj.backgroundColor;
highLight.children[0].style.backgroundColor = textHighObj.backgroundColor;
highLight.children[0].style.borderColor = textHighObj.backgroundColor;
highLight.children[0].addEventListener(‘click’,() => openTextControlBox(textHighObj.element,textHighObj.id),false);
highLight.children[1].style.backgroundColor = textHighObj.backgroundColor;
let textElem = loadElementFromUnqiueSelector(textHighObj.element)
if(textHighObj.newStyles) {
textElem.style.fontFamily = textHighObj.newStyles.fontFamily;
textElem.style.fontWeight = textHighObj.newStyles.fontWeight;
textElem.style.fontStyle = textHighObj.newStyles.fontStyle;
}
document.body.appendChild(highLight);
})
rdData.circles.forEach((circleObj, index) => {
if (circleObj.type == ‘highlighter’) {
circleObj.highlighter = true;
}
console.log(circleObj, “circle-obj”)
var dot = createDot(circleObj);
if (circleObj.type == ‘highlighter’) {
let highlighterDOM = document.getElementById(circleObj.highlighterID);
let highlighterStyles = highlighterDOM.getBoundingClientRect();
dot.style.top = addPixels(getTop(highlighterDOM));
dot.style.left = addPixels(highlighterStyles.left);
}
dot.id = circleObj.id;
dot.style.backgroundColor = circleObj.backgroundColor;
dot.setAttribute(‘data-commentbox’, circleObj.commentID);
dot.addEventListener(‘click’, commentBoxHighLighterToggle, false);
let commentBox = createComment(dot);
if (circleObj.type == ‘highlighter’) {
commentBox.style.top = removePixels(commentBox.style.top) + COMMENTBOX_HEIGHT / 2 + ‘px’;
}
document.body.appendChild(dot);
commentBox.children[1].value = rdData.commentBoxes[index].comment;
document.body.appendChild(commentBox);
})

// Reloading the count of variables.
if(rdData.totalElements) {
highlighterCount = rdData.totalElements.highlighterCount || 0;
dotCommentCount = rdData.totalElements.dotCommentCount || 0;
textHighlighterCount = rdData.totalElements.textHighlighterCount || 0;
}else {
highlighterCount = 0;
dotCommentCount = 0;
textHighlighterCount = 0;
}
}

Highlighters and general elements are rendered using the loadBasicComponents and then the re-renderfunction will help us to resize the elements.

Encryption/Decryption using cryptr


Now the next hurdle was to load the data in such a way that it should be available to any person who is visiting the link of annotated site but only the users who are authorized and created that site are able to save the site data.
So for that I am using the encryption and decryption of the userId’s. 

Encryption

Encryption is a security method in which information is encoded in such a way that only authorized user can read it. It uses encryption algorithm to generate ciphertext that can only be read if decrypted.

Types of Encryption

There are two types of encryptions schemes as listed below:

  • Symmetric Key encryption
  • Public Key encryption

SYMMETRIC KEY ENCRYPTION

Symmetric key encryption algorithm uses same cryptographic keys for both encryption and decryption of cipher text.

internet_technologies_tutorial

PUBLIC KEY ENCRYPTION

Public key encryption algorithm uses pair of keys, one of which is a secret key and one of which is public. These two keys are mathematically linked with each other.

internet_technologies_tutorial

Decryption

Decryption is the process of taking encoded or encrypted text or other data and converting it back into text that you or the computer can read and understand. This term could be used to describe a method of un-encrypting the data manually or with un-encrypting the data using the proper codes or keys.

Data may be encrypted to make it difficult for someone to steal the information. Some companies also encrypt data for general protection of company data and trade secrets. If this data needs to be viewable, it may require decryption. If a decryption passcode or key is not available, special software may be needed to decrypt the data using algorithms to crack the decryption and make the data readable.

So for using the encryption and decryption in my application, I am using cryptr npm package.

cryptr

cryptr is a simple encrypt and decrypt module for node.js

It is for doing simple encryption of values UTF-8 strings that need to be decrypted at a later time.

If you require anything more than that you probably want to use something more advanced or crypto directly.

The Cryptr constructor takes 1 required and 1 optional argument.

Cryptr(secret[, algorithm])

If an algorithm is not provided it defaults to aes-256-ctr.

DO NOT USE THIS MODULE FOR ENCRYPTING PASSWORDS!

Passwords should be a one way hash. Use bcrypt for that.

Install

npm install cryptr
How I am using this library is on the user creation I am storing the encryption of user inside another collection crypted users.

// on the creation of user
Accounts.onCreateUser((options,user) => {
console.log(“user created “,user._id,user);
if(user._id) {
const encryptedString = cryptr.encrypt(user._id),
decryptedString = cryptr.decrypt(encryptedString);

console.log(encryptedString,”encrypte”); // 8db06003a73db491e9a27ae9e10d106d29
console.log(decryptedString,user._id,”decrypted”); // bacon
if(encryptedString && decryptedString) {
const cryptId = CryptedUsers.insert({userId : user._id, encryptedUserId: encryptedString, date : new Date()})
console.log(cryptId,”cryptId”);
return user;
}
}
return user;
})

Storing the encrypted form of users and then using that encrypted user inside the mongo collection for loading of data.

Saving the Annotated Data


Now the biggest hurdle is to create the annotated data for storing in the database and then delivering that data to the user whenever he/she needs the data again. So first we will be creating objects that will be stored inside of the mongo collection.

Following are the methods that are self-explanatory as well. There are 3 methods basically createDot , createComment, createHighlighterObj. All will be going to create an object that will be stored in the database.

//This function will create 2 things
//create a circle DOM obj.
// highlighter ====> param will tell whether this circle is a highlighter circle / or normal one.
var createDot = function({
commentCircleID,
commentBoxID,
type,
top,
left,
highlighter,
highlighterID
}) {
let commentCircle = document.createElement(‘div’);
commentCircle.id = commentCircleID;
commentCircle.className = ‘rd-circle’;
commentCircle.style.backgroundColor = CURRENT_COLOR;
commentCircle.setAttribute(‘data-commentbox’, commentBoxID);
commentCircle.setAttribute(‘data-dot-type’, type);

if (highlighter)
commentCircle.setAttribute(‘data-highlighter’, highlighterID)

if (type == ‘normal’) {
commentCircle.style.top = top;
commentCircle.style.left = left;
}
return commentCircle;
}

// render the highlighter common part…
var renderHighlighterCommonPart = function(newElement, dataObj) {
// step 2 : A new Elem should be created which has same styles as of highlightedElem and append it to body
newElement.style.width = addPixels(dataObj.width);
newElement.style.height = addPixels(dataObj.height);
newElement.style.top = addPixels(dataObj.top);
newElement.style.left = addPixels(dataObj.left);
return newElement;
}

// Type can have 2 values,
// normal —> In normal highlighter we will create a dot as well
// image —-> In image highlighter we will create a dotted bordered element around image.
var createHighLighter = function(dataObj) {
let newElement = document.createElement(‘div’);

var deleteDiv = document.createElement(‘div’);
// deleteDiv.className = ‘rd-highlighter-delete-btn’;
// let buttonChild = document.createElement(‘button’);
deleteDiv.className = “rd-circle-close”;
deleteDiv.style.backgroundColor = CURRENT_COLOR;
let divText = document.createTextNode(“x”);
deleteDiv.appendChild(divText);
deleteDiv.addEventListener(‘click’, deleteHighlighter, false);
// deleteDiv.appendChild(buttonChild);
deleteDiv.style.position = ‘absolute’;
let divWidth = dataObj.width – 17;
deleteDiv.style.cursor = “pointer”;
deleteDiv.style.pointerEvents= “auto”;
deleteDiv.style.top = -2 + “px”;
deleteDiv.style.right = -2 + “px”;
newElement.appendChild(deleteDiv);

newElement.id = dataObj.id;
newElement.className = ‘rd-new-highlight’;
newElement.setAttribute(‘data-rdhighlighter-type’, dataObj.type);
newElement = renderHighlighterCommonPart(newElement, dataObj);
//newElement.style.display = ‘none’;
if (dataObj.type == ‘image’) {
newElement.style.border = ‘2px dotted ‘+CURRENT_COLOR;
} else {
newElement.style.border = ‘2px solid ‘+CURRENT_COLOR;
}
newElement.style.backgroundColor = “transparent”;
//newElement.style.cssText = toolObj.highlightedElem.style.cssText;
return newElement;
}

/// Since commentBox Appears where there are dot objects so we will
// make the comment Boxes appear right there using that dot circle.
var createComment = function(commentCircleDOMObj) {
let commentDiv = document.createElement(“div”);
let highLighter = commentCircleDOMObj.getAttribute(‘data-highlighter’);

commentDiv.className = ‘rd-comment’;
commentDiv.id = commentCircleDOMObj.getAttribute(‘data-commentBox’);
commentDiv.setAttribute(‘data-commentCircleID’, commentCircleDOMObj.id);
if (highLighter)
commentDiv.style.top = removePixels(commentCircleDOMObj.style.top) + COMMENTBOX_HEIGHT / 2 + ‘px’;
else
commentDiv.style.top = commentCircleDOMObj.style.top;

commentDiv.style.left = commentCircleDOMObj.style.left;

let pChild = document.createElement(‘p’);
let pChildText = document.createTextNode(‘Comment For’);
pChild.appendChild(pChildText);

let textAreaChild = document.createElement(‘textarea’);
let ParentDiv = document.createElement(‘div’);
ParentDiv.style.display = “flex”;
let okButtonChild = document.createElement(‘button’);
let okButtonText = document.createTextNode(‘OK’)
okButtonChild.appendChild(okButtonText);

// Binding the current Comment Button with the commentCircle
okButtonChild.setAttribute(‘data-circle’, commentCircleDOMObj.id);
okButtonChild.setAttribute(‘data-highlighter’, commentCircleDOMObj.getAttribute(‘data-highlighter’));
okButtonChild.addEventListener(‘click’, commentClick, false)

let delButtonChild = document.createElement(‘button’);
let delButtonText = document.createTextNode(‘DELETE’)
delButtonChild.appendChild(delButtonText);

// Binding the current Comment Button with the commentCircle
delButtonChild.setAttribute(‘data-circle’, commentCircleDOMObj.id);
delButtonChild.setAttribute(‘data-highlighter’, commentCircleDOMObj.getAttribute(‘data-highlighter’));
delButtonChild.addEventListener(‘click’, deleteCommentBox, false)

ParentDiv.appendChild(okButtonChild);
ParentDiv.appendChild(delButtonChild);

commentDiv.appendChild(pChild);
commentDiv.appendChild(textAreaChild);
commentDiv.appendChild(ParentDiv);
return commentDiv;
}

To actually save the data the user have to click on the save button and then make a call to the API. This facility of saving the data is only available to the authorized users 🙂 so adding the desired security automatically to your applications.

var saveData = function() {
console.log(rdData, “rdData…………”);
let fetchPath = API_PATH + ‘save-data’;
console.log(localStorage.getItem(‘authToken’), localStorage.getItem(‘userId’))

//Saving the count of variables as well…
rdData.totalElements.highlighterCount = highlighterCount;
rdData.totalElements.dotCommentCount = dotCommentCount;
rdData.totalElements.textHighlighterCount = textHighlighterCount;

// Call the api to save the data.
fetch(fetchPath, {
method: ‘post’,
headers: {
‘Accept’: ‘application/json, text/plain, */*’,
‘Content-Type’: ‘application/json’,
‘X-Auth-Token’: localStorage.getItem(‘authToken’),
‘X-User-Id’: localStorage.getItem(‘userId’)
},
mode: ‘cors’,
body: JSON.stringify({
data: rdData,
url: SITE_URL
})
})
.then(res => res.json())
.then(res => {
if (res.status == ‘success’) {
alert(‘Data has been saved successfully’)
} else if (res.statusCode == 500 || res.status == ‘error’) {
alert(‘There has been some error while saving the data’);
}
})
.catch(e => {
alert(e.message);
})
}

This method here shown will make an API call to the backend and will be saving the data in the mongo collection using fetch API. 

This is the backend API method to store the data.

//Path api/save-data
Api.addRoute(‘save-data’,{authRequired: true},{
post: function() {
let data = this.bodyParams.data;
let userId = this.user._id;
let siteUrl = this.bodyParams.url;
console.log(siteUrl,”siteUrl”);
if(userId){
let checkExistence = MirroredSites.find({userId : userId, url : siteUrl}).fetch();
if(checkExistence){
//console.log(“does exist”);
let rdDataId = MirroredSites.update({userId : userId, url : siteUrl},{$set : {rdData : data}});
return {
status : ‘success’,
statusCode : 200,
data : “data is entered into the collection”,
rdDataId : rdDataId
}
}
else{
console.log(“does not exist”);
return {
status : ‘error’,
statusCode : 500,
data : ‘user with this url is not found’,
}
}
}
else {
return {
status : ‘error’,
statusCode : 500,
data : ‘can not find user’
}
}
}
});