Created
September 12, 2015 03:18
-
-
Save justsml/dcc787630a86a5a1eca7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<link href='http://fonts.googleapis.com/css?family=Roboto:400,700' rel='stylesheet' type='text/css'> | |
<link href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/font-awesome.min.css' rel='stylesheet' type='text/css'> | |
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js'></script> | |
<script> | |
'use strict' | |
var Utils = { | |
escapeHtml: function _escapeHtml(s) {// Prevent code injection and rendering issues | |
return String(s).replace(/&/g,'&').replace(/>/g,'>').replace(/</g,'<'); | |
} | |
} | |
function Panel(opts) { | |
return ['<div class="panel">', | |
' <div class="panel-heading">', | |
' <h4>', | |
' <span class="status"><b class="fa fa-', opts.icon, '"></b></span>', | |
' <span class="name">', Utils.escapeHtml(opts.title), '</span>', | |
' </h4>', | |
' </div>', | |
' <div class="panel-body">', | |
(opts.trustedBody || Utils.escapeHtml(opts.body)), | |
' </div>', | |
' <div class="panel-footer">', ICONS_HTML, '</div>', | |
'</div>'].join(''); | |
} | |
function ConfigurationController(element) { | |
if ( !element ) { throw new Error('Cannot create ConfigurationController: Element Required');} | |
var self = this, | |
_limit = 2, | |
apiUrl = "http://somedomain/download/request?host="; | |
// Expose Public method(s) | |
this.load = load; | |
this.render = render; | |
function load(limit) { | |
reset(); | |
limit = parseInt(limit || 2); | |
_limit = limit; | |
// Attempt ajax call; on fail: use dummy data | |
$.ajax(apiUrl + encodeURIComponent(limit)) | |
.success(render) | |
.fail(errorHandler) | |
.always(function() { | |
console.log('http.always', arguments); | |
if (element.children().length === 0) { | |
errorHandler(); | |
} | |
}); | |
} | |
function render(data) { | |
// Do basic response check | |
if (!data || !data.configurations || !Array.isArray(data.configurations)) { | |
console.error(new Error('Invalid Server response')); | |
return render(dummyData(_limit)); | |
} else if (data.configurations.length === 0) { | |
} | |
reset(); | |
// render out config | |
if (data.configurations && data.configurations.length > 1) { | |
/* | |
Cute, but inefficient (returns an in-memory transform of an already-big array, only bigger. finally serially call append() for each returned renderItem): | |
data.configurations | |
.map(renderItem) | |
.map(element.append);*/ | |
data.configurations | |
//let's use `this` binding to pass a callback FN for the resulting DOM/jQuery element | |
//.map(renderItem, element) | |
.map(renderItem, function(el) { | |
element.append(el); | |
}); | |
//.map(element.append); | |
} else { | |
errorHandler('Failed to render'); | |
} | |
} | |
function reset() { | |
// reset UI | |
element.children().remove(); | |
return element; | |
} | |
function renderItem(obj, index, list) { | |
var callback = typeof this === 'function' ? this : false; | |
// This could use document create fragment, possible speed boost | |
var randStatus = STATUS_ICONS[Math.floor(Math.random() * STATUS_ICONS.length)]; | |
// 20% of the time, pick random status icon, in all other cases pick the 'good' check icon. | |
var statusIcon = (index % 20 === 0 ? randStatus : STATUS_ICONS[0]); | |
var itemPanel = Panel({icon: statusIcon, | |
title: obj.name, | |
trustedBody: [ | |
' <div><b class="fa fa-desktop"></b> host: ', Utils.escapeHtml(obj.hostname), '</div>', | |
' <div><b class="fa fa-link"></b> port: ', Utils.escapeHtml(obj.port), '</div>', | |
' <div><b class="fa fa-user"></b> user: ', Utils.escapeHtml(obj.username), '</div>'].join('\n') }); | |
itemPanel = $(itemPanel); | |
return (typeof(callback) === 'function' ? callback(itemPanel) : itemPanel); | |
} | |
function errorHandler(err) { | |
console.log('errorHandler', arguments); | |
// reset(); | |
element.append('<div class="panel tile has-error">Failed</div>'); | |
// Load dummy data | |
render(MOCK_DATA); | |
} | |
} | |
$(document).ready(function _onLoad() { | |
var ctrl = new ConfigurationController($('#configurationList')); | |
ctrl.load(); | |
var limitBtns = $('.btn[data-limit], .btn[limit]').click(function(e) { | |
var el = $(e); | |
ctrl.load(parseInt(el.attr('limit'), null)); | |
}); | |
}); | |
// Mock/dummy helpers | |
function dummyData(limit) { | |
return {configurations: Array.apply(0, Array(limit || 100)).map(function (x, y) { | |
var seq = y + 1; | |
return getFakeSite(); })}; | |
} | |
// Using jQuery + native JS/DOM code | |
const SVCS_ICONS = {'ftp': 'file', 'ntp': 'clock', 'web': 'globe', 'ldap': 'user', 'fw': 'lock'}; | |
const SVCS = Object.keys(SVCS_ICONS); | |
const USERS = ['alice', 'bob', 'carol', 'dan', 'eve']; | |
const STATUS_ICONS = ['check', 'thumbs-up', 'fire', 'exclamation-circle']; | |
const TOOLBAR_ICONS = ['line-chart', 'map']; | |
const ICONS_HTML = TOOLBAR_ICONS | |
.map(getIconHtml) | |
.join(''); | |
const MOCK_DATA = dummyData(500);// NOTE: way slower with animation/transitions enabled | |
// fa-exclamation-circle | |
function getIconHtml(ico) {return '<b title="' + Utils.escapeHtml(ico) + '" class="fa fa-' + Utils.escapeHtml(ico) + '"></b>';} | |
function rand(limit) {return parseInt(Math.random() * limit);} | |
function getFakeSite() { | |
var hostId = rand(1001); | |
var svc = SVCS[rand(SVCS.length)]; | |
return { | |
name: svc + hostId + '-nessus.com', | |
hostname: 'nessus-' + svc + '-' + hostId, | |
port: rand(1000) + 100, | |
username: USERS[rand(USERS.length)], | |
service: svc | |
} | |
} | |
</script> | |
<style> | |
body { | |
font-family: Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif; | |
margin: 0px; | |
padding: 78px 0px 0px 0px; | |
/* for fixed heading */ | |
/*padding-top: 67px;*/ | |
} | |
h1 { margin: 4px 2px; } | |
h4 { margin: 2px 2px; } | |
.page-header-fixed { | |
position: fixed; | |
top: 0px; | |
left: 0; | |
right: 0; | |
} | |
/* Define a bootstrap-ish minimal style | |
---- 425363 */ | |
.text-success { color: #94c0e9; } | |
.text-info { color: #0071ce; } | |
.text-warning { color: #ff8300; } | |
.page-header { | |
/*line-height: 48px;*/ | |
transition: all 0.2s; | |
color: #FFF; | |
background: #425363; | |
padding: 6px 10px; | |
margin-bottom: 10px; | |
box-shadow: rgba(0, 0, 0, 0.90) 5px 1px 4px 0px; | |
} | |
.panel { | |
transition: all 0.25s ease-in; | |
box-sizing: border-box; | |
color: #666666; | |
background-color: #FFF; | |
border: 1px solid #DEDEDE; | |
border-top: none; | |
border-radius: 2px 2px 2px 2px; | |
font-family: Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif; | |
font-size: 15px; | |
line-height: 20px; | |
margin: 8px 2px; | |
} | |
.panel-heading { | |
cursor: pointer; | |
background-color: #0071ce; | |
transition: background-color 0.25s ease-in, color 0.25s ease-in; | |
border-color: #2196F3; | |
border-top-left-radius: 2px 2px 0px 0px; | |
box-sizing: border-box; | |
color: #fff; | |
display: block; | |
font-family: Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif; | |
font-size: 18px; | |
font-weight: 400; | |
line-height: 19px; | |
padding: 10px 15px 10px 15px; | |
} | |
.panel-body { | |
transition: background-color 0.25s ease-in, color 0.25s ease-in; | |
padding: 15px; | |
} | |
/*.btn { | |
border: none; | |
font-family: inherit; | |
cursor: pointer; | |
padding: 25px 80px; | |
display: inline-block; | |
margin: 15px 30px; | |
text-transform: uppercase; | |
letter-spacing: 1px; | |
font-weight: 700; | |
outline: none; | |
position: relative; | |
}*/ | |
.status .fa { | |
font-size: 22px; | |
margin: 6px; | |
} | |
.panel-footer { | |
width: 100%; | |
text-align: right; | |
} | |
.panel-footer .fa { | |
font-size: 18px; | |
margin: 8px; | |
cursor: pointer; | |
} | |
.panel .fa { | |
opacity: 1; | |
} | |
.panel:hover .panel-heading { | |
background-color: #ff8300; | |
} | |
.panel:hover { | |
box-shadow: 2px 2px 3px 3px rgba(102, 102, 102, 0.8); | |
} | |
.panel:hover .fa { | |
transition: all 0.2s ease, font-size 0.2s ease-in; | |
color: #ff8300; | |
text-shadow: 2px 2px 0px #FFFFFF; | |
} | |
.panel:hover .panel-heading .fa { | |
color: #333333; | |
} | |
.panel-heading .fa-exclamation-circle { | |
color: yellow; | |
} | |
.panel-heading .fa-check { | |
color: #FFF; | |
} | |
.panel-heading .fa-fire { | |
color: #ff8300; | |
font-size: 26px; | |
} | |
.panel-body b.fa { | |
width: 30px; | |
font-size: 20px; | |
text-align: center; | |
} | |
#configurationList { | |
width: 99%; | |
-webkit-flex-flow: row wrap; | |
display: flex; | |
flex-flow: row wrap; | |
justify-content: space-around; | |
} | |
#configurationList .panel { | |
display: inline-block; | |
width: 250px; | |
min-height: 100px; | |
} | |
@media screen and (max-width: 720px) { | |
#configurationList .panel { | |
width: 340px; /* up to 2 col */ | |
} | |
} | |
@media screen and (max-width: 1024px) { | |
#configurationList .panel { | |
width: 200px; /* up to 5 col */ | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div class='page-header page-header-fixed'> | |
<h1>Configuration</h1> | |
</div> | |
<div id='configurationList'> | |
<div class='tile'> | |
<h2> | |
<b class='fa fa-refresh fa-spin'></b> | |
<i>Loading, please wait...</i> | |
</h2> | |
</div> | |
</div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment