This commit is contained in:
martind2000 2016-03-08 21:10:12 +00:00
commit cf113a90e6
56 changed files with 15776 additions and 0 deletions

79
.gitignore vendored Normal file
View 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
View 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
}
}

90
app/bindex.html Normal file
View File

@ -0,0 +1,90 @@
<!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" />
<link href="css/read.css" rel="stylesheet" type="text/css" />
<link href="css/gist.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>
<link rel="apple-touch-icon" sizes="57x57" href="fav/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="fav/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="fav/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="fav/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="fav/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="fav/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="fav/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="fav/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="fav/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="fav/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="fav/favicon-194x194.png" sizes="194x194">
<link rel="icon" type="image/png" href="fav/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="fav/android-chrome-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="fav/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="fav/manifest.json">
<link rel="mask-icon" href="fav/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#ffc40d">
<meta name="msapplication-TileImage" content="fav/mstile-144x144.png">
<meta name="theme-color" content="#ffc40d">
<title>Keeper Silvtree</title>
</head>
<body>
<div id="sidedrawer" class="mui--no-user-select">
<div id="sidedrawer-brand" class="mui--appbar-line-height mui--text-title">Keeper</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">Keeper</span>
</div>
</div>
</header>
<div id="content-wrapper">
<div class="mui--appbar-height"></div>
<!--<div class="mui-container-fluid" id="bodyContents" class="mui-panel">-->
<!-- content here -->
<div class="mui-container" id="bodyContents" class="mui-panel">
</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>

174
app/css/app.css Normal file
View 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"
}

123
app/css/gist.css Normal file
View File

@ -0,0 +1,123 @@
.gist {
color: #000;
}
.gist div {
padding: 0;
margin: 0;
}
.gist .gist-file {
border: 1px solid #dedede; /* gray */
font-family: Monaco, "Courier New", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", monospace;
margin-bottom: 1em;
}
.gist .gist-file .gist-meta {
overflow: hidden;
font-size: 85%;
padding: .5em;
color: #666;
background-color: #eaeaea;
}
.gist .gist-file .gist-meta a {
color: #369;
}
.gist .gist-file .gist-meta a:visited {
color: #737;
}
.gist .gist-file .gist-data {
overflow: auto;
word-wrap: normal;
background-color: #f8f8ff;
border-bottom: 1px solid #ddd;
font-size: 100%;
}
.gist .gist-file .gist-data pre {
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
background: transparent !important;
margin: 0 !important;
border: none !important;
padding: .25em .5em .5em .5em !important;
}
.gist .gist-file .gist-data .gist-highlight {
background: transparent !important;
}
.gist .gist-file .gist-data .gist-line-numbers {
background-color: #ececec;
color: #aaa;
border-right: 1px solid #ddd;
text-align: right;
}
.gist .gist-file .gist-data .gist-line-numbers span {
clear: right;
display: block;
}
.gist-syntax { background: #ffffff; }
.gist-syntax .c { color: #999988; font-style: italic } /* Comment */
.gist-syntax .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.gist-syntax .k { color: #000000; font-weight: bold } /* Keyword */
.gist-syntax .o { color: #000000; font-weight: bold } /* Operator */
.gist-syntax .cm { color: #999988; font-style: italic } /* Comment.Multiline */
.gist-syntax .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
.gist-syntax .c1 { color: #999988; font-style: italic } /* Comment.Single */
.gist-syntax .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
.gist-syntax .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.gist-syntax .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */
.gist-syntax .ge { color: #000000; font-style: italic } /* Generic.Emph */
.gist-syntax .gr { color: #aa0000 } /* Generic.Error */
.gist-syntax .gh { color: #999999 } /* Generic.Heading */
.gist-syntax .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.gist-syntax .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */
.gist-syntax .go { color: #888888 } /* Generic.Output */
.gist-syntax .gp { color: #555555 } /* Generic.Prompt */
.gist-syntax .gs { font-weight: bold } /* Generic.Strong */
.gist-syntax .gu { color: #aaaaaa } /* Generic.Subheading */
.gist-syntax .gt { color: #aa0000 } /* Generic.Traceback */
.gist-syntax .kc { color: #000000; font-weight: bold } /* Keyword.Constant */
.gist-syntax .kd { color: #000000; font-weight: bold } /* Keyword.Declaration */
.gist-syntax .kp { color: #000000; font-weight: bold } /* Keyword.Pseudo */
.gist-syntax .kr { color: #000000; font-weight: bold } /* Keyword.Reserved */
.gist-syntax .kt { color: #445588; font-weight: bold } /* Keyword.Type */
.gist-syntax .m { color: #009999 } /* Literal.Number */
.gist-syntax .s { color: #d14 } /* Literal.String */
.gist-syntax .na { color: #008080 } /* Name.Attribute */
.gist-syntax .nb { color: #0086B3 } /* Name.Builtin */
.gist-syntax .nc { color: #445588; font-weight: bold } /* Name.Class */
.gist-syntax .no { color: #008080 } /* Name.Constant */
.gist-syntax .ni { color: #800080 } /* Name.Entity */
.gist-syntax .ne { color: #990000; font-weight: bold } /* Name.Exception */
.gist-syntax .nf { color: #990000; font-weight: bold } /* Name.Function */
.gist-syntax .nn { color: #555555 } /* Name.Namespace */
.gist-syntax .nt { color: #000080 } /* Name.Tag */
.gist-syntax .nv { color: #008080 } /* Name.Variable */
.gist-syntax .ow { color: #000000; font-weight: bold } /* Operator.Word */
.gist-syntax .w { color: #bbbbbb } /* Text.Whitespace */
.gist-syntax .mf { color: #009999 } /* Literal.Number.Float */
.gist-syntax .mh { color: #009999 } /* Literal.Number.Hex */
.gist-syntax .mi { color: #009999 } /* Literal.Number.Integer */
.gist-syntax .mo { color: #009999 } /* Literal.Number.Oct */
.gist-syntax .sb { color: #d14 } /* Literal.String.Backtick */
.gist-syntax .sc { color: #d14 } /* Literal.String.Char */
.gist-syntax .sd { color: #d14 } /* Literal.String.Doc */
.gist-syntax .s2 { color: #d14 } /* Literal.String.Double */
.gist-syntax .se { color: #d14 } /* Literal.String.Escape */
.gist-syntax .sh { color: #d14 } /* Literal.String.Heredoc */
.gist-syntax .si { color: #d14 } /* Literal.String.Interpol */
.gist-syntax .sx { color: #d14 } /* Literal.String.Other */
.gist-syntax .sr { color: #009926 } /* Literal.String.Regex */
.gist-syntax .s1 { color: #d14 } /* Literal.String.Single */
.gist-syntax .ss { color: #990073 } /* Literal.String.Symbol */
.gist-syntax .bp { color: #999999 } /* Name.Builtin.Pseudo */
.gist-syntax .vc { color: #008080 } /* Name.Variable.Class */
.gist-syntax .vg { color: #008080 } /* Name.Variable.Global */
.gist-syntax .vi { color: #008080 } /* Name.Variable.Instance */
.gist-syntax .il { color: #009999 } /* Literal.Number.Integer.Long */

53
app/css/md.css Normal file
View 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 {
}

13673
app/css/read.css Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

12
app/fav/browserconfig.xml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="/mstile-70x70.png"/>
<square150x150logo src="/mstile-150x150.png"/>
<square310x310logo src="/mstile-310x310.png"/>
<wide310x150logo src="/mstile-310x150.png"/>
<TileColor>#ffc40d</TileColor>
</tile>
</msapplication>
</browserconfig>

BIN
app/fav/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

BIN
app/fav/favicon-194x194.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
app/fav/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

BIN
app/fav/favicon-96x96.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 B

BIN
app/fav/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

41
app/fav/manifest.json Normal file
View File

@ -0,0 +1,41 @@
{
"name": "Keeper",
"icons": [
{
"src": "\/android-chrome-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": 0.75
},
{
"src": "\/android-chrome-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": 1
},
{
"src": "\/android-chrome-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": 1.5
},
{
"src": "\/android-chrome-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": 2
},
{
"src": "\/android-chrome-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": 3
},
{
"src": "\/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": 4
}
]
}

BIN
app/fav/mstile-144x144.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
app/fav/mstile-150x150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
app/fav/mstile-310x150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
app/fav/mstile-310x310.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
app/fav/mstile-70x70.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 B

View File

@ -0,0 +1,28 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M3897 4499 c-112 -17 -250 -74 -335 -139 -38 -29 -2117 -2106 -2159
-2157 -74 -89 -111 -197 -111 -322 0 -181 82 -329 236 -428 176 -113 417 -103
578 25 38 30 1467 1452 1587 1580 40 42 50 92 26 131 -30 49 -83 65 -132 39
-12 -6 -378 -367 -814 -802 -592 -592 -804 -798 -840 -816 -68 -34 -169 -39
-243 -11 -175 66 -249 289 -148 446 15 22 507 520 1095 1106 1147 1145 1094
1096 1233 1133 75 20 218 14 291 -12 292 -104 423 -428 288 -712 -29 -60 -129
-163 -1348 -1383 -1407 -1409 -1373 -1378 -1529 -1434 -90 -33 -128 -38 -262
-37 -93 1 -130 6 -192 26 -241 79 -417 264 -479 505 -20 80 -28 216 -15 288
10 63 38 155 47 161 5 3 9 12 9 20 0 9 21 48 46 87 36 57 267 293 1082 1109
569 570 1040 1044 1044 1053 29 50 10 113 -42 140 -31 16 -64 15 -97 -4 -10
-5 -489 -481 -1064 -1058 -679 -681 -1059 -1068 -1083 -1106 -39 -60 -106
-197 -106 -216 0 -6 -4 -19 -9 -29 -34 -66 -50 -280 -31 -399 107 -663 827
-997 1397 -648 68 42 253 222 1396 1364 725 724 1338 1343 1362 1376 48 67 83
140 111 230 29 93 26 297 -5 390 -45 134 -105 230 -199 317 -155 145 -382 217
-585 187z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

66
app/index.prod.html Normal file
View 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
View 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
View 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
View 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 '&lt;' 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)
}
})();

File diff suppressed because one or more lines are too long

200
app/libs/view.js Normal file
View 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
View File

@ -0,0 +1,4 @@
<% for(var i=0; i<list.length; i++) {%>
<div class="entry" id="<%= list[i].id %>"><%= list[i].title %></div>
<% } %>

33
ecosystem.json Normal file
View File

@ -0,0 +1,33 @@
{
/**
* Application configuration section
* http://pm2.keymetrics.io/docs/usage/application-declaration/
*/
apps: [
// First application
{
"name": "Keeper",
"script": "keeper-server.js",
"cwd": "/var/www/keeper",
"watch": true,
"max_restarts": 64,
"merge_logs" : true,
"autorestart" : true,
"restart_delay" : 3500,
"max_memory_restart" : "300M",
env: {
COMMON_VARIABLE: "true"
},
env_production: {
NODE_ENV: "production"
}
}
],
/**
* Deployment section
* http://pm2.keymetrics.io/docs/usage/deployment/
*/
deploy: {
}
}

13
gulp/build.js Normal file
View 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
View 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');
});

33
logger-server.js Normal file
View File

@ -0,0 +1,33 @@
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'),
loggerHandler = require('./server/loggerhandler')
;
var app = express();
app.set('port', process.env.PORT || 8027);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.text());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'app')));
app.use('/', loggerHandler);
/**
* 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.
*/

43
package.json Normal file
View File

@ -0,0 +1,43 @@
{
"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",
"markdown": "^0.5.0",
"morgan": "^1.7.0",
"nano": "^6.2.0",
"require-dir": "^0.3.0",
"serve-favicon": "^2.3.0",
"string": "^3.3.1"
},
"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"
}
}

BIN
server/body.html Normal file

Binary file not shown.

117
server/loggerhandler.js Normal file
View File

@ -0,0 +1,117 @@
"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 markdown = require( "markdown" ).markdown;
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 = 'logger';
var dbCouch = nano.use(db_name);
function insertLog(obj) {
logger.debug('Inserting into couch...');
logger.info(util.inspect(obj));
var newObj = {body:obj};
dbCouch.insert(newObj, function(err, body,header) {
if (err) {
logger.error('Error inserting into couch');
return;
}
});
logger.debug('Insert done..');
}
var doInsertLog = (obj) =>{
// logger.info('sendSocket: ' + JSON.stringify(obj));
insertLog(obj);
};
// Events
busEmitter.on('saveLog', doInsertLog);
router.get('/', function (req, res) {
logger.debug('list..');
dbCouch.view('body','body',function(err, body) {
if (!err) {
var outJSON = [];
console.log(body);
body.rows.forEach(function(doc) {
outJSON.push({id:doc.id, body:markdown.toHTML( doc.value ) })
});
logger.debug({data:{list:outJSON}});
res.render('index', {data:{list:outJSON}});
/* res.writeHead(500, {"ContentType": "application/json"});
res.end(JSON.stringify({data:{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 = {};
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('/log', function (req, res) {
logger.debug('add entry..');
var t = STRING(req.body).replaceAll('%0A', '\n').s
logger.debug(t);
busEmitter.emit("saveLog", t);
res.writeHead(200, {"ContentType": "application/json"});
res.end(JSON.stringify({adding: t.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
View File

@ -0,0 +1,42 @@
var nano = require('nano')('http://localhost:5984');
// clean up the database we created previously
nano.db.destroy('logger', function() {
// create a new database
nano.db.create('logger', function() {
// specify the database we are going to use
var keeper = nano.use('logger');
// and insert a document in it
keeper.insert(
{ "views":
{ "body":
{ "map": function(doc) { emit(null, doc.body); } }
}
}, '_design/body', function (error, response) {
console.log("_design/body added");
});
/* 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
View 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
View 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">&#xBB;</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">&#xBB;</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&apos;ve come to the right place.</p>
<p>Here&apos;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&amp;utm_medium=link&amp;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&amp;utm_medium=link&amp;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&amp;utm_medium=link&amp;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&amp;utm_medium=link&amp;utm_campaign=mda_wwsgd_pb_homepage" target="_blank">PrimalBlueprint.com</a>. There you&apos;ll find <a title="Books and Media" href="https://www.primalblueprint.com/books/?utm_source=mda_wwsgd&amp;utm_medium=link&amp;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&amp;utm_medium=link&amp;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&amp;utm_medium=link&amp;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&#x2019;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&#x2019;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&#x2019;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&#x2019;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&#x2019;re minutes away from tasting a culinary miracle. The chicken breasts are not only moist, they&#x2019;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&#x2019;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 &#xB0;F (204 &#xB0;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&#x2019;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 &#xB0;F (74 &#xB0;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
View 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>

25
views/index.ejs Normal file
View File

@ -0,0 +1,25 @@
<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">Logger</div>
</div>
<div id="container" class="mui-panel">
<% for (var t=0;t<data.list.length;t++)
{
%>
<%-data.list[t].body%>
<%
}%>
</div>
</div>
</body>
</html>