diff --git a/README.md b/README.md
index e7464c0f6..16d1a3df2 100755
--- a/README.md
+++ b/README.md
@@ -2,6 +2,9 @@ nodePPT - 让你爱上做分享!
=============
[This is a readme file in English](./README_EN.md)
+[![NPM](https://nodei.co/npm-dl/nodeppt.png)](https://nodei.co/npm/nodeppt/)
+[![NPM](https://nodei.co/npm/nodeppt.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/nodeppt/)
+
## 为什么选择nodePPT
**这可能是迄今为止最好的网页版PPT**
@@ -18,7 +21,10 @@ nodePPT - 让你爱上做分享!
* [支持进入/退出回调](#callback),做在线demo很方便
* 支持事件update函数,查看[demo](http://qdemo.sinaapp.com/#12)
-## 0.9.0新功能
+## 1.0.0新功能
+ * 实现watch功能`nodeppt start -w`
+
+## 0.9.0 新功能
* 添加画板多端同步
* 添加按钮控制进度
* 新增两种转场动效,增加事件绑定方法:`Slide.on`
diff --git a/README_EN.md b/README_EN.md
index 22be72958..bcface51f 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -2,49 +2,38 @@ nodePPT - This is probably the best web presentation tool so far!
==================================
中文说明:[README](./README.md)
+[![NPM](https://nodei.co/npm-dl/nodeppt.png)](https://nodei.co/npm/nodeppt/)
+[![NPM](https://nodei.co/npm/nodeppt.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/nodeppt/)
+
## why nodePPT?
* markdown based on GFM;
-
* mix-code with html and markdown
-
* export your work with html and pdf format;
-
* 18 different transition animations, and you can choose single page animation well;
-
* Setting one page background image different than others;
-
* overview mode, multiscreen mode, remote control with socket, shake to page-flipping with ipad/iphone;
-
* canvas is also supported, with socket, we **sync your multiscreen in real time**, and you can type some notes;
-
* syntax highlighting of course, and you may want to customize your [syntax highlighting style](https://highlightjs.org/), it's supported well;
-
* Animation in single page, one-step animation;
-
* [forward and backward callback function](#callback)
-## 0.9.0 new features
-
+## 1.0.0 new features
* real time sync canvas drawing across multiple devices
-
+ * watch
* add buttons to control page-flipping
-
* bugs fixed
## demo
* http://qdemo.sinaapp.com/
-
* sync multiscreen in real time: http://qdemo.sinaapp.com/?_multiscreen=1 (make sure alert is allowed in your browser)
-
* front-end experience of mobile baidu: http://qdemo.sinaapp.com/box-fe-road.htm
## customize your theme
* default theme is not cool? just customize your theme! take a look with [theme.moon](https://github.com/ksky521/nodePPT/blob/master/assets/scss/theme.moon.scss)
-
* write your customize theme's template path in setting md:
```markdown
diff --git a/assets/js/nodeppt.control.js b/assets/js/nodeppt.control.js
index ab6627bf1..4c1ba419c 100755
--- a/assets/js/nodeppt.control.js
+++ b/assets/js/nodeppt.control.js
@@ -46,8 +46,6 @@
t.sendUpdateItem(slideID, buildItem);
}).on('slide event keyup', function(e) {
t.sendKeyEvent(e.keyCode);
- }).on('overview', function(e) {
- t.sendKeyEvent(79);
}).on('show paint', function(e){
t.sendKeyEvent(80);
}).on('remove paint', function(){
diff --git a/assets/js/nodeppt.control.socket.js b/assets/js/nodeppt.control.socket.js
index 350ad0b69..9b56df95d 100755
--- a/assets/js/nodeppt.control.socket.js
+++ b/assets/js/nodeppt.control.socket.js
@@ -90,20 +90,12 @@ Slide.Control.add('socket', function(S, broadcast) {
action = action.replace('client', 'control');
broadcast.fire(action, data);
-
- // switch (action) {
- // case 'from client update':
- // broadcast.fire('from control update', data);
- // break;
- // case 'from client updateItem':
- // broadcast.fire('from control updateItem', data);
- // break;
- // }
});
},
connect: function() {
- webSocket = io.connect(this.host);
+ webSocket = io.connect(location.host+'/ppt');
+ // console.log(io);
webSocket.on('UUID', function(uid) {
webSocket.uid = uid;
if (Socket.role === 'client') {
@@ -111,7 +103,7 @@ Slide.Control.add('socket', function(S, broadcast) {
qrcodeLink();
var url = location.href.split('#')[0];
url += (!~url.indexOf('?')) ? '?' : '&';
- url += 'iscontroller=1&clientid=' + uid;
+ url += 'iscontroller=1&clientid=' + encodeURIComponent(uid);
var qrcode = new QRCode('qrcode', {
text: url,
width: 256,
@@ -162,7 +154,6 @@ Slide.Control.add('socket', function(S, broadcast) {
// console.log(this.clientUID);
//角色,是否为控制端
if (args.isControl) {
- console.log(this.clientUID);
this.role = 'control';
var $body = document.body;
$body.classList.add('popup');
@@ -202,9 +193,15 @@ Slide.Control.add('socket', function(S, broadcast) {
});
}
- MixJS.loadJS(socketIOURL, function() {
+ if (io && io.connect) {
+ //已经存在
Socket.connect();
- });
+ } else {
+ MixJS.loadJS(socketIOURL, function() {
+ Socket.connect();
+ });
+ }
+
}
};
return Socket;
diff --git a/assets/js/nodeppt.js b/assets/js/nodeppt.js
index d01376fd7..58ecc5a8b 100755
--- a/assets/js/nodeppt.js
+++ b/assets/js/nodeppt.js
@@ -185,7 +185,7 @@
build: item.dataset.index
})
list = item.classList;
- $B.fire('slide.update',curIndex|0, item.dataset.index|0+1);
+ $B.fire('slide.update', curIndex | 0, item.dataset.index | 0 + 1);
list.remove('to-build');
list.add('building');
@@ -240,7 +240,7 @@
if ($doc.body.classList.contains('overview')) {
focusOverview_();
return;
- }else if(!$doc.body.classList.contains('popup')){
+ } else if (!$doc.body.classList.contains('popup')) {
$doc.body.classList.remove('with-notes');
}
@@ -283,7 +283,7 @@
break;
}
}
- $B.fire('slide.update', curIndex,0, pageClass);
+ $B.fire('slide.update', curIndex, 0, pageClass);
}
@@ -345,102 +345,101 @@
/*************************events***************/
//pc键盘翻页事件逻辑
-
function evtDocUp(e) {
- var key = e.keyCode;
- var target = e.target;
- //防止input和textarea,和可以编辑tag
- if (/^(input|textarea)$/i.test(target.nodeName) || target.isContentEditable) {
- return;
- }
- if (!e.isFromControl) {
- switch (key) {
- case 13:
- case 72:
- case 87:
- case 79:
- case 78:
- case 80:
- case 67:
- $B.fire('slide event keyup', e);
- break;
- }
- }
+ var key = e.keyCode;
+ var target = e.target;
+ //防止input和textarea,和可以编辑tag
+ if (/^(input|textarea)$/i.test(target.nodeName) || target.isContentEditable) {
+ return;
+ }
+ if (!e.isFromControl) {
switch (key) {
case 13:
- // Enter
- if ($doc.body.classList.contains('overview')) {
- overview(e.isFromControl);
- }
-
- break;
case 72:
- // H: Toggle code highlighting
- $doc.body.classList.toggle('highlight-code');
- setTimeout(function() {
- $doc.body.classList.toggle('highlight-code');
- }, 2000);
- break;
- // 下掉宽屏模式,默认width:100%
case 87:
- // W: Toggle widescreen
- // Only respect 'w' on body. Don't want to capture keys from an .
- if (!(e.shiftKey && e.metaKey)) {
- if (!$body.classList.contains('popup'))
- $container.classList.toggle('layout-widescreen');
- }
- break;
case 79:
- // O: Toggle overview
- overview(e.isFromControl);
-
- break;
case 78:
- // N
- if (!$body.classList.contains('popup'))
- $doc.body.classList.toggle('with-notes');
- break;
case 80:
- //P
- if (!$body.classList.contains('popup')) {
- showPaint(e.isFromControl);
- }
- break;
case 67:
- //c
- if (!$body.classList.contains('popup')) {
- removePaint(e.isFromControl);
- }
- break;
- //上一页
- case 33:
- // pg up
- case 37:
- // left
- case 38:
- // up
- prevSlide();
- break;
- //下一页
- // case 9:
- // tab
- case 32:
- // space
- case 34:
- // pg down
- case 39:
- // right
- case 40:
- // down
- nextSlide()
+ $B.fire('slide event keyup', e);
break;
}
-
- // $container.style.marginLeft = -(curIndex * slideWidth) + 'px';
- // setProgress();
- // setHistory();
}
- /******************************** Touch events *********************/
+ switch (key) {
+ case 13:
+ // Enter
+ if ($doc.body.classList.contains('overview')) {
+ overview(e.isFromControl);
+ }
+
+ break;
+ case 72:
+ // H: Toggle code highlighting
+ $doc.body.classList.toggle('highlight-code');
+ setTimeout(function() {
+ $doc.body.classList.toggle('highlight-code');
+ }, 2000);
+ break;
+ // 下掉宽屏模式,默认width:100%
+ case 87:
+ // W: Toggle widescreen
+ // Only respect 'w' on body. Don't want to capture keys from an .
+ if (!(e.shiftKey && e.metaKey)) {
+ if (!$body.classList.contains('popup'))
+ $container.classList.toggle('layout-widescreen');
+ }
+ break;
+ case 79:
+ // O: Toggle overview
+ overview(e.isFromControl);
+
+ break;
+ case 78:
+ // N
+ if (!$body.classList.contains('popup'))
+ $doc.body.classList.toggle('with-notes');
+ break;
+ case 80:
+ //P
+ if (!$body.classList.contains('popup')) {
+ showPaint(e.isFromControl);
+ }
+ break;
+ case 67:
+ //c
+ if (!$body.classList.contains('popup')) {
+ removePaint(e.isFromControl);
+ }
+ break;
+ //上一页
+ case 33:
+ // pg up
+ case 37:
+ // left
+ case 38:
+ // up
+ prevSlide();
+ break;
+ //下一页
+ // case 9:
+ // tab
+ case 32:
+ // space
+ case 34:
+ // pg down
+ case 39:
+ // right
+ case 40:
+ // down
+ nextSlide()
+ break;
+ }
+
+ // $container.style.marginLeft = -(curIndex * slideWidth) + 'px';
+ // setProgress();
+ // setHistory();
+ };
+ /******************************** Touch events *********************/
var isStopTouchEvent = false;
function evtTouchStart(event) {
diff --git a/bin/nodeppt b/bin/nodeppt
index 2fcf28c7a..05fe7d64f 100755
--- a/bin/nodeppt
+++ b/bin/nodeppt
@@ -70,6 +70,7 @@ program
.option('-p, --port [port]', 'set server port ', 8080)
.option('-c, --controller [socket]', 'support websocket mutil screen controller')
.option('-H, --host [host]', 'set host address', ipv4 || '0.0.0.0')
+ .option('-w, --watch', 'livereload')
.action(function (cmd){
if(typeof cmd !== 'object'){
this.outputHelp();
diff --git a/lib/server.js b/lib/server.js
index b48557d80..bf70a8b60 100755
--- a/lib/server.js
+++ b/lib/server.js
@@ -10,9 +10,10 @@ var libDir = __dirname;
var rootDir = path.join(libDir, '../') + path.sep;
var templateDir = path.join(rootDir, 'template') + path.sep;
+var chokidar = require('chokidar');
var md_parser = require('./md_parser');
-//session 相关
-var cookie = require('cookie');
+//session相关
+var Cookie = require('cookie');
var parseSignedCookie = connect.utils.parseSignedCookie;
var MemoryStore = connect.middleware.session.MemoryStore;
var Session = connect.middleware.session.Session;
@@ -20,9 +21,13 @@ var Session = connect.middleware.session.Session;
var storeMemory = new MemoryStore({
reapInterval: 60000 * 10
});
-
module.exports.start = function(port, pptDir, host, argvObj) {
port = parseInt(port, 10) || 8080;
+ pptDir = (pptDir || path.join(__dirname, '../ppts')) + path.sep;
+ try {
+ pptDir = fs.realpathSync(pptDir) + path.sep;
+ } catch (e) {}
+
var app = startApp(port, pptDir, host, argvObj);
var io = require('socket.io').listen(app, {
log: false,
@@ -32,10 +37,10 @@ module.exports.start = function(port, pptDir, host, argvObj) {
io.set('authorization', function(handshakeData, accept) {
// 通过客户端的cookie字符串来获取其session数据
var ccc = '';
- if(handshakeData.headers && handshakeData.headers.cookie){
+ if (handshakeData.headers && handshakeData.headers.cookie) {
ccc = handshakeData.headers.cookie;
}
- handshakeData.cookie = cookie.parse(ccc);
+ handshakeData.cookie = Cookie.parse(ccc);
var connect_sid = parseSignedCookie(handshakeData.cookie['connect.sid'], 'wyq');
if (connect_sid) {
storeMemory.get(connect_sid, function(error, session) {
@@ -53,19 +58,38 @@ module.exports.start = function(port, pptDir, host, argvObj) {
accept('nosession');
}
});
+ //watcher对应的socket
+ var watchSockets = {};
+ if (argvObj.watch) {
+ //添加watch功能
+ var exclude = ["\\/\\.", "node_modules"];
+
+ var watcher = chokidar.watch(pptDir, {
+ ignored: new RegExp(exclude.join('|')),
+ persistent: true
+ });
+ watcher.on('change', function() {
+ sendChangedNotice();
+ });
+ io.of('/watcher').on('connection', function(socket) {
+ watchSockets[socket.id] = socket;
+ });
+ }
+
+ //用户双屏通信
var now = Date.now();
var sockets = {};
var userMap = {};
- io.sockets.on('connection', function(socket) {
- var session = socket.handshake.session; //session
- // var uid = now++;
- var uid = session.uid;
+ io.of('/ppt').on('connection', function(socket) {
+ //解析cookie中的sid
+ var cookie = socket.handshake.headers.cookie;
+ cookie = Cookie.parse(cookie || '');
+ var uid = cookie['connect.sid'];
socket.uid = uid;
- console.log('socket.io:' + uid);
sockets[uid] = socket;
//监听添加map
@@ -88,7 +112,6 @@ module.exports.start = function(port, pptDir, host, argvObj) {
var otherSocket = sockets[targetUid];
otherSocket && otherSocket.emit('data from another client', data);
});
-
//将当前分配的uid告知客户端
socket.emit('UUID', uid);
@@ -104,15 +127,23 @@ module.exports.start = function(port, pptDir, host, argvObj) {
});
});
+
+ function sendChangedNotice() {
+ if (!Object.keys(watchSockets)) {
+ return console.error('[watch-connect]', 'No client connected to socket.io');
+ }
+ Object.keys(watchSockets).forEach(function(s) {
+ if (watchSockets[s] && watchSockets[s].emit) {
+ watchSockets[s].emit('file changed');
+ }
+ });
+ }
};
function startApp(port, dir, host, argvObj) {
host = host || '0.0.0.0';
var staticDir = path.normalize(path.join(__dirname, '../assets')) + path.sep;
- var pptDir = (dir || path.join(__dirname, '../ppts')) + path.sep;
- try {
- pptDir = fs.realpathSync(pptDir) + path.sep;
- } catch (e) {}
+ var pptDir = dir;
var now = Date.now();
var app = connect(
@@ -121,33 +152,16 @@ function startApp(port, dir, host, argvObj) {
secret: 'wyq',
store: storeMemory
}),
-
function(req, res) {
var url = URL.parse(req.url).pathname;
var dirname = path.dirname(url);
var realPath, ext;
-
- var uid = req.session.uid;
- if (!uid) {
- uid = now++; //当前连接用户的UID
- req.session.uid = uid;
- console.log('new user: ' + uid);
- } else {
- console.log('welcome back ' + uid);
- }
-
if (url === '/') {
+ //根目录显示list
pptlist(res, pptDir, argvObj);
return;
} else if (dirname === '/md') {
- uid = req.session.uid;
- if (!uid) {
- uid = now++; //当前连接用户的UID
- req.session.uid = uid;
- console.log('new user: ' + uid);
- } else {
- console.log('welcome back ' + uid);
- }
+ //md文件解析
url = URL.parse(req.url).pathname;
var basename = path.basename(url);
var queryObj = URL.parse(req.url, true).query;
@@ -173,7 +187,7 @@ function startApp(port, dir, host, argvObj) {
var url = ' http://' + address + ':' + server.port
console.log('ppt directory:'.cyan + ' ' + pptDir);
- console.log('assets directory:'.cyan + ' '+staticDir);
+ console.log('assets directory:'.cyan + ' ' + staticDir);
console.log('nodeppt server started:'.cyan + url.yellow);
if (process.platform === 'win32') {
@@ -232,6 +246,15 @@ function markdown(realPath, url, res, argvObj, queryObj) {
var content = fs.readFileSync(realPath, 'utf-8').toString();
try {
var html = md_parser(content, function() {}, argvObj, queryObj);
+ //添加socket的html代码
+ var extraHtml = '';
+ if (argvObj.watch) {
+ extraHtml += getWatcherHtml();
+ }
+ if (extraHtml.trim() !== '') {
+ html = html.split('').join(extraHtml);
+ }
+
res.writeHead(200, {
'Powered-By': 'nodePPT',
'Content-Type': mimes.html
@@ -324,3 +347,17 @@ function pptlist(res, dir, argvObj) {
}
res.end();
}
+
+
+function getWatcherHtml() {
+ var html = ['',
+ ''
+ ].join('');
+ return html;
+}
diff --git a/package.json b/package.json
index b46d0dd4f..2bf48f596 100755
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "nodeppt",
"jsname": "nodeppt",
"description": "A simple, in-browser, markdown-driven presentation framework",
- "version": "0.9.8-5",
+ "version": "1.0.0",
"site": "https://github.com/ksky521/nodePPT",
"author": {
"name": "Theo Wang",
@@ -23,7 +23,8 @@
"ejs": "0.8.4",
"ipv4": "0.0.4",
"read": ">=1.0.4",
- "socket.io": "0.9.13"
+ "socket.io": "1.3.3",
+ "chokidar":"1.0.0-rc3"
},
"devDependencies": {
"grunt": ">=0.4.0",
diff --git a/template/markdown.ejs b/template/markdown.ejs
index 95bdba6bd..3f7bc39aa 100755
--- a/template/markdown.ejs
+++ b/template/markdown.ejs
@@ -79,7 +79,7 @@ Slide.init({
args:{
isControl: <% if (query.iscontroller) {%>true<%} else{%> false<%}%>,
host: base,
- clientId: <% if (query.clientid) {%><%-query.clientid%><%} else{%> 0<%}%>,
+ clientId: "<% if (query.clientid) {%><%-query.clientid%><%} else{%> 0<%}%>",
//摇一摇
shake: <% if (!query.noshake) {%>true<%} else{%> false<%}%>
}
@@ -102,5 +102,6 @@ MixJS.loadJS('highlight/hljs-0.8.js',function(){
});
<%- files %>
+