Initial commit
This commit is contained in:
commit
8f551e614e
79
.gitignore
vendored
Normal file
79
.gitignore
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
|
||||
|
||||
*.iml
|
||||
|
||||
/dist
|
||||
## Directory-based project format:
|
||||
.idea/
|
||||
# if you remove the above rule, at least ignore the following:
|
||||
|
||||
# User-specific stuff:
|
||||
# .idea/workspace.xml
|
||||
# .idea/tasks.xml
|
||||
# .idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
# .idea/dataSources.ids
|
||||
# .idea/dataSources.xml
|
||||
# .idea/sqlDataSources.xml
|
||||
# .idea/dynamic.xml
|
||||
# .idea/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
# .idea/gradle.xml
|
||||
# .idea/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
# .idea/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
### Node template
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directory
|
||||
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
|
||||
node_modules
|
||||
|
30
.jshintrc
Normal file
30
.jshintrc
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"node": true,
|
||||
"esnext": true,
|
||||
"bitwise": true,
|
||||
"camelcase": true,
|
||||
"curly": true,
|
||||
"eqeqeq": true,
|
||||
"immed": true,
|
||||
"indent": 2,
|
||||
"latedef": true,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"quotmark": "single",
|
||||
"regexp": true,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"strict": true,
|
||||
"trailing": true,
|
||||
"smarttabs": true,
|
||||
"white": true,
|
||||
"validthis": true,
|
||||
"browser" : true,
|
||||
"jquery":true,
|
||||
|
||||
"globals": {
|
||||
|
||||
"angular": false
|
||||
|
||||
}
|
||||
}
|
174
app/css/app.css
Normal file
174
app/css/app.css
Normal file
@ -0,0 +1,174 @@
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
input,
|
||||
textarea,
|
||||
buttons {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.004);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Layout CSS
|
||||
*/
|
||||
#header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
transition: left 0.2s;
|
||||
}
|
||||
|
||||
#sidedrawer {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 200px;
|
||||
left: -200px;
|
||||
overflow: auto;
|
||||
z-index: 2;
|
||||
background-color: #fff;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
#content-wrapper {
|
||||
min-height: 100%;
|
||||
overflow-x: hidden;
|
||||
margin-left: 0px;
|
||||
transition: margin-left 0.2s;
|
||||
|
||||
/* sticky bottom */
|
||||
margin-bottom: -160px;
|
||||
padding-bottom: 160px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
height: 160px;
|
||||
margin-left: 0px;
|
||||
transition: margin-left 0.2s;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
#header {
|
||||
left: 200px;
|
||||
}
|
||||
|
||||
#sidedrawer {
|
||||
transform: translate(200px);
|
||||
}
|
||||
|
||||
#content-wrapper {
|
||||
margin-left: 200px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
margin-left: 200px;
|
||||
}
|
||||
|
||||
body.hide-sidedrawer #header {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
body.hide-sidedrawer #sidedrawer {
|
||||
transform: translate(0px);
|
||||
}
|
||||
|
||||
body.hide-sidedrawer #content-wrapper {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
body.hide-sidedrawer #footer {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Toggle Side drawer
|
||||
*/
|
||||
#sidedrawer.active {
|
||||
transform: translate(200px);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Header CSS
|
||||
*/
|
||||
.sidedrawer-toggle {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.sidedrawer-toggle:hover {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Footer CSS
|
||||
*/
|
||||
#footer {
|
||||
background-color: #0288D1;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#footer a {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Side drawer CSS
|
||||
*/
|
||||
#sidedrawer-brand {
|
||||
/* padding-left: 20px; */
|
||||
}
|
||||
|
||||
#sidedrawer ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#sidedrawer > ul {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
#sidedrawer > ul > li:first-child {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
#sidedrawer strong {
|
||||
display: block;
|
||||
padding: 15px 22px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#sidedrawer .entry:hover {
|
||||
background-color: #E0E0E0;
|
||||
}
|
||||
|
||||
#sidedrawer strong + ul > li {
|
||||
padding: 6px 0px;
|
||||
}
|
||||
#sidedrawer .entry {
|
||||
padding-bottom: 5px;
|
||||
border-bottom:1px solid #e0c0e0;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
#sidedrawer .entry:after {
|
||||
font-family:Linearicons-Free;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
|
||||
float:right;
|
||||
content:"\e87a"
|
||||
}
|
53
app/css/md.css
Normal file
53
app/css/md.css
Normal file
@ -0,0 +1,53 @@
|
||||
body {
|
||||
font-family: 'Roboto Slab', "Helvetica Neue", Helvetica, Arial;
|
||||
}
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0 4px 0 0;
|
||||
}
|
||||
|
||||
.dates {
|
||||
padding: 2px;
|
||||
border: solid 1px #80007e;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#btc, #fx {
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
.up, .ontime {
|
||||
color: darkgreen;
|
||||
}
|
||||
|
||||
.down, .delayed {
|
||||
color: darkred;
|
||||
}
|
||||
|
||||
.nochange {
|
||||
color: #000000;
|
||||
}
|
||||
.password {
|
||||
border: 1px solid #cccccc;
|
||||
background-color: #efefef;
|
||||
font-family: monospace;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.mui--text-danger {
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
.fnBlock {
|
||||
font-size:20px;
|
||||
}
|
||||
|
||||
.fnRefresh {
|
||||
|
||||
}
|
67
app/index.html
Normal file
67
app/index.html
Normal file
@ -0,0 +1,67 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="//fonts.googleapis.com/css?family=Roboto+Slab:400,300,700" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="//cdn.linearicons.com/free/1.0.0/icon-font.min.css">
|
||||
<link href="//cdn.muicss.com/mui-0.4.6/css/mui.min.css" rel="stylesheet" type="text/css" />
|
||||
<!-- inject:css -->
|
||||
<link href="css/app.css" rel="stylesheet" type="text/css" />
|
||||
<link href="css/md.css" rel="stylesheet" type="text/css" />
|
||||
<!-- endinject -->
|
||||
<script src="//cdn.muicss.com/mui-0.4.6/js/mui.min.js"></script>
|
||||
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
|
||||
<script src="libs/ejs.js"></script>
|
||||
<script src="libs/view.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="sidedrawer" class="mui--no-user-select">
|
||||
<div id="sidedrawer-brand" class="mui--appbar-line-height mui--text-title">Recipes</div>
|
||||
<div class="mui-divider"></div>
|
||||
<div id="functions" class="fnBlock">
|
||||
<span id="fnRefresh" class="lnr lnr-sync"></span>
|
||||
<span id="fnSearch" class="lnr lnr-magnifier"></span>
|
||||
</div>
|
||||
<div id='searchbox' class="mui-textfield" style="display: none;">
|
||||
<input id='newsearch' type="text" placeholder="Search for..">
|
||||
</div>
|
||||
<div class="mui-divider"></div>
|
||||
<div id="listContainer">
|
||||
|
||||
</div>
|
||||
<div class="mui-divider"></div>
|
||||
<div class="mui-textfield">
|
||||
<input id='newurl' type="text" placeholder="Add new url">
|
||||
<div id="addstatus" style="display:none;">Adding...</div>
|
||||
</div>
|
||||
</div>
|
||||
<header id="header">
|
||||
<div class="mui-appbar mui--appbar-line-height">
|
||||
<div class="mui-container-fluid">
|
||||
<a class="sidedrawer-toggle mui--visible-xs-inline-block js-show-sidedrawer">☰</a>
|
||||
<a class="sidedrawer-toggle mui--hidden-xs js-hide-sidedrawer">☰</a>
|
||||
<span class="mui--text-title mui--visible-xs-inline-block">Brand.io</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div id="content-wrapper">
|
||||
<div class="mui--appbar-height"></div>
|
||||
<div class="mui-container-fluid" id="bodyContents">
|
||||
<!-- content here -->
|
||||
</div>
|
||||
</div>
|
||||
<footer id="footer">
|
||||
<div class="mui-container-fluid">
|
||||
<br>
|
||||
Made with ♥ by Martin</a>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
<!-- inject:js -->
|
||||
<script src="js/shell.js"></script>
|
||||
<script src="js/app.js"></script>
|
||||
<!-- endinject -->
|
||||
</html>
|
66
app/index.prod.html
Normal file
66
app/index.prod.html
Normal file
@ -0,0 +1,66 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="//fonts.googleapis.com/css?family=Roboto+Slab:400,300,700" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="//cdn.linearicons.com/free/1.0.0/icon-font.min.css">
|
||||
<link href="//cdn.muicss.com/mui-0.4.6/css/mui.min.css" rel="stylesheet" type="text/css" />
|
||||
<!-- inject:css -->
|
||||
<link href="css/app.min.css" rel="stylesheet" type="text/css" />
|
||||
<link href="css/md.min.css" rel="stylesheet" type="text/css" />
|
||||
<!-- endinject -->
|
||||
<script src="//cdn.muicss.com/mui-0.4.6/js/mui.min.js"></script>
|
||||
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
|
||||
<script src="libs/ejs_production.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="sidedrawer" class="mui--no-user-select">
|
||||
<div id="sidedrawer-brand" class="mui--appbar-line-height mui--text-title">Recipes</div>
|
||||
<div class="mui-divider"></div>
|
||||
<div id="functions" class="fnBlock">
|
||||
<span id="fnRefresh" class="lnr lnr-sync"></span>
|
||||
<span id="fnSearch" class="lnr lnr-magnifier"></span>
|
||||
</div>
|
||||
<div id='searchbox' class="mui-textfield" style="display: none;">
|
||||
<input id='newsearch' type="text" placeholder="Search for..">
|
||||
</div>
|
||||
<div class="mui-divider"></div>
|
||||
<div id="listContainer">
|
||||
|
||||
</div>
|
||||
<div class="mui-divider"></div>
|
||||
<div class="mui-textfield">
|
||||
<input id='newurl' type="text" placeholder="Add new url">
|
||||
<div id="addstatus" style="display:none;">Adding...</div>
|
||||
</div>
|
||||
</div>
|
||||
<header id="header">
|
||||
<div class="mui-appbar mui--appbar-line-height">
|
||||
<div class="mui-container-fluid">
|
||||
<a class="sidedrawer-toggle mui--visible-xs-inline-block js-show-sidedrawer">☰</a>
|
||||
<a class="sidedrawer-toggle mui--hidden-xs js-hide-sidedrawer">☰</a>
|
||||
<span class="mui--text-title mui--visible-xs-inline-block">Brand.io</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div id="content-wrapper">
|
||||
<div class="mui--appbar-height"></div>
|
||||
<div class="mui-container-fluid" id="bodyContents">
|
||||
<!-- content here -->
|
||||
</div>
|
||||
</div>
|
||||
<footer id="footer">
|
||||
<div class="mui-container-fluid">
|
||||
<br>
|
||||
Made with ♥ by Martin</a>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
<!-- inject:js -->
|
||||
<script src="js/main.min.js"></script>
|
||||
|
||||
<!-- endinject -->
|
||||
</html>
|
150
app/js/app.js
Normal file
150
app/js/app.js
Normal file
@ -0,0 +1,150 @@
|
||||
'use strict';
|
||||
/**
|
||||
* Created by Martin on 24/02/2016.
|
||||
*/
|
||||
|
||||
$.fn.pressEnter = function (fn) {
|
||||
|
||||
return this.each(function () {
|
||||
$(this).bind('enterPress', fn);
|
||||
$(this).keyup(function (e) {
|
||||
if (e.keyCode === 13) {
|
||||
$(this).trigger('enterPress');
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
(function () {
|
||||
console.log('GO!');
|
||||
var $list = $('#listContainer');
|
||||
var displayList = function (obj) {
|
||||
var html = new EJS({url: '/partials/list.ejs'}).render(obj);
|
||||
console.log(html);
|
||||
$list.empty();
|
||||
$list.append(html);
|
||||
$('#listContainer').find('.entry').not('.emptyMessage').click(function () {
|
||||
console.log('Clicked list. ' + this.id);
|
||||
getRecipe(this.id);
|
||||
});
|
||||
}, displayPage = function (obj) {
|
||||
var $bodyContents = $('#bodyContents');
|
||||
|
||||
if (obj.reduced.length > 0) {
|
||||
$bodyContents.empty();
|
||||
$bodyContents.append(obj.reduced);
|
||||
}
|
||||
}, getRecipe = function (id) {
|
||||
console.log('get recipe');
|
||||
var url = '/entry/' + id;
|
||||
var data = '';
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: url,
|
||||
data: data,
|
||||
dataType: 'json',
|
||||
|
||||
timeout: 10000,
|
||||
|
||||
//contentType: ('application/json'),
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'PUT, GET, POST, DELETE, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type'
|
||||
|
||||
},
|
||||
success: function (data) {
|
||||
// console.log(data);
|
||||
displayPage(data);
|
||||
},
|
||||
error: function (xhr, type) {
|
||||
console.log('ajax error');
|
||||
console.log(xhr);
|
||||
console.log(type);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getList = function () {
|
||||
|
||||
var url = '/list';
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: url,
|
||||
data: '',
|
||||
dataType: 'json',
|
||||
|
||||
timeout: 10000,
|
||||
|
||||
//contentType: ('application/json'),
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'PUT, GET, POST, DELETE, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type'
|
||||
|
||||
},
|
||||
success: function (data) {
|
||||
// console.log(data);
|
||||
displayList(data);
|
||||
},
|
||||
error: function (xhr, type) {
|
||||
console.log('ajax error');
|
||||
console.log(xhr);
|
||||
console.log(type);
|
||||
}
|
||||
});
|
||||
}, addNew = function (newUrl) {
|
||||
var url = '/add';
|
||||
|
||||
var data = {url: JSON.stringify(newUrl)};
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: url,
|
||||
data: data,
|
||||
dataType: 'json',
|
||||
|
||||
timeout: 10000,
|
||||
|
||||
//contentType: ('application/json'),
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'PUT, GET, POST, DELETE, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type'
|
||||
|
||||
},
|
||||
success: function () {
|
||||
// console.log(data);
|
||||
// displayList(data);
|
||||
},
|
||||
error: function (xhr, type) {
|
||||
console.log('ajax error');
|
||||
console.log(xhr);
|
||||
console.log(type);
|
||||
}
|
||||
});
|
||||
},
|
||||
start = function () {
|
||||
|
||||
getList();
|
||||
};
|
||||
|
||||
$('#newurl').pressEnter(function () {
|
||||
var url = $(this).val();
|
||||
if (url !== null) {
|
||||
console.log('Adding: ' + url);
|
||||
addNew(url);
|
||||
$('#addstatus').fadeIn(400).delay(1500).fadeOut(400);
|
||||
$(this).val('');
|
||||
/* setTimeout(function () {
|
||||
getList();
|
||||
}, 5000);*/
|
||||
}
|
||||
});
|
||||
|
||||
$('#fnRefresh').on('click', function () {
|
||||
getList();
|
||||
});
|
||||
|
||||
start();
|
||||
})();
|
45
app/js/shell.js
Normal file
45
app/js/shell.js
Normal file
@ -0,0 +1,45 @@
|
||||
jQuery(function($) {
|
||||
var $bodyEl = $('body'),
|
||||
$sidedrawerEl = $('#sidedrawer');
|
||||
|
||||
|
||||
function showSidedrawer() {
|
||||
// show overlay
|
||||
var options = {
|
||||
onclose: function() {
|
||||
$sidedrawerEl
|
||||
.removeClass('active')
|
||||
.appendTo(document.body);
|
||||
}
|
||||
};
|
||||
|
||||
var $overlayEl = $(mui.overlay('on', options));
|
||||
|
||||
// show element
|
||||
$sidedrawerEl.appendTo($overlayEl);
|
||||
setTimeout(function() {
|
||||
$sidedrawerEl.addClass('active');
|
||||
}, 20);
|
||||
}
|
||||
|
||||
|
||||
function hideSidedrawer() {
|
||||
$bodyEl.toggleClass('hide-sidedrawer');
|
||||
}
|
||||
|
||||
|
||||
$('.js-show-sidedrawer').on('click', showSidedrawer);
|
||||
$('.js-hide-sidedrawer').on('click', hideSidedrawer);
|
||||
});
|
||||
|
||||
var $titleEls = $('strong', $sidedrawerEl);
|
||||
|
||||
$titleEls
|
||||
.next()
|
||||
.hide();
|
||||
|
||||
$titleEls.on('click', function() {
|
||||
$(this).next().slideToggle(200);
|
||||
});/**
|
||||
* Created by Martin on 24/02/2016.
|
||||
*/
|
505
app/libs/ejs.js
Normal file
505
app/libs/ejs.js
Normal file
@ -0,0 +1,505 @@
|
||||
(function(){
|
||||
|
||||
|
||||
var rsplit = function(string, regex) {
|
||||
var result = regex.exec(string),retArr = new Array(), first_idx, last_idx, first_bit;
|
||||
while (result != null)
|
||||
{
|
||||
first_idx = result.index; last_idx = regex.lastIndex;
|
||||
if ((first_idx) != 0)
|
||||
{
|
||||
first_bit = string.substring(0,first_idx);
|
||||
retArr.push(string.substring(0,first_idx));
|
||||
string = string.slice(first_idx);
|
||||
}
|
||||
retArr.push(result[0]);
|
||||
string = string.slice(result[0].length);
|
||||
result = regex.exec(string);
|
||||
}
|
||||
if (! string == '')
|
||||
{
|
||||
retArr.push(string);
|
||||
}
|
||||
return retArr;
|
||||
},
|
||||
chop = function(string){
|
||||
return string.substr(0, string.length - 1);
|
||||
},
|
||||
extend = function(d, s){
|
||||
for(var n in s){
|
||||
if(s.hasOwnProperty(n)) d[n] = s[n]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EJS = function( options ){
|
||||
options = typeof options == "string" ? {view: options} : options
|
||||
this.set_options(options);
|
||||
if(options.precompiled){
|
||||
this.template = {};
|
||||
this.template.process = options.precompiled;
|
||||
EJS.update(this.name, this);
|
||||
return;
|
||||
}
|
||||
if(options.element)
|
||||
{
|
||||
if(typeof options.element == 'string'){
|
||||
var name = options.element
|
||||
options.element = document.getElementById( options.element )
|
||||
if(options.element == null) throw name+'does not exist!'
|
||||
}
|
||||
if(options.element.value){
|
||||
this.text = options.element.value
|
||||
}else{
|
||||
this.text = options.element.innerHTML
|
||||
}
|
||||
this.name = options.element.id
|
||||
this.type = '['
|
||||
}else if(options.url){
|
||||
options.url = EJS.endExt(options.url, this.extMatch);
|
||||
this.name = this.name ? this.name : options.url;
|
||||
var url = options.url
|
||||
//options.view = options.absolute_url || options.view || options.;
|
||||
var template = EJS.get(this.name /*url*/, this.cache);
|
||||
if (template) return template;
|
||||
if (template == EJS.INVALID_PATH) return null;
|
||||
try{
|
||||
this.text = EJS.request( url+(this.cache ? '' : '?'+Math.random() ));
|
||||
}catch(e){}
|
||||
|
||||
if(this.text == null){
|
||||
throw( {type: 'EJS', message: 'There is no template at '+url} );
|
||||
}
|
||||
//this.name = url;
|
||||
}
|
||||
var template = new EJS.Compiler(this.text, this.type);
|
||||
|
||||
template.compile(options, this.name);
|
||||
|
||||
|
||||
EJS.update(this.name, this);
|
||||
this.template = template;
|
||||
};
|
||||
/* @Prototype*/
|
||||
EJS.prototype = {
|
||||
/**
|
||||
* Renders an object with extra view helpers attached to the view.
|
||||
* @param {Object} object data to be rendered
|
||||
* @param {Object} extra_helpers an object with additonal view helpers
|
||||
* @return {String} returns the result of the string
|
||||
*/
|
||||
render : function(object, extra_helpers){
|
||||
object = object || {};
|
||||
this._extra_helpers = extra_helpers;
|
||||
var v = new EJS.Helpers(object, extra_helpers || {});
|
||||
return this.template.process.call(object, object,v);
|
||||
},
|
||||
update : function(element, options){
|
||||
if(typeof element == 'string'){
|
||||
element = document.getElementById(element)
|
||||
}
|
||||
if(options == null){
|
||||
_template = this;
|
||||
return function(object){
|
||||
EJS.prototype.update.call(_template, element, object)
|
||||
}
|
||||
}
|
||||
if(typeof options == 'string'){
|
||||
params = {}
|
||||
params.url = options
|
||||
_template = this;
|
||||
params.onComplete = function(request){
|
||||
var object = eval( request.responseText )
|
||||
EJS.prototype.update.call(_template, element, object)
|
||||
}
|
||||
EJS.ajax_request(params)
|
||||
}else
|
||||
{
|
||||
element.innerHTML = this.render(options)
|
||||
}
|
||||
},
|
||||
out : function(){
|
||||
return this.template.out;
|
||||
},
|
||||
/**
|
||||
* Sets options on this view to be rendered with.
|
||||
* @param {Object} options
|
||||
*/
|
||||
set_options : function(options){
|
||||
this.type = options.type || EJS.type;
|
||||
this.cache = options.cache != null ? options.cache : EJS.cache;
|
||||
this.text = options.text || null;
|
||||
this.name = options.name || null;
|
||||
this.ext = options.ext || EJS.ext;
|
||||
this.extMatch = new RegExp(this.ext.replace(/\./, '\.'));
|
||||
}
|
||||
};
|
||||
EJS.endExt = function(path, match){
|
||||
if(!path) return null;
|
||||
match.lastIndex = 0
|
||||
return path+ (match.test(path) ? '' : this.ext )
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* @Static*/
|
||||
EJS.Scanner = function(source, left, right) {
|
||||
|
||||
extend(this,
|
||||
{left_delimiter: left +'%',
|
||||
right_delimiter: '%'+right,
|
||||
double_left: left+'%%',
|
||||
double_right: '%%'+right,
|
||||
left_equal: left+'%=',
|
||||
left_comment: left+'%#'})
|
||||
|
||||
this.SplitRegexp = left=='[' ? /(\[%%)|(%%\])|(\[%=)|(\[%#)|(\[%)|(%\]\n)|(%\])|(\n)/ : new RegExp('('+this.double_left+')|(%%'+this.double_right+')|('+this.left_equal+')|('+this.left_comment+')|('+this.left_delimiter+')|('+this.right_delimiter+'\n)|('+this.right_delimiter+')|(\n)') ;
|
||||
|
||||
this.source = source;
|
||||
this.stag = null;
|
||||
this.lines = 0;
|
||||
};
|
||||
|
||||
EJS.Scanner.to_text = function(input){
|
||||
if(input == null || input === undefined)
|
||||
return '';
|
||||
if(input instanceof Date)
|
||||
return input.toDateString();
|
||||
if(input.toString)
|
||||
return input.toString();
|
||||
return '';
|
||||
};
|
||||
|
||||
EJS.Scanner.prototype = {
|
||||
scan: function(block) {
|
||||
scanline = this.scanline;
|
||||
regex = this.SplitRegexp;
|
||||
if (! this.source == '')
|
||||
{
|
||||
var source_split = rsplit(this.source, /\n/);
|
||||
for(var i=0; i<source_split.length; i++) {
|
||||
var item = source_split[i];
|
||||
this.scanline(item, regex, block);
|
||||
}
|
||||
}
|
||||
},
|
||||
scanline: function(line, regex, block) {
|
||||
this.lines++;
|
||||
var line_split = rsplit(line, regex);
|
||||
for(var i=0; i<line_split.length; i++) {
|
||||
var token = line_split[i];
|
||||
if (token != null) {
|
||||
try{
|
||||
block(token, this);
|
||||
}catch(e){
|
||||
throw {type: 'EJS.Scanner', line: this.lines};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
EJS.Buffer = function(pre_cmd, post_cmd) {
|
||||
this.line = new Array();
|
||||
this.script = "";
|
||||
this.pre_cmd = pre_cmd;
|
||||
this.post_cmd = post_cmd;
|
||||
for (var i=0; i<this.pre_cmd.length; i++)
|
||||
{
|
||||
this.push(pre_cmd[i]);
|
||||
}
|
||||
};
|
||||
EJS.Buffer.prototype = {
|
||||
|
||||
push: function(cmd) {
|
||||
this.line.push(cmd);
|
||||
},
|
||||
|
||||
cr: function() {
|
||||
this.script = this.script + this.line.join('; ');
|
||||
this.line = new Array();
|
||||
this.script = this.script + "\n";
|
||||
},
|
||||
|
||||
close: function() {
|
||||
if (this.line.length > 0)
|
||||
{
|
||||
for (var i=0; i<this.post_cmd.length; i++){
|
||||
this.push(pre_cmd[i]);
|
||||
}
|
||||
this.script = this.script + this.line.join('; ');
|
||||
line = null;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
EJS.Compiler = function(source, left) {
|
||||
this.pre_cmd = ['var ___ViewO = [];'];
|
||||
this.post_cmd = new Array();
|
||||
this.source = ' ';
|
||||
if (source != null)
|
||||
{
|
||||
if (typeof source == 'string')
|
||||
{
|
||||
source = source.replace(/\r\n/g, "\n");
|
||||
source = source.replace(/\r/g, "\n");
|
||||
this.source = source;
|
||||
}else if (source.innerHTML){
|
||||
this.source = source.innerHTML;
|
||||
}
|
||||
if (typeof this.source != 'string'){
|
||||
this.source = "";
|
||||
}
|
||||
}
|
||||
left = left || '<';
|
||||
var right = '>';
|
||||
switch(left) {
|
||||
case '[':
|
||||
right = ']';
|
||||
break;
|
||||
case '<':
|
||||
break;
|
||||
default:
|
||||
throw left+' is not a supported deliminator';
|
||||
break;
|
||||
}
|
||||
this.scanner = new EJS.Scanner(this.source, left, right);
|
||||
this.out = '';
|
||||
};
|
||||
EJS.Compiler.prototype = {
|
||||
compile: function(options, name) {
|
||||
options = options || {};
|
||||
this.out = '';
|
||||
var put_cmd = "___ViewO.push(";
|
||||
var insert_cmd = put_cmd;
|
||||
var buff = new EJS.Buffer(this.pre_cmd, this.post_cmd);
|
||||
var content = '';
|
||||
var clean = function(content)
|
||||
{
|
||||
content = content.replace(/\\/g, '\\\\');
|
||||
content = content.replace(/\n/g, '\\n');
|
||||
content = content.replace(/"/g, '\\"');
|
||||
return content;
|
||||
};
|
||||
this.scanner.scan(function(token, scanner) {
|
||||
if (scanner.stag == null)
|
||||
{
|
||||
switch(token) {
|
||||
case '\n':
|
||||
content = content + "\n";
|
||||
buff.push(put_cmd + '"' + clean(content) + '");');
|
||||
buff.cr();
|
||||
content = '';
|
||||
break;
|
||||
case scanner.left_delimiter:
|
||||
case scanner.left_equal:
|
||||
case scanner.left_comment:
|
||||
scanner.stag = token;
|
||||
if (content.length > 0)
|
||||
{
|
||||
buff.push(put_cmd + '"' + clean(content) + '")');
|
||||
}
|
||||
content = '';
|
||||
break;
|
||||
case scanner.double_left:
|
||||
content = content + scanner.left_delimiter;
|
||||
break;
|
||||
default:
|
||||
content = content + token;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch(token) {
|
||||
case scanner.right_delimiter:
|
||||
switch(scanner.stag) {
|
||||
case scanner.left_delimiter:
|
||||
if (content[content.length - 1] == '\n')
|
||||
{
|
||||
content = chop(content);
|
||||
buff.push(content);
|
||||
buff.cr();
|
||||
}
|
||||
else {
|
||||
buff.push(content);
|
||||
}
|
||||
break;
|
||||
case scanner.left_equal:
|
||||
buff.push(insert_cmd + "(EJS.Scanner.to_text(" + content + ")))");
|
||||
break;
|
||||
}
|
||||
scanner.stag = null;
|
||||
content = '';
|
||||
break;
|
||||
case scanner.double_right:
|
||||
content = content + scanner.right_delimiter;
|
||||
break;
|
||||
default:
|
||||
content = content + token;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (content.length > 0)
|
||||
{
|
||||
// Chould be content.dump in Ruby
|
||||
buff.push(put_cmd + '"' + clean(content) + '")');
|
||||
}
|
||||
buff.close();
|
||||
this.out = buff.script + ";";
|
||||
var to_be_evaled = '/*'+name+'*/this.process = function(_CONTEXT,_VIEW) { try { with(_VIEW) { with (_CONTEXT) {'+this.out+" return ___ViewO.join('');}}}catch(e){e.lineNumber=null;throw e;}};";
|
||||
|
||||
try{
|
||||
eval(to_be_evaled);
|
||||
}catch(e){
|
||||
if(typeof JSLINT != 'undefined'){
|
||||
JSLINT(this.out);
|
||||
for(var i = 0; i < JSLINT.errors.length; i++){
|
||||
var error = JSLINT.errors[i];
|
||||
if(error.reason != "Unnecessary semicolon."){
|
||||
error.line++;
|
||||
var e = new Error();
|
||||
e.lineNumber = error.line;
|
||||
e.message = error.reason;
|
||||
if(options.view)
|
||||
e.fileName = options.view;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//type, cache, folder
|
||||
/**
|
||||
* Sets default options for all views
|
||||
* @param {Object} options Set view with the following options
|
||||
* <table class="options">
|
||||
<tbody><tr><th>Option</th><th>Default</th><th>Description</th></tr>
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>'<'</td>
|
||||
<td>type of magic tags. Options are '<' or '['
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>cache</td>
|
||||
<td>true in production mode, false in other modes</td>
|
||||
<td>true to cache template.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
*
|
||||
*/
|
||||
EJS.config = function(options){
|
||||
EJS.cache = options.cache != null ? options.cache : EJS.cache;
|
||||
EJS.type = options.type != null ? options.type : EJS.type;
|
||||
EJS.ext = options.ext != null ? options.ext : EJS.ext;
|
||||
|
||||
var templates_directory = EJS.templates_directory || {}; //nice and private container
|
||||
EJS.templates_directory = templates_directory;
|
||||
EJS.get = function(path, cache){
|
||||
if(cache == false) return null;
|
||||
if(templates_directory[path]) return templates_directory[path];
|
||||
return null;
|
||||
};
|
||||
|
||||
EJS.update = function(path, template) {
|
||||
if(path == null) return;
|
||||
templates_directory[path] = template ;
|
||||
};
|
||||
|
||||
EJS.INVALID_PATH = -1;
|
||||
};
|
||||
EJS.config( {cache: true, type: '<', ext: '.ejs' } );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* By adding functions to EJS.Helpers.prototype, those functions will be available in the
|
||||
* views.
|
||||
* @init Creates a view helper. This function is called internally. You should never call it.
|
||||
* @param {Object} data The data passed to the view. Helpers have access to it through this._data
|
||||
*/
|
||||
EJS.Helpers = function(data, extras){
|
||||
this._data = data;
|
||||
this._extras = extras;
|
||||
extend(this, extras );
|
||||
};
|
||||
/* @prototype*/
|
||||
EJS.Helpers.prototype = {
|
||||
/**
|
||||
* Renders a new view. If data is passed in, uses that to render the view.
|
||||
* @param {Object} options standard options passed to a new view.
|
||||
* @param {optional:Object} data
|
||||
* @return {String}
|
||||
*/
|
||||
view: function(options, data, helpers){
|
||||
if(!helpers) helpers = this._extras
|
||||
if(!data) data = this._data;
|
||||
return new EJS(options).render(data, helpers);
|
||||
},
|
||||
/**
|
||||
* For a given value, tries to create a human representation.
|
||||
* @param {Object} input the value being converted.
|
||||
* @param {Object} null_text what text should be present if input == null or undefined, defaults to ''
|
||||
* @return {String}
|
||||
*/
|
||||
to_text: function(input, null_text) {
|
||||
if(input == null || input === undefined) return null_text || '';
|
||||
if(input instanceof Date) return input.toDateString();
|
||||
if(input.toString) return input.toString().replace(/\n/g, '<br />').replace(/''/g, "'");
|
||||
return '';
|
||||
}
|
||||
};
|
||||
EJS.newRequest = function(){
|
||||
var factories = [function() { return new ActiveXObject("Msxml2.XMLHTTP"); },function() { return new XMLHttpRequest(); },function() { return new ActiveXObject("Microsoft.XMLHTTP"); }];
|
||||
for(var i = 0; i < factories.length; i++) {
|
||||
try {
|
||||
var request = factories[i]();
|
||||
if (request != null) return request;
|
||||
}
|
||||
catch(e) { continue;}
|
||||
}
|
||||
}
|
||||
|
||||
EJS.request = function(path){
|
||||
var request = new EJS.newRequest()
|
||||
request.open("GET", path, false);
|
||||
|
||||
try{request.send(null);}
|
||||
catch(e){return null;}
|
||||
|
||||
if ( request.status == 404 || request.status == 2 ||(request.status == 0 && request.responseText == '') ) return null;
|
||||
|
||||
return request.responseText
|
||||
}
|
||||
EJS.ajax_request = function(params){
|
||||
params.method = ( params.method ? params.method : 'GET')
|
||||
|
||||
var request = new EJS.newRequest();
|
||||
request.onreadystatechange = function(){
|
||||
if(request.readyState == 4){
|
||||
if(request.status == 200){
|
||||
params.onComplete(request)
|
||||
}else
|
||||
{
|
||||
params.onComplete(request)
|
||||
}
|
||||
}
|
||||
}
|
||||
request.open(params.method, params.url)
|
||||
request.send(null)
|
||||
}
|
||||
|
||||
|
||||
})();
|
1
app/libs/ejs_production.js
Normal file
1
app/libs/ejs_production.js
Normal file
File diff suppressed because one or more lines are too long
200
app/libs/view.js
Normal file
200
app/libs/view.js
Normal file
@ -0,0 +1,200 @@
|
||||
EJS.Helpers.prototype.date_tag = function(name, value , html_options) {
|
||||
if(! (value instanceof Date))
|
||||
value = new Date()
|
||||
|
||||
var month_names = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
||||
var years = [], months = [], days =[];
|
||||
var year = value.getFullYear();
|
||||
var month = value.getMonth();
|
||||
var day = value.getDate();
|
||||
for(var y = year - 15; y < year+15 ; y++)
|
||||
{
|
||||
years.push({value: y, text: y})
|
||||
}
|
||||
for(var m = 0; m < 12; m++)
|
||||
{
|
||||
months.push({value: (m), text: month_names[m]})
|
||||
}
|
||||
for(var d = 0; d < 31; d++)
|
||||
{
|
||||
days.push({value: (d+1), text: (d+1)})
|
||||
}
|
||||
var year_select = this.select_tag(name+'[year]', year, years, {id: name+'[year]'} )
|
||||
var month_select = this.select_tag(name+'[month]', month, months, {id: name+'[month]'})
|
||||
var day_select = this.select_tag(name+'[day]', day, days, {id: name+'[day]'})
|
||||
|
||||
return year_select+month_select+day_select;
|
||||
}
|
||||
|
||||
EJS.Helpers.prototype.form_tag = function(action, html_options) {
|
||||
|
||||
|
||||
html_options = html_options || {};
|
||||
html_options.action = action
|
||||
if(html_options.multipart == true) {
|
||||
html_options.method = 'post';
|
||||
html_options.enctype = 'multipart/form-data';
|
||||
}
|
||||
|
||||
return this.start_tag_for('form', html_options)
|
||||
}
|
||||
|
||||
EJS.Helpers.prototype.form_tag_end = function() { return this.tag_end('form'); }
|
||||
|
||||
EJS.Helpers.prototype.hidden_field_tag = function(name, value, html_options) {
|
||||
return this.input_field_tag(name, value, 'hidden', html_options);
|
||||
}
|
||||
|
||||
EJS.Helpers.prototype.input_field_tag = function(name, value , inputType, html_options) {
|
||||
|
||||
html_options = html_options || {};
|
||||
html_options.id = html_options.id || name;
|
||||
html_options.value = value || '';
|
||||
html_options.type = inputType || 'text';
|
||||
html_options.name = name;
|
||||
|
||||
return this.single_tag_for('input', html_options)
|
||||
}
|
||||
|
||||
EJS.Helpers.prototype.is_current_page = function(url) {
|
||||
return (window.location.href == url || window.location.pathname == url ? true : false);
|
||||
}
|
||||
|
||||
EJS.Helpers.prototype.link_to = function(name, url, html_options) {
|
||||
if(!name) var name = 'null';
|
||||
if(!html_options) var html_options = {}
|
||||
|
||||
if(html_options.confirm){
|
||||
html_options.onclick =
|
||||
" var ret_confirm = confirm(\""+html_options.confirm+"\"); if(!ret_confirm){ return false;} "
|
||||
html_options.confirm = null;
|
||||
}
|
||||
html_options.href=url
|
||||
return this.start_tag_for('a', html_options)+name+ this.tag_end('a');
|
||||
}
|
||||
|
||||
EJS.Helpers.prototype.submit_link_to = function(name, url, html_options){
|
||||
if(!name) var name = 'null';
|
||||
if(!html_options) var html_options = {}
|
||||
html_options.onclick = html_options.onclick || '' ;
|
||||
|
||||
if(html_options.confirm){
|
||||
html_options.onclick =
|
||||
" var ret_confirm = confirm(\""+html_options.confirm+"\"); if(!ret_confirm){ return false;} "
|
||||
html_options.confirm = null;
|
||||
}
|
||||
|
||||
html_options.value = name;
|
||||
html_options.type = 'submit'
|
||||
html_options.onclick=html_options.onclick+
|
||||
(url ? this.url_for(url) : '')+'return false;';
|
||||
//html_options.href='#'+(options ? Routes.url_for(options) : '')
|
||||
return this.start_tag_for('input', html_options)
|
||||
}
|
||||
|
||||
EJS.Helpers.prototype.link_to_if = function(condition, name, url, html_options, post, block) {
|
||||
return this.link_to_unless((condition == false), name, url, html_options, post, block);
|
||||
}
|
||||
|
||||
EJS.Helpers.prototype.link_to_unless = function(condition, name, url, html_options, block) {
|
||||
html_options = html_options || {};
|
||||
if(condition) {
|
||||
if(block && typeof block == 'function') {
|
||||
return block(name, url, html_options, block);
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
} else
|
||||
return this.link_to(name, url, html_options);
|
||||
}
|
||||
|
||||
EJS.Helpers.prototype.link_to_unless_current = function(name, url, html_options, block) {
|
||||
html_options = html_options || {};
|
||||
return this.link_to_unless(this.is_current_page(url), name, url, html_options, block)
|
||||
}
|
||||
|
||||
|
||||
EJS.Helpers.prototype.password_field_tag = function(name, value, html_options) { return this.input_field_tag(name, value, 'password', html_options); }
|
||||
|
||||
EJS.Helpers.prototype.select_tag = function(name, value, choices, html_options) {
|
||||
html_options = html_options || {};
|
||||
html_options.id = html_options.id || name;
|
||||
html_options.value = value;
|
||||
html_options.name = name;
|
||||
|
||||
var txt = ''
|
||||
txt += this.start_tag_for('select', html_options)
|
||||
|
||||
for(var i = 0; i < choices.length; i++)
|
||||
{
|
||||
var choice = choices[i];
|
||||
var optionOptions = {value: choice.value}
|
||||
if(choice.value == value)
|
||||
optionOptions.selected ='selected'
|
||||
txt += this.start_tag_for('option', optionOptions )+choice.text+this.tag_end('option')
|
||||
}
|
||||
txt += this.tag_end('select');
|
||||
return txt;
|
||||
}
|
||||
|
||||
EJS.Helpers.prototype.single_tag_for = function(tag, html_options) { return this.tag(tag, html_options, '/>');}
|
||||
|
||||
EJS.Helpers.prototype.start_tag_for = function(tag, html_options) { return this.tag(tag, html_options); }
|
||||
|
||||
EJS.Helpers.prototype.submit_tag = function(name, html_options) {
|
||||
html_options = html_options || {};
|
||||
//html_options.name = html_options.id || 'commit';
|
||||
html_options.type = html_options.type || 'submit';
|
||||
html_options.value = name || 'Submit';
|
||||
return this.single_tag_for('input', html_options);
|
||||
}
|
||||
|
||||
EJS.Helpers.prototype.tag = function(tag, html_options, end) {
|
||||
if(!end) var end = '>'
|
||||
var txt = ' '
|
||||
for(var attr in html_options) {
|
||||
if(html_options[attr] != null)
|
||||
var value = html_options[attr].toString();
|
||||
else
|
||||
var value=''
|
||||
if(attr == "Class") // special case because "class" is a reserved word in IE
|
||||
attr = "class";
|
||||
if( value.indexOf("'") != -1 )
|
||||
txt += attr+'=\"'+value+'\" '
|
||||
else
|
||||
txt += attr+"='"+value+"' "
|
||||
}
|
||||
return '<'+tag+txt+end;
|
||||
}
|
||||
|
||||
EJS.Helpers.prototype.tag_end = function(tag) { return '</'+tag+'>'; }
|
||||
|
||||
EJS.Helpers.prototype.text_area_tag = function(name, value, html_options) {
|
||||
html_options = html_options || {};
|
||||
html_options.id = html_options.id || name;
|
||||
html_options.name = html_options.name || name;
|
||||
value = value || ''
|
||||
if(html_options.size) {
|
||||
html_options.cols = html_options.size.split('x')[0]
|
||||
html_options.rows = html_options.size.split('x')[1];
|
||||
delete html_options.size
|
||||
}
|
||||
|
||||
html_options.cols = html_options.cols || 50;
|
||||
html_options.rows = html_options.rows || 4;
|
||||
|
||||
return this.start_tag_for('textarea', html_options)+value+this.tag_end('textarea')
|
||||
}
|
||||
EJS.Helpers.prototype.text_tag = EJS.Helpers.prototype.text_area_tag
|
||||
|
||||
EJS.Helpers.prototype.text_field_tag = function(name, value, html_options) { return this.input_field_tag(name, value, 'text', html_options); }
|
||||
|
||||
EJS.Helpers.prototype.url_for = function(url) {
|
||||
return 'window.location="'+url+'";'
|
||||
}
|
||||
EJS.Helpers.prototype.img_tag = function(image_location, alt, options){
|
||||
options = options || {};
|
||||
options.src = image_location
|
||||
options.alt = alt
|
||||
return this.single_tag_for('img', options)
|
||||
}
|
4
app/partials/list.ejs
Normal file
4
app/partials/list.ejs
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
<% for(var i=0; i<list.length; i++) {%>
|
||||
<div class="entry" id="<%= list[i].id %>"><%= list[i].title %></div>
|
||||
<% } %>
|
13
gulp/build.js
Normal file
13
gulp/build.js
Normal file
@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
var gulp = require('gulp');
|
||||
|
||||
|
||||
function handleError(err) {
|
||||
console.error(err.toString());
|
||||
this.emit('end');
|
||||
}
|
||||
|
||||
gulp.task('clean', function () {
|
||||
return gulp.src(['.tmp', 'dist'], { read: false }).pipe($.rimraf());
|
||||
});
|
61
gulpfile.js
Normal file
61
gulpfile.js
Normal file
@ -0,0 +1,61 @@
|
||||
"use strict";
|
||||
var gulp = require('gulp'),
|
||||
|
||||
autoprefixer = require('gulp-autoprefixer'),
|
||||
cssnano = require('gulp-cssnano'),
|
||||
jshint = require('gulp-jshint'),
|
||||
uglify = require('gulp-uglify'),
|
||||
|
||||
rename = require('gulp-rename'),
|
||||
concat = require('gulp-concat'),
|
||||
notify = require('gulp-notify'),
|
||||
cache = require('gulp-cache'),
|
||||
livereload = require('gulp-livereload'),
|
||||
htmlmin = require('gulp-htmlmin'),
|
||||
inject = require('gulp-inject'),
|
||||
del = require('del');
|
||||
|
||||
|
||||
|
||||
gulp.task('scripts', function() {
|
||||
return gulp.src('app/js/**/*.js')
|
||||
.pipe(jshint('.jshintrc'))
|
||||
.pipe(jshint.reporter('default'))
|
||||
.pipe(concat('main.js'))
|
||||
.pipe(gulp.dest('dist/js'))
|
||||
.pipe(rename({suffix: '.min'}))
|
||||
.pipe(uglify())
|
||||
.pipe(gulp.dest('dist/js'))
|
||||
.pipe(notify({ message: 'Scripts task complete' }));
|
||||
});
|
||||
|
||||
gulp.task('styles', function() {
|
||||
return gulp.src('app/css/**/*.css')
|
||||
.pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
|
||||
.pipe(gulp.dest('dist/css'))
|
||||
.pipe(rename({suffix: '.min'}))
|
||||
.pipe(cssnano())
|
||||
.pipe(gulp.dest('dist/css'))
|
||||
.pipe(notify({ message: 'Styles task complete' }));
|
||||
});
|
||||
|
||||
gulp.task('partials', function() {
|
||||
|
||||
gulp.src(['app/partials/**/*']).pipe(gulp.dest('dist/partials'));
|
||||
gulp.src(['app/libs/ejs_production.js']).pipe(gulp.dest('dist/libs'));
|
||||
});
|
||||
|
||||
gulp.task('minify-html', function () {
|
||||
|
||||
return gulp.src(['app/index.prod.html']).pipe(htmlmin({removeComments: true, collapseWhitespace: true, keepClosingSlash: true}))
|
||||
.pipe(gulp.dest('dist/index.html'));
|
||||
});
|
||||
|
||||
gulp.task('clean', function() {
|
||||
return del(['dist']);
|
||||
});
|
||||
|
||||
|
||||
gulp.task('default', ['clean'], function() {
|
||||
gulp.start('styles', 'scripts','partials','minify-html');
|
||||
});
|
32
keeper-server.js
Normal file
32
keeper-server.js
Normal file
@ -0,0 +1,32 @@
|
||||
var express = require('express'), path = require('path'), http = require('http'),
|
||||
|
||||
|
||||
favicon = require('serve-favicon'),
|
||||
logger = require('morgan'),
|
||||
cookieParser = require('cookie-parser'),
|
||||
bodyParser = require('body-parser'),
|
||||
keeper = require('./server/keeper')
|
||||
|
||||
;
|
||||
var app = express();
|
||||
|
||||
app.set('port', process.env.PORT || 8026);
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
app.set('view engine', 'ejs');
|
||||
app.use(logger('dev'));
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(cookieParser());
|
||||
app.use(express.static(path.join(__dirname, 'app')));
|
||||
|
||||
app.use('/', keeper);
|
||||
|
||||
/**
|
||||
* create the server
|
||||
*/
|
||||
|
||||
app.listen(app.get('port'), function () {
|
||||
console.log('Keeper Server listening on ' + app.get('port'));
|
||||
});/**
|
||||
* Created by Martin on 22/02/2016.
|
||||
*/
|
44
package.json
Normal file
44
package.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "recipe",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "recipe-server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.15.0",
|
||||
"cheerio": "^0.20.0",
|
||||
"cookie-parser": "^1.4.1",
|
||||
"ejs": "^2.4.1",
|
||||
"express": "^4.13.4",
|
||||
"morgan": "^1.7.0",
|
||||
"nano": "^6.2.0",
|
||||
"require-dir": "^0.3.0",
|
||||
"serve-favicon": "^2.3.0",
|
||||
"sqlite3": "^3.1.1",
|
||||
"string": "^3.3.1",
|
||||
"zlib": "^1.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"del": "^2.2.0",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-autoprefixer": "^3.1.0",
|
||||
"gulp-cache": "^0.4.2",
|
||||
"gulp-concat": "^2.6.0",
|
||||
"gulp-cssnano": "^2.1.1",
|
||||
"gulp-htmlmin": "^1.3.0",
|
||||
"gulp-inject": "^3.0.0",
|
||||
"gulp-jshint": "^2.0.0",
|
||||
"gulp-livereload": "^3.8.1",
|
||||
"gulp-notify": "^2.2.0",
|
||||
"gulp-rename": "^1.2.2",
|
||||
"gulp-uglify": "^1.5.3",
|
||||
"jshint": "^2.9.1",
|
||||
"jsonfile": "^2.2.3",
|
||||
"log4js": "^0.6.31",
|
||||
"simplecrawler": "^0.6.2"
|
||||
}
|
||||
}
|
77
recipes.json
Normal file
77
recipes.json
Normal file
@ -0,0 +1,77 @@
|
||||
{"recipes":[
|
||||
{"url":"", "title":""},
|
||||
|
||||
|
||||
{"url":"http://www.simplyrecipes.com/recipes/grilled_lime_chicken_with_black_bean_sauce/", "title":"Grilled Lime Chicken with Black Bean Sauce Recipe"},
|
||||
{"url":"http://www.marksdailyapple.com/shakshuka-eggs-poached-in-spicy-tomato-sauce/#axzz29jTSubMo", "title":"Shakshuka (Eggs Poached in Spicy Tomato Sauce) "},
|
||||
{"url":"http://www.marksdailyapple.com/spiced-pork-and-butternut-squash-with-sage/#axzz29jTSubMo", "title":"Spiced Pork and Butternut Squash with Sage"},
|
||||
{"url":"http://www.marksdailyapple.com/dairy-free-green-goddess-dressing/#axzz29jTSubMo", "title":"Dairy-Free Green Goddess Dressing"},
|
||||
{"url":"http://www.marksdailyapple.com/pork-stuffed-jalapeno-peppers/#axzz29jTSubMo", "title":"Pork-Stuffed Jalapeño Peppers"},
|
||||
{"url":"http://www.marksdailyapple.com/herb-chicken-cooked-under-a-brick/#axzz29jTSubMo", "title":"Herb Chicken Cooked Under a Brick"},
|
||||
{"url":"http://www.marksdailyapple.com/balsamic-glazed-drumsticks/#axzz29jTSubMo", "title":"Balsamic-Glazed Drumsticks"},
|
||||
{"url":"http://www.marksdailyapple.com/slow-cooked-coconut-ginger-pork/#axzz29jTSubMo", "title":"Slow-Cooked Coconut Ginger Pork"},
|
||||
{"url":"http://www.marksdailyapple.com/lime-and-basil-beef-kebabs/#axzz29jTSubMo", "title":"Lime and Basil Beef Kebabs"},
|
||||
{"url":"http://www.marksdailyapple.com/taco-bowl-with-crispy-kale-chips/#axzz29jTSubMo", "title":"Taco Bowl with Crispy Kale Chips"},
|
||||
{"url":"http://www.marksdailyapple.com/grilled-eggs-with-mexican-chorizo/#axzz29jTSubMo", "title":"Grilled Eggs with Mexican Chorizo"},
|
||||
{"url":"http://www.marksdailyapple.com/banh-mi-salad/#axzz29jTSubMo", "title":"Banh Mi Salad"},
|
||||
{"url":"http://www.marksdailyapple.com/tender-lemon-parsley-brisket/#axzz29jTSubMo", "title":"Tender Lemon-Parsley Brisket"},
|
||||
{"url":"http://www.marksdailyapple.com/butter-stuffed-chicken-kiev/#axzz29jTSubMo", "title":"Butter-Stuffed Chicken Kiev"},
|
||||
{"url":"http://www.marksdailyapple.com/primal-chicken-tikka-masala/#axzz29jTSubMo", "title":"Primal Chicken Tikka Masala"},
|
||||
{"url":"http://www.nerdfitness.com/blog/2012/02/21/how-to-cook-paleo-spaghetti/", "title":"Paleo Spaghetti"},
|
||||
{"url":"http://www.nerdfitness.com/blog/2011/02/21/a-decent-meal/", "title":"How to Grow Up And Cook a Decent Meal"},
|
||||
{"url":"http://www.marksdailyapple.com/fajita-frittata-with-avocado-salsa/#axzz29jTSubMo", "title":"Fajita Frittata with Avocado Salsa"},
|
||||
{"url":"http://everydaypaleo.com/2012/09/20/moroccan-burgers-and-beet-salad/#more-5442", "title":"Moroccan Burgers and Beet Salad"},
|
||||
{"url":"http://www.marksdailyapple.com/dark-chocolate-macadamia-bark-sprinkled-with-sea-salt/#axzz29jTSubMo", "title":"Dark Chocolate Macadamia Bark Sprinkled with Sea Salt"},
|
||||
{"url":"http://www.marksdailyapple.com/primal-texas-chili/#axzz29jTSubMo", "title":"Primal Texas Chili"},
|
||||
{"url":"http://www.marksdailyapple.com/sausage-egg-breakfast-bites/#axzz29jTSubMo", "title":"Sausage & Egg Breakfast Bites"},
|
||||
{"url":"http://www.marksdailyapple.com/primal-tex-mex-tortillas-and-taco-seasoning/#axzz29jTSubMo", "title":"Primal Tex-Mex Tortillas and Taco Seasoning"},
|
||||
{"url":"http://www.marksdailyapple.com/how-to-make-homemade-sausage/#axzz29jTSubMo", "title":"Homemade Sausage Links and Patties"},
|
||||
{"url":"http://www.marksdailyapple.com/beef-stew-and-chicken-soup-in-35-minutes-or-less/#axzz29jTSubMo", "title":"Beef Stew and Chicken Soup in 35 Minutes or Less"},
|
||||
{"url":"http://www.marksdailyapple.com/primal-breakfast-casserole/#axzz29jTSubMo", "title":"Primal Breakfast Casserole"},
|
||||
{"url":"http://www.marksdailyapple.com/vegetable-latkes/#axzz29jTSubMo", "title":"Vegetable Latkes"},
|
||||
{"url":"http://www.marksdailyapple.com/primal-holiday-desserts/#axzz29jTSubMo", "title":"Primal Holiday Desserts"},
|
||||
{"url":"http://www.marksdailyapple.com/hazelnut-crusted-chicken-with-stealth-coconut/#axzz29jTSubMo", "title":"Hazelnut Crusted Chicken with Stealth Coconut"},
|
||||
{"url":"http://paleolunch.org/", "title":"Paleo Lunch - Your Source For Paleo Diet Recipes"},
|
||||
{"url":"http://www.marksdailyapple.com/how-to-cook-the-perfect-steak/#axzz29jTSubMo", "title":"How to Cook the Perfect Steak"},
|
||||
{"url":"http://www.marksdailyapple.com/primal-blueprint-snacks/#axzz29jTSubMo", "title":"Primal Blueprint Snacks"},
|
||||
{"url":"http://www.marksdailyapple.com/top-10-ways-to-go-nuts/#axzz29jTSubMo", "title":"Creative Nut Recipes"},
|
||||
{"url":"http://www.marksdailyapple.com/primal-energy-bar-redux/#axzz29jTSubMo", "title":"Primal Energy Bar Redux"},
|
||||
{"url":"http://www.marksdailyapple.com/butter-chicken-in-a-silky-sauce/#axzz29jTSubMo", "title":"Butter Chicken in a Silky Sauce"},
|
||||
{"url":"http://www.marksdailyapple.com/spicy-chicken-and-bacon-poppers/#axzz29jTSubMo", "title":"Spicy Chicken and Bacon Poppers"},
|
||||
{"url":"http://www.marksdailyapple.com/crispy-nut-and-herb-fried-chicken-with-creamy-avocado/#axzz29jTSubMo", "title":"Crispy Nut and Herb Fried Chicken with Creamy Avocado"},
|
||||
{"url":"http://www.marksdailyapple.com/sesame-chicken-and-rice-with-fiery-ginger-and-chile-sauce/#axzz29jTSubMo", "title":"Sesame Chicken and “Rice” with Fiery Ginger and Chile Sauce"},
|
||||
{"url":"http://www.marksdailyapple.com/crock-pot-pork-stuffed-peppers/#axzz29jTSubMo", "title":"Crock Pot Pork-Stuffed Peppers"},
|
||||
{"url":"http://www.marksdailyapple.com/garlic-pulled-pork/#axzz29jTSubMo", "title":"Garlic Pulled Pork"},
|
||||
{"url":"http://www.marksdailyapple.com/cocoa-and-coconut-snacks/#axzz29jTSubMo", "title":"Cocoa and Coconut Snacks"},
|
||||
{"url":"http://www.marksdailyapple.com/5-sweet-savory-primal-shakes/#axzz29jTSubMo", "title":"5 Sweet and Savory Primal Shakes"},
|
||||
{"url":"http://www.marksdailyapple.com/almond-banana-pancakes/#axzz29jTSubMo", "title":"Almond Banana Pancakes"},
|
||||
{"url":"http://www.marksdailyapple.com/omelet-muffins/#axzz29jTSubMo", "title":"Omelet Muffins"},
|
||||
{"url":"http://www.marksdailyapple.com/frittata-aleta/#axzz29jTSubMo", "title":"Frittata Aleta"},
|
||||
{"url":"http://www.marksdailyapple.com/primal-meatballs/#axzz29jTSubMo", "title":"Italian Sausage Meatballs with Fresh Herbs"},
|
||||
{"url":"http://www.marksdailyapple.com/beef-burgundy-recipe/#axzz29jTSubMo", "title":"Beef Burgundy"},
|
||||
{"url":"http://www.marksdailyapple.com/asian-pepper-steak-crock-pot-recipe/#axzz29jTSubMo", "title":"Asian Pepper Steak Crock Pot Recipe"},
|
||||
{"url":"http://everydaypaleo.com/2012/06/29/sun-dried-tomato-meatballs-with-creamy-pesto/", "title":""},
|
||||
{"url":"http://paleodietlifestyle.com/eggs-benedict-burgers/", "title":"Eggs Benedict Burgers"},
|
||||
{"url":"http://paleodietlifestyle.com/coconut-squares/", "title":"Coconut Squares"},
|
||||
{"url":"http://www.paleotable.com/2011/01/asian-marinated-steaks.html", "title":"Asian-Marinated Steaks"},
|
||||
{"url":"http://www.paleotable.com/2011/01/tango-burgers.html", "title":"Tango Burgers"},
|
||||
{"url":"http://www.paleotable.com/2011/02/meatza.html", "title":"Meatza"},
|
||||
{"url":"http://www.paleotable.com/2011/01/baked-chicken-with-roasted-tomatoes.html", "title":"aked Chicken with Roasted Tomatoes"},
|
||||
{"url":"http://www.paleotable.com/2011/02/blackened-chicken.html", "title":"blackened-chicken"},
|
||||
{"url":"http://www.paleotable.com/2011/01/breaded-baked-chicken.html", "title":""},
|
||||
{"url":"http://www.paleotable.com/2011/03/chicken-and-avocado-tostadas.html", "title":""},
|
||||
{"url":"http://www.paleotable.com/2011/01/sausages-and-pepperonata.html", "title":""},
|
||||
{"url":"http://www.health-bent.com/soups/paleo-mediterranean-beef-stew", "title":""},
|
||||
{"url":"", "title":""},
|
||||
{"url":"", "title":""},
|
||||
{"url":"", "title":""},
|
||||
{"url":"", "title":""},
|
||||
{"url":"", "title":""},
|
||||
{"url":"", "title":""},
|
||||
{"url":"", "title":""},
|
||||
{"url":"", "title":""},
|
||||
{"url":"", "title":""},
|
||||
{"url":"", "title":""},
|
||||
{"url":"", "title":""},
|
||||
{"url":"", "title":""},
|
||||
]}
|
BIN
server/body.html
Normal file
BIN
server/body.html
Normal file
Binary file not shown.
241
server/keeper.js
Normal file
241
server/keeper.js
Normal file
@ -0,0 +1,241 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Created by Martin on 22/02/2016.
|
||||
*/
|
||||
var express = require('express');
|
||||
var http = require('http'), request = require('request'), cheerio = require('cheerio'), util = require('util');
|
||||
var jsonfile = require('jsonfile'), fs = require('fs'), STRING = require('string');
|
||||
var zlib = require("zlib");
|
||||
var log4js = require('log4js');
|
||||
var logger = log4js.getLogger();
|
||||
|
||||
var router = express.Router();
|
||||
|
||||
var EventEmitter = require('events');
|
||||
|
||||
var nano = require('nano')('http://localhost:5984');
|
||||
var busEmitter = new EventEmitter();
|
||||
|
||||
var db_name = 'keeper';
|
||||
var dbCouch = nano.use(db_name);
|
||||
|
||||
|
||||
var bodyfile = __dirname + '/' + 'body.html';
|
||||
var htmlfile = __dirname + '/' + 'testoutput.html';
|
||||
var generics = ['ARTICLE', 'div.content_column', 'div.post'];
|
||||
|
||||
|
||||
function cleaner(b) {
|
||||
var _b = b;
|
||||
|
||||
var unwanted = ['div#disqus_thread', 'SCRIPT', 'FOOTER', 'div.ssba', '.shareaholic-canvas', '.yarpp-related', 'div.dfad', 'div.postFooterShare', 'div#nextPrevLinks', '.post-comments'];
|
||||
|
||||
for (var i = 0; i < unwanted.length; i++) {
|
||||
_b.find(unwanted[i]).remove();
|
||||
}
|
||||
return _b;
|
||||
}
|
||||
|
||||
function insertBookmark(obj) {
|
||||
logger.debug('Inserting into couch...');
|
||||
logger.info(util.inspect(obj));
|
||||
dbCouch.insert(obj, function(err, body,header) {
|
||||
if (err) {
|
||||
logger.error('Error inserting into couch');
|
||||
return;
|
||||
}
|
||||
});
|
||||
logger.debug('Insert done..');
|
||||
}
|
||||
|
||||
var doInsertBookmark = (obj) =>{
|
||||
// logger.info('sendSocket: ' + JSON.stringify(obj));
|
||||
insertBookmark(obj);
|
||||
};
|
||||
|
||||
var doGetBookmark = (url) =>{
|
||||
// logger.info('sendSocket: ' + JSON.stringify(obj));
|
||||
genericGrab(url);
|
||||
};
|
||||
|
||||
var doGetBookmarkRes = (url,res) =>{
|
||||
logger.debug('doGetBookmarkRes');
|
||||
// logger.info('sendSocket: ' + JSON.stringify(obj));
|
||||
genericGrab(url,res);
|
||||
};
|
||||
|
||||
// Events
|
||||
busEmitter.on('saveBookmarkData', doInsertBookmark);
|
||||
busEmitter.on('getBookmark', doGetBookmark);
|
||||
busEmitter.on('getBookmarkRes', doGetBookmarkRes);
|
||||
|
||||
function processBody(body,url) {
|
||||
|
||||
var $ = cheerio.load(body);
|
||||
var title = $('TITLE').text();
|
||||
|
||||
// try to find a body to grab
|
||||
|
||||
var i = 0;
|
||||
|
||||
while (($(generics[i]).length == 0) && (i < generics.length)) {
|
||||
// logger.info(generics[i]);
|
||||
// logger.info($(generics[i]));
|
||||
// logger.info('i: ' + i + ', ' + $(generics[i]).length);
|
||||
i++;
|
||||
}
|
||||
logger.debug(i);
|
||||
|
||||
if (i < generics.length) {
|
||||
var tdihbody = $(generics[i]);
|
||||
var obj = {};
|
||||
|
||||
logger.debug(tdihbody.length);
|
||||
tdihbody = cleaner(tdihbody);
|
||||
logger.debug(title);
|
||||
|
||||
obj.url=url;
|
||||
obj.html = $.html();
|
||||
obj.reduced = STRING(tdihbody.html()).collapseWhitespace().s;
|
||||
obj.title = STRING(title).collapseWhitespace().s;
|
||||
|
||||
busEmitter.emit("saveBookmarkData", obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
function genericGrab(url,res) {
|
||||
logger.info(url);
|
||||
request(url, function (err, resp, body) {
|
||||
if (err)
|
||||
throw err;
|
||||
|
||||
console.log("headers: ", resp.headers);
|
||||
console.log(resp.statusCode);
|
||||
logger.info('A');
|
||||
logger.info(body);
|
||||
if (resp.headers.hasOwnProperty('content-encoding')) {
|
||||
logger.warn('content-encoding');
|
||||
if (resp.headers['content-encoding'] == 'gzip') {
|
||||
|
||||
// to test http://chaosinthekitchen.com/2009/07/lime-and-coconut-chicken/
|
||||
|
||||
|
||||
var gunzip = zlib.createGunzip();
|
||||
var jsonString = '';
|
||||
resp.pipe(gunzip);
|
||||
gunzip.on('data', function (chunk) {
|
||||
jsonString += chunk;
|
||||
});
|
||||
gunzip.on('end', function () {
|
||||
console.log((jsonString));
|
||||
callback(JSON.stringify(jsonString));
|
||||
});
|
||||
gunzip.on('error', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.info('Processing other body...');
|
||||
var b = processBody(body,url);
|
||||
console.log(b);
|
||||
if (res != null)
|
||||
{
|
||||
res.render('grabbed');
|
||||
}
|
||||
}
|
||||
|
||||
} else
|
||||
{
|
||||
logger.info('Processing body...');
|
||||
var b = processBody(body,url);
|
||||
if (res != null)
|
||||
{
|
||||
console.log({data:b});
|
||||
res.render('grabbed',{data:b});
|
||||
}
|
||||
}
|
||||
|
||||
logger.info('END');
|
||||
|
||||
//fs.writeFileSync(htmlfile, tdihbody.html());
|
||||
// fs.writeFileSync(bodyfile, $.html());
|
||||
});
|
||||
}
|
||||
|
||||
router.get('/list', function (req, res) {
|
||||
logger.debug('list..');
|
||||
|
||||
dbCouch.view('titles','titles',function(err, body) {
|
||||
if (!err) {
|
||||
|
||||
var outJSON = [];
|
||||
body.rows.forEach(function(doc) {
|
||||
logger.info(doc);
|
||||
outJSON.push({id:doc.id, title:doc.value })
|
||||
});
|
||||
|
||||
//logger.debug(util.inspect(body));
|
||||
res.writeHead(200, {"ContentType": "application/json"});
|
||||
res.end(JSON.stringify({list: outJSON}));
|
||||
|
||||
} else
|
||||
{
|
||||
res.writeHead(500, {"ContentType": "application/json"});
|
||||
res.end(JSON.stringify({}));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/entry/:id', function (req, res) {
|
||||
logger.debug('entry..');
|
||||
|
||||
logger.debug(req.params.id);
|
||||
|
||||
dbCouch.get(req.params.id,function(err, body) {
|
||||
if (!err) {
|
||||
|
||||
var outJSON = {};
|
||||
logger.debug(body);
|
||||
outJSON.title = body.title;
|
||||
outJSON.reduced = body.reduced;
|
||||
//logger.debug(util.inspect(body));
|
||||
res.writeHead(200, {"ContentType": "application/json"});
|
||||
res.end(JSON.stringify(outJSON));
|
||||
|
||||
} else
|
||||
{
|
||||
res.writeHead(500, {"ContentType": "application/json"});
|
||||
res.end(JSON.stringify({}));
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
router.post('/add', function (req, res) {
|
||||
logger.debug('add entry..');
|
||||
|
||||
var t = req.body;
|
||||
if (t.hasOwnProperty('url')) {
|
||||
var url = JSON.parse(t.url.toString());
|
||||
logger.debug(url);
|
||||
busEmitter.emit("getBookmark", url);
|
||||
}
|
||||
else {
|
||||
logger.error('No data block!');
|
||||
}
|
||||
res.writeHead(200, {"ContentType": "application/json"});
|
||||
res.end(JSON.stringify({adding: url}));
|
||||
|
||||
});
|
||||
|
||||
router.get('/new', function (req, res) {
|
||||
logger.debug('Save new');
|
||||
busEmitter.emit("getBookmarkRes", req.query.url ,res);
|
||||
|
||||
});
|
||||
|
||||
module.exports = router;
|
42
server/maker.js
Normal file
42
server/maker.js
Normal file
@ -0,0 +1,42 @@
|
||||
var nano = require('nano')('http://localhost:5984');
|
||||
|
||||
// clean up the database we created previously
|
||||
nano.db.destroy('keeper', function() {
|
||||
// create a new database
|
||||
nano.db.create('keeper', function() {
|
||||
// specify the database we are going to use
|
||||
var keeper = nano.use('keeper');
|
||||
// and insert a document in it
|
||||
/* keeper.insert({ crazy: true }, 'rabbit', function(err, body, header) {
|
||||
if (err) {
|
||||
console.log('[alice.insert] ', err.message);
|
||||
return;
|
||||
}
|
||||
console.log('you have inserted the rabbit.')
|
||||
console.log(body);
|
||||
});*/
|
||||
|
||||
|
||||
keeper.insert(
|
||||
{ "views":
|
||||
{ "titles":
|
||||
{ "map": function(doc) { emit(null, doc.title); } }
|
||||
}
|
||||
}, '_design/titles', function (error, response) {
|
||||
console.log("_design/titles added");
|
||||
});
|
||||
|
||||
keeper.insert(
|
||||
{ "views":
|
||||
{ "reducedView":
|
||||
{ "map": function(doc) { emit(null, [doc.title, doc.reduced]); } }
|
||||
}
|
||||
}, '_design/reducedView', function (error, response) {
|
||||
console.log("_design/reducedView added");
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});/**
|
||||
* Created by Martin on 02/03/2016.
|
||||
*/
|
52
server/simplecrawler.js
Normal file
52
server/simplecrawler.js
Normal file
@ -0,0 +1,52 @@
|
||||
var Crawler = require("simplecrawler"),fs = require('fs');
|
||||
|
||||
|
||||
//var myCrawler = new Crawler("http://www.bbc.co.uk/food/recipes/chicken_piperade_with_23608");
|
||||
|
||||
var myCrawler = new Crawler("www.bbc.co.uk", "/food/recipes/chicken_piperade_with_23608", 80);
|
||||
|
||||
var htmlfile = __dirname + '/' + 'test.html';
|
||||
|
||||
myCrawler.maxDepth = 1;
|
||||
//myCrawler.interval = 10000; // Ten seconds
|
||||
myCrawler.maxConcurrency = 1;
|
||||
|
||||
|
||||
myCrawler.on('crawlstart', function() {
|
||||
console.log('Crawling started...');
|
||||
});
|
||||
|
||||
myCrawler.on('fetchstart ', function(a, b) {
|
||||
console.log('fetchstart ...');
|
||||
console.log(a);
|
||||
console.log(b);
|
||||
});
|
||||
|
||||
myCrawler.on('fetcherror ', function(a, b) {
|
||||
console.log('Crawling error...');
|
||||
console.log(a);
|
||||
console.log(b);
|
||||
});
|
||||
|
||||
myCrawler.on('fetchclienterror ', function(a, b) {
|
||||
console.log('fetchclienterror error...');
|
||||
console.log(a);
|
||||
console.log(b);
|
||||
});
|
||||
|
||||
myCrawler.on('queueadd ', function(a) {
|
||||
console.log('fetchclienterror error...');
|
||||
console.log(a);
|
||||
|
||||
});
|
||||
|
||||
myCrawler.on("fetchcomplete", function(queueItem, responseBuffer, response) {
|
||||
console.log("I just received %s (%d bytes)", queueItem.url, responseBuffer.length);
|
||||
console.log("It was a resource of type %s", response.headers['content-type']);
|
||||
|
||||
// Do something with the data in responseBuffer
|
||||
|
||||
fs.writeFileSync(htmlfile, responseBuffer);
|
||||
});
|
||||
|
||||
myCrawler.start();
|
63
server/testoutput.html
Normal file
63
server/testoutput.html
Normal file
@ -0,0 +1,63 @@
|
||||
|
||||
<!-- RDFa Breadcrumbs Plugin by Nitin Yawalkar --><div class="breadcrumb breadcrumbs"><div class="rdfa-breadcrumb"><div xmlns:v="http://rdf.data-vocabulary.org/#"><p><span class="breadcrumbs-title"> </span><span typeof="v:Breadcrumb"><a rel="v:url" property="v:title" href="http://www.marksdailyapple.com/" class="home">Home</a></span> <span class="separator">»</span> <span typeof="v:Breadcrumb"><a rel="v:url" property="v:title" href="http://www.marksdailyapple.com/category/recipes/" title="Recipes">Recipes</a></span> <span class="separator">»</span> Herb Chicken Cooked Under a Brick</p></div></div></div><!-- RDFa Breadcrumbs Plugin by Nitin Yawalkar --> <div class="postLiner">
|
||||
<div class="postFlag">
|
||||
<div>
|
||||
18 Aug </div>
|
||||
</div>
|
||||
<div class="postContent postContentInside">
|
||||
<h1><a href="http://www.marksdailyapple.com/herb-chicken-cooked-under-a-brick/" rel="bookmark" title="Permanent Link to Herb Chicken Cooked Under a Brick">
|
||||
Herb Chicken Cooked Under a Brick </a></h1>
|
||||
<div class="wwsgd" style="display:none;"><div style="border: thin dotted black; padding: 10px 10px 0 10px; margin-bottom: 10px;"><p>Welcome! If you want to lose weight, gain muscle, increase energy levels or just generally look and feel healthier you've come to the right place.</p>
|
||||
<p>Here's where to start:</p>
|
||||
<ol>
|
||||
<li>Visit the <a title="Start Here" href="http://www.marksdailyapple.com/welcome-to-marks-daily-apple/?utm_source=mda_wwsgd&utm_medium=link&utm_campaign=mda_wwsgd_start_here" target="_blank">Start Here</a> and <a title="Primal Blueprint 101" href="http://www.marksdailyapple.com/primal-blueprint-101/?utm_source=mda_wwsgd&utm_medium=link&utm_campaign=mda_wwsgd_pb_101" target="_blank">Primal Blueprint 101</a> pages to learn more about the Primal lifestyle.</li>
|
||||
<li>Subscribe to my <a title="Subscribe" href="http://www.marksdailyapple.com/subscribe-to-blog/?utm_source=mda_wwsgd&utm_medium=link&utm_campaign=mda_wwsgd_newsletter" target="_blank">weekly newsletter</a> to receive an eBook called <em>Primal Blueprint Fitness</em> and more - all for free.</li>
|
||||
<li>Cut to the chase by visiting <a title="PrimalBlueprint.com" href="https://www.primalblueprint.com/?utm_source=mda_wwsgd&utm_medium=link&utm_campaign=mda_wwsgd_pb_homepage" target="_blank">PrimalBlueprint.com</a>. There you'll find <a title="Books and Media" href="https://www.primalblueprint.com/books/?utm_source=mda_wwsgd&utm_medium=link&utm_campaign=mda_wwsgd_books" target="_blank">books</a>, <a title="Primal Kitchen" href="https://www.primalblueprint.com/primal-kitchen/mayo-12-oz/?utm_source=mda_wwsgd&utm_medium=link&utm_campaign=mda_wwsgd_food" target="_blank">food</a>, and the best <a title="Supplements" href="https://www.primalblueprint.com/supplements/?utm_source=mda_wwsgd&utm_medium=link&utm_campaign=mda_wwsgd_supplements" target="_blank">supplements</a> on the planet to help you take control of your health for life.</li>
|
||||
</ol>
|
||||
<p>Thanks for visiting!</p></div></div><p><img class="alignright" title="Brick Chicken" src="http://cdn.marksdailyapple.com/wordpress/wp-content/themes/Marks-Daily-Apple-Responsive/images/blog2/BrickChicken2.jpg" alt="" width="320" height="212">Who would’ve guessed that the secret to the juiciest, most tender <a title="Choosing Chicken: A Primal Purchasing Guide " href="http://www.marksdailyapple.com/chicken-labels/#axzz2302s02YL">chicken</a> breast you’ve ever tasted was a brick? Not a fancy culinary instrument that happens to be called a brick, but an actual brick, the type used to build houses and fireplaces and to landscape yards. A brick set on top of a cooking chicken applies just enough pressure to push the bird against the hot pan, crisping up the skin and cooking all the meat evenly and quickly before it dries out. The bird comes out juicy and tender on the inside, crispy and golden on the outside.</p>
|
||||
<p><span id="more-30764"></span></p>
|
||||
<p>As long as you have a few bricks laying around, the technique couldn’t be easier. First, remove the backbone from the chicken so the bird can be splayed out flat. With a pair of kitchen shears, this is quick work. Next, rub the chicken down with something tasty. In this case, a smoky, herbal rub made from thyme, oregano, garlic and smoked paprika add tons of flavor. You can go this route, or use any of your own favorite rubs or <a title="How to Marinate Meat" href="http://www.marksdailyapple.com/how-to-marinate-meat/#axzz2302kCvBn">marinades</a>.</p>
|
||||
<p>Now, it’s time for the bricks to work their magic. Heat an ovenproof skillet on the stove and set the chicken in it, skin side down. Put the bricks on top and leave it alone for 6-8 minutes. Transfer the skillet to a hot oven and leave the chicken alone again, with bricks on top, for 20 minutes or so. Flip the bird, let it cook a little longer, and you’re minutes away from tasting a culinary miracle. The chicken breasts are not only moist, they’re down right succulent. The rest of the bird is amazing too. You might as well make room in your kitchen cupboard now to permanently store two bricks. After trying this recipe, you’ll never want to roast chicken any other way.</p>
|
||||
<p><strong>Ingredients:</strong></p>
|
||||
<img class="alignnone" title="Ingredients" src="http://cdn.marksdailyapple.com/wordpress/wp-content/themes/Marks-Daily-Apple-Responsive/images/blog2/ingredients-29.jpg" alt="" width="540" height="360">
|
||||
<ul>
|
||||
<li>1 whole chicken, 3-4 pounds (approx 1.5 kg)</li>
|
||||
<li>2 tablespoons oil (30 ml)</li>
|
||||
<li>1 tablespoon fresh thyme (15 ml)</li>
|
||||
<li>1 teaspoon dried oregano (5 ml)</li>
|
||||
<li>1/4 teaspoon garlic powder (approx 1 ml)</li>
|
||||
<li>1 teaspoon smoked paprika (5 ml)</li>
|
||||
<li>1/2 teaspoon salt (2.5 ml)</li>
|
||||
<li>1/4 teaspoon pepper (approx 1 ml)</li>
|
||||
</ul>
|
||||
<p><strong>Tools:</strong></p>
|
||||
<ul>
|
||||
<li>Ovenproof skillet</li>
|
||||
<li>1-2 bricks, wrapped in foil</li>
|
||||
</ul>
|
||||
<p><strong>Instructions:</strong></p>
|
||||
<p>In a bowl, mix together 1 tablespoon (15 ml) of the oil with the thyme, oregano, garlic powder, paprika, salt and pepper. Set aside.</p>
|
||||
<p>Set the chicken on a cutting board breast side down.</p>
|
||||
<p>Starting at the tail, use a knife or better yet, kitchen shears, to cut all the way down the back, keeping as close to the backbone as you can. Then, cut down the other side of the backbone, splitting the chicken open. Remove the backbone.</p>
|
||||
<img class="alignnone" title="Step 1" src="http://cdn.marksdailyapple.com/wordpress/wp-content/themes/Marks-Daily-Apple-Responsive/images/blog2/step1-2.jpg" alt="" width="540" height="441">
|
||||
<img class="alignnone" title="Step 2" src="http://cdn.marksdailyapple.com/wordpress/wp-content/themes/Marks-Daily-Apple-Responsive/images/blog2/step2-2.jpg" alt="" width="540" height="360">
|
||||
<p>Spread the chicken open, lightly pressing down to flatten it. Rub the spice mixture all over the chicken, getting some under the skin and directly onto the meat.</p>
|
||||
<img class="alignnone" title="Step 3" src="http://cdn.marksdailyapple.com/wordpress/wp-content/themes/Marks-Daily-Apple-Responsive/images/blog2/step3-2.jpg" alt="" width="540" height="360">
|
||||
<p>Preheat oven to 400 °F (204 °C)</p>
|
||||
<p>Heat the remaining tablespoon of oil in a large ovenproof skillet over medium-high heat. When the skillet is really hot, add the chicken skin side down and place the bricks on top to push the bird down against the skillet. You can get away with using one brick if the chicken is small, but larger birds usually need two bricks.</p>
|
||||
<img class="alignnone" title="Step 4" src="http://cdn.marksdailyapple.com/wordpress/wp-content/themes/Marks-Daily-Apple-Responsive/images/blog2/step4-2.jpg" alt="" width="540" height="360">
|
||||
<p>Cook until the skin is golden brown, 6-8 minutes (it’s okay to take the bricks off and peek).</p>
|
||||
<img class="alignnone" title="Cooked Chicken" src="http://cdn.marksdailyapple.com/wordpress/wp-content/themes/Marks-Daily-Apple-Responsive/images/blog2/cooked_chicken.jpg" alt="" width="540" height="360">
|
||||
<p>Put the skillet in the oven and roast the chicken with the bricks on top for 25 minutes. Take off the bricks and turn the chicken over. Put the bricks back on and roast another 10 or so minutes until the chicken is done. The juices should run clear when you pierce the bird with a fork; you can also stab it with a thermometer and make sure it reads at least 165 °F (74 °C).</p>
|
||||
<p><img class="alignnone" title="Brick Chicken" src="http://cdn.marksdailyapple.com/wordpress/wp-content/themes/Marks-Daily-Apple-Responsive/images/blog2/BrickChicken2.jpg" alt="" width="540" height="360"><br>
|
||||
<div></div></p>
|
||||
|
||||
<div class="clear"></div>
|
||||
<div class="postFooter">
|
||||
<p class="postAuthor smallCaps">Posted By:
|
||||
Worker Bee </p></div>
|
||||
<div class="clear"></div>
|
||||
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
20
views/grabbed.ejs
Normal file
20
views/grabbed.ejs
Normal file
@ -0,0 +1,20 @@
|
||||
<html>
|
||||
<head>
|
||||
<link href="//cdn.muicss.com/mui-0.4.6/css/mui.min.css" rel="stylesheet" type="text/css" />
|
||||
<script src="//cdn.muicss.com/mui-0.4.6/js/mui.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="mui-container">
|
||||
<div class="mui-panel">
|
||||
<div class="mui-text-headline mui-text-accent">Grabbed</div>
|
||||
</div>
|
||||
<div id="container" class="mui-panel">
|
||||
<%= data.title %>
|
||||
</div>
|
||||
<div id="container" class="mui-panel">
|
||||
<%- data.reduced %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user