mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-21 07:56:16 -04:00
created a folder structure in node
This commit is contained in:
parent
115f7d6b51
commit
8736b945c3
16 changed files with 0 additions and 0 deletions
92
node/utils/Abiword.js
Normal file
92
node/utils/Abiword.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* Controls the communication with the Abiword application
|
||||
*/
|
||||
|
||||
/*
|
||||
* 2011 Peter 'Pita' Martischka
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var util = require('util');
|
||||
var spawn = require('child_process').spawn;
|
||||
var async = require("async");
|
||||
var settings = require("./settings");
|
||||
|
||||
//Queue with the converts we have to do
|
||||
var queue = async.queue(doConvertTask, 1);
|
||||
|
||||
//spawn the abiword process
|
||||
var abiword = spawn(settings.abiword, ["--plugin", "AbiCommand"]);
|
||||
|
||||
//append error messages to the buffer
|
||||
abiword.stderr.on('data', function (data)
|
||||
{
|
||||
stdoutBuffer += data.toString();
|
||||
});
|
||||
|
||||
//throw exceptions if abiword is dieing
|
||||
abiword.on('exit', function (code)
|
||||
{
|
||||
throw "Abiword died with exit code " + code;
|
||||
});
|
||||
|
||||
//delegate the processing of stdout to a other function
|
||||
abiword.stdout.on('data',onAbiwordStdout);
|
||||
|
||||
var stdoutCallback = null;
|
||||
var stdoutBuffer = "";
|
||||
var firstPrompt = true;
|
||||
|
||||
function onAbiwordStdout(data)
|
||||
{
|
||||
//add data to buffer
|
||||
stdoutBuffer+=data.toString();
|
||||
|
||||
//we're searching for the prompt, cause this means everything we need is in the buffer
|
||||
if(stdoutBuffer.search("AbiWord:>") != -1)
|
||||
{
|
||||
//filter the feedback message
|
||||
var err = stdoutBuffer.search("OK") != -1 ? null : stdoutBuffer;
|
||||
|
||||
//reset the buffer
|
||||
stdoutBuffer = "";
|
||||
|
||||
//call the callback with the error message
|
||||
//skip the first prompt
|
||||
if(stdoutCallback != null && !firstPrompt)
|
||||
{
|
||||
stdoutCallback(err);
|
||||
stdoutCallback = null;
|
||||
}
|
||||
|
||||
firstPrompt = false;
|
||||
}
|
||||
}
|
||||
|
||||
function doConvertTask(task, callback)
|
||||
{
|
||||
abiword.stdin.write("convert " + task.srcFile + " " + task.destFile + " " + task.type + "\n");
|
||||
|
||||
//create a callback that calls the task callback and the caller callback
|
||||
stdoutCallback = function (err)
|
||||
{
|
||||
callback();
|
||||
task.callback(err);
|
||||
};
|
||||
}
|
||||
|
||||
exports.convertFile = function(srcFile, destFile, type, callback)
|
||||
{
|
||||
queue.push({"srcFile": srcFile, "destFile": destFile, "type": type, "callback": callback});
|
||||
};
|
90
node/utils/AttributePoolFactory.js
Normal file
90
node/utils/AttributePoolFactory.js
Normal file
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* This code represents the Attribute Pool Object of the original Etherpad.
|
||||
* 90% of the code is still like in the original Etherpad
|
||||
* Look at https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js
|
||||
* You can find a explanation what a attribute pool is here:
|
||||
* https://github.com/Pita/etherpad-lite/blob/master/doc/easysync/easysync-notes.txt
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
exports.createAttributePool = function () {
|
||||
var p = {};
|
||||
p.numToAttrib = {}; // e.g. {0: ['foo','bar']}
|
||||
p.attribToNum = {}; // e.g. {'foo,bar': 0}
|
||||
p.nextNum = 0;
|
||||
|
||||
p.putAttrib = function (attrib, dontAddIfAbsent) {
|
||||
var str = String(attrib);
|
||||
if (str in p.attribToNum) {
|
||||
return p.attribToNum[str];
|
||||
}
|
||||
if (dontAddIfAbsent) {
|
||||
return -1;
|
||||
}
|
||||
var num = p.nextNum++;
|
||||
p.attribToNum[str] = num;
|
||||
p.numToAttrib[num] = [String(attrib[0] || ''), String(attrib[1] || '')];
|
||||
return num;
|
||||
};
|
||||
|
||||
p.getAttrib = function (num) {
|
||||
var pair = p.numToAttrib[num];
|
||||
if (!pair) {
|
||||
return pair;
|
||||
}
|
||||
return [pair[0], pair[1]]; // return a mutable copy
|
||||
};
|
||||
|
||||
p.getAttribKey = function (num) {
|
||||
var pair = p.numToAttrib[num];
|
||||
if (!pair) return '';
|
||||
return pair[0];
|
||||
};
|
||||
|
||||
p.getAttribValue = function (num) {
|
||||
var pair = p.numToAttrib[num];
|
||||
if (!pair) return '';
|
||||
return pair[1];
|
||||
};
|
||||
|
||||
p.eachAttrib = function (func) {
|
||||
for (var n in p.numToAttrib) {
|
||||
var pair = p.numToAttrib[n];
|
||||
func(pair[0], pair[1]);
|
||||
}
|
||||
};
|
||||
|
||||
p.toJsonable = function () {
|
||||
return {
|
||||
numToAttrib: p.numToAttrib,
|
||||
nextNum: p.nextNum
|
||||
};
|
||||
};
|
||||
|
||||
p.fromJsonable = function (obj) {
|
||||
p.numToAttrib = obj.numToAttrib;
|
||||
p.nextNum = obj.nextNum;
|
||||
p.attribToNum = {};
|
||||
for (var n in p.numToAttrib) {
|
||||
p.attribToNum[String(p.numToAttrib[n])] = Number(n);
|
||||
}
|
||||
return p;
|
||||
};
|
||||
|
||||
return p;
|
||||
}
|
1962
node/utils/Changeset.js
Normal file
1962
node/utils/Changeset.js
Normal file
File diff suppressed because it is too large
Load diff
422
node/utils/ExportHtml.js
Normal file
422
node/utils/ExportHtml.js
Normal file
|
@ -0,0 +1,422 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
var async = require("async");
|
||||
var Changeset = require("../Changeset");
|
||||
var padManager = require("../PadManager");
|
||||
|
||||
|
||||
function getPadPlainText(pad, revNum) {
|
||||
var atext = ((revNum !== undefined) ? pad.getInternalRevisionAText(revNum) :
|
||||
pad.atext());
|
||||
var textLines = atext.text.slice(0,-1).split('\n');
|
||||
var attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text);
|
||||
var apool = pad.pool();
|
||||
|
||||
var pieces = [];
|
||||
for(var i=0;i<textLines.length;i++) {
|
||||
var line = _analyzeLine(textLines[i], attribLines[i], apool);
|
||||
if (line.listLevel) {
|
||||
var numSpaces = line.listLevel*2-1;
|
||||
var bullet = '*';
|
||||
pieces.push(new Array(numSpaces+1).join(' '), bullet, ' ', line.text, '\n');
|
||||
}
|
||||
else {
|
||||
pieces.push(line.text, '\n');
|
||||
}
|
||||
}
|
||||
|
||||
return pieces.join('');
|
||||
}
|
||||
|
||||
function getPadHTML(pad, revNum, callback) {
|
||||
var atext = pad.atext;
|
||||
var html;
|
||||
async.waterfall([
|
||||
// fetch revision atext
|
||||
function (callback) {
|
||||
if (revNum != undefined) {
|
||||
pad.getInternalRevisionAText(revNum, function (err, revisionAtext) {
|
||||
atext = revisionAtext;
|
||||
callback(err);
|
||||
});
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
},
|
||||
|
||||
// convert atext to html
|
||||
function (callback) {
|
||||
html = getHTMLFromAtext(pad, atext);
|
||||
callback(null);
|
||||
}
|
||||
],
|
||||
// run final callback
|
||||
function (err) {
|
||||
callback(err, html);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function getHTMLFromAtext(pad, atext) {
|
||||
var apool = pad.apool();
|
||||
var textLines = atext.text.slice(0,-1).split('\n');
|
||||
var attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text);
|
||||
|
||||
var tags = ['h1', 'h2', 'strong','em','u','s'];
|
||||
var props = ['heading1', 'heading2', 'bold','italic','underline','strikethrough'];
|
||||
var anumMap = {};
|
||||
|
||||
props.forEach(function(propName, i) {
|
||||
var propTrueNum = apool.putAttrib([propName,true], true);
|
||||
if (propTrueNum >= 0) {
|
||||
anumMap[propTrueNum] = i;
|
||||
}
|
||||
});
|
||||
|
||||
function getLineHTML(text, attribs) {
|
||||
var propVals = [false, false, false];
|
||||
var ENTER = 1;
|
||||
var STAY = 2;
|
||||
var LEAVE = 0;
|
||||
|
||||
// Use order of tags (b/i/u) as order of nesting, for simplicity
|
||||
// and decent nesting. For example,
|
||||
// <b>Just bold<b> <b><i>Bold and italics</i></b> <i>Just italics</i>
|
||||
// becomes
|
||||
// <b>Just bold <i>Bold and italics</i></b> <i>Just italics</i>
|
||||
|
||||
var taker = Changeset.stringIterator(text);
|
||||
var assem = Changeset.stringAssembler();
|
||||
|
||||
function emitOpenTag(i) {
|
||||
assem.append('<');
|
||||
assem.append(tags[i]);
|
||||
assem.append('>');
|
||||
}
|
||||
function emitCloseTag(i) {
|
||||
assem.append('</');
|
||||
assem.append(tags[i]);
|
||||
assem.append('>');
|
||||
}
|
||||
|
||||
var urls = _findURLs(text);
|
||||
|
||||
var idx = 0;
|
||||
function processNextChars(numChars) {
|
||||
if (numChars <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var iter = Changeset.opIterator(Changeset.subattribution(attribs,
|
||||
idx, idx+numChars));
|
||||
idx += numChars;
|
||||
|
||||
while (iter.hasNext()) {
|
||||
var o = iter.next();
|
||||
var propChanged = false;
|
||||
Changeset.eachAttribNumber(o.attribs, function(a) {
|
||||
if (a in anumMap) {
|
||||
var i = anumMap[a]; // i = 0 => bold, etc.
|
||||
if (! propVals[i]) {
|
||||
propVals[i] = ENTER;
|
||||
propChanged = true;
|
||||
}
|
||||
else {
|
||||
propVals[i] = STAY;
|
||||
}
|
||||
}
|
||||
});
|
||||
for(var i=0;i<propVals.length;i++) {
|
||||
if (propVals[i] === true) {
|
||||
propVals[i] = LEAVE;
|
||||
propChanged = true;
|
||||
}
|
||||
else if (propVals[i] === STAY) {
|
||||
propVals[i] = true; // set it back
|
||||
}
|
||||
}
|
||||
// now each member of propVal is in {false,LEAVE,ENTER,true}
|
||||
// according to what happens at start of span
|
||||
|
||||
if (propChanged) {
|
||||
// leaving bold (e.g.) also leaves italics, etc.
|
||||
var left = false;
|
||||
for(var i=0;i<propVals.length;i++) {
|
||||
var v = propVals[i];
|
||||
if (! left) {
|
||||
if (v === LEAVE) {
|
||||
left = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (v === true) {
|
||||
propVals[i] = STAY; // tag will be closed and re-opened
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var i=propVals.length-1; i>=0; i--) {
|
||||
if (propVals[i] === LEAVE) {
|
||||
emitCloseTag(i);
|
||||
propVals[i] = false;
|
||||
}
|
||||
else if (propVals[i] === STAY) {
|
||||
emitCloseTag(i);
|
||||
}
|
||||
}
|
||||
for(var i=0; i<propVals.length; i++) {
|
||||
if (propVals[i] === ENTER || propVals[i] === STAY) {
|
||||
emitOpenTag(i);
|
||||
propVals[i] = true;
|
||||
}
|
||||
}
|
||||
// propVals is now all {true,false} again
|
||||
} // end if (propChanged)
|
||||
|
||||
var chars = o.chars;
|
||||
if (o.lines) {
|
||||
chars--; // exclude newline at end of line, if present
|
||||
}
|
||||
var s = taker.take(chars);
|
||||
|
||||
assem.append(_escapeHTML(s));
|
||||
} // end iteration over spans in line
|
||||
|
||||
for(var i=propVals.length-1; i>=0; i--) {
|
||||
if (propVals[i]) {
|
||||
emitCloseTag(i);
|
||||
propVals[i] = false;
|
||||
}
|
||||
}
|
||||
} // end processNextChars
|
||||
|
||||
if (urls) {
|
||||
urls.forEach(function(urlData) {
|
||||
var startIndex = urlData[0];
|
||||
var url = urlData[1];
|
||||
var urlLength = url.length;
|
||||
processNextChars(startIndex - idx);
|
||||
assem.append('<a href="'+url.replace(/\"/g, '"')+'">');
|
||||
processNextChars(urlLength);
|
||||
assem.append('</a>');
|
||||
});
|
||||
}
|
||||
processNextChars(text.length - idx);
|
||||
|
||||
return _processSpaces(assem.toString());
|
||||
} // end getLineHTML
|
||||
|
||||
var pieces = [];
|
||||
|
||||
// Need to deal with constraints imposed on HTML lists; can
|
||||
// only gain one level of nesting at once, can't change type
|
||||
// mid-list, etc.
|
||||
// People might use weird indenting, e.g. skip a level,
|
||||
// so we want to do something reasonable there. We also
|
||||
// want to deal gracefully with blank lines.
|
||||
var lists = []; // e.g. [[1,'bullet'], [3,'bullet'], ...]
|
||||
for(var i=0;i<textLines.length;i++) {
|
||||
var line = _analyzeLine(textLines[i], attribLines[i], apool);
|
||||
var lineContent = getLineHTML(line.text, line.aline);
|
||||
|
||||
if (line.listLevel || lists.length > 0) {
|
||||
// do list stuff
|
||||
var whichList = -1; // index into lists or -1
|
||||
if (line.listLevel) {
|
||||
whichList = lists.length;
|
||||
for(var j=lists.length-1;j>=0;j--) {
|
||||
if (line.listLevel <= lists[j][0]) {
|
||||
whichList = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (whichList >= lists.length) {
|
||||
lists.push([line.listLevel, line.listTypeName]);
|
||||
pieces.push('<ul><li>', lineContent || '<br>');
|
||||
}
|
||||
else if (whichList == -1) {
|
||||
if (line.text) {
|
||||
// non-blank line, end all lists
|
||||
pieces.push(new Array(lists.length+1).join('</li></ul\n>'));
|
||||
lists.length = 0;
|
||||
pieces.push(lineContent, '<br>');
|
||||
}
|
||||
else {
|
||||
pieces.push('<br><br>');
|
||||
}
|
||||
}
|
||||
else {
|
||||
while (whichList < lists.length-1) {
|
||||
pieces.push('</li></ul>');
|
||||
lists.length--;
|
||||
}
|
||||
pieces.push('</li><li>', lineContent || '<br>');
|
||||
}
|
||||
}
|
||||
else {
|
||||
pieces.push(lineContent, '<br>');
|
||||
}
|
||||
}
|
||||
pieces.push(new Array(lists.length+1).join('</li></ul>'));
|
||||
|
||||
return pieces.join('');
|
||||
}
|
||||
|
||||
function _analyzeLine(text, aline, apool) {
|
||||
var line = {};
|
||||
|
||||
// identify list
|
||||
var lineMarker = 0;
|
||||
line.listLevel = 0;
|
||||
if (aline) {
|
||||
var opIter = Changeset.opIterator(aline);
|
||||
if (opIter.hasNext()) {
|
||||
var listType = Changeset.opAttributeValue(opIter.next(), 'list', apool);
|
||||
if (listType) {
|
||||
lineMarker = 1;
|
||||
listType = /([a-z]+)([12345678])/.exec(listType);
|
||||
if (listType) {
|
||||
line.listTypeName = listType[1];
|
||||
line.listLevel = Number(listType[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lineMarker) {
|
||||
line.text = text.substring(1);
|
||||
line.aline = Changeset.subattribution(aline, 1);
|
||||
}
|
||||
else {
|
||||
line.text = text;
|
||||
line.aline = aline;
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
exports.getPadHTMLDocument = function(padId, revNum, noDocType, callback) {
|
||||
padManager.getPad(padId, function(err, pad)
|
||||
{
|
||||
if(err)
|
||||
{
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var head = (noDocType?'':'<!doctype html>\n')+
|
||||
'<html lang="en">\n'+
|
||||
(noDocType?'':
|
||||
'<head>\n'+
|
||||
'<meta charset="utf-8">\n'+
|
||||
'<style> * { font-family: arial, sans-serif;\n'+
|
||||
'font-size: 13px;\n'+
|
||||
'line-height: 17px; }</style>\n' +
|
||||
'</head>\n')+
|
||||
'<body>';
|
||||
|
||||
var foot = '</body>\n</html>\n';
|
||||
|
||||
getPadHTML(pad, revNum, function (err, html) {
|
||||
callback(err, head + html + foot);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function _escapeHTML(s) {
|
||||
var re = /[&<>]/g;
|
||||
if (! re.MAP) {
|
||||
// persisted across function calls!
|
||||
re.MAP = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
};
|
||||
}
|
||||
return s.replace(re, function(c) { return re.MAP[c]; });
|
||||
}
|
||||
|
||||
// copied from ACE
|
||||
function _processSpaces(s) {
|
||||
var doesWrap = true;
|
||||
if (s.indexOf("<") < 0 && ! doesWrap) {
|
||||
// short-cut
|
||||
return s.replace(/ /g, ' ');
|
||||
}
|
||||
var parts = [];
|
||||
s.replace(/<[^>]*>?| |[^ <]+/g, function(m) { parts.push(m); });
|
||||
if (doesWrap) {
|
||||
var endOfLine = true;
|
||||
var beforeSpace = false;
|
||||
// last space in a run is normal, others are nbsp,
|
||||
// end of line is nbsp
|
||||
for(var i=parts.length-1;i>=0;i--) {
|
||||
var p = parts[i];
|
||||
if (p == " ") {
|
||||
if (endOfLine || beforeSpace)
|
||||
parts[i] = ' ';
|
||||
endOfLine = false;
|
||||
beforeSpace = true;
|
||||
}
|
||||
else if (p.charAt(0) != "<") {
|
||||
endOfLine = false;
|
||||
beforeSpace = false;
|
||||
}
|
||||
}
|
||||
// beginning of line is nbsp
|
||||
for(var i=0;i<parts.length;i++) {
|
||||
var p = parts[i];
|
||||
if (p == " ") {
|
||||
parts[i] = ' ';
|
||||
break;
|
||||
}
|
||||
else if (p.charAt(0) != "<") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(var i=0;i<parts.length;i++) {
|
||||
var p = parts[i];
|
||||
if (p == " ") {
|
||||
parts[i] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
|
||||
// copied from ACE
|
||||
var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
|
||||
var _REGEX_SPACE = /\s/;
|
||||
var _REGEX_URLCHAR = new RegExp('('+/[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source+'|'+_REGEX_WORDCHAR.source+')');
|
||||
var _REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source+_REGEX_URLCHAR.source+'*(?![:.,;])'+_REGEX_URLCHAR.source, 'g');
|
||||
|
||||
// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...]
|
||||
function _findURLs(text) {
|
||||
_REGEX_URL.lastIndex = 0;
|
||||
var urls = null;
|
||||
var execResult;
|
||||
while ((execResult = _REGEX_URL.exec(text))) {
|
||||
urls = (urls || []);
|
||||
var startIndex = execResult.index;
|
||||
var url = execResult[0];
|
||||
urls.push([startIndex, url]);
|
||||
}
|
||||
|
||||
return urls;
|
||||
}
|
298
node/utils/Minify.js
Normal file
298
node/utils/Minify.js
Normal file
|
@ -0,0 +1,298 @@
|
|||
/**
|
||||
* This Module manages all /minified/* requests. It controls the
|
||||
* minification && compression of Javascript and CSS.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 2011 Peter 'Pita' Martischka
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var settings = require('./settings');
|
||||
var async = require('async');
|
||||
var fs = require('fs');
|
||||
var cleanCSS = require('clean-css');
|
||||
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 server = require('./server');
|
||||
|
||||
var padJS = ["jquery.min.js", "pad_utils.js", "plugins.js", "undo-xpopup.js", "json2.js", "pad_cookie.js", "pad_editor.js", "pad_editbar.js", "pad_docbar.js", "pad_modals.js", "ace.js", "collab_client.js", "pad_userlist.js", "pad_impexp.js", "pad_savedrevs.js", "pad_connectionstatus.js", "pad2.js", "jquery-ui.js", "chat.js"];
|
||||
|
||||
var timesliderJS = ["jquery.min.js", "plugins.js", "undo-xpopup.js", "json2.js", "colorutils.js", "draggable.js", "pad_utils.js", "pad_cookie.js", "pad_editor.js", "pad_editbar.js", "pad_docbar.js", "pad_modals.js", "easysync2_client.js", "domline_client.js", "linestylefilter_client.js", "cssmanager_client.js", "broadcast.js", "broadcast_slider.js", "broadcast_revisions.js"];
|
||||
|
||||
/**
|
||||
* creates the minifed javascript for the given minified name
|
||||
* @param req the Express request
|
||||
* @param res the Express response
|
||||
*/
|
||||
exports.minifyJS = function(req, res, jsFilename)
|
||||
{
|
||||
res.header("Content-Type","text/javascript");
|
||||
|
||||
//choose the js files we need
|
||||
if(jsFilename == "pad.js")
|
||||
{
|
||||
jsFiles = padJS;
|
||||
}
|
||||
else if(jsFilename == "timeslider.js")
|
||||
{
|
||||
jsFiles = timesliderJS;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Error("there is no profile for creating " + name);
|
||||
}
|
||||
|
||||
//minifying is enabled
|
||||
if(settings.minify)
|
||||
{
|
||||
var fileValues = {};
|
||||
var embeds = {};
|
||||
var latestModification = 0;
|
||||
|
||||
async.series([
|
||||
//find out the highest modification date
|
||||
function(callback)
|
||||
{
|
||||
var folders2check = ["../static/css","../static/js"];
|
||||
|
||||
//go trough this two folders
|
||||
async.forEach(folders2check, function(path, callback)
|
||||
{
|
||||
//read the files in the folder
|
||||
fs.readdir(path, function(err, files)
|
||||
{
|
||||
if(err) { callback(err); return; }
|
||||
|
||||
//we wanna check the directory itself for changes too
|
||||
files.push(".");
|
||||
|
||||
//go trough all files in this folder
|
||||
async.forEach(files, function(filename, callback)
|
||||
{
|
||||
//get the stat data of this file
|
||||
fs.stat(path + "/" + filename, function(err, stats)
|
||||
{
|
||||
if(err) { callback(err); return; }
|
||||
|
||||
//get the modification time
|
||||
var modificationTime = stats.mtime.getTime();
|
||||
|
||||
//compare the modification time to the highest found
|
||||
if(modificationTime > latestModification)
|
||||
{
|
||||
latestModification = modificationTime;
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
}, callback);
|
||||
});
|
||||
}, callback);
|
||||
},
|
||||
function(callback)
|
||||
{
|
||||
//check the modification time of the minified js
|
||||
fs.stat("../var/minified_" + jsFilename, function(err, stats)
|
||||
{
|
||||
if(err && err.code != "ENOENT") callback(err);
|
||||
|
||||
//there is no minfied file or there new changes since this file was generated, so continue generating this file
|
||||
if((err && err.code == "ENOENT") || stats.mtime.getTime() < latestModification)
|
||||
{
|
||||
callback();
|
||||
}
|
||||
//the minified file is still up to date, stop minifying
|
||||
else
|
||||
{
|
||||
callback("stop");
|
||||
}
|
||||
});
|
||||
},
|
||||
//load all js files
|
||||
function (callback)
|
||||
{
|
||||
async.forEach(jsFiles, function (item, callback)
|
||||
{
|
||||
fs.readFile("../static/js/" + item, "utf-8", function(err, data)
|
||||
{
|
||||
fileValues[item] = data;
|
||||
callback(err);
|
||||
});
|
||||
}, callback);
|
||||
},
|
||||
//find all includes in ace.js and embed them
|
||||
function(callback)
|
||||
{
|
||||
//if this is not the creation of pad.js, skip this part
|
||||
if(jsFilename != "pad.js")
|
||||
{
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var filename = item.match(/"[^"]*"/g)[0].substr(1);
|
||||
filename = filename.substr(0,filename.length-1);
|
||||
|
||||
var type = item.match(/INCLUDE_[A-Z]+/g)[0].substr("INCLUDE_".length);
|
||||
|
||||
var quote = item.search("_Q") != -1;
|
||||
|
||||
//read the included file
|
||||
fs.readFile(filename, "utf-8", function(err, data)
|
||||
{
|
||||
//compress the file
|
||||
if(type == "JS")
|
||||
{
|
||||
embeds[item] = "<script>\n" + compressJS([data])+ "\n\\x3c/script>";
|
||||
}
|
||||
else
|
||||
{
|
||||
embeds[item] = "<style>" + compressCSS([data])+ "</style>";
|
||||
}
|
||||
|
||||
//do the first escape
|
||||
embeds[item] = JSON.stringify(embeds[item]).replace(/'/g, "\\'").replace(/\\"/g, "\"");
|
||||
embeds[item] = embeds[item].substr(1);
|
||||
embeds[item] = embeds[item].substr(0, embeds[item].length-1);
|
||||
|
||||
//add quotes, if wished
|
||||
if(quote)
|
||||
{
|
||||
embeds[item] = "'" + embeds[item] + "'";
|
||||
}
|
||||
|
||||
//do the second escape
|
||||
embeds[item] = JSON.stringify(embeds[item]).replace(/'/g, "\\'").replace(/\"/g, "\"");
|
||||
embeds[item] = embeds[item].substr(1);
|
||||
embeds[item] = embeds[item].substr(0, embeds[item].length-1);
|
||||
embeds[item] = "'" + embeds[item] + "'";
|
||||
|
||||
callback(err);
|
||||
});
|
||||
}, function(err)
|
||||
{
|
||||
//replace the include command with the include
|
||||
for(var i in embeds)
|
||||
{
|
||||
fileValues["ace.js"]=fileValues["ace.js"].replace(i, embeds[i]);
|
||||
}
|
||||
|
||||
callback(err);
|
||||
});
|
||||
},
|
||||
//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 result = compressJS(values);
|
||||
|
||||
async.parallel([
|
||||
//write the results plain in a file
|
||||
function(callback)
|
||||
{
|
||||
fs.writeFile("../var/minified_" + jsFilename, result, "utf8", callback);
|
||||
},
|
||||
//write the results compressed in a file
|
||||
function(callback)
|
||||
{
|
||||
gzip(result, 9, function(err, compressedResult){
|
||||
if(err) {callback(err); return}
|
||||
|
||||
fs.writeFile("../var/minified_" + jsFilename + ".gz", compressedResult, callback);
|
||||
});
|
||||
}
|
||||
],callback);
|
||||
}
|
||||
], function(err)
|
||||
{
|
||||
if(err && err != "stop") throw err;
|
||||
|
||||
//check if gzip is supported by this browser
|
||||
var gzipSupport = req.header('Accept-Encoding', '').indexOf('gzip') != -1;
|
||||
|
||||
var pathStr;
|
||||
if(gzipSupport)
|
||||
{
|
||||
pathStr = path.normalize(__dirname + "/../var/minified_" + jsFilename + ".gz");
|
||||
res.header('Content-Encoding', 'gzip');
|
||||
}
|
||||
else
|
||||
{
|
||||
pathStr = path.normalize(__dirname + "/../var/minified_" + jsFilename );
|
||||
}
|
||||
|
||||
res.sendfile(pathStr, { maxAge: server.maxAge });
|
||||
})
|
||||
}
|
||||
//minifying is disabled, so put the files together in one file
|
||||
else
|
||||
{
|
||||
var fileValues = {};
|
||||
|
||||
//read all js files
|
||||
async.forEach(jsFiles, function (item, callback)
|
||||
{
|
||||
fs.readFile("../static/js/" + item, "utf-8", function(err, data)
|
||||
{
|
||||
fileValues[item] = data;
|
||||
callback(err);
|
||||
});
|
||||
},
|
||||
//send all files together
|
||||
function(err)
|
||||
{
|
||||
if(err) throw err;
|
||||
|
||||
for(var i=0;i<jsFiles.length;i++)
|
||||
{
|
||||
var fileName = jsFiles[i];
|
||||
res.write("\n\n\n/*** File: static/js/" + fileName + " ***/\n\n\n");
|
||||
res.write(fileValues[fileName]);
|
||||
}
|
||||
|
||||
res.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function compressJS(values)
|
||||
{
|
||||
var complete = values.join("\n");
|
||||
var ast = jsp.parse(complete); // parse code and get the initial AST
|
||||
ast = pro.ast_mangle(ast); // get a new AST with mangled names
|
||||
ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
|
||||
return pro.gen_code(ast); // compressed code here
|
||||
}
|
||||
|
||||
function compressCSS(values)
|
||||
{
|
||||
var complete = values.join("\n");
|
||||
return cleanCSS.process(complete);
|
||||
}
|
93
node/utils/Settings.js
Normal file
93
node/utils/Settings.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* The Settings Modul reads the settings out of settings.json and provides
|
||||
* this information to the other modules
|
||||
*/
|
||||
|
||||
/*
|
||||
* 2011 Peter 'Pita' Martischka
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var fs = require("fs");
|
||||
|
||||
/**
|
||||
* The Port ep-lite should listen to
|
||||
*/
|
||||
exports.port = 9001;
|
||||
/*
|
||||
* The Type of the database
|
||||
*/
|
||||
exports.dbType = "sqlite";
|
||||
/**
|
||||
* This setting is passed with dbType to ueberDB to set up the database
|
||||
*/
|
||||
exports.dbSettings = { "filename" : "../var/sqlite.db" };
|
||||
/**
|
||||
* A flag that shows if http requests should be loged to stdout
|
||||
*/
|
||||
exports.logHTTP = true;
|
||||
/**
|
||||
* The default Text of a new pad
|
||||
*/
|
||||
exports.defaultPadText = "Welcome to Etherpad Lite!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nEtherpad Lite on Github: http:\/\/j.mp/ep-lite\n";
|
||||
/**
|
||||
* A flag that shows if minification is enabled or not
|
||||
*/
|
||||
exports.minify = true;
|
||||
|
||||
/**
|
||||
* The path of the abiword executable
|
||||
*/
|
||||
exports.abiword = null;
|
||||
|
||||
//read the settings sync
|
||||
var settingsStr = fs.readFileSync("../settings.json").toString();
|
||||
|
||||
//remove all comments
|
||||
settingsStr = settingsStr.replace(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/gm,"").replace(/#.*/g,"").replace(/\/\/.*/g,"");
|
||||
|
||||
//try to parse the settings
|
||||
var settings;
|
||||
try
|
||||
{
|
||||
settings = JSON.parse(settingsStr);
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
console.error("There is a syntax error in your settings.json file");
|
||||
console.error(e.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
//loop trough the settings
|
||||
for(var i in settings)
|
||||
{
|
||||
//test if the setting start with a low character
|
||||
if(i.charAt(0).search("[a-z]") !== 0)
|
||||
{
|
||||
console.error("WARNING: Settings should start with a low character: '" + i + "'");
|
||||
}
|
||||
|
||||
//we know this setting, so we overwrite it
|
||||
if(exports[i] !== undefined)
|
||||
{
|
||||
exports[i] = settings[i];
|
||||
}
|
||||
//this setting is unkown, output a warning and throw it away
|
||||
else
|
||||
{
|
||||
console.error("WARNING: Unkown Setting: '" + i + "'");
|
||||
console.error("If this isn't a mistake, add the default settings for this value to node/settings.js");
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue