├── robots.txt ├── static ├── images │ └── bing.ico ├── fonts │ ├── iconfont.eot │ ├── iconfont.ttf │ ├── iconfont.woff │ └── iconfont.svg ├── js │ ├── mobile.js │ ├── mine-instantclick.js │ ├── baidu-analysis.js │ ├── like.js │ ├── html5shiv.js │ ├── device.js │ ├── progressively.js │ ├── js.cookie.js │ ├── instantclick.min.js │ ├── respond.js │ └── Valine.min.js ├── css │ ├── progressively.css │ ├── about.css │ ├── iconfont.css │ ├── common.css │ ├── main.css │ └── vue.css └── about.html ├── .gitignore ├── readme.me ├── routes ├── users.js ├── photo.js ├── weibo.js ├── index.js └── v1.js ├── views ├── layout.pug ├── error.pug ├── common_headers.pug ├── error_layout.pug ├── about.pug ├── detail.pug └── index.pug ├── utils ├── mailUtils.js ├── commonUtils.js ├── qiniuUtils.js ├── bingUtils.js ├── dbUtils.js └── weiboUtils.js ├── License ├── package.json ├── api.md ├── configs └── config.js ├── bin └── www └── app.js /robots.txt: -------------------------------------------------------------------------------- 1 | User-Agent: * 2 | Allow: / -------------------------------------------------------------------------------- /static/images/bing.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xCss/bing/HEAD/static/images/bing.ico -------------------------------------------------------------------------------- /static/fonts/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xCss/bing/HEAD/static/fonts/iconfont.eot -------------------------------------------------------------------------------- /static/fonts/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xCss/bing/HEAD/static/fonts/iconfont.ttf -------------------------------------------------------------------------------- /static/fonts/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xCss/bing/HEAD/static/fonts/iconfont.woff -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | db.json 4 | *.log 5 | node_modules/ 6 | static/css/style.css 7 | static/css/*.map -------------------------------------------------------------------------------- /static/js/mobile.js: -------------------------------------------------------------------------------- 1 | var e=/(iphone|ipod|ipad|android|iemobile|blackberry|symbianos|windows phone|meego)/.test(navigator.userAgent.toLowerCase()); 2 | -------------------------------------------------------------------------------- /readme.me: -------------------------------------------------------------------------------- 1 | 深感遗憾地告诉各位,因黑客侵袭导致网站数据,包括备份文件的一并丧失,恰如一场突如其来的飓风,不得不宣告必应壁纸服务从今日起告别互联网的舞台。 2 | 3 | 在此向各位表达我最诚挚的感谢,感激你们的不离不弃。愿在未来的日子里,有缘江湖相见。 4 | 5 | 2023-11-21 -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /static/js/mine-instantclick.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | InstantClick.on('change', function(isInitialLoad) { 3 | if (isInitialLoad === false) { 4 | if (typeof _hmt !== 'undefined') // support 百度统计 5 | _hmt.push(['_trackPageview', location.pathname + location.search]); 6 | if (typeof ga !== 'undefined') // support google analytics 7 | ga('send', 'pageview', location.pathname + location.search); 8 | } 9 | }); 10 | InstantClick.init(); 11 | }); -------------------------------------------------------------------------------- /views/layout.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | include common_headers.pug 5 | - var desc = page.desc || '必应每日壁纸:风景, 这边独好。'; 6 | title #{page.curr} - #{title} 7 | meta(name="keywords",content="必应壁纸,必应高清壁纸打包下载,必应壁纸合集,必应每日壁纸下载,必应壁纸接口,必应壁纸官方下载,Bing壁纸,微软bing每日壁纸,必应每日壁纸下载,Bing壁纸api,高清壁纸分享,bing壁纸官方下载,必应高清壁纸,必应壁纸打包下载") 8 | meta(name="description",content=desc) 9 | link(rel='stylesheet', href='/static/css/common.min.css') 10 | link(rel='stylesheet', href='/static/css/main.min.css') 11 | link(rel='stylesheet',href="/static/css/progressively.min.css") 12 | 13 | body 14 | block content 15 | 16 | 17 | -------------------------------------------------------------------------------- /views/error.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang="en") 3 | head 4 | include common_headers.pug 5 | title 404 | 必应壁纸,必应每日壁纸,必应高清壁纸 6 | meta(name="keywords",content="必应壁纸,必应高清壁纸打包下载,必应壁纸合集,必应每日壁纸下载,必应壁纸接口,必应壁纸官方下载,Bing壁纸,微软bing每日壁纸,必应每日壁纸下载,Bing壁纸api,高清壁纸分享,bing壁纸官方下载,必应高清壁纸,必应壁纸打包下载") 7 | link(rel='stylesheet', href='/static/css/common.css') 8 | link(rel='stylesheet', href='/static/css/main.css') 9 | body 10 | 11 | script(src="/static/js/elm.js") 12 | script. 13 | Elm.Main.fullscreen({ 14 | randomSeed: Date.now(), 15 | imagesUrl: 'img', 16 | devicePixelRatio: window.devicePixelRatio 17 | }); -------------------------------------------------------------------------------- /static/css/progressively.css: -------------------------------------------------------------------------------- 1 | /* 2 | * progressively 1.1.2 3 | * https://github.com/thinker3197/progressively 4 | * @license MIT licensed 5 | * 6 | * Copyright (C) 2016-17 Ashish 7 | */ 8 | 9 | .progressive { 10 | overflow: hidden; 11 | position: relative; 12 | background: #efefef; 13 | } 14 | 15 | .progressive__img { 16 | width: 100%; 17 | height: 100%; 18 | transform: translateZ(0); 19 | } 20 | 21 | .progressive--not-loaded { 22 | filter: blur(30px); 23 | } 24 | 25 | .progressive--is-loaded { 26 | filter: blur(20px); 27 | animation: sharpen 0.5s both; 28 | } 29 | 30 | @keyframes sharpen { 31 | from { 32 | filter: blur(20px); 33 | } 34 | to { 35 | filter: blur(0px); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /static/js/baidu-analysis.js: -------------------------------------------------------------------------------- 1 | if (!/^http:\/\/localhost/.test(location.href)) { 2 | var _hmt = _hmt || []; 3 | var hm = document.createElement("script"); 4 | hm.src = "//hm.baidu.com/hm.js?667639aad0d4654c92786a241a486361"; 5 | var s = document.getElementsByTagName("script")[0]; 6 | s.parentNode.insertBefore(hm, s); 7 | 8 | var bp = document.createElement('script'); 9 | var curProtocol = window.location.protocol.split(':')[0]; 10 | if (curProtocol === 'https') { 11 | bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; 12 | } 13 | else { 14 | bp.src = 'http://push.zhanzhang.baidu.com/push.js'; 15 | } 16 | var s = document.getElementsByTagName("script")[0]; 17 | s.parentNode.insertBefore(bp, s); 18 | } 19 | -------------------------------------------------------------------------------- /utils/mailUtils.js: -------------------------------------------------------------------------------- 1 | var nodemailer = require('nodemailer'); 2 | module.exports = { 3 | send: function(data) { 4 | var user = 'bugs@xone.me', 5 | pass = 'kpuzaltbtylobibh'; 6 | var smtpTransport = nodemailer.createTransport("SMTP", { 7 | service: "QQ", 8 | auth: { 9 | user: user, 10 | pass: pass 11 | } 12 | }); 13 | 14 | smtpTransport.sendMail({ 15 | from: 'Bugs<' + user + '>', 16 | to: '', 17 | subject: data.title || new Date().toLocaleString(), 18 | html: '
' + data.message + '
' + data.stack + '
' 19 | }, function(err, res) { 20 | if (err) { 21 | console.log(err); 22 | } else { 23 | console.log(res); 24 | } 25 | }); 26 | } 27 | }; -------------------------------------------------------------------------------- /static/js/like.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | var likes = Cookies.get('likes') || ''; 4 | likes = likes.replace(/\,+/g,'_').split('_'); 5 | Cookies.remove('likes'); 6 | Cookies.set('likes', likes.join('_'),{ expires: 730 }); 7 | for (var i = 0, len = likes.length; i < len; i++) { 8 | $(".ctrl.heart[photo='"+likes[i]+"']").addClass('active'); 9 | } 10 | var click = 'ontouchstart' in window ? 'touchstart' : 'click'; 11 | $(document).off(click,'.ctrl.heart').on(click, '.ctrl.heart', function(e) { 12 | var _this = $(this); 13 | var num = Number(_this.attr('likes')); 14 | var rid = _this.attr('photo'); 15 | if (_this.hasClass('active')) return; 16 | $.get('/photo/'+rid+'?force=like', function(data, state) { 17 | likes.push(rid); 18 | Cookies.set('likes', likes.join('_'),{ expires: 730 }); 19 | _this.addClass('active').children('em').html(num + 1); 20 | }); 21 | }); 22 | }); -------------------------------------------------------------------------------- /views/common_headers.pug: -------------------------------------------------------------------------------- 1 | // 2 | BingPictrues by xCss 3 | https://github.com/xCss/bing | xioveliu@gamil.com 4 | meta(charset="UTF-8") 5 | meta(name="author",content="xCss") 6 | meta(name="mobile-web-app-capable",content="yes") 7 | meta(name="apple-mobile-web-app-capable",content="yes") 8 | meta(name="apple-mobile-web-app-status-bar-style",content="black") 9 | meta(content="telephone=no,email=no",name="format-detection") 10 | meta(http-equiv="X-UA-Compatible",content="IE=Edge,Chrome=1") 11 | meta(name="renderer",content="webkit") 12 | meta(property="wb:webmaster",content="4513019856d2091e") 13 | meta(name="google-site-verification",content="fto43W1-4i099gqlMImLOannS1KjzlC5A-a1dnT0iVs") 14 | meta(name="viewport",content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no") 15 | link(rel="stylesheet" type="text/css" href="/static/css/iconfont.css") 16 | link(rel="icon",href="/static/images/bing.ico",sizes="32x32") 17 | script(src='/static/js/js.cookie.js') 18 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 xCss (xioveliu@gmail.com) and contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bing", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "supervisor node ./bin/www" 7 | }, 8 | "author": { 9 | "name": "xCss", 10 | "mail": "xioveliu@gmail.com" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/xCss/bing.git" 15 | }, 16 | "keywords": [ 17 | "Bing", 18 | "BingPicture API", 19 | "必应图片", 20 | "必应壁纸接口" 21 | ], 22 | "bugs": { 23 | "url": "https://github.com/xCss/bing/issues" 24 | }, 25 | "readme": "./readme.md", 26 | "homepage": "https://github.com/xCss/bing#readme", 27 | "dependencies": { 28 | "body-parser": "~1.15.1", 29 | "cookie-parser": "~1.4.3", 30 | "debug": "~2.2.0", 31 | "express": "~4.13.4", 32 | "express-flash": "0.0.2", 33 | "express-session": "^1.14.1", 34 | "helmet": "^2.3.0", 35 | "moment": "^2.18.1", 36 | "morgan": "~1.7.0", 37 | "mysql": "^2.11.1", 38 | "node-schedule": "^1.2.0", 39 | "nodemailer": "^0.7.1", 40 | "object-assign": "^4.1.0", 41 | "pug": "^2.0.0-beta6", 42 | "qiniu": "^7.1.0", 43 | "serve-favicon": "~2.3.0", 44 | "superagent": "^2.3.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /utils/commonUtils.js: -------------------------------------------------------------------------------- 1 | var mailUtils = require('./mailUtils'); 2 | module.exports = { 3 | /** 4 | * 公共转换函数 5 | * @err 错误信息 6 | * @res 响应信息 7 | * @callback 回调函数 8 | */ 9 | convert: function(err, res, callback) { 10 | try { 11 | if (!err && res.status === 200) { 12 | var body = null; 13 | if (res && res.text) { 14 | body = res.text; 15 | } 16 | if (typeof body === 'string') { 17 | try { 18 | body = JSON.parse(body); 19 | } catch (error) { 20 | throw new Error(error); 21 | } 22 | } 23 | if (body.error_code || body.error) { 24 | throw new Error(res); 25 | } else { 26 | callback && callback(body); 27 | } 28 | } else { 29 | throw new Error(res); 30 | } 31 | } catch (error) { 32 | // send mail 33 | //console.log(error.message); 34 | console.log(error); 35 | mailUtils.send(error); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /views/error_layout.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | // 5 | BingPictrues by xCss 6 | https://github.com/xCss/bing | xioveliu@gamil.com 7 | 8 | title= error.status 9 | meta('charset'="utf-8") 10 | meta(name="keywords",content="Bing,Bing壁纸,bing壁纸api,Bing最新壁纸,壁纸分享,壁纸下载,必应壁纸") 11 | meta(name="description",content="Bing每日壁纸:风景, 这边独好。") 12 | meta(name="viewport",content="width=device-width,user-scalable=no,initial-scale=1,minimum-scale=1,maximum-scale=1") 13 | meta(name="author",content="xCss") 14 | meta(http-equiv="X-UA-Compatible",content="IE=edge,chrome=1") 15 | meta(name="renderer",content="webkit") 16 | meta(property="wb:webmaster",content="4513019856d2091e") 17 | link(rel="icon",href="/static/images/bing.ico",sizes="32x32") 18 | 21 | 24 | 27 | noscript 28 | link(rel="stylesheet",href="/static/css/skin/noscript.css") 29 | link(rel='stylesheet', href='/static/css/error.css') 30 | 31 | script(src='/static/js/error.js') 32 | 33 | body(onload="generateStars()") 34 | block content -------------------------------------------------------------------------------- /api.md: -------------------------------------------------------------------------------- 1 | 2 | # Bing Pictures Interface | 必应壁纸接口 3 | > :hammer: `Bing 壁纸 Api`重装上阵啦 :smile: http://bing.ioliu.cn 4 | 5 | ## 目前开放的壁纸接口: 6 | - `/v1{d,w,h,p,size,callback}` 返回今日的壁纸完整数据(`可选参数{d,w,h,p,size,callback}`): 7 | 8 | > 若指定参数`{w,h}` ,则直接返回图片 9 | 10 | |参数名|类型|是否必要|备注| 11 | |:----:|:---------:|:--------:|---| 12 | |d|`Int`|否|自今日起第`d`天前的数据| 13 | |w|`Int`|否|图片宽度| 14 | |h|`Int`|否|图片高度| 15 | |p|`Int`|否|`Page 页码`:第x页| 16 | |size|`Int`|否|`Size 条数`:每页条数| 17 | |callback|`String`|否|JSONP的回调函数名| 18 | 19 | - `/v1/rand{w,h,type,callback}` 返回随机的壁纸(`可选参数{w,h,type,callback}`): 20 | 21 | |参数名|类型|是否必要|备注| 22 | |:----:|:---------:|:--------:|---| 23 | |w|`Int`|否|图片宽度| 24 | |h|`Int`|否|图片高度| 25 | |type|`String`|否|返回值类型(`json`)| 26 | |callback|`String`|否|JSONP的回调函数名| 27 | 28 | - `/v1/blur{d,w,h,r}` 返回高斯模糊壁纸(`可选参数{d,w,h,r}`): 29 | 30 | |参数名|类型|是否必要|备注| 31 | |:----:|:---------:|:--------:|---| 32 | |d|`Int`|否|自今日起第`d`天前的数据| 33 | |w|`Int`|否|图片宽度| 34 | |h|`Int`|否|图片高度| 35 | |r|`Int`|否|模糊半径(`1~50`)| 36 | 37 | ### **:warning:** `高斯模糊`接口目前只支持指定分辨率(`w,h`)的图片,具体分辨率如下: 38 | ```js 39 | /** 40 | * 已知分辨率 41 | */ 42 | resolutions: [ 43 | '1920x1200', 44 | '1920x1080', 45 | '1366x768', 46 | '1280x768', 47 | '1024x768', 48 | '800x600', 49 | '800x480', 50 | '768x1280', 51 | '720x1280', 52 | '640x480', 53 | '480x800', 54 | '400x240', 55 | '320x240', 56 | '240x320' 57 | ] 58 | ``` 59 | 60 | 欢迎点评→[issue](https://github.com/xCss/bing/issues) -------------------------------------------------------------------------------- /configs/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | weibo: { 3 | CLIENT_ID: '1833831541', 4 | CLIENT_SECRET: '204239c8f3e37e59f2d3eb95de2811a9', 5 | ACCESS_TOKEN: '', 6 | MASTER_ACCESS_TOKEN: '', 7 | MASTER_UID: '5893653736', 8 | USER_UID: '' 9 | }, 10 | mysql_dev: { 11 | host: process.env.mysql_host, 12 | database: process.env.mysql_database, 13 | user: process.env.mysql_user, 14 | password: process.env.mysql_password, 15 | port: '3306', 16 | connectionLimit: 10, 17 | supportBigNumbers: true, 18 | multipleStatements: true, 19 | insecureAuth: true 20 | }, 21 | //disabled: [process.env.disabled.split(',')], 22 | /** 23 | * 已知分辨率 24 | */ 25 | resolutions: [ 26 | '1920x1200', 27 | '1920x1080', 28 | '1366x768', 29 | '1280x768', 30 | '1080x1920', 31 | '1024x768', 32 | '800x600', 33 | '800x480', 34 | '768x1366', 35 | '768x1280', 36 | '768x1024', 37 | '720x1280', 38 | '640x480', 39 | '640x360', 40 | '480x800', 41 | '400x240', 42 | '320x240', 43 | '320x180', 44 | '240x400', 45 | '240x320', 46 | '240x240', 47 | '200x200', 48 | '150x150' 49 | ], 50 | 51 | global_link: function() { 52 | return Math.random() > 0.5 ? 'https://static.ioliu.cn' : 'https://bing-images.bitmoe.cn'; 53 | }, 54 | global_http: function() { 55 | return Math.random() > 0.5 ? 'http://static.ioliu.cn' : 'http://bing-images.bitmoe.cn'; 56 | } 57 | 58 | 59 | } -------------------------------------------------------------------------------- /static/css/about.css: -------------------------------------------------------------------------------- 1 | code { 2 | color: #1abc9c; 3 | } 4 | 5 | html { 6 | background: #eee; 7 | } 8 | 9 | body { 10 | width: 95%; 11 | max-width: 960px; 12 | background: #fff; 13 | margin: 2em auto 0; 14 | padding-top: 1em; 15 | border: 1px solid #ddd; 16 | border-width: 0 1px; 17 | } 18 | 19 | pre { 20 | white-space: pre-wrap; 21 | } 22 | 23 | i.serif { 24 | text-transform: lowercase; 25 | color: #1abc9c; 26 | } 27 | 28 | :-moz-any(h1, h2, h3, h4, h5, h5) i.serif { 29 | text-transform: capitalize; 30 | } 31 | 32 | i.serif:hover { 33 | color: inherit; 34 | } 35 | 36 | #wrapper { 37 | padding: 2% 2%; 38 | position: relative; 39 | } 40 | 41 | #tagline { 42 | color: #999; 43 | font-size: 1em; 44 | margin: -2em 0 2em; 45 | padding-bottom: 2em; 46 | border-bottom: 3px double #eee; 47 | } 48 | 49 | #fork { 50 | position: fixed; 51 | top: 0; 52 | right: 0; 53 | _position: absolute; 54 | } 55 | 56 | #table { 57 | margin-bottom: 2em; 58 | color: #888; 59 | } 60 | 61 | #github { 62 | position: absolute; 63 | top: 1em; 64 | } 65 | 66 | #github iframe { 67 | display: inline; 68 | margin-right: 1em; 69 | } 70 | 71 | @media only screen and (max-width: 640px) { 72 | body { 73 | width: 100%; 74 | border: none; 75 | } 76 | table { 77 | word-break: break-all; 78 | word-wrap: break-word; 79 | font-size: 12px; 80 | } 81 | .typo table th, 82 | .typo table td, 83 | .typo-table th, 84 | .typo-table td .typo table caption { 85 | padding: 0.5em; 86 | } 87 | #fork { 88 | display: none; 89 | } 90 | } 91 | 92 | .icon { 93 | font-size: 1em; 94 | } 95 | 96 | #QR { 97 | font-size: 0; 98 | } 99 | 100 | #wechat, 101 | #alipay { 102 | font-size: 14px; 103 | width: 50%; 104 | box-sizing: border-box; 105 | text-align: center; 106 | } 107 | 108 | #wechat_qr, 109 | #alipay_qr { 110 | display: inline-block; 111 | max-width: 150px; 112 | } -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | /** 7 | * 设置时区 8 | */ 9 | process.env.TZ = "Asia/Shanghai"; 10 | 11 | var app = require('../app'); 12 | var debug = require('debug')('API:server'); 13 | var http = require('http'); 14 | /** 15 | * Get port from environment and store in Express. 16 | */ 17 | 18 | var port = normalizePort(process.env.PORT || '999'); 19 | app.set('port', port); 20 | 21 | /** 22 | * Create HTTP server. 23 | */ 24 | 25 | var server = http.createServer(app); 26 | 27 | /** 28 | * Listen on provided port, on all network interfaces. 29 | */ 30 | 31 | server.listen(port); 32 | server.on('error', onError); 33 | server.on('listening', onListening); 34 | 35 | /** 36 | * Normalize a port into a number, string, or false. 37 | */ 38 | 39 | function normalizePort(val) { 40 | var port = parseInt(val, 10); 41 | 42 | if (isNaN(port)) { 43 | // named pipe 44 | return val; 45 | } 46 | 47 | if (port >= 0) { 48 | // port number 49 | return port; 50 | } 51 | 52 | return false; 53 | } 54 | 55 | /** 56 | * Event listener for HTTP server "error" event. 57 | */ 58 | 59 | function onError(error) { 60 | if (error.syscall !== 'listen') { 61 | throw error; 62 | } 63 | 64 | var bind = typeof port === 'string' ? 65 | 'Pipe ' + port : 66 | 'Port ' + port; 67 | 68 | // handle specific listen errors with friendly messages 69 | switch (error.code) { 70 | case 'EACCES': 71 | console.error(bind + ' requires elevated privileges'); 72 | process.exit(1); 73 | break; 74 | case 'EADDRINUSE': 75 | console.error(bind + ' is already in use'); 76 | process.exit(1); 77 | break; 78 | default: 79 | throw error; 80 | } 81 | } 82 | 83 | /** 84 | * Event listener for HTTP server "listening" event. 85 | */ 86 | 87 | function onListening() { 88 | var addr = server.address(); 89 | var bind = typeof addr === 'string' ? 90 | 'pipe ' + addr : 91 | 'port ' + addr.port; 92 | debug('Listening on ' + bind); 93 | } -------------------------------------------------------------------------------- /static/js/html5shiv.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.6.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup main mark meter nav output progress section summary time video",version:"3.6.2",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment(); 8 | for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d= 2) { 57 | break; 58 | } 59 | _ver.push(ver[i]); 60 | } 61 | _ver = _ver.join("."); 62 | DEVICE.ver = _ver; 63 | 64 | DEVICE.isMobile = (DEVICE.isAndroid || DEVICE.isIpad || DEVICE.isIphone); 65 | 66 | var p = navigator.platform; 67 | var win = p.indexOf("Win") === 0; 68 | var mac = p.indexOf("Mac") === 0; 69 | var x11 = (p == "X11") || (p.indexOf("Linux") === 0); 70 | 71 | DEVICE.isPc = (win || mac || x11); 72 | DEVICE.isMobile = !(win || mac || x11); 73 | DEVICE.isMac = mac; 74 | DEVICE.isWin = win; 75 | DEVICE.isLinux = x11; 76 | 77 | window.DEVICE = DEVICE; 78 | })(); -------------------------------------------------------------------------------- /utils/qiniuUtils.js: -------------------------------------------------------------------------------- 1 | var qiniu = require('qiniu'); 2 | var config = require('../configs/config'); 3 | var CDN = 'http://h1.ioliu.cn/bing/'; 4 | // access_key and secret_key 5 | qiniu.conf.ACCESS_KEY = process.env.qiniu_access_key; 6 | qiniu.conf.SECRET_KEY = process.env.qiniu_secret_key; 7 | var encryptKey = process.env.qiniu_encrypt_key 8 | 9 | // 上传的空间 10 | var bucket = 'ioliu'; 11 | 12 | module.exports = { 13 | /** 14 | * 上传到骑牛 15 | * @param imgUrl 远程图片地址 16 | * @param callback 17 | */ 18 | fetchToQiniu: function (imgURL, callback) { 19 | var client = new qiniu.rs.Client(); 20 | for (var i = 0, len = config.resolutions.length; i < len; i++) { 21 | var _temp = config.resolutions[i]; 22 | var remoteURL = imgURL.replace('1920x1080', _temp); 23 | var _tempName = 'bing/' + imgURL.substr(imgURL.lastIndexOf('/') + 1, imgURL.length); 24 | var imgName = _tempName.replace('1920x1080', _temp); 25 | client.fetch(remoteURL, bucket, imgName, function (err, ret) { 26 | if (!err) { 27 | console.log(ret); 28 | } else { 29 | console.log(err); 30 | } 31 | }); 32 | } 33 | callback && callback(); 34 | }, 35 | /** 36 | * 创建预览图片 37 | * @param {String} url 图片名称 38 | * @param {Number} width 缩略图宽度(默认1920) 39 | * @param {Number} height 缩略图高度(默认1080) 40 | * @param {Number} quality 图片质量(0-100,默认100) 41 | * @param {Number} mode 缩略图处理模式(默认1) 42 | * 1:表示限定目标缩略图的宽度和高度,放大并从缩略图中央处裁剪为指定 x 大小的图片。 43 | * 2:指定 ,表示限定目标缩略图的长和宽,将缩略图的大小限定在指定的宽高矩形内。 44 | * 2:指定 但不指定 ,表示限定目标缩略图的宽度,高度等比缩略自适应。 45 | * 2:指定 但不指定 ,表示限定目标缩略图的高度,宽度等比缩略自适应。 46 | * @return {String} fullURL 47 | * 48 | */ 49 | imageView: function (url, width, height, quality, mode) { 50 | width = width || 1920; 51 | height = height || 1080; 52 | quality = quality || 100; 53 | mode = mode || 1; 54 | url = url.indexOf('1920x1080') == -1 ? url + '_1920x1080.jpg' : url; 55 | url = /^(http|https)/.test(url) ? url : CDN + url; 56 | return `${url}?imageView2/${mode}/w/${width}/h/${height}/format/jpg/interlace/1/q/${quality}` 57 | // var imageView = new qiniu.fop.ImageView(mode, width, height, quality); 58 | // return imageView.makeRequest(url); 59 | }, 60 | 61 | specialFetchToQiniu(imgURL, name) { 62 | var client = new qiniu.rs.Client(); 63 | for (var i = 0, len = config.resolutions.length; i < len; i++) { 64 | var _temp = config.resolutions[i]; 65 | var remoteURL = imgURL + `?imageView2/1/w/${_temp.split('x')[0]}/h/${_temp.split('x')[1]}/q/100`; 66 | console.log(1); 67 | console.log(remoteURL); 68 | var img = imgURL.split('?')[0].split('/bing/')[1]; 69 | var _tempName = 'bing/' + img; 70 | var imgName = _tempName.replace('1920x1080', _temp); 71 | client.fetch(remoteURL, bucket, imgName, function (err, ret) { 72 | if (!err) { 73 | console.log(ret); 74 | } else { 75 | console.log(err); 76 | } 77 | }); 78 | } 79 | //callback && callback(); 80 | }, 81 | 82 | encryptURI : (file) => { 83 | let deadline = parseInt(Date.now() / 1000) + 10 84 | let cdnManager = new qiniu.cdn.CdnManager(null) 85 | return cdnManager.createTimestampAntiLeechUrl('', file, null, encryptKey, deadline); 86 | } 87 | }; -------------------------------------------------------------------------------- /utils/bingUtils.js: -------------------------------------------------------------------------------- 1 | var request = require('superagent'); 2 | var objectAssign = require('object-assign'); 3 | var commonUtils = require('./commonUtils'); 4 | var bingURL = 'http://www.bing.com/HPImageArchive.aspx'; 5 | var story = 'http://cn.bing.com/cnhp/coverstory/'; 6 | var cookie = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36' }; 7 | module.exports = { 8 | /** 9 | * 获取 当日Bing图片 10 | */ 11 | fetchPicture: function(options, callback) { 12 | var defaultOptions = { 13 | ids: 0, 14 | n: 1, 15 | format: 'js' 16 | }; 17 | if (Object.prototype.toString.call(options) === '[object Object]') { 18 | // 合并对象 19 | defaultOptions = objectAssign(defaultOptions, options); 20 | } else { 21 | callback = options; 22 | } 23 | request 24 | .get(bingURL) 25 | .set(cookie) 26 | .query(defaultOptions) 27 | .end(function(err, res) { 28 | commonUtils.convert(err, res, function(data) { 29 | for (var i in data['images']) { 30 | var images = data['images'][i]; 31 | module.exports.fetchStory({ 32 | d: images['enddate'] 33 | }, function(data) { 34 | data = objectAssign(images, data); 35 | var newData = { 36 | startdate: data.startdate, 37 | fullstartdate: data.fullstartdate, 38 | enddate: data.enddate, 39 | url: /(http|https)\:\/\//gi.test(data.url) ? data.url : 'http://s.cn.bing.net' + data.url, 40 | urlbase: data.urlbase, 41 | copyright: data.copyright, 42 | copyrightlink: data.copyrightlink, 43 | hsh: data.hsh, 44 | title: data.title, 45 | description: data.description, 46 | attribute: data.attribute, 47 | country: data.country, 48 | city: data.city, 49 | longitude: data.longitude, 50 | latitude: data.latitude, 51 | continent: data.continent 52 | } 53 | callback && callback(newData); 54 | }); 55 | } 56 | }); 57 | }); 58 | }, 59 | /** 60 | * 获取 当前Bing返回的所有图片集合 61 | */ 62 | fetchPictures: function(callback) { 63 | var options = { 64 | ids: 14, 65 | n: 100 66 | }; 67 | module.exports.fetchPicture(options, callback); 68 | }, 69 | /** 70 | * 获取 每日故事(默认当日) 71 | * 72 | * 若需要查询指定日期: 73 | * options = { 74 | * d:20161015 75 | * } 76 | */ 77 | fetchStory: function(options, callback) { 78 | if (Object.prototype.toString.call(options) === '[object Function]') { 79 | callback = options; 80 | options = {}; 81 | } 82 | request 83 | .get(story) 84 | .set(cookie) 85 | .query(options) 86 | .end(function(err, res) { 87 | commonUtils.convert(err, res, function(data) { 88 | data['description'] = data.para1 || data.para2 || ''; 89 | data['country'] = data.Country || ''; 90 | data['city'] = data.City || ''; 91 | data['longitude'] = data.Longitude || ''; 92 | data['latitude'] = data.Latitude || ''; 93 | data['continent'] = data.Continent || ''; 94 | callback && callback(data); 95 | }); 96 | }); 97 | } 98 | }; -------------------------------------------------------------------------------- /static/js/progressively.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * progressively 1.1.2 3 | * https://github.com/thinker3197/progressively 4 | * @license MIT licensed 5 | * 6 | * Copyright (C) 2016-17 Ashish 7 | */ 8 | 9 | ; 10 | (function (root, factory) { 11 | if (typeof define === 'function' && define.amd) { 12 | define(function () { 13 | return factory(root) 14 | }) 15 | } else if (typeof exports === 'object') { 16 | module.exports = factory 17 | } else { 18 | root.progressively = factory(root) 19 | } 20 | })(this, function (root) { 21 | 'use strict' 22 | 23 | var progressively = {} 24 | 25 | var defaults, poll, onLoad, inodes 26 | 27 | onLoad = function () {} 28 | 29 | function extend (primaryObject, secondaryObject) { 30 | var o = {} 31 | for (var prop in primaryObject) { 32 | o[prop] = secondaryObject.hasOwnProperty(prop) ? secondaryObject[prop] : primaryObject[prop] 33 | } 34 | return o 35 | }; 36 | 37 | function isHidden (el) { 38 | return (el.offsetParent === null) 39 | }; 40 | 41 | function inView (el) { 42 | if (isHidden(el)) { 43 | return false 44 | } 45 | 46 | var box = el.getBoundingClientRect() 47 | var top = box.top 48 | var height = box.height 49 | 50 | el = el.parentNode 51 | 52 | do { 53 | box = el.getBoundingClientRect() 54 | 55 | if (top <= box.bottom === false) { 56 | return false 57 | } 58 | if ((top + height) <= box.top) { 59 | return false 60 | } 61 | 62 | el = el.parentNode 63 | } while (el !== document.body) 64 | 65 | return top <= document.documentElement.clientHeight 66 | }; 67 | 68 | function loadImage (el) { 69 | setTimeout(function () { 70 | var img = new Image() 71 | 72 | img.onload = function () { 73 | el.classList.remove('progressive--not-loaded') 74 | el.classList.add('progressive--is-loaded') 75 | el.src = this.src 76 | 77 | onLoad(el) 78 | } 79 | 80 | img.src = el.dataset.progressive 81 | }, defaults.delay) 82 | }; 83 | 84 | function listen () { 85 | if (poll) { 86 | return 87 | } 88 | clearTimeout(poll) 89 | poll = setTimeout(function () { 90 | progressively.check() 91 | progressively.render() 92 | poll = null 93 | }, defaults.throttle) 94 | } 95 | /* 96 | * default settings 97 | */ 98 | 99 | defaults = { 100 | throttle: 300, // appropriate value, don't change unless intended 101 | delay: 100, 102 | onLoadComplete: function () {}, 103 | onLoad: function () {} 104 | } 105 | 106 | progressively.init = function (options) { 107 | options = options || {} 108 | 109 | defaults = extend(defaults, options) 110 | 111 | onLoad = defaults.onLoad || onLoad 112 | 113 | inodes = [].slice.call(document.querySelectorAll('.progressive__img')) 114 | 115 | progressively.render() 116 | 117 | if (document.addEventListener) { 118 | root.addEventListener('scroll', listen, false) 119 | root.addEventListener('load', listen, false) 120 | } else { 121 | root.attachEvent('onscroll', listen) 122 | root.attachEvent('onload', listen) 123 | } 124 | } 125 | 126 | progressively.render = function () { 127 | var elem 128 | 129 | for (var i = inodes.length - 1; i >= 0; --i) { 130 | elem = inodes[i] 131 | 132 | if (inView(elem) && elem.classList.contains('progressive--not-loaded')) { 133 | loadImage(elem) 134 | inodes.splice(i, 1) 135 | } 136 | } 137 | 138 | this.check() 139 | } 140 | 141 | progressively.check = function () { 142 | if (!inodes.length) { 143 | defaults.onLoadComplete() 144 | this.drop() 145 | } 146 | } 147 | 148 | progressively.drop = function () { 149 | if (document.removeEventListener) { 150 | root.removeEventListener('scroll', listen) 151 | } else { 152 | root.detachEvent('onscroll', listen) 153 | } 154 | clearTimeout(poll) 155 | } 156 | 157 | return progressively 158 | }) 159 | -------------------------------------------------------------------------------- /views/about.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang="en") 3 | head 4 | include common_headers.pug 5 | title 关于 - 必应每日高清壁纸下载:精彩,从这里开始 6 | link(rel="stylesheet",type="text/css" href="/static/css/typo.css") 7 | link(href="/static/css/about.css", rel="stylesheet") 8 | meta(name="keywords",content="必应壁纸,必应高清壁纸打包下载,必应壁纸合集,必应每日壁纸下载,必应壁纸接口,必应壁纸官方下载,Bing壁纸,微软bing每日壁纸,必应每日壁纸下载,Bing壁纸api,高清壁纸分享,bing壁纸官方下载,必应高清壁纸,必应壁纸打包下载") 9 | 10 | 11 |
12 |


13 |

Bing 每日壁纸,你值得拥有

14 |
15 | 16 | 17 |
18 | 19 |

最开始做 Bing 壁纸的目的很纯粹,就是看到必应搜索 cn.bing.com 官网的壁纸很赞,但是想找到之前的壁纸就有点困难,然后就自己用PHP语言开发了一个,每天同步cn.bing.com 官网的壁纸,同步抓取每日故事,存在自己的数据库里。源代码可以在GitHub上找到。当时主机是用的 20 | 阿里云送的免费两年的云主机,可能大部分人都没见过。整体布局如下:

21 | 22 |

由于我不是专业的 PHP工程师(前端攻城狮一枚),PHP版本 到后期维护就越来越困难,每次花费在改问题的时间越来越长,于是后面干脆就用Node.js 重写了Bing 壁纸,将版本号升级到了2.0版本(哈哈,自己定义的,无需在意),图片存储采用的七牛云存储,毕竟早期的流量小,免费额度还是撑得住的。程序部署在DaoCloud上,有兴趣的可以去看看Dockerfile。并且将域名由 23 | api.ioliu.cn 换成了 bing.ioliu.cn,这个版本可能大家就更熟悉一些,应该是上线时间最长的版本了吧。 24 |

25 |

后面由于 道客(DocCloud) 访问速度慢,最后干脆咬牙买了阿里云主机(ECS)1核/2G,并更新了SSL证书,相信现在的你一定发现了本站的访问速度简直是超级快(哈哈,自我陶醉下)。

26 | 27 |

截止到 2017-03-05,Bing 壁纸 项目已经稳步运行了一年,相信在未来的日子里,Bing 壁纸 会越来越好,功能越来越完善。现在,Bing壁纸 开通了留言功能,大家有什么想说的可以在下面留言咯\(^o^)/YES!

28 |

为了把壁纸的美好带给大家,攻城狮小哥花费了很多个人时间和精力进行开发,甚至自己掏钱购买服务器,看攻城狮小哥这么可怜,快来打赏杯咖啡吧。≡ω≡

29 |
30 |
31 | 云淡风轻 WeChat Pay 32 |

微信打赏

33 |
34 |
35 | 云淡风轻 Alipay 36 |

支付宝打赏

37 |
38 |
39 |

云淡风轻
2017-03-28

40 |
41 | script. 42 | var cloudTieConfig = { 43 | url: document.location.href, 44 | sourceId: "", 45 | productKey: "86c5aa95f7b240b5ae7e4f97bc08d5ff", 46 | target: "cloud-tie-wrapper" 47 | }; 48 | script(src="https://img1.cache.netease.com/f2e/tie/yun/sdk/loader.js") 49 |
50 | 51 | 52 | img(src='https://ws1.sinaimg.cn/large/006qRazegy1fe4s9xlfauj3045045jrb.jpg') 53 | 54 | 55 | script(src='/static/js/baidu-analysis.js') 56 | -------------------------------------------------------------------------------- /routes/photo.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var qiniuUtils = require('../utils/qiniuUtils'); 4 | var request = require('superagent'); 5 | var db = require('../utils/dbUtils'); 6 | var config = require('../configs/config'); 7 | const CDN1 = 'http://h1.ioliu.cn/'; 8 | 9 | /* GET photo listing. */ 10 | router.get('/:photo', function(req, res, next) { 11 | var force = req.query.force || ''; 12 | var photo = req.params.photo; 13 | var isAjax = !!req.headers['x-requested-with']; 14 | switch (force) { 15 | case 'like': 16 | if (isAjax) { 17 | var ck = req.cookies['likes'] || ''; 18 | ck = ck.split('_'); 19 | if (ck.indexOf(photo) > -1) { 20 | res.json({ 21 | msg: '', 22 | code: 200 23 | }); 24 | return; 25 | } 26 | var sql = `update bing as a join (select likes,id from bing WHERE id='${photo}') as b on a.id=b.id set a.likes=(b.likes+1)`; 27 | db.commonQuery(sql, function(rows) { 28 | var ret = { 29 | msg: '', 30 | code: 200 31 | }; 32 | if (rows.affectedRows == 0) { 33 | ret.msg = 'something happend.' 34 | } 35 | res.json(ret); 36 | }); 37 | } else {} 38 | return; 39 | break; 40 | case 'download': 41 | var ua = req.get('User-Agent'); 42 | if (!isAjax && !/(spider|bot)/ig.test(ua)) { 43 | var sql = `update bing as a join (select downloads,id from bing WHERE qiniu_url='${photo}') as b on a.id=b.id set a.downloads=(b.downloads+1)`; 44 | db.commonQuery(sql, function(rows) {}); 45 | res.set({ 46 | 'Content-Type': 'application/octet-stream', 47 | 'Content-Disposition': 'attachment; filename=' + encodeURI(`${photo}_1920x1080.jpg`) 48 | }); 49 | request.get(`${CDN1}bing/${photo}_1920x1080.jpg`) 50 | .set({ 51 | 'User-Agent': ua, 52 | referer: 'https://bing.ioliu.cn' 53 | }) 54 | .pipe(res); 55 | //console.log(`${CDN}bing/${photo}_1920x1080.jpg`) 56 | } else { 57 | res.json({ 58 | code: 200, 59 | msg: 'bad request' 60 | }) 61 | } 62 | return; 63 | break; 64 | } 65 | 66 | var sql = `select id,title,attribute,description,copyright,qiniu_url as photo,city,country,continent,DATE_FORMAT(enddate, '%Y-%m-%d') as dt,likes,views,downloads,thumbnail_pic from bing 67 | where qiniu_url='${photo}'`; 68 | if (isAjax) { 69 | res.send({ 70 | code: 200, 71 | msg: 'bad request' 72 | }); 73 | } else { 74 | // 修改展示量 75 | db.commonQuery(`update bing as a join (select views,id from bing WHERE qiniu_url='${photo}') as b on a.id=b.id set a.views=(b.views+1)`, function(rows) {}); 76 | // 返回数据 77 | db.commonQuery(sql, function(rows) { 78 | if (rows.length > 0) { 79 | var doc = rows[0]; 80 | doc['large'] = `${CDN1}/bing/${photo}_1920x1080.jpg`; 81 | doc['small'] = `${CDN1}/bing/${photo}_640x360.jpg`; 82 | if (force.indexOf('_') > -1) { 83 | var rt = force.split('_'); 84 | doc['back_url'] = rt[0] === 'ranking' ? '/ranking?p=' + rt[1] : '/?p=' + rt[1]; 85 | } else { 86 | doc['back_url'] = '/'; 87 | } 88 | res.render('detail', { doc: doc }); 89 | } else { 90 | res.redirect(`/`); 91 | } 92 | }); 93 | } 94 | }); 95 | /** 96 | * 如果没有参数,则跳转到首页 97 | */ 98 | router.get('/', function(req, res, next) { 99 | res.redirect('/'); 100 | }); 101 | 102 | module.exports = router; -------------------------------------------------------------------------------- /static/js/js.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * JavaScript Cookie v2.1.3 3 | * https://github.com/js-cookie/js-cookie 4 | * 5 | * Copyright 2006, 2015 Klaus Hartl & Fagner Brack 6 | * Released under the MIT license 7 | */ 8 | ;(function (factory) { 9 | var registeredInModuleLoader = false; 10 | if (typeof define === 'function' && define.amd) { 11 | define(factory); 12 | registeredInModuleLoader = true; 13 | } 14 | if (typeof exports === 'object') { 15 | module.exports = factory(); 16 | registeredInModuleLoader = true; 17 | } 18 | if (!registeredInModuleLoader) { 19 | var OldCookies = window.Cookies; 20 | var api = window.Cookies = factory(); 21 | api.noConflict = function () { 22 | window.Cookies = OldCookies; 23 | return api; 24 | }; 25 | } 26 | }(function () { 27 | function extend () { 28 | var i = 0; 29 | var result = {}; 30 | for (; i < arguments.length; i++) { 31 | var attributes = arguments[ i ]; 32 | for (var key in attributes) { 33 | result[key] = attributes[key]; 34 | } 35 | } 36 | return result; 37 | } 38 | 39 | function init (converter) { 40 | function api (key, value, attributes) { 41 | var result; 42 | if (typeof document === 'undefined') { 43 | return; 44 | } 45 | 46 | // Write 47 | 48 | if (arguments.length > 1) { 49 | attributes = extend({ 50 | path: '/' 51 | }, api.defaults, attributes); 52 | 53 | if (typeof attributes.expires === 'number') { 54 | var expires = new Date(); 55 | expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5); 56 | attributes.expires = expires; 57 | } 58 | 59 | try { 60 | result = JSON.stringify(value); 61 | if (/^[\{\[]/.test(result)) { 62 | value = result; 63 | } 64 | } catch (e) {} 65 | 66 | if (!converter.write) { 67 | value = encodeURIComponent(String(value)) 68 | .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent); 69 | } else { 70 | value = converter.write(value, key); 71 | } 72 | 73 | key = encodeURIComponent(String(key)); 74 | key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent); 75 | key = key.replace(/[\(\)]/g, escape); 76 | 77 | return (document.cookie = [ 78 | key, '=', value, 79 | attributes.expires ? '; expires=' + attributes.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 80 | attributes.path ? '; path=' + attributes.path : '', 81 | attributes.domain ? '; domain=' + attributes.domain : '', 82 | attributes.secure ? '; secure' : '' 83 | ].join('')); 84 | } 85 | 86 | // Read 87 | 88 | if (!key) { 89 | result = {}; 90 | } 91 | 92 | // To prevent the for loop in the first place assign an empty array 93 | // in case there are no cookies at all. Also prevents odd result when 94 | // calling "get()" 95 | var cookies = document.cookie ? document.cookie.split('; ') : []; 96 | var rdecode = /(%[0-9A-Z]{2})+/g; 97 | var i = 0; 98 | 99 | for (; i < cookies.length; i++) { 100 | var parts = cookies[i].split('='); 101 | var cookie = parts.slice(1).join('='); 102 | 103 | if (cookie.charAt(0) === '"') { 104 | cookie = cookie.slice(1, -1); 105 | } 106 | 107 | try { 108 | var name = parts[0].replace(rdecode, decodeURIComponent); 109 | cookie = converter.read ? 110 | converter.read(cookie, name) : converter(cookie, name) || 111 | cookie.replace(rdecode, decodeURIComponent); 112 | 113 | if (this.json) { 114 | try { 115 | cookie = JSON.parse(cookie); 116 | } catch (e) {} 117 | } 118 | 119 | if (key === name) { 120 | result = cookie; 121 | break; 122 | } 123 | 124 | if (!key) { 125 | result[name] = cookie; 126 | } 127 | } catch (e) {} 128 | } 129 | 130 | return result; 131 | } 132 | 133 | api.set = api; 134 | api.get = function (key) { 135 | return api.call(api, key); 136 | }; 137 | api.getJSON = function () { 138 | return api.apply({ 139 | json: true 140 | }, [].slice.call(arguments)); 141 | }; 142 | api.defaults = {}; 143 | 144 | api.remove = function (key, attributes) { 145 | api(key, '', extend(attributes, { 146 | expires: -1 147 | })); 148 | }; 149 | 150 | api.withConverter = init; 151 | 152 | return api; 153 | } 154 | 155 | return init(function () {}); 156 | })); 157 | -------------------------------------------------------------------------------- /views/detail.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang="en") 3 | head 4 | include common_headers.pug 5 | - var tt = doc.title ? doc.title+' - '+doc.copyright+' | 必应每日高清壁纸 - 精彩,从这里开始' : doc.copyright+' | 必应每日高清壁纸 - 精彩,从这里开始'; 6 | title #{tt} 7 | - var location = (doc.country && doc.city) ? doc.country + ', ' + doc.city : false; 8 | - location = doc.continent ? (location ? doc.continent + ', ' + location : doc.continent) : location; 9 | meta(name="keywords",content=`${doc.copyright}, ${doc.title}, ${location}, ${doc.dt}`) 10 | meta(name="description",content=`${doc.description}`) 11 | meta(name="author",content=`${doc.copyright}`) 12 | link(rel='stylesheet',href="/static/css/progressively.css") 13 | link(rel='stylesheet', href='/static/css/common.css') 14 | link(rel='stylesheet', href='/static/css/main.css') 15 | 16 | 17 | body.detail(oncontextmenu='self.event.returnValue=false') 18 | div.preview 19 | - var small = doc.photo; 20 | div.progressive 21 | - var cdn1 = `http://h1.ioliu.cn/bing/${doc.photo}_1366x768.jpg`; 22 | - var root = 'https://bing.ioliu.cn' ; 23 | - var attr = (!!doc['title'] && !!doc['description']) ? `#${doc['title']}# ${doc['description']}` : `${doc['copyright']}`; 24 | - var desc = `#必应壁纸# ${doc.dt} / ${attr}`; 25 | - var link = `http://service.weibo.com/share/share.php?url=${root}/photo/${doc.photo}&appkey=1833831541&ralateUid=5893653736&pic=${cdn1}&title=`+encodeURIComponent(desc.substring(0,126)+'...'); 26 | img.target.progressive__img.progressive--not-loaded(src=doc.small,data-progressive=doc.large) 27 | div.mark 28 | div.options 29 | a.ctrl.left(href=`${doc.back_url}`) 30 | i.icon.icon-left 31 | em.t BACK 32 | a.ctrl.share(href=link,target="_blank",rel="nofollow",title="分享到微博") 33 | i.icon.icon-share 34 | em.t 分享 35 | span.ctrl.heart(photo=doc.id,likes=doc.likes) 36 | i.icon.icon-heart 37 | em.t #{doc.likes} 38 | a.ctrl.download(href=`/photo/${doc.photo}?force=download`,target="_blank") 39 | i.icon.icon-download 40 | em.t #{doc.downloads} 41 | div.description 42 | p.title #{doc.copyright} 43 | p.sub #{doc.description} 44 | p.calendari.icon.icon-calendar 45 | em.t #{doc.dt} 46 | if location 47 | p.location 48 | i.icon.icon-location 49 | em.t #{location} 50 | p.view 51 | i.icon.icon-eye 52 | em.t #{doc.views} 53 | script(src='/static/js/jquery-1.12.4.min.js') 54 | script(src='/static/js/progressively.js') 55 | script(src='/static/js/baidu-analysis.js') 56 | script(src='/static/js/like.js') 57 | //- script(data-no-instant,src="/static/js/instantclick.min.js") 58 | //- script(data-no-instant,src="/static/js/mine-instantclick.js") 59 | script. 60 | $(function() { 61 | var i=0; 62 | var img = $('.target'); 63 | var mini = img.attr('src'); 64 | var mark = $('.mark'); 65 | mark.css({ 66 | 'background-image': 'url('+mini+')' 67 | }); 68 | progressively.init({ 69 | delay: 20, 70 | throttle: 300, 71 | onLoad: function(elem) { 72 | }, 73 | onLoadComplete: function() { 74 | i++; 75 | if(i>=2){ 76 | var big = img.attr('src'); 77 | mark.css({ 78 | backgroundImage: 'url('+big+')', 79 | filter: 'blur(0)' 80 | }); 81 | img.remove(); 82 | } 83 | } 84 | }); 85 | 86 | $(document).on('keydown',function(e){ 87 | e = e.event || window.event; 88 | if(e.keyCode == 123 || (e.ctrlKey&&e.shiftKey&&e.keyCode==73) || (e.ctrlKey&&e.keyCode==85)) return false 89 | }) 90 | }); -------------------------------------------------------------------------------- /routes/weibo.js: -------------------------------------------------------------------------------- 1 | var router = require('express').Router(), 2 | request = require('superagent'), 3 | weibo = require('../configs/config').weibo, 4 | weiboUtils = require('../utils/weiboUtils'); 5 | var dbUtils = require('../utils/dbUtils'); 6 | var cookie = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36' }; 7 | var redirect_uri = 'http://bing.ioliu.cn/weibo/callback'; 8 | /** 9 | * 微博认证 10 | */ 11 | router.get('/', function(req, res) { 12 | res.redirect('https://api.weibo.com/oauth2/authorize?client_id=' + weibo.CLIENT_ID + '&redirect_uri=' + redirect_uri); 13 | }); 14 | 15 | /** 16 | * 认证回调 17 | */ 18 | router.get('/callback', function(req, res, next) { 19 | var code = req.query.code; 20 | request 21 | .post('https://api.weibo.com/oauth2/access_token') 22 | .set(cookie) 23 | .type('form') 24 | .send({ 25 | client_id: weibo.CLIENT_ID, 26 | client_secret: weibo.CLIENT_SECRET, 27 | grant_type: 'authorization_code', 28 | code: code, 29 | redirect_uri: redirect_uri 30 | }) 31 | .end(function(err, response) { 32 | if (!err && response.status === 200) { 33 | var text = JSON.parse(response.text); 34 | dbUtils.get('bing_session', { 35 | uid: text.uid 36 | }, function(rows) { 37 | if (rows.length === 0) { 38 | dbUtils.set('bing_session', { 39 | token: text.access_token, 40 | expires_in: text.expires_in, 41 | insertdate: Date.now(), 42 | uid: text.uid 43 | }, function(rows) { 44 | if (rows.length > 0) { 45 | var data = rows[0]; 46 | if (data.uid === weibo.MASTER_UID && !weibo.MASTER_ACCESS_TOKEN) { 47 | weibo.MASTER_ACCESS_TOKEN = data.access_token; 48 | } else { 49 | weibo.ACCESS_TOKEN = data.access_token; 50 | weibo.USER_UID = data.uid; 51 | } 52 | req.session['weibo'] = weibo; 53 | res.redirect('/'); 54 | } 55 | }); 56 | } else { 57 | var data = rows[0]; 58 | dbUtils.update('bing_session', { 59 | body: { 60 | token: text.access_token, 61 | expires_in: text.expires_in, 62 | }, 63 | condition: { 64 | uid: text.uid 65 | } 66 | }, function(r) { 67 | if (text.uid === weibo.MASTER_UID && !weibo.MASTER_ACCESS_TOKEN) { 68 | weibo.MASTER_ACCESS_TOKEN = text.access_token; 69 | weibo.ACCESS_TOKEN = text.access_token; 70 | } else { 71 | weibo.ACCESS_TOKEN = text.access_token; 72 | weibo.USER_UID = text.uid; 73 | } 74 | req.session['weibo'] = weibo; 75 | res.redirect('/'); 76 | }); 77 | } 78 | }); 79 | } 80 | }); 81 | }); 82 | /** 83 | * 发送微博 84 | */ 85 | router.get('/send', function(req, res, next) { 86 | if (req.session && req.session['weibo']) { 87 | weibo = req.session['weibo']; 88 | } 89 | if (weibo.ACCESS_TOKEN === '') { 90 | res.redirect('/weibo'); 91 | } else { 92 | weiboUtils.update(function() { 93 | res.redirect('/'); 94 | }); 95 | } 96 | }); 97 | /** 98 | * 获取短链 99 | */ 100 | router.get('/shorten', function(req, res, next) { 101 | if (req.session && req.session['weibo']) { 102 | weibo = req.session['weibo']; 103 | } 104 | if (weibo.ACCESS_TOKEN === '') { 105 | res.redirect('/weibo'); 106 | } else { 107 | var url = req.query.url; 108 | weiboUtils.shorten(url, function(data) { 109 | res.send(data); 110 | }); 111 | } 112 | }); 113 | module.exports = router; -------------------------------------------------------------------------------- /static/css/iconfont.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face {font-family: "iconfont"; 3 | src: url('../fonts/iconfont.eot?t=1513930402584'); /* IE9*/ 4 | src: url('../fonts/iconfont.eot?t=1513930402584#iefix') format('embedded-opentype'), /* IE6-IE8 */ 5 | url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAzIAAsAAAAAEewAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFZW7km4Y21hcAAAAYAAAACwAAACYpmdBuhnbHlmAAACMAAACD4AAApocPQoRmhlYWQAAApwAAAALwAAADYP5gNBaGhlYQAACqAAAAAeAAAAJAffA6tobXR4AAAKwAAAABkAAAA0NAcAAGxvY2EAAArcAAAAHAAAABwPEhFqbWF4cAAACvgAAAAfAAAAIAEeAJFuYW1lAAALGAAAAUUAAAJtPlT+fXBvc3QAAAxgAAAAaAAAAImbnOGMeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BkYWScwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGBwYKp4fZW7438AQw9zA0AAUZgTJAQDiPww8eJzFkjEOwjAMRX+akBLSgZm5Uhe2HqDX6tgjcAQOwoTUq/weo9gxA0iFDeHoRfK3Y1uRAewAeOEsBMDd4aB2E9UV3eNQ9ICL+CccizKyYmBkYmbLjj0HTsu8rpLzLbZtTupuH43VqLBHg4gknbPM7Mus8WO9n5v7X+t3a8p9ffHGJzIiK0N+EAwGVIuG/CqYDH3LbEDjrQGNd4ZuAnsDmjsYukWcDN2mZTYQHqt0PAl4nHVWfWwT5xl/n/e9D58d3+XOd77YIXbsS+5IQpwP22e1ThxDgCQOBFoaWMdXu/FVCm1HqdqVEmi1dl3XwkADNMo6lVFQxagoTGuLgG6oTKxqWaHTxKQh1g1ENWl/IFWiW8ltz9lQbX/Mev28790973vPx+/5PUd4Qv79GTvJGkiETCU9ZCaZTwgIHZCWaROknFyGdoCR4g1Tl5ljOSnRSmdYP5hpQY/2ujnbFERBARkSkE31uk6GOpDPlWgReqNNALHG+AKtdYrGfgTBBifxvFeh+8FIWlOUUqc3Mm1A722OBJ6s07SYpr0cEHg+QCmnyLDejEq8FBS8A7wSN04m22gS6mJOfM794eZG7cEXc480tZoSwNatEGlslg8NqHEVxzPxaESLifXhQEM8bLXo8OTVUEOkrsn+G8Efh75+gL72kDpikBTpRE/TgmgkoNfth5zt3PHJkUFMgFmCQgZYBhxc4KUMVNDjAPGp8biux6vTiZs8f/NETe77hOM+2VeV8H5cP48qvjjS6DTi+OBrvRM3b125o4iyZtcpdobNRIvuJevIekIiObeAb80mwOjRMRtokKpHTYxwXs252Z7eqKniPUvgLLQPbJmKdnVHHv9u1EwwEy3WMTNC2slAvrWnBPnakQY65Rj+oTamTjRKPIQL9mdhRQnf0AxQVbo/3lJfF1YAch3mAe/kga4+nq4trlrq1iXpXYUdS0DgAwrfVOy7K8YLQLnrks5JzLvAicU5Rk/sO29lvpEBrrPjXntVio4FVCltAyh4It+kTv5FTUjNCcj57/M+6uiTOAr5LvgJJCvfvLv/7UWjI+yVQJDjeC5R7htIcEGJk7mbgQAf4LKM4/osSYNNp7se7HoMNxqxykaHsGr8brFBYhKbjBEigY0AFkEwVB+W+ayBgSuoOd7y022pEqhWPme39uQcKHSB7VgYKFPEW7hNaAJRhmjvAOC+Vly47GHvzUAQggHvTQmUIMS2QCyogHf9eJ0MH0IJSh8q8OOA9wvYxvGIYX4ndCuhx0LKlgBVouKOPqDwezGqwEbIiqL3caDOeyIUvnIlHIIf1r2qhM7Ct6DRu+a9djakvBMMYD43UQbANkPbcEhRQpceCRiqsOUPr//VWyeopOrvO+wG6ycKschdVRTr0ayfcLQ2n4FCT8YHdlX0CDoWiptjEmQAC7M3mgTEQW+C6gI9XK9p9e1FiCQ1eOBg+9oNa9urYtEi2At7F33f2+3tTiYSpflL5ie7u5M4lRIJelBLRqDY7u8deWB22+3f4quLvVWwd/FV74a3G9beVva31Q4gREC7T7PLbAYJYaY6SY6UyRBa/3+qjXd9pOLDqMkKZiFquihN3hH9x+yOFnrJi/4ek9aNrqR05ejoCkpXjBYqAJVCVQKSEzuzc+cZjjbtem9kwnHa7YmR9yYfdjeON83vPcIOTUwcYlVJ0+5Q97ymcbqPrqzUjqus9M7TSsGtUFpxCxW5PG/HGY47s2N0pvcv48gVJCHp8mHDflTa1TFdg9LmQxx3aHP1vPPrN+nT23YRqPotot9R9NZ2sFLBTYAgSmgY1icgBBEiEBDC8FNtWgpUbxs8pkJ6muqtRj6ENRQBG4YX6sHqrPf2e/vUaVY9/IBRvnr2VraabSUSsoaptnYBb4Cz9yNIbbsffuvBx2zr8GT3wcHLP/vc+9MGQlH/K+YxHhlwASGt1tegt2RIVmHv2rfjXuVB3aylJ+tY1dwMgGP7JFODkZ8D0awlwhJrOaSHn9JlR4l8VxalA8GY9IYkXhSOP7/g5c5kemgZhXZM85ZvP1saKo9TcFpiEge5qclYa6T5ieHVLzEWa+8Bel95rPTy8gkGfdfelXVdfhfk0NkQDvnz549yMDYzDHTZUCiUWuYu38pV2MLpbbMMZYosO3mAyJS26RVgL62ZGtJGMuX7EAJsYnlpFfYAv9/9gwMmE5WkSTtWzzDmJON7LyJpZmsoFLCN+VzpJqghg1NANsZYRKKciXBzfF3HbYlYdotTqCHXFDj6q9al2fXb0aWRJVm6zl/80ZlVaAL68D3UnoUJX3ex3NII7tAzv4PmhgHjy6P8sS+1sgHxT4Vz3tWGsv7VsWP/1AYM7zodarG43etzKyoF4Pavz6/z3oKGfL89fylAOJu373n019DYAmU6ArPYuT31m3cC3blJ3X321jX+0z3altco/8Yz8T0XCRHR37NYcyVST6Zgd1+G/eUp8j0MRDpDfTpAHpAp+68SxGA4qu88Ztnv4al8zvVnn1+MVK0LRXXBqoImhWyD3we6kLb7IVWdMQG3d2areqm0nXP9zwFcO/7a72q4pje6x2da1szx5QsHW1oGF3bNe4jSh+aN+XJyVlsBoNC2AOSoDI8rhrKg3aW3bsR1mBFERvTe1+P0zxDXF+Aj7xVfiRJfd3pIlkOTL/py9/+KojU4vnx80MJp2cJBC35O14yNraFV6b2K76ID+M60r5quSbz3G+857PiyodDzcX37dj1e9p+Ua3Ly3ONoS3A6vrY2jisIFl/7Ns5OY0+fQZrJNFKsfVe1Clxziw+savXLYNZi7trYokv+FdyhNFSKmAVXu8OAmk94fgjTd4qzupWdEb0vkBC+EKWjZSlMJenxJZReOHXqAqVLN0pBFg7MeNsbWvw0k/Z3Zl6X2NNzZdm8pLA9GzbsYcolcxi6K904LsPcu++eC1U5Z/bw8Oz+ogiSFqZaVk31U9qfUrMRCGtBCBTpBN289Lm/d7Rff2Hp5sn5YVmBKUc37mVs78ZfetcVuGr39Iz29rYW5wHMKxbHKB0j/wH+DhdlAAB4nGNgZGBgAOKz2d298fw2Xxm4WRhA4FrS/UUI+n8DCyNzA5DLwcAEEgUATd0LhAB4nGNgZGBgbvjfwBDDIssABCyMDIwMqIAXAEn7ApQAAHicY2FgYGB+ycDAwoCGZdH4jAg2ADOVATsAAAAAAAAAAHYAwgFiAdoCNgKyAuAC+AN4A/AErAU0eJxjYGRgYOBlaGVgZwABJiDmAkIGhv9gPgMAF0cBsAB4nGWPTU7DMBCFX/oHpBKqqGCH5AViASj9EatuWFRq911036ZOmyqJI8et1ANwHo7ACTgC3IA78EgnmzaWx9+8eWNPANzgBx6O3y33kT1cMjtyDRe4F65TfxBukF+Em2jjVbhF/U3YxzOmwm10YXmD17hi9oR3YQ8dfAjXcI1P4Tr1L+EG+Vu4iTv8CrfQ8erCPuZeV7iNRy/2x1YvnF6p5UHFockikzm/gple75KFrdLqnGtbxCZTg6BfSVOdaVvdU+zXQ+ciFVmTqgmrOkmMyq3Z6tAFG+fyUa8XiR6EJuVYY/62xgKOcQWFJQ6MMUIYZIjK6Og7VWb0r7FDwl57Vj3N53RbFNT/c4UBAvTPXFO6stJ5Ok+BPV8bUnV0K27LnpQ0kV7NSRKyQl7WtlRC6gE2ZVeOEXpc0Yk/KGdI/wAJWm7IAAAAeJxtxtEOgjAMBdBe2GSg+I+FVUaYbTJnlL+XhFfP06GGTgP9N6JBCwePCzoE9BhwxQ0j7oSv58ne1b8SF3HJnuKTcKldYd1WXVyWR3XTsRDto9k4trJLmDmLRi4h28x1NSX6ARkPGpM=') format('woff'), 6 | url('../fonts/iconfont.ttf?t=1513930402584') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 7 | url('../fonts/iconfont.svg?t=1513930402584#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .icon { 11 | font-family:"iconfont" !important; 12 | font-size:16px; 13 | font-style:normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-about:before { content: "\e604"; } 19 | 20 | .icon-share:before { content: "\e680"; } 21 | 22 | .icon-home:before { content: "\e7c5"; } 23 | 24 | .icon-heart:before { content: "\e63f"; } 25 | 26 | .icon-ranking:before { content: "\e60b"; } 27 | 28 | .icon-left:before { content: "\e602"; } 29 | 30 | .icon-bing:before { content: "\e606"; } 31 | 32 | .icon-download:before { content: "\e624"; } 33 | 34 | .icon-eye:before { content: "\e627"; } 35 | 36 | .icon-calendar:before { content: "\e633"; } 37 | 38 | .icon-location:before { content: "\e609"; } 39 | 40 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | var session = require('express-session'); 8 | var request = require('superagent'); 9 | var index = require('./routes/index'); 10 | var photo = require('./routes/photo'); 11 | var weibo = require('./routes/weibo'); 12 | var v1 = require('./routes/v1'); 13 | 14 | // 定时器 15 | var schedule = require('node-schedule'); 16 | 17 | // 各种工具类 18 | var dbUtils = require('./utils/dbUtils'); 19 | var bingUtils = require('./utils/bingUtils'); 20 | var mailUtils = require('./utils/mailUtils'); 21 | var qiniuUtils = require('./utils/qiniuUtils'); 22 | var weiboUtils = require('./utils/weiboUtils'); 23 | var config = require('./configs/config'); 24 | 25 | var app = express(); 26 | app.disable('x-powered-by'); 27 | app.use('/static', express.static(path.join(__dirname, 'static'))); 28 | app.use(favicon(__dirname + '/static/images/bing.ico')); 29 | // view engine setup 30 | app.set('views', path.join(__dirname, 'views')); 31 | app.set('view engine', 'pug'); 32 | app.enable('trust proxy'); 33 | app.use(bodyParser.json()); 34 | app.use(bodyParser.urlencoded({ 35 | extended: true 36 | })); 37 | app.use(cookieParser('bing.ioliu.cn')); 38 | app.use(session({ 39 | secret: 'bing app', //secret的值建议使用随机字符串 40 | resave: true, 41 | saveUninitialized: false, 42 | cookie: { 43 | secure: true, 44 | maxAge: 60 * 30 * 1000 // 过期时间(毫秒) 45 | } 46 | })); 47 | // 设置日志 48 | app.use(logger('combined', { 49 | skip: function(req, res) { return res.statusCode < 400 } 50 | })); 51 | 52 | // 每天 08:30,12:30,15:30,18:30,21:30 定时发送微博 53 | schedule.scheduleJob('*/30 8-21 * * *', function() { 54 | weiboUtils.update(function(data) { 55 | if (data && data.id) { 56 | // mailUtils.send({ 57 | // message: '发送微博成功', 58 | // title: '发送微博成功', 59 | // stack: JSON.stringify(data, '', 4) 60 | // }); 61 | } else { 62 | mailUtils.send({ 63 | message: '发送微博失败', 64 | title: '发送微博失败', 65 | stack: JSON.stringify(data, '', 4) 66 | }); 67 | } 68 | }, true); 69 | }); 70 | 71 | 72 | /** 73 | * 处理OPTIONS请求 74 | */ 75 | app.use(function(req, res, next) { 76 | // console.log('-----------------------------') 77 | // console.log(req.headers['host']) 78 | // console.log(req.headers['referer']) 79 | // console.log('-----------------------------') 80 | // if (config.disabled.indexOf(req.headers['host']) > -1) { 81 | // res.sendStatus(400) 82 | // } 83 | // 84 | if (req.method === 'OPTIONS') { 85 | res.sendStatus(200); 86 | } else next(); 87 | }); 88 | 89 | // var images = [ 90 | // 'MangroveRoots_ZH-CN10720576635', 91 | // 'IzmirFaceWall_ZH-CN8661261728', 92 | // 'CapeSebastian_ZH-CN9469145123', 93 | // 'FireEscapes_ZH-CN9251582421', 94 | // 'LaurelMoss_ZH-CN9578543974' 95 | // ]; 96 | 97 | // var resolutions = require('./configs/config').resolutions; 98 | // for (var i in images) { 99 | // var name = images[i]; 100 | // var link = `http://images.ioliu.cn/bing/${name}_1920x1080.jpg`; 101 | // qiniuUtils.specialFetchToQiniu(link); 102 | // } 103 | 104 | app.use('/', index); 105 | app.use('/photo', photo); 106 | app.use('/weibo', weibo); 107 | app.use('/v1', v1); 108 | 109 | app.get('/about.html', function(req, res, next) { 110 | res.render('about'); 111 | }); 112 | 113 | app.get('/fetch',function(req,res,next){ 114 | if(req.query.url){ 115 | //console.log(url) 116 | qiniuUtils.fetchToQiniu(req.query.url) 117 | }else{ 118 | res.redirect('/'); 119 | } 120 | }) 121 | /** 122 | * Robots.txt 123 | */ 124 | app.get('/robots.txt', function(req, res, next) { 125 | res.header('content-type', 'text/plain'); 126 | res.send('User-Agent: * \nAllow: /'); 127 | }); 128 | app.get('/test', function(req, res, next) { 129 | var images = []; 130 | bingUtils.fetchPicture(function(data) { 131 | var enddate = req.query.d || data.enddate; 132 | dbUtils.get('bing', { 133 | enddate: enddate 134 | }, function(data) { 135 | res.send(data); 136 | }); 137 | }); 138 | }); 139 | 140 | 141 | // catch 404 and forward to error handler 142 | app.use(function(req, res, next) { 143 | // var err = new Error('啊( ⊙ o ⊙ ),你发现了新大陆 ∑(っ °Д °;)っ'); 144 | // err.status = 404; 145 | // next(err); 146 | 147 | res.redirect('/'); 148 | }); 149 | // error handlers 150 | // development error handler 151 | // will print stacktrace 152 | if (app.get('env') === 'development') { 153 | app.use(function(err, req, res, next) { 154 | res.status(err.status || 500); 155 | res.render('error', { 156 | message: err.message, 157 | error: err 158 | }); 159 | }); 160 | } 161 | // production error handler 162 | // no stacktraces leaked to user 163 | app.use(function(err, req, res, next) { 164 | res.status(err.status || 500); 165 | res.render('error', { 166 | message: err.message, 167 | error: {} 168 | }); 169 | }); 170 | module.exports = app; -------------------------------------------------------------------------------- /views/index.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang="en") 3 | head 4 | include common_headers.pug 5 | - var title = page.currText ? page.currText + ' | 必应每日高清壁纸 - 精彩,从这里开始' : '必应每日高清壁纸 - 精彩,从这里开始' 6 | title #{title} 7 | meta(name="keywords",content="必应壁纸,必应美图,必应每日壁纸,必应每日美图,必应壁纸下载,必应每日壁纸下载,必应每日壁纸接口,必应每日壁纸官方下载,Bing壁纸,微软bing每日壁纸") 8 | link(rel='stylesheet',href="/static/css/progressively.css") 9 | link(rel='stylesheet', href='/static/css/common.css') 10 | link(rel='stylesheet', href='/static/css/main.css') 11 | body(oncontextmenu='self.event.returnValue=false') 12 | header 13 | div.container 14 | a.logo(href="/") 15 | i.icon.icon-bing 16 | span 必应壁纸 17 | nav 18 | ul.menu 19 | li(class=`${page.currPage === 'home' ? 'active' : ''}`) 20 | a(href="/") 21 | i.icon.icon-home 22 | p.text 首页 23 | li(class=`${page.currPage === 'ranking' ? 'active' : ''}`) 24 | a(href="/ranking") 25 | i.icon.icon-ranking 26 | p.text 下载榜 27 | li 28 | a(data-no-instant,href="/static/about.html") 29 | i.icon.icon-about 30 | p.text 关于 31 | 32 | //-
  • 33 | //- 37 | //-
  • 38 | 39 | div.holder 40 | div.mask 41 | div.container 42 | each val in doc 43 | - var location = (val.country && val.city) ? val.country + ', ' + val.city : false; 44 | - location = val.continent ? (location ? val.continent + ', ' + location : val.continent) : location; 45 | div.item 46 | div.card.progressive 47 | img.progressive__img.progressive--not-loaded(data-progressive=val.middle,src=val.small ) 48 | a.mark(href=`/photo/${val.photo}?force=${page.currPage}_${page.curr}`) 49 | div.description 50 | h3 #{val.copyright} 51 | p.calendar 52 | i.icon.icon-calendar 53 | em.t #{val.dt} 54 | if location 55 | p.location 56 | i.icon.icon-location 57 | em.t #{location} 58 | p.view 59 | i.icon.icon-eye 60 | em.t #{val.views} 61 | div.options 62 | a.ctrl.share(href=val.share,target="_blank",rel="nofollow",title="分享到微博") 63 | i.icon.icon-share 64 | em.t 分享 65 | span.ctrl.heart(photo=val.id,likes=val.likes) 66 | i.icon.icon-heart 67 | em.t #{val.likes} 68 | 69 | a.ctrl.download(href=`/photo/${val.photo}?force=download`,target="_blank",rel="nofollow") 70 | i.icon.icon-download 71 | em.t #{val.downloads} 72 | div.page 73 | - var url = page.currPage === 'home' ? '/' : `/${page.currPage}` 74 | - var prev = page.prev === 1 ? `${url}` : `${url}?p=${page.prev}`; 75 | - var next = `${url}?p=${page.next}`; 76 | a(href=prev) 上一页 77 | span #{page.curr} / #{page.pageCount} 78 | a(href=next) 下一页 79 | div.copyright 80 | p 本站所有图片均来自必应搜索 81 | p Copyright © 2016 - 2018 云淡风轻 82 | 83 | script(src="/static/js/jquery-1.12.4.min.js") 84 | script(src="/static/js/progressively.js") 85 | script(src="/static/js/device.js") 86 | script(src="/static/js/baidu-analysis.js") 87 | script(src="/static/js/like.js") 88 | //- script(data-no-instant,src="/static/js/instantclick.min.js") 89 | //- script(data-no-instant,src="/static/js/mine-instantclick.js") 90 | script. 91 | $(function() { 92 | var i = 0; 93 | var timer = setInterval(function(){ 94 | progressively.init(); 95 | if(i++ === 5) clearInterval(timer); 96 | },1000/4); 97 | var click = 'ontouchstart' in window ? 'touchstart' : 'click'; 98 | if (DEVICE.isMobile) { 99 | $(document.body).addClass('mobile'); 100 | } 101 | $(document).on('keydown',function(e){ 102 | e = e.event || window.event; 103 | if(e.keyCode == 123 || (e.ctrlKey&&e.shiftKey&&e.keyCode==73) || (e.ctrlKey&&e.keyCode==85)) return false 104 | }) 105 | }); -------------------------------------------------------------------------------- /utils/dbUtils.js: -------------------------------------------------------------------------------- 1 | // https://github.com/mysqljs/mysql#readme 2 | var mysql = require('mysql'); 3 | // 获取数据库配置 4 | var config = require('../configs/config').mysql_dev; 5 | var objectAssign = require('object-assign'); 6 | // 使用连接池 7 | var pool = mysql.createPool(config); 8 | 9 | module.exports = { 10 | /** 11 | * 获取数据 12 | * @table 表名 13 | * @params 参数{k:v} 14 | * 15 | * or 参数:{body:{k:v},page:{no:1,size:10}} 16 | * body:参数键值(可以直接传条件字符串:'a>b or c>d') 17 | * page:分页对象(no-页码,size-每页显示条数) 18 | * 19 | * @callback 20 | */ 21 | get: function(table, params, callback) { 22 | var defaultPage = { 23 | no: 1, 24 | size: 10 25 | } 26 | var condition = []; 27 | var body = params.body || params; 28 | var page = params.page || {}; 29 | page = objectAssign(defaultPage, page); 30 | var sql = 'select * from ' + table; 31 | if (Object.prototype.toString.call(body) === '[object Object]') { 32 | for (var i in body) { 33 | condition.push(i + '="' + body[i] + '"') 34 | } 35 | if (condition.length > 0) { 36 | sql += ' where ' + condition.join(' and '); 37 | } 38 | } else if (Object.prototype.toString.call(body) === '[object String]') { 39 | sql += ' where ' + body; 40 | } 41 | sql += ' order by id desc limit ' + (page.no - 1) * page.size + ',' + page.size; 42 | //console.log(sql); 43 | module.exports.commonQuery(sql, callback); 44 | }, 45 | /** 46 | * 获得总条数 47 | * @table 表名 48 | * @params 参数{k:v} 49 | * 或者直接是条件字符串: 'weibo=1 and id>2' 50 | * @callback 51 | */ 52 | getCount: function(table, params, callback) { 53 | var sql = 'select count(id) as sum from ' + table; 54 | var _condition = []; 55 | if (Object.prototype.toString.call(params) === '[object Object]') { 56 | for (var i in params) { 57 | _condition.push(i + '="' + params[i] + '"'); 58 | } 59 | if (_condition.length > 0) { 60 | sql += ' where ' + _condition.join(' and '); 61 | } 62 | } else if (Object.prototype.toString.call(params) === '[objcet String]') { 63 | sql += ' where ' + params; 64 | } 65 | module.exports.commonQuery(sql, callback); 66 | }, 67 | /** 68 | * 插入数据 69 | * @table 表名 70 | * @params 参数{k:v} 71 | * @callback 72 | */ 73 | set: function(table, params, callback) { 74 | var keys = []; 75 | var vals = []; 76 | for (var i in params) { 77 | keys.push(i); 78 | vals.push(params[i]); 79 | } 80 | var sql = 'insert into ' + table + '(' + keys.join(',') + ') values("' + vals.join('","') + '")'; 81 | module.exports.commonQuery(sql, callback); 82 | }, 83 | /** 84 | * 修改数据 85 | * @table 表名 86 | * @params 参数{body:{k:v},condition{k:v}} 87 | * body: 修改键值对 88 | * condition: 条件键值对 89 | * @callback 90 | */ 91 | update: function(table, params, callback) { 92 | var body = params.body; 93 | var condition = params.condition; 94 | var body_temp = []; 95 | var condition_temp = []; 96 | for (var i in body) { 97 | body_temp.push(i + '="' + body[i] + '"'); 98 | } 99 | for (var j in condition) { 100 | condition_temp.push(j + '="' + condition[j] + '"'); 101 | } 102 | if (body_temp.length > 0 && condition_temp.length > 0) { 103 | var sql = 'update ' + table + ' set ' + body_temp.join(',') + ' where ' + condition_temp.join(' and '); 104 | module.exports.commonQuery(sql, callback); 105 | } 106 | }, 107 | /** 108 | * 删除数据 109 | * @table 表名 110 | * @params 参数{k:v} 111 | * 注: 如果是or条件,请直接发送字符串参数 params = 'id=1 or id=2' 112 | * 或者: params = 'id in (1,2)' 113 | * @callback 114 | */ 115 | del: function(table, params, callback) { 116 | var condition = []; 117 | var sql = 'delete from ' + table; 118 | if (Object.prototype.toString.call(params) === '[object Object]') { 119 | // 如果参数是对象 120 | for (var i in params) { 121 | condition.push(i + '="' + params[i] + '"'); 122 | } 123 | sql = sql + ' where ' + condition.join(' and '); 124 | } else if (Object.prototype.toString.call(params) === ['object String']) { 125 | // 如果参数是字符串 126 | sql = sql + ' where ' + params; 127 | } 128 | 129 | module.exports.commonQuery(sql, callback); 130 | }, 131 | /** 132 | * 公共查询 133 | * @sql 134 | * @callback 135 | */ 136 | commonQuery: function(sql, callback) { 137 | //console.log(sql); 138 | try { 139 | pool.getConnection(function(err, connection) { 140 | connection.query(sql, function(err, rows) { 141 | connection.release(); 142 | if (!err) { 143 | callback && callback(rows); 144 | } else { 145 | // send mail 146 | console.log(err); 147 | } 148 | }); 149 | }); 150 | } catch (error) { 151 | console.log(error); 152 | console.log(sql); 153 | callback && callback([]); 154 | } 155 | } 156 | }; -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var dbUtils = require('../utils/dbUtils'); 3 | var router = express.Router(); 4 | const moment = require('moment'); 5 | const CDN1 = 'http://h1.ioliu.cn/'; 6 | const ROOT = 'https://bing.ioliu.cn/'; 7 | /* GET home page. */ 8 | router.get('/', function(req, res, next) { 9 | var today = moment().format('YYYYMMDD'); 10 | var pageNo = req.query.p; 11 | pageNo = !!pageNo && Number(pageNo) > 0 ? Number(pageNo) : 1; 12 | var pageSize = 12; // pageSize 13 | dbUtils.commonQuery(`select count(0) as sum from bing where (!ISNULL(qiniu_url) || qiniu_url<>"") and mkt like '%zh-cn%' and enddate<='${today}'`, function(rows) { 14 | var count = rows[0]['sum'] || 0; 15 | if (count > 0) { 16 | var page = { 17 | size: pageSize, 18 | count: count, 19 | pageCount: Math.ceil(count / pageSize), 20 | next: pageNo + 1 > Math.ceil(count / pageSize) ? Math.ceil(count / pageSize) : pageNo + 1, 21 | prev: pageNo - 1 > 0 ? pageNo - 1 : 1, 22 | curr: pageNo > Math.ceil(count / pageSize) ? Math.ceil(count / pageSize) : pageNo, 23 | currText: pageNo === 1 ? '' : '第' + pageNo + '页', 24 | currPage: 'home' 25 | } 26 | var sql = `select id,title,attribute,description,copyright,qiniu_url as photo,city,country,continent,DATE_FORMAT(enddate, '%Y-%m-%d') as dt,likes,views,downloads,thumbnail_pic,original_pic from bing 27 | where (!ISNULL(qiniu_url) || qiniu_url<>"") and mkt like '%zh-cn%' and enddate<='${today}' 28 | order by id desc 29 | limit ${(page.curr-1)*page.size},${page.size}`; 30 | dbUtils.commonQuery(sql, function(rs) { 31 | if (rs.length > 0) { 32 | var data = convert(rs) 33 | res.render('index', { 34 | doc: data, 35 | page: page 36 | }); 37 | }else{ 38 | res.redirect('/') 39 | } 40 | }); 41 | } 42 | }); 43 | }); 44 | 45 | 46 | /* GET ranking listing. */ 47 | router.get('/ranking', function(req, res, next) { 48 | var isAjax = !!req.headers['x-requested-with']; 49 | var pageNo = req.query.p; 50 | pageNo = !!pageNo && Number(pageNo) > 0 ? Number(pageNo) : 1; 51 | var pageSize = 12; // pageSize 52 | dbUtils.commonQuery(`select count(0) as sum from bing where (!ISNULL(qiniu_url) || qiniu_url<>"") and mkt like '%zh-cn%'`, function(rows) { 53 | var count = rows[0]['sum'] || 0; 54 | if (count > 0) { 55 | var page = { 56 | size: pageSize, 57 | count: count, 58 | pageCount: Math.ceil(count / pageSize), 59 | next: pageNo + 1 > Math.ceil(count / pageSize) ? Math.ceil(count / pageSize) : pageNo + 1, 60 | prev: pageNo - 1 > 0 ? pageNo - 1 : 1, 61 | curr: pageNo > Math.ceil(count / pageSize) ? Math.ceil(count / pageSize) : pageNo, 62 | currText: pageNo === 1 ? '下载榜' : '第' + pageNo + '页', 63 | currPage: 'ranking' 64 | } 65 | if (pageNo > page.curr && !isAjax) { 66 | res.redirect(`/?p=${page.curr}`); 67 | } 68 | var sql = `select id,title,attribute,description,copyright,qiniu_url as photo,city,country,continent,DATE_FORMAT(enddate, '%Y-%m-%d') as dt,likes,views,downloads,thumbnail_pic,original_pic from bing 69 | where (!ISNULL(qiniu_url) || qiniu_url<>"") and mkt like '%zh-cn%' 70 | order by downloads desc 71 | limit ${(page.curr-1)*page.size},${page.size}`; 72 | dbUtils.commonQuery(sql, function(rs) { 73 | if (rs.length > 0) { 74 | var data = convert(rs) 75 | res.render('index', { 76 | doc: data, 77 | page: page 78 | }); 79 | }else{ 80 | res.redirect('/') 81 | } 82 | }); 83 | } 84 | }); 85 | }); 86 | 87 | function convert(rs){ 88 | var data = []; 89 | for (var i in rs) { 90 | var temp = rs[i]; 91 | /** 92 | * 1024x576 93 | * 120x67 94 | */ 95 | var middle = `${CDN1}bing/${temp['photo']}_800x480.jpg`; 96 | var small = `${CDN1}bing/${temp['photo']}_400x240.jpg`; 97 | var sharepic = `${CDN1}bing/${temp['photo']}_1366x768.jpg`; 98 | var desc = `#必应壁纸# ${temp['dt']} / #${temp['title']}# ${temp['description']}`; 99 | var share = `http://service.weibo.com/share/share.php?url=${ROOT}photo/${temp['photo']}&appkey=1833831541&pic=${sharepic}&ralateUid=5893653736&title=${encodeURIComponent(desc.substring(0,126)+'...')}`; 100 | 101 | data.push({ 102 | id: temp['id'], 103 | title: temp['title'], 104 | attribute: temp['attribute'], 105 | description: temp['description'], 106 | copyright: temp['copyright'], 107 | photo: temp['photo'], 108 | city: temp['city'], 109 | country: temp['country'], 110 | continent: temp['continent'], 111 | middle: middle, 112 | small: small, 113 | dt: temp['dt'], 114 | likes: temp['likes'], 115 | views: temp['views'], 116 | downloads: temp['downloads'], 117 | share:share 118 | }); 119 | } 120 | 121 | return data 122 | } 123 | 124 | module.exports = router; -------------------------------------------------------------------------------- /static/js/instantclick.min.js: -------------------------------------------------------------------------------- 1 | /* InstantClick 3.1.0 | (C) 2014 Alexandre Dieulot | http://instantclick.io/license */ 2 | var InstantClick=function(d,e){function w(a){var b=a.indexOf("#");return 0>b?a:a.substr(0,b)}function z(a){for(;a&&"A"!=a.nodeName;)a=a.parentNode;return a}function A(a){var b=e.protocol+"//"+e.host;if(!(b=a.target||a.hasAttribute("download")||0!=a.href.indexOf(b+"/")||-1+new Date-500||(a=z(a.target))&&A(a)&&x(a.href)}function N(a){G>+new Date-500||(a=z(a.target))&&A(a)&&(a.addEventListener("mouseout",T),H?(O=a.href,l=setTimeout(x,H)):x(a.href))}function U(a){G=+new Date;(a=z(a.target))&&A(a)&&(D?a.removeEventListener("mousedown", 5 | M):a.removeEventListener("mouseover",N),x(a.href))}function V(a){var b=z(a.target);!b||!A(b)||1p.readyState)&&0!=p.status){q.ready=+new Date-q.start;if(p.getResponseHeader("Content-Type").match(/\/(x|ht|xht)ml/)){var a=d.implementation.createHTMLDocument("");a.documentElement.innerHTML=p.responseText.replace(//gi,"");y=a.title; 6 | u=a.body;var b=t("receive",r,u,y);b&&("body"in b&&(u=b.body),"title"in b&&(y=b.title));b=w(r);h[b]={body:u,title:y,scrollY:b in h?h[b].scrollY:0};for(var a=a.head.children,b=0,c,g=a.length-1;0<=g;g--)if(c=a[g],c.hasAttribute("data-instant-track")){c=c.getAttribute("href")||c.getAttribute("src")||c.innerHTML;for(var e=E.length-1;0<=e;e--)E[e]==c&&b++}b!=E.length&&(F=!0)}else F=!0;m&&(m=!1,P(r))}}function L(a){d.body.addEventListener("touchstart",U,!0);D?d.body.addEventListener("mousedown",M,!0):d.body.addEventListener("mouseover", 7 | N,!0);d.body.addEventListener("click",V,!0);if(!a){a=d.body.getElementsByTagName("script");var b,c,g,e;i=0;for(j=a.length;i+new Date-(q.start+q.display)||(l&&(clearTimeout(l),l=!1),a||(a=O),v&&(a==r||m))||(v=!0,m=!1,r=a,F=u=!1,q={start:+new Date},t("fetch"), 8 | p.open("GET",a),p.send())}function P(a){"display"in q||(q.display=+new Date-q.start);l||!v?l&&r&&r!=a?e.href=a:(x(a),C.start(0,!0),t("wait"),m=!0):m?e.href=a:F?e.href=r:u?(h[k].scrollY=pageYOffset,m=v=!1,K(y,u,r)):(C.start(0,!0),t("wait"),m=!0)}var I=navigator.userAgent,S=-1b;b++)a[b]+"Transform"in h.style&&(k=a[b]+"Transform");var c="transition";if(!(c in h.style))for(b=0;3>b;b++)a[b]+"Transition"in 11 | h.style&&(c="-"+a[b].toLowerCase()+"-"+c);a=d.createElement("style");a.innerHTML="#instantclick{position:"+(Q?"absolute":"fixed")+";top:0;left:0;width:100%;pointer-events:none;z-index:2147483647;"+c+":opacity .25s .1s}.instantclick-bar{background:#29d;width:100%;margin-left:-100%;height:2px;"+c+":all .25s}";d.head.appendChild(a);Q&&(m(),addEventListener("resize",m),addEventListener("scroll",m))},start:a,done:e}}(),R="pushState"in history&&(!I.match("Android")||I.match("Chrome/"))&&"file:"!=e.protocol; 12 | return{supported:R,init:function(){if(!k)if(R){for(var a=arguments.length-1;0<=a;a--){var b=arguments[a];!0===b?J=!0:"mousedown"==b?D=!0:"number"==typeof b&&(H=b)}k=w(e.href);h[k]={body:d.body,title:d.title,scrollY:pageYOffset};for(var b=d.head.children,c,a=b.length-1;0<=a;a--)c=b[a],c.hasAttribute("data-instant-track")&&(c=c.getAttribute("href")||c.getAttribute("src")||c.innerHTML,E.push(c));p=new XMLHttpRequest;p.addEventListener("readystatechange",W);L(!0);C.init();t("change",!0);addEventListener("popstate", 13 | function(){var a=w(e.href);a!=k&&(a in h?(h[k].scrollY=pageYOffset,k=a,K(h[a].title,h[a].body,!1,h[a].scrollY)):e.href=e.href)})}else t("change",!0)},on:function(a,b){B[a].push(b)}}}(document,location); 14 | -------------------------------------------------------------------------------- /static/css/common.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | -webkit-box-sizing: border-box; 5 | box-sizing: border-box; 6 | -webkit-transition: all .3s linear; 7 | transition: all .3s linear; 8 | } 9 | 10 | body { 11 | margin: 0; 12 | line-height: 1.5; 13 | font-size: .16rem; 14 | position: relative; 15 | background-color: #FFF; 16 | -webkit-text-size-adjust: 100%; 17 | -ms-text-size-adjust: 100%; 18 | text-size-adjust: 100%; 19 | -webkit-overflow-scrolling: touch; 20 | -webkit-font-smoothing: antialiased; 21 | color: #0C8484; 22 | font-family: "Source Han Sans SC", "Noto Sans CJK SC", "Source Han Sans CN", "Noto Sans SC", "Source Han Sans TC", "Noto Sans CJK TC", "PingFang SC", "Hiragino Sans GB", "Neue Haas Grotesk Text Pro", "Microsoft YaHei", "Microsoft JhengHei", sans-serif; 23 | } 24 | 25 | .clearfix::before { 26 | display: table; 27 | content: ""; 28 | } 29 | 30 | .clearfix::after { 31 | display: table; 32 | clear: both; 33 | content: ""; 34 | } 35 | 36 | .float-right { 37 | float: right !important; 38 | } 39 | 40 | .float-left { 41 | float: left !important; 42 | } 43 | 44 | .float-none { 45 | float: none !important; 46 | } 47 | 48 | a { 49 | text-decoration: none; 50 | -webkit-transition: color .15s linear; 51 | transition: color .15s linear; 52 | color: #0C8484; 53 | } 54 | 55 | a:hover { 56 | color: #888; 57 | } 58 | 59 | ul, 60 | li { 61 | list-style: none; 62 | padding: 0; 63 | margin: 0; 64 | display: inline-block; 65 | vertical-align: middle; 66 | } 67 | 68 | header { 69 | padding: .1rem 0; 70 | display: block; 71 | background: rgba(255, 255, 255, 0.93); 72 | height: .7rem; 73 | width: 100%; 74 | z-index: 99; 75 | } 76 | 77 | header .container { 78 | padding: 0 .5rem; 79 | } 80 | 81 | header .logo { 82 | display: inline-block; 83 | height: 100%; 84 | font-size: .3rem; 85 | color: #0C8484; 86 | } 87 | 88 | header .logo i { 89 | font-size: .4rem; 90 | } 91 | 92 | header nav { 93 | float: right; 94 | } 95 | 96 | header nav .menu { 97 | height: .6rem; 98 | display: block; 99 | -webkit-transition: all .3s ease; 100 | transition: all .3s ease; 101 | } 102 | 103 | header nav .menu li { 104 | font-size: .14rem; 105 | display: inline-block; 106 | height: 100%; 107 | vertical-align: middle; 108 | overflow: hidden; 109 | } 110 | 111 | header nav .menu li.active a { 112 | color: #f15151; 113 | } 114 | 115 | header nav .menu li a { 116 | display: block; 117 | height: 100%; 118 | padding: 0 .25rem; 119 | text-align: center; 120 | line-height: .55rem; 121 | } 122 | 123 | header nav .menu li a .icon { 124 | font-size: .25rem; 125 | display: none; 126 | } 127 | 128 | header nav .menu li .search { 129 | height: 100%; 130 | position: relative; 131 | display: block; 132 | } 133 | 134 | header nav .menu li .search input { 135 | font-size: .12rem; 136 | border: 1px solid #dedede; 137 | -webkit-border-radius: 5px; 138 | border-radius: 5px; 139 | height: .4rem; 140 | margin: .1rem 0; 141 | padding: 0 .1rem; 142 | outline: none; 143 | width: 1.68rem; 144 | -webkit-transition: all .2s ease; 145 | transition: all .2s ease; 146 | } 147 | 148 | header nav .menu li .search input:focus { 149 | margin-left: 0; 150 | opacity: 1; 151 | border: 1px solid #a7afef; 152 | } 153 | 154 | header nav .menu li .search input:focus+.search-btn .icon { 155 | color: #a7afef; 156 | } 157 | 158 | header nav .menu li .search .search-btn { 159 | border: none; 160 | position: absolute; 161 | right: .05rem; 162 | top: -0.04rem; 163 | padding: .05rem; 164 | background: transparent; 165 | outline: none; 166 | cursor: pointer; 167 | } 168 | 169 | header nav .menu li .search .search-btn .icon { 170 | display: block; 171 | color: #dedede; 172 | -webkit-transition: all .2s ease; 173 | transition: all .2s ease; 174 | } 175 | 176 | html { 177 | font-size: 100px; 178 | } 179 | 180 | @media only screen and (max-width: 768px) { 181 | html { 182 | font-size: 115px; 183 | } 184 | html .holder { 185 | height: .65rem; 186 | } 187 | html header { 188 | text-align: center; 189 | } 190 | html header .container { 191 | padding: 0 !important; 192 | } 193 | html nav { 194 | float: none; 195 | } 196 | html nav .menu { 197 | position: absolute; 198 | top: .65rem; 199 | left: 0; 200 | width: 100%; 201 | display: none !important; 202 | transition: all .5s ease; 203 | } 204 | html nav .menu { 205 | display: block !important; 206 | z-index: 2; 207 | } 208 | html nav .menu li { 209 | background: rgba(255, 255, 255, 0.9); 210 | width: 33.33%; 211 | } 212 | html nav .menu li .text { 213 | display: none; 214 | } 215 | html nav .menu li .icon { 216 | display: inline-block !important; 217 | } 218 | html nav .menu li.search-wrap { 219 | width: 100%; 220 | padding: 0 .1rem; 221 | } 222 | html nav .menu li.search-wrap input { 223 | width: 100%; 224 | } 225 | } 226 | 227 | @media only screen and (max-width: 640px) { 228 | html { 229 | font-size: 110px; 230 | } 231 | html .item { 232 | width: 100% !important; 233 | } 234 | html .item .card .mark, 235 | html .item .card .description, 236 | html .item .card .options { 237 | opacity: 1 !important; 238 | } 239 | html .sub { 240 | display: none; 241 | } 242 | } 243 | 244 | @media only screen and (max-width: 480px) { 245 | html { 246 | font-size: 100px; 247 | } 248 | } 249 | 250 | @media only screen and (max-width: 320px) { 251 | html { 252 | font-size: 85px; 253 | } 254 | } -------------------------------------------------------------------------------- /utils/weiboUtils.js: -------------------------------------------------------------------------------- 1 | var request = require('superagent'); 2 | var moment = require('moment'); 3 | var bingUtils = require('./bingUtils'); 4 | var commonUtils = require('./commonUtils'); 5 | var dbUtils = require('./dbUtils'); 6 | var fs = require('fs'); 7 | var weibo = require('../configs/config').weibo; 8 | var mailUtils = require('./mailUtils'); 9 | var cookie = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36' }; 10 | var update_url_text = 'https://api.weibo.com/2/statuses/upload_url_text.json'; 11 | var share = 'https://api.weibo.com/2/statuses/share.json'; 12 | module.exports = { 13 | /** 14 | * 发送微博 15 | * @callback 16 | * @isAuto 是否自动发送(true,false) 17 | */ 18 | update: function(callback, isAuto) { 19 | // 查询数据库中是否存在今天的新数据 20 | dbUtils.get('bing', { 21 | weibo: 0, 22 | enddate: moment().format('YYYYMMDD') 23 | }, function(rows) { 24 | if (rows.length === 0) {} else { 25 | // 如果存在,但没有发送微博 26 | var data = rows[0]; 27 | module.exports.commonSend(data, callback, isAuto); 28 | } 29 | }); 30 | 31 | 32 | 33 | 34 | }, 35 | /** 36 | * 获取短链 37 | */ 38 | shorten: function(url, callback) { 39 | module.exports.checkOauth(function(token) { 40 | request 41 | .post('https://api.weibo.com/2/short_url/shorten.json') 42 | .type('form') 43 | .set(cookie) 44 | .send({ 45 | access_token: token, 46 | url_long: url 47 | }) 48 | .end(function(err, res) { 49 | commonUtils.convert(err, res, callback); 50 | }); 51 | }); 52 | }, 53 | /** 54 | * 公共发送微博方法 55 | */ 56 | commonSend: function(data, callback, isAuto) { 57 | module.exports.checkOauth(function(token) { 58 | var date = data.enddate; 59 | var y = date.substr(0, 4); 60 | var m = date.substr(4, 2); 61 | var d = date.substr(6, 2); 62 | var full = y + '-' + m + '-' + d; 63 | var post = {}; 64 | if (data.title && data.description && data.mkt.indexOf('zh-cn') > -1) { 65 | var status = ('#必应壁纸# ' + full + ' / #' + data.title + '# ' + data.description).slice(0, 138) + `... http://bing.ioliu.cn/photo/${data.qiniu_url}?from=weibo`; 66 | post = { 67 | access_token: token, 68 | status: status, 69 | //url: data.url, 70 | lat: data.latitude, 71 | long: data.longitude, 72 | annotations: { 73 | place: { 74 | title: data.title, 75 | url: data.copyrightlink, 76 | locality: data.city, 77 | country_name: data.country, 78 | region: data.country, 79 | latitude: data.latitude, 80 | longitude: data.longitude 81 | } 82 | } 83 | }; 84 | } else { 85 | var status = '#Bing Picture# ' + full + ' / ' + data.copyright + ` from ${data.mkt} http://bing.ioliu.cn/photo/${data.qiniu_url}?from=weibo`; 86 | post = { 87 | access_token: token, 88 | status: status, 89 | //url: data.url, 90 | annotations: { 91 | place: { 92 | title: data.copyright, 93 | } 94 | } 95 | }; 96 | } 97 | request 98 | .post(share) 99 | .type('form') 100 | .set(cookie) 101 | .send(post) 102 | .end(function(err, response) { 103 | if (err) { 104 | console.log(err) 105 | return; 106 | } 107 | commonUtils.convert(err, response, function(body) { 108 | data['weibo'] = 1; 109 | // data['thumbnail_pic'] = body.thumbnail_pic; 110 | // data['bmiddle_pic'] = body.bmiddle_pic; 111 | // data['original_pic'] = body.original_pic; 112 | dbUtils.update('bing', { 113 | body: { 114 | weibo: 1, 115 | // thumbnail_pic: data.thumbnail_pic, 116 | // bmiddle_pic: data.bmiddle_pic, 117 | // original_pic: data.original_pic 118 | }, 119 | condition: { 120 | id: data.id 121 | } 122 | }, function(rows) { 123 | callback && callback(body); 124 | }); 125 | }); 126 | }); 127 | // module.exports.fetchToLocal(data.url, function(bb) { 128 | // //post['pic'] = bb.toString('base64'); 129 | 130 | 131 | // }) 132 | }, isAuto); 133 | 134 | }, 135 | /** 136 | * 检查授权 137 | * @callback 138 | * @isAuto 是否自动发送(true/false) 139 | */ 140 | checkOauth: function(callback, isAuto) { 141 | var uid = !!weibo.USER_UID ? weibo.USER_UID : weibo.MASTER_UID; 142 | if (isAuto) { 143 | uid = weibo.MASTER_UID; 144 | } 145 | dbUtils.get('bing_session', { 146 | body: { 147 | uid: uid 148 | } 149 | }, function(rows) { 150 | if (rows.length === 0) { 151 | callback && callback(-1); 152 | } else { 153 | callback && callback(rows[0].token); 154 | } 155 | }) 156 | }, 157 | 158 | fetchToLocal: function(url, callback) { 159 | let name = Math.random().toString(36).substr(2, 15); 160 | const stream = fs.createWriteStream('./' + name + '.jpg'); 161 | request.get(url).set(cookie).end(function(err, response) { 162 | callback && callback(response.body); 163 | }); 164 | } 165 | } -------------------------------------------------------------------------------- /static/css/main.css: -------------------------------------------------------------------------------- 1 | .container { 2 | font-size: 0; 3 | margin: 0 auto; 4 | position: relative; 5 | } 6 | 7 | .container .item { 8 | display: inline-block; 9 | width: 33.33%; 10 | height: auto; 11 | position: relative; 12 | border: none; 13 | } 14 | 15 | .container .item .card { 16 | display: block; 17 | height: 100%; 18 | background: #dedede; 19 | position: relative; 20 | cursor: -webkit-zoom-in; 21 | cursor: zoom-in; 22 | } 23 | 24 | .container .item .card img { 25 | width: 100%; 26 | border: 0; 27 | } 28 | 29 | .container .item .card .mark { 30 | width: 100%; 31 | height: 100%; 32 | position: absolute; 33 | top: 0; 34 | left: 0; 35 | opacity: 0; 36 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(0, 0, 0, 0.4)), color-stop(40%, transparent), color-stop(60%, transparent), color-stop(60%, rgba(0, 0, 0, 0.6))); 37 | background: -webkit-linear-gradient(top, rgba(0, 0, 0, 0.4) 0, transparent 40%, transparent 60%, rgba(0, 0, 0, 0.6)); 38 | background: linear-gradient(180deg, rgba(0, 0, 0, 0.4) 0, transparent 40%, transparent 60%, rgba(0, 0, 0, 0.6)); 39 | -webkit-filter: blur(15px); 40 | filter: blur(15px); 41 | z-index: 1; 42 | -webkit-transition: all .5s ease; 43 | transition: all .5s ease; 44 | } 45 | 46 | .container .item .card .options { 47 | font-size: .12rem; 48 | position: absolute; 49 | right: .05rem; 50 | bottom: .05rem; 51 | padding: .05rem; 52 | display: block; 53 | text-align: right; 54 | -webkit-transition: all .5s ease; 55 | transition: all .5s ease; 56 | opacity: 0; 57 | z-index: 3; 58 | } 59 | 60 | .container .item .card .ctrl { 61 | display: inline-block; 62 | height: .32rem; 63 | line-height: .31rem; 64 | background-color: rgba(255, 255, 255, 0.8); 65 | border: 1px solid transparent; 66 | cursor: pointer; 67 | -webkit-border-radius: .06rem; 68 | border-radius: .06rem; 69 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); 70 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); 71 | text-align: center; 72 | padding: 0 .1rem; 73 | } 74 | 75 | .container .item .card .ctrl+.ctrl { 76 | margin-left: .05rem; 77 | } 78 | 79 | .container .item .card .ctrl.heart .icon { 80 | color: #f15151 !important; 81 | } 82 | 83 | .container .item .card .ctrl:hover { 84 | background: #fff; 85 | } 86 | 87 | .container .item .card .t { 88 | margin-left: .05rem; 89 | font-style: normal; 90 | } 91 | 92 | .container .item .card .description { 93 | position: absolute; 94 | width: 100%; 95 | left: 0; 96 | top: 0; 97 | padding: .05rem; 98 | display: block; 99 | color: #fff; 100 | opacity: 0; 101 | -webkit-transition: all .5s ease; 102 | transition: all .5s ease; 103 | z-index: 1; 104 | pointer-events: none; 105 | } 106 | 107 | .container .item .card .description h3 { 108 | font-size: .14rem; 109 | letter-spacing: .01rem; 110 | font-weight: 400; 111 | color: #fff; 112 | padding: .02rem; 113 | } 114 | 115 | .container .item .card .description p { 116 | font-size: .12rem; 117 | } 118 | 119 | .container .item .card .description p .icon { 120 | color: #dedede; 121 | } 122 | 123 | .container .item .card .description p .t { 124 | color: #dedede; 125 | } 126 | 127 | .container .item:hover .mark { 128 | opacity: 1; 129 | } 130 | 131 | .container .item:hover .options { 132 | opacity: 1; 133 | } 134 | 135 | .container .item:hover .description { 136 | opacity: 1; 137 | } 138 | 139 | .mobile .mark, 140 | .mobile .options, 141 | .mobile .description { 142 | opacity: 1 !important; 143 | } 144 | 145 | .ctrl.hide { 146 | display: none; 147 | } 148 | 149 | @media only screen and (max-width: 980px) { 150 | .item { 151 | width: 50% !important; 152 | } 153 | } 154 | 155 | .page { 156 | text-align: center; 157 | padding: .3rem; 158 | } 159 | 160 | .page a { 161 | display: inline-block; 162 | height: .32rem; 163 | line-height: .31rem; 164 | background-color: rgba(255, 255, 255, 0.8); 165 | border: 1px solid #dedede; 166 | cursor: pointer; 167 | -webkit-border-radius: .06rem; 168 | border-radius: .06rem; 169 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); 170 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); 171 | text-align: center; 172 | padding: 0 .1rem; 173 | } 174 | 175 | .page span { 176 | display: inline-block; 177 | padding: 0 .1rem; 178 | } 179 | 180 | .copyright { 181 | padding-bottom: .3rem; 182 | text-align: center; 183 | } 184 | 185 | 186 | /**********************Detail.css***********************/ 187 | 188 | .detail { 189 | height: 100vh; 190 | position: relative; 191 | background: rgba(1, 1, 1, .5); 192 | } 193 | 194 | .detail .mark { 195 | position: absolute; 196 | top: 0; 197 | left: 0; 198 | right: 0; 199 | bottom: 0; 200 | z-index: -1; 201 | background-size: cover; 202 | background-position: center; 203 | -webkit-transition: all .5s ease; 204 | transition: all .5s ease; 205 | -webkit-filter: blur(30px); 206 | filter: blur(20px); 207 | } 208 | 209 | .detail .preview img { 210 | width: 0; 211 | height: 0; 212 | border: 0; 213 | } 214 | 215 | .detail .preview .progressive { 216 | background: transparent; 217 | } 218 | 219 | .detail .preview .options { 220 | font-size: .12rem; 221 | padding: .1rem; 222 | width: 100%; 223 | display: block; 224 | text-align: right; 225 | -webkit-transition: all .5s ease; 226 | transition: all .5s ease; 227 | position: absolute; 228 | right: 0; 229 | top: 0; 230 | } 231 | 232 | .detail .preview .ctrl { 233 | display: inline-block; 234 | height: .32rem; 235 | line-height: .31rem; 236 | background-color: rgba(255, 255, 255, 0.8); 237 | border: 1px solid transparent; 238 | cursor: pointer; 239 | -webkit-border-radius: .06rem; 240 | border-radius: .06rem; 241 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); 242 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); 243 | text-align: center; 244 | padding: 0 .1rem; 245 | } 246 | 247 | .detail .preview .ctrl+.ctrl { 248 | margin-left: .05rem; 249 | } 250 | 251 | .detail .preview .ctrl.heart .icon { 252 | color: #f15151 !important; 253 | } 254 | 255 | .detail .preview .ctrl.left { 256 | left: .15rem; 257 | top: .1rem; 258 | position: absolute; 259 | } 260 | 261 | .detail .preview .ctrl:hover { 262 | background: #fff; 263 | } 264 | 265 | .detail .preview .ctrl .t { 266 | font-style: normal; 267 | margin-left: .05rem; 268 | display: inline-block; 269 | vertical-align: top; 270 | } 271 | 272 | .detail .preview .description { 273 | position: absolute; 274 | left: 0; 275 | bottom: 0; 276 | padding: .1rem; 277 | display: block; 278 | width: 100%; 279 | color: #fff; 280 | background: rgba(1, 1, 1, 0.5); 281 | } 282 | 283 | .detail .preview .description .title { 284 | font-size: .2rem; 285 | } 286 | 287 | .detail .preview .description .sub { 288 | color: #c6cccc; 289 | } 290 | 291 | .detail .preview .description .t { 292 | font-style: normal; 293 | margin-left: .05rem; 294 | display: inline-block; 295 | vertical-align: top; 296 | } 297 | 298 | .detail .preview .description .show+.show { 299 | margin-left: .3rem; 300 | } -------------------------------------------------------------------------------- /static/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 关于 | 必应每日高清壁纸 - 精彩,从这里开始 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 96 | 97 | 98 | 99 | 100 |
    101 |
    102 |
    103 | 104 | 105 |
    106 |

    107 | 必应壁纸 108 |

    109 |

    Bing 每日壁纸,你值得拥有

    110 |
    111 |

    最开始做 Bing 壁纸的目的很纯粹,就是看到必应搜索 cn.bing.com 官网的壁纸很赞,但是想找到之前的壁纸就有点困难,然后就自己用PHP语言开发了一个,每天同步cn.bing.com 官网的壁纸,同步抓取每日故事,存在自己的数据库里。源代码可以在GitHub上找到。当时主机是用的 112 | 阿里云送的免费两年的云主机,可能大部分人都没见过。整体布局如下:

    113 | 114 |

    由于我不是专业的 PHP工程师(前端攻城狮一枚),PHP版本 到后期维护就越来越困难,每次花费在改问题的时间越来越长,于是后面干脆就用Node.js 重写了Bing 壁纸,将版本号升级到了2.0版本(哈哈,自己定义的,无需在意),图片存储采用的七牛云存储,毕竟早期的流量小,免费额度还是撑得住的。程序部署在DaoCloud上,有兴趣的可以去看看Dockerfile。并且将域名由 117 | api.ioliu.cn 换成了 bing.ioliu.cn,这个版本可能大家就更熟悉一些,应该是上线时间最长的版本了吧。 118 |

    119 |

    后面由于 道客(DocCloud) 访问速度慢,最后干脆咬牙买了阿里云主机(ECS)1核/2G,并更新了SSL证书,相信现在的你一定发现了本站的访问速度简直是超级快(哈哈,自我陶醉下)。

    120 | 121 |

    截止到 2017-03-05,Bing 壁纸 项目已经稳步运行了一年,相信在未来的日子里,Bing 壁纸 会越来越好,功能越来越完善。现在,Bing壁纸 开通了留言功能,大家有什么想说的可以在下面留言咯\(^o^)/YES!

    122 |

    为了把壁纸的美好带给大家,攻城狮小哥花费了很多个人时间和精力进行开发,甚至自己掏钱购买服务器,看攻城狮小哥这么可怜,快来打赏杯咖啡吧。≡ω≡

    123 | 124 |

    大家可以先扫一扫支付宝红包码,领红包再打赏,金额不多还不花钱~~

    125 |
    126 |
    127 | 支付宝红包 128 |

    支付宝红包码

    129 |
    130 |
    131 | 云淡风轻 Alipay 132 |

    支付宝打赏

    133 |
    134 |
    135 | 云淡风轻 WeChat Pay 136 |

    微信打赏

    137 |
    138 |
    139 |
    140 | 149 |
    150 |
    151 | 152 | 153 | Fork me on GitHub 154 | 155 | 156 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /static/js/respond.js: -------------------------------------------------------------------------------- 1 | /*! Respond.js v1.4.2: min/max-width media query polyfill 2 | * Copyright 2014 Scott Jehl 3 | * Licensed under MIT 4 | * http://j.mp/respondjs */ 5 | ! function (a) { 6 | "use strict"; 7 | a.matchMedia = a.matchMedia || function (a) { 8 | var b, c = a.documentElement 9 | , d = c.firstElementChild || c.firstChild 10 | , e = a.createElement("body") 11 | , f = a.createElement("div"); 12 | return f.id = "mq-test-1", f.style.cssText = "position:absolute;top:-100em", e.style.background = "none", e.appendChild(f) 13 | , function (a) { 14 | return f.innerHTML = '­', c.insertBefore(e, d), b = 42 === f.offsetWidth, c.removeChild(e), { 15 | matches: b 16 | , media: a 17 | } 18 | } 19 | }(a.document) 20 | }(this) 21 | , function (a) { 22 | "use strict"; 23 | 24 | function b() { 25 | v(!0) 26 | } 27 | var c = {}; 28 | a.respond = c, c.update = function () {}; 29 | var d = [] 30 | , e = function () { 31 | var b = !1; 32 | try { 33 | b = new a.XMLHttpRequest 34 | } 35 | catch (c) { 36 | b = new a.ActiveXObject("Microsoft.XMLHTTP") 37 | } 38 | return function () { 39 | return b 40 | } 41 | }() 42 | , f = function (a, b) { 43 | var c = e(); 44 | c && (c.open("GET", a, !0), c.onreadystatechange = function () { 45 | 4 !== c.readyState || 200 !== c.status && 304 !== c.status || b(c.responseText) 46 | }, 4 !== c.readyState && c.send(null)) 47 | } 48 | , g = function (a) { 49 | return a.replace(c.regex.minmaxwh, "").match(c.regex.other) 50 | }; 51 | if (c.ajax = f, c.queue = d, c.unsupportedmq = g, c.regex = { 52 | media: /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi 53 | , keyframes: /@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi 54 | , comments: /\/\*[^*]*\*+([^/][^*]*\*+)*\//gi 55 | , urls: /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g 56 | , findStyles: /@media *([^\{]+)\{([\S\s]+?)$/ 57 | , only: /(only\s+)?([a-zA-Z]+)\s?/ 58 | , minw: /\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/ 59 | , maxw: /\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/ 60 | , minmaxwh: /\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi 61 | , other: /\([^\)]*\)/g 62 | }, c.mediaQueriesSupported = a.matchMedia && null !== a.matchMedia("only all") && a.matchMedia("only all").matches, !c.mediaQueriesSupported) { 63 | var h, i, j, k = a.document 64 | , l = k.documentElement 65 | , m = [] 66 | , n = [] 67 | , o = [] 68 | , p = {} 69 | , q = 30 70 | , r = k.getElementsByTagName("head")[0] || l 71 | , s = k.getElementsByTagName("base")[0] 72 | , t = r.getElementsByTagName("link") 73 | , u = function () { 74 | var a, b = k.createElement("div") 75 | , c = k.body 76 | , d = l.style.fontSize 77 | , e = c && c.style.fontSize 78 | , f = !1; 79 | return b.style.cssText = "position:absolute;font-size:1em;width:1em", c || (c = f = k.createElement("body"), c.style.background = "none"), l.style.fontSize = "100%", c.style.fontSize = "100%", c.appendChild(b), f && l.insertBefore(c, l.firstChild), a = b.offsetWidth, f ? l.removeChild(c) : c.removeChild(b), l.style.fontSize = d, e && (c.style.fontSize = e), a = j = parseFloat(a) 80 | } 81 | , v = function (b) { 82 | var c = "clientWidth" 83 | , d = l[c] 84 | , e = "CSS1Compat" === k.compatMode && d || k.body[c] || d 85 | , f = {} 86 | , g = t[t.length - 1] 87 | , p = (new Date).getTime(); 88 | if (b && h && q > p - h) return a.clearTimeout(i), i = a.setTimeout(v, q), void 0; 89 | h = p; 90 | for (var s in m) 91 | if (m.hasOwnProperty(s)) { 92 | var w = m[s] 93 | , x = w.minw 94 | , y = w.maxw 95 | , z = null === x 96 | , A = null === y 97 | , B = "em"; 98 | x && (x = parseFloat(x) * (x.indexOf(B) > -1 ? j || u() : 1)), y && (y = parseFloat(y) * (y.indexOf(B) > -1 ? j || u() : 1)), w.hasquery && (z && A || !(z || e >= x) || !(A || y >= e)) || (f[w.media] || (f[w.media] = []), f[w.media].push(n[w.rules])) 99 | } 100 | for (var C in o) o.hasOwnProperty(C) && o[C] && o[C].parentNode === r && r.removeChild(o[C]); 101 | o.length = 0; 102 | for (var D in f) 103 | if (f.hasOwnProperty(D)) { 104 | var E = k.createElement("style") 105 | , F = f[D].join("\n"); 106 | E.type = "text/css", E.media = D, r.insertBefore(E, g.nextSibling), E.styleSheet ? E.styleSheet.cssText = F : E.appendChild(k.createTextNode(F)), o.push(E) 107 | } 108 | } 109 | , w = function (a, b, d) { 110 | var e = a.replace(c.regex.comments, "").replace(c.regex.keyframes, "").match(c.regex.media) 111 | , f = e && e.length || 0; 112 | b = b.substring(0, b.lastIndexOf("/")); 113 | var h = function (a) { 114 | return a.replace(c.regex.urls, "$1" + b + "$2$3") 115 | } 116 | , i = !f && d; 117 | b.length && (b += "/"), i && (f = 1); 118 | for (var j = 0; f > j; j++) { 119 | var k, l, o, p; 120 | i ? (k = d, n.push(h(a))) : (k = e[j].match(c.regex.findStyles) && RegExp.$1, n.push(RegExp.$2 && h(RegExp.$2))), o = k.split(","), p = o.length; 121 | for (var q = 0; p > q; q++) l = o[q], g(l) || m.push({ 122 | media: l.split("(")[0].match(c.regex.only) && RegExp.$2 || "all" 123 | , rules: n.length - 1 124 | , hasquery: l.indexOf("(") > -1 125 | , minw: l.match(c.regex.minw) && parseFloat(RegExp.$1) + (RegExp.$2 || "") 126 | , maxw: l.match(c.regex.maxw) && parseFloat(RegExp.$1) + (RegExp.$2 || "") 127 | }) 128 | } 129 | v() 130 | } 131 | , x = function () { 132 | if (d.length) { 133 | var b = d.shift(); 134 | f(b.href, function (c) { 135 | w(c, b.href, b.media), p[b.href] = !0, a.setTimeout(function () { 136 | x() 137 | }, 0) 138 | }) 139 | } 140 | } 141 | , y = function () { 142 | for (var b = 0; b < t.length; b++) { 143 | var c = t[b] 144 | , e = c.href 145 | , f = c.media 146 | , g = c.rel && "stylesheet" === c.rel.toLowerCase(); 147 | e && g && !p[e] && (c.styleSheet && c.styleSheet.rawCssText ? (w(c.styleSheet.rawCssText, e, f), p[e] = !0) : (!/^([a-zA-Z:]*\/\/)/.test(e) && !s || e.replace(RegExp.$1, "").split("/")[0] === a.location.host) && ("//" === e.substring(0, 2) && (e = a.location.protocol + e), d.push({ 148 | href: e 149 | , media: f 150 | }))) 151 | } 152 | x() 153 | }; 154 | y(), c.update = y, c.getEmValue = u, a.addEventListener ? a.addEventListener("resize", b, !1) : a.attachEvent && a.attachEvent("onresize", b) 155 | } 156 | }(this); -------------------------------------------------------------------------------- /routes/v1.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var request = require('superagent'); 3 | var dbUtils = require('../utils/dbUtils'); 4 | var qiniuUtils = require('../utils/qiniuUtils'); 5 | var config = require('../configs/config'); 6 | var cookie = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36' }; 7 | var CDN = 'http://h1.ioliu.cn/bing/'; 8 | var router = express.Router(); 9 | 10 | /** 11 | * v1 默认返回最新的bing壁纸信息 12 | */ 13 | router.get('/', function(req, res, next) { 14 | v1(req, res, next); 15 | }); 16 | router.post('/', function(req, res, next) { 17 | v1(req, res, next); 18 | }); 19 | 20 | var v1 = function(req, res, next) { 21 | var d = req.query.d || req.body.d; 22 | var w = req.query.w || req.body.w || '1366'; 23 | var h = req.query.h || req.body.h || '768'; 24 | var t = req.query.type || req.body.type; 25 | var size = w + 'x' + h; 26 | var cb = req.query.callback; 27 | var enddate = 0; 28 | if (!isNaN(d)) { 29 | var date = new Date().getTime() - parseInt(d) * 1000 * 60 * 60 * 24; 30 | var newDate = new Date(date); 31 | enddate = newDate.getFullYear() + '' + ((newDate.getMonth() + 1) > 9 ? (newDate.getMonth() + 1) : '0' + (newDate.getMonth() + 1)) + '' + (newDate.getDate() > 9 ? newDate.getDate() : '0' + newDate.getDate()); 32 | } 33 | var params = { 34 | page: { 35 | no: 1, 36 | size: 1 37 | }, 38 | body: `mkt like '%zh-cn%'` 39 | }; 40 | if (!!enddate) { 41 | params['body'] = `mkt like '%zh-cn%' and enddate='${enddate}'` 42 | } 43 | dbUtils.get('bing', params, function(rows) { 44 | if (rows.length > 0) { 45 | var data = rows[0]; 46 | if (t === 'json' || !!cb) { 47 | var output = { 48 | data: { 49 | enddate: data.enddate, 50 | url: data.url, 51 | bmiddle_pic: data.bmiddle_pic, 52 | original_pic: data.original_pic, 53 | thumbnail_pic: data.thumbnail_pic, 54 | }, 55 | status: { 56 | code: 200, 57 | message: '' 58 | } 59 | }; 60 | if (cb) { 61 | res.jsonp(output); 62 | } else { 63 | res.json(output); 64 | } 65 | } else { 66 | 67 | if (config.resolutions.indexOf(size) === -1) { 68 | data['url'] = qiniuUtils.imageView(data.qiniu_url, w, h); 69 | } else { 70 | data['url'] = CDN + data.qiniu_url + '_' + size + '.jpg'; 71 | } 72 | request.get(data['url']) 73 | .set({ 74 | 'user-agent': req.headers['user-agent'], 75 | 'referer': req.headers['host'] 76 | }) 77 | .end(function(err, response) { 78 | if (err) { 79 | res.send({ 80 | data: {}, 81 | status: { 82 | code: err.status, 83 | message: err.status == 404 ? 'Did not find this picture, please try another resolution' : (response && response.text || '') 84 | } 85 | }) 86 | } else { 87 | res.header('content-type', 'image/jpg'); 88 | res.send(response.body); 89 | } 90 | }); 91 | } 92 | } else { 93 | res.json({ 94 | data: {}, 95 | status: { 96 | code: -1, 97 | message: '好厉害啊,这么隐秘的地方都被你发现了n(*≧▽≦*)n,我已经通知主人咯♪(^∀^●)ノ,请您稍后再来试一下吧(❤´艸`❤)!' 98 | } 99 | }); 100 | } 101 | }); 102 | } 103 | 104 | /** 105 | * 获得随机图片 106 | */ 107 | router.get('/rand', function(req, res, next) { 108 | random(req, res, next); 109 | }); 110 | router.post('/rand', function(req, res, next) { 111 | random(req, res, next); 112 | }); 113 | 114 | var random = function(req, res, next) { 115 | var t = req.query.type || req.body.type; 116 | var w = req.query.w || req.body.w || '1366'; 117 | var h = req.query.h || req.body.h || '768'; 118 | var size = w + 'x' + h; 119 | var callback = req.query.callback || req.body.callback; 120 | dbUtils.getCount('bing', {}, function(rows) { 121 | if (rows.length > 0) { 122 | var sum = Number(rows[0].sum); 123 | var rand = Math.floor(Math.random() * (sum - 1) + 1); 124 | dbUtils.get('bing', { 125 | id: rand 126 | }, function(rs) { 127 | if (rs.length > 0) { 128 | var data = rs[0]; 129 | if (t === 'json' || !!callback) { 130 | var output = { 131 | data: { 132 | enddate: data.enddate, 133 | url: data.url, 134 | bmiddle_pic: data.bmiddle_pic, 135 | original_pic: data.original_pic, 136 | thumbnail_pic: data.thumbnail_pic, 137 | }, 138 | status: { 139 | code: 200, 140 | message: '' 141 | } 142 | }; 143 | if (callback) { 144 | res.jsonp(output); 145 | } else { 146 | res.json(output); 147 | } 148 | } else { 149 | var data = rs[0]; 150 | if (config.resolutions.indexOf(size) === -1) { 151 | data['url'] = qiniuUtils.imageView(data.qiniu_url, w, h); 152 | } else { 153 | data['url'] = CDN + data.qiniu_url + '_' + size + '.jpg'; 154 | } 155 | request.get(data['url']) 156 | .set({ 157 | 'user-agent': req.headers['user-agent'], 158 | 'referer': req.headers['host'] 159 | }) 160 | .end(function(err, response) { 161 | if (err) { 162 | res.send({ 163 | data: {}, 164 | status: { 165 | code: err.status, 166 | message: err.status == 404 ? 'Did not find this picture, please try another resolution' : (response && response.text || '') 167 | } 168 | }) 169 | } else { 170 | res.header('content-type', 'image/jpg'); 171 | res.send(response.body); 172 | } 173 | }); 174 | } 175 | } else { 176 | var parmas = '?'; 177 | params += !!t ? '&t=' + t : ''; 178 | params += !!w ? '&w=' + w : ''; 179 | params += !!h ? '&h=' + h : ''; 180 | params += !!callback ? '&callback=' + callback : ''; 181 | res.redirect('/v1/rand' + params); 182 | } 183 | }); 184 | } else { 185 | var parmas = '?'; 186 | params += !!t ? '&t=' + t : ''; 187 | params += !!w ? '&w=' + w : ''; 188 | params += !!h ? '&h=' + h : ''; 189 | params += !!callback ? '&callback=' + callback : ''; 190 | res.redirect('/v1/rand' + params); 191 | } 192 | }); 193 | } 194 | 195 | 196 | /** 197 | * 获取高斯模糊图片 198 | */ 199 | router.get('/blur', function(req, res, next) { 200 | blur(req, res, next); 201 | }); 202 | router.post('/blur', function(req, res, next) { 203 | blur(req, res, next); 204 | }); 205 | 206 | var blur = function(req, res, next) { 207 | var d = req.query.d || req.body.d; 208 | var w = req.query.w || req.body.w || 1366; 209 | var h = req.query.h || req.body.h || 768; 210 | var r = req.query.r || req.body.r; 211 | r = isNaN(r) ? 10 : parseInt(r) > 50 ? 50 : parseInt(r) <= 0 ? 1 : r; 212 | var size = w + 'x' + h; 213 | var enddate = 0; 214 | if (!isNaN(d)) { 215 | var date = new Date().getTime() - parseInt(d) * 1000 * 60 * 60 * 24; 216 | var newDate = new Date(date); 217 | enddate = newDate.getFullYear() + '' + ((newDate.getMonth() + 1) > 9 ? (newDate.getMonth() + 1) : '0' + (newDate.getMonth() + 1)) + '' + (newDate.getDate() > 9 ? newDate.getDate() : '0' + newDate.getDate()); 218 | } 219 | var params = { 220 | page: { 221 | no: 1, 222 | size: 1 223 | }, 224 | body: `mkt like '%zh-cn%'` 225 | }; 226 | if (!!enddate) { 227 | params['body'] = `mkt like '%zh-cn%' and enddate='${enddate}'` 228 | } 229 | dbUtils.get('bing', params, function(rows) { 230 | if (rows.length > 0) { 231 | var data = rows[0]; 232 | if (config.resolutions.indexOf(size) > -1) { 233 | data['url'] = CDN + data.qiniu_url + '_' + size + '.jpg'; 234 | } 235 | var qiniu_url = /^(http|https)/.test(data.url) ? data.url : CDN + data.qiniu_url + '_1920x1080.jpg'; 236 | qiniu_url += '?imageMogr2/blur/' + r + 'x50' 237 | request.get(qiniu_url) 238 | .set({ 239 | 'user-agent': req.headers['user-agent'], 240 | 'referer': req.headers['host'] 241 | }) 242 | .end(function(err, response) { 243 | if (err) { 244 | res.send({ 245 | data: {}, 246 | status: { 247 | code: err.status, 248 | message: err.status == 404 ? 'Did not find this picture, please try another resolution' : (response && response.text || '') 249 | } 250 | }) 251 | } else { 252 | res.header('content-type', 'image/jpg'); 253 | res.send(response.body); 254 | } 255 | }); 256 | } else { 257 | res.json({ 258 | data: {}, 259 | status: { 260 | code: -1, 261 | message: '好厉害啊,这么隐秘的地方都被你发现了n(*≧▽≦*)n,我已经通知主人咯♪(^∀^●)ノ,请您稍后再来试一下吧(❤´艸`❤)!' 262 | } 263 | }); 264 | } 265 | }); 266 | } 267 | 268 | module.exports = router; -------------------------------------------------------------------------------- /static/fonts/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /static/css/vue.css: -------------------------------------------------------------------------------- 1 | @import url("//fonts.cat.net/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600"); 2 | * { 3 | -webkit-font-smoothing: antialiased; 4 | -webkit-overflow-scrolling: touch; 5 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 6 | -webkit-text-size-adjust: none; 7 | -webkit-touch-callout: none; 8 | box-sizing: border-box 9 | } 10 | 11 | div#app { 12 | font-size: 30px; 13 | font-weight: lighter; 14 | margin: 40vh auto; 15 | text-align: center 16 | } 17 | 18 | div#app:empty:before { 19 | content: "Loading..." 20 | } 21 | 22 | .emoji { 23 | height: 19.2px; 24 | height: 1.2rem; 25 | vertical-align: middle 26 | } 27 | 28 | .progress { 29 | background-color: #42b983; 30 | background-color: var(--theme-color, #42b983); 31 | height: 2px; 32 | left: 0; 33 | position: fixed; 34 | right: 0; 35 | top: 0; 36 | transition: width .2s, opacity .4s; 37 | width: 0; 38 | z-index: 5 39 | } 40 | 41 | .search .search-keyword, 42 | .search a:hover { 43 | color: #42b983; 44 | color: var(--theme-color, #42b983) 45 | } 46 | 47 | .search .search-keyword { 48 | font-style: normal; 49 | font-weight: 700 50 | } 51 | 52 | body, 53 | html { 54 | height: 100% 55 | } 56 | 57 | body { 58 | -moz-osx-font-smoothing: grayscale; 59 | -webkit-font-smoothing: antialiased; 60 | color: #34495e; 61 | font-family: Source Sans Pro, Helvetica Neue, Arial, sans-serif; 62 | font-size: 15px; 63 | letter-spacing: 0; 64 | margin: 0; 65 | overflow-x: hidden 66 | } 67 | 68 | img { 69 | max-width: 100% 70 | } 71 | 72 | a[disabled] { 73 | cursor: not-allowed; 74 | opacity: .6 75 | } 76 | 77 | kbd { 78 | border: 1px solid #ccc; 79 | border-radius: 3px; 80 | display: inline-block; 81 | font-size: 12px!important; 82 | line-height: 12px; 83 | margin-bottom: 3px; 84 | padding: 3px 5px; 85 | vertical-align: middle 86 | } 87 | 88 | .app-nav { 89 | left: 0; 90 | margin: 25px 60px 0 0; 91 | position: absolute; 92 | right: 0; 93 | text-align: right; 94 | z-index: 2 95 | } 96 | 97 | .app-nav p { 98 | margin: 0 99 | } 100 | 101 | .app-nav>a { 102 | margin: 0 16px; 103 | margin: 0 1rem; 104 | padding: 5px 0 105 | } 106 | 107 | .app-nav li, 108 | .app-nav ul { 109 | display: inline-block; 110 | list-style: none; 111 | margin: 0 112 | } 113 | 114 | .app-nav a { 115 | color: inherit; 116 | font-size: 16px; 117 | text-decoration: none; 118 | transition: color .3s 119 | } 120 | 121 | .app-nav a.active, 122 | .app-nav a:hover { 123 | color: #42b983; 124 | color: var(--theme-color, #42b983) 125 | } 126 | 127 | .app-nav a.active { 128 | border-bottom: 2px solid #42b983; 129 | border-bottom: 2px solid var(--theme-color, #42b983) 130 | } 131 | 132 | .app-nav li { 133 | display: inline-block; 134 | margin: 0 16px; 135 | margin: 0 1rem; 136 | padding: 5px 0; 137 | position: relative 138 | } 139 | 140 | .app-nav li ul { 141 | background-color: #fff; 142 | border: 1px solid #ddd; 143 | border-bottom-color: #ccc; 144 | border-radius: 4px; 145 | box-sizing: border-box; 146 | display: none; 147 | max-height: calc(100vh - 61px); 148 | overflow-y: scroll; 149 | padding: 10px 0; 150 | position: absolute; 151 | right: -15px; 152 | text-align: left; 153 | top: 100%; 154 | white-space: nowrap 155 | } 156 | 157 | .app-nav li ul li { 158 | display: block; 159 | font-size: 14px; 160 | line-height: 16px; 161 | line-height: 1rem; 162 | margin: 0; 163 | margin: 8px 14px; 164 | white-space: nowrap 165 | } 166 | 167 | .app-nav li ul a { 168 | display: block; 169 | font-size: inherit; 170 | margin: 0; 171 | padding: 0 172 | } 173 | 174 | .app-nav li ul a.active { 175 | border-bottom: 0 176 | } 177 | 178 | .app-nav li:hover ul { 179 | display: block 180 | } 181 | 182 | .app-nav.no-badge { 183 | margin-right: 25px 184 | } 185 | 186 | .github-corner { 187 | border-bottom: 0; 188 | position: fixed; 189 | right: 0; 190 | text-decoration: none; 191 | top: 0; 192 | z-index: 1 193 | } 194 | 195 | .github-corner svg { 196 | color: #fff; 197 | fill: #42b983; 198 | fill: var(--theme-color, #42b983); 199 | height: 80px; 200 | width: 80px 201 | } 202 | 203 | .github-corner:hover .octo-arm { 204 | -webkit-animation: a .56s ease-in-out; 205 | animation: a .56s ease-in-out 206 | } 207 | 208 | main { 209 | display: block; 210 | position: relative; 211 | width: 100vw; 212 | height: 100%; 213 | z-index: 0 214 | } 215 | 216 | .anchor { 217 | display: inline-block; 218 | text-decoration: none; 219 | transition: all .3s 220 | } 221 | 222 | .anchor span { 223 | color: #34495e 224 | } 225 | 226 | .anchor:hover { 227 | text-decoration: underline 228 | } 229 | 230 | 231 | .content { 232 | padding-top: 60px; 233 | transition: left .25s ease 234 | } 235 | 236 | .markdown-section { 237 | margin: 0 auto; 238 | max-width: 800px; 239 | padding: 30px 15px 40px; 240 | position: relative 241 | } 242 | 243 | .markdown-section>* { 244 | box-sizing: border-box; 245 | font-size: inherit 246 | } 247 | 248 | .markdown-section>:first-child { 249 | margin-top: 0!important 250 | } 251 | 252 | .markdown-section hr { 253 | border: none; 254 | border-bottom: 1px solid #eee; 255 | margin: 2em 0 256 | } 257 | 258 | .markdown-section table { 259 | border-collapse: collapse; 260 | border-spacing: 0; 261 | display: block; 262 | margin-bottom: 16px; 263 | margin-bottom: 1rem; 264 | overflow: auto; 265 | width: 100% 266 | } 267 | 268 | .markdown-section th { 269 | font-weight: 700 270 | } 271 | 272 | .markdown-section td, 273 | .markdown-section th { 274 | border: 1px solid #ddd; 275 | padding: 6px 13px 276 | } 277 | 278 | .markdown-section tr { 279 | border-top: 1px solid #ccc 280 | } 281 | 282 | .markdown-section p.tip, 283 | .markdown-section tr:nth-child(2n) { 284 | background-color: #f8f8f8 285 | } 286 | 287 | .markdown-section p.tip { 288 | border-bottom-right-radius: 2px; 289 | border-left: 4px solid #f66; 290 | border-top-right-radius: 2px; 291 | margin: 2em 0; 292 | padding: 12px 24px 12px 30px; 293 | position: relative 294 | } 295 | 296 | .markdown-section p.tip code { 297 | background-color: #efefef 298 | } 299 | 300 | .markdown-section p.tip em { 301 | color: #34495e 302 | } 303 | 304 | .markdown-section p.tip:before { 305 | background-color: #f66; 306 | border-radius: 100%; 307 | color: #fff; 308 | content: "!"; 309 | font-family: Dosis, Source Sans Pro, Helvetica Neue, Arial, sans-serif; 310 | font-size: 14px; 311 | font-weight: 700; 312 | left: -12px; 313 | line-height: 20px; 314 | position: absolute; 315 | width: 20px; 316 | height: 20px; 317 | text-align: center; 318 | top: 14px 319 | } 320 | 321 | .markdown-section p.warn { 322 | background: rgba(66, 185, 131, .1); 323 | border-radius: 2px; 324 | padding: 16px; 325 | padding: 1rem 326 | } 327 | 328 | body.close .sidebar { 329 | -webkit-transform: translateX(-300px); 330 | transform: translateX(-300px) 331 | } 332 | 333 | body.close .sidebar-toggle { 334 | width: auto 335 | } 336 | 337 | body.close .content { 338 | left: 0 339 | } 340 | 341 | @media print { 342 | .app-nav, 343 | .github-corner, 344 | .sidebar, 345 | .sidebar-toggle { 346 | display: none 347 | } 348 | } 349 | 350 | @media screen and (max-width:768px) { 351 | .github-corner, 352 | .sidebar, 353 | .sidebar-toggle { 354 | position: fixed 355 | } 356 | .app-nav { 357 | margin-top: 16px 358 | } 359 | .app-nav li ul { 360 | top: 30px 361 | } 362 | main { 363 | height: auto; 364 | overflow-x: hidden 365 | } 366 | .sidebar { 367 | left: -300px; 368 | transition: -webkit-transform .25s ease-out; 369 | transition: transform .25s ease-out; 370 | transition: transform .25s ease-out, -webkit-transform .25s ease-out 371 | } 372 | .content { 373 | left: 0; 374 | max-width: 100vw; 375 | position: static; 376 | padding-top: 20px; 377 | transition: -webkit-transform .25s ease; 378 | transition: transform .25s ease; 379 | transition: transform .25s ease, -webkit-transform .25s ease 380 | } 381 | .app-nav, 382 | .github-corner { 383 | transition: -webkit-transform .25s ease-out; 384 | transition: transform .25s ease-out; 385 | transition: transform .25s ease-out, -webkit-transform .25s ease-out 386 | } 387 | .sidebar-toggle { 388 | background-color: transparent; 389 | width: auto 390 | } 391 | body.close .sidebar { 392 | -webkit-transform: translateX(300px); 393 | transform: translateX(300px) 394 | } 395 | body.close .sidebar-toggle { 396 | background-color: hsla(0, 0%, 100%, .8); 397 | transition: background-color 1s; 398 | width: 284px 399 | } 400 | body.close .content { 401 | -webkit-transform: translateX(300px); 402 | transform: translateX(300px) 403 | } 404 | body.close .app-nav, 405 | body.close .github-corner { 406 | display: none 407 | } 408 | .github-corner .octo-arm { 409 | -webkit-animation: a .56s ease-in-out; 410 | animation: a .56s ease-in-out 411 | } 412 | .github-corner:hover .octo-arm { 413 | -webkit-animation: none; 414 | animation: none 415 | } 416 | } 417 | 418 | @-webkit-keyframes a { 419 | 0%, 420 | to { 421 | -webkit-transform: rotate(0); 422 | transform: rotate(0) 423 | } 424 | 20%, 425 | 60% { 426 | -webkit-transform: rotate(-25deg); 427 | transform: rotate(-25deg) 428 | } 429 | 40%, 430 | 80% { 431 | -webkit-transform: rotate(10deg); 432 | transform: rotate(10deg) 433 | } 434 | } 435 | 436 | @keyframes a { 437 | 0%, 438 | to { 439 | -webkit-transform: rotate(0); 440 | transform: rotate(0) 441 | } 442 | 20%, 443 | 60% { 444 | -webkit-transform: rotate(-25deg); 445 | transform: rotate(-25deg) 446 | } 447 | 40%, 448 | 80% { 449 | -webkit-transform: rotate(10deg); 450 | transform: rotate(10deg) 451 | } 452 | } 453 | 454 | section.cover { 455 | -webkit-box-align: center; 456 | -ms-flex-align: center; 457 | align-items: center; 458 | background-position: 50%; 459 | background-repeat: no-repeat; 460 | background-size: cover; 461 | height: 100vh; 462 | display: none 463 | } 464 | 465 | section.cover .cover-main { 466 | -webkit-box-flex: 1; 467 | -ms-flex: 1; 468 | flex: 1; 469 | margin: -20px 16px 0; 470 | text-align: center; 471 | z-index: 1 472 | } 473 | 474 | section.cover a { 475 | color: inherit 476 | } 477 | 478 | section.cover a, 479 | section.cover a:hover { 480 | text-decoration: none 481 | } 482 | 483 | section.cover p { 484 | line-height: 24px; 485 | line-height: 1.5rem; 486 | margin: 1em 0 487 | } 488 | 489 | section.cover h1 { 490 | color: inherit; 491 | font-size: 40px; 492 | font-size: 2.5rem; 493 | font-weight: 300; 494 | margin: 10px 0 40px; 495 | margin: .625rem 0 2.5rem; 496 | position: relative; 497 | text-align: center 498 | } 499 | 500 | section.cover h1 a { 501 | display: block 502 | } 503 | 504 | section.cover h1 small { 505 | bottom: -7px; 506 | bottom: -.4375rem; 507 | font-size: 16px; 508 | font-size: 1rem; 509 | position: absolute 510 | } 511 | 512 | section.cover blockquote { 513 | font-size: 24px; 514 | font-size: 1.5rem; 515 | text-align: center 516 | } 517 | 518 | section.cover ul { 519 | line-height: 1.8; 520 | list-style-type: none; 521 | margin: 1em auto; 522 | max-width: 500px; 523 | padding: 0 524 | } 525 | 526 | section.cover .cover-main>p:last-child a { 527 | border-color: #42b983; 528 | border: 1px solid var(--theme-color, #42b983); 529 | border-radius: 2rem; 530 | box-sizing: border-box; 531 | color: #42b983; 532 | color: var(--theme-color, #42b983); 533 | display: inline-block; 534 | font-size: 16.8px; 535 | font-size: 1.05rem; 536 | letter-spacing: 1.6px; 537 | letter-spacing: .1rem; 538 | margin-right: 16px; 539 | margin-right: 1rem; 540 | padding: .75em 32px; 541 | padding: .75em 2rem; 542 | text-decoration: none; 543 | transition: all .15s ease 544 | } 545 | 546 | section.cover .cover-main>p:last-child a:last-child { 547 | background-color: #42b983; 548 | background-color: var(--theme-color, #42b983); 549 | color: #fff; 550 | margin-right: 0 551 | } 552 | 553 | section.cover .cover-main>p:last-child a:last-child:hover { 554 | color: inherit; 555 | opacity: .8 556 | } 557 | 558 | section.cover .cover-main>p:last-child a:hover { 559 | color: inherit 560 | } 561 | 562 | section.cover blockquote>p>a { 563 | border-bottom: 2px solid #42b983; 564 | border-bottom: 2px solid var(--theme-color, #42b983); 565 | transition: color .3s 566 | } 567 | 568 | section.cover blockquote>p>a:hover { 569 | color: #42b983; 570 | color: var(--theme-color, #42b983) 571 | } 572 | 573 | section.cover.show { 574 | display: -webkit-box; 575 | display: -ms-flexbox; 576 | display: flex 577 | } 578 | 579 | section.cover.has-mask .mask { 580 | background-color: #fff; 581 | opacity: .8; 582 | position: absolute; 583 | width: 100%; 584 | height: 100% 585 | } 586 | 587 | .sidebar, 588 | body { 589 | background-color: #fff 590 | } 591 | 592 | .sidebar { 593 | color: #364149 594 | } 595 | 596 | .sidebar li { 597 | margin: 6px 0 6px 15px 598 | } 599 | 600 | .sidebar ul li a { 601 | color: #505d6b; 602 | font-size: 14px; 603 | font-weight: 400; 604 | overflow: hidden; 605 | text-decoration: none; 606 | text-overflow: ellipsis; 607 | white-space: nowrap 608 | } 609 | 610 | .sidebar ul li a:hover { 611 | text-decoration: underline 612 | } 613 | 614 | .sidebar ul li ul { 615 | padding: 0 616 | } 617 | 618 | .sidebar ul li.active>a { 619 | border-right: 2px solid; 620 | color: #42b983; 621 | color: var(--theme-color, #42b983); 622 | font-weight: 600 623 | } 624 | 625 | .app-sub-sidebar li:before { 626 | content: "-"; 627 | padding-right: 4px; 628 | float: left 629 | } 630 | 631 | .markdown-section h1, 632 | .markdown-section h2, 633 | .markdown-section h3, 634 | .markdown-section h4, 635 | .markdown-section strong { 636 | color: #2c3e50; 637 | font-weight: 600 638 | } 639 | 640 | .markdown-section a { 641 | color: #42b983; 642 | color: var(--theme-color, #42b983); 643 | font-weight: 600 644 | } 645 | 646 | .markdown-section h1 { 647 | font-size: 32px; 648 | font-size: 2rem; 649 | margin: 0 0 16px; 650 | margin: 0 0 1rem 651 | } 652 | 653 | .markdown-section h2 { 654 | font-size: 28px; 655 | font-size: 1.75rem; 656 | margin: 45px 0 12.8px; 657 | margin: 45px 0 .8rem 658 | } 659 | 660 | .markdown-section h3 { 661 | font-size: 24px; 662 | font-size: 1.5rem; 663 | margin: 40px 0 9.6px; 664 | margin: 40px 0 .6rem 665 | } 666 | 667 | .markdown-section h4 { 668 | font-size: 20px; 669 | font-size: 1.25rem 670 | } 671 | 672 | .markdown-section h5, 673 | .markdown-section h6 { 674 | font-size: 16px; 675 | font-size: 1rem 676 | } 677 | 678 | .markdown-section h6 { 679 | color: #777 680 | } 681 | 682 | .markdown-section figure, 683 | .markdown-section ol, 684 | .markdown-section p, 685 | .markdown-section ul { 686 | margin: 1.2em 0 687 | } 688 | 689 | .markdown-section ol, 690 | .markdown-section p, 691 | .markdown-section ul { 692 | line-height: 25.6px; 693 | line-height: 1.6rem; 694 | word-spacing: .8px; 695 | word-spacing: .05rem 696 | } 697 | 698 | .markdown-section ol, 699 | .markdown-section ul { 700 | padding-left: 24px; 701 | padding-left: 1.5rem 702 | } 703 | 704 | .markdown-section blockquote { 705 | border-left: 4px solid #42b983; 706 | border-left: 4px solid var(--theme-color, #42b983); 707 | color: #858585; 708 | margin: 2em 0; 709 | padding-left: 20px 710 | } 711 | 712 | .markdown-section blockquote p { 713 | font-weight: 600; 714 | margin-left: 0 715 | } 716 | 717 | .markdown-section iframe { 718 | margin: 1em 0 719 | } 720 | 721 | .markdown-section em { 722 | color: #7f8c8d 723 | } 724 | 725 | .markdown-section code { 726 | border-radius: 2px; 727 | color: #e96900; 728 | font-size: 12.8px; 729 | font-size: .8rem; 730 | margin: 0 2px; 731 | padding: 3px 5px; 732 | white-space: pre-wrap 733 | } 734 | 735 | .markdown-section code, 736 | .markdown-section pre { 737 | background-color: #f8f8f8; 738 | font-family: Roboto Mono, Monaco, courier, monospace 739 | } 740 | 741 | .markdown-section pre { 742 | -moz-osx-font-smoothing: initial; 743 | -webkit-font-smoothing: initial; 744 | line-height: 24px; 745 | line-height: 1.5rem; 746 | margin: 1.2em 0; 747 | overflow: auto; 748 | padding: 0 22.4px; 749 | padding: 0 1.4rem; 750 | position: relative; 751 | word-wrap: normal 752 | } 753 | 754 | .token.cdata, 755 | .token.comment, 756 | .token.doctype, 757 | .token.prolog { 758 | color: #8e908c 759 | } 760 | 761 | .token.namespace { 762 | opacity: .7 763 | } 764 | 765 | .token.boolean, 766 | .token.number { 767 | color: #c76b29 768 | } 769 | 770 | .token.punctuation { 771 | color: #525252 772 | } 773 | 774 | .token.property { 775 | color: #c08b30 776 | } 777 | 778 | .token.tag { 779 | color: #2973b7 780 | } 781 | 782 | .token.string { 783 | color: #42b983; 784 | color: var(--theme-color, #42b983) 785 | } 786 | 787 | .token.selector { 788 | color: #6679cc 789 | } 790 | 791 | .token.attr-name { 792 | color: #2973b7 793 | } 794 | 795 | .language-css .token.string, 796 | .style .token.string, 797 | .token.entity, 798 | .token.url { 799 | color: #22a2c9 800 | } 801 | 802 | .token.attr-value, 803 | .token.control, 804 | .token.directive, 805 | .token.unit { 806 | color: #42b983; 807 | color: var(--theme-color, #42b983) 808 | } 809 | 810 | .token.keyword { 811 | color: #e96900 812 | } 813 | 814 | .token.atrule, 815 | .token.regex, 816 | .token.statement { 817 | color: #22a2c9 818 | } 819 | 820 | .token.placeholder, 821 | .token.variable { 822 | color: #3d8fd1 823 | } 824 | 825 | .token.deleted { 826 | text-decoration: line-through 827 | } 828 | 829 | .token.inserted { 830 | border-bottom: 1px dotted #202746; 831 | text-decoration: none 832 | } 833 | 834 | .token.italic { 835 | font-style: italic 836 | } 837 | 838 | .token.bold, 839 | .token.important { 840 | font-weight: 700 841 | } 842 | 843 | .token.important { 844 | color: #c94922 845 | } 846 | 847 | .token.entity { 848 | cursor: help 849 | } 850 | 851 | .markdown-section pre>code { 852 | -moz-osx-font-smoothing: initial; 853 | -webkit-font-smoothing: initial; 854 | background-color: #f8f8f8; 855 | border-radius: 2px; 856 | color: #525252; 857 | display: block; 858 | font-family: Roboto Mono, Monaco, courier, monospace; 859 | font-size: 12.8px; 860 | font-size: .8rem; 861 | line-height: inherit; 862 | margin: 0 2px; 863 | max-width: inherit; 864 | overflow: inherit; 865 | padding: 2.2em 5px; 866 | white-space: inherit 867 | } 868 | 869 | .markdown-section code:after, 870 | .markdown-section code:before { 871 | letter-spacing: .8px; 872 | letter-spacing: .05rem 873 | } 874 | 875 | code .token { 876 | -moz-osx-font-smoothing: initial; 877 | -webkit-font-smoothing: initial; 878 | min-height: 24px; 879 | min-height: 1.5rem 880 | } 881 | 882 | pre:after { 883 | color: #ccc; 884 | content: attr(data-lang); 885 | font-size: 9.6px; 886 | font-size: .6rem; 887 | font-weight: 600; 888 | height: 15px; 889 | line-height: 15px; 890 | padding: 5px 10px 0; 891 | position: absolute; 892 | right: 0; 893 | text-align: right; 894 | top: 0 895 | } -------------------------------------------------------------------------------- /static/js/Valine.min.js: -------------------------------------------------------------------------------- 1 | !function(n,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define("Valine",[],e):"object"==typeof exports?exports.Valine=e():n.Valine=e()}(this,function(){return function(n){function e(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return n[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var t={};return e.m=n,e.c=t,e.i=function(n){return n},e.d=function(n,t,r){e.o(n,t)||Object.defineProperty(n,t,{configurable:!1,enumerable:!0,get:r})},e.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return e.d(t,"a",t),t},e.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},e.p="/dist/",e(e.s=3)}([function(n,e,t){var r;!function(i){"use strict";function a(n,e){var t=(65535&n)+(65535&e);return(n>>16)+(e>>16)+(t>>16)<<16|65535&t}function o(n,e){return n<>>32-e}function l(n,e,t,r,i,l){return a(o(a(a(e,n),a(r,l)),i),t)}function c(n,e,t,r,i,a,o){return l(e&t|~e&r,n,e,i,a,o)}function s(n,e,t,r,i,a,o){return l(e&r|t&~r,n,e,i,a,o)}function v(n,e,t,r,i,a,o){return l(e^t^r,n,e,i,a,o)}function u(n,e,t,r,i,a,o){return l(t^(e|~r),n,e,i,a,o)}function d(n,e){n[e>>5]|=128<>>9<<4)]=e;var t,r,i,o,l,d=1732584193,f=-271733879,p=-1732584194,m=271733878;for(t=0;t>5]>>>e%32&255);return t}function p(n){var e,t=[];for(t[(n.length>>2)-1]=void 0,e=0;e>5]|=(255&n.charCodeAt(e/8))<16&&(i=d(i,8*n.length)),t=0;t<16;t+=1)a[t]=909522486^i[t],o[t]=1549556828^i[t];return r=d(a.concat(p(e)),512+8*e.length),f(d(o.concat(r),640))}function g(n){var e,t,r="0123456789abcdef",i="";for(t=0;t>>4&15)+r.charAt(15&e);return i}function b(n){return unescape(encodeURIComponent(n))}function y(n){return m(b(n))}function w(n){return g(y(n))}function x(n,e){return h(b(n),b(e))}function k(n,e){return g(x(n,e))}function A(n,e,t){return e?t?x(e,n):k(e,n):t?y(n):w(n)}void 0!==(r=function(){return A}.call(e,t,e,n))&&(n.exports=r)}()},function(n,e,t){"use strict";function r(n){return n.replace(RegExp("^"+(n.match(/^(\t| )+/)||"")[0],"gm"),"")}function i(n){return(n+"").replace(/"/g,""").replace(//g,">")}function a(n){function e(n){var e=o[n.replace(/\*/g,"_")[1]||""],t=f[f.length-1]==n;return e?e[1]?(f[t?"pop":"push"](n),e[0|t]):e[0]:n}function t(){for(var n="";f.length;)n+=e(f[f.length-1]);return n}var l,c,s,v,u,d=/((?:^|\n+)(?:\n---+|\* \*(?: \*)+)\n)|(?:^```(\w*)\n([\s\S]*?)\n```$)|((?:(?:^|\n+)(?:\t| {2,}).+)+\n*)|((?:(?:^|\n)([>*+-]|\d+\.)\s+.*)+)|(?:\!\[([^\]]*?)\]\(([^\)]+?)\))|(\[)|(\](?:\(([^\)]+?)\))?)|(?:(?:^|\n+)([^\s].*)\n(\-{3,}|={3,})(?:\n+|$))|(?:(?:^|\n+)(#{1,3})\s*(.+)(?:\n+|$))|(?:`([^`].*?)`)|( \n\n*|\n{2,}|__|\*\*|[_*])/gm,f=[],p="",m=0,h={};for(n=n.replace(/^\[(.+?)\]:\s*(.+)$/gm,function(n,e,t){return h[e.toLowerCase()]=t,""}).replace(/^\n+|\n+$/g,"");s=d.exec(n);)c=n.substring(m,s.index),m=d.lastIndex,l=s[0],c.match(/[^\\](\\\\)*\\$/)||(s[3]||s[4]?l='
    '+r(i(s[3]||s[4]).replace(/^\n+|\n+$/g,""))+"
    ":s[6]?(u=s[6],u.match(/\./)&&(s[5]=s[5].replace(/^\d+/gm,"")),v=a(r(s[5].replace(/^\s*[>*+.-]/gm,""))),">"===u?u="blockquote":(u=u.match(/\./)?"ol":"ul",v=v.replace(/^(.*)(\n|$)/gm,"
  • $1
  • ")),l="<"+u+">"+v+""):s[8]?l=''+i(s[7])+'':s[10]?(p=p.replace("",''),l=t()+""):s[9]?l="":s[12]||s[14]?(u="h"+(s[14]?s[14].length:"="===s[13][0]?1:2),l="<"+u+">"+a(s[12]||s[15])+""):s[16]?l=""+i(s[16])+"":(s[17]||s[1])&&(l=e(s[17]||"--"))),p+=c,p+=l;return(p+n.substring(m)+t()).trim()}Object.defineProperty(e,"__esModule",{value:!0});var o={"":["",""],_:["",""],"\n":["
    "]," ":["
    "],"-":["
    "]};e.default=a},function(n,e,t){var r=t(4);"string"==typeof r&&(r=[[n.i,r,""]]);var i={};i.transform=void 0;t(6)(r,i);r.locals&&(n.exports=r.locals)},function(n,e,t){"use strict";function r(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}var i=function(){function n(n,e){for(var t=0;t
      ';e.el.innerHTML=i;var a=e.el.querySelector(".vempty");e.nodata={show:function(n){a.innerHTML=n||"还没有评论哦,快来抢沙发吧!",a.setAttribute("style","display:block;")},hide:function(){a.setAttribute("style","display:none;")}};var o=e.el.querySelector(".vloading");o.innerHTML='
      ',e.loading={show:function(){o.setAttribute("style","display:block;"),e.nodata.hide()},hide:function(){o.setAttribute("style","display:none;"),0===e.el.querySelectorAll(".vcard").length&&e.nodata.show()}},e.notify=n.notify||!1,e.verify=n.verify||!1;var l=n.av||AV;l.init({appId:n.app_id||n.appId,appKey:n.app_key||n.appKey}),e.v=l,c.url=n.path||location.pathname,s.pagination="[object Boolean]"=={}.toString.call(n.pagination)&&n.pagination,s.pageSize=isNaN(n.pageSize)?15:n.pageSize}catch(n){var v="https://github.com/xCss/Valine/issues";return void(e.el?e.nodata.show('
      '+n+"
      Valine:"+e.version+"
      反馈:"+v+"
      "):console&&console.log("%c"+n+"\n%cValine%c"+e.version+" "+v,"color:red;","background:#000;padding:5px;line-height:30px;color:#fff;","background:#456;line-height:30px;padding:5px;color:#fff;"))}var u=e.el.querySelector(".vmark");e.alert={show:function(n){u.innerHTML='
      '+n.text+'
      ';var t=u.querySelector(".vbtns"),r='",i='";if(t.innerHTML=""+r+(n.type&&i),u.querySelector(".vcancel").addEventListener("click",function(n){e.alert.hide()}),u.setAttribute("style","display:block;"),n&&n.type){var a=u.querySelector(".vsure");d.on("click",a,function(t){e.alert.hide(),n.cb&&n.cb()})}},hide:function(){u.setAttribute("style","display:none;")}},e.bind()}},{key:"bind",value:function(){var n=this,e=function(n){n.offsetHeight>180&&(n.classList.add("expand"),d.on("click",n,function(e){n.setAttribute("class","vcomment")}))},t=function(e){var t=new n.v.Query("Comment");return t.equalTo("url",c.url),t.descending("createdAt"),t};!function(){arguments.length>0&&void 0!==arguments[0]&&arguments[0];n.loading.show();var e=t();e.limit("1000"),e.find().then(function(e){var t=e.length;if(t){n.el.querySelector(".vlist").innerHTML="";for(var i=0;i'+t+")"}n.loading.hide()}).catch(function(e){n.loading.hide()})}();var r=function(t,r){var i=document.createElement("li");i.setAttribute("class","vcard"),i.setAttribute("id",t.id),i.innerHTML='
      '+t.get("nick")+'
      '+t.get("comment")+'
      '+g(t.get("createdAt"))+"回复
      ';var a=n.el.querySelector(".vlist"),o=a.querySelectorAll("li"),c=i.querySelector(".vat"),s=i.querySelectorAll("a");for(var v in s)if(s.hasOwnProperty(v)){var u=s[v];"at"!=u.getAttribute("class")&&(u.setAttribute("target","_blank"),u.setAttribute("rel","nofollow"))}L(c),r?a.appendChild(i):a.insertBefore(i,o[0]);var d=i.querySelector(".vcomment");e(d)},i={veditor:"comment",vnick:"nick",vlink:"link",vmail:"mail"},a={};for(var s in i)i.hasOwnProperty(s)&&function(){var e=i[s],t=n.el.querySelector("."+s);a[e]=t,d.on("input",t,function(n){c[e]=m.encode(t.value.replace(/(^\s*)|(\s*$)/g,""))})}();var u=function(){var e=v&&v.getItem("ValineCache");if(e){e=JSON.parse(e);var t=["nick","link","mail"];for(var r in t){var i=t[r];n.el.querySelector(".v"+i).value=e[i],c[i]=e[i]}}};u();var h={rmail:"",at:""},b=function(){for(var e in i)if(i.hasOwnProperty(e)){var t=i[e],r=n.el.querySelector("."+e);r.value="",c[t]=""}h.at="",h.rmail="",c.rid="",c.nick="Guest",u()},y=n.el.querySelector(".vsubmit"),w=function(e){if(y.getAttribute("disabled"))return void n.alert.show({type:0,text:'再等等,评论正在提交中ヾ(๑╹◡╹)ノ"',ctxt:"好的"});if(""==c.comment)return void a.comment.focus();if(""==c.nick&&(c.nick="小调皮"),c.comment=(0,o.default)(c.comment),c.comment.indexOf(h.at)>-1&&""!=h.at){var t='"+h.at+"";c.comment=c.comment.replace(h.at,t)}var r=p.mail(c.mail),i=p.link(c.link);r.k||i.k?r.k?i.k?(c.mail=r.v,c.link=i.v,n.notify||n.verify?A(k):k()):(c.link="",c.mail=r.v,n.alert.show({type:1,text:"您的网址格式不正确, 是否继续提交?",cb:function(){n.notify||n.verify?A(k):k()}})):(c.mail="",c.link=i.v,n.alert.show({type:1,text:"您的邮箱格式不正确, 是否继续提交?",cb:function(){n.notify||n.verify?A(k):k()}})):(c.mail="",c.link="",n.alert.show({type:1,text:"您的网址和邮箱格式不正确, 是否继续提交?",cb:function(){n.notify||n.verify?A(k):k()}}))},x=function(){var e=new n.v.ACL;return e.setPublicReadAccess(!0),e.setPublicWriteAccess(!1),e},k=function(){y.setAttribute("disabled",!0),n.loading.show();var e=n.v.Object.extend("Comment"),t=new e;for(var i in c)if(c.hasOwnProperty(i)){var a=c[i];t.set(i,a)}t.setACL(x()),t.save().then(function(e){"Guest"!=c.nick&&v&&v.setItem("ValineCache",JSON.stringify({nick:c.nick,link:c.link,mail:c.mail}));var t=n.el.querySelector(".num"),i=1;try{t?(i=Number(t.innerText)+1,t.innerText=i):n.el.querySelector(".count").innerHTML='评论(1)',r(e),c.mail&&S({username:c.nick,mail:c.mail}),h.at&&h.rmail&&n.notify&&M({username:h.at.replace("@",""),mail:h.rmail}),y.removeAttribute("disabled"),n.loading.hide(),b()}catch(n){console.log(n)}}).catch(function(e){n.loading.hide()})},A=function e(t){var r=Math.floor(20*Math.random()+1),i=Math.floor(20*Math.random()+1),a=Math.floor(20*Math.random()+1),o=["+","-","x"],l=o[Math.floor(3*Math.random())],c=o[Math.floor(3*Math.random())],s=""+r+l+i+c+a,v=s+" = ";n.alert.show({type:1,text:v,ctxt:"取消",otxt:"确认",cb:function(){var r=+n.el.querySelector(".vcode").value;new Function("return "+s.replace(/x/g,"*"))()===r?t&&t():n.alert.show({type:1,text:"(T_T)这么简单都算错,也是没谁了",ctxt:"伤心了,不回了",otxt:"再试试?",cb:function(){e(t)}})}})},S=function(e){var t=new n.v.User;return t.setUsername(e.username),t.setPassword(e.mail),t.setEmail(e.mail),t.setACL(x()),t.signUp()},M=function e(t){n.v.User.requestPasswordReset(t.mail).then(function(n){}).catch(function(r){1==r.code?n.alert.show({type:0,text:"ヾ(o・ω・)ノ At太频繁啦,提醒功能暂时宕机。
      "+r.error,ctxt:"好的"}):S(t).then(function(n){e(t)}).catch(function(n){})})},L=function(n){d.on("click",n,function(e){var t=n.getAttribute("at"),r=n.getAttribute("rid"),i=n.getAttribute("mail");h.at=t,h.rmail=i,c.rid=r,a.comment.value=t+" ,",a.comment.focus()})};d.off("click",y,w),d.on("click",y,w)}}]),n}(),d={on:function(n,e,t,r){e.addEventListener?e.addEventListener(n,t,r||!1):e.attachEvent?e.attachEvent("on"+n,t):e["on"+n]=t},off:function(n,e,t,r){e.removeEventListener?e.removeEventListener(n,t,r||!1):e.detachEvent?e.detachEvent("on"+n,t):e["on"+n]=null}},f=function(n){return n.link||n.mail&&"mailto:"+n.mail||"javascript:void(0);"},p={mail:function(n){return{k:/[\w-\.]+@([\w-]+\.)+[a-z]{2,3}/.test(n),v:n}},link:function(n){return n=n.length>0&&(/^(http|https)/.test(n)?n:"http://"+n),{k:/(http|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:\/~\+#]*[\w\-\@?^=%&\/~\+#])?/.test(n),v:n}}},m={encode:function(n){return n?n.replace(/&/g,"&").replace(//g,">").replace(/ /g," ").replace(/\'/g,"'").replace(/\"/g,"""):""},decode:function(n){return n?n.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/ /g," ").replace(/'/g,"'").replace(/"/g,'"'):""}},h=function(n){var e=b(n.getDate(),2),t=b(n.getMonth()+1,2),r=b(n.getFullYear(),2);b(n.getHours(),2),b(n.getMinutes(),2),b(n.getSeconds(),2);return r+"-"+t+"-"+e},g=function(n){try{var e=n.getTime(),t=(new Date).getTime(),r=t-e,i=Math.floor(r/864e5);if(0===i){var a=r%864e5,o=Math.floor(a/36e5);if(0===o){var l=a%36e5,c=Math.floor(l/6e4);if(0===c){var s=l%6e4;return Math.round(s/1e3)+" 秒前"}return c+" 分钟前"}return o+" 小时前"}return i<0?"刚刚":i<4?i+" 天前":h(n)}catch(n){console.log(n)}},b=function(n,e){for(var t=n.toString();t.length div {\n background-color: #9c9c9c;\n height: 100%;\n width: .375rem;\n margin-right: 0.19rem;\n display: inline-block;\n -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out;\n animation: sk-stretchdelay 1.2s infinite ease-in-out; }\n .valine .spinner .r2 {\n -webkit-animation-delay: -1.1s;\n animation-delay: -1.1s; }\n .valine .spinner .r3 {\n -webkit-animation-delay: -1.0s;\n animation-delay: -1.0s; }\n .valine .spinner .r4 {\n -webkit-animation-delay: -0.9s;\n animation-delay: -0.9s; }\n .valine .spinner .r5 {\n -webkit-animation-delay: -0.8s;\n animation-delay: -0.8s; }\n\n@-webkit-keyframes sk-stretchdelay {\n 0%,\n 40%,\n 100% {\n -webkit-transform: scaleY(0.4); }\n 20% {\n -webkit-transform: scaleY(1); } }\n\n@keyframes sk-stretchdelay {\n 0%,\n 40%,\n 100% {\n transform: scaleY(0.4);\n -webkit-transform: scaleY(0.4); }\n 20% {\n transform: scaleY(1);\n -webkit-transform: scaleY(1); } }\n',""])},function(n,e){function t(n,e){var t=n[1]||"",i=n[3];if(!i)return t;if(e&&"function"==typeof btoa){var a=r(i);return[t].concat(i.sources.map(function(n){return"/*# sourceURL="+i.sourceRoot+n+" */"})).concat([a]).join("\n")}return[t].join("\n")}function r(n){return"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(n))))+" */"}n.exports=function(n){var e=[];return e.toString=function(){return this.map(function(e){var r=t(e,n);return e[2]?"@media "+e[2]+"{"+r+"}":r}).join("")},e.i=function(n,t){"string"==typeof n&&(n=[[null,n,""]]);for(var r={},i=0;i=0&&y.splice(e,1)}function l(n){var e=document.createElement("style");return n.attrs.type="text/css",s(e,n.attrs),a(n,e),e}function c(n){var e=document.createElement("link");return n.attrs.type="text/css",n.attrs.rel="stylesheet",s(e,n.attrs),a(n,e),e}function s(n,e){Object.keys(e).forEach(function(t){n.setAttribute(t,e[t])})}function v(n,e){var t,r,i,a;if(e.transform&&n.css){if(!(a=e.transform(n.css)))return function(){};n.css=a}if(e.singleton){var s=b++;t=g||(g=l(e)),r=u.bind(null,t,s,!1),i=u.bind(null,t,s,!0)}else n.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(t=c(e),r=f.bind(null,t,e),i=function(){o(t),t.href&&URL.revokeObjectURL(t.href)}):(t=l(e),r=d.bind(null,t),i=function(){o(t)});return r(n),function(e){if(e){if(e.css===n.css&&e.media===n.media&&e.sourceMap===n.sourceMap)return;r(n=e)}else i()}}function u(n,e,t,r){var i=t?"":r.css;if(n.styleSheet)n.styleSheet.cssText=x(e,i);else{var a=document.createTextNode(i),o=n.childNodes;o[e]&&n.removeChild(o[e]),o.length?n.insertBefore(a,o[e]):n.appendChild(a)}}function d(n,e){var t=e.css,r=e.media;if(r&&n.setAttribute("media",r),n.styleSheet)n.styleSheet.cssText=t;else{for(;n.firstChild;)n.removeChild(n.firstChild);n.appendChild(document.createTextNode(t))}}function f(n,e,t){var r=t.css,i=t.sourceMap,a=void 0===e.convertToAbsoluteUrls&&i;(e.convertToAbsoluteUrls||a)&&(r=w(r)),i&&(r+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(i))))+" */");var o=new Blob([r],{type:"text/css"}),l=n.href;n.href=URL.createObjectURL(o),l&&URL.revokeObjectURL(l)}var p={},m=function(n){var e;return function(){return void 0===e&&(e=n.apply(this,arguments)),e}}(function(){return window&&document&&document.all&&!window.atob}),h=function(n){var e={};return function(t){return void 0===e[t]&&(e[t]=n.call(this,t)),e[t]}}(function(n){return document.querySelector(n)}),g=null,b=0,y=[],w=t(7);n.exports=function(n,e){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");e=e||{},e.attrs="object"==typeof e.attrs?e.attrs:{},e.singleton||(e.singleton=m()),e.insertInto||(e.insertInto="head"),e.insertAt||(e.insertAt="bottom");var t=i(n,e);return r(t,e),function(n){for(var a=[],o=0;o