diff --git a/.gitignore b/.gitignore index 50cd6e21..32f9ea7d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ node_modules settings.json -static/js/jquery.min.js +static/js/jquery.js APIKEY.txt bin/abiword.exe bin/node.exe diff --git a/README.md b/README.md index aaf2c8fb..4995e852 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Here is the **[FAQ](https://github.com/Pita/etherpad-lite/wiki/FAQ)**
  1. Install the dependencies. We need gzip, git, curl, libssl develop libraries, python and gcc.
    For Debian/Ubuntu apt-get install gzip git-core curl python libssl-dev build-essential
    - For Fedora/CentOS yum install gzip git-core curl python openssl-dev && yum groupinstall "Development Tools" + For Fedora/CentOS yum install gzip git-core curl python openssl-devel && yum groupinstall "Development Tools"

  2. Install node.js
      @@ -98,7 +98,7 @@ Look at this wiki pages: You can find more information in the [wiki](https://github.com/Pita/etherpad-lite/wiki). Feel free to improve these wiki pages # Develop -If you're new to git and github, start here . +If you're new to git and github, start by watching [this video](http://youtu.be/67-Q26YH97E) then read this [git guide](http://learn.github.com/p/intro.html). If you're new to node.js, start with this video . diff --git a/bin/installDeps.sh b/bin/installDeps.sh index 0533caf2..a3f767a2 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -48,8 +48,8 @@ npm install || { echo "Ensure jQuery is downloaded and up to date..." DOWNLOAD_JQUERY="true" NEEDED_VERSION="1.7" -if [ -f "static/js/jquery.min.js" ]; then - VERSION=$(cat static/js/jquery.min.js | head -n 3 | grep -o "v[0-9].[0-9]"); +if [ -f "static/js/jquery.js" ]; then + VERSION=$(cat static/js/jquery.js | head -n 3 | grep -o "v[0-9].[0-9]"); if [ ${VERSION#v} = $NEEDED_VERSION ]; then DOWNLOAD_JQUERY="false" @@ -57,7 +57,7 @@ if [ -f "static/js/jquery.min.js" ]; then fi if [ $DOWNLOAD_JQUERY = "true" ]; then - curl -lo static/js/jquery.min.js http://code.jquery.com/jquery-$NEEDED_VERSION.min.js || exit 1 + curl -lo static/js/jquery.js http://code.jquery.com/jquery-$NEEDED_VERSION.js || exit 1 fi #Remove all minified data to force node creating it new diff --git a/node/server.js b/node/server.js index 50f48633..c0d6ce6a 100644 --- a/node/server.js +++ b/node/server.js @@ -78,7 +78,12 @@ async.waterfall([ { //create server var app = express.createServer(); - + + app.use(function (req, res, next) { + res.header("Server", serverName); + next(); + }); + //load modules that needs a initalized db readOnlyManager = require("./db/ReadOnlyManager"); exporthtml = require("./utils/ExportHtml"); @@ -109,31 +114,24 @@ async.waterfall([ gracefulShutdown(); }); + //serve minified files + app.get('/minified/:filename', minify.minifyJS); + //serve static files + app.get('/static/js/require-kernel.js', function (req, res, next) { + res.header("Content-Type","application/javascript; charset: utf-8"); + res.write(minify.requireDefinition()); + res.end(); + }); app.get('/static/*', function(req, res) { - res.header("Server", serverName); var filePath = path.normalize(__dirname + "/.." + req.url.replace(/\.\./g, '').split("?")[0]); res.sendfile(filePath, { maxAge: exports.maxAge }); }); //serve minified files - app.get('/minified/:id', function(req, res, next) - { - res.header("Server", serverName); - - var id = req.params.id; - - if(id == "pad.js" || id == "timeslider.js") - { - minify.minifyJS(req,res,id); - } - else - { - next(); - } - }); + app.get('/minified/:filename', minify.minifyJS); //checks for padAccess function hasPadAccess(req, res, callback) @@ -178,8 +176,6 @@ async.waterfall([ //serve read only pad app.get('/ro/:id', function(req, res) { - res.header("Server", serverName); - var html; var padId; var pad; @@ -264,7 +260,6 @@ async.waterfall([ app.get('/p/:pad', function(req, res, next) { goToPad(req, res, function() { - res.header("Server", serverName); var filePath = path.normalize(__dirname + "/../static/pad.html"); res.sendfile(filePath, { maxAge: exports.maxAge }); }); @@ -274,7 +269,6 @@ async.waterfall([ app.get('/p/:pad/timeslider', function(req, res, next) { goToPad(req, res, function() { - res.header("Server", serverName); var filePath = path.normalize(__dirname + "/../static/timeslider.html"); res.sendfile(filePath, { maxAge: exports.maxAge }); }); @@ -301,7 +295,6 @@ async.waterfall([ } res.header("Access-Control-Allow-Origin", "*"); - res.header("Server", serverName); hasPadAccess(req, res, function() { @@ -321,8 +314,6 @@ async.waterfall([ return; } - res.header("Server", serverName); - hasPadAccess(req, res, function() { importHandler.doImport(req, res, req.params.pad); @@ -335,7 +326,6 @@ async.waterfall([ //This is for making an api call, collecting all post information and passing it to the apiHandler var apiCaller = function(req, res, fields) { - res.header("Server", serverName); res.header("Content-Type", "application/json; charset=utf-8"); apiLogger.info("REQUEST, " + req.params.func + ", " + JSON.stringify(fields)); @@ -396,7 +386,6 @@ async.waterfall([ //serve index.html under / app.get('/', function(req, res) { - res.header("Server", serverName); var filePath = path.normalize(__dirname + "/../static/index.html"); res.sendfile(filePath, { maxAge: exports.maxAge }); }); @@ -404,7 +393,6 @@ async.waterfall([ //serve robots.txt app.get('/robots.txt', function(req, res) { - res.header("Server", serverName); var filePath = path.normalize(__dirname + "/../static/robots.txt"); res.sendfile(filePath, { maxAge: exports.maxAge }); }); @@ -412,7 +400,6 @@ async.waterfall([ //serve favicon.ico app.get('/favicon.ico', function(req, res) { - res.header("Server", serverName); var filePath = path.normalize(__dirname + "/../static/custom/favicon.ico"); res.sendfile(filePath, { maxAge: exports.maxAge }, function(err) { diff --git a/node/utils/Minify.js b/node/utils/Minify.js index 3477cd01..ea7834dc 100644 --- a/node/utils/Minify.js +++ b/node/utils/Minify.js @@ -28,10 +28,15 @@ var jsp = require("uglify-js").parser; var pro = require("uglify-js").uglify; var path = require('path'); var Buffer = require('buffer').Buffer; -var gzip = require('gzip'); +var zlib = require('zlib'); +var RequireKernel = require('require-kernel'); var server = require('../server'); var os = require('os'); +var ROOT_DIR = path.normalize(__dirname + "/../" ); +var JS_DIR = ROOT_DIR + '../static/js/'; +var CSS_DIR = ROOT_DIR + '../static/css/'; +var CACHE_DIR = ROOT_DIR + '../var/'; var TAR_PATH = path.join(__dirname, 'tar.json'); var tar = JSON.parse(fs.readFileSync(TAR_PATH, 'utf8')); @@ -40,20 +45,31 @@ var tar = JSON.parse(fs.readFileSync(TAR_PATH, 'utf8')); * @param req the Express request * @param res the Express response */ -exports.minifyJS = function(req, res, jsFilename) +exports.minifyJS = function(req, res, next) { - res.header("Content-Type","text/javascript"); + var jsFilename = req.params['filename']; //choose the js files we need var jsFiles = undefined; if (Object.prototype.hasOwnProperty.call(tar, jsFilename)) { jsFiles = tar[jsFilename]; + _handle(req, res, jsFilename, jsFiles) } else { - throw new Error("there is no profile for creating " + name); + // Not in tar list, but try anyways, if it fails, pass to `next`. + jsFiles = [jsFilename]; + fs.stat(JS_DIR + jsFilename, function (error, stats) { + if (error || !stats.isFile()) { + next(); + } else { + _handle(req, res, jsFilename, jsFiles); + } + }); } +} - var rootPath = path.normalize(__dirname + "/../../" ); - +function _handle(req, res, jsFilename, jsFiles) { + res.header("Content-Type","text/javascript"); + //minifying is enabled if(settings.minify) { @@ -65,7 +81,7 @@ exports.minifyJS = function(req, res, jsFilename) //find out the highest modification date function(callback) { - var folders2check = [rootPath + "static/css", rootPath + "static/js"]; + var folders2check = [CSS_DIR, JS_DIR]; //go trough this two folders async.forEach(folders2check, function(path, callback) @@ -104,7 +120,7 @@ exports.minifyJS = function(req, res, jsFilename) function(callback) { //check the modification time of the minified js - fs.stat(rootPath + "var/minified_" + jsFilename, function(err, stats) + fs.stat(CACHE_DIR + "/minified_" + jsFilename, function(err, stats) { if(err && err.code != "ENOENT") { @@ -129,7 +145,7 @@ exports.minifyJS = function(req, res, jsFilename) { async.forEach(jsFiles, function (item, callback) { - fs.readFile(rootPath + "static/js/" + item, "utf-8", function(err, data) + fs.readFile(JS_DIR + item, "utf-8", function(err, data) { if(ERR(err, callback)) return; fileValues[item] = data; @@ -147,7 +163,7 @@ exports.minifyJS = function(req, res, jsFilename) return; } - var founds = fileValues["ace.js"].match(/\$\$INCLUDE_[a-zA-Z_]+\([a-zA-Z0-9.\/_"]+\)/gi); + var founds = fileValues["ace.js"].match(/\$\$INCLUDE_[a-zA-Z_]+\([a-zA-Z0-9.\/_"-]+\)/gi); //go trough all includes async.forEach(founds, function (item, callback) @@ -158,21 +174,31 @@ exports.minifyJS = function(req, res, jsFilename) var type = item.match(/INCLUDE_[A-Z]+/g)[0].substr("INCLUDE_".length); //read the included file - fs.readFile(filename, "utf-8", function(err, data) + var shortFilename = filename.replace(/^..\/static\/js\//, ''); + if (shortFilename == 'require-kernel.js') { + // the kernel isn’t actually on the file system. + handleEmbed(null, requireDefinition()); + } else { + fs.readFile(ROOT_DIR + filename, "utf-8", handleEmbed); + } + function handleEmbed(err, data) { if(ERR(err, callback)) return; if(type == "JS") { - embeds[filename] = compressJS([data]); + if (shortFilename == 'require-kernel.js') { + embeds[filename] = compressJS([data]); + } else { + embeds[filename] = compressJS([isolateJS(data, shortFilename)]); + } } else { embeds[filename] = compressCSS([data]); } - callback(); - }); + } }, function(err) { if(ERR(err, callback)) return; @@ -193,42 +219,28 @@ exports.minifyJS = function(req, res, jsFilename) //put all together and write it into a file function(callback) { - //put all javascript files in an array - var values = []; - for(var i in jsFiles) - { - values.push(fileValues[jsFiles[i]]); - } - //minify all javascript files to one + var values = []; + tarCode(jsFiles, fileValues, function (content) {values.push(content)}); var result = compressJS(values); async.parallel([ //write the results plain in a file function(callback) { - fs.writeFile(rootPath + "var/minified_" + jsFilename, result, "utf8", callback); + fs.writeFile(CACHE_DIR + "minified_" + jsFilename, result, "utf8", callback); }, //write the results compressed in a file function(callback) { - //spawn a gzip process if we're on a unix system - if(os.type().indexOf("Windows") == -1) - { - gzip(result, 9, function(err, compressedResult){ - //weird gzip bug that returns 0 instead of null if everything is ok - err = err === 0 ? null : err; + zlib.gzip(result, function(err, compressedResult){ + //weird gzip bug that returns 0 instead of null if everything is ok + err = err === 0 ? null : err; + + if(ERR(err, callback)) return; - if(ERR(err, callback)) return; - - fs.writeFile(rootPath + "var/minified_" + jsFilename + ".gz", compressedResult, callback); - }); - } - //skip this step on windows - else - { - callback(); - } + fs.writeFile(CACHE_DIR + "minified_" + jsFilename + ".gz", compressedResult, callback); + }); } ],callback); } @@ -245,12 +257,12 @@ exports.minifyJS = function(req, res, jsFilename) var pathStr; if(gzipSupport && os.type().indexOf("Windows") == -1) { - pathStr = path.normalize(rootPath + "var/minified_" + jsFilename + ".gz"); + pathStr = path.normalize(CACHE_DIR + "minified_" + jsFilename + ".gz"); res.header('Content-Encoding', 'gzip'); } else { - pathStr = path.normalize(rootPath + "var/minified_" + jsFilename ); + pathStr = path.normalize(CACHE_DIR + "minified_" + jsFilename ); } res.sendfile(pathStr, { maxAge: server.maxAge }); @@ -264,7 +276,7 @@ exports.minifyJS = function(req, res, jsFilename) //read all js files async.forEach(jsFiles, function (item, callback) { - fs.readFile(rootPath + "static/js/" + item, "utf-8", function(err, data) + fs.readFile(JS_DIR + item, "utf-8", function(err, data) { if(ERR(err, callback)) return; fileValues[item] = data; @@ -276,18 +288,44 @@ exports.minifyJS = function(req, res, jsFilename) { if(ERR(err)) return; - for(var i=0;i'); + buffer.push(Ace2Editor.EMBEDED[KERNEL_SOURCE]); + buffer.push('<\/script>'); + } else { + buffer.push(' + @@ -337,5 +338,13 @@ + + diff --git a/static/timeslider.html b/static/timeslider.html index c1310cc6..4e85047e 100644 --- a/static/timeslider.html +++ b/static/timeslider.html @@ -9,6 +9,7 @@ + @@ -19,6 +20,10 @@ //