#StackBounty: Lazy load content of slides inside carousel (content is oEmbeds/iframes)

Bounty: 100

I am loading in ajax a modal/popin with a Bootstrap carousel in which each slide is (not an image as in many question about lazy loading) but an iframe from oEmbed from various social networks (facebook, instagram, twitter, …).

The issue is that when I click the button that laods the modal, ALL the slides content get loaded, that is to say 15 to 20 oembeds(eahc of them loading content tetx, image and and javascript…).

I would like to be clever about it and only “lazy loading” slide by slide or even smarter 3 slides by 3 slides.

I am just also mentioning for the sake of information that I am using scrollMonitor and Hubspot Messenger. But i’d rather use Bootstrap slide events to trigger the apparition/load of each slide or any suggestion you would have.

I’m using ruby on rails as back end language

The url of the oEmbed programmatically change as they are inputed from a Admin Backoffice and change on each Article/page but you’ll find below an example :

Page.html

//button to click to make modal appear
<a class="btn btn-primary" onclick="loadModal()" id="socialStoryModal">
      load modal
</a>

Load Modal with Hubspot Messenger in page.js

function loadModal() {  
      var msg;
      msg = Messenger().post({
        message:  'modal.html.erb',/see below the carousel
        showCloseButton: true,
        hideAfter: false
      }); 
    }

modal.html.erb
= Modal with the carousel and the social embeds (here can be up to 50 of them)

I’ve seen tons of libraries to lazy load images and even sometimes scripts/iframes but they all need to have directly add certain classes I n the block, which is of no help for me as I use oembed above and I have nowhere to put these lazy classes.

I need to make it work with these oEmbeds iframes.


Get this bounty!!!

#StackBounty: Google Maps API Winding path within polygon with rotate/grow/shrink/order options

Bounty: 50

I’m not certain what I’m trying to achieve is possible, but I know there are some creative people here and admittedly, I’m going to need some hand holding on this one.

Here is a mockup of what I’d like to add:
Example

Using these links below for reference of what’s possible. I need to apply each of these features to every user-drawn polygon.

  1. Auto-generating/filling a shape inside a polygon. For my purpose, I don’t need boxes, I need a single winding path to fill every user-created polygon. I need that winding path to hug the boundaries of the polygon as in my picture above.
    Draw small boxes inside a polygon

  2. Rotating a shape. The solution for rotating a shape 90 degrees offered by Vadim Gremyachev. Except, instead of rotating the polygon, I need to be able to rotate the path within the polygon and have it recalculate to maintain the winding path as in my pic above, only now with a different oritentation.
    Google Maps Rotate Polygon

  3. Another awesome example of drawing inside a polygon by geocodezip
    jsfiddle

  4. As in my mockup, I also need to be able to widen and shrink the path (grow = less winding and shrink = more winding). Arrows in mockup are just to illustrate the grow/shrink points.

  5. I need to be able to designate the order of paths via some way of passing the ability to number the final paths to the user.

  6. Finally, I need to be able to collect (save) all user generated paths in the correct sequence order. This includes any manually drawn paths as well as all of the paths auto-generated within each user-drawn polygon.

Here is my code I’m trying to apply these features to:

//debugger;

/////////////////////////////////////////////////////////////
//Map Specifications

function initialize() {
  var map = new google.maps.Map(document.getElementById('map'), {
    zoom: 18,
    center: new google.maps.LatLng(33.27144940863937, -117.2983479390361),
    mapTypeId: google.maps.MapTypeId.SATELLITE,
    mapTypeId: google.maps.MapTypeId.HYBRID,
    tilt: 0,
    disableDefaultUI: true,
    zoomControl: true,
    mapTypeControl: false,
    scaleControl: true,
    streetViewControl: true,
    rotateControl: true,
    fullscreenControl: false
  });

  // Creates a drawing manager attached to the map that allows the user to draw
  // markers, lines, and shapes.
  drawingManager = new google.maps.drawing.DrawingManager({
    drawingControlOptions: {
      position: google.maps.ControlPosition.TOP_CENTER,
      drawingModes: [
        google.maps.drawing.OverlayType.POLYLINE,
        google.maps.drawing.OverlayType.POLYGON
      ]
    },
    markerOptions: {
      draggable: false
    },
    //https://developers.google.com/maps/documentation/javascript/reference#PolygonOptions
    polygonOptions: {
      clickable: true,
      draggable: false,
      editable: true,
      fillColor: '#00FF00',
      fillOpacity: 0.45,
      geodesic: false,
      strokeColor: '#000000',
      strokeOpacity: 08,
      //strokePosition: CENTER,
      strokeWeight: 3,
      visible: true,
      zIndex: 0
    },
    //https://developers.google.com/maps/documentation/javascript/reference#PolylineOptions
    polylineOptions: {
      clickable: true,
      draggable: false,
      editable: true,
      geodesic: false,
      //icons: ,
      strokeColor: '#FF00FF',
      strokeOpacity: 0.8,
      strokeWeight: 3,
      visible: true,
      zIndex: 0
    }
  });

  ////////////////////////////////////////////////////////////////////////////////
  var drawingManager;
  var deleteSelectedShape;
  var selectedShape;

  function clearSelection() {
    if (selectedShape) {
      if (selectedShape.type !== 'marker') {
        selectedShape.setEditable(false);
      }

      selectedShape = null;
    }
  }

  function setSelection(shape) {
    if (shape.type !== 'marker') {
      clearSelection();
      shape.setEditable(true);
    }

    selectedShape = shape;
  }
  DeleteShape = function deleteSelectedShape() {
    if (selectedShape) {
      selectedShape.setMap(null);
    }
    if (selectedShape.type == 'polygon') {
      document.getElementById("action_gon").value = 'adds, moves, deletions'
    } else if (selectedShape.type == 'polyline') {
      document.getElementById("action_line").value = 'adds, moves, deletions'
    }
  };

  /////////////////////////////////////////////////////////////
  //Populate textboxes with geo data when new polygon and polyline shape added

  drawingManager.setMap(map);

  google.maps.event.addDomListener(drawingManager, 'markercomplete', function(marker) {
    document.getElementById("action").value += "#markern";
    document.getElementById("action").value += marker.getPosition() + "n";
  });

  google.maps.event.addDomListener(drawingManager, 'polylinecomplete', function(line) {
    path = line.getPath();
    //document.getElementById("action_line").value = ''
    document.getElementById("action_line").value = "#polyline shape addedn";
    for (var i = 0; i < path.length; i++) {
      document.getElementById("action_line").value += path.getAt(i) + "n";
    }
  });

  google.maps.event.addDomListener(drawingManager, 'polygoncomplete', function(polygon) {
    var markerCnt = 0;
    path = polygon.getPath();
    //document.getElementById("action_gon").value = ''
    document.getElementById("action_gon").value = "#polygon shape addedn";
    for (var i = 0; i < path.length; i++) {
      document.getElementById("action_gon").value += path.getAt(i) + 'n';
    }
  });

  //////////////////////////////////////////////////////////////////////

  google.maps.event.addListener(drawingManager, 'overlaycomplete', function(e) {

    var newShape = e.overlay;
    newShape.type = e.type;

    if (e.type !== google.maps.drawing.OverlayType.MARKER) {
      // Switch back to non-drawing mode after drawing a shape.
      drawingManager.setDrawingMode(null);

      if (e.type == google.maps.drawing.OverlayType.POLYGON) {
        var coordinatesArray = e.overlay.getPath().getArray();
        document.getElementById("count_gon").value += "#n";
        document.getElementById("count_gon").value += coordinatesArray + "n";
      }

      //Catch vertex modifications (moves)
      function processVertices(e) {
        var ele;
        if (newShape.type == "polygon") {
          ele = document.getElementById("action_gon");
          //ele.value = "Modified vertex: "+e+"n"+this.getAt(e)+"nPolygon coords :n";
          ele.value = "#polygon vertex " + e + " movedn" + this.getAt(e) + "n";
        } else if (newShape.type == "polyline") {
          ele = document.getElementById("action_line");
          //ele.value = "Modified vertex: "+e+"n"+this.getAt(e)+"nPolyline coords :n";
          ele.value = "#polyline vertex " + e + " movedn" + this.getAt(e) + "n";
        } else return;
        for (var i = 0; i < newShape.getPath().getLength(); i++) {
          ele.value += newShape.getPath().getAt(i) + 'n';
        };
      };

      google.maps.event.addListener(newShape.getPath(), 'set_at', processVertices);
      google.maps.event.addListener(newShape.getPath(), 'insert_at', processVertices);

      /////////////////////////////////////////////////////////////
      // Add an event listener that selects the newly-drawn shape when the user clicks it.
      google.maps.event.addListener(newShape, 'click', function(e) {
        if (e.vertex !== undefined) {
          if (newShape.type === google.maps.drawing.OverlayType.POLYGON) {
            var path = newShape.getPaths().getAt(e.path);
            path.removeAt(e.vertex);

            /////////////////////////////////////////////////////////////
            //Update textboxes with geo data when polygon vertex deleted
            document.getElementById("action_gon").value = "#polygon vertex deletedn";
            for (var i = 0; i < path.length; i++) {
              document.getElementById("action_gon").value += path.getAt(i) + 'n';
            }

            if (path.length < 3) {
              newShape.setMap(null);
              document.getElementById("action_gon").value = 'This box shows updated coords for POLYGONS based on user interactions (adds, moves, deletions).'
            }
          }

          if (newShape.type === google.maps.drawing.OverlayType.POLYLINE) {
            var path = newShape.getPath();
            path.removeAt(e.vertex);
            /////////////////////////////////////////////////////////////
            //Update textboxes with geo data when polyline vertex deleted
            document.getElementById("action_line").value = "#polyline vertex deletedn";
            for (var i = 0; i < path.length; i++) {
              document.getElementById("action_line").value += path.getAt(i) + 'n';
            }

            if (path.length < 2) {
              newShape.setMap(null);
              document.getElementById("action_line").value = 'This box shows updated coords for POLYLINES based on user interactions (adds, moves, deletions).'
            }
          }
        }

        setSelection(newShape);
      });
      setSelection(newShape);
    } else {
      google.maps.event.addListener(newShape, 'click', function(e) {
        setSelection(newShape);
      });
      setSelection(newShape);
    }
  });

  // Link delete button to the UI element.
  var delbtn = /** @type {HTMLInputElement} */ (
    document.getElementById('delete-button'));
  map.controls[google.maps.ControlPosition.TOP_RIGHT].push(delbtn);

  // Clear the current selection when the drawing mode is changed, or when the
  // map is clicked.
  google.maps.event.addListener(drawingManager, 'drawingmode_changed', clearSelection);
  google.maps.event.addListener(map, 'click', clearSelection);

  // Listen for delete button click.
  google.maps.event.addDomListener(document.getElementById('delete-button'), 'click', deleteSelectedShape);

  /////////////////////////////////////////////////////////////////////

  //Places Search Box Setup
  var markers = [];
  var input = /** @type {HTMLInputElement} */ (
    document.getElementById('pac-input'));
  map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);

  var searchBox = new google.maps.places.SearchBox(
    /** @type {HTMLInputElement} */
    (input));

  // [START region_getplaces]
  // Listen for the event fired when the user selects an item from the
  // pick list. Retrieve the matching places for that item.
  google.maps.event.addListener(searchBox, 'places_changed', function() {
    var places = searchBox.getPlaces();

    if (places.length == 0) {
      return;
    }
    for (var i = 0, marker; marker = markers[i]; i++) {
      marker.setMap(null);
    }

    // For each place, get the icon, place name, and location.
    markers = [];
    var bounds = new google.maps.LatLngBounds();
    for (var i = 0, place; place = places[i]; i++) {
      var image = {
        url: place.icon,
        size: new google.maps.Size(71, 71),
        origin: new google.maps.Point(0, 0),
        anchor: new google.maps.Point(17, 34),
        scaledSize: new google.maps.Size(25, 25)
      };

      bounds.extend(place.geometry.location);
    }

    map.fitBounds(bounds);
  });
}

google.maps.event.addDomListener(window, 'load', initialize);
#map,
html,
body {
  padding: 0;
  margin: 0;
  width: 100%;
  height: 100%;
}
#geoinfoboxes {
  display: none;
}
#delete-button {
  background: #0084ff;
  background-image: -webkit-linear-gradient(top, #0084ff, #000000);
  background-image: -moz-linear-gradient(top, #0084ff, #000000);
  background-image: -o-linear-gradient(top, #0084ff, #000000);
  background-image: linear-gradient(to bottom, #0084ff, #000000);
  border-radius: 30px;
  text-shadow: 0px 1px 3px #cfcdcf;
  -webkit-box-shadow: 0px 1px 3px #666666;
  -moz-box-shadow: 0px 1px 3px #666666;
  box-shadow: 0px 1px 3px #666666;
  font-family: Arial;
  margin-top: 5px;
  right: 0.5%;
  color: #ffffff;
  font-size: 15px;
  padding: 8px 10px 8px 10px;
  border: solid #a8a8a8 2px;
  text-decoration: none;
}
#delete-button:hover {
  background: #09ff00;
  background-image: -webkit-linear-gradient(top, #09ff00, #000000);
  background-image: -moz-linear-gradient(top, #09ff00, #000000);
  background-image: -o-linear-gradient(top, #09ff00, #000000);
  background-image: linear-gradient(to bottom, #09ff00, #000000);
  text-decoration: none;
}
.controls {
  border: 1px solid transparent;
  border-radius: 30px 30px 30px 30px;
  box-sizing: border-box;
  -moz-box-sizing: border-box;
  height: 32px;
  outline: none;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  margin-top: 5px;
}
#pac-input {
  background-color: #fff;
  font-family: Roboto;
  font-size: 15px;
  font-weight: 300;
  margin-left: 12px;
  padding: 0 11px 0 13px;
  text-overflow: ellipsis;
  width: 400px;
}
#pac-input:focus {
  border-color: #4d90fe;
}
.pac-container {
  font-family: Roboto;
}
<!DOCTYPE html>
<!-- saved from url=(0014)about:internet -->
<html>

<head>
  <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
  <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
  <meta charset="UTF-8">
  <title>Test</title>
  http://maps.google.com/maps/api/js?key=AIzaSyBgmfaITmUDhXxk-0V33IPmNPd43mMd4ZU&libraries=drawing,places
</head>
<!-- -->

<body>
  <input id="pac-input" class="controls" type="text" placeholder="Search...">
  <input id="delete-button" onclick="DeleteShape();" type=button value="Delete Selected Shape">
  
</body> </html>

I realize this is a tall order, so I plan to add a bounty if I can’t get there with general help. Also, willing to work with someone offline.

EDIT: Looks like the grid-filled polygon from my image has been done using the Google API. Here’s an exact example of the grid type I’m wanting to create (skip to 2:10): https://www.youtube.com/watch?v=u-k8ax2JGC4


Get this bounty!!!

#StackBounty: Writting a JS library to create a HTML form

Bounty: 50

I’m currently writting a JS Library in order to create a form in HTML.
This library need to be :

  • Cross-browser, with compatibility with at least IE10 (earlier version can be considered as bonus)
  • Multi language : you should be able to change the form language
  • Able to load external javascript dependencies*
  • Communicate with my API
  • Fast
  • As simple to use as possible

So, here is what I’ve done for now :

window.MyBasicLibrary = (function() 
{
'use strict';

    // Utilitarian function not to loose the scope 
    function bind(context, name){
        return function(){
            return context[name].apply(context, arguments);
        };
    }

    // Utilitarian function to get DOM by class
    function getElementsByClassName(node, classname) 
    {
        var a = [];
        var re = new RegExp('(^| )'+ classname +'( |$)');
        var els = node.getElementsByTagName("*");

        for (var i = 0, j = els.length; i < j; i++)
        {
            if (re.test(els[i].className))
                a.push(els[i]);
        }
        return (a);
    }

    /*
    ** Utilitarian function to serialize a form
    ** See : https://code.google.com/archive/p/form-serialize/
    */
    function serialize(form) 
    {
        if (!form || form.nodeName !== "FORM") {
            return;
        }
        var i, j, q = [];
        for (i = form.elements.length - 1; i >= 0; i = i - 1) {
            if (form.elements[i].name === "") {
                continue;
            }
            switch (form.elements[i].nodeName) {
            case 'INPUT':
                switch (form.elements[i].type) {
                case 'text':
                case 'hidden':
                case 'password':
                case 'button':
                case 'reset':
                case 'submit':
                    q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
                    break;
                case 'checkbox':
                case 'radio':
                    if (form.elements[i].checked) {
                        q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
                    }                       
                    break;
                case 'file':
                    break;
                }
                break;           
            case 'TEXTAREA':
                q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
                break;
            case 'SELECT':
                switch (form.elements[i].type) {
                case 'select-one':
                    q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
                    break;
                case 'select-multiple':
                    for (j = form.elements[i].options.length - 1; j >= 0; j = j - 1) {
                        if (form.elements[i].options[j].selected) {
                            q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].options[j].value));
                        }
                    }
                    break;
                }
                break;
            case 'BUTTON':
                switch (form.elements[i].type) {
                case 'reset':
                case 'submit':
                case 'button':
                    q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
                    break;
                }
                break;
            }
        }
        return q.join("&");
    }

    // Element should be the result of getElementByID.
    var mb = function(element)
    {
        this.mainContainer = element;
    };


    mb.prototype.regional = {
        mcta_1: "Form A <This One Works>",
        mcta_2: "Form B",
        mcta_3: "Form C",
        submitFormA: "Submit"
    };

    mb.prototype.mainContainer = undefined;
    mb.prototype.currentForm = undefined;
    mb.prototype.APIEndpoint = 'https://www.example.com/api/';

    mb.prototype.initialize =  function()
    {
        this.createMainPageHTML();
    };

    mb.prototype.hideContainers = function()
    {
        var list = getElementsByClassName(this.mainContainer, "mbform_container");
        for (var i = 0; i < list.length; i++)
        {
            list[i].style["display"] = "none";
        }
    }

    mb.prototype.createMainPageHTML = function()
    {
        var element = document.getElementById('mbform_mainpage');

        this.hideContainers();

        if (typeof(element) != 'undefined' && element != null)
        {
            element.style["display"] = "block";
        }
        else
        {
            var mcta1 = '
'+ this.regional.mcta_1 +'
'; var mcta2 = '
'+ this.regional.mcta_2 +'
'; var mcta3 = '
'+ this.regional.mcta_3 +'
'; var mpHTML = '
'+mcta1 + mcta2 + mcta3 +'
'; // TODO Load CSS // Add HTML to DOM this.mainContainer.innerHTML = mpHTML; // takes an HTML string // TODO Load JS Dependencies // Add Event listener document.getElementById('mbform_mcta1').addEventListener("click", bind(this, "createFormA"), false); document.getElementById('mbform_mcta2').addEventListener("click", bind(this, "createFormB"), false); document.getElementById('mbform_mcta3').addEventListener("click", bind(this, "createFormC"), false); } }; mb.prototype.createFormA = function() { var element = document.getElementById('mbform_FormAContainer'); this.hideContainers(); this.currentForm = "mbform_FormA"; if (typeof(element) != 'undefined' && element != null) { element.style["display"] = "block"; } else { var iemail = '<input type="text" value="" name="email" id="mbform_iemail"/><br/>'; var ifirstname = '<input type="text" value="" name="firstname" id="mbform_ifirstname"/><br/>'; var ilastname = '<input type="text" value="" name="lastname" id="mbform_ilastname"/><br/>'; var formHTML = '<form id="mbform_FormA">'+iemail + ifirstname + ilastname + '</form>' // TODO : Disable form default bahaviour (Submit when Enter Keypress + submit button), button is placed outside form for testing purpose. var submitButton = '<button id="mbform_FormASubmit">'+ this.regional.submitFormA +'</button>'; var formContainerHTML = '
'+ formHTML + submitButton +'
'; this.mainContainer.innerHTML += formContainerHTML; // Append HTML to existing document.getElementById('mbform_FormASubmit').addEventListener("click", bind(this, "saveForm"), false); } }; mb.prototype.createFormB = function() { console.log(this.mainContainer); // Is scope ok ? }; mb.prototype.createFormC = function() { console.log(this.mainContainer); // Is scope ok ? }; mb.prototype.getPostFromCurrentForm = function() { var data = serialize(document.getElementById(this.currentForm)); console.log(data); return (data); } mb.prototype.saveForm = function() { var xhr = new XMLHttpRequest(); var params = this.getPostFromCurrentForm(); xhr.open('POST', this.APIEndpoint, true); xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhr.onload = function () { console.log("Loading..."); }; // Current APIEndpoint is example.com, do not send the form. //xhr.send(params); } return (mb); })();

And here is how you initialize the library

(function() {
            var dom = document.getElementById('myForm');
            var mb = new MyBasicLibrary(dom);
            mb.initialize();
})();

I did my best to respect javascript best practice but I’m not sure if I’ve done it well. What do you think about it ?
Also, having raw HTML inside variable seems to be a bad practice, however I need to minimize the number of request made to the server for performance purpose. It means that I can’t store my HTML server side and send it through AJAX. Is it the best solution ?

I’ve created a JSFiddle in case you want to tweak it.
https://jsfiddle.net/xd4ojka2/


Get this bounty!!!

Fetch GET parameters in JS/jQuery

If you have a URL with some GET parameters as follows:

www.test.com/t.html?a=1&b=3&c=m2-m3-m4-m5 

and need to get the values of each parameters then below is a nifty piece of code solving your requirement.

JavaScript has nothing built in for handling query string parameters.

You could access location.search, which would give you from the ? character on to the end of the URL or the start of the fragment identifier (#foo), whichever comes first.

You can then access QueryString.c

System Design Interview Prep Material

System design is a very broad topic. Even a software engineer with many years of working experience at top IT company may not be an expert on system design. If you want to become an expert, you need to read many books, articles, and solve real large scale system design problems. This repository only teaches you to handle the system design interview with a systematic approach in a short time. You can dive into each topic if you have time. Of course, welcome to add your thoughts!

Table of Contents

System Design Interview Tips:

  • Clarify the constraints and identify the user cases Spend a few minutes questioning the interviewer and agreeing on the scope of the system. Remember to make sure you know all the requirements the interviewer didn’t tell your about in the beginning. User cases indicate the main functions of the system, and constraints list the scale of the system such as requests per second, requests types, data written per second, data read per second.
  • High-level architecture design Sketch the important components and the connections between them, but don’t go into some details. Usually, a scalable system includes web server (load balancer), service (service partition), database (master/slave database cluster plug cache).
  • Component design For each component, you need to write the specific APIs for each component. You may need to finish the detailed OOD design for a particular function. You may also need to design the database schema for the database.

Basic Knowledge about System Design:

Here are some articles about system design related topics.

Of course, if you want to dive into system related topics, here is a good collection of reading list about services-engineering, and a good collection of material about distributed systems.

Company Engineering Blogs:

If you are going to have an onsite with a company, you should read their engineering blog.

Products and Systems:

The following papers/articles/slides can help you to understand the general design idea of different real products and systems.

Hot Questions and Reference:

There are some good references for each question. The references here are slides and articles.
Design a CDN network Reference:

Design a Google document system Reference:

Design a random ID generation system Reference:

Design a key-value database Reference:

Design the Facebook news feed function Reference:

Design the Facebook timeline function Reference:

Design a function to return the top k requests during past time interval Reference:

Design an online multiplayer card game Reference:

Design a graph search function Reference:

Design a picture sharing system Reference:

Design a search engine Reference:

Design a recommendition system Reference:

Design a tinyurl system Reference:

Design a garbage collection system Reference:

Design a scalable web crawling system Reference:

Design the Facebook chat function Reference:

Design a trending topic system Reference:

Design a cache system Reference:

Good Books:

Object Oriented Design:

Tips for OOD Interview

Clarify the scenario, write out user cases Use case is a description of sequences of events that, taken together, lead to a system doing something useful. Who is going to use it and how they are going to use it. The system may be very simple or very complicated. Special system requirements such as multi-threading, read or write oriented.
Define objects Map identity to class: one scenario for one class, each core object in this scenario for one class. Consider the relationships among classes: certain class must have unique instance, one object has many other objects (composition), one object is another object (inheritance). Identify attributes for each class: change noun to variable and action to methods. Use design patterns such that it can be reused in multiple applications.

Useful Websites

Original Source

jSoup: Java HTML Parser

jsoup is a Java library for working with real-world HTML. It provides a very convenient API for extracting and manipulating data, using the best of DOM, CSS, and jquery-like methods.

jsoup implements the WHATWG HTML5 specification, and parses HTML to the same DOM as modern browsers do.

  • scrape and parse HTML from a URL, file, or string
  • find and extract data, using DOM traversal or CSS selectors
  • manipulate the HTML elements, attributes, and text
  • clean user-submitted content against a safe white-list, to prevent XSS attacks
  • output tidy HTML

jsoup is designed to deal with all varieties of HTML found in the wild; from pristine and validating, to invalid tag-soup; jsoup will create a sensible parse tree.

Example

Fetch the Wikipedia homepage, parse it to a DOM, and select the headlines from theIn the news section into a list of Elements (online sample):

Document doc = Jsoup.connect("http://en.wikipedia.org/").get();
Elements newsHeadlines = doc.select("#mp-itn b a");

Open source

jsoup is an open source project distributed under the liberal MIT license. The source code is available at GitHub.

Getting started

  1. Download the jsoup jar (version 1.8.3)
  2. Read the cookbook introduction
  3. Enjoy!

Resources