<html>
<head>
<meta charset="UTF-8">
<meta name="description" content="Item Recommendation">
<meta name="author" content="Your Name">
<title>Item Recommendation Final</title>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,300,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
<link rel="stylesheet" href="styles/main.css">
</head>
<body>
<header class="top-header">
<nav class="top-nav">
<a href="#">Home</a>
<a href="#">Contact</a>
<a href="#">About</a>
</nav>
<span id="welcome-msg"></span>
<i id="avatar" class="avatar fa fa-user fa-2x"></i>
</header>
<div class="container">
<header>
<p>
<span>Item</span>
<br /> Recommendation
</p>
</header>
<section class="main-section">
<aside id="item-nav">
<div class="nav-icon">
<i class="fa fa-sitemap fa-2x"></i>
</div>
<nav class="main-nav">
<a href="#" id="nearby-btn" class="main-nav-btn active">
<i class="fa fa-map-marker"></i> Nearby
</a>
<a href="#" id="fav-btn" class="main-nav-btn">
<i class="fa fa-heart"></i> My Favorites
</a>
<a href="#" id="recommend-btn" class="main-nav-btn">
<i class="fa fa-thumbs-up"></i> Recommendation
</a>
</nav>
</aside>
<ul id="item-list">
<li class="item">
<img alt="item image" src="https://s3-media3.fl.yelpcdn.com/bphoto/EmBj4qlyQaGd9Q4oXEhEeQ/ms.jpg" />
<div>
<a class="item-name" href="#">Item</a>
<p class="item-category">Vegetarian</p>
<div class="stars">
<i class="fa fa-star"></i>
<i class="fa fa-star"></i>
<i class="fa fa-star"></i>
</div>
</div>
<p class="item-address">699 Calderon Ave<br/>Mountain View<br/> CA</p>
<div class="fav-link">
<i class="fa fa-heart"></i>
</div>
</li>
</ul>
</section>
</div>
<footer>
<p class="title">What We Do</p>
<p>"Help you find the best place around."</p>
<ul>
<li>
<p><i class="fa fa-map-o fa-2x"></i></p>
<p>LaiOffer office, CA</p>
</li>
<li>
<p><i class="fa fa-envelope-o fa-2x"></i></p>
<p>info@laioffer.com</p>
</li>
<li>
<p><i class="fa fa-phone fa-2x"></i></p>
<p>+1 800 123 456</p>
</li>
</ul>
</footer>
<script src="https://rawgit.com/emn178/js-md5/master/build/md5.min.js"></script>
<script src="scripts/main.js"></script>
</body>
</html>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: #434343;
color: #FFFFFF;
font-family: 'Open Sans', sans-serif;
font-weight: 300;
font-size: 0.9em;
}
ul {
list-style: none;
}
/**
* top header
*/
.top-header {
align-items: center;
background: #DF574B;
box-shadow: 0px 2px 10px #333333;
display: flex;
height: 60px;
position: fixed;
top: 0;
width: 100%;
z-index: 1;
}
.avatar {
background: #FFFFFF;
border-radius: 50%;
color: #333C4D;
height: 40px;
margin-right: 20px;
padding-left: 9px;
padding-top: 5px;
width: 40px;
}
/**
* top navigation
*/
.top-nav {
flex: 1;
}
.top-nav a {
color: #F9F9F9;
font-weight: 400;
margin-left: 20px;
text-decoration: none;
}
#welcome-msg {
border-right: 1px solid #FFFFFF;
padding-right: 10px;
}
/**
* main container
*/
.container {
margin: 60px auto;
max-width: 1000px;
min-width: 600px;
}
/**
* main header
*/
.container>header {
align-items: center;
background:
url('http://folkartcarsi.com/img/4.jpg')
no-repeat 50% 50%;
background-size: 100%;
display: flex;
height: 250px;
}
.container>header p {
border-left: 1px solid #FFFFFF;
font-weight: 400;
font-size: 2em;
line-height: 1em;
margin-left: 220px;
padding-left: 5px;
}
.container>header span {
color: #FBAF41;
}
/**
* main section
*/
.main-section {
background: #F3BB43;
}
/**
* aside
*/
#item-nav {
float: left;
width: 180px;
}
.nav-icon {
color: #624630;
padding: 20px;
text-align: center;
}
/**
* main navigation
*/
.main-nav-btn {
background: none;
border-top: 1px solid #FFFFFF;
color: #FFFFFF;
display: block;
padding: 20px;
text-align: left;
text-decoration: none;
}
.main-nav-btn:hover {
background: rgba(255, 255, 255, 0.8);
color: #624630;
}
.main-nav-btn.active {
background: #F2EBD9;
color: #624630;
}
.main-nav-btn i {
width: 20px;
}
/**
* item list
*/
#item-list {
background: #F2EBD9;
color: #624630;
list-style: none;
margin-left: 180px;
min-height: 250px;
padding: 10px;
}
#item-list .notice {
font-size: 16px;
padding: 60px;
text-align: center;
}
/**
* item
*/
.item {
align-items: center;
border-bottom: 1px solid #FFFFFF;
display: flex;
margin: 10px;
padding: 15px;
transition: background-color 100ms linear;
}
.item:last-child {
border: none;
}
.item:hover {
background: rgba(255, 255, 255, 0.8);
transition: background-color 100ms linear;
}
.item img {
border: 1px solid #FFFFFF;
height: 80px;
width: 80px;
}
.item>div:first-of-type {
flex: 1;
margin-left: 10px;
margin-right: 10px;
}
.item-name {
color: #624630;
font-weight: 400;
text-decoration: none;
}
.item-name:hover {
text-decoration: underline;
}
.item-address {
line-height: 20px;
padding-right: 20px;
text-align: right;
}
.stars {
align-items: center;
display: flex;
padding-top: 10px;
}
.fav-link {
border-left: 1px solid #FFFFFF;
cursor: pointer;
line-height: 60px;
text-align: center;
width: 60px;
}
.fav-link:hover {
color: #F13F3F;
}
/**
* footer section
*/
footer {
background: #434343;
font-size: 0.8em;
height: 200px;
position: relative;
}
footer p {
text-align: center;
}
footer p.title {
font-size: 1.2em;
padding: 15px;
}
footer ul {
align-items: center;
display: flex;
padding: 20px;
}
footer ul li {
flex: 1;
}
(function() {
/**
* Variables
*/
var user_id = '1111';
var user_fullname = 'John';
var lng = -122.08;
var lat = 37.38;
/**
* Initialize
*/
function init() {
// Register event listeners
$('nearby-btn').addEventListener('click', loadNearbyItems);
$('fav-btn').addEventListener('click', loadFavoriteItems);
$('recommend-btn').addEventListener('click', loadRecommendedItems);
var welcomeMsg = $('welcome-msg');
welcomeMsg.innerHTML = 'Welcome, ' + user_fullname;
initGeoLocation();
}
function initGeoLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(onPositionUpdated,
onLoadPositionFailed, {
maximumAge: 60000
});
showLoadingMessage('Retrieving your location...');
} else {
onLoadPositionFailed();
}
}
function onPositionUpdated(position) {
lat = position.coords.latitude;
lng = position.coords.longitude;
loadNearbyItems();
}
function onLoadPositionFailed() {
console.warn('navigator.geolocation is not available');
getLocationFromIP();
}
function getLocationFromIP() {
// Get location from http://ipinfo.io/json
var url = 'http://ipinfo.io/json'
var req = null;
ajax('GET', url, req, function(res) {
var result = JSON.parse(res);
if ('loc' in result) {
var loc = result.loc.split(',');
lat = loc[0];
lng = loc[1];
} else {
console.warn('Getting location by IP failed.');
}
loadNearbyItems();
});
}
// -----------------------------------
// Helper Functions
// -----------------------------------
/**
* A helper function that makes a navigation button active
*
* @param btnId -
* The id of the navigation button
*/
function activeBtn(btnId) {
var btns = document.getElementsByClassName('main-nav-btn');
// deactivate all navigation buttons
for (var i = 0; i < btns.length; i++) {
btns[i].className = btns[i].className.replace(/\bactive\b/, '');
}
// active the one that has id = btnId
var btn = $(btnId);
btn.className += ' active';
}
function showLoadingMessage(msg) {
var itemList = $('item-list');
itemList.innerHTML = '<p class="notice"><i class="fa fa-spinner fa-spin"></i> ' +
msg + '</p>';
}
function showWarningMessage(msg) {
var itemList = $('item-list');
itemList.innerHTML = '<p class="notice"><i class="fa fa-exclamation-triangle"></i> ' +
msg + '</p>';
}
function showErrorMessage(msg) {
var itemList = $('item-list');
itemList.innerHTML = '<p class="notice"><i class="fa fa-exclamation-circle"></i> ' +
msg + '</p>';
}
/**
* A helper function that creates a DOM element <tag options...>
*
* @param tag
* @param options
* @returns
*/
function $(tag, options) {
if (!options) {
return document.getElementById(tag);
}
var element = document.createElement(tag);
for (var option in options) {
if (options.hasOwnProperty(option)) {
element[option] = options[option];
}
}
return element;
}
function hideElement(element) {
element.style.display = 'none';
}
function showElement(element, style) {
var displayStyle = style ? style : 'block';
element.style.display = displayStyle;
}
/**
* AJAX helper
*
* @param method -
* GET|POST|PUT|DELETE
* @param url -
* API end point
* @param callback -
* This the successful callback
* @param errorHandler -
* This is the failed callback
*/
function ajax(method, url, data, callback, errorHandler) {
var xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.onload = function() {
if (xhr.status === 200) {
callback(xhr.responseText);
} else {
errorHandler();
}
};
xhr.onerror = function() {
console.error("The request couldn't be completed.");
errorHandler();
};
if (data === null) {
xhr.send();
} else {
xhr.setRequestHeader("Content-Type",
"application/json;charset=utf-8");
xhr.send(data);
}
}
// -------------------------------------
// AJAX call server-side APIs
// -------------------------------------
/**
* API #1 Load the nearby items API end point: [GET]
* /Dashi/search?user_id=1111&lat=37.38&lon=-122.08
*/
function loadNearbyItems() {
console.log('loadNearbyItems');
activeBtn('nearby-btn');
// The request parameters
var url = './search';
var params = 'user_id=' + user_id + '&lat=' + lat + '&lon=' + lng;
var req = JSON.stringify({});
// display loading message
showLoadingMessage('Loading nearby items...');
// make AJAX call
ajax('GET', url + '?' + params, req,
// successful callback
function(res) {
var items = JSON.parse(res);
if (!items || items.length === 0) {
showWarningMessage('No nearby item.');
} else {
listItems(items);
}
},
// failed callback
function() {
showErrorMessage('Cannot load nearby items.');
});
}
/**
* API #2 Load favorite (or visited) items API end point: [GET]
* /Dashi/history?user_id=1111
*/
function loadFavoriteItems() {
activeBtn('fav-btn');
// The request parameters
var url = './history';
var params = 'user_id=' + user_id;
var req = JSON.stringify({});
// display loading message
showLoadingMessage('Loading favorite items...');
// make AJAX call
ajax('GET', url + '?' + params, req, function(res) {
var items = JSON.parse(res);
if (!items || items.length === 0) {
showWarningMessage('No favorite item.');
} else {
listItems(items);
}
}, function() {
showErrorMessage('Cannot load favorite items.');
});
}
/**
* API #3 Load recommended items API end point: [GET]
* /Dashi/recommendation?user_id=1111
*/
function loadRecommendedItems() {
activeBtn('recommend-btn');
// The request parameters
var url = './recommendation';
var params = 'user_id=' + user_id + '&lat=' + lat + '&lon=' + lng;
var req = JSON.stringify({});
// display loading message
showLoadingMessage('Loading recommended items...');
// make AJAX call
ajax(
'GET',
url + '?' + params,
req,
// successful callback
function(res) {
var items = JSON.parse(res);
if (!items || items.length === 0) {
showWarningMessage('No recommended item. Make sure you have favorites.');
} else {
listItems(items);
}
},
// failed callback
function() {
showErrorMessage('Cannot load recommended items.');
});
}
/**
* API #4 Toggle favorite (or visited) items
*
* @param item_id -
* The item business id
*
* API end point: [POST]/[DELETE] /Dashi/history request json data: {
* user_id: 1111, visited: [a_list_of_business_ids] }
*/
function changeFavoriteItem(item_id) {
// Check whether this item has been visited or not
var li = $('item-' + item_id);
var favIcon = $('fav-icon-' + item_id);
var favorite = li.dataset.favorite !== 'true';
// The request parameters
var url = './history';
var req = JSON.stringify({
user_id: user_id,
favorite: [item_id]
});
var method = favorite ? 'POST' : 'DELETE';
ajax(method, url, req,
// successful callback
function(res) {
var result = JSON.parse(res);
if (result.result === 'SUCCESS') {
li.dataset.favorite = favorite;
favIcon.className = favorite ? 'fa fa-heart' : 'fa fa-heart-o';
}
});
}
// -------------------------------------
// Create item list
// -------------------------------------
/**
* List items
*
* @param items -
* An array of item JSON objects
*/
function listItems(items) {
// Clear the current results
var itemList = $('item-list');
itemList.innerHTML = '';
for (var i = 0; i < items.length; i++) {
addItem(itemList, items[i]);
}
}
/**
* Add item to the list
*
* @param itemList -
* The
* <ul id="item-list">
* tag
* @param item -
* The item data (JSON object)
*/
function addItem(itemList, item) {
var item_id = item.item_id;
// create the <li> tag and specify the id and class attributes
var li = $('li', {
id: 'item-' + item_id,
className: 'item'
});
// set the data attribute
li.dataset.item_id = item_id;
li.dataset.favorite = item.favorite;
// item image
if (item.image_url) {
li.appendChild($('img', {
src: item.image_url
}));
} else {
li.appendChild($(
'img', {
src: 'https://assets-cdn.github.com/images/modules/logos_page/GitHub-Mark.png'
}))
}
// section
var section = $('div', {});
// title
var title = $('a', {
href: item.url,
target: '_blank',
className: 'item-name'
});
title.innerHTML = item.name;
section.appendChild(title);
// category
var category = $('p', {
className: 'item-category'
});
category.innerHTML = 'Category: ' + item.categories.join(', ');
section.appendChild(category);
// TODO(vincent). here we might have a problem showing 3.5 as 3.
// stars
var stars = $('div', {
className: 'stars'
});
for (var i = 0; i < item.rating; i++) {
var star = $('i', {
className: 'fa fa-star'
});
stars.appendChild(star);
}
if (('' + item.rating).match(/\.5$/)) {
stars.appendChild($('i', {
className: 'fa fa-star-half-o'
}));
}
section.appendChild(stars);
li.appendChild(section);
// address
var address = $('p', {
className: 'item-address'
});
address.innerHTML = item.address.replace(/,/g, '<br/>').replace(/\"/g,
'');
li.appendChild(address);
// favorite link
var favLink = $('p', {
className: 'fav-link'
});
favLink.onclick = function() {
changeFavoriteItem(item_id);
};
favLink.appendChild($('i', {
id: 'fav-icon-' + item_id,
className: item.favorite ? 'fa fa-heart' : 'fa fa-heart-o'
}));
li.appendChild(favLink);
itemList.appendChild(li);
}
init();
})();
Output
300px
This bin was created anonymously and its free preview time has expired (learn why). — Get a free unrestricted account
Dismiss xKeyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |