Updated to the new version of keeper
32
.editorconfig
Normal file
@ -0,0 +1,32 @@
|
||||
; http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.txt]
|
||||
insert_final_newline = false
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.py]
|
||||
indent_size = 4
|
||||
|
||||
[*.m]
|
||||
indent_size = 4
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
|
||||
[*.{js,json}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
46
.jscsrc
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
"disallowKeywords": ["with"],
|
||||
"disallowKeywordsOnNewLine": ["else"],
|
||||
"disallowMixedSpacesAndTabs": true,
|
||||
"disallowMultipleVarDecl": "exceptUndefined",
|
||||
"disallowNewlineBeforeBlockStatements": true,
|
||||
"disallowQuotedKeysInObjects": true,
|
||||
"disallowSpaceAfterObjectKeys": true,
|
||||
"disallowSpaceAfterPrefixUnaryOperators": true,
|
||||
"disallowSpacesInFunction": {
|
||||
"beforeOpeningRoundBrace": true
|
||||
},
|
||||
"disallowSpacesInsideParentheses": true,
|
||||
"disallowTrailingWhitespace": true,
|
||||
"maximumLineLength": 120,
|
||||
"requireCamelCaseOrUpperCaseIdentifiers": false,
|
||||
"requireCapitalizedComments": true,
|
||||
"requireCapitalizedConstructors": true,
|
||||
"requireCurlyBraces": true,
|
||||
"requireSpaceAfterKeywords": [
|
||||
"if",
|
||||
"else",
|
||||
"for",
|
||||
"while",
|
||||
"do",
|
||||
"switch",
|
||||
"case",
|
||||
"return",
|
||||
"try",
|
||||
"catch",
|
||||
"typeof"
|
||||
],
|
||||
"requireSpaceAfterLineComment": true,
|
||||
"requireSpaceAfterBinaryOperators": true,
|
||||
"requireSpaceBeforeBinaryOperators": true,
|
||||
"requireSpaceBeforeBlockStatements": true,
|
||||
"requireSpaceBeforeObjectValues": true,
|
||||
"requireSpacesInFunction": {
|
||||
"beforeOpeningCurlyBrace": true
|
||||
},
|
||||
"requireTrailingComma": false,
|
||||
"requireEarlyReturn": false,
|
||||
"validateIndentation": 2,
|
||||
"validateLineBreaks": "LF",
|
||||
"validateQuoteMarks": "'"
|
||||
}
|
123
app/css/gist.css
Normal file
@ -0,0 +1,123 @@
|
||||
.code, .gist {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.code, .gist div {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.code, .gist .gist-file {
|
||||
border: 1px solid #dedede; /* gray */
|
||||
font-family: Monaco, "Courier New", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", monospace;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.code, .gist .gist-file .gist-meta {
|
||||
overflow: hidden;
|
||||
font-size: 85%;
|
||||
padding: .5em;
|
||||
color: #666;
|
||||
background-color: #eaeaea;
|
||||
}
|
||||
|
||||
.code, .gist .gist-file .gist-meta a {
|
||||
color: #369;
|
||||
}
|
||||
|
||||
.code, .gist .gist-file .gist-meta a:visited {
|
||||
color: #737;
|
||||
}
|
||||
|
||||
.code, .gist .gist-file .gist-data {
|
||||
overflow: auto;
|
||||
word-wrap: normal;
|
||||
background-color: #f8f8ff;
|
||||
border-bottom: 1px solid #ddd;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.code, .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;
|
||||
}
|
||||
|
||||
.code, .gist .gist-file .gist-data .gist-highlight {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.code, .gist .gist-file .gist-data .gist-line-numbers {
|
||||
background-color: #ececec;
|
||||
color: #aaa;
|
||||
border-right: 1px solid #ddd;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.code, .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 */
|
13673
app/css/read.css
Normal file
2222
app/engadget.html
Normal file
BIN
app/fav/android-chrome-144x144.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
app/fav/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
app/fav/android-chrome-36x36.png
Normal file
After Width: | Height: | Size: 597 B |
BIN
app/fav/android-chrome-48x48.png
Normal file
After Width: | Height: | Size: 832 B |
BIN
app/fav/android-chrome-72x72.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
app/fav/android-chrome-96x96.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
app/fav/apple-touch-icon-114x114.png
Normal file
After Width: | Height: | Size: 950 B |
BIN
app/fav/apple-touch-icon-120x120.png
Normal file
After Width: | Height: | Size: 978 B |
BIN
app/fav/apple-touch-icon-144x144.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
app/fav/apple-touch-icon-152x152.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
app/fav/apple-touch-icon-180x180.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
app/fav/apple-touch-icon-57x57.png
Normal file
After Width: | Height: | Size: 648 B |
BIN
app/fav/apple-touch-icon-60x60.png
Normal file
After Width: | Height: | Size: 658 B |
BIN
app/fav/apple-touch-icon-72x72.png
Normal file
After Width: | Height: | Size: 704 B |
BIN
app/fav/apple-touch-icon-76x76.png
Normal file
After Width: | Height: | Size: 743 B |
BIN
app/fav/apple-touch-icon-precomposed.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
app/fav/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
12
app/fav/browserconfig.xml
Normal 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
After Width: | Height: | Size: 389 B |
BIN
app/fav/favicon-194x194.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
app/fav/favicon-32x32.png
Normal file
After Width: | Height: | Size: 510 B |
BIN
app/fav/favicon-96x96.png
Normal file
After Width: | Height: | Size: 920 B |
BIN
app/fav/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
41
app/fav/manifest.json
Normal 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
After Width: | Height: | Size: 1.2 KiB |
BIN
app/fav/mstile-150x150.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
app/fav/mstile-310x150.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
app/fav/mstile-310x310.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
app/fav/mstile-70x70.png
Normal file
After Width: | Height: | Size: 912 B |
28
app/fav/safari-pinned-tab.svg
Normal 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 |
BIN
app/gfx/fm.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
55
app/libs/microevent.js
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* MicroEvent - to make any js object an event emitter (server or browser)
|
||||
*
|
||||
* - pure javascript - server compatible, browser compatible
|
||||
* - dont rely on the browser doms
|
||||
* - super simple - you get it immediatly, no mistery, no magic involved
|
||||
*
|
||||
* - create a MicroEventDebug with goodies to debug
|
||||
* - make it safer to use
|
||||
*/
|
||||
|
||||
var MicroEvent = function(){};
|
||||
MicroEvent.prototype = {
|
||||
bind : function(event, fct){
|
||||
this._events = this._events || {};
|
||||
this._events[event] = this._events[event] || [];
|
||||
this._events[event].push(fct);
|
||||
},
|
||||
unbind : function(event, fct){
|
||||
this._events = this._events || {};
|
||||
if( event in this._events === false ) return;
|
||||
this._events[event].splice(this._events[event].indexOf(fct), 1);
|
||||
},
|
||||
trigger : function(event /* , args... */){
|
||||
this._events = this._events || {};
|
||||
if( event in this._events === false ) return;
|
||||
for(var i = 0; i < this._events[event].length; i++){
|
||||
this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* mixin will delegate all MicroEvent.js function in the destination object
|
||||
*
|
||||
* - require('MicroEvent').mixin(Foobar) will make Foobar able to use MicroEvent
|
||||
*
|
||||
* @param {Object} the object which will support MicroEvent
|
||||
*/
|
||||
MicroEvent.mixin = function(destObject){
|
||||
var props = ['bind', 'unbind', 'trigger'];
|
||||
for(var i = 0; i < props.length; i ++){
|
||||
if( typeof destObject === 'function' ){
|
||||
destObject.prototype[props[i]] = MicroEvent.prototype[props[i]];
|
||||
}else{
|
||||
destObject[props[i]] = MicroEvent.prototype[props[i]];
|
||||
}
|
||||
}
|
||||
return destObject;
|
||||
}
|
||||
|
||||
// export in common js
|
||||
if( typeof module !== "undefined" && ('exports' in module)){
|
||||
module.exports = MicroEvent;
|
||||
}
|
4
app/partials/taglist.ejs
Normal file
@ -0,0 +1,4 @@
|
||||
<li><a href="#?"><em>No tag</em></a></li>
|
||||
<% for(var i=0; i<list.length; i++) {%>
|
||||
<li><a href="#?<%= list[i] %>"><%= list[i] %></a></li>
|
||||
<% } %>
|
36
app/partials/view.ejs
Normal file
@ -0,0 +1,36 @@
|
||||
<div class="mui-container">
|
||||
<div class="mui-panel">
|
||||
<div class="mui--text-headline"><%= data.title %></div>
|
||||
<div >
|
||||
<span id='tags' class="mui--text-left">
|
||||
<span id="visualTabs">
|
||||
<span class="lnr lnr-tag"></span>
|
||||
<% for(var i=0; i<data.tags.list.length; i++) {%>
|
||||
<a class="mui--text-dark-secondary mui--text-button" href="#?<%=data.tags.list[i]%>"><%=data.tags.list[i]%></a>
|
||||
<% } %>
|
||||
<span class="lnr lnr-pencil" id="tageditmode"></span>
|
||||
</span>
|
||||
<span id="tagForm" style="display:none;">
|
||||
<span class="mui-form--inline">
|
||||
<span class="mui-textfield">
|
||||
<input type="text" id="edittags" name="edittags" value="<%=data.tags.solid%>">
|
||||
</span>
|
||||
|
||||
<button class="mui-btn" id="tagSave">Save</button>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span id="othercontrols" class="mui--pull-right">
|
||||
<span id="redo" class="mui--text-accent mui--text-button" style="cursor:pointer;">Redo <span class="lnr lnr-redo"></span></span>
|
||||
<span class="mui--divider-left"> <%= link_to('Link', data.url) %> <span class="lnr lnr-link"></span> </span>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="mui-container">
|
||||
<div class="mui-panel">
|
||||
<%= data.reduced %>
|
||||
</div>
|
||||
</div>
|
109
app/pocket.html
Normal file
@ -0,0 +1,109 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<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 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" />
|
||||
<!-- build:css -->
|
||||
|
||||
|
||||
<!-- endbuild -->
|
||||
<script src="libs/microevent.js"></script>
|
||||
<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>
|
||||
<!-- build:vendor -->
|
||||
<script src="libs/ejs.js"></script>
|
||||
<script src="libs/view.js"></script>
|
||||
<!-- endbuild -->
|
||||
|
||||
<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>
|
||||
<style>
|
||||
.item_content {
|
||||
height: 100px;
|
||||
/* border: 1px solid grey;*/
|
||||
min-height: 100px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.item_content a.title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #313131;
|
||||
}
|
||||
|
||||
.item_content div.body, .item_content div.site, .item_content div.tags {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #313131;
|
||||
}
|
||||
|
||||
.item_content div.site {
|
||||
font-size: 90%;
|
||||
color: silver;
|
||||
}
|
||||
|
||||
.item_content div.tags {
|
||||
color:blue;
|
||||
}
|
||||
|
||||
.mui-panel {
|
||||
|
||||
margin-bottom: 4px !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content" class="mui-container">
|
||||
|
||||
<div class="mui-panel">
|
||||
<div class="mui-row">
|
||||
<div class="mui-col-xs-4 mui-col-md-2"><img src="gfx/100.png"></div>
|
||||
<div class="mui-col-xs-8 mui-col-md-10 item_content" style="border:1px solid red;">
|
||||
<div class="mui-row">
|
||||
<div class="mui--text-title "><a class='title'
|
||||
href="#">Something long and text like which should fit ok</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mui-row">
|
||||
<div class="mui--text-body1 body">
|
||||
[13/Apr/2016:10:04:45 +0000] "GET / HTTP/1.1" 200 7784 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"
|
||||
</div>
|
||||
<div class="mui--text-body1 site">
|
||||
[13/Apr/2016:10:04:45 +0000] "GET / HTTP/1.1" 200 7784 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"
|
||||
</div>
|
||||
<div class="mui--text-body1 tags">
|
||||
[13/Apr/2016:10:04:45 +0000] "GET / HTTP/1.1" 200 7784 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
23
bower.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "Keeper",
|
||||
"description": "",
|
||||
"main": "keeper-server.js",
|
||||
"authors": [
|
||||
"Martin Donnelly <martind2000@gmail.com>"
|
||||
],
|
||||
"license": "ISC",
|
||||
"homepage": "",
|
||||
"private": true,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"src/bower_modules",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"jquery": "^3.0.0",
|
||||
"mui": "^0.6.5"
|
||||
}
|
||||
}
|
33
ecosystem.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
/**
|
||||
* Application configuration section
|
||||
* http://pm2.keymetrics.io/docs/usage/application-declaration/
|
||||
*/
|
||||
apps: [
|
||||
// First application
|
||||
{
|
||||
"name": "Recipe",
|
||||
"script": "recipe-server.js",
|
||||
"cwd": "/var/www/recipes",
|
||||
"watch": true,
|
||||
"ignore_watch" : ["node_modules"],
|
||||
"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: {
|
||||
|
||||
}
|
||||
}
|
911
server/keeper.js
Normal file
@ -0,0 +1,911 @@
|
||||
'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 converter = require('html-to-markdown');
|
||||
var markdown = require( "markdown" ).markdown;
|
||||
var zlib = require('zlib');
|
||||
var log4js = require('log4js');
|
||||
var logger = log4js.getLogger();
|
||||
var URL = require('url');
|
||||
|
||||
var router = express.Router();
|
||||
|
||||
var EventEmitter = require('events');
|
||||
|
||||
var nano = require('nano')('http://localhost:5984');
|
||||
//var nano = require('nano')('http://martind2000:1V3D4m526i@localhost:5984');
|
||||
var busEmitter = new EventEmitter();
|
||||
|
||||
var db_name = 'recipes';
|
||||
var dbCouch = nano.use(db_name);
|
||||
|
||||
var jsonFile = __dirname + '/' + 'output.json';
|
||||
var bodyfile = __dirname + '/' + 'body.html';
|
||||
var htmlfile = __dirname + '/' + 'testoutput.html';
|
||||
var generics = [
|
||||
'ARTICLE',
|
||||
'div.content_column',
|
||||
'div.post',
|
||||
'div.page',
|
||||
'#recipe-single',
|
||||
'div.content.body',
|
||||
'div.container'
|
||||
];
|
||||
|
||||
var specialHandlers = [{
|
||||
url: 'www.reddit.com', fn: function(body, url) {
|
||||
return doReddit(body, url);
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'developer.android.com', fn: function(body, url) {
|
||||
return doAndroidDeveloper(body, url);
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'www.engadget.com', fn: function(body, url) {
|
||||
return doEngadget(body, url);
|
||||
}
|
||||
}
|
||||
,
|
||||
{
|
||||
url: 'www.bbcgoodfood.com', fn: function(body, url) {
|
||||
return doBBCGoodFood(body, url);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
];
|
||||
|
||||
|
||||
|
||||
function cleaner(b) {
|
||||
var _b = b;
|
||||
|
||||
var unwanted = [
|
||||
'LINK',
|
||||
'META',
|
||||
'TITLE',
|
||||
'div#disqus_thread',
|
||||
'SCRIPT',
|
||||
'FOOTER',
|
||||
'div.ssba',
|
||||
'.shareaholic-canvas',
|
||||
'.yarpp-related',
|
||||
'div.dfad',
|
||||
'div.postFooterShare',
|
||||
'div#nextPrevLinks',
|
||||
'.post-comments',
|
||||
'HEADER',
|
||||
'.post-title',
|
||||
'#side-menu',
|
||||
'.footer-container',
|
||||
'#pre-footer',
|
||||
'#cakephp-global-navigation',
|
||||
'.masthead',
|
||||
'.breadcrumb-header',
|
||||
'.single-recipe-sidebar',
|
||||
'#recipe-related-videos',
|
||||
'#tnav',
|
||||
'.footer',
|
||||
'#tb-wrapper',
|
||||
'#comments',
|
||||
'#menu',
|
||||
'aside',
|
||||
'#ad-mpu-premium-1-mobile',
|
||||
'#recipetools',
|
||||
'.adsense-ads-separator',
|
||||
'.comments',
|
||||
'.related-content',
|
||||
'.tip-wrapper',
|
||||
'#recipe-related-video-mobile',
|
||||
'.float-wrapper',
|
||||
'.source-jamie',
|
||||
'.ad.mobile',
|
||||
'.foodity-wrapper',
|
||||
'#ad-most-watched-mobile',
|
||||
'#sticky',
|
||||
'.nutrition-expand',
|
||||
'.grid-list-wrapper',
|
||||
'#recipe-finder__box',
|
||||
'.browser-upgrade-alert-message',
|
||||
'.main-menu',
|
||||
'.recipe-media'
|
||||
|
||||
|
||||
|
||||
|
||||
];
|
||||
|
||||
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..');
|
||||
}
|
||||
|
||||
function updateBookmark(obj, _id, _rev) {
|
||||
logger.debug('Updating couch...');
|
||||
var _obj = obj;
|
||||
_obj._id = _id;
|
||||
_obj._rev = _rev;
|
||||
|
||||
dbCouch.insert(_obj, function(err, body, header) {
|
||||
if (err) {
|
||||
logger.error('Error updating into couch');
|
||||
return;
|
||||
} else {
|
||||
logger.info('I think we updated ok...');
|
||||
busEmitter.emit('updateTagsDB');
|
||||
|
||||
}
|
||||
});
|
||||
logger.debug('Update done..');
|
||||
}
|
||||
var doInsertBookmark = (obj) => {
|
||||
// Logger.info('sendSocket: ' + JSON.stringify(obj));
|
||||
insertBookmark(obj);
|
||||
};
|
||||
|
||||
var doUpdateBookmark = (obj, _id, _rev) => {
|
||||
// Logger.info('sendSocket: ' + JSON.stringify(obj));
|
||||
updateBookmark(obj, _id, _rev);
|
||||
};
|
||||
|
||||
var doGetBookmark = (obj) => {
|
||||
// Logger.info('sendSocket: ' + JSON.stringify(obj));
|
||||
genericGrab(obj);
|
||||
};
|
||||
|
||||
var doGetBookmarkRedo = (obj) => {
|
||||
// Logger.info('sendSocket: ' + JSON.stringify(obj));
|
||||
genericGrab(obj);
|
||||
};
|
||||
|
||||
var doGetBookmarkRes = (url, res) => {
|
||||
logger.debug('doGetBookmarkRes');
|
||||
// Logger.info('sendSocket: ' + JSON.stringify(obj));
|
||||
genericGrab(url, res);
|
||||
};
|
||||
|
||||
var doSaveNew = (obj) => {
|
||||
logger.debug('doGetBookmarkRes');
|
||||
// Logger.info('sendSocket: ' + JSON.stringify(obj));
|
||||
saveNew(obj);
|
||||
};
|
||||
|
||||
var doUpdateTagsDB = () => {
|
||||
logger.debug('Update the tags database...');
|
||||
|
||||
dbCouch.view('getAllTags', 'getAllTags', function(err, body) {
|
||||
var masterList = [];
|
||||
if (!err) {
|
||||
body.rows.forEach(function(doc) {
|
||||
|
||||
masterList = masterList.concat(doc.value);
|
||||
});
|
||||
|
||||
masterList = masterList.filter((value, index, self) => {
|
||||
return self.indexOf(value) === index;
|
||||
});
|
||||
|
||||
dbCouch.view('taglist', 'taglist', function(err, body) {
|
||||
// Logger.debug(body);
|
||||
if (!err) {
|
||||
|
||||
var outJSON = {};
|
||||
|
||||
body.rows.forEach(function(doc) {
|
||||
doSaveTagsDB(doc.value, masterList);
|
||||
});
|
||||
|
||||
} else {
|
||||
logger.error('NO TAG LIST EXISTS');
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
var doSaveTagsDB = (orig, newList) => {
|
||||
logger.debug('doSaveTagsDB');
|
||||
|
||||
var _obj = orig;
|
||||
|
||||
_obj.taglist = newList;
|
||||
|
||||
dbCouch.insert(_obj, function(err, body, header) {
|
||||
if (err) {
|
||||
logger.error('Error updating into couch');
|
||||
return;
|
||||
} else {
|
||||
logger.info('Updated the tags list...');
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Events
|
||||
busEmitter.on('saveBookmarkData', doInsertBookmark);
|
||||
busEmitter.on('updateBookmarkData', doUpdateBookmark);
|
||||
busEmitter.on('getBookmark', doGetBookmark);
|
||||
busEmitter.on('getBookmarkRes', doGetBookmarkRes);
|
||||
|
||||
busEmitter.on('getBookmarkRedo', doGetBookmarkRedo);
|
||||
busEmitter.on('updateTagsDB', doUpdateTagsDB);
|
||||
busEmitter.on('saveTagsDB', doSaveTagsDB);
|
||||
|
||||
busEmitter.on('saveNew', doSaveNew);
|
||||
|
||||
function doBBCGoodFood(body, url) {
|
||||
logger.info('GRABBING BBCGoodFood');
|
||||
var obj = {}, tdihbody, i, urlObj, urlPrefix;
|
||||
|
||||
var $ = cheerio.load(body);
|
||||
var title = $('TITLE').text();
|
||||
|
||||
tdihbody = $('DIV#main-content');
|
||||
|
||||
logger.debug('Length:' , tdihbody.length);
|
||||
tdihbody = cleaner(tdihbody);
|
||||
logger.debug('Title: ', title);
|
||||
|
||||
urlObj = URL.parse(url);
|
||||
urlPrefix = urlObj.protocol + '//' + urlObj.host + '/';
|
||||
|
||||
|
||||
|
||||
try {
|
||||
tdihbody.find('IMG').each(function(i, elem) {
|
||||
let s, src = $(this).attr('src');
|
||||
|
||||
if (src !== null) {
|
||||
if (!STRING(src).startsWith('http')) {
|
||||
logger.debug('Stripping:' + src);
|
||||
src = urlPrefix + STRING(src).stripLeft('/').trim().s;
|
||||
}
|
||||
|
||||
if (typeof obj.thumbnail === 'undefined') {
|
||||
obj.thumbnail = src;
|
||||
}
|
||||
|
||||
s = 'http://image.silvrtree.co.uk/900,fit/' + src;
|
||||
|
||||
$(this).attr('src', s);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
logger.error(e);
|
||||
}
|
||||
|
||||
obj.url = STRING(url).trim().s;
|
||||
obj.html = $.html();
|
||||
obj.reduced = STRING(tdihbody.html()).trim().s;
|
||||
obj.nib = STRING(tdihbody.text()).collapseWhitespace().trim().left(300).s;
|
||||
obj.title = STRING(title).collapseWhitespace().s;
|
||||
obj.markdown = converter.convert(obj.reduced);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
function doEngadget(body, url) {
|
||||
logger.info('GRABBING Engadget');
|
||||
var obj = {}, tdihbody, i, urlObj, urlPrefix;
|
||||
|
||||
var $ = cheerio.load(body);
|
||||
var title = $('TITLE').text();
|
||||
|
||||
tdihbody = $('DIV#page_body');
|
||||
|
||||
logger.debug('Length:' , tdihbody.length);
|
||||
tdihbody = cleaner(tdihbody);
|
||||
logger.debug('Title: ', title);
|
||||
|
||||
urlObj = URL.parse(url);
|
||||
urlPrefix = urlObj.protocol + '//' + urlObj.host + '/';
|
||||
|
||||
try {
|
||||
tdihbody.find('IMG').each(function(i, elem) {
|
||||
let s, src = $(this).attr('src');
|
||||
|
||||
if (src !== null) {
|
||||
if (!STRING(src).startsWith('http')) {
|
||||
logger.debug('Stripping:' + src);
|
||||
src = urlPrefix + STRING(src).stripLeft('/').trim().s;
|
||||
}
|
||||
|
||||
if (typeof obj.thumbnail === 'undefined') {
|
||||
obj.thumbnail = src;
|
||||
}
|
||||
|
||||
s = 'http://image.silvrtree.co.uk/900,fit/' + src;
|
||||
|
||||
$(this).attr('src', s);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
logger.error(e);
|
||||
}
|
||||
|
||||
obj.url = STRING(url).trim().s;
|
||||
obj.html = $.html();
|
||||
obj.reduced = STRING(tdihbody.html()).trim().s;
|
||||
obj.nib = STRING(tdihbody.text()).collapseWhitespace().trim().left(300).s;
|
||||
obj.title = STRING(title).collapseWhitespace().s;
|
||||
obj.markdown = converter.convert(obj.reduced);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
function doAndroidDeveloper(body, url) {
|
||||
logger.info('GRABBING AndroidDeveloper');
|
||||
var obj = {}, tdihbody, i, urlObj, urlPrefix;
|
||||
|
||||
var $ = cheerio.load(body);
|
||||
var title = $('TITLE').text();
|
||||
|
||||
tdihbody = $('DIV.jd-descr');
|
||||
|
||||
logger.debug(tdihbody.length);
|
||||
tdihbody = cleaner(tdihbody);
|
||||
logger.debug(title);
|
||||
|
||||
urlObj = URL.parse(url);
|
||||
urlPrefix = urlObj.protocol + '//' + urlObj.host + '/';
|
||||
|
||||
try {
|
||||
tdihbody.find('IMG').each(function(i, elem) {
|
||||
let s, src = $(this).attr('src');
|
||||
|
||||
if (src !== null) {
|
||||
if (!STRING(src).startsWith('http')) {
|
||||
logger.debug('Stripping:' + src);
|
||||
src = urlPrefix + STRING(src).stripLeft('/').trim().s;
|
||||
}
|
||||
|
||||
if (typeof obj.thumbnail === 'undefined') {
|
||||
obj.thumbnail = src;
|
||||
}
|
||||
|
||||
s = 'http://image.silvrtree.co.uk/900,fit/' + src;
|
||||
|
||||
$(this).attr('src', s);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
logger.error(e);
|
||||
}
|
||||
|
||||
obj.url = STRING(url).trim().s;
|
||||
obj.html = $.html();
|
||||
obj.reduced = STRING(tdihbody.html()).trim().s;
|
||||
obj.nib = STRING(tdihbody.text()).collapseWhitespace().trim().left(300).s;
|
||||
obj.title = STRING(title).collapseWhitespace().s;
|
||||
obj.markdown = converter.convert(obj.reduced);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
function doReddit(body, url) {
|
||||
logger.info('GRABBING REDDIT');
|
||||
var obj = {}, tdihbody, i, urlObj, urlPrefix;
|
||||
|
||||
var $ = cheerio.load(body);
|
||||
var title = $('TITLE').text();
|
||||
|
||||
tdihbody = $('DIV.entry');
|
||||
|
||||
tdihbody.find('A.thumbnail').each(function(i, elem) {
|
||||
|
||||
logger.warn($(this));
|
||||
});
|
||||
|
||||
logger.info('++++++');
|
||||
// Logger.debug(tdihbody.html());
|
||||
|
||||
logger.debug(tdihbody.length);
|
||||
tdihbody = cleaner(tdihbody);
|
||||
logger.debug(title);
|
||||
|
||||
obj.url = STRING(url).trim().s;
|
||||
obj.html = $.html();
|
||||
obj.reduced = STRING(tdihbody.html()).trim().s;
|
||||
obj.nib = STRING(tdihbody.text()).collapseWhitespace().trim().left(300).s;
|
||||
obj.title = STRING(title).collapseWhitespace().s;
|
||||
obj.markdown = converter.convert(obj.reduced);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
function saveNew(obj) {
|
||||
logger.info('Saving new page');
|
||||
|
||||
var md = markdown.toHTML(obj.body);
|
||||
obj.url = '';
|
||||
obj.html = md;
|
||||
obj.reduced = STRING(md).trim().s;
|
||||
obj.nib = STRING(md).collapseWhitespace().trim().left(300).s;
|
||||
obj.title = STRING(obj.title).collapseWhitespace().s;
|
||||
obj.markdown = obj.body;
|
||||
|
||||
busEmitter.emit('saveBookmarkData', obj);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function genericProcessor(body, url) {
|
||||
logger.info('USING DEFAULT PROCESSOR');
|
||||
var obj = {}, tdihbody, i, urlObj, urlPrefix;
|
||||
var $ = cheerio.load(body);
|
||||
var title = $('TITLE').text();
|
||||
|
||||
i = 0;
|
||||
|
||||
while (($(generics[i]).length == 0) && (i < generics.length)) {
|
||||
i++;
|
||||
}
|
||||
logger.debug(i);
|
||||
|
||||
if (i < generics.length) {
|
||||
logger.warn('Used a generic');
|
||||
tdihbody = $(generics[i]);
|
||||
logger.debug(tdihbody.length);
|
||||
tdihbody = cleaner(tdihbody);
|
||||
logger.debug(title);
|
||||
|
||||
} else {
|
||||
logger.warn('Using whole body');
|
||||
// Bah. nothing to reduce so just grab the body, tidy it and use that
|
||||
tdihbody = $('BODY');
|
||||
|
||||
if (tdihbody.length === 0) {
|
||||
|
||||
tdihbody = $(':root');
|
||||
|
||||
}
|
||||
|
||||
logger.debug(tdihbody.length);
|
||||
tdihbody = cleaner(tdihbody);
|
||||
logger.debug(title);
|
||||
}
|
||||
|
||||
// Logger.info(util.inspect(tdihbody));
|
||||
|
||||
urlObj = URL.parse(url);
|
||||
urlPrefix = urlObj.protocol + '//' + urlObj.host + '/';
|
||||
|
||||
try {
|
||||
tdihbody.find('IMG').each(function(i, elem) {
|
||||
let s, src = $(this).attr('src');
|
||||
|
||||
console.log('!!!!' + src);
|
||||
if (src !== null && typeof src !== 'undefined') {
|
||||
if (!STRING(src).startsWith('http')) {
|
||||
logger.debug('Stripping:' + src);
|
||||
src = urlPrefix + STRING(src).stripLeft('/').trim().s;
|
||||
}
|
||||
|
||||
if (typeof obj.thumbnail === 'undefined') {
|
||||
obj.thumbnail = src;
|
||||
}
|
||||
|
||||
s = 'http://image.silvrtree.co.uk/900,fit/' + src;
|
||||
|
||||
$(this).attr('src', s);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
logger.error(e);
|
||||
}
|
||||
|
||||
obj.url = STRING(url).trim().s;
|
||||
obj.html = $.html();
|
||||
obj.reduced = STRING(tdihbody.html()).trim().s;
|
||||
obj.nib = STRING(tdihbody.text()).collapseWhitespace().trim().left(300).s;
|
||||
obj.title = STRING(title).collapseWhitespace().s;
|
||||
obj.markdown = converter.convert(obj.reduced);
|
||||
|
||||
|
||||
return obj;
|
||||
}
|
||||
function processBody(body, url, _id, _rev) {
|
||||
|
||||
var obj = {}, i, urlObj, urlPrefix;
|
||||
|
||||
|
||||
|
||||
|
||||
// Try to find a body to grab
|
||||
|
||||
urlObj = URL.parse(url);
|
||||
|
||||
logger.debug('host:', urlObj.host);
|
||||
|
||||
var flag;
|
||||
for (i = 0;i < specialHandlers.length;i++) {
|
||||
if (urlObj.host === specialHandlers[i].url) {
|
||||
flag = true;
|
||||
obj = specialHandlers[i].fn(body,url);
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag) {
|
||||
// Do generic processing
|
||||
obj = genericProcessor(body,url);
|
||||
}
|
||||
|
||||
// Logger.warn(obj.reduced);
|
||||
|
||||
obj.host = urlObj.host;
|
||||
|
||||
|
||||
/* Jsonfile.writeFile(jsonFile, obj, function (err) {
|
||||
console.error(err);
|
||||
});*/
|
||||
|
||||
if (_id !== null) {
|
||||
busEmitter.emit('updateBookmarkData', obj, _id, _rev);
|
||||
} else {
|
||||
busEmitter.emit('saveBookmarkData', obj);
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
||||
}
|
||||
function genericGrab(obj, res) {
|
||||
|
||||
var url, _id = null, _ver = null;
|
||||
|
||||
if (typeof obj === 'string') {
|
||||
logger.info(obj);
|
||||
url = obj;
|
||||
} else {
|
||||
url = obj.url;
|
||||
_id = obj._id || null;
|
||||
_ver = obj._rev || null;
|
||||
}
|
||||
|
||||
logger.warn(typeof obj);
|
||||
|
||||
logger.info(url);
|
||||
logger.info(_id);
|
||||
logger.info(_ver);
|
||||
|
||||
var options = {
|
||||
url: url,
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36'
|
||||
},
|
||||
jar: true,
|
||||
followRedirect: true,
|
||||
followAllRedirects: true
|
||||
};
|
||||
|
||||
request(options, function(err, resp, body) {
|
||||
if (err)
|
||||
throw err;
|
||||
|
||||
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 {
|
||||
var b = processBody(body, url, _id, _ver);
|
||||
if (res != null) {
|
||||
res.render('grabbed');
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
var b = processBody(body, url, _id, _ver);
|
||||
if (res != null) {
|
||||
res.render('grabbed', {data: b});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
router.get('/pocket', function(req, res) {
|
||||
logger.debug('list..');
|
||||
|
||||
|
||||
dbCouch.view('pocketList', 'pocketList', function(err, body) {
|
||||
if (!err) {
|
||||
|
||||
var outJSON = [];
|
||||
body.rows.forEach(function(doc) {
|
||||
var obj = {id: doc.id, entry: doc.value};
|
||||
console.log(typeof obj.entry.tn);
|
||||
if (typeof obj.entry.tn === 'string') {
|
||||
console.log('its a string:', typeof obj.entry.tn)
|
||||
obj.entry.tn = 'http://image.silvrtree.co.uk/100,fit,q80/' + obj.entry.tn;
|
||||
} else {
|
||||
obj.entry.tn = 'gfx/fm.png';
|
||||
}
|
||||
|
||||
outJSON.push(obj);
|
||||
|
||||
|
||||
});
|
||||
|
||||
logger.debug(util.inspect(body));
|
||||
logger.info(util.inspect(outJSON));
|
||||
res.render('pocket', {data: outJSON});
|
||||
} else {
|
||||
res.writeHead(500, {ContentType: 'application/json'});
|
||||
res.end(JSON.stringify({}));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
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) {
|
||||
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 = {};
|
||||
outJSON._id = body._id;
|
||||
outJSON._rev = body._rev;
|
||||
outJSON.title = body.title;
|
||||
outJSON.reduced = body.reduced;
|
||||
outJSON.url = body.url;
|
||||
outJSON.tags = body.tags || {solid: '', list: []};
|
||||
|
||||
//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.route('/tags')
|
||||
.get(function(req, res, next) {
|
||||
logger.debug('tag list..');
|
||||
|
||||
logger.debug(req.params.id);
|
||||
|
||||
dbCouch.view('taglist', 'taglist', function(err, body) {
|
||||
if (!err) {
|
||||
logger.debug(body);
|
||||
var outJSON = [];
|
||||
body.rows.forEach(function(doc) {
|
||||
logger.info(doc.value.taglist);
|
||||
if (doc.value[0] == req.params.id) {
|
||||
outJSON = doc.value.taglist.sort();
|
||||
}
|
||||
});
|
||||
|
||||
//Logger.debug(util.inspect(body));
|
||||
res.writeHead(200, {ContentType: 'application/json'});
|
||||
res.end(JSON.stringify({list: outJSON}));
|
||||
|
||||
} else {
|
||||
logger.error(err);
|
||||
res.writeHead(500, {ContentType: 'application/json'});
|
||||
res.end(JSON.stringify({}));
|
||||
}
|
||||
});
|
||||
|
||||
}).post(function(req, res, next) {
|
||||
var t = req.body;
|
||||
console.log(t);
|
||||
|
||||
logger.info('regetting:' + req.body._id);
|
||||
|
||||
dbCouch.get(req.body._id, function(err, body) {
|
||||
if (!err) {
|
||||
|
||||
var obj = {};
|
||||
|
||||
obj.url = body.url;
|
||||
obj.html = body.html;
|
||||
obj.reduced = body.reduced;
|
||||
obj.title = body.title;
|
||||
obj.tags = req.body.tags;
|
||||
|
||||
logger.info('Updating...');
|
||||
busEmitter.emit('updateBookmarkData', obj, body._id, body._rev, res);
|
||||
|
||||
var outJSON = {};
|
||||
outJSON._id = body._id;
|
||||
outJSON._rev = body._rev;
|
||||
outJSON.title = body.title;
|
||||
outJSON.reduced = body.reduced;
|
||||
outJSON.url = body.url;
|
||||
outJSON.tags = req.body.tags;
|
||||
|
||||
//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.get('/tags/:id', function(req, res) {
|
||||
logger.debug('entry..');
|
||||
|
||||
logger.debug(req.params.id);
|
||||
|
||||
dbCouch.view('getTagByKey', 'getTagByKey', function(err, body) {
|
||||
if (!err) {
|
||||
// Logger.debug(body);
|
||||
var outJSON = [];
|
||||
body.rows.forEach(function(doc) {
|
||||
// Logger.debug(doc);
|
||||
if (doc.value[0] == req.params.id) {
|
||||
outJSON.push({id: doc.id, title: doc.value[1]})
|
||||
}
|
||||
});
|
||||
|
||||
//Logger.debug(util.inspect(body));
|
||||
res.writeHead(200, {ContentType: 'application/json'});
|
||||
res.end(JSON.stringify({list: outJSON}));
|
||||
|
||||
} else {
|
||||
logger.error(err);
|
||||
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', t);
|
||||
} else {
|
||||
logger.error('No data block!');
|
||||
}
|
||||
res.writeHead(200, {ContentType: 'application/json'});
|
||||
res.end(JSON.stringify({adding: url}));
|
||||
|
||||
});
|
||||
|
||||
router.post('/savenew', function(req, res) {
|
||||
logger.debug('save new entry..');
|
||||
|
||||
var t = req.body;
|
||||
logger.debug(t);
|
||||
|
||||
|
||||
if (t.hasOwnProperty('title')) {
|
||||
var title = t.title.toString();
|
||||
logger.debug(title);
|
||||
busEmitter.emit('saveNew', t);
|
||||
} else {
|
||||
logger.error('No data block!');
|
||||
}
|
||||
|
||||
res.writeHead(200, {ContentType: 'application/json'});
|
||||
res.end(JSON.stringify({}));
|
||||
|
||||
});
|
||||
|
||||
router.post('/redo', function(req, res) {
|
||||
logger.debug('redoing entry..');
|
||||
|
||||
var t = req.body;
|
||||
console.log(t);
|
||||
if (t.hasOwnProperty('url')) {
|
||||
var url = t.url.toString();
|
||||
logger.debug(url);
|
||||
busEmitter.emit('getBookmark', t);
|
||||
} else {
|
||||
logger.error('No data block!');
|
||||
}
|
||||
res.writeHead(200, {ContentType: 'application/json'});
|
||||
res.end(JSON.stringify({adding: url}));
|
||||
|
||||
});
|
||||
|
||||
router.route('/new')
|
||||
.get(function(req, res, next) {
|
||||
logger.debug('Save new');
|
||||
busEmitter.emit('getBookmarkRes', req.query.url, res);
|
||||
}).post(function(req, res, next) {
|
||||
logger.debug('Posted Save new');
|
||||
logger.info(req.body);
|
||||
if (Object.keys(req.body).length !== 0) {
|
||||
busEmitter.emit('getBookmarkRes', req.body.url, res);
|
||||
} else {
|
||||
res.status(422).end();
|
||||
}
|
||||
});
|
||||
|
||||
busEmitter.emit('updateTagsDB');
|
||||
|
||||
module.exports = router;
|
62
server/maker.js
Normal file
@ -0,0 +1,62 @@
|
||||
var nano = require('nano')('http://localhost:5984');
|
||||
|
||||
// clean up the database we created previously
|
||||
nano.db.destroy('recipes', function () {
|
||||
// create a new database
|
||||
nano.db.create('recipes', function () {
|
||||
// specify the database we are going to use
|
||||
var recipes = nano.use('recipes');
|
||||
// and insert a document in it
|
||||
/* recipes.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);
|
||||
});*/
|
||||
|
||||
recipes.insert(
|
||||
{
|
||||
"views": {
|
||||
"titles": {
|
||||
"map": function (doc) { emit(null, doc.title); }
|
||||
}
|
||||
}
|
||||
}, '_design/titles', function (error, response) {
|
||||
console.log("_design/titles added");
|
||||
});
|
||||
|
||||
recipes.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.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
{
|
||||
"getTagByKey": {
|
||||
"map": function(doc) {
|
||||
if (doc.tags.list.length > 0) {
|
||||
for (var t = 0; t < doc.tags.list.length; t++) {
|
||||
emit(doc._id, [doc.tags.list[t], doc.title]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
1
server/output.json
Normal file
21
server/processor.js
Normal file
@ -0,0 +1,21 @@
|
||||
var generics = [
|
||||
'ARTICLE',
|
||||
'div.content_column',
|
||||
'div.post',
|
||||
'div.page',
|
||||
'#recipe-single',
|
||||
'div.content.body'
|
||||
];
|
||||
|
||||
var specialHandlers = [{
|
||||
url: 'www.reddit.com', fn: function (body, url) {
|
||||
return doReddit(body, url);
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'developer.android.com', fn: function (body, url) {
|
||||
return doAndroidDeveloper(body, url);
|
||||
}
|
||||
}
|
||||
|
||||
];
|
13
server/seedtags.js
Normal file
@ -0,0 +1,13 @@
|
||||
var nano = require('nano')('http://localhost:5984');
|
||||
var logger = require('log4js').getLogger();
|
||||
|
||||
var db_name = 'keeper';
|
||||
var keeper = nano.use(db_name);
|
||||
keeper.insert({type:1,taglist:[]}, function(error, response) {
|
||||
if (error) {
|
||||
logger.error(error);
|
||||
}
|
||||
else {
|
||||
logger.info(response);
|
||||
}
|
||||
});
|
128
server/viewinsert.js
Normal file
@ -0,0 +1,128 @@
|
||||
var nano = require('nano')('http://localhost:5984');
|
||||
var logger = require('log4js').getLogger();
|
||||
|
||||
var db_name = 'recipes';
|
||||
var keeper = nano.use(db_name);
|
||||
|
||||
var tableList = [
|
||||
{
|
||||
name: '_design/titles', view: {
|
||||
"titles": {
|
||||
"map": function(doc) { emit(null, doc.title); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
, {
|
||||
name: '_design/reducedView', view: {
|
||||
"reducedView": {
|
||||
"map": function(doc) { emit(null, [doc.title, doc.reduced]); }
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: '_design/taglist', view: {
|
||||
"taglist": {
|
||||
"map": function(doc) { if (doc.type == 1) { emit(null, doc); } }
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: '_design/getAllTags', view: {
|
||||
"getAllTags": {
|
||||
"map": function(doc) {
|
||||
if (doc.tags.list.length > 0) {
|
||||
emit(null, doc.tags.list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: '_design/getTagByKey', view: {
|
||||
"getTagByKey": {
|
||||
"map": function(doc) {
|
||||
if (doc.tags.list.length > 0) {
|
||||
for (var t = 0; t < doc.tags.list.length; t++) {
|
||||
emit(doc._id, [doc.tags.list[t], doc.title]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
name: '_design/pocketList', view: {
|
||||
"pocketList": {
|
||||
"map":function (doc) { emit(doc._id, {'host':doc.host, 'tn':doc.thumbnail, 'title':doc.title, 'nib':doc.nib, 'tags':doc.tags}); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
];
|
||||
var killTable = (table) => {
|
||||
|
||||
console.log(table.name);
|
||||
keeper.get(table.name, {revs_info: true}, function(err, body) {
|
||||
if (err)
|
||||
console.log(err);
|
||||
|
||||
if (typeof body !== 'undefined') {
|
||||
|
||||
keeper.destroy(table.name, body._rev, function(_err, _body) {
|
||||
if (err) {
|
||||
console.log(_err);
|
||||
}
|
||||
else {
|
||||
console.log(table.name + ' deleted');
|
||||
console.log(_body);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var updateTable = (table) => {
|
||||
|
||||
logger.info(table.name);
|
||||
keeper.get(table.name, {revs_info: true}, function(err, body) {
|
||||
if (err)
|
||||
logger.error(body);
|
||||
|
||||
if (typeof body !== 'undefined') {
|
||||
|
||||
logger.debug(body);
|
||||
keeper.insert({_id: body._id, _rev: body._rev, views: table.view},
|
||||
function(_err, _body) {
|
||||
if (err) {
|
||||
logger.error(_err);
|
||||
}
|
||||
else {
|
||||
logger.info(table.name + ' updated');
|
||||
logger.info(_body);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
else {
|
||||
keeper.insert({
|
||||
"views": table.view
|
||||
}, table.name, function(error, response) {
|
||||
if (error) {
|
||||
logger.error(error);
|
||||
}
|
||||
else {
|
||||
logger.info(response);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
for (var t = 0; t < tableList.length; t++) {
|
||||
updateTable(tableList[t]);
|
||||
}
|
48
test.md
Normal file
@ -0,0 +1,48 @@
|
||||
![pic](https://s3-eu-west-1.amazonaws.com/rakh-images/e6768419-2953-4a19-903c-efb75016527c.jpeg)
|
||||
|
||||
* 3 Tbsp coconut oil
|
||||
|
||||
* 1 large red onion, diced
|
||||
|
||||
* 3 organic peppers (I like a mixture of red and green), diced
|
||||
|
||||
* 6 cloves garlic, minced
|
||||
|
||||
* 1 small butternut squash, diced
|
||||
|
||||
* 1 7 oz jar tomato paste (I like this brand)
|
||||
|
||||
* 2 lbs ground beef
|
||||
|
||||
* 1 26 oz tetra pack diced Pomi tomatoes (or whatever diced tomatoes you prefer)
|
||||
|
||||
* 1/2 quart homemade beef stock
|
||||
|
||||
* 2 Tbsp chili powder
|
||||
|
||||
* 2 Tbsp cumin
|
||||
|
||||
* 2 Tbsp cocoa powder (I got this idea from The Clothes Make the Girl– she is genius!)
|
||||
|
||||
* 1 tsp crushed red pepper (add more or less depending on spice preference)
|
||||
|
||||
* 1 tsp sea salt
|
||||
|
||||
* 1/2 tsp ground black pepper
|
||||
|
||||
* 1/2 tsp all spice
|
||||
|
||||
* scallions (optional)
|
||||
|
||||
* avocado (optional)
|
||||
|
||||
#### Instructions ####
|
||||
|
||||
1. Melt coconut oil in a large pot over medium heat. Saute onion, peppers, squash, garlic, and tomato paste until soft.
|
||||
|
||||
2. Add ground beef and sauteed until browned– stir frequently. Drain the fat if necessary.
|
||||
|
||||
3. Add tomatoes, stock, and seasonings. Simmer for a few hours over low heat.
|
||||
|
||||
4. Top with sliced scallions and avocado and enjoy.
|
||||
|
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>
|
145
views/pocket.ejs
Normal file
@ -0,0 +1,145 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<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 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" />
|
||||
<!-- build:css -->
|
||||
|
||||
|
||||
<!-- endbuild -->
|
||||
<script src="libs/microevent.js"></script>
|
||||
<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>
|
||||
<!-- build:vendor -->
|
||||
<script src="libs/ejs.js"></script>
|
||||
<script src="libs/view.js"></script>
|
||||
<!-- endbuild -->
|
||||
|
||||
<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>
|
||||
<style>
|
||||
.item_content {
|
||||
height: 100px;
|
||||
/* border: 1px solid grey;*/
|
||||
min-height: 100px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.item_content a.title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #313131;
|
||||
}
|
||||
|
||||
.item_content div.body, .item_content div.site, .item_content div.tags {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #313131;
|
||||
}
|
||||
|
||||
.item_content div.site {
|
||||
font-size: 90%;
|
||||
color: silver;
|
||||
}
|
||||
|
||||
.item_content div.tags {
|
||||
color:blue;
|
||||
}
|
||||
|
||||
.img100 {
|
||||
width:100px;
|
||||
border:1px solid grey;
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.mui-panel {
|
||||
|
||||
margin-bottom: 4px !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="content" class="mui-container">
|
||||
|
||||
<% for(var i=0; i<data.length; i++) {%>
|
||||
|
||||
<div class="mui-panel">
|
||||
<div class="mui-row">
|
||||
<div class="mui-col-xs-4 mui-col-md-2"><img src="<%= data[i].entry.tn %>" class="img100"></div>
|
||||
<div class="mui-col-xs-8 mui-col-md-10 item_content">
|
||||
<div class="mui-row">
|
||||
<div class="mui--text-title "><a class='title'
|
||||
href="<%= data[i].id %>"><%= data[i].entry.title %></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mui-row">
|
||||
<div class="mui--text-body1 body">
|
||||
<%= data[i].entry.nib %>
|
||||
</div>
|
||||
<div class="mui--text-body1 site">
|
||||
<%= data[i].entry.host %>
|
||||
</div>
|
||||
<div class="mui--text-body1 tags">
|
||||
<% if (typeof data[i].entry.tags !== 'undefined') { %>
|
||||
TAGS:
|
||||
<%=data[i].entry.tags.solid %>
|
||||
|
||||
<% } %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<% } %>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|