Initial commit
This commit is contained in:
		
						commit
						3490d8aab7
					
				
							
								
								
									
										21
									
								
								LICENSE.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								LICENSE.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2015 Thomas FORGIONE
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
							
								
								
									
										18
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
# slideshow.io
 | 
			
		||||
 | 
			
		||||
## What is it
 | 
			
		||||
Client-server application to manage slides on different computers during a
 | 
			
		||||
presentation.
 | 
			
		||||
 | 
			
		||||
You can upload your pdf, and a link will be sent so you can share it with your
 | 
			
		||||
viewer. You will have a *speaker* interface, allowing you to switch between
 | 
			
		||||
slides and interact with them (underline elements, use a pointer...) and your
 | 
			
		||||
viewer will see what you are currently doing.
 | 
			
		||||
 | 
			
		||||
## Install
 | 
			
		||||
A simple `npm install` will work.
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
This software is under MIT license. Check
 | 
			
		||||
[LICENSE.md](https://github.com/tforgione/slideshow.io/blob/master/LICENSE.md) for
 | 
			
		||||
more information.
 | 
			
		||||
							
								
								
									
										61
									
								
								lib/get.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								lib/get.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
			
		||||
var express = require('express');
 | 
			
		||||
var fs = require('fs');
 | 
			
		||||
var log = require('log.js')
 | 
			
		||||
 | 
			
		||||
module.exports = function(parent){
 | 
			
		||||
 | 
			
		||||
    log.debug("Loading get :");
 | 
			
		||||
 | 
			
		||||
    fs.readdirSync(__dirname + '/../routes').forEach(function(name){
 | 
			
		||||
 | 
			
		||||
        // index.js in controller, with function as pages (views.py for django)
 | 
			
		||||
        var obj = require('./../routes/' + name + '/index');
 | 
			
		||||
 | 
			
		||||
        // urls.js, just like django urls.py
 | 
			
		||||
        var urls = require('./../routes/' + name + '/urls');
 | 
			
		||||
        name = obj.name || name;
 | 
			
		||||
        var app = express();
 | 
			
		||||
 | 
			
		||||
        // allow specifying the view engine
 | 
			
		||||
        if (obj.engine) app.set('view engine', obj.engine);
 | 
			
		||||
        app.set('views', __dirname + '/../routes/' + name + '/views');
 | 
			
		||||
 | 
			
		||||
        // generate routes based
 | 
			
		||||
        // on the exported methods
 | 
			
		||||
 | 
			
		||||
        log.debug('   ' + name + ':');
 | 
			
		||||
 | 
			
		||||
        for (var key in urls) {
 | 
			
		||||
 | 
			
		||||
            app.get(key, ((key) => function(req, res, next) {
 | 
			
		||||
 | 
			
		||||
                var path = obj[urls[key]](req, res, function(view) {
 | 
			
		||||
                    res.render(
 | 
			
		||||
                        __dirname +
 | 
			
		||||
                        '/../routes/' +
 | 
			
		||||
                        name + '/views/' +
 | 
			
		||||
                        view,
 | 
			
		||||
                        res.locals,
 | 
			
		||||
                        function(err, out) {
 | 
			
		||||
                            if (err !== null) {
 | 
			
		||||
                                log.pugerror(err);
 | 
			
		||||
                            }
 | 
			
		||||
                            res.send(out);
 | 
			
		||||
                        }
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                }, next);
 | 
			
		||||
 | 
			
		||||
            })(key));
 | 
			
		||||
 | 
			
		||||
            log.debug('      ' + key + ' -> ' + name + '.' + urls[key]);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        log.debug();
 | 
			
		||||
 | 
			
		||||
        // mount the app
 | 
			
		||||
        parent.use(app);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										145
									
								
								lib/log.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								lib/log.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,145 @@
 | 
			
		||||
var express = require('express');
 | 
			
		||||
var http = require('http');
 | 
			
		||||
var yargs = require('yargs');
 | 
			
		||||
 | 
			
		||||
var argv = yargs.argv;
 | 
			
		||||
 | 
			
		||||
var log = {};
 | 
			
		||||
 | 
			
		||||
var Color = {
 | 
			
		||||
    DEFAULT:0,
 | 
			
		||||
    BLACK:1,
 | 
			
		||||
    RED:2,
 | 
			
		||||
    GREEN:3,
 | 
			
		||||
    YELLOW:4,
 | 
			
		||||
    BLUE:5,
 | 
			
		||||
    MAGENTA:6,
 | 
			
		||||
    CYAN:7,
 | 
			
		||||
    ORANGE:8
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getColorCode(c) {
 | 
			
		||||
    switch (c) {
 | 
			
		||||
        case Color.DEFAULT: return '\u001b[0m';
 | 
			
		||||
        case Color.BLACK:   return '\u001b[30m';
 | 
			
		||||
        case Color.RED:     return '\u001b[31m';
 | 
			
		||||
        case Color.GREEN:   return '\u001b[32m';
 | 
			
		||||
        case Color.YELLOW:  return '\u001b[33m';
 | 
			
		||||
        case Color.BLUE:    return '\u001b[34m';
 | 
			
		||||
        case Color.MAGENTA: return '\u001b[35m';
 | 
			
		||||
        case Color.CYAN:    return '\u001b[36m';
 | 
			
		||||
        case Color.ORANGE:  return '\u001b[38;5;202m';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var isDev = require('express')().get('env') === 'development';
 | 
			
		||||
 | 
			
		||||
var write;
 | 
			
		||||
 | 
			
		||||
if (argv.nolisten || argv.n) {
 | 
			
		||||
    write = function(elt, color) { }
 | 
			
		||||
} else if (isDev) {
 | 
			
		||||
    write = function(elt, color) {
 | 
			
		||||
        console.log(getColorCode(color) + elt + getColorCode(Color.DEFAULT));
 | 
			
		||||
    }
 | 
			
		||||
} else {
 | 
			
		||||
    write = function(elt, color) {
 | 
			
		||||
        console.log(elt);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
log.ready = function(msg) {
 | 
			
		||||
    write('[RDY] ' + new Date() + ' ' + msg, Color.GREEN);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
log.request = function(req, res, time) {
 | 
			
		||||
 | 
			
		||||
    if (req.headers['x-forwarded-for'] !== undefined || isDev) {
 | 
			
		||||
 | 
			
		||||
        var isStatic = req.url.substr(0, 7) === '/static' || req.url === '/favicon.ico';
 | 
			
		||||
 | 
			
		||||
        write(
 | 
			
		||||
            '[REQ] ' + new Date() + ' ' +
 | 
			
		||||
                (req.headers['x-forwarded-for'] || req.connection.remoteAddress) +
 | 
			
		||||
                (time !== undefined ? (' in ' + ("      " + time).slice(-6) + ' ms') : '') +
 | 
			
		||||
                ' : ' + (isStatic && req.url !== '/favicon.ico' ? '/static' + req.url : req.url),
 | 
			
		||||
            isStatic ? Color.YELLOW : Color.CYAN
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
log.socket = {};
 | 
			
		||||
 | 
			
		||||
log.socket.connection = function(socket, isTest) {
 | 
			
		||||
    write(
 | 
			
		||||
        '[SOK] ' + new Date() + ' ' +
 | 
			
		||||
        (socket.handshake.headers['x-forwarded-for'] || socket.handshake.address) + (isTest ? ' test' : '') + ' connection',
 | 
			
		||||
        Color.MAGENTA
 | 
			
		||||
     );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
log.socket.disconnect = function(socket, isTest) {
 | 
			
		||||
    write(
 | 
			
		||||
        '[SOK] ' + new Date() + ' ' +
 | 
			
		||||
            (socket.handshake.headers['x-forwarded-for'] || socket.handshake.address) + (isTest ? ' test' : '') + ' disconnect',
 | 
			
		||||
        Color.MAGENTA
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
log.dberror = function(error) {
 | 
			
		||||
    write(
 | 
			
		||||
        '[DBE] ' + new Date() + ' ' + error,
 | 
			
		||||
        Color.RED
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
log.prefetcherror = function(error) {
 | 
			
		||||
    write(
 | 
			
		||||
        '[PFE] ' + new Date() + ' ' + error,
 | 
			
		||||
        Color.RED
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
log.mailerror = function(error) {
 | 
			
		||||
    write(
 | 
			
		||||
        '[MLE] ' + new Date() + ' ' + error,
 | 
			
		||||
        Color.RED
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
log.debug = function(info, force) {
 | 
			
		||||
    if (isDev || force === true) {
 | 
			
		||||
        write(
 | 
			
		||||
            '[DBG] ' + (info !== undefined ? info : ''),
 | 
			
		||||
            Color.YELLOW
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
log.pugerror = function(error) {
 | 
			
		||||
    write(
 | 
			
		||||
        '[PER] ' + new Date() + ' ' + error,
 | 
			
		||||
        Color.RED
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
log.warning = function(message) {
 | 
			
		||||
 | 
			
		||||
    write(
 | 
			
		||||
        '[WRN] ' + new Date() + ' ' + message,
 | 
			
		||||
        Color.ORANGE
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
log.faceerror = function(message) {
 | 
			
		||||
 | 
			
		||||
    write(
 | 
			
		||||
        '[FER] ' + new Date() + ' ' + message,
 | 
			
		||||
        Color.RED
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = log;
 | 
			
		||||
							
								
								
									
										34
									
								
								lib/socket.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								lib/socket.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
			
		||||
module.exports = function(io) {
 | 
			
		||||
 | 
			
		||||
    io.on('connection', function(socket) {
 | 
			
		||||
 | 
			
		||||
        socket.emit('welcome');
 | 
			
		||||
 | 
			
		||||
        socket.on('viewer', function(filename) {
 | 
			
		||||
            socket.join(filename);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        socket.on('speaker', function(filename, socketId) {
 | 
			
		||||
            socket.join(filename);
 | 
			
		||||
            socket.speakerId = socketId;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        socket.on('change-slide', function(filename, pageNumber) {
 | 
			
		||||
            socket.broadcast.to(filename).emit('update', pageNumber);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        socket.on('pointer', function(filename, id, x, y) {
 | 
			
		||||
            if (id === socket.speakerId) {
 | 
			
		||||
                socket.broadcast.to(filename).emit('pointer', 'speaker', x, y);
 | 
			
		||||
            } else {
 | 
			
		||||
                socket.broadcast.to(filename).emit('pointer', 'speaker', x, y);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        socket.on('viewer-laser', function(filename, enabled) {
 | 
			
		||||
            socket.broadcast.to(filename).emit('viewer-laser', enabled);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
{
 | 
			
		||||
    "name" : "slideshow.io",
 | 
			
		||||
    "version" : "0.1.0",
 | 
			
		||||
    "dependencies" : {
 | 
			
		||||
        "express" : "*",
 | 
			
		||||
        "pug" : "*",
 | 
			
		||||
        "socket.io" : "*",
 | 
			
		||||
        "node-uuid" : "*",
 | 
			
		||||
        "body-parser" : "*",
 | 
			
		||||
        "app-module-path":"*"
 | 
			
		||||
    },
 | 
			
		||||
    "repository" : {
 | 
			
		||||
        "type" : "git",
 | 
			
		||||
        "url" : "https://github.com/tforgione/slideshow.io"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								routes/index/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								routes/index/index.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
			
		||||
module.exports.index = function(req, res, render, next) {
 | 
			
		||||
 | 
			
		||||
    res.setHeader('Content-Type', 'text/html');
 | 
			
		||||
    render('index.pug', res.locals)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								routes/index/urls.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								routes/index/urls.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
module.exports = {
 | 
			
		||||
    '/' : 'index'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								routes/index/views/index.pug
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								routes/index/views/index.pug
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
extends ../../../views/main.pug
 | 
			
		||||
 | 
			
		||||
block content
 | 
			
		||||
    p.
 | 
			
		||||
        Hello world !
 | 
			
		||||
							
								
								
									
										25
									
								
								routes/speaker/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								routes/speaker/index.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
			
		||||
var fs = require('fs');
 | 
			
		||||
 | 
			
		||||
module.exports.index = function(req, res, render, next) {
 | 
			
		||||
 | 
			
		||||
    fs.stat('static/uploaded/' + req.params.file + '.pdf', function(err) {
 | 
			
		||||
 | 
			
		||||
        if (err === null) {
 | 
			
		||||
 | 
			
		||||
            res.locals.file = req.params.file;
 | 
			
		||||
 | 
			
		||||
            res.setHeader('Content-Type', 'text/html');
 | 
			
		||||
            render('index.pug', res.locals);
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            // 404 : file does not exist
 | 
			
		||||
            var error = new Error('File does not exist');
 | 
			
		||||
            error.status = 404;
 | 
			
		||||
            next(error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								routes/speaker/urls.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								routes/speaker/urls.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
module.exports = {
 | 
			
		||||
    '/speaker/:file' : 'index'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								routes/speaker/views/index.pug
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								routes/speaker/views/index.pug
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
extends ../../../views/main.pug
 | 
			
		||||
 | 
			
		||||
block append css
 | 
			
		||||
    link(href="/static/css/style.css", rel="stylesheet")
 | 
			
		||||
    link(href="/static/css/speaker.css", rel="stylesheet")
 | 
			
		||||
 | 
			
		||||
block content
 | 
			
		||||
 | 
			
		||||
    .row
 | 
			
		||||
        .col-md-8
 | 
			
		||||
            #canvases
 | 
			
		||||
                canvas#canvas-pdf(style={"border" : "1px solid #000000", position:'absolute'})
 | 
			
		||||
                canvas#canvas-paint.noselect(onmousedown="sio.onMouseDown(event);", onmousemove="sio.onMouseMove(event);", onmouseup="sio.onMouseUp(event);", onmouseout="sio.onMouseUp(event);", style={position:'absolute'})
 | 
			
		||||
 | 
			
		||||
            p
 | 
			
		||||
                .btn-group
 | 
			
		||||
                    button.btn.btn-default#first(onclick="sio.firstSlide();")
 | 
			
		||||
                        span.glyphicon.glyphicon-step-backward
 | 
			
		||||
                    button.btn.btn-default#previous(aria-hidden='true', onclick="sio.previousSlide();")
 | 
			
		||||
                        span.glyphicon.glyphicon-play.glyphicon-flip
 | 
			
		||||
                    input.btn.btn-default#counter(type='text', onclick="sio.clearCounter();", onblur="sio.update();", onkeydown="if (event.keyCode === 13) sio.changeSlideFromCounter(event);")
 | 
			
		||||
                    button.btn.btn-default#next(onclick="sio.nextSlide();")
 | 
			
		||||
                        span.glyphicon.glyphicon-play
 | 
			
		||||
                    button.btn.btn-default#last(onclick="sio.lastSlide();")
 | 
			
		||||
                        span.glyphicon.glyphicon-step-forward
 | 
			
		||||
 | 
			
		||||
                span(style={'margin-left':'10px'})
 | 
			
		||||
                .btn-group
 | 
			
		||||
                    button.btn.btn-default#sync(onclick="sio.syncAudience();") Sync viewer
 | 
			
		||||
 | 
			
		||||
                span(style={'margin-left':'10px'})
 | 
			
		||||
                .btn-group
 | 
			
		||||
                    button.btn.btn-default#viewer-laser(onclick="sio.switchAudienceLaser();") Audience laser is disabled
 | 
			
		||||
 | 
			
		||||
        .col-md-4
 | 
			
		||||
            .btn-group
 | 
			
		||||
                button.btn.btn-default#start(onclick="sio.startPresentation();")
 | 
			
		||||
                    span.glyphicon.glyphicon-play
 | 
			
		||||
                button.btn.btn-default#stop(onclick="sio.stopPresentation();", disabled)
 | 
			
		||||
                    span.glyphicon.glyphicon-stop
 | 
			
		||||
            p Total time : 
 | 
			
		||||
                span#totalTime
 | 
			
		||||
            p Slide time : 
 | 
			
		||||
                span#slideTime
 | 
			
		||||
 | 
			
		||||
block js
 | 
			
		||||
    script filename = '#{file}';
 | 
			
		||||
    script(src="/static/js/pdf.js")
 | 
			
		||||
    script(src="/static/js/pdf.worker.js")
 | 
			
		||||
    script(src="/socket.io/socket.io.js")
 | 
			
		||||
    script(src="/static/js/speaker.js")
 | 
			
		||||
							
								
								
									
										25
									
								
								routes/viewer/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								routes/viewer/index.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
			
		||||
var fs = require('fs');
 | 
			
		||||
 | 
			
		||||
module.exports.index = function(req, res, render, next) {
 | 
			
		||||
 | 
			
		||||
    fs.stat('static/uploaded/' + req.params.file + '.pdf', function(err) {
 | 
			
		||||
 | 
			
		||||
        if (err === null) {
 | 
			
		||||
 | 
			
		||||
            res.locals.file = req.params.file;
 | 
			
		||||
 | 
			
		||||
            res.setHeader('Content-Type', 'text/html');
 | 
			
		||||
            render('index.pug', res.locals);
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            // 404 : file does not exist
 | 
			
		||||
            var error = new Error('File does not exist');
 | 
			
		||||
            error.status = 404;
 | 
			
		||||
            next(error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								routes/viewer/urls.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								routes/viewer/urls.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
module.exports = {
 | 
			
		||||
    '/viewer/:file' : 'index'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								routes/viewer/views/index.pug
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								routes/viewer/views/index.pug
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
extends ../../../views/base.pug
 | 
			
		||||
 | 
			
		||||
block css
 | 
			
		||||
    link(href="/static/css/style.css", rel="stylesheet")
 | 
			
		||||
    link(href="/static/css/viewer.css", rel="stylesheet")
 | 
			
		||||
 | 
			
		||||
block base_content
 | 
			
		||||
    #canvases
 | 
			
		||||
        canvas#canvas-pdf.absoluteCenter(style={position:'absolute'})
 | 
			
		||||
        canvas#canvas-paint.absoluteCenter.noselect(onmousedown="sio.onMouseDown(event);", onmousemove="sio.onMouseMove(event);", onmouseup="sio.onMouseUp(event);", onmouseout="sio.onMouseUp(event);", style={position:'absolute'})
 | 
			
		||||
 | 
			
		||||
block js
 | 
			
		||||
    script filename = '#{file}';
 | 
			
		||||
    script(src="/static/js/pdf.js")
 | 
			
		||||
    script(src="/static/js/pdf.worker.js")
 | 
			
		||||
    script(src="/socket.io/socket.io.js")
 | 
			
		||||
    script(src="/static/js/viewer.js")
 | 
			
		||||
							
								
								
									
										72
									
								
								server.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								server.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
			
		||||
require('app-module-path').addPath('./lib');
 | 
			
		||||
 | 
			
		||||
var http = require('http');
 | 
			
		||||
var express = require('express');
 | 
			
		||||
var pug = require('pug');
 | 
			
		||||
var bp = require('body-parser');
 | 
			
		||||
var socket = require('socket.js');
 | 
			
		||||
var log = require('log.js');
 | 
			
		||||
var app = express();
 | 
			
		||||
 | 
			
		||||
// Socket.io initialization
 | 
			
		||||
var http = require('http').Server(app);
 | 
			
		||||
var io = require('socket.io')(http);
 | 
			
		||||
require('./lib/socket.js')(io);
 | 
			
		||||
 | 
			
		||||
var isDev = app.get('env') === 'development';
 | 
			
		||||
 | 
			
		||||
app.set('view engine', 'pug');
 | 
			
		||||
app.set('trust proxy', 1);
 | 
			
		||||
 | 
			
		||||
// parse application/x-www-form-urlencoded
 | 
			
		||||
app.use(bp.urlencoded({ extended: false }))
 | 
			
		||||
 | 
			
		||||
// parse application/json
 | 
			
		||||
app.use(bp.json())
 | 
			
		||||
 | 
			
		||||
// Log request and time to answer
 | 
			
		||||
app.use(function(req, res, next) {
 | 
			
		||||
    let start = Date.now();
 | 
			
		||||
    res.on('finish', function() {
 | 
			
		||||
        log.request(req, res, Date.now() - start);
 | 
			
		||||
    });
 | 
			
		||||
    res.locals.title = "slideshow.io";
 | 
			
		||||
    next();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Load controllers
 | 
			
		||||
require('./lib/get')(app);
 | 
			
		||||
 | 
			
		||||
// Static files
 | 
			
		||||
app.use('/static', express.static('static'));
 | 
			
		||||
 | 
			
		||||
// When error raised
 | 
			
		||||
app.use(function(err, req, res, next) {
 | 
			
		||||
    if (err.status === 404) {
 | 
			
		||||
        res.setHeader('Content-Type', 'text/html');
 | 
			
		||||
        res.send('Error 404');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// When route not found, raise not found
 | 
			
		||||
app.use(function(req, res) {
 | 
			
		||||
    res.setHeader('Content-Type', 'text/html');
 | 
			
		||||
    res.send('Error 404');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Set ports and ip address
 | 
			
		||||
var serverPort, serverIpAddress;
 | 
			
		||||
 | 
			
		||||
if ( isDev ) {
 | 
			
		||||
    serverPort = 4001;
 | 
			
		||||
    serverIpAddress = 'localhost';
 | 
			
		||||
} else {
 | 
			
		||||
    // Openhift conf
 | 
			
		||||
    serverPort = process.env.OPENSHIFT_NODEJS_PORT || 8080;
 | 
			
		||||
    serverIpAddress = process.env.OPENSHIFT_NODEJS_IP || '127.0.0.1';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start server
 | 
			
		||||
http.listen(serverPort, serverIpAddress, function() {
 | 
			
		||||
    log.debug("Now listening " + serverIpAddress + ":" + serverPort);
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										34
									
								
								static/css/signin.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								static/css/signin.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
			
		||||
.form-signin {
 | 
			
		||||
  max-width: 330px;
 | 
			
		||||
  padding: 15px;
 | 
			
		||||
  margin: 0 auto;
 | 
			
		||||
}
 | 
			
		||||
.form-signin .form-signin-heading,
 | 
			
		||||
.form-signin .checkbox {
 | 
			
		||||
  margin-bottom: 10px;
 | 
			
		||||
}
 | 
			
		||||
.form-signin .checkbox {
 | 
			
		||||
  font-weight: normal;
 | 
			
		||||
}
 | 
			
		||||
.form-signin .form-control {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  height: auto;
 | 
			
		||||
  -webkit-box-sizing: border-box;
 | 
			
		||||
     -moz-box-sizing: border-box;
 | 
			
		||||
          box-sizing: border-box;
 | 
			
		||||
  padding: 10px;
 | 
			
		||||
  font-size: 16px;
 | 
			
		||||
}
 | 
			
		||||
.form-signin .form-control:focus {
 | 
			
		||||
  z-index: 2;
 | 
			
		||||
}
 | 
			
		||||
.form-signin input[type="email"] {
 | 
			
		||||
  margin-bottom: -1px;
 | 
			
		||||
  border-bottom-right-radius: 0;
 | 
			
		||||
  border-bottom-left-radius: 0;
 | 
			
		||||
}
 | 
			
		||||
.form-signin input[type="password"] {
 | 
			
		||||
  margin-bottom: 10px;
 | 
			
		||||
  border-top-left-radius: 0;
 | 
			
		||||
  border-top-right-radius: 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								static/css/speaker.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								static/css/speaker.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
.glyphicon-flip {
 | 
			
		||||
    -moz-transform: scaleX(-1);
 | 
			
		||||
    -o-transform: scaleX(-1);
 | 
			
		||||
    -webkit-transform: scaleX(-1);
 | 
			
		||||
    transform: scaleX(-1);
 | 
			
		||||
    filter: FlipH;
 | 
			
		||||
    -ms-filter: "FlipH";
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								static/css/style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								static/css/style.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
.noselect {
 | 
			
		||||
    cursor: default;
 | 
			
		||||
    -webkit-touch-callout: none;
 | 
			
		||||
    -webkit-user-select: none;
 | 
			
		||||
    -khtml-user-select: none;
 | 
			
		||||
    -moz-user-select: none;
 | 
			
		||||
    -ms-user-select: none;
 | 
			
		||||
    user-select: none;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								static/css/viewer.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								static/css/viewer.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
#canvas {
 | 
			
		||||
    margin:auto;
 | 
			
		||||
    position:absolute;
 | 
			
		||||
    top:0;
 | 
			
		||||
    bottom:0;
 | 
			
		||||
    left:0;
 | 
			
		||||
    right:0;
 | 
			
		||||
    max-height:100%;
 | 
			
		||||
    max-width:100%;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10375
									
								
								static/js/pdf.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10375
									
								
								static/js/pdf.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										42034
									
								
								static/js/pdf.worker.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42034
									
								
								static/js/pdf.worker.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										291
									
								
								static/js/speaker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								static/js/speaker.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,291 @@
 | 
			
		||||
$(function() { sio = (function() {
 | 
			
		||||
 | 
			
		||||
    var sio = {};
 | 
			
		||||
 | 
			
		||||
    var counter = $('#counter');
 | 
			
		||||
 | 
			
		||||
    var canvasPdf = $('#canvas-pdf').get(0);
 | 
			
		||||
    var contextPdf = canvasPdf.getContext('2d');
 | 
			
		||||
 | 
			
		||||
    var canvasPaint = $('#canvas-paint').get(0);
 | 
			
		||||
    var contextPaint = canvasPaint.getContext('2d');
 | 
			
		||||
 | 
			
		||||
    var pdf;
 | 
			
		||||
    var currentPage = 1;
 | 
			
		||||
 | 
			
		||||
    var startTime = 0;
 | 
			
		||||
    var startSlide = 0;
 | 
			
		||||
    var refreshIntervalId = 0;
 | 
			
		||||
 | 
			
		||||
    var pointer = false;
 | 
			
		||||
    var shouldUpdatePointer = false;
 | 
			
		||||
 | 
			
		||||
    var canvasPdfBounding;
 | 
			
		||||
 | 
			
		||||
    var lasers = {};
 | 
			
		||||
    var drawAudienceLasers = false;
 | 
			
		||||
 | 
			
		||||
    PDFJS.getDocument('/static/uploaded/' + filename + '.pdf').then(function getPdf(_pdf) {
 | 
			
		||||
        pdf = _pdf;
 | 
			
		||||
        updateUI();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    var socket = io();
 | 
			
		||||
 | 
			
		||||
    socket.on('welcome', function() {
 | 
			
		||||
        socket.emit('speaker', filename, socket.id);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    socket.on('pointer', canvasPointer);
 | 
			
		||||
 | 
			
		||||
    function filterInt(value) {
 | 
			
		||||
        if(/^(\-|\+)?([0-9]+|Infinity)$/.test(value))
 | 
			
		||||
            return Number(value);
 | 
			
		||||
        return NaN;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var updateUI = function() {
 | 
			
		||||
 | 
			
		||||
        counter.val(currentPage + '/' + pdf.pdfInfo.numPages);
 | 
			
		||||
 | 
			
		||||
        $('#previous,#first').prop('disabled', currentPage === 1);
 | 
			
		||||
        $('#next,#last').prop('disabled', currentPage === pdf.pdfInfo.numPages);
 | 
			
		||||
 | 
			
		||||
        pdf.getPage(currentPage).then(function(page) {
 | 
			
		||||
 | 
			
		||||
            previousPage = currentPage;
 | 
			
		||||
 | 
			
		||||
            var viewport = page.getViewport(2.0);
 | 
			
		||||
 | 
			
		||||
            canvasPdf.width = viewport.width;
 | 
			
		||||
            canvasPdf.height = viewport.height;
 | 
			
		||||
 | 
			
		||||
            canvasPaint.width = viewport.width;
 | 
			
		||||
            canvasPaint.height = viewport.height;
 | 
			
		||||
 | 
			
		||||
            $('#canvases').width(canvasPdf.width + 'px');
 | 
			
		||||
            $('#canvases').height(canvasPdf.height + 'px');
 | 
			
		||||
 | 
			
		||||
            canvasPdfBounding = canvasPdf.getBoundingClientRect();
 | 
			
		||||
 | 
			
		||||
            var renderContext = {
 | 
			
		||||
                canvasContext: contextPdf,
 | 
			
		||||
                viewport: viewport
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            page.render(renderContext);
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    function updateAudience() { socket.emit('change-slide', filename, currentPage); }
 | 
			
		||||
 | 
			
		||||
    function formatTime(ms) {
 | 
			
		||||
        var sec = padToTwo(Math.floor((ms / 1000) % 60));
 | 
			
		||||
        var min = padToTwo(Math.floor(((ms / 1000 - sec) % 3600) / 60));
 | 
			
		||||
 | 
			
		||||
        return min + ':' + sec;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function updateTimers() {
 | 
			
		||||
        if (refreshIntervalId !== 0) {
 | 
			
		||||
            $('#totalTime').html(formatTime(Date.now() - startTime));
 | 
			
		||||
            $('#slideTime').html(formatTime(Date.now() - startSlide));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function padToTwo(number) {
 | 
			
		||||
        if (number<=99) { number = ("0"+number).slice(-2); }
 | 
			
		||||
        return number;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function resetTimeSlide() {
 | 
			
		||||
        startSlide = Date.now();
 | 
			
		||||
        updateTimers();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // Check if number is valid, and updates
 | 
			
		||||
    // number undefined means sync all clients
 | 
			
		||||
    function changeSlide(number) {
 | 
			
		||||
 | 
			
		||||
        if (isNaN(number) || number < 1 || number > pdf.pdfInfo.numPages) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (currentPage === undefined || currentPage === number) {
 | 
			
		||||
            updateAudience();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        currentPage = number;
 | 
			
		||||
 | 
			
		||||
        updateUI();
 | 
			
		||||
        updateAudience();
 | 
			
		||||
        resetTimeSlide();
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function canvasPointer(id,x,y) {
 | 
			
		||||
 | 
			
		||||
        // Clear canvas
 | 
			
		||||
        canvasPaint.width = canvasPaint.width;
 | 
			
		||||
 | 
			
		||||
        if (id != undefined) {
 | 
			
		||||
 | 
			
		||||
            if (x != undefined && y != undefined) {
 | 
			
		||||
 | 
			
		||||
                lasers[id] = {x:x, y:y};
 | 
			
		||||
 | 
			
		||||
            } else {
 | 
			
		||||
 | 
			
		||||
                delete lasers[id];
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (var laserIndex in lasers) {
 | 
			
		||||
 | 
			
		||||
            var laser = lasers[laserIndex];
 | 
			
		||||
 | 
			
		||||
            if (drawAudienceLasers === true || laserIndex === socket.id) {
 | 
			
		||||
 | 
			
		||||
                contextPaint.fillStyle = laserIndex === socket.id ? 'red' : 'green';
 | 
			
		||||
                contextPaint.beginPath();
 | 
			
		||||
                contextPaint.arc(laser.x * canvasPdf.width, laser.y * canvasPdf.height, 10, 0, Math.PI*2, true);
 | 
			
		||||
                contextPaint.closePath();
 | 
			
		||||
                contextPaint.fill();
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sio.onMouseDown = function(event) {
 | 
			
		||||
 | 
			
		||||
        var x = (event.x - canvasPdfBounding.left) / canvasPdf.width;
 | 
			
		||||
        var y = (event.y - canvasPdfBounding.top) / canvasPdf.height;
 | 
			
		||||
 | 
			
		||||
        socket.emit('pointer', filename, socket.id, x, y);
 | 
			
		||||
 | 
			
		||||
        pointer = true;
 | 
			
		||||
        shouldUpdatePointer = true;
 | 
			
		||||
 | 
			
		||||
        canvasPointer(socket.id, x, y);
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    sio.onMouseMove = function(event) {
 | 
			
		||||
 | 
			
		||||
        if (pointer) {
 | 
			
		||||
 | 
			
		||||
            var x = (event.x - canvasPdfBounding.left) / canvasPdf.width;
 | 
			
		||||
            var y = (event.y - canvasPdfBounding.top) / canvasPdf.height;
 | 
			
		||||
 | 
			
		||||
            if (shouldUpdatePointer) {
 | 
			
		||||
                // Update
 | 
			
		||||
                socket.emit('pointer', filename, socket.id, x, y);
 | 
			
		||||
 | 
			
		||||
                shouldUpdatePointer = false;
 | 
			
		||||
 | 
			
		||||
                setTimeout(function() {
 | 
			
		||||
 | 
			
		||||
                    shouldUpdatePointer = true;
 | 
			
		||||
 | 
			
		||||
                }, 20);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            canvasPointer(socket.id, x, y);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    sio.onMouseUp = function(event) {
 | 
			
		||||
 | 
			
		||||
        socket.emit('pointer', filename, socket.id);
 | 
			
		||||
 | 
			
		||||
        pointer = false;
 | 
			
		||||
        shouldUpdatePointer = false;
 | 
			
		||||
 | 
			
		||||
        canvasPointer(socket.id);
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    sio.startPresentation = function() {
 | 
			
		||||
 | 
			
		||||
        startTime = startSlide = Date.now();
 | 
			
		||||
 | 
			
		||||
        refreshIntervalId = setInterval(updateTimers, 1000);
 | 
			
		||||
        updateTimers();
 | 
			
		||||
 | 
			
		||||
        $('#start').prop('disabled', true);
 | 
			
		||||
        $('#stop').prop('disabled', false);
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    sio.stopPresentation = function() {
 | 
			
		||||
 | 
			
		||||
        clearInterval(refreshIntervalId);
 | 
			
		||||
        refreshIntervalId = 0;
 | 
			
		||||
 | 
			
		||||
        $('#start').prop('disabled', false);
 | 
			
		||||
        $('#stop').prop('disabled', true);
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    sio.changeSlideFromCounter = function() {
 | 
			
		||||
 | 
			
		||||
        var slideNumber = filterInt($('#counter').val());
 | 
			
		||||
 | 
			
		||||
        changeSlide(slideNumber);
 | 
			
		||||
 | 
			
		||||
        // lose focus
 | 
			
		||||
        $(':focus').blur();
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    sio.switchAudienceLaser = function() {
 | 
			
		||||
 | 
			
		||||
        var laser = $('#viewer-laser');
 | 
			
		||||
 | 
			
		||||
        if (drawAudienceLasers === false) {
 | 
			
		||||
 | 
			
		||||
            // Laser is disabled, will be enabled
 | 
			
		||||
            laser.text('Audience laser is enabled');
 | 
			
		||||
            laser.removeClass('btn-default').addClass('btn-primary');
 | 
			
		||||
            drawAudienceLasers = true;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            laser.text('Audience laser is disabled');
 | 
			
		||||
            laser.removeClass('btn-primary').addClass('btn-default');
 | 
			
		||||
            drawAudienceLasers = false;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        socket.emit('viewer-laser', filename, drawAudienceLasers);
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    sio.clearCounter   = function() { $('#counter').val('');             };
 | 
			
		||||
    sio.nextSlide      = function() { changeSlide(currentPage + 1);      };
 | 
			
		||||
    sio.previousSlide  = function() { changeSlide(currentPage - 1);      };
 | 
			
		||||
    sio.firstSlide     = function() { changeSlide(1);                    };
 | 
			
		||||
    sio.lastSlide      = function() { changeSlide(pdf.pdfInfo.numPages); };
 | 
			
		||||
 | 
			
		||||
    sio.syncAudience   = updateAudience;
 | 
			
		||||
    sio.update         = updateUI;
 | 
			
		||||
 | 
			
		||||
    $(window).resize(updateUI);
 | 
			
		||||
 | 
			
		||||
    return sio;
 | 
			
		||||
 | 
			
		||||
})(); });
 | 
			
		||||
							
								
								
									
										175
									
								
								static/js/viewer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								static/js/viewer.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,175 @@
 | 
			
		||||
$(function() { sio = (function() {
 | 
			
		||||
 | 
			
		||||
    var sio = {};
 | 
			
		||||
 | 
			
		||||
    var canvasPdf = $('#canvas-pdf').get(0);
 | 
			
		||||
    var contextPdf = canvasPdf.getContext('2d');
 | 
			
		||||
 | 
			
		||||
    var canvasPaint = $('#canvas-paint').get(0);
 | 
			
		||||
    var contextPaint = canvasPaint.getContext('2d');
 | 
			
		||||
 | 
			
		||||
    var pdf;
 | 
			
		||||
    var pageNumber = 1;
 | 
			
		||||
 | 
			
		||||
    var pointer = false;
 | 
			
		||||
    var canvasPdfBounding;
 | 
			
		||||
 | 
			
		||||
    var drawAudienceLaser = false;
 | 
			
		||||
    var lasers = {};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    PDFJS.getDocument('/static/uploaded/' + filename + '.pdf').then(function getPdfHelloWorld(_pdf) {
 | 
			
		||||
 | 
			
		||||
        pdf = _pdf;
 | 
			
		||||
        update();
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    var socket = io();
 | 
			
		||||
 | 
			
		||||
    socket.on('welcome', function() {
 | 
			
		||||
        socket.emit('viewer', filename);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    socket.on('update', function(newPageNumber) {
 | 
			
		||||
        pageNumber = newPageNumber;
 | 
			
		||||
        update();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    socket.on('viewer-laser', function(enabled) {
 | 
			
		||||
        drawAudienceLaser = enabled;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    socket.on('pointer', canvasPointer);
 | 
			
		||||
 | 
			
		||||
    function update() {
 | 
			
		||||
 | 
			
		||||
        pdf.getPage(pageNumber).then(function(page) {
 | 
			
		||||
 | 
			
		||||
            var scale;
 | 
			
		||||
 | 
			
		||||
            // width / height
 | 
			
		||||
            var ratio = page.pageInfo.view[2] / page.pageInfo.view[3];
 | 
			
		||||
 | 
			
		||||
            if (window.innerWidth < window.innerHeight) {
 | 
			
		||||
 | 
			
		||||
                canvasPdf.width = window.innerWidth;
 | 
			
		||||
                canvasPdf.height = canvasPdf.width / ratio;
 | 
			
		||||
                scale = canvasPdf.width / page.getViewport(1.0).width;
 | 
			
		||||
 | 
			
		||||
            } else {
 | 
			
		||||
 | 
			
		||||
                canvasPdf.height = window.innerHeight;
 | 
			
		||||
                canvasPdf.width = ratio * canvasPdf.height;
 | 
			
		||||
                scale = canvasPdf.width / page.getViewport(1.0).width;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            canvasPaint.width = canvasPdf.width;
 | 
			
		||||
            canvasPaint.height = canvasPdf.height;
 | 
			
		||||
 | 
			
		||||
            $('#canvases').width(canvasPdf.width + 'px');
 | 
			
		||||
            $('#canvases').height(canvasPdf.height + 'px');
 | 
			
		||||
 | 
			
		||||
            canvasPdfBounding = canvasPdf.getBoundingClientRect();
 | 
			
		||||
 | 
			
		||||
            var viewport = page.getViewport(scale);
 | 
			
		||||
 | 
			
		||||
            var renderContext = {
 | 
			
		||||
                canvasContext: contextPdf,
 | 
			
		||||
                viewport: viewport
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            page.render(renderContext);
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function canvasPointer(id,x,y) {
 | 
			
		||||
 | 
			
		||||
        // Clear canvas
 | 
			
		||||
        canvasPaint.width = canvasPaint.width;
 | 
			
		||||
 | 
			
		||||
        if (id != undefined) {
 | 
			
		||||
            if ( x != undefined && y != undefined) {
 | 
			
		||||
                lasers[id] = {x:x, y:y};
 | 
			
		||||
            } else {
 | 
			
		||||
                delete lasers[id];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (var laserIndex in lasers) {
 | 
			
		||||
 | 
			
		||||
            var laser = lasers[laserIndex];
 | 
			
		||||
 | 
			
		||||
            if (drawAudienceLaser === true || laserIndex === 'speaker') {
 | 
			
		||||
 | 
			
		||||
                contextPaint.fillStyle = laserIndex === 'speaker' ? 'red' : 'green';
 | 
			
		||||
                contextPaint.beginPath();
 | 
			
		||||
                contextPaint.arc(laser.x * canvasPdf.width, laser.y * canvasPdf.height, 10, 0, Math.PI*2, true);
 | 
			
		||||
                contextPaint.closePath();
 | 
			
		||||
                contextPaint.fill();
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $(window).resize(update);
 | 
			
		||||
 | 
			
		||||
    sio.onMouseDown = function() {
 | 
			
		||||
 | 
			
		||||
        var x = (event.x - canvasPdfBounding.left) / canvasPdf.width;
 | 
			
		||||
        var y = (event.y - canvasPdfBounding.top) / canvasPdf.height;
 | 
			
		||||
 | 
			
		||||
        socket.emit('pointer', filename, socket.id, x, y);
 | 
			
		||||
 | 
			
		||||
        pointer = true;
 | 
			
		||||
        shouldUpdatePointer = true;
 | 
			
		||||
 | 
			
		||||
        canvasPointer(socket.id, x, y);
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    sio.onMouseMove = function(event) {
 | 
			
		||||
 | 
			
		||||
        if (pointer) {
 | 
			
		||||
 | 
			
		||||
            var x = (event.x - canvasPdfBounding.left) / canvasPdf.width;
 | 
			
		||||
            var y = (event.y - canvasPdfBounding.top) / canvasPdf.height;
 | 
			
		||||
 | 
			
		||||
            if (shouldUpdatePointer) {
 | 
			
		||||
                // Update
 | 
			
		||||
                socket.emit('pointer', filename, socket.id, x, y);
 | 
			
		||||
 | 
			
		||||
                shouldUpdatePointer = false;
 | 
			
		||||
 | 
			
		||||
                setTimeout(function() {
 | 
			
		||||
 | 
			
		||||
                    shouldUpdatePointer = true;
 | 
			
		||||
 | 
			
		||||
                }, 20);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            canvasPointer(socket.id, x, y);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    sio.onMouseUp = function(event) {
 | 
			
		||||
 | 
			
		||||
        socket.emit('pointer', filename, socket.id);
 | 
			
		||||
 | 
			
		||||
        pointer = false;
 | 
			
		||||
        shouldUpdatePointer = false;
 | 
			
		||||
 | 
			
		||||
        canvasPointer(socket.id);
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return sio;
 | 
			
		||||
 | 
			
		||||
})(); });
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								static/uploaded/last_slides.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/uploaded/last_slides.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								static/uploaded/main.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/uploaded/main.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										14
									
								
								views/base.pug
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								views/base.pug
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
doctype
 | 
			
		||||
html( lang="en" )
 | 
			
		||||
    head
 | 
			
		||||
        title slideshow.io
 | 
			
		||||
        meta( charset='utf-8' )
 | 
			
		||||
        meta( http-equiv='X-UA-Compatible', content='IE=edge' )
 | 
			
		||||
        block css
 | 
			
		||||
 | 
			
		||||
    body
 | 
			
		||||
        block base_content
 | 
			
		||||
 | 
			
		||||
        script( src='http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js' )
 | 
			
		||||
 | 
			
		||||
        block js
 | 
			
		||||
							
								
								
									
										28
									
								
								views/main.pug
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								views/main.pug
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
extends ./base.pug
 | 
			
		||||
 | 
			
		||||
block css
 | 
			
		||||
    meta( name='viewport', content='width=device-width, initial-scale=1.0' )
 | 
			
		||||
    meta( name='description', content='Baking Bootstrap Snippets with Jade' )
 | 
			
		||||
    //- Bootswatch Theme
 | 
			
		||||
    link(href="https://bootswatch.com/cerulean/bootstrap.min.css", rel="stylesheet")
 | 
			
		||||
 | 
			
		||||
block base_content
 | 
			
		||||
    nav.navbar.navbar-default( role="navigation" )
 | 
			
		||||
        .container
 | 
			
		||||
            .navbar-header
 | 
			
		||||
                button.navbar-toggle.collapsed( type="button", data-toggle="collapse", data-target="#navbar-inverse", aria-expanded="false", aria-controls="navbar")
 | 
			
		||||
                    span.sr-only Toggle navigation
 | 
			
		||||
                    span.icon-bar
 | 
			
		||||
                    span.icon-bar
 | 
			
		||||
                    span.icon-bar
 | 
			
		||||
                a.navbar-brand(href="#") slideshow.io
 | 
			
		||||
 | 
			
		||||
            #navbar-inverse.collapse.navbar-collapse
 | 
			
		||||
                ul.nav.navbar-nav
 | 
			
		||||
                    li: a( href="#about" ) About
 | 
			
		||||
 | 
			
		||||
    .container
 | 
			
		||||
        block content
 | 
			
		||||
 | 
			
		||||
block prepend js
 | 
			
		||||
    script( src='http://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js' )
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user