• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Simple RSS Reader inspired by Gxxgle Reader


Commit MetaInfo

Revision2aed231f6b845c329fc0fc3a3d6219c72f380321 (tree)
Time2013-07-11 01:03:14
Authorhylom <hylom@hylo...>
Commiterhylom

Log Message

to use hogan.js template engine in client-side

Change Summary

Incremental Difference

--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
11 Grrreader
2-========================================
2+=========
33
4-Grrreader - gxxgle-Reader-like Rss READER
4+Grrreader - Gxxgle-Reader-inspired Rss READER
55 (a.k.a. Gxxgle Reader Clone)
66
77
@@ -20,8 +20,8 @@ Requires
2020 --------
2121 * Python 2.7.x
2222 * Node.js 0.10.x
23- * Some python modules: feedparser, dateutil.parser, mysql.connector
24- * Some node.js modules: defined in client/package.json and forever
23+ * Some python modules: "feedparser", "dateutil.parser", "mysql.connector"
24+ * Some node.js modules: defined in client/package.json and "forever"
2525 * MySQL
2626
2727
@@ -29,7 +29,7 @@ How to install
2929 --------------
3030
3131 1. install Python (>2.7.x), Node.js (>0.10.x), MySQL
32-2. create MySQL database and user for use
32+2. create MySQL database and user, tables for use
3333 3. run `npm install` in client directory
3434 4. copy client/config.json.sample to client/config.json
3535 5. edit client/config.json
@@ -40,6 +40,26 @@ How to install
4040 10. add backend/feedfetcher.py to crontab
4141 11. start rrreader service like: `# service grreader start`
4242
43+Create Tables
44+---------------
45+To create Tables, do below commands.
46+
47+ $ cd backend
48+ $ python
49+ Python 2.7.2 (default, Oct 11 2012, 20:14:37)
50+ [GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] on darwin
51+ Type "help", "copyright", "credits" or "license" for more information.
52+ >>> import db
53+ >>> db.MySQLDatabase().create_tables()
54+ >>> ^D
55+ $
56+
57+
58+Import Google Reader's registered feeds
59+---------------------------------------
60+ $ cd backend
61+ $ python greaderimport.py < ../subscriptions.xml
62+
4363
4464 Sample crontab
4565 --------------
--- a/client/package.json
+++ b/client/package.json
@@ -9,6 +9,7 @@
99 "express": "3.3.1",
1010 "jade": "*",
1111 "mysql": "*",
12- "winston": "*"
12+ "winston": "*",
13+ "hogan.js": "*"
1314 }
1415 }
--- a/client/public/js/grdc.js
+++ b/client/public/js/grdc.js
@@ -1,33 +1,44 @@
11 // grdc.js
2+// require hogan.js template engine.
23
34 (function () {
5+ // private functions
6+
47 function updateContentsPane(data) {
58 var pane = $('#contentsPane');
69 pane.empty();
10+ var tmpl = '<tr class="contentHeader" id="chead{{content_id}}">'
11+ + '<td class="contentTitle"><h4>'
12+ + ' <a href="#" class="contentTitleString" cid="{{content_id}}">{{title}}</a>'
13+ + '</h4></td>'
14+ + '<td class="contentTimestamp">{{formatedTimestamp}}</td>'
15+ + '<td class="contentLink">'
16+ + ' <a target="_blank" href="{{url}}">'
17+ + ' <i class="icon-chevron-right"></i></a>'
18+ + '</td></tr>'
19+ + '<tr class="contentBody" id="cbody{{content_id}}">'
20+ + ' <td colspan="3">{{{body}}}</td>'
21+ + '</tr>'
22+ ;
23+ var tableRows = Hogan.compile(tmpl);
24+
725 for (var i = 0; i < data.length; i++) {
826 var item = data[i];
9- var elem = '<tr class="contentHeader" id="chead' + item.content_id + '">'
10- + '<td class="contentTitle"><h4><a href="#" class="contentTitleString" cid="' + item.content_id + '">' + item.title + '</a></h4></td>'
11- + '<td class="contentTimestamp">' + item.formatedTimestamp + '</td>'
12- + '<td class="contentLink"><a target="_blank" href="' + item.url + '">'
13- + '<i class="icon-chevron-right"></i></a></td>'
14- + '</tr>'
15- + '<tr class="contentBody" id="cbody' + item.content_id + '">'
16- + '<td colspan="3">'
17- + item.body
18- + '</td>'
19- + '</tr>'
20- ;
27+ var elem = tableRows.render(item);
2128 pane.append(elem);
2229 }
2330 }
2431
2532 function loadFeed(event) {
2633 var feedId = $(event.target).attr('feed-id');
34+ var feedTitle = $(event.target).text();
2735 if (feedId !== undefined) {
2836 var url = '/api/feed/' + feedId + '/contents';
2937 $.getJSON(url, updateContentsPane);
3038 }
39+ $('#feedTitle').text(feedTitle);
40+ var pane = $('#contentsPane');
41+ pane.empty();
3142 return false;
3243 }
3344
@@ -42,6 +53,12 @@
4253
4354 $(document).on('click', ".feedItem", loadFeed);
4455 $(document).on('click', ".contentTitleString", toggleContentBody);
56+
57+ $(document).ready(function () {
58+ var feedId = 0;
59+ var url = '/api/feed/' + feedId + '/contents';
60+ $.getJSON(url, updateContentsPane);
61+ });
4562 }).apply();
4663
4764
--- /dev/null
+++ b/client/public/js/hogan-2.0.0.min.js
@@ -0,0 +1,5 @@
1+/**
2+* @preserve Copyright 2012 Twitter, Inc.
3+* @license http://www.apache.org/licenses/LICENSE-2.0.txt
4+*/
5+var Hogan={};(function(a,b){function i(a){return String(a===null||a===undefined?"":a)}function j(a){return a=i(a),h.test(a)?a.replace(c,"&amp;").replace(d,"&lt;").replace(e,"&gt;").replace(f,"&#39;").replace(g,"&quot;"):a}a.Template=function(a,c,d,e){this.r=a||this.r,this.c=d,this.options=e,this.text=c||"",this.buf=b?[]:""},a.Template.prototype={r:function(a,b,c){return""},v:j,t:i,render:function(b,c,d){return this.ri([b],c||{},d)},ri:function(a,b,c){return this.r(a,b,c)},rp:function(a,b,c,d){var e=c[a];return e?(this.c&&typeof e=="string"&&(e=this.c.compile(e,this.options)),e.ri(b,c,d)):""},rs:function(a,b,c){var d=a[a.length-1];if(!k(d)){c(a,b,this);return}for(var e=0;e<d.length;e++)a.push(d[e]),c(a,b,this),a.pop()},s:function(a,b,c,d,e,f,g){var h;return k(a)&&a.length===0?!1:(typeof a=="function"&&(a=this.ls(a,b,c,d,e,f,g)),h=a===""||!!a,!d&&h&&b&&b.push(typeof a=="object"?a:b[b.length-1]),h)},d:function(a,b,c,d){var e=a.split("."),f=this.f(e[0],b,c,d),g=null;if(a==="."&&k(b[b.length-2]))return b[b.length-1];for(var h=1;h<e.length;h++)f&&typeof f=="object"&&e[h]in f?(g=f,f=f[e[h]]):f="";return d&&!f?!1:(!d&&typeof f=="function"&&(b.push(g),f=this.lv(f,b,c),b.pop()),f)},f:function(a,b,c,d){var e=!1,f=null,g=!1;for(var h=b.length-1;h>=0;h--){f=b[h];if(f&&typeof f=="object"&&a in f){e=f[a],g=!0;break}}return g?(!d&&typeof e=="function"&&(e=this.lv(e,b,c)),e):d?!1:""},ho:function(a,b,c,d,e){var f=this.c,g=this.options;g.delimiters=e;var d=a.call(b,d);return d=d==null?String(d):d.toString(),this.b(f.compile(d,g).render(b,c)),!1},b:b?function(a){this.buf.push(a)}:function(a){this.buf+=a},fl:b?function(){var a=this.buf.join("");return this.buf=[],a}:function(){var a=this.buf;return this.buf="",a},ls:function(a,b,c,d,e,f,g){var h=b[b.length-1],i=null;if(!d&&this.c&&a.length>0)return this.ho(a,h,c,this.text.substring(e,f),g);i=a.call(h);if(typeof i=="function"){if(d)return!0;if(this.c)return this.ho(i,h,c,this.text.substring(e,f),g)}return i},lv:function(a,b,c){var d=b[b.length-1],e=a.call(d);if(typeof e=="function"){e=i(e.call(d));if(this.c&&~e.indexOf("{{"))return this.c.compile(e,this.options).render(d,c)}return i(e)}};var c=/&/g,d=/</g,e=/>/g,f=/\'/g,g=/\"/g,h=/[&<>\"\']/,k=Array.isArray||function(a){return Object.prototype.toString.call(a)==="[object Array]"}})(typeof exports!="undefined"?exports:Hogan),function(a){function h(a){a.n.substr(a.n.length-1)==="}"&&(a.n=a.n.substring(0,a.n.length-1))}function i(a){return a.trim?a.trim():a.replace(/^\s*|\s*$/g,"")}function j(a,b,c){if(b.charAt(c)!=a.charAt(0))return!1;for(var d=1,e=a.length;d<e;d++)if(b.charAt(c+d)!=a.charAt(d))return!1;return!0}function k(a,b,c,d){var e=[],f=null,g=null;while(a.length>0){g=a.shift();if(g.tag=="#"||g.tag=="^"||l(g,d))c.push(g),g.nodes=k(a,g.tag,c,d),e.push(g);else{if(g.tag=="/"){if(c.length===0)throw new Error("Closing tag without opener: /"+g.n);f=c.pop();if(g.n!=f.n&&!m(g.n,f.n,d))throw new Error("Nesting error: "+f.n+" vs. "+g.n);return f.end=g.i,e}e.push(g)}}if(c.length>0)throw new Error("missing closing tag: "+c.pop().n);return e}function l(a,b){for(var c=0,d=b.length;c<d;c++)if(b[c].o==a.n)return a.tag="#",!0}function m(a,b,c){for(var d=0,e=c.length;d<e;d++)if(c[d].c==a&&c[d].o==b)return!0}function n(a){return a.replace(f,"\\\\").replace(c,'\\"').replace(d,"\\n").replace(e,"\\r")}function o(a){return~a.indexOf(".")?"d":"f"}function p(a){var b="";for(var c=0,d=a.length;c<d;c++){var e=a[c].tag;e=="#"?b+=q(a[c].nodes,a[c].n,o(a[c].n),a[c].i,a[c].end,a[c].otag+" "+a[c].ctag):e=="^"?b+=r(a[c].nodes,a[c].n,o(a[c].n)):e=="<"||e==">"?b+=s(a[c]):e=="{"||e=="&"?b+=t(a[c].n,o(a[c].n)):e=="\n"?b+=v('"\\n"'+(a.length-1==c?"":" + i")):e=="_v"?b+=u(a[c].n,o(a[c].n)):e===undefined&&(b+=v('"'+n(a[c])+'"'))}return b}function q(a,b,c,d,e,f){return"if(_.s(_."+c+'("'+n(b)+'",c,p,1),'+"c,p,0,"+d+","+e+',"'+f+'")){'+"_.rs(c,p,"+"function(c,p,_){"+p(a)+"});c.pop();}"}function r(a,b,c){return"if(!_.s(_."+c+'("'+n(b)+'",c,p,1),c,p,1,0,0,"")){'+p(a)+"};"}function s(a){return'_.b(_.rp("'+n(a.n)+'",c,p,"'+(a.indent||"")+'"));'}function t(a,b){return"_.b(_.t(_."+b+'("'+n(a)+'",c,p,0)));'}function u(a,b){return"_.b(_.v(_."+b+'("'+n(a)+'",c,p,0)));'}function v(a){return"_.b("+a+");"}var b=/\S/,c=/\"/g,d=/\n/g,e=/\r/g,f=/\\/g,g={"#":1,"^":2,"/":3,"!":4,">":5,"<":6,"=":7,_v:8,"{":9,"&":10};a.scan=function(c,d){function w(){p.length>0&&(q.push(new String(p)),p="")}function x(){var a=!0;for(var c=t;c<q.length;c++){a=q[c].tag&&g[q[c].tag]<g._v||!q[c].tag&&q[c].match(b)===null;if(!a)return!1}return a}function y(a,b){w();if(a&&x())for(var c=t,d;c<q.length;c++)q[c].tag||((d=q[c+1])&&d.tag==">"&&(d.indent=q[c].toString()),q.splice(c,1));else b||q.push({tag:"\n"});r=!1,t=q.length}function z(a,b){var c="="+v,d=a.indexOf(c,b),e=i(a.substring(a.indexOf("=",b)+1,d)).split(" ");return u=e[0],v=e[1],d+c.length-1}var e=c.length,f=0,k=1,l=2,m=f,n=null,o=null,p="",q=[],r=!1,s=0,t=0,u="{{",v="}}";d&&(d=d.split(" "),u=d[0],v=d[1]);for(s=0;s<e;s++)m==f?j(u,c,s)?(--s,w(),m=k):c.charAt(s)=="\n"?y(r):p+=c.charAt(s):m==k?(s+=u.length-1,o=g[c.charAt(s+1)],n=o?c.charAt(s+1):"_v",n=="="?(s=z(c,s),m=f):(o&&s++,m=l),r=s):j(v,c,s)?(q.push({tag:n,n:i(p),otag:u,ctag:v,i:n=="/"?r-v.length:s+u.length}),p="",s+=v.length-1,m=f,n=="{"&&(v=="}}"?s++:h(q[q.length-1]))):p+=c.charAt(s);return y(r,!0),q},a.generate=function(b,c,d){var e='var _=this;_.b(i=i||"");'+p(b)+"return _.fl();";return d.asString?"function(c,p,i){"+e+";}":new a.Template(new Function("c","p","i",e),c,a,d)},a.parse=function(a,b,c){return c=c||{},k(a,"",[],c.sectionTags||[])},a.cache={},a.compile=function(a,b){b=b||{};var c=a+"||"+!!b.asString,d=this.cache[c];return d?d:(d=this.generate(this.parse(this.scan(a,b.delimiters),a,b),a,b),this.cache[c]=d)}}(typeof exports!="undefined"?exports:Hogan)
\ No newline at end of file
--- a/client/routes/index.js
+++ b/client/routes/index.js
@@ -89,14 +89,30 @@ exports.feedContent = function (req, res) {
8989 exports.feedContents = function (req, res) {
9090 var feedId = req.params.fid;
9191 var connection = mysql.createConnection(config.mysql);
92+ var skip = req.query.skip || 0;
93+ var count = req.query.count || 20;
94+
95+ if (feedId === '0') {
96+ // show all feeds
97+ // TODO: if use multiple users, need checking subscribing feeds
98+ var sql = 'SELECT content_id, feed_id, title, url, body, timestamp'
99+ + ' FROM feed_contents'
100+ + ' ORDER BY timestamp DESC'
101+ + ' LIMIT ?, ?'
102+ + ';';
103+ var params = [skip, count];
104+ } else {
105+ var sql = 'SELECT content_id, feed_id, title, url, body, timestamp'
106+ + ' FROM feed_contents'
107+ + ' WHERE feed_id = ?'
108+ + ' ORDER BY timestamp DESC'
109+ + ' LIMIT ?, ?'
110+ + ';';
111+ var params = [feedId, skip, count];
112+ }
92113
93114 connection.connect();
94- var sql = 'SELECT content_id, feed_id, title, url, body, timestamp'
95- + ' FROM feed_contents'
96- + ' WHERE feed_id = ?'
97- + ' ORDER BY timestamp DESC'
98- + ';';
99- connection.query(sql, feedId, function (err, rows, fields) {
115+ connection.query(sql, params, function (err, rows, fields) {
100116 connection.end();
101117 if (err) {
102118 logger.debug("query error at index.feedContents: " + util.inspect(err));
--- a/client/views/index.jade
+++ b/client/views/index.jade
@@ -4,8 +4,10 @@ block content
44 #contents-body
55 .row
66 .span3
7+ a.feedItem(href='#', feed-id='0') All Feeds
78 ul.unstyled
89 each feed in feeds
910 li: a.feedItem(href='#', feed-id=feed.feed_id) #{feed.title}
1011 .span9
12+ h4#feedTitle All Feeds:
1113 table.table#contentsPane
--- a/client/views/layout.jade
+++ b/client/views/layout.jade
@@ -8,6 +8,7 @@ html
88 body
99 script(src="http://code.jquery.com/jquery.js")
1010 script(src="/js/bootstrap.min.js")
11+ script(src="/js/hogan-2.0.0.min.js")
1112 script(src="/js/grdc.js")
1213 #header
1314 p grdc - RSS reader