├── .gitignore ├── app.bundle ├── bundle.sh ├── config ├── controllers └── default.js ├── definitions ├── func.js └── helpers.js ├── index.js ├── license.txt ├── package.json ├── public ├── css │ └── default.css ├── favicon.ico ├── js │ ├── default.js │ └── ui.js └── robots.txt ├── readme.md ├── resources └── default.resource └── views ├── detail.html ├── index.html ├── layout.html └── tiles.html /.gitignore: -------------------------------------------------------------------------------- 1 | themes/default/.DS_Store 2 | sftp-config.json 3 | logs/* 4 | tmp/* 5 | databases/* 6 | debug.pid 7 | /index.js.json 8 | /index.js.map 9 | /index.pid 10 | -------------------------------------------------------------------------------- /app.bundle: -------------------------------------------------------------------------------- 1 | /controllers/ : # 2 | /definitions/ : # 3 | /public/ : # 4 | /resources/ : # 5 | /views/ : # 6 | /public/css/ : # 7 | /public/js/ : # 8 | /controllers/default.js : H4sIAAAAAAAAE8VWW2/bNhR+tn7FmR9CGfWkuMiGIpZkbEFSdECTYnAehmEYVOnYZktRHkmlzhz994IX2bLseO4WYE8iz43fuergalkKJQPKpUoZgxhmFc8ULbk/gLXX+/Xufnrtk7fXUwjJEJalVHIw3qUbYrimeb0R+TNHlVLWlVSUodRC5tDlCml4Qu5xPsmSa5b+dnnrLFU4L8Vj6/WG1JVNl9RhaNCa2xZtPfa8JgDWlg3DQypAIptBDGpB5djr6VvwV4XiMWC0oApiaJGW6Rzh7GyP9F0cAxkRmMDoDVzC6KJjKeXzKp0jzSGGq7vbmw2lkSuwKAX9G31zqwSDV0AmBF6BI9BAYiqyxRDIaygorxRKMtzNqtczDhlE+7CfnjTEsdfr0Rn4VsjBPjuzqGz6dGB6vZv726vAhmprqfUgCjEEgXJZconudWsahXA2etY5yh9SRnPDGFu6QFUJbi/1VnUPhcNhSP4aTEou4QLqLpJdpV4DzGrq9Jrv0xNcv/8w/e3u51+ur6YOiwX5QPGLTyjPcUVajjmZ2h1qQCbRO03NKm1VngmpOWcpYx/T7HNja6B1tYG6Vbk2DKdX7ujH/6UOd5J2xFMjcMRT178+zY+5vAd5CGTkoB1DtrXeBXYQUXv+vTyijvW9UFnO4Vjp6XlKURxL/jd3+7bVvefaXJe9bchZKcDX4KjCAsrZxlKgCXJg+19hYSrOoXPF1/yGzuuQBLNSFKnyjSjNbZeZt00MOlZN8+2HS8jjLfRCLXBq5P/doO3M2WfGbGvK2iGrXf5YUZajgBh+J9FkVTB4QCFpyeP+KDjvA/KszCmfx/376c33b/owSSIh5VbqdXDeT6JskXKOLIkUVQyTNU8LrKPQ3iJG+edkXQlWR6E5RznKTNCldilZty51FLZZJEjF3PwHhkAWqmBk8Mf4lBpykThQRTYWzu1gWcmFTyIt94/Y2yvQQT9kVRSpeOz4ENEinWMSVYIl6yXNVCW0eX2NQscLDQDrrT5uvHW/Dm8fcrgJuV6pEtIq/6zkCrnyG41PJeU+IYMhEIUrFa60ZdMPz4+2ZrnyN1vW4SbRlEYEYnj/07vbIGN6h3DHwHEpSpgcIAYzyvN3Cguf6JCiIEPYvnkJvGI6Z7rEd6C8aHu2+7N5xfRncwloPu7IHe/j/746/tCsjs6WqcdNDAJdpTbn37xIuLw3m4i1vhDll4vzC19Xw1e6N2NTMAwAAA== 9 | /definitions/func.js : H4sIAAAAAAAAE7WV3W6bQBCFr+EpRlTRgkIBN41U2cFRUjlRL+pUcZKbqqowjO1VNqy7u/hHDu9esYCNKzmJk/TG0izLmW/mjIeYp1LB4O4SQiAncjaGOU3UJLRaQXBgwQTpeKLqaEZxfs4XoRVAAKsgh1Urt2CGQlKehlbLa1mweGCpDK2JUtO278/nc29+5HEx9j8FQeDL2djqngiMFRQyFiz1b5VzFeSblFpbqiXD0BpRxtofhsOh5XdPCpEu6ZjmxW3/qxczCGGUpbGiPLXjiLFhFN87sDKNWSSARek4i8YIIXy96l94ddwxjeve4OY8oyxB4V32bmxSMMu27w+z0QjFPGLMi/mDH02pjws/Zj6BQ7DXiqdgk9M6oklYPK1DB9pAiON4E4wSFDZZfFT8HlPilhg6cDxcYLxh7ph5XVUUxzxL1c7S9mSv5HzyaqApl0o2cf5kKJYu1HddUPQBeaZ0ooLRNOgI7OYpdOFI0xv1Wza5KZ8TF9KMMadjGoZAlYm0Yxq5uXehGtMnLmi8F1W7rgiFcEGgnPJUogs8U9NMlbxFJWXsSRWpTEIYhvA5+OKYhmFIVFUZ9qZXLhwHQcXRbNN2Rx4fIXDgEFq6cmQSC8F1e7aQiit50xNFGT7lyWsmRWvu18Cd4/I7QRXRrf8nTd6GV/kLh0CTN7hbzqe2tT4CmkoVpTHyEZwJES1L5zde1Bd/Br88FIKLxshuZlYP7V4G/ocuVSa+R5eea9KLW/Ty2a5hQOBIoJzYpVvVtn+S9fvZt375RajPK93q9WoPPq+x2b//COUNPppS9VY448fZ7aA36F3f9a5tEvMEGZWKOO/JvZ2jurJOkZumedW3iUQxozESt/HJKS6i0PrFGFQxHMCxXoCBo9d15VLH1MSFlsAoWRJXd6g421nkDrK/+z+jRJYIAAA= 10 | /definitions/helpers.js : H4sIAAAAAAAAE4WOvW7DMAyE5/gpiCyyAEf9GTrUSbq0HYuMWQWVsgTIkkHRbgrD717EQVIvRTfe8XDfvb69K4ehQ8qq042PmhF2YPto2KdYtukTg4SxWA2aYJGI+AWHi7zllEl95AouotMNXu/gW88VsPNZOUJbivNXVCCO30chFWEXtMFylhWI8X4SUtbFipB7irMxPkzj4ySUTdRqLq9blM8HwgFebutURziUUjluQym2HkzQOe/WVoPVG+NwoBQ3AS2v99s7vz8DTYpMKQgJzyBE9ds1lzzJhePzB554yYt44n955Bv3J1DWxVT/APAqgeWMAQAA 11 | /public/favicon.ico : H4sIAAAAAAAAE72XXWhcVRDHZ7vRxIpmRQRBMcEHEeybIogpvWfOJlgk2pcFrWDxA7EvQVGsvpiLOXPubgJBKpSKL/VFaixVFFohPmwgeQgFrUrVB63RKlZSQvacu7uJ2Wbk7GYlrpvk3vTGgWE///c3Z2bu3HMAUrALMhmAFPTCix0A9wBAb2/j88kMwHgHwL0AkHHfQ+N7Z14HRDL2B3eXFL4UjniPRFM0dV5XReGTZS1nq4FkG2Auku7o/k6rMFcmnHG6vwpZrmjJJRIHo+iNEiecxnlIWPdKINlo71AUvSWcXhnNsiWsu9O7OAzh85H0Wk46dlPvfCkv2Wo8HCl+Lc626pfzWS4pMRRFHxJ+2qq/OtbPZgSPROIrPN3UlzVybbTf5e9KGMhI9beEH9YKWV4pZJ2+agmPGeX1VXX2GUsDe7bUK5xYY56yI4ihlocqgfx6LSZrlDxVJvHopfGHbmi/fvlKNZBPGSUet4RTLvf1/DfX4/pCu/fyXEXLoUrBu3O9no/uv9mQOO5i4LH+ev1tG3fx8PgAL2l5MSTZ/4+eIWXJ22O1fNMS/rCcb/SwY67XVgJcLQfyTEhCsp+7vt1a2PcyIYmDoZaTZS2XXR+6Xnb3RlnLA5zLpaPUpB6TFvsqGqcN4ZGNcrfEAHMM4G/mwwB+N4CfBvBTDV/sBKj1NOaMmy7eujnjZ6JEuLkt5LPdLp8hiZev/WrRbb7w8E2GxBuWcKExC6T+P7hu5oYaX7WE8+t7zigc21mu12WVGLKEl9v1vCV8e0e4buZrPGwJf9uA2/RjiXLfvf86N9ct4S9bcJv+XpJ8S/hTRO5a/cWJhPm1OHyr8YOE+dVYfMKPEubbeHz5ScL8hVj1JzyTKF/jn/HqLyYT5RP+HrP+xST5hnAuJn8mSb4l/DFe/eW5hPnfx1q/wvMJ87+Nmf8LifIVfhmdLX61JJ9Ikh8SzkZgl4zG13mDPdu1mOvnTbgrVuM7Vu+9rR7rqHe7JXHckvzcKBxk39+VAL+4Aftjo6U7ysLlsYEbLeFw66wOCX82Wr5mtLz1GvhftFxz1mi51/3GE7m0JfGCVfjHFvWpuudyWXkPxOUbJc42uOKi6y23T4fGGfAxS/hdzHujHn9I4mm3n4rEJ/m+Oy81/19S+KA7M8XltvH5UGFQVdizGd/l2L0uBtm7rRYnLeFqAuz1XnPPbHc+a+b2Pz3QOKctJ8xt9avuHNE2B/7g7nqfafxmJ9ihws/sW+K+KP1gSQpLeDr2vrC9zxiV7YvCbbVqPntXSJi3hFe2wb1gtDywHW6rufOQIfGsJflVBO4lo+VzzZ5O2txcshon6nP539wFNwddrDvBbbWKwjssoXJ7p1CJwmLQd0vca3CJmaeYOcVcSzEvppiL2/E0c7FrHxd7u7nop3kKYHgaoOc8QHqu4Z01gB4GGOYiMC+lmVe7+W8sod97NhUAAA== 12 | /public/robots.txt : H4sIAAAAAAAAEwstTi3STUxPzSuxUtDi5XLMyckvt1LQ5+UCAJW9MwUZAAAA 13 | /resources/default.resource : H4sIAAAAAAAAEwMAAAAAAAAAAAA= 14 | /views/detail.html : H4sIAAAAAAAAE5VVXW+lNhB9hl8xRasFtAskjdqHrKFIUSv14SbVSqt9qPpgsAEr2EbG3CxC/PfKBu6XblbJC2AzZ+bMGc84nzjVOOCS0DYWmFP4BD5E4MMneHh6/MvufYblfz9wjtW4LTWu+3B23XzqaamZFCBrsze7DjJeoVOyo0qPqSfre810Sz0opdBU6NTLp58Hnb3sqh9C+1KxzsS74m2l+BqYcVxfI9GxUg/q1ZgWdv/CiG5OwLe/3tz8FNBQVjf6BPH7q4BBtWe0rAyDaud8Mk8vc/OJCmLVZhWcsY6ZIPTHUxX4lHd69ENIU4huZxcRtoeyxX2fetzLEOM19Kq8kjbg9qIks7chGa8jRftOip7tKdilHAShxIMkQwlhe8OuYrProoFFpeSdFFRoMH5Sj2P1TOSL6BTdM/pi06xYnXqN5u397SHOZucBwRpHJda0lmpk5EjsuGcrlU+/LPuFJONs1gdpesZZixV8/Hi+EbdU1LoxR/RUnDU0bB/Ram6iOKi5yxDbbCsMFY4KKZ+Bq9+8DCUsgzx40g1V0Mle9yFKmjsLHFrzyqdKKorLBjgwcc5ndh3HQS3LUN9hscWoFR69LJ94THQ3FC3rG0riSiqOdeDnwTiOY7TbRYSEfjijxGAzhKFRtEq9xLJIDJyROTGOjC4xrikguJtRkeXB45/fQ5QUmS2cMbVFRwnOUNKyhbY9cI6DEpvHWmlnKbWDCgVJ5qLktOTZ6TTosKDthdIdJoSJGoo66rl8povAJwbPdNzjdlh+XP7xsjz4Z9Mj3Aidm63o7chcF5AQ2O12MRghFwlXV+uHTf9wmLZjN7+X68MKfBvVQ5hLNs6q+bvDy0FoME3Xv5GBAVj7+cc1EgdFFMWmjJpx+m5Rvi5YMOC30TqNBnnAmYjD1zQ6o2luo2PHO0jgY8vr25uF4tX2XO8xxzl21R8a16lpilIS+u3r3w/boQ94eJyWBSY1BfuMCpvF5ehocN9oXC+TI5+4bbqFyKHhBD5N6TBjlxsALRdg5jpPj4FvxBn9z1ANwrZdEMLkOs4eK6AtpPAh8ONtrPnhF9dxPgR+QzGhCvC/dtSaETvdzN5//tYitI2x1ooE/nHk+mEYxtimEvg9bWmpKbEe5/CLi5KV1f/ofnGlUggAAA== 15 | /views/index.html : H4sIAAAAAAAAE7VVTW+cMBA9h18xoVIWpCxkmyaHlkVI/bglrdRDz148gCVsE9ubDSL898oGsuxu1KaHnvDHvPHMm3lD1rECtqqG9XoNi3jRe2dZx9GQ4PP3+2+RIBwvwS0p6lyxxjApwt7LuoL1npdN8PMJnlQryGui9dqvVn6adYaZGvskrlbpAephi6qNDClPMcEPqY2GTQuGlB9DuHjYSvMp6/aQ4eTU6SwTuLgALinWkWE16t5LKHucnlFy56c21UIqJHkFHJiYm0fMINe9dzZHcchlvazL5bVbPOnlrfVyYGPROVHUB0oMWTK69rOOR4z2zvQsIVApLNZ+/M6aTncTnHFSop8mjJegVT6AG5abrUJ4foZFzHgZI29MG5WsWMyR5VKhbqTQ7BF9ILUZ0FuNytax92EjFUW19q98iNMkJkNEs+itnS0ad5Xvk5iyxxMjSsxoRE2z3dRMV0ijQipOTLDIAkrh7u4ugrZt23AR7r1Mi+mbdSho743bQ6YLvXpvGdvz5eoSW24mq4IoKMjScQZc3fhpErM0C35WcgekrsFBQpfo+Mi8Wc6HertKRzWK0lS24BsFcTr7zsLKURhUJzXnh0HZmGzxN0SjXd88DYHtebDfLPgy2VCJWiwM5FIYwgQQ0UJjJRCFx5QdRTem89bW/mNTO04Gu8YSanW0sioKmKD45PZXtgf321XY32Yd1hr7Dy4U50vzV3Rh83G6OFKByzMeZRD/Tx28VQNWL7a9LRtOPIy68cYjuRN211tvbu1cQhYUUoXgCDgQ3Ew+f8vXTcmXVy39CVz3iW6IeNEm7uxsvP/6K0xie5G6FxNtlBTlXLTjSQT2TG85J6rtX0v1X5U8hqeQUCZKwzj2l+6R+QlkAWciAnsWDiG+bQB4Rx2oIYXV4dxuSMkEsf8gNxsEcW6zrsK6QaWj8R4D5yW0rR47o7n8fwPzXzUy9AYAAA== 16 | /views/layout.html : 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 17 | /views/tiles.html : H4sIAAAAAAAAE2VSwa6bMBA8h6/YugeCWqBV1R4qgpBa9Zb20h/Y4AVWwjaynfQhHv9eGcJ7pD3tGmbWM56tJkUej3F1/M09ua/QWKMgieEdfPv180emUdH7tZXkasuDZ6OTOYqqiRt4o4ykPmNPymU96dZ3c3QoLhbyclcl36Du0bmTqEl7sqKMDg+flSgL3g4NQoOpRI8XdBT6z0+iLHIui1zybeOW1fH7hpGGnI491EZ7ZA2oR/DBUZZsnJf6qK6aGp6jaC/Gmj9BYDU1xhLWHShgDTur86MnBbXp075NPy3Nk0u//GcwiKnRSgHBV8ryJKpJZSznBXooEDpLzUnki+z8bSgbYhvCClsKL6VacLZeRwxc+6sleH6GOGfV5qQGP2YtN/Ge2aaW3GC04xsJwN6v7KsjG0KeBVyMlWRP4oOAvCxyXHXtPAScKANrYbyE8QCS6O8g6YfrpWfXkcwaYxX6sGdSwvl8zmAcxzGJk9cp/+ZUTaTlHN2P676tGQzYkoMSPs4PsQ3Yssawn+FJC43L2GrqqB/Iuuz+n47LlCSEmC+g+xXLJvwFLFIvVBEDAAA= 18 | /public/css/default.css : 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 19 | /public/js/default.js : H4sIAAAAAAAAE41SwY7TMBA9218xSKu1rU2TtKinKEgrUbFwAAGVlgNC8saTxsi1q9ihFLb/jpykkLIscIszb96beW/qzlZBOwstev0Na9dWyAV8p5R8kS3soYTbl8/XN1wUlOga+B7KsgT21TO4v4fx5bdMUEJaDF1ri7EXDZRwwVl659SBnfrRpAbtJjRRhPTAJorcwAzQpK6uPQYu0uB2MINFnheUEDRp5T1nW21nDepNE1gCTaQkUWAnLRom/oBBkw7fXET0EdB4pIR4DGu9RdcFPlk8gWWei4IeKb3gtyJ1lrOhzJKpQaKgb17HklQHlkA9etj7Rs6MLOijUvO813q0/vQf9eVQP4qC0tMEELRBhUFqw7Xqx4meawXa+iBtha6Gz287bA8xL62gBK1SGUKrONMqpkR//m5xZ2SFnEVWlgAbyz3jmCI8g8UyXsITnn36qK4ykQb0IaqfX4RxlYwjpo30MfCBFK5Aq7MlF4PcsMTU3HEfcv3q+gNnL1ZryOROZxHss4Fogm7R75z1OPSQ96v1evVuSp35xu2HWHtgMvGuP6xjfzB9TgnYzpgEtPrtOOIuVSPtBh+cQXTpbOdfhs3zaM0kqnOc7+58aLXd8Lk4BfyADS4v4W/0/8v+AxK/JuUABAAA 20 | /public/js/ui.js : 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 21 | -------------------------------------------------------------------------------- /bundle.sh: -------------------------------------------------------------------------------- 1 | mkdir -p .bundle/ 2 | 3 | cd .bundle 4 | cp -a ../controllers/ controllers 5 | cp -a ../definitions/ definitions 6 | cp -a ../public/ public 7 | cp -a ../resources/ resources 8 | cp -a ../views/ views 9 | 10 | total4 --bundle app.bundle 11 | cp app.bundle ../app.bundle 12 | 13 | cd .. 14 | rm -rf .bundle 15 | echo "DONE" -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | name : Bufferwall template 2 | allow_custom_titles : true 3 | allow_ssc_validation : true 4 | 5 | // Bufferwall token 6 | token : YOUR_TOKEN_TO_BUFFERWALL 7 | tiles : true 8 | 9 | // Language: en, de, etc.. 10 | language : 11 | 12 | url : https://template.bufferwall.com 13 | // description : CUSTOM DESCRIPTION IN RIGHT PANEL (otherwise will be copied from Bufferwall profile) 14 | 15 | facebook : https://www.facebook.com/totaljs.web.framework/ 16 | twitter : https://twitter.com/totalframework 17 | github : https://github.com/totaljs/ 18 | linkedin : https://www.linkedin.com/groups/8109884/ 19 | youtube : 20 | instagram : -------------------------------------------------------------------------------- /controllers/default.js: -------------------------------------------------------------------------------- 1 | exports.install = function() { 2 | ROUTE('GET /', posts); 3 | ROUTE('GET /posts/{id}/', posts_detail); 4 | ROUTE('GET /tiles/', tiles); 5 | ROUTE('GET /rss/', rss); 6 | ROUTE('GET /json/', json); 7 | ROUTE('GET /{category}/', posts_category); 8 | ROUTE('GET /api/tiles/{id}/', tiles_detail); 9 | }; 10 | 11 | function posts() { 12 | var self = this; 13 | self.query.limit = self.query.page && self.query.page !== '1' ? 18 : 14; 14 | self.query.languageid = CONF.language; 15 | self.memorize(self.url + '?' + self.uri.search, '2 minutes', function() { 16 | 17 | var page = self.query.page || '1'; 18 | if (page === '1' && CONF.tiles) { 19 | FUNC.posts(self.query, function(err, response) { 20 | 21 | if (err) { 22 | self.invalid(err); 23 | return; 24 | } 25 | 26 | if (CONF.tiles) { 27 | FUNC.tiles({ limit: 4 }, function(err, tiles) { 28 | response.tiles = tiles || EMPTYOBJECT; 29 | self.view('index', response); 30 | }); 31 | } else 32 | self.view('index', response); 33 | }); 34 | } else 35 | FUNC.posts(self.query, self.callback('index')); 36 | }); 37 | } 38 | 39 | function tiles() { 40 | var self = this; 41 | self.query.limit = 16; 42 | self.query.languageid = CONF.language; 43 | self.memorize(self.url + '?' + self.uri.search, '2 minutes', function() { 44 | FUNC.tiles(self.query, self.callback('tiles')); 45 | }); 46 | } 47 | 48 | function tiles_detail(id) { 49 | var self = this; 50 | self.memorize(self.url, '1 minute', function() { 51 | FUNC.tiles_detail(id, self.callback()); 52 | }); 53 | } 54 | 55 | function posts_detail(id) { 56 | var self = this; 57 | self.memorize(self.url, '1 minute', function() { 58 | FUNC.posts_detail(id, self.callback('detail')); 59 | }); 60 | } 61 | 62 | function json() { 63 | var self = this; 64 | self.query.languageid = CONF.language; 65 | FUNC.posts(self.query, function(err, response) { 66 | if (err) 67 | self.invalid(err); 68 | else { 69 | for (var item of response.items) 70 | item.url = CONF.url + '/posts/{0}/'.format(item.id); 71 | self.json(response.items); 72 | } 73 | }); 74 | } 75 | 76 | function rss() { 77 | var self = this; 78 | self.memorize(self.url + '?' + self.uri.search, '2 minutes', function() { 79 | self.query.languageid = CONF.language; 80 | FUNC.posts(self.query, function(err, response) { 81 | 82 | if (err) { 83 | self.invalid(err); 84 | return; 85 | } 86 | 87 | var builder = ['{name}{url}{description}'.arg(CONF, 'html')]; 88 | for (var item of response.items) { 89 | item.url = CONF.url; 90 | builder.push('{name}{url}/posts/{id}/{summary}{picture}'.arg(item, 'html')); 91 | } 92 | builder.push(''); 93 | self.content(builder.join(''), 'text/xml'); 94 | }); 95 | }); 96 | } 97 | 98 | function posts_category(category) { 99 | var self = this; 100 | var category = MAIN.cl && MAIN.cl.categories ? MAIN.cl.categories.findItem('linker', category) : null; 101 | if (category) { 102 | self.memorize(self.url + '?' + self.uri.search, '2 minutes', function() { 103 | self.query.categoryid = category.id; 104 | self.query.languageid = CONF.language; 105 | self.query.limit = self.query.page && self.query.page !== '1' ? 15 : 14; 106 | self.title(category.name); 107 | FUNC.posts(self.query, self.callback('index')); 108 | }); 109 | } else 110 | self.throw404(); 111 | } -------------------------------------------------------------------------------- /definitions/func.js: -------------------------------------------------------------------------------- 1 | const SVG = ''; 2 | 3 | FUNC.cl = function(callback) { 4 | var language = CONF.language; 5 | RESTBuilder.GET('https://bufferwall.com/api/ex/cl/' + (language ? ('?languageid=' + language) : '')).header('x-token', CONF.token).exec(callback); 6 | }; 7 | 8 | FUNC.account = function(callback) { 9 | RESTBuilder.GET('https://bufferwall.com/api/ex/account/').header('x-token', CONF.token).exec(callback); 10 | }; 11 | 12 | FUNC.posts = function(query, callback, timeoutcount) { 13 | 14 | if (timeoutcount > 3) { 15 | callback('Timeout', null); 16 | return; 17 | } 18 | 19 | RESTBuilder.GET('https://bufferwall.com/api/ex/posts/', query).header('x-token', CONF.token).exec(function(err, response, output) { 20 | if (output.status === 408) 21 | setTimeout(FUNC.posts, 500, query, callback, (timeoutcount || 0) + 1); 22 | else 23 | callback(err, response); 24 | }); 25 | }; 26 | 27 | FUNC.tiles = function(query, callback) { 28 | RESTBuilder.GET('https://bufferwall.com/api/ex/tiles/', query).header('x-token', CONF.token).exec(callback); 29 | }; 30 | 31 | FUNC.posts_detail = function(id, callback) { 32 | RESTBuilder.GET('https://bufferwall.com/api/ex/posts/' + id).header('x-token', CONF.token).exec(function(err, response) { 33 | 34 | if (response instanceof Array) { 35 | callback(response[0].error, null); 36 | return; 37 | } 38 | 39 | callback(err, response); 40 | }); 41 | }; 42 | 43 | FUNC.tiles_detail = function(id, callback) { 44 | RESTBuilder.GET('https://bufferwall.com/api/ex/tiles/' + id).header('x-token', CONF.token).exec(function(err, response) { 45 | if (response instanceof Array) 46 | callback(response[0].error, null); 47 | else 48 | callback(err, response); 49 | }); 50 | }; 51 | 52 | function refresh() { 53 | 54 | FUNC.cl(function(err, response) { 55 | MAIN.cl = response; 56 | }); 57 | 58 | FUNC.account(function(err, response) { 59 | MAIN.account = response; 60 | }); 61 | } 62 | 63 | function init() { 64 | 65 | FUNC.cl(function(err, response) { 66 | MAIN.cl = response; 67 | PAUSESERVER('codelist'); 68 | }); 69 | 70 | FUNC.account(function(err, response) { 71 | MAIN.account = response; 72 | PAUSESERVER('account'); 73 | }); 74 | } 75 | 76 | 77 | ON('service', function(counter) { 78 | if (counter % 5 === 0) 79 | refresh(); 80 | }); 81 | 82 | ON('ready', init); 83 | 84 | PAUSESERVER('codelist'); 85 | PAUSESERVER('account'); -------------------------------------------------------------------------------- /definitions/helpers.js: -------------------------------------------------------------------------------- 1 | DEF.helpers.paginate = function(model) { 2 | var paginate = new Pagination(model.count, model.page, model.limit, this.href('page', 'XyX').replace('XyX', '{0}')); 3 | return '{0}{1}{2}'.format(paginate.isPrev ? paginate.prev().html('', 'control') : '', paginate.html(6), paginate.isNext ? paginate.next().html('', 'control') : ''); 4 | }; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // =================================================== 2 | // Total.js start script 3 | // https://www.totaljs.com 4 | // =================================================== 5 | 6 | const options = {}; 7 | 8 | // options.ip = '127.0.0.1'; 9 | // options.port = parseInt(process.argv[2]); 10 | // options.unixsocket = require('path').join(require('os').tmpdir(), 'app_name'); 11 | // options.unixsocket777 = true; 12 | // options.config = { name: 'Total.js' }; 13 | // options.sleep = 3000; 14 | // options.inspector = 9229; 15 | // options.watch = ['private']; 16 | // options.livereload = 'https://yourhostname'; 17 | 18 | // Enables cluster: 19 | // options.cluster = 'auto'; 20 | // options.cluster_limit = 10; // max 10. threads (works only with "auto" scaling) 21 | 22 | // Enables threads: 23 | // options.cluster = 'auto'; 24 | // options.cluster_limit = 10; // max 10. threads (works only with "auto" scaling) 25 | // options.timeout = 5000; 26 | // options.threads = '/api/'; 27 | // options.logs = 'isolated'; 28 | 29 | var type = process.argv.indexOf('--release', 1) !== -1 || process.argv.indexOf('release', 1) !== -1 ? 'release' : 'debug'; 30 | require('total4/' + type)(options); -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | Copyright 2019-2021 (c) Peter Širka 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a 5 | copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to permit 9 | persons to whom the Software is furnished to do so, subject to the 10 | following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included 13 | in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bufferwall", 3 | "description": "Total.js Bufferwall Reader", 4 | "version": "1.0.0", 5 | "main": "index.js", 6 | "dependencies": { 7 | "total4": "latest" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": ["blogengine", "bufferwall"], 13 | "author": "Peter Širka", 14 | "license": "MIT" 15 | } -------------------------------------------------------------------------------- /public/css/default.css: -------------------------------------------------------------------------------- 1 | /*auto*/ 2 | 3 | $font : Arial; 4 | $color : black; 5 | 6 | html,body { margin: 0; padding: 0; } 7 | .img-rounded { border-radius: 2px; } 8 | 9 | .color { color: gray; } 10 | .fs12 { font-size: 12px; } 11 | 12 | header { border-bottom: 1px solid #E0E0E0; } 13 | header .top { padding: 30px 20px; } 14 | header hr { margin: 0; border-color: #E0E0E0; } 15 | header .br { border-right: 1px solid #E0E0E0; } 16 | header nav { height: 48px; line-height: 40px; font-family: $font; padding: 0; background-color: white; } 17 | header nav a { color: black; padding: 0; display: inline-block; vertical-align: middle; font-size: 16px; font-weight: bold; margin: 0 0 0 20px; line-height: 46px; border-bottom: 3px solid transparent; } 18 | header nav a i { margin-right: 7px; } 19 | header nav .tiles { font-size: 20px; float: right; } 20 | header nav .tiles i { margin-right: 0; } 21 | header nav a:first-child { margin-left: 0; } 22 | header nav a:hover, header nav a.selected { text-decoration: none; border-bottom-color: black; } 23 | header .search { margin: 0; padding: 12px 0 0 15px; border-right: 1px solid #E0E0E0; height: 48px; background-color: #F8F8F8; } 24 | header .search > span { float: left; width: 30px; margin: 1px 0 0; font-size: 16px; color: gray; } 25 | header .search > div { margin: 2px 0 0 30px; } 26 | header .search input { border: 0; background: transparent; outline: 0; width: 100%; font-size: 16px; padding: 0; margin: 0; line-height: 20px; } 27 | 28 | footer { text-align: center; padding: 70px 0; border-top: 1px solid #E0E0E0; font-size: 12px; color: gray; } 29 | footer a { color: black; } 30 | 31 | .panel { min-height: 600px; padding: 30px 15px 0 0; border-right: 1px solid #E0E0E0; } 32 | .panel .photo, .top .photo { display: block; max-width: 150px; margin: 0 auto; } 33 | .panel .photo img, .top .photo img { border-radius: 200px; border: 2px solid #E0E0E0; } 34 | .panel hr { border-color: #dadada; margin-top: 18px; margin-bottom: 15px; } 35 | .panel .padding { padding: 15px; } 36 | .panel .name, .top .name { font-size: 22px; font-weight: bold; text-align: center; font-family: $font; margin: 20px 0 0; line-height: 22px; } 37 | .panel .signature, .top .signature { font-size: 14px; margin: 3px 0 0; text-align: center; font-family: $font; padding: 0; line-height: 16px; } 38 | 39 | .npt { padding-top: 0 !important; } 40 | .npb { padding-bottom: 0 !important; } 41 | .nmt { margin-top: 0 !important; } 42 | .nmb { margin-bottom: 0 !important; } 43 | .mr5 { margin-right: 5px; } 44 | .ml5 { margin-left: 5px; } 45 | 46 | @media(min-width: 981px) { 47 | .npl { padding-left: 0; } 48 | } 49 | 50 | .h1 { cursor: default; margin: 0 0 20px; font-size: 28px; padding: 0; color: black; font-family: Arial; font-weight: bold; line-height: 28px; } 51 | .body { border-right: 1px solid #E0E0E0; } 52 | .body .padding { padding: 20px 5px 15px 0; } 53 | 54 | .b { font-weight: bold; } 55 | .m { margin-bottom: 20px; } 56 | 57 | .right { text-align: right; } 58 | .center { text-align: center; } 59 | 60 | .bg-yellow { background-color: #FFFFD5; } 61 | .bg-smoke { background-color: #F8F8F8; } 62 | 63 | .social { margin: 10px 0; text-align: center; } 64 | .social a { padding: 5px; padding-bottom: 0px; cursor: pointer; display: inline-block; color: #333; } 65 | .social a.facebook:hover { color: #2e4da7; } 66 | .social a.twitter:hover { color: #00abee; } 67 | .social a.instagram:hover { color: #2C6A93; } 68 | .social a.youtube:hover { color: #C31A1E; } 69 | .social a.github:hover { color: #040204; } 70 | .social a.linkedin:hover { color: #04669A; } 71 | .social a.dribbble:hover { color: #c32361; } 72 | .social a.pinterest:hover { color: #C91517; } 73 | .social a.whatsapp:hover { color: #1fca42; } 74 | .social a.messenger:hover { color: #0071ff; } 75 | .social a.telegram:hover { color: #289cd3; } 76 | 77 | .badge { font-size: 12px; padding: 4px 6px; border-radius: 2px; background-color: #F0F0F0; line-height: 12px; vertical-align: middle; position: relative; display: inline-block; color: white; margin: 0 5px 5px 0; } 78 | .badge .fa { margin-right: 3px; } 79 | .badge-blue { background-color: #346bd0; } 80 | .badge-red { background-color: #DA4453; } 81 | .badge-green { background-color: #8CC152; } 82 | .badge-yellow { background-color: #EFDC05; } 83 | .badge-orange { background-color: #E9573F; } 84 | .badge-gray { background-color: #606060; } 85 | .badge-purple { background-color: #967ADC; } 86 | .badge-pink { background-color: #D770AD; } 87 | .badge-silver { background-color: #E0E0E0; color: gray; } 88 | .badge-large { padding: 3px 8px; font-size: 14px; line-height: 16px; } 89 | .badge-large .fa { margin-right: 5px; } 90 | .badge-small { font-size: 10px; padding: 0px 3px 1px; } 91 | 92 | .postcard { background-color: #F8F8F8; } 93 | .postcard .image { display: block; margin-bottom: 10px; border: 1px solid #E0E0E0; } 94 | .postcard .title { display: block; height: 54px; line-height: 17px; font-size: 14px; color: #505050; padding: 0 10px; white-space: pre-line; overflow: hidden; } 95 | .postcard .title strong { color: black; } 96 | .postcard .title:hover { text-decoration: none; } 97 | .postcard .title .new { background-color: red; color: #FFF; font-size: 11px; padding: 2px 4px; border-radius: var(--radius); float: left; line-height: 11px; font-weight: bold; margin: 0 5px 0 0; } 98 | .postcard .user { font-size: 11px; padding: 0 10px; color: gray; } 99 | .postcard .date { font-size: 12px; color: gray; padding: 0 10px 10px; } 100 | 101 | .tilecard { background-color: white; border: 1px solid #E0E0E0; } 102 | .tilecard .image { display: block; margin-bottom: 10px; cursor: pointer; position: relative; border-bottom: 1px solid #E0E0E0; } 103 | .tilecard .image span { position: absolute; background-color: white; border-radius: 100px; width: 24px; height: 24px; line-height: 24px; text-align: center; right: 15px; top: 10px; border: 1px solid #D0D0D0; } 104 | .tilecard .name { font-size: 11px; padding: 0 10px; color: gray; height: 14px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } 105 | .tilecard .name a { color: #505050; } 106 | .tilecard .date { font-size: 11px; color: gray; padding: 1px 10px 5px 10px; } 107 | .tilecard .date b { color: $color; } 108 | .tilecard .date a { color: black; } 109 | 110 | .markdown { font-size: 16px; line-height: 22px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; color: #404040; overflow-x: hidden; } 111 | .markdown code { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; } 112 | .markdown h1 { cursor: default; margin: 0 0 20px; font-size: 32px; padding: 0 0 10px; border-bottom: 1px solid #E0E0E0; color: black; font-family: Arial; font-weight: normal; line-height: 32px; } 113 | .markdown h2 { cursor: default; margin: 30px 0 18px; font-weight: bold; font-size: 25px; padding: 0 0 8px; border-bottom: 1px solid #E0E0E0; color: black; line-height: 28px; font-family: Arial; } 114 | .markdown h3 { cursor: default; margin: 30px 0 10px; font-size: 20px; padding: 0 0 5px; color: black; font-weight: bold; line-height: 22px; font-family: Arial; } 115 | .markdown h4, .markdown h5 { cursor: default; margin: 20px 0 10px; font-size: 17px; color: black; font-weight: bold; line-height: 22px; font-family: Arial; background-color: #F0F0F0; padding: 4px 3px; border-radius: 3px; border-bottom: 1px solid #E0E0E0; } 116 | .markdown p { margin: 0 0 20px; padding-left: 0; padding-right: 0; } 117 | .markdown p code, .markdown li code, .markdown blockquote code, .markdown h1 code, .markdown h2 code, .markdown h3 code, .markdown h4 code, .markdown td code { background-color: #E0E0E0; padding: 1px 4px 2px; border-radius: 3px; font-size: 14px; } 118 | .markdown p code, .markdown li code, .markdown blockquote code, .markdown td code { border: 1px solid #E0E0E0; background-color: #F5F5F5; } 119 | .markdown pre { padding: 0; border-radius: 4px; margin: 20px 0; width: 100%; } 120 | .markdown pre code { padding: 10px; font-size: 14px; line-height: 18px; overflow-scrolling: touch; border-radius: 3px; } 121 | .markdown table { width: 100%; margin: 0 0 20px; } 122 | .markdown ul { margin: 0 0 20px 0; padding: 0 0 0 2em; } 123 | .markdown img { max-width: 100%; } 124 | .markdown th { background-color: #F8F8F8; } 125 | .markdown blockquote { color: black; margin: 10px 0 20px; padding: 20px; position: relative; background-color: #F0F0F0; border-radius: 3px; } 126 | .markdown blockquote:before { content: '\201C'; position: absolute; margin-left: -14px; margin-top: -13px; font: 45px 'PT Sans', sans-serif; color: #A0A0A0; } 127 | 128 | .markdown hr { border-color: #E0E0E0; border-width: 2px; } 129 | .markdown h1 code { font-size: 28px; font-weight: normal; background-color: #DAE8F8; } 130 | .markdown h2 code { font-size: 23px; font-weight: normal; background-color: #DAE8F8; } 131 | .markdown h3 code { font-size: 18px; font-weight: normal; background-color: #DAE8F8; } 132 | .markdown h4 code { font-size: 17px; font-weight: normal; background-color: #DAE8F8; } 133 | .markdown a code { background-color: #DAE8F8; text-decoration: none !important; border-color: #d5dfeb; } 134 | .markdown section { padding: 15px 20px; border: 2px solid #f7901e; background-color: rgba(247,144,30,0.1); margin: 10px 0 20px; border-radius: 3px; } 135 | 136 | .markdown .showsecret { cursor: pointer; display: block; padding: 8px 15px; user-select: none; } 137 | .markdown .showsecret b { margin-left: 8px; } 138 | .markdown .showsecret .pull-right { margin: 2px 0 0; } 139 | .markdown .secret { padding: 0; border-radius: 2px; border: 1px solid #E0E0E0; } 140 | .markdown .secret > div { padding: 15px 5px 1px; border-radius: 0 0 2px 2px; background-color: #F8F8F8; } 141 | 142 | .markdown .gallery { vertical-align: top; margin: 0 15px 15px 0; border: 1px solid #E0E0E0; width: 22%; cursor: pointer; border-radius: 2px; } 143 | .markdown-similar { border: 2px solid #E0E0E0; border-radius: 3px; padding: 20px 20px 0; background-color: #F8F8F8; margin-top: 20px; font-size: 16px; font-family: Arial; } 144 | .markdown-similar h3 { margin-top: 0; } 145 | .markdown-similar li span { float: right; } 146 | .markdown-similar li b { background-color: red; color: #FFF; font-size: 11px; padding: 2px 4px; border-radius: var(--radius); float: left; line-height: 11px; font-weight: bold; margin: 3px 5px 0 0; } 147 | .markdown-similar li a { margin-right: 50px; display: block; } 148 | 149 | .video { position: relative; padding-bottom: 56.25%; padding-top: 25px; height: 0; margin-bottom: 15px; } 150 | .video iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } 151 | 152 | .keyvalue { font-size: 12px; border-bottom: 1px solid #E0E0E0; padding: 2px 0; } 153 | .keyvalue .key { float: left; width: 80px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } 154 | .keyvalue .value { margin-left: 80px; text-align: right; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } 155 | 156 | .pagination { padding: 15px 0; text-align: center; } 157 | .pagination nav { margin: 10px 0 15px; } 158 | .pagination nav a { position: relative; display: inline-block; width: 30px; line-height: 30px; text-align: center; font-size: 14px; border-radius: 2px; background-color: white; margin: 0 2px; color: $color; } 159 | .pagination nav a:hover { text-decoration: none; background-color: $color; color: white; } 160 | .pagination nav .selected { background-color: $color; color: white; font-weight: bold; } 161 | 162 | @media(max-width: 980px) { 163 | .body { border: 0; } 164 | header .br { border: 0; } 165 | header .search { border: 1px solid #E0E0E0; margin: 15px 0 5px; border-radius: 2px; } 166 | .panel { border-right: 0; padding-right: 0; min-height: auto; border-top: 1px solid #E0E0E0; } 167 | header nav { height: auto; line-height: auto; padding: 10px 0; } 168 | header nav a { display: block; margin: 0; line-height: 28px; margin-bottom: 2px; padding: 2px 5px; border-radius: 2px; border: 0; } 169 | header nav a:hover, header nav a.selected { text-decoration: none; background-color: $color; color: white; } 170 | } 171 | 172 | .ui-imageviewer { position: fixed; left: 0; top: 0; bottom: 0; right: 0; z-index: 1000; background-color: rgba(240,240,240,0.9); } 173 | .ui-imageviewer img { border-radius: 4px; box-shadow: 0 0 10px rgba(0,0,0,0.05); } 174 | .ui-imageviewer-loading { position: absolute; left: 0; top: 45px; right: 0; bottom: 0; background-color: rgba(255,255,255,0.9); z-index: 2; } 175 | .ui-imageviewer-loading > div { background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzgiIGhlaWdodD0iMzgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IHgxPSI4LjA0MiUiIHkxPSIwJSIgeDI9IjY1LjY4MiUiIHkyPSIyMy44NjUlIiBpZD0iYSI+PHN0b3Agc3RvcC1jb2xvcj0iI0EwQTBBMCIgc3RvcC1vcGFjaXR5PSIwIiBvZmZzZXQ9IjAlIi8+PHN0b3Agc3RvcC1jb2xvcj0iI0EwQTBBMCIgc3RvcC1vcGFjaXR5PSIuNjMxIiBvZmZzZXQ9IjYzLjE0NiUiLz48c3RvcCBzdG9wLWNvbG9yPSIjQTBBMEEwIiBvZmZzZXQ9IjEwMCUiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxIDEpIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0zNiAxOGMwLTkuOTQtOC4wNi0xOC0xOC0xOCIgc3Ryb2tlPSJ1cmwoI2EpIiBzdHJva2Utd2lkdGg9IjIiPjxhbmltYXRlVHJhbnNmb3JtIGF0dHJpYnV0ZU5hbWU9InRyYW5zZm9ybSIgdHlwZT0icm90YXRlIiBmcm9tPSIwIDE4IDE4IiB0bz0iMzYwIDE4IDE4IiBkdXI9IjAuOXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIi8+PC9wYXRoPjxjaXJjbGUgZmlsbD0iI0EwQTBBMCIgY3g9IjM2IiBjeT0iMTgiIHI9IjEiPjxhbmltYXRlVHJhbnNmb3JtIGF0dHJpYnV0ZU5hbWU9InRyYW5zZm9ybSIgdHlwZT0icm90YXRlIiBmcm9tPSIwIDE4IDE4IiB0bz0iMzYwIDE4IDE4IiBkdXI9IjAuOXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIi8+PC9jaXJjbGU+PC9nPjwvc3ZnPg==) no-repeat 50% 50%; background-size: 80px 80px; width: 80px; height: 80px; position: absolute; left: 50%; top: 50%; margin: -40px 0 0 -40px; } 176 | .ui-imageviewer-header { height: 45px; border-bottom: 1px solid #E0E0E0; background-color: white; } 177 | .ui-imageviewer-header > div { line-height: 16px; float: left; margin: 7px 0 0 15px; } 178 | .ui-imageviewer-header .help { margin-top: 0; } 179 | .ui-imageviewer-header button { float: right; font-size: 16px; background-color: transparent; border: 0; margin: 0; width: 45px; height: 44px; border-left: 1px solid #E0E0E0; cursor: pointer; color: red; } 180 | .ui-imageviewer-header button:hover { background-color: #F8F8F8; } 181 | .ui-imageviewer-buttons { position: absolute; left: 0; right: 0; top: 45px; bottom: 0; } 182 | .ui-imageviewer-buttons button { background-color: white; border: 1px solid white; border-radius: 3px; width: 35px; height: 35px; position: absolute; top: 50%; margin-top: -17px; box-shadow: 0 2px 5px rgba(0,0,0,0.07); } 183 | .ui-imageviewer-buttons button:hover { background-color: #E0E0E0; border-color: #E0E0E0; } 184 | .ui-imageviewer-buttons button:disabled { color: #A0A0A0; cursor: not-allowed; border-color: #F8F8F8; background-color: #F8F8F8; opacity: 0.3; } 185 | .ui-imageviewer-buttons button[name='prev'] { left: 15px; } 186 | .ui-imageviewer-buttons button[name='next'] { right: 15px; } 187 | .ui-imageviewer-viewer { display: table; width: 100%; } 188 | .ui-imageviewer-cell { display: table-cell; width: 100%; height: 100%; vertical-align: middle; text-align: center; } 189 | 190 | .ui-tiledetail { position: fixed; left: 0; right: 0; top: 0; bottom: 0; z-index: 10; background-color: rgba(0,0,0,0.8); } 191 | .ui-tiledetail-noscroll { overflow: hidden; } 192 | .ui-tiledetail-container { position: absolute; background-color: white; width: 900px; height: 600px; box-shadow: 5px 5px 40px rgba(0,0,0,0.4); top: 50%; left: 50%; margin: -300px 0 0 -450px; } 193 | .ui-tiledetail-image { width: 600px; height: 600px; float: left; } 194 | .ui-tiledetail-body { margin-left: 600px; border-left: 1px solid #E0E0E0; height: 600px; overflow: hidden; } 195 | .ui-tiledetail-body .meta { background-color: #F8F8F8; padding: 15px 15px 12px; line-height: 18px; border-bottom: 1px solid #E0E0E0; font-size: 12px; } 196 | .ui-tiledetail-body .meta img { width: 35px; border-radius: 100px; float: left; margin-right: 10px; } 197 | .ui-tiledetail-body .meta a { color: black; } 198 | .ui-tiledetail-body .meta .date { font-size: 12px; margin: 1px 0 0; color: gray; line-height: 14px; } 199 | .ui-tiledetail-body .keyvalue { border-bottom: 0; } 200 | .ui-tiledetail-body .meta .date i { margin-right: 5px; font-size: 12px; vertical-align: top; padding: 2px 0 0; } 201 | .ui-tiledetail-body .text { font-size: 16px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; padding: 15px; word-wrap: break-word; } 202 | .ui-tiledetail-body .url { border-top: 1px solid #E0E0E0; padding: 10px 0; } 203 | 204 | .ui-tiledetail-controls { position: relative; } 205 | .ui-tiledetail-controls button { position: absolute; background: transparent; border: 0; font-size: 30px; color: white; top: 270px; } 206 | .ui-tiledetail-controls button[name='prev'] { left: -50px; } 207 | .ui-tiledetail-controls button[name='next'] { right: -50px; } 208 | 209 | @media(min-width:768px) and (max-width:991px) { 210 | .ui-tiledetail-container { width: 600px; height: 400px; margin: -200px 0 0 -300px; } 211 | .ui-tiledetail-image { width: 400px; height: 400px; } 212 | .ui-tiledetail-body { margin-left: 400px; height: 400px; } 213 | .ui-tiledetail-body .text { font-size: 13px; line-height: 16px; } 214 | .ui-tiledetail-controls button { top: 180px; } 215 | } 216 | 217 | @media(max-width:768px) { 218 | .ui-tiledetail-container { width: 100%; margin: 0; left: 0; top: 0; z-index: 1; height: 100%; } 219 | .ui-tiledetail-image { width: 100%; height: auto; float: none; } 220 | .ui-tiledetail-body { margin-left: 0; height: auto; width: auto; } 221 | .ui-tiledetail-body .text { font-size: 13px; line-height: 16px; } 222 | .ui-tiledetail-controls button { display: none; } 223 | } 224 | 225 | .markdown-block { margin: 0; } 226 | .markdown-block:last-child { margin-bottom: 15px; } 227 | .markdown-block > div { padding: 10px 10px 1px; border: 1px solid #E0E0E0; margin: 10px 0; border-radius: var(--radius); } 228 | .markdown-block > div > *:last-child { margin-bottom: 10px; } 229 | .markdown-showblock { cursor: pointer; display: block; padding: 3px 0; user-select: none; border-bottom: 1px solid #E0E0E0; } 230 | .markdown-showblock i { width: 16px; font-size: 12px; padding: 6px 0 0; float: left; } 231 | .markdown-showblock-visible { border-bottom: 0; } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/totaljs/blogengine/644065c43bcb13d522bc8c254556b38ae8bb3767/public/favicon.ico -------------------------------------------------------------------------------- /public/js/default.js: -------------------------------------------------------------------------------- 1 | function resizeforce() { 2 | 3 | var w = WIDTH(); 4 | if (w === 'xs' || w === 'sm') 5 | return; 6 | 7 | var el = $('.body'); 8 | if (el.length) { 9 | var h = WH - el.offset().top - 200; 10 | el.css('min-height', h); 11 | $('.panel').css('min-height', el.height()); 12 | } else 13 | setTimeout(resizeforce, 500); 14 | } 15 | 16 | $(W).on('resize', resizeforce); 17 | ON('ready', function() { 18 | resizeforce(); 19 | setTimeout(resizeforce, 1000); 20 | setTimeout(resizeforce, 3000); 21 | setTimeout(resizeforce, 5000); 22 | }); 23 | 24 | function tiledetail(id) { 25 | if (id instanceof jQuery) 26 | id = id.attrd('id'); 27 | 28 | id = id.replace('tile', ''); 29 | 30 | if (id.length > 25 || !(/^\d+/).test(id)) 31 | return; 32 | 33 | location.hash = 'tile' + id; 34 | setTimeout2('tiledetail', function(id) { 35 | AJAX('GET /api/tiles/' + id, function(response) { 36 | SETTER('tiledetail/show', response, tiledetail); 37 | }); 38 | }, 100, null, id); 39 | } 40 | 41 | $(W).on('hashchange', function() { 42 | if (location.hash.length > 10) 43 | tiledetail(location.hash.substring(1)); 44 | }); 45 | 46 | if (location.hash && location.hash.length > 10) 47 | tiledetail(location.hash.substring(1)); 48 | -------------------------------------------------------------------------------- /public/js/ui.js: -------------------------------------------------------------------------------- 1 | COMPONENT('aselected', function(self, config) { 2 | 3 | self.readonly(); 4 | 5 | self.make = function() { 6 | self.refresh(); 7 | ON('location', self.refresh); 8 | }; 9 | 10 | self.refresh = function() { 11 | var arr = self.find('a'); 12 | var url = location.pathname; 13 | for (var i = 0; i < arr.length; i++) { 14 | var el = $(arr[i]); 15 | var href = el.attr('href'); 16 | var selected = config.strict ? url === href : url.indexOf(href) !== -1; 17 | el.tclass('selected', selected); 18 | } 19 | }; 20 | }); 21 | 22 | COMPONENT('lazyimages', function(self) { 23 | 24 | var is = null; 25 | var regtest = /[?./]/; 26 | 27 | self.readonly(); 28 | self.singleton(); 29 | 30 | self.make = function() { 31 | is = true; 32 | $(W).on('scroll', self.refresh); 33 | setTimeout(self.refresh, 1000); 34 | }; 35 | 36 | self.refresh = function() { 37 | setTimeout2(self.id, self.prepare, 200); 38 | }; 39 | 40 | self.released = self.refresh; 41 | self.setter = self.refresh; 42 | 43 | self.prepare = function() { 44 | var scroll = $(W).scrollTop(); 45 | var beg = scroll; 46 | var end = beg + WH; 47 | var off = 50; 48 | var arr = document.querySelectorAll('img[data-src]'); 49 | for (var i = 0; i < arr.length; i++) { 50 | var t = arr[i]; 51 | if (!t.$lazyload) { 52 | var src = t.getAttribute('data-src'); 53 | if (src && regtest.test(src)) { 54 | var el = $(t); 55 | var top = (is ? 0 : scroll) + el.offset().top; 56 | if ((top + off) >= beg && (top - off) <= end) { 57 | t.setAttribute('src', src); 58 | t.$lazyload = true; 59 | t.removeAttribute('data-src'); 60 | } 61 | } 62 | } 63 | } 64 | }; 65 | }); 66 | 67 | COMPONENT('imageviewer', 'selector:.img-viewer;container:body;loading:1', function(self, config) { 68 | 69 | var cls = 'ui-imageviewer'; 70 | var cls2 = '.' + cls; 71 | var isclosed = false; 72 | var isrendering = false; 73 | var events = {}; 74 | var current; 75 | 76 | events.keydown = function(e) { 77 | switch (e.which) { 78 | case 38: 79 | case 37: // prev 80 | self.find('button[name="prev"]').trigger('click'); 81 | break; 82 | case 32: // next 83 | case 39: 84 | case 40: 85 | self.find('button[name="next"]').trigger('click'); 86 | break; 87 | case 27: // close 88 | self.close(); 89 | break; 90 | } 91 | }; 92 | 93 | events.bind = function() { 94 | if (!events.is) { 95 | events.is = true; 96 | $(W).on('keydown', events.keydown); 97 | } 98 | }; 99 | 100 | events.unbind = function() { 101 | if (events.is) { 102 | events.is = false; 103 | $(W).off('keydown', events.keydown); 104 | } 105 | }; 106 | 107 | self.readonly(); 108 | self.blind(); 109 | self.singleton(); 110 | self.nocompile && self.nocompile(); 111 | 112 | self.make = function() { 113 | self.aclass(cls + ' hidden'); 114 | self.append('
Name
Dimension
'.format(cls)); 115 | self.resize(); 116 | 117 | $(W).on('resize', self.resize); 118 | 119 | $(document.body).on('click', config.selector, function() { 120 | var el = $(this); 121 | isclosed = false; 122 | self.show(el); 123 | }); 124 | 125 | self.event('click', 'button[name]', function() { 126 | var t = this; 127 | if (!t.disabled) { 128 | if (t.name === 'close') 129 | self.close(); 130 | else 131 | self.show($(this.$el)); 132 | } 133 | }); 134 | 135 | self.find('img').on('load', function() { 136 | isrendering = false; 137 | self.loading(false); 138 | }); 139 | }; 140 | 141 | self.close = function() { 142 | isclosed = true; 143 | isrendering = false; 144 | $('html,body').rclass(cls + '-noscroll'); 145 | self.aclass('hidden'); 146 | events.unbind(); 147 | current = null; 148 | }; 149 | 150 | self.loading = function(is) { 151 | 152 | if (!config.loading) 153 | return; 154 | 155 | var el = self.find(cls2 + '-loading'); 156 | if (is) { 157 | el.rclass('hidden', is); 158 | return; 159 | } 160 | 161 | setTimeout(function() { 162 | el.aclass('hidden'); 163 | }, 500); 164 | }; 165 | 166 | self.show = function(el) { 167 | 168 | if (isrendering || el == null || isclosed) 169 | return; 170 | 171 | var parent = el.closest(config.container); 172 | var arr = parent.find(config.selector).toArray(); 173 | var index = arr.indexOf(el[0]); 174 | var buttons = self.find(cls2 + '-buttons').find('button'); 175 | current = el; 176 | 177 | buttons[0].disabled = index === 0; // prev 178 | buttons[1].disabled = index >= arr.length - 1; // next 179 | 180 | if (!buttons[0].disabled) 181 | buttons[0].$el = arr[index - 1]; 182 | 183 | if (!buttons[1].disabled) 184 | buttons[1].$el = arr[index + 1]; 185 | 186 | self.loading(true); 187 | isrendering = true; 188 | 189 | var image = new Image(); 190 | //image.crossOrigin = 'anonymous'; 191 | image.src = el[0].src; 192 | image.onload = function() { 193 | 194 | var img = this; 195 | var ratio; 196 | 197 | var mw = WW - 10; 198 | var mh = WH - 85; 199 | 200 | if (img.width > img.height) 201 | ratio = mw / (img.width / 100); 202 | else 203 | ratio = mh / (img.height / 100); 204 | 205 | if (ratio > 100) 206 | ratio = 100; 207 | 208 | if (isclosed) 209 | return; 210 | 211 | events.bind(); 212 | self.find('img').attr('src', img.src).attr('width', img.width / 100 * ratio).attr('height', img.height / 100 * ratio); 213 | self.find('.help').html(img.width + 'x' + img.height + 'px'); 214 | self.find('b').html(el.attr('alt') || el.attr('title') || 'Unknown image'); 215 | self.rclass('hidden'); 216 | $('html,body').aclass(cls + '-noscroll'); 217 | }; 218 | }; 219 | 220 | self.resize = function() { 221 | var viewer = self.find(cls2 + '-viewer'); 222 | var loading = self.find(cls2 + '-loading'); 223 | var css = {}; 224 | css.height = WH - 45; 225 | css.width = WW; 226 | viewer.css(css); 227 | loading.css(css); 228 | current && setTimeout2(self.ID, function() { 229 | self.show(current); 230 | }, 500); 231 | }; 232 | 233 | }); 234 | 235 | COMPONENT('markdownpreview', 'showsecret:Show secret data;hidesecret:Hide secret data', function(self, config) { 236 | 237 | var cls = 'ui-markdownpreview'; 238 | var cls2 = '.' + cls; 239 | var elcache; 240 | var elbody; 241 | 242 | self.bindvisible(); 243 | self.readonly(); 244 | self.nocompile && self.nocompile(); 245 | 246 | self.make = function() { 247 | 248 | if (!config.html) { 249 | self.append('
'.format(cls)); 250 | elcache = self.find(cls2 + '-cache'); 251 | elbody = self.find(cls2 + '-body'); 252 | } 253 | 254 | self.event('click', '.showsecret', function() { 255 | var el = $(this); 256 | var next = el.next(); 257 | next.tclass('hidden'); 258 | var is = next.hclass('hidden'); 259 | var icons = el.find('i'); 260 | icons.eq(0).tclass('ti-unlock', !is).tclass('ti-lock', is); 261 | icons.eq(1).tclass('ti-angle-up', !is).tclass('ti-angle-down', is); 262 | el.find('b').html(config[(is ? 'show' : 'hide') + 'secret']); 263 | }); 264 | }; 265 | 266 | var markdown_id = function(value) { 267 | var end = ''; 268 | var beg = ''; 269 | if (value.charAt(0) === '<') 270 | beg = '-'; 271 | if (value.charAt(value.length - 1) === '>') 272 | end = '-'; 273 | return (beg + value.slug() + end).replace(/-{2,}/g, '-'); 274 | }; 275 | 276 | self.redraw = function(el) { 277 | 278 | el.find('.lang-secret').each(function() { 279 | var el = $(this); 280 | el.parent().replaceWith('
' + config.showsecret + '
'); 281 | }); 282 | 283 | el.find('.lang-video').each(function() { 284 | var t = this; 285 | if (t.$mdloaded) 286 | return; 287 | t.$mdloaded = 1; 288 | var el = $(t); 289 | var html = el.html(); 290 | if (html.indexOf('youtube') !== -1) 291 | el.parent().replaceWith('
'); 292 | else if (html.indexOf('vimeo') !== -1) 293 | el.parent().replaceWith('
'); 294 | }); 295 | 296 | el.find('.lang-barchart').each(function() { 297 | 298 | var t = this; 299 | if (t.$mdloaded) 300 | return; 301 | 302 | t.$mdloaded = 1; 303 | var el = $(t); 304 | var arr = el.html().split('\n').trim(); 305 | var series = []; 306 | var categories = []; 307 | var y = ''; 308 | 309 | for (var i = 0; i < arr.length; i++) { 310 | var line = arr[i].split('|').trim(); 311 | for (var j = 1; j < line.length; j++) { 312 | if (i === 0) 313 | series.push({ name: line[j], data: [] }); 314 | else 315 | series[j - 1].data.push(+line[j]); 316 | } 317 | if (i) 318 | categories.push(line[0]); 319 | else 320 | y = line[0]; 321 | } 322 | 323 | var options = { 324 | chart: { 325 | height: 300, 326 | type: 'bar', 327 | }, 328 | yaxis: { title: { text: y }}, 329 | series: series, 330 | xaxis: { categories: categories, }, 331 | fill: { opacity: 1 }, 332 | }; 333 | 334 | var chart = new ApexCharts($(this).parent().empty()[0], options); 335 | chart.render(); 336 | }); 337 | 338 | el.find('.lang-linerchart').each(function() { 339 | 340 | var t = this; 341 | if (t.$mdloaded) 342 | return; 343 | t.$mdloaded = 1; 344 | 345 | var el = $(t); 346 | var arr = el.html().split('\n').trim(); 347 | var series = []; 348 | var categories = []; 349 | var y = ''; 350 | 351 | for (var i = 0; i < arr.length; i++) { 352 | var line = arr[i].split('|').trim(); 353 | for (var j = 1; j < line.length; j++) { 354 | if (i === 0) 355 | series.push({ name: line[j], data: [] }); 356 | else 357 | series[j - 1].data.push(+line[j]); 358 | } 359 | if (i) 360 | categories.push(line[0]); 361 | else 362 | y = line[0]; 363 | } 364 | 365 | var options = { 366 | chart: { 367 | height: 300, 368 | type: 'line', 369 | }, 370 | yaxis: { title: { text: y }}, 371 | series: series, 372 | xaxis: { categories: categories, }, 373 | fill: { opacity: 1 }, 374 | }; 375 | 376 | var chart = new ApexCharts($(this).parent().empty()[0], options); 377 | chart.render(); 378 | }); 379 | 380 | el.find('.lang-iframe').each(function() { 381 | 382 | var t = this; 383 | if (t.$mdloaded) 384 | return; 385 | t.$mdloaded = 1; 386 | 387 | var el = $(t); 388 | el.parent().replaceWith('
' + el.html().replace(/</g, '<').replace(/>/g, '>') + '
'); 389 | }); 390 | 391 | el.find('pre code').each(function(i, block) { 392 | var t = this; 393 | if (t.$mdloaded) 394 | return; 395 | t.$mdloaded = 1; 396 | hljs.highlightBlock(block); 397 | }); 398 | 399 | el.find('a').each(function() { 400 | var t = this; 401 | if (t.$mdloaded) 402 | return; 403 | t.$mdloaded = 1; 404 | var el = $(t); 405 | var href = el.attr('href'); 406 | var c = href.substring(0, 1); 407 | 408 | if (c !== '/' && c !== '#') 409 | el.attr('target', '_blank'); 410 | 411 | if (href === '#') { 412 | var beg = ''; 413 | var end = ''; 414 | var text = el.html(); 415 | if (text.substring(0, 1) === '<') 416 | beg = '-'; 417 | if (text.substring(text.length - 1) === '>') 418 | end = '-'; 419 | el.attr('href', '#' + (beg + markdown_id(el.text()) + end)); 420 | } 421 | }); 422 | 423 | el.find('.markdown-code').rclass('hidden'); 424 | 425 | if (config.headlines) { 426 | var arr = []; 427 | el.find('h1,h2,h3,h4').each(function() { 428 | var el = $(this); 429 | arr.push({ name: el.text(), id: el.attr('id'), tag: this.tagName.toLowerCase(), offset: el.offset().top }); 430 | }); 431 | SEEX(config.headlines, arr); 432 | } 433 | 434 | }; 435 | 436 | self.setter = function(value) { 437 | 438 | if (config.html) { 439 | self.redraw(self.element); 440 | return; 441 | } 442 | 443 | // Waits for markdown component 444 | if (!String.prototype.markdown) { 445 | setTimeout(self.setter, 500, value); 446 | return; 447 | } 448 | 449 | var cache = {}; 450 | var html = (value || '').markdown(); 451 | var vdom = $(html); 452 | 453 | elcache.empty(); 454 | 455 | elbody.find('.code').each(function() { 456 | var t = this; 457 | cache[t.getAttribute('data-checksum')] = t; 458 | elcache[0].appendChild(t); 459 | }); 460 | 461 | elbody.find('img').each(function() { 462 | var t = this; 463 | cache[t.getAttribute('data-checksum')] = t; 464 | elcache[0].appendChild(t); 465 | }); 466 | 467 | vdom.find('.code').each(function() { 468 | var t = this; 469 | var h = 'code' + HASH(t.outerHTML, true) + ''; 470 | if (cache[h]) 471 | $(t).replaceWith(cache[h]); 472 | else 473 | t.setAttribute('data-checksum', h); 474 | }).rclass('hidden'); 475 | 476 | vdom.find('img').each(function() { 477 | var t = this; 478 | var h = 'img' + HASH(t.outerHTML, true) + ''; 479 | if (cache[h]) 480 | $(t).replaceWith(cache[h]); 481 | else 482 | t.setAttribute('data-checksum', h); 483 | }); 484 | 485 | elbody.html(vdom); 486 | self.redraw(elbody); 487 | config.render && EXEC(self.makepath(config.render), elbody); 488 | }; 489 | 490 | self.readingtime = function() { 491 | var arr = self.find('h1,h2,h3,h4,h5,p,li'); 492 | var sum = 0; 493 | for (var i = 0; i < arr.length; i++) { 494 | var text = $(arr[i]).text(); 495 | var words = text.split(' '); 496 | for (var j = 0; j < words.length; j++) { 497 | var word = words[j]; 498 | sum += (word.length * 0.650) / 10; // Reading time for 10 characters word is 450 ms 499 | } 500 | } 501 | return (sum / 60) >> 0; 502 | }; 503 | 504 | }, ['https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css', 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/apexcharts/3.8.5/apexcharts.min.js']); 505 | 506 | COMPONENT('markdown', 'highlight:true;charts:false', function (self, config) { 507 | 508 | self.readonly(); 509 | self.singleton(); 510 | self.blind(); 511 | self.nocompile(); 512 | 513 | self.make = function() { 514 | 515 | if (config.highlight) { 516 | IMPORT('https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/highlight.min.js'); 517 | IMPORT('https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/styles/github.min.css'); 518 | } 519 | 520 | if (config.charts) 521 | IMPORT('https://cdn.componentator.com/apexcharts.min@310.js'); 522 | 523 | // Remove from DOM because Markdown is used as a String prototype and Tangular helper 524 | setTimeout(function() { 525 | self.remove(); 526 | }, 500); 527 | 528 | $(document).on('click', '.markdown-showsecret,.markdown-showblock', function() { 529 | var el = $(this); 530 | var next = el.next(); 531 | next.tclass('hidden'); 532 | var is = next.hclass('hidden'); 533 | var icons = el.find('i'); 534 | if (el.hclass('markdown-showsecret')) { 535 | icons.eq(0).tclass('ti-unlock', !is).tclass('ti-lock', is); 536 | icons.eq(1).tclass('ti-angle-up', !is).tclass('ti-angle-down', is); 537 | } else { 538 | icons.eq(0).tclass('ti-minus', !is).tclass('ti-plus', is); 539 | el.tclass('markdown-showblock-visible', !is); 540 | } 541 | el.find('b').html(el.attrd(is ? 'show' : 'hide')); 542 | }); 543 | }; 544 | 545 | (function Markdown() { 546 | 547 | var keywords = /\{.*?\}\(.*?\)/g; 548 | var linksexternal = /(https|http):\/\//; 549 | var format = /__.*?__|_.*?_|\*\*.*?\*\*|\*.*?\*|~~.*?~~|~.*?~/g; 550 | var ordered = /^[a-z|0-9]{1,3}\.\s|^-\s/i; 551 | var orderedsize = /^(\s|\t)+/; 552 | var code = /`.*?`/g; 553 | var encodetags = /<|>/g; 554 | var regdash = /-{2,}/g; 555 | var regicons = /(^|[^\w]):((fab|far|fas|fal|fad|fa|ti)\s(fa|ti)-)?[a-z-]+:([^\w]|$)/g; 556 | var regemptychar = /\s|\W/; 557 | var regtags = /<[^>]*>/g; 558 | 559 | var encode = function(val) { 560 | return '&' + (val === '<' ? 'lt' : 'gt') + ';'; 561 | }; 562 | 563 | function markdown_code(value) { 564 | return value ? ('' + value.substring(1, value.length - 1) + '') : ''; 565 | } 566 | 567 | function markdown_imagelinks(value) { 568 | 569 | if (!value) 570 | return ''; 571 | 572 | var end = value.lastIndexOf(')') + 1; 573 | var img = value.substring(0, end); 574 | var url = value.substring(end + 2, value.length - 1); 575 | var label = markdown_links(img); 576 | var footnote = label.substring(0, 13); 577 | 578 | if (footnote === '' + label + ''; 582 | } 583 | 584 | function markdown_table(value, align, ishead) { 585 | 586 | var columns = value.substring(1, value.length - 1).split('|'); 587 | var builder = ''; 588 | 589 | for (var i = 0; i < columns.length; i++) { 590 | var column = columns[i].trim(); 591 | if (column.charAt(0) != '-') { 592 | var a = align[i]; 593 | builder += '<' + (ishead ? 'th' : 'td') + (a && a !== 'left' ? (' class="' + a + '"') : '') + '>' + column + ''; 594 | } 595 | } 596 | 597 | return '' + builder + ''; 598 | } 599 | 600 | function markdown_links(value) { 601 | 602 | if (!value) 603 | return ''; 604 | 605 | var end = value.lastIndexOf(']'); 606 | var img = value.charAt(0) === '!'; 607 | var text = value.substring(img ? 2 : 1, end); 608 | var link = value.substring(end + 2, value.length - 1); 609 | 610 | // footnotes 611 | if ((/^#\d+$/).test(link)) { 612 | return (/^\d+$/).test(text) ? '{1}'.format(link.substring(1), text) : '{1}'.format(link.substring(1), text); 613 | } 614 | 615 | if (link.substring(0, 4) === 'www.') 616 | link = 'https://' + link; 617 | 618 | var nofollow = link.charAt(0) === '@' ? ' rel="nofollow"' : linksexternal.test(link) ? ' target="_blank"' : ''; 619 | return '' + text + ''; 620 | } 621 | 622 | function markdown_image(value) { 623 | 624 | var end = value.lastIndexOf(']'); 625 | var text = value.substring(2, end); 626 | var link = value.substring(end + 2, value.length - 1); 627 | var responsive = 1; 628 | var f = text.charAt(0); 629 | 630 | if (f === '+') { 631 | responsive = 2; 632 | text = text.substring(1); 633 | } else if (f === '-') { 634 | // gallery 635 | responsive = 3; 636 | text = text.substring(1); 637 | } 638 | 639 | return '' + text + ''; 640 | } 641 | 642 | function markdown_keywords(value) { 643 | var keyword = value.substring(1, value.indexOf('}')); 644 | var type = value.substring(value.lastIndexOf('(') + 1, value.lastIndexOf(')')); 645 | return '{1}'.format(type, keyword); 646 | } 647 | 648 | function markdown_links2(value) { 649 | value = value.substring(4, value.length - 4); 650 | return '' + value + ''; 651 | } 652 | 653 | function markdown_format(value, index, text) { 654 | 655 | var p = text.charAt(index - 1); 656 | var n = text.charAt(index + value.length); 657 | 658 | if ((!p || regemptychar.test(p)) && (!n || regemptychar.test(n))) { 659 | 660 | var beg = ''; 661 | var end = ''; 662 | var tag; 663 | 664 | if (value.indexOf('*') !== -1) { 665 | tag = value.indexOf('**') === -1 ? 'em' : 'strong'; 666 | beg += '<' + tag + '>'; 667 | end = '' + end; 668 | } 669 | 670 | if (value.indexOf('_') !== -1) { 671 | tag = value.indexOf('__') === -1 ? 'u' : 'b'; 672 | beg += '<' + tag + '>'; 673 | end = '' + end; 674 | } 675 | 676 | if (value.indexOf('~') !== -1) { 677 | beg += ''; 678 | end = '' + end; 679 | } 680 | 681 | var count = value.charAt(1) === value.charAt(0) ? 2 : 1; 682 | return beg + value.substring(count, value.length - count) + end; 683 | } 684 | 685 | return value; 686 | } 687 | 688 | function markdown_id(value) { 689 | value = value.replace(regtags, ''); 690 | return value.slug().replace(regdash, '-'); 691 | } 692 | 693 | function markdown_icon(value) { 694 | 695 | var beg = -1; 696 | var end = -1; 697 | 698 | for (var i = 0; i < value.length; i++) { 699 | var code = value.charCodeAt(i); 700 | if (code === 58) { 701 | if (beg === -1) 702 | beg = i + 1; 703 | else 704 | end = i; 705 | } 706 | } 707 | 708 | var icon = value.substring(beg, end); 709 | if (icon.indexOf(' ') === -1) 710 | icon = 'ti ti-' + icon; 711 | return value.substring(0, beg - 1) + '' + value.substring(end + 1); 712 | } 713 | 714 | function markdown_urlify(str) { 715 | return str.replace(/(^|\s)+(((https?:\/\/)|(www\.))[^\s]+)/g, function(url, b, c) { 716 | var len = url.length; 717 | var l = url.charAt(len - 1); 718 | var f = url.charAt(0); 719 | if (l === '.' || l === ',') 720 | url = url.substring(0, len - 1); 721 | else 722 | l = ''; 723 | url = (c === 'www.' ? 'http://' + url : url).trim(); 724 | return (f.charCodeAt(0) < 40 ? f : '') + '[' + url + '](' + url + ')' + l; 725 | }); 726 | } 727 | 728 | function parseul(builder) { 729 | 730 | var ul = {}; 731 | var is = false; 732 | var currentindex = -1; 733 | var output = []; 734 | 735 | for (var i = 0; i < builder.length; i++) { 736 | 737 | var line = builder[i]; 738 | 739 | if (line.charAt(0) === '\0') { 740 | 741 | if (!is) 742 | currentindex = output.push('