Merge branch 'develop' of git://github.com/Pita/etherpad-lite into body-block

Resolved conflicts:
	src/templates/pad.html
This commit is contained in:
Edy 2012-04-30 15:17:23 +02:00
commit cf54c23228
26 changed files with 730 additions and 483 deletions

View File

@ -40,22 +40,35 @@
"minify" : true,
/* How long may clients use served javascript code (in seconds)? Without versioning this
is may cause problems during deployment. Set to 0 to disable caching */
"maxAge" : 21600, // 6 hours
may cause problems during deployment. Set to 0 to disable caching */
"maxAge" : 21600, // 60 * 60 * 6 = 6 hours
/* This is the path to the Abiword executable. Setting it to null, disables abiword.
Abiword is needed to enable the import/export of pads*/
"abiword" : null,
/* This setting is used if you need http basic auth */
// "httpAuth" : "user:pass",
/* This setting is used if you require authentication of all users.
Note: /admin always requires authentication. */
"requireAuthentication": false,
/* This setting is used for http basic auth for admin pages. If not set, the admin page won't be accessible from web*/
// "adminHttpAuth" : "user:pass",
/* Require authorization by a module, or a user with is_admin set, see below. */
"requireAuthorization": false,
/* Users for basic authentication. is_admin = true gives access to /admin.
If you do not uncomment this, /admin will not be available! */
/*
"users": {
"admin": {
"password": "changeme1",
"is_admin": true
},
"user": {
"password": "changeme1",
"is_admin": false
}
},
*/
/* The log level we are using, can be: DEBUG, INFO, WARN, ERROR */
"loglevel": "INFO",
/* cache 6 hours = 1000*60*60*6 */
"maxAge": 21600000
"loglevel": "INFO"
}

View File

@ -23,6 +23,7 @@ var ejs = require("ejs");
var fs = require("fs");
var path = require("path");
var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks.js");
var resolve = require("resolve");
exports.info = {
buf_stack: [],
@ -91,13 +92,28 @@ exports.inherit = function (name, args) {
exports.info.file_stack[exports.info.file_stack.length-1].inherit.push({name:name, args:args});
}
exports.require = function (name, args) {
exports.require = function (name, args, mod) {
if (args == undefined) args = {};
if ((name.indexOf("./") == 0 || name.indexOf("../") == 0) && exports.info.file_stack.length) {
name = path.join(path.dirname(exports.info.file_stack[exports.info.file_stack.length-1].path), name);
var basedir = __dirname;
var paths = [];
if (exports.info.file_stack.length) {
basedir = path.dirname(exports.info.file_stack[exports.info.file_stack.length-1].path);
}
var ejspath = require.resolve(name)
if (mod) {
basedir = path.dirname(mod.filename);
paths = mod.paths;
}
var ejspath = resolve.sync(
name,
{
paths : paths,
basedir : basedir,
extensions : [ '.html', '.ejs' ],
}
)
args.e = exports;
args.require = require;

View File

@ -155,8 +155,6 @@ function createTimesliderClientVars (padId, callback)
var clientVars = {
viewId: padId,
colorPalette: ["#ffc7c7", "#fff1c7", "#e3ffc7", "#c7ffd5", "#c7ffff", "#c7d5ff", "#e3c7ff", "#ffc7f1", "#ff8f8f", "#ffe38f", "#c7ff8f", "#8fffab", "#8fffff", "#8fabff", "#c78fff", "#ff8fe3", "#d97979", "#d9c179", "#a9d979", "#79d991", "#79d9d9", "#7991d9", "#a979d9", "#d979c1", "#d9a9a9", "#d9cda9", "#c1d9a9", "#a9d9b5", "#a9d9d9", "#a9b5d9", "#c1a9d9", "#d9a9cd"],
sliderEnabled : true,
supportsSlider: true,
savedRevisions: [],
padIdForUrl: padId,
fullWidth: false,

View File

@ -21,13 +21,15 @@ exports.expressCreateServer = function (hook_name, args, cb) {
exports.socketio = function (hook_name, args, cb) {
var io = args.io.of("/pluginfw/installer");
io.on('connection', function (socket) {
if (!socket.handshake.session.user || !socket.handshake.session.user.is_admin) return;
socket.on("load", function (query) {
socket.emit("installed-results", {results: plugins.plugins});
});
socket.on("search", function (query) {
socket.emit("progress", {progress:0, message:'Fetching results...'});
installer.search(query, function (progress) {
installer.search(query, true, function (progress) {
if (progress.results)
socket.emit("search-result", progress);
socket.emit("progress", progress);

View File

@ -7,11 +7,27 @@ var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks");
var padMessageHandler = require("../../handler/PadMessageHandler");
var timesliderMessageHandler = require("../../handler/TimesliderMessageHandler");
var connect = require('connect');
exports.expressCreateServer = function (hook_name, args, cb) {
//init socket.io and redirect all requests to the MessageHandler
var io = socketio.listen(args.app);
/* Require an express session cookie to be present, and load the
* session. See http://www.danielbaulig.de/socket-ioexpress for more
* info */
io.set('authorization', function (data, accept) {
if (!data.headers.cookie) return accept('No session cookie transmitted.', false);
data.cookie = connect.utils.parseCookie(data.headers.cookie);
data.sessionID = data.cookie.express_sid;
args.app.sessionStore.get(data.sessionID, function (err, session) {
if (err || !session) return accept('Bad session / session has expired', false);
data.session = new connect.middleware.session.Session(data, session);
accept(null, true);
});
});
//this is only a workaround to ensure it works with all browers behind a proxy
//we should remove this when the new socket.io version is more stable
io.set('transports', ['xhr-polling']);

View File

@ -2,50 +2,108 @@ var express = require('express');
var log4js = require('log4js');
var httpLogger = log4js.getLogger("http");
var settings = require('../../utils/Settings');
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
//checks for basic http auth
exports.basicAuth = function (req, res, next) {
// When handling HTTP-Auth, an undefined password will lead to no authorization at all
var pass = settings.httpAuth || '';
if (req.path.indexOf('/admin') == 0) {
var pass = settings.adminHttpAuth;
}
// Just pass if password is an empty string
if (pass === '') {
return next();
}
// If a password has been set and auth headers are present...
if (pass && req.headers.authorization && req.headers.authorization.search('Basic ') === 0) {
// ...check login and password
if (new Buffer(req.headers.authorization.split(' ')[1], 'base64').toString() === pass) {
return next();
var hookResultMangle = function (cb) {
return function (err, data) {
return cb(!err && data.length && data[0]);
}
}
// Otherwise return Auth required Headers, delayed for 1 second, if auth failed.
res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
if (req.headers.authorization) {
setTimeout(function () {
res.send('Authentication required', 401);
}, 1000);
} else {
res.send('Authentication required', 401);
var authorize = function (cb) {
// Do not require auth for static paths...this could be a bit brittle
if (req.path.match(/^\/(static|javascripts|pluginfw)/)) return cb(true);
if (req.path.indexOf('/admin') != 0) {
if (!settings.requireAuthentication) return cb(true);
if (!settings.requireAuthorization && req.session && req.session.user) return cb(true);
}
if (req.session && req.session.user && req.session.user.is_admin) return cb(true);
hooks.aCallFirst("authorize", {req: req, res:res, next:next, resource: req.path}, hookResultMangle(cb));
}
var authenticate = function (cb) {
// If auth headers are present use them to authenticate...
if (req.headers.authorization && req.headers.authorization.search('Basic ') === 0) {
var userpass = new Buffer(req.headers.authorization.split(' ')[1], 'base64').toString().split(":")
var username = userpass[0];
var password = userpass[1];
if (settings.users[username] != undefined && settings.users[username].password == password) {
settings.users[username].username = username;
req.session.user = settings.users[username];
return cb(true);
}
return hooks.aCallFirst("authenticate", {req: req, res:res, next:next, username: username, password: password}, hookResultMangle(cb));
}
hooks.aCallFirst("authenticate", {req: req, res:res, next:next}, hookResultMangle(cb));
}
/* Authentication OR authorization failed. */
var failure = function () {
return hooks.aCallFirst("authFailure", {req: req, res:res, next:next}, hookResultMangle(function (ok) {
if (ok) return;
/* No plugin handler for invalid auth. Return Auth required
* Headers, delayed for 1 second, if authentication failed
* before. */
res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
if (req.headers.authorization) {
setTimeout(function () {
res.send('Authentication required', 401);
}, 1000);
} else {
res.send('Authentication required', 401);
}
}));
}
/* This is the actual authentication/authorization hoop. It is done in four steps:
1) Try to just access the thing
2) If not allowed using whatever creds are in the current session already, try to authenticate
3) If authentication using already supplied credentials succeeds, try to access the thing again
4) If all els fails, give the user a 401 to request new credentials
Note that the process could stop already in step 3 with a redirect to login page.
*/
authorize(function (ok) {
if (ok) return next();
authenticate(function (ok) {
if (!ok) return failure();
authorize(function (ok) {
if (ok) return next();
failure();
});
});
});
}
exports.expressConfigure = function (hook_name, args, cb) {
args.app.use(exports.basicAuth);
// If the log level specified in the config file is WARN or ERROR the application server never starts listening to requests as reported in issue #158.
// Not installing the log4js connect logger when the log level has a higher severity than INFO since it would not log at that level anyway.
if (!(settings.loglevel === "WARN" || settings.loglevel == "ERROR"))
args.app.use(log4js.connectLogger(httpLogger, { level: log4js.levels.INFO, format: ':status, :method :url'}));
args.app.use(express.cookieParser());
/* Do not let express create the session, so that we can retain a
* reference to it for socket.io to use. Also, set the key (cookie
* name) to a javascript identifier compatible string. Makes code
* handling it cleaner :) */
args.app.sessionStore = new express.session.MemoryStore();
args.app.use(express.session({store: args.app.sessionStore,
key: 'express_sid',
secret: apikey = randomString(32)}));
args.app.use(exports.basicAuth);
}

View File

@ -30,6 +30,7 @@ var path = require('path');
var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins");
var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks");
var npm = require("npm/lib/npm.js");
var _ = require("underscore");
//try to get the git version
var version = "";
@ -88,11 +89,11 @@ async.waterfall([
//let the server listen
app.listen(settings.port, settings.ip);
console.log("Server is listening at " + settings.ip + ":" + settings.port);
if(settings.adminHttpAuth){
if(!_.isEmpty(settings.users)){
console.log("Plugin admin page listening at " + settings.ip + ":" + settings.port + "/admin/plugins");
}
else{
console.log("Admin username and password not set in settings.json. To access admin please uncomment and edit adminHttpAuth in settings.json");
console.log("Admin username and password not set in settings.json. To access admin please uncomment and edit 'users' in settings.json");
}
callback(null);
}

View File

@ -80,15 +80,12 @@ exports.abiword = null;
*/
exports.loglevel = "INFO";
/**
* Http basic auth, with "user:password" format
*/
exports.httpAuth = null;
/**
* Http basic auth, with "user:password" format
*/
exports.adminHttpAuth = null;
/* This setting is used if you need authentication and/or
* authorization. Note: /admin always requires authentication, and
* either authorization by a module, or a user with is_admin set */
exports.requireAuthentication = false;
exports.requireAuthorization = false;
exports.users = {};
//checks if abiword is avaiable
exports.abiwordAvailable = function()

View File

@ -13,10 +13,12 @@
"yajsml" : "1.1.2",
"request" : "2.9.100",
"require-kernel" : "1.0.5",
"resolve" : "0.2.1",
"socket.io" : "0.8.7",
"ueberDB" : "0.1.7",
"async" : "0.1.18",
"express" : "2.5.8",
"connect" : "1.8.7",
"clean-css" : "0.3.2",
"uglify-js" : "1.2.5",
"formidable" : "1.0.9",

View File

@ -1,6 +1,5 @@
body {
margin: 0;
height: 100%;
color: #333;
font: 14px helvetica, sans-serif;
background: #ddd;
@ -42,34 +41,33 @@ form {
width: 300px;
margin: 0 auto;
}
button, input {
input {
font-weight: bold;
font-size: 15px;
}
input[type="button"] {
height: 30px;
padding: 4px 6px;
margin: 0;
display: block;
}
input[value="Uninstall"], input[value="Install"] {
input[type="button"].do-install, input[type="button"].do-uninstall {
float: right;
width: 100px;
}
input[type="button"]#do-search {
display: block;
}
input[type="text"] {
border-radius: 3px;
box-sizing: border-box;
-moz-box-sizing: border-box;
-moz-box-sizing: border-box;
padding: 10px;
*padding: 0; /* IE7 hack */
width: 100%;
outline: none;
border: 1px solid #ddd;
margin: 0 0 5px 1px;
margin: 0 0 5px 0;
max-width: 500px;
}
button{
display:block;
}
table {
border: 1px solid #ddd;
border-radius: 3px;
@ -95,13 +93,13 @@ td, th {
height: 500px;
margin-left: -350px;
margin-top: -250px;
border: 3px solid #999999;
background: #eeeeee;
border: 3px solid #999;
background: #eee;
}
.dialog .title {
margin: 0;
padding: 2px;
border-bottom: 3px solid #999999;
border-bottom: 3px solid #999;
font-size: 24px;
line-height: 24px;
height: 24px;
@ -109,10 +107,11 @@ td, th {
}
.dialog .title .close {
float: right;
padding: 1px 10px;
}
.dialog .history {
background: #222222;
color: #eeeeee;
background: #222;
color: #eee;
position: absolute;
top: 41px;
bottom: 10px;

View File

@ -1402,7 +1402,7 @@ input[type=checkbox] {
float: left;
width: 50%;
}
#settingsmenu,
#settings,
#importexport,
#embed {
position: absolute;
@ -1546,7 +1546,7 @@ input[type=checkbox] {
box-sizing: border-box;
width: 100%;
}
#settingsmenu,
#settings,
#importexport,
#embed {
left: 0;

View File

@ -167,7 +167,13 @@ require.setGlobalKeyPath("require");\n\
buffer.push(Ace2Editor.EMBEDED[KERNEL_SOURCE]);
buffer.push(KERNEL_BOOT);
buffer.push('<\/script>');
}
} else {
file = KERNEL_SOURCE;
buffer.push('<script type="application/javascript" src="' + KERNEL_SOURCE + '"><\/script>');
buffer.push('<script type="text/javascript">');
buffer.push(KERNEL_BOOT);
buffer.push('<\/script>');
}
}
function pushScriptsTo(buffer) {
/* Folling is for packaging regular expression. */

View File

@ -4635,7 +4635,7 @@ function Ace2Inner(){
function setClassPresence(elem, className, present)
{
if (present) $(elem).addClass(className);
else $(elem).removeClass(elem, className);
else $(elem).removeClass(className);
}
function setup()
@ -5401,7 +5401,9 @@ function Ace2Inner(){
// Init documentAttributeManager
documentAttributeManager = new AttributeManager(rep, performDocumentApplyChangeset);
editorInfo.ace_performDocumentApplyAttributesToRange = documentAttributeManager.setAttributesOnRange;
editorInfo.ace_performDocumentApplyAttributesToRange = function () {
return documentAttributeManager.setAttributesOnRange.apply(documentAttributeManager, arguments);
};
$(document).ready(function(){
doc = document; // defined as a var in scope outside

View File

@ -0,0 +1,143 @@
$(document).ready(function () {
var socket,
loc = document.location,
port = loc.port == "" ? (loc.protocol == "https:" ? 443 : 80) : loc.port,
url = loc.protocol + "//" + loc.hostname + ":" + port + "/",
pathComponents = location.pathname.split('/'),
// Strip admin/plugins
baseURL = pathComponents.slice(0,pathComponents.length-2).join('/') + '/',
resource = baseURL.substring(1) + "socket.io";
//connect
socket = io.connect(url, {resource : resource}).of("/pluginfw/installer");
$('.search-results').data('query', {
pattern: '',
offset: 0,
limit: 4,
});
var doUpdate = false;
var search = function () {
socket.emit("search", $('.search-results').data('query'));
}
function updateHandlers() {
$("#progress.dialog .close").unbind('click').click(function () {
$("#progress.dialog").hide();
});
$("#do-search").unbind('click').click(function () {
var query = $('.search-results').data('query');
query.pattern = $("#search-query")[0].value;
query.offset = 0;
search();
});
$(".do-install").unbind('click').click(function (e) {
var row = $(e.target).closest("tr");
doUpdate = true;
socket.emit("install", row.find(".name").html());
});
$(".do-uninstall").unbind('click').click(function (e) {
var row = $(e.target).closest("tr");
doUpdate = true;
socket.emit("uninstall", row.find(".name").html());
});
$(".do-prev-page").unbind('click').click(function (e) {
var query = $('.search-results').data('query');
query.offset -= query.limit;
if (query.offset < 0) {
query.offset = 0;
}
search();
});
$(".do-next-page").unbind('click').click(function (e) {
var query = $('.search-results').data('query');
var total = $('.search-results').data('total');
if (query.offset + query.limit < total) {
query.offset += query.limit;
}
search();
});
}
updateHandlers();
socket.on('progress', function (data) {
if (data.progress > 0 && $('#progress.dialog').data('progress') > data.progress) return;
$("#progress.dialog .close").hide();
$("#progress.dialog").show();
$('#progress.dialog').data('progress', data.progress);
var message = "Unknown status";
if (data.message) {
message = "<span class='status'>" + data.message.toString() + "</span>";
}
if (data.error) {
message = "<span class='error'>" + data.error.toString() + "<span>";
}
$("#progress.dialog .message").html(message);
$("#progress.dialog .history").append("<div>" + message + "</div>");
if (data.progress >= 1) {
if (data.error) {
$("#progress.dialog .close").show();
} else {
if (doUpdate) {
doUpdate = false;
socket.emit("load");
}
$("#progress.dialog").hide();
}
}
});
socket.on('search-result', function (data) {
var widget=$(".search-results");
widget.data('query', data.query);
widget.data('total', data.total);
widget.find('.offset').html(data.query.offset);
widget.find('.limit').html(data.query.offset + data.query.limit);
widget.find('.total').html(data.total);
widget.find(".results *").remove();
for (plugin_name in data.results) {
var plugin = data.results[plugin_name];
var row = widget.find(".template tr").clone();
for (attr in plugin) {
row.find("." + attr).html(plugin[attr]);
}
widget.find(".results").append(row);
}
updateHandlers();
});
socket.on('installed-results', function (data) {
$("#installed-plugins *").remove();
for (plugin_name in data.results) {
var plugin = data.results[plugin_name];
var row = $("#installed-plugin-template").clone();
for (attr in plugin.package) {
row.find("." + attr).html(plugin.package[attr]);
}
$("#installed-plugins").append(row);
}
updateHandlers();
});
socket.emit("load");
search();
});

View File

@ -162,11 +162,8 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
function showReconnectUI()
{
if (!clientVars.sliderEnabled || !clientVars.supportsSlider)
{
$("#padmain, #rightbars").css('top', "130px");
$("#timeslider").show();
}
$("#padmain, #rightbars").css('top', "130px");
$("#timeslider").show();
$('#error').show();
}
@ -216,7 +213,7 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
authorsList.append(' (');
_.each(colorsAnonymous, function(color, i){
if( i > 0 ) authorsList.append(' ');
$('<span /> ')
$('<span>&nbsp;</span>')
.css('background-color', color)
.addClass('author author-anonymous')
.appendTo(authorsList);
@ -287,55 +284,52 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
{
disableSelection($("#playpause_button")[0]);
disableSelection($("#timeslider")[0]);
if (clientVars.sliderEnabled && clientVars.supportsSlider)
$(document).keyup(function(e)
{
$(document).keyup(function(e)
{
var code = -1;
if (!e) var e = window.event;
if (e.keyCode) code = e.keyCode;
else if (e.which) code = e.which;
var code = -1;
if (!e) var e = window.event;
if (e.keyCode) code = e.keyCode;
else if (e.which) code = e.which;
if (code == 37)
{ // left
if (!e.shiftKey)
{
setSliderPosition(getSliderPosition() - 1);
}
else
{
var nextStar = 0; // default to first revision in document
for (var i = 0; i < savedRevisions.length; i++)
{
var pos = parseInt(savedRevisions[i].attr('pos'));
if (pos < getSliderPosition() && nextStar < pos) nextStar = pos;
}
setSliderPosition(nextStar);
}
}
else if (code == 39)
if (code == 37)
{ // left
if (!e.shiftKey)
{
if (!e.shiftKey)
{
setSliderPosition(getSliderPosition() + 1);
}
else
{
var nextStar = sliderLength; // default to last revision in document
for (var i = 0; i < savedRevisions.length; i++)
{
var pos = parseInt(savedRevisions[i].attr('pos'));
if (pos > getSliderPosition() && nextStar > pos) nextStar = pos;
}
setSliderPosition(nextStar);
}
setSliderPosition(getSliderPosition() - 1);
}
else if (code == 32) playpause();
});
}
else
{
var nextStar = 0; // default to first revision in document
for (var i = 0; i < savedRevisions.length; i++)
{
var pos = parseInt(savedRevisions[i].attr('pos'));
if (pos < getSliderPosition() && nextStar < pos) nextStar = pos;
}
setSliderPosition(nextStar);
}
}
else if (code == 39)
{
if (!e.shiftKey)
{
setSliderPosition(getSliderPosition() + 1);
}
else
{
var nextStar = sliderLength; // default to last revision in document
for (var i = 0; i < savedRevisions.length; i++)
{
var pos = parseInt(savedRevisions[i].attr('pos'));
if (pos > getSliderPosition() && nextStar > pos) nextStar = pos;
}
setSliderPosition(nextStar);
}
}
else if (code == 32) playpause();
});
$(window).resize(function()
{
updateSliderElements();
@ -485,37 +479,16 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
$("#revision").css('right', "20px");
$("#revision").css('top', "20px");
}
if (clientVars.sliderEnabled)
$("#timeslider").show();
setSliderLength(clientVars.totalRevs);
setSliderPosition(clientVars.revNum);
_.each(clientVars.savedRevisions, function(revision)
{
if (clientVars.supportsSlider)
{
$("#timeslider").show();
setSliderLength(clientVars.totalRevs);
setSliderPosition(clientVars.revNum);
_.each(clientVars.savedRevisions, function(revision)
{
addSavedRevision(revision.revNum, revision);
})
}
else
{
// slider is not supported
$("#padmain, #rightbars").css('top', "130px");
$("#timeslider").show();
$("#error").html("The timeslider feature is not supported on this pad. <a href=\"/ep/about/faq#disabledslider\">Why not?</a>");
$("#error").show();
}
}
else
{
if (clientVars.supportsSlider)
{
setSliderLength(clientVars.totalRevs);
setSliderPosition(clientVars.revNum);
}
}
addSavedRevision(revision.revNum, revision);
})
}
});
})();

View File

@ -229,6 +229,10 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
result.node.innerHTML = curHTML;
}
if (lineClass !== null) result.node.className = lineClass;
hooks.callAll("acePostWriteDomLineHTML", {
node: result.node
});
}
result.prepareForAdd = writeHTML;
result.finishUpdate = writeHTML;

View File

@ -203,7 +203,7 @@ function handshake()
//create the url
var url = loc.protocol + "//" + loc.hostname + ":" + port + "/";
//find out in which subfolder we are
var resource = loc.pathname.substr(1, loc.pathname.indexOf("/p/")) + "socket.io";
var resource = exports.baseURL.substring(1) + "socket.io";
//connect
socket = pad.socket = io.connect(url, {
resource: resource,
@ -1029,7 +1029,7 @@ var settings = {
};
pad.settings = settings;
exports.baseURL = '';
exports.settings = settings;
exports.createCookie = createCookie;
exports.readCookie = readCookie;

View File

@ -119,7 +119,7 @@ var padeditbar = (function()
}
else if (cmd == 'settings')
{
self.toogleDropDown("settingsmenu");
self.toogleDropDown("settings");
}
else if (cmd == 'embed')
{
@ -177,12 +177,11 @@ var padeditbar = (function()
},
toogleDropDown: function(moduleName)
{
var modules = ["settingsmenu", "importexport", "embed", "users"];
var modules = ["settings", "importexport", "embed", "users"];
//hide all modules
// hide all modules and remove highlighting of all buttons
if(moduleName == "none")
{
$("#editbar ul#menu_right > li").removeClass("selected");
for(var i=0;i<modules.length;i++)
{
//skip the userlist
@ -193,29 +192,27 @@ var padeditbar = (function()
if(module.css('display') != "none")
{
$("#" + modules[i] + "link").removeClass("selected");
module.slideUp("fast");
}
}
}
else
{
var nth_child = indexOf(modules, moduleName) + 1;
if (nth_child > 0 && nth_child <= (modules.length-1)) {
$("#editbar ul#menu_right li:not(:nth-child(" + nth_child + "))").removeClass("selected");
$("#editbar ul#menu_right li:nth-child(" + nth_child + ")").toggleClass("selected");
}
if(modules[modules.length-1] === moduleName) $("#editbar ul#menu_right li").removeClass("selected");
//hide all modules that are not selected and show the selected one
// hide all modules that are not selected and remove highlighting
// respectively add highlighting to the corresponding button
for(var i=0;i<modules.length;i++)
{
var module = $("#" + modules[i]);
if(module.css('display') != "none")
{
$("#" + modules[i] + "link").removeClass("selected");
module.slideUp("fast");
}
else if(modules[i]==moduleName)
{
$("#" + modules[i] + "link").addClass("selected");
module.slideDown("fast");
}
}

View File

@ -748,13 +748,14 @@ function closeColorPicker(accept)
var newColor = $("#mycolorpickerpreview").css("background-color");
var parts = newColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
// parts now should be ["rgb(0, 70, 255", "0", "70", "255"]
delete (parts[0]);
for (var i = 1; i <= 3; ++i) {
parts[i] = parseInt(parts[i]).toString(16);
if (parts[i].length == 1) parts[i] = '0' + parts[i];
if (parts) {
delete (parts[0]);
for (var i = 1; i <= 3; ++i) {
parts[i] = parseInt(parts[i]).toString(16);
if (parts[i].length == 1) parts[i] = '0' + parts[i];
}
var newColor = "#" +parts.join(''); // "0070ff"
}
var newColor = "#" +parts.join(''); // "0070ff"
myUserInfo.colorId = newColor;
pad.notifyChangeColor(newColor);
paduserlist.renderMyUserInfo();

View File

@ -4,27 +4,63 @@ var _;
/* FIXME: Ugly hack, in the future, use same code for server & client */
if (plugins.isClient) {
var async = require("ep_etherpad-lite/static/js/pluginfw/async");
_ = require("ep_etherpad-lite/static/js/underscore");
var _ = require("ep_etherpad-lite/static/js/underscore");
} else {
var async = require("async");
_ = require("underscore");
var _ = require("underscore");
}
exports.bubbleExceptions = true
var hookCallWrapper = function (hook, hook_name, args, cb) {
if (cb === undefined) cb = function (x) { return x; };
// Normalize output to list for both sync and async cases
var normalize = function(x) {
if (x == undefined) return [];
return x;
}
var normalizedhook = function () {
return normalize(hook.hook_fn(hook_name, args, function (x) {
return cb(normalize(x));
}));
}
if (exports.bubbleExceptions) {
return hook.hook_fn(hook_name, args, cb);
return normalizedhook();
} else {
try {
return hook.hook_fn(hook_name, args, cb);
return normalizedhook();
} catch (ex) {
console.error([hook_name, hook.part.full_name, ex.stack || ex]);
}
}
}
exports.syncMapFirst = function (lst, fn) {
var i;
var result;
for (i = 0; i < lst.length; i++) {
result = fn(lst[i])
if (result.length) return result;
}
return undefined;
}
exports.mapFirst = function (lst, fn, cb) {
var i = 0;
next = function () {
if (i >= lst.length) return cb(undefined);
fn(lst[i++], function (err, result) {
if (err) return cb(err);
if (result.length) return cb(null, result);
next();
});
}
next();
}
/* Don't use Array.concat as it flatterns arrays within the array */
exports.flatten = function (lst) {
@ -44,9 +80,9 @@ exports.flatten = function (lst) {
exports.callAll = function (hook_name, args) {
if (!args) args = {};
if (plugins.hooks[hook_name] === undefined) return [];
return exports.flatten(_.map(plugins.hooks[hook_name], function (hook) {
return _.flatten(_.map(plugins.hooks[hook_name], function (hook) {
return hookCallWrapper(hook, hook_name, args);
}));
}), true);
}
exports.aCallAll = function (hook_name, args, cb) {
@ -59,7 +95,7 @@ exports.aCallAll = function (hook_name, args, cb) {
hookCallWrapper(hook, hook_name, args, function (res) { cb(null, res); });
},
function (err, res) {
cb(null, exports.flatten(res));
cb(null, _.flatten(res, true));
}
);
}
@ -67,14 +103,22 @@ exports.aCallAll = function (hook_name, args, cb) {
exports.callFirst = function (hook_name, args) {
if (!args) args = {};
if (plugins.hooks[hook_name][0] === undefined) return [];
return exports.flatten(hookCallWrapper(plugins.hooks[hook_name][0], hook_name, args));
return exports.syncMapFirst(plugins.hooks[hook_name], function (hook) {
return hookCallWrapper(hook, hook_name, args);
});
}
exports.aCallFirst = function (hook_name, args, cb) {
if (!args) args = {};
if (!cb) cb = function () {};
if (plugins.hooks[hook_name][0] === undefined) return cb(null, []);
hookCallWrapper(plugins.hooks[hook_name][0], hook_name, args, function (res) { cb(null, exports.flatten(res)); });
if (plugins.hooks[hook_name] === undefined) return cb(null, []);
exports.mapFirst(
plugins.hooks[hook_name],
function (hook, cb) {
hookCallWrapper(hook, hook_name, args, function (res) { cb(null, res); });
},
cb
);
}
exports.callAllStr = function(hook_name, args, sep, pre, post) {

View File

@ -55,19 +55,41 @@ exports.install = function(plugin_name, cb) {
);
};
exports.search = function(pattern, cb) {
exports.searchCache = null;
exports.search = function(query, cache, cb) {
withNpm(
function (cb) {
registry.get(
"/-/all", null, 600, false, true,
var getData = function (cb) {
if (cache && exports.searchCache) {
cb(null, exports.searchCache);
} else {
registry.get(
"/-/all", null, 600, false, true,
function (er, data) {
if (er) return cb(er);
exports.searchCache = data;
cb(er, data);
}
);
}
}
getData(
function (er, data) {
if (er) return cb(er);
var res = {};
var i = 0;
for (key in data) {
if (key.indexOf(plugins.prefix) == 0 && key.indexOf(pattern) != -1)
res[key] = data[key];
if ( key.indexOf(plugins.prefix) == 0
&& key.indexOf(query.pattern) != -1) {
i++;
if (i > query.offset
&& i <= query.offset + query.limit) {
res[key] = data[key];
}
}
}
cb(null, {results:res});
cb(null, {results:res, query: query, total:i});
}
);
},

View File

@ -24,6 +24,7 @@ exports.loaded = false;
exports.plugins = {};
exports.parts = [];
exports.hooks = {};
exports.baseURL = '';
exports.ensure = function (cb) {
if (!exports.loaded)
@ -61,7 +62,7 @@ exports.loadFn = function (path, hookName) {
return fn;
};
exports.extractHooks = function (parts, hook_set_name) {
exports.extractHooks = function (parts, hook_set_name, plugins) {
var hooks = {};
_.each(parts,function (part) {
_.chain(part[hook_set_name] || {})
@ -69,14 +70,27 @@ exports.extractHooks = function (parts, hook_set_name) {
.each(function (hook_name) {
if (hooks[hook_name] === undefined) hooks[hook_name] = [];
var hook_fn_name = part[hook_set_name][hook_name];
var hook_fn = exports.loadFn(hook_fn_name, hook_name);
/* On the server side, you can't just
* require("pluginname/whatever") if the plugin is installed as
* a dependency of another plugin! Bah, pesky little details of
* npm... */
if (!exports.isClient) {
hook_fn_name = path.normalize(path.join(path.dirname(exports.plugins[part.plugin].package.path), hook_fn_name));
}
try {
var hook_fn = exports.loadFn(hook_fn_name, hook_name);
if (!hook_fn) {
throw "Not a function";
}
} catch (exc) {
console.error("Failed to load '" + hook_fn_name + "' for '" + part.full_name + "/" + hook_set_name + "/" + hook_name + "': " + exc.toString())
}
if (hook_fn) {
hooks[hook_name].push({"hook_name": hook_name, "hook_fn": hook_fn, "hook_fn_name": hook_fn_name, "part": part});
} else {
console.error("Unable to load hook function for " + part.full_name + " for hook " + hook_name + ": " + part.hooks[hook_name]);
}
}
});
});
return hooks;
@ -90,7 +104,7 @@ if (exports.isClient) {
// which appears to fix the issue.
var callback = function () {setTimeout(cb, 0);};
jQuery.getJSON('/pluginfw/plugin-definitions.json', function(data) {
jQuery.getJSON(exports.baseURL + 'pluginfw/plugin-definitions.json', function(data) {
exports.plugins = data.plugins;
exports.parts = data.parts;
exports.hooks = exports.extractHooks(exports.parts, "client_hooks");
@ -139,7 +153,7 @@ exports.update = function (cb) {
if (err) cb(err);
exports.plugins = plugins;
exports.parts = exports.sortParts(parts);
exports.hooks = exports.extractHooks(exports.parts, "hooks");
exports.hooks = exports.extractHooks(exports.parts, "hooks");
exports.loaded = true;
exports.callInit(cb);
}

View File

@ -60,8 +60,8 @@ function init() {
//create the url
var url = loc.protocol + "//" + loc.hostname + ":" + port + "/";
//find out in which subfolder we are
var resource = loc.pathname.substr(1,loc.pathname.indexOf("/p/")) + "socket.io";
var resource = exports.baseURL.substring(1) + 'socket.io';
//build up the socket io connection
socket = io.connect(url, {resource: resource});
@ -153,4 +153,5 @@ function handleClientVars(message)
}
}
exports.baseURL = '';
exports.init = init;

View File

@ -1,106 +1,15 @@
<html>
<head>
<title>Plugin manager</title>
<link href="../../static/css/admin.css" rel="stylesheet" type="text/css" />
<script src="../../static/js/jquery.js"></script>
<script src="../../socket.io/socket.io.js"></script>
<script>
$(document).ready(function () {
var socket = io.connect().of("/pluginfw/installer");
var doUpdate = false;
function updateHandlers() {
$("#progress.dialog .close").unbind('click').click(function () {
$("#progress.dialog").hide();
});
$("#do-search").unbind('click').click(function () {
if ($("#search-query")[0].value != "")
socket.emit("search", $("#search-query")[0].value);
});
$(".do-install").unbind('click').click(function (e) {
var row = $(e.target).closest("tr");
doUpdate = true;
socket.emit("install", row.find(".name").html());
});
$(".do-uninstall").unbind('click').click(function (e) {
var row = $(e.target).closest("tr");
doUpdate = true;
socket.emit("uninstall", row.find(".name").html());
});
}
updateHandlers();
socket.on('progress', function (data) {
$("#progress.dialog .close").hide();
$("#progress.dialog").show();
var message = "Unknown status";
if (data.message) {
message = "<span class='status'>" + data.message.toString() + "</span>";
}
if (data.error) {
message = "<span class='error'>" + data.error.toString() + "<span>";
}
$("#progress.dialog .message").html(message);
$("#progress.dialog .history").append("<div>" + message + "</div>");
if (data.progress >= 1) {
if (data.error) {
$("#progress.dialog .close").show();
} else {
if (doUpdate) {
doUpdate = false;
socket.emit("load");
}
$("#progress.dialog").hide();
}
}
});
socket.on('search-result', function (data) {
$("#search-results *").remove();
for (plugin_name in data.results) {
var plugin = data.results[plugin_name];
var row = $("#search-result-template").clone();
for (attr in plugin) {
row.find("." + attr).html(plugin[attr]);
}
$("#search-results").append(row);
}
updateHandlers();
});
socket.on('installed-results', function (data) {
$("#installed-plugins *").remove();
for (plugin_name in data.results) {
var plugin = data.results[plugin_name];
var row = $("#installed-plugin-template").clone();
for (attr in plugin.package) {
row.find("." + attr).html(plugin.package[attr]);
}
$("#installed-plugins").append(row);
}
updateHandlers();
});
socket.emit("load");
});
</script>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<link rel="stylesheet" href="../static/css/admin.css">
<script src="../static/js/jquery.js"></script>
<script src="../socket.io/socket.io.js"></script>
<script src="../static/js/admin/plugins.js"></script>
</head>
<body>
<div id="wrapper">
<h1>Etherpad Lite</h1>
<div class="seperator"></div>
<% if (errors.length) { %>
<div class="errors">
<% errors.forEach(function (item) { %>
@ -110,6 +19,8 @@
<% } %>
<h1>Etherpad Lite</h1>
<div class="seperator"></div>
<h2>Installed plugins</h2>
<table>
<thead>
@ -131,32 +42,38 @@
<tbody id="installed-plugins">
</tbody>
</table>
<div class="seperator"></div>
<h2>Search for plugins to install</h2>
<form>
<input type="text" name="search" placeholder="Search term" id="search-query">
<input type="button" value="Search" id="do-search">
</form>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<td></td>
</tr>
</thead>
<tbody class="template">
<tr id="search-result-template">
<td class="name"></td>
<td class="description"></td>
<td class="actions">
<input type="button" value="Install" class="do-install">
</td>
</tr>
</tbody>
<tbody id="search-results">
</tbody>
</table>
<div class="paged listing search-results">
<div class="seperator"></div>
<h2>Search for plugins to install</h2>
<form>
<input type="text" name="search" placeholder="Search term" id="search-query">
<input type="button" value="Search" id="do-search">
</form>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<td></td>
</tr>
</thead>
<tbody class="template">
<tr>
<td class="name"></td>
<td class="description"></td>
<td class="actions">
<input type="button" value="Install" class="do-install">
</td>
</tr>
</tbody>
<tbody class="results">
</tbody>
</table>
<input type="button" value="<<" class="do-prev-page">
<span class="offset"></span>..<span class="limit"></span> of <span class="total"></span>.
<input type="button" value=">>" class="do-next-page">
</div>
<div id="progress" class="dialog">
<h1 class="title">
@ -167,4 +84,4 @@
</div>
</div>
</body>
</html>
</html>

View File

@ -11,9 +11,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<% e.begin_block("styles"); %>
<link href="../static/css/pad.css" rel="stylesheet">
<link href="../static/custom/pad.css" rel="stylesheet">
<style title="dynamicsyntax"></style>
<link href="../static/css/pad.css" rel="stylesheet">
<link href="../static/custom/pad.css" rel="stylesheet">
<style title="dynamicsyntax"></style>
<% e.end_block(); %>
<!-- head and body had been removed intentionally -->
@ -21,70 +21,68 @@
<% e.begin_block("body"); %>
<div id="editbar" class="toolbar">
<ul class="menu_left">
<% e.begin_block("editbarMenuLeft"); %>
<li id="bold" onClick="window.pad&amp;&amp;pad.editbarClick('bold');return false">
<a class="buttonicon buttonicon-bold" title="Bold (ctrl-B)"></a>
</li>
<li id="italic" onClick="window.pad&amp;&amp;pad.editbarClick('italic'); return false;">
<a class="buttonicon buttonicon-italic" title="Italics (ctrl-I)"></a>
</li>
<li id="underline" onClick="window.pad&amp;&amp;pad.editbarClick('underline');return false;" >
<a class="buttonicon buttonicon-underline" title="Underline (ctrl-U)"></a>
</li>
<li id="strikethrough" onClick="window.pad&amp;&amp;pad.editbarClick('strikethrough');return false;">
<a class="buttonicon buttonicon-strikethrough" title="Strikethrough"></a>
</li>
<li class="separator"></li>
<li id="oderedlist" onClick="window.pad&amp;&amp;pad.editbarClick('insertorderedlist');return false;">
<a class="buttonicon buttonicon-insertorderedlist" title="Toggle Ordered List"></a>
</li>
<li id="unoderedlist" onClick="window.pad&amp;&amp;pad.editbarClick('insertunorderedlist');return false;">
<a class="buttonicon buttonicon-insertunorderedlist" title="Toggle Bullet List"></a>
</li>
<li id="indent" onClick="window.pad&amp;&amp;pad.editbarClick('indent');return false;">
<a class="buttonicon buttonicon-indent" title="Indent"></a>
</li>
<li id="outdent" onClick="window.pad&amp;&amp;pad.editbarClick('outdent');return false;">
<a class="buttonicon buttonicon-outdent" title="Unindent"></a>
</li>
<li class="separator"></li>
<li id="undo" onClick="window.pad&amp;&amp;pad.editbarClick('undo');return false;">
<a class="buttonicon buttonicon-undo" title="Undo (ctrl-Z)"></a>
</li>
<li id="redo" onClick="window.pad&amp;&amp;pad.editbarClick('redo');return false;">
<a class="buttonicon buttonicon-redo" title="Redo (ctrl-Y)"></a>
</li>
<li class="separator"></li>
<li id="clearAuthorship" onClick="window.pad&amp;&amp;pad.editbarClick('clearauthorship');return false;">
<a class="buttonicon buttonicon-clearauthorship" title="Clear Authorship Colors"></a>
</li>
<% e.end_block(); %>
</ul>
<% e.begin_block("editbarMenuLeft"); %>
<li id="bold" onClick="window.pad&amp;&amp;pad.editbarClick('bold');return false">
<a class="buttonicon buttonicon-bold" title="Bold (ctrl-B)"></a>
</li>
<li id="italic" onClick="window.pad&amp;&amp;pad.editbarClick('italic'); return false;">
<a class="buttonicon buttonicon-italic" title="Italics (ctrl-I)"></a>
</li>
<li id="underline" onClick="window.pad&amp;&amp;pad.editbarClick('underline');return false;" >
<a class="buttonicon buttonicon-underline" title="Underline (ctrl-U)"></a>
</li>
<li id="strikethrough" onClick="window.pad&amp;&amp;pad.editbarClick('strikethrough');return false;">
<a class="buttonicon buttonicon-strikethrough" title="Strikethrough"></a>
</li>
<li class="separator"></li>
<li id="oderedlist" onClick="window.pad&amp;&amp;pad.editbarClick('insertorderedlist');return false;">
<a class="buttonicon buttonicon-insertorderedlist" title="Toggle Ordered List"></a>
</li>
<li id="unoderedlist" onClick="window.pad&amp;&amp;pad.editbarClick('insertunorderedlist');return false;">
<a class="buttonicon buttonicon-insertunorderedlist" title="Toggle Bullet List"></a>
</li>
<li id="indent" onClick="window.pad&amp;&amp;pad.editbarClick('indent');return false;">
<a class="buttonicon buttonicon-indent" title="Indent"></a>
</li>
<li id="outdent" onClick="window.pad&amp;&amp;pad.editbarClick('outdent');return false;">
<a class="buttonicon buttonicon-outdent" title="Unindent"></a>
</li>
<li class="separator"></li>
<li id="undo" onClick="window.pad&amp;&amp;pad.editbarClick('undo');return false;">
<a class="buttonicon buttonicon-undo" title="Undo (ctrl-Z)"></a>
</li>
<li id="redo" onClick="window.pad&amp;&amp;pad.editbarClick('redo');return false;">
<a class="buttonicon buttonicon-redo" title="Redo (ctrl-Y)"></a>
</li>
<li class="separator"></li>
<li id="clearAuthorship" onClick="window.pad&amp;&amp;pad.editbarClick('clearauthorship');return false;">
<a class="buttonicon buttonicon-clearauthorship" title="Clear Authorship Colors"></a>
</li>
<% e.end_block(); %>
</ul>
<ul class="menu_right">
<% e.begin_block("editbarMenuRight"); %>
<li onClick="window.pad&amp;&amp;pad.editbarClick('savedRevision');return false;">
<a id="settingslink" title="Mark this revision as a saved revision">
<div class="buttonicon buttonicon-savedRevision"></div>
</a>
</li>
<li id="settingslink" onClick="window.pad&amp;&amp;pad.editbarClick('settings');return false;">
<a class="buttonicon buttonicon-settings" id="settingslink" title="Settings of this pad"></a>
</li>
<li id="importexportlink" onClick="window.pad&amp;&amp;pad.editbarClick('import_export');return false;">
<a class="buttonicon buttonicon-import_export" id="exportlink" title="Import/Export from/to different document formats"></a>
</li>
<li id="embedlink" onClick="window.pad&amp;&amp;pad.editbarClick('embed');return false;" >
<a class="buttonicon buttonicon-embed" id="embedlink" title="Share and Embed this pad"></a>
</li>
<li class="separator"></li>
<li id="timesliderlink" onClick="document.location = document.location.pathname+ '/timeslider'">
<a class="buttonicon buttonicon-history" title="Show the history of this pad"></a>
</li>
<li id="usericon" onClick="window.pad&amp;&amp;pad.editbarClick('showusers');return false;" title="Show connected users">
<span class="buttonicon buttonicon-showusers" id="usericonback"></span>
<span id="online_count">1</span>
</li>
<% e.end_block(); %>
<% e.begin_block("editbarMenuRight"); %>
<li id="settingslink" onClick="window.pad&amp;&amp;pad.editbarClick('settings');return false;">
<a class="buttonicon buttonicon-settings" title="Settings of this pad"></a>
</li>
<li id="importexportlink" onClick="window.pad&amp;&amp;pad.editbarClick('import_export');return false;">
<a class="buttonicon buttonicon-import_export" title="Import/Export from/to different document formats"></a>
</li>
<li id="embedlink" onClick="window.pad&amp;&amp;pad.editbarClick('embed');return false;">
<a class="buttonicon buttonicon-embed" title="Share and Embed this pad"></a>
</li>
<li id="revisionlink" onClick="window.pad&amp;&amp;pad.editbarClick('savedRevision');return false;">
<a class="buttonicon buttonicon-savedRevision" title="Mark this revision as a saved revision"></a>
</li>
<li class="separator"></li>
<li id="timesliderlink" onClick="document.location = document.location.pathname+ '/timeslider'">
<a class="buttonicon buttonicon-history" title="Show the history of this pad"></a>
</li>
<li id="usericon" onClick="window.pad&amp;&amp;pad.editbarClick('showusers');return false;" title="Show connected users">
<span class="buttonicon buttonicon-showusers" id="usericonback"></span>
<span id="online_count">1</span>
</li>
<% e.end_block(); %>
</ul>
</div>
@ -96,17 +94,17 @@
<button id="mycolorpickersave">Save</button>
<button id="mycolorpickercancel">Cancel</button>
<span id="mycolorpickerpreview" class="myswatchboxhoverable"></span>
</div>
</div>
<div id="myswatchbox"><div id="myswatch"></div></div>
<div id="myusernameform"><input type="text" id="myusernameedit" disabled="disabled"></div>
<div id="mystatusform"><input type="text" id="mystatusedit" disabled="disabled"></div>
<div id="mystatusform"><input type="text" id="mystatusedit" disabled="disabled"></div>
</div>
<div id="otherusers">
<div id="guestprompts"></div>
<table id="otheruserstable" cellspacing="0" cellpadding="0" border="0">
<tr><td></td></tr>
</table>
<div id="nootherusers"></div>
<div id="nootherusers"></div>
</div>
<div id="userlistbuttonarea"></div>
</div>
@ -116,7 +114,7 @@
<div id="editorloadingbox">Loading...</div>
</div>
<div id="settingsmenu" class="popup">
<div id="settings" class="popup">
<h1>Pad settings</h1>
<div class="column">
<% e.begin_block("mySettings"); %>
@ -156,20 +154,20 @@
<% e.begin_block("importColumn"); %>
<h2>Import from text file, HTML, PDF, Word, ODT or RTF</h2><br>
<form id="importform" method="post" action="" target="importiframe" enctype="multipart/form-data">
<div class="importformdiv" id="importformfilediv">
<input type="file" name="file" size="15" id="importfileinput">
<div class="importmessage" id="importmessagefail"></div>
</div>
<div class="importformdiv" id="importformfilediv">
<input type="file" name="file" size="15" id="importfileinput">
<div class="importmessage" id="importmessagefail"></div>
</div>
<div id="import"></div>
<div class="importmessage" id="importmessagesuccess">Successful!</div>
<div class="importformdiv" id="importformsubmitdiv">
<input type="hidden" name="padId" value="blpmaXT35R">
<span class="nowrap">
<input type="submit" name="submit" value="Import Now" disabled="disabled" id="importsubmitinput">
<img alt="" id="importstatusball" src="../static/img/loading.gif" align="top">
<img alt="" id="importarrow" src="../static/img/leftarrow.png" align="top">
</span>
</div>
<div class="importmessage" id="importmessagesuccess">Successful!</div>
<div class="importformdiv" id="importformsubmitdiv">
<input type="hidden" name="padId" value="blpmaXT35R">
<span class="nowrap">
<input type="submit" name="submit" value="Import Now" disabled="disabled" id="importsubmitinput">
<img alt="" id="importstatusball" src="../static/img/loading.gif" align="top">
<img alt="" id="importarrow" src="../static/img/leftarrow.png" align="top">
</span>
</div>
</form>
<% e.end_block(); %>
</div>
@ -234,80 +232,90 @@
<div id="modaloverlay">
<div id="modaloverlay-inner"></div>
</div>
<div id="mainmodals">
<% e.begin_block("modals"); %>
<div id="connectionbox" class="modaldialog">
<div id="connectionboxinner" class="modaldialog-inner">
<div class="connecting">Connecting...</div>
<div class="reconnecting">Reestablishing connection...</div>
<div class="disconnected">
<h2 class="h2_disconnect">Disconnected.</h2>
<h2 class="h2_userdup">Opened in another window.</h2>
<h2 class="h2_unauth">No Authorization.</h2>
<div id="disconnected_looping">
<p><b>We're having trouble talking to the EtherPad lite synchronization server.</b> You may be connecting through an incompatible firewall or proxy server.</p>
</div>
<div id="disconnected_initsocketfail">
<p><b>We were unable to connect to the EtherPad lite synchronization server.</b> This may be due to an incompatibility with your web browser or internet connection.</p>
</div>
<div id="disconnected_userdup">
<p><b>You seem to have opened this pad in another browser window.</b> If you'd like to use this window instead, you can reconnect.</p>
</div>
<div id="disconnected_unknown">
<p><b>Lost connection with the EtherPad lite synchronization server.</b> This may be due to a loss of network connectivity.</p>
</div>
<div id="disconnected_slowcommit">
<p><b>Server not responding.</b> This may be due to network connectivity issues or high load on the server.</p>
</div>
<div id="disconnected_unauth">
<p>Your browser's credentials or permissions have changed while viewing this pad. Try reconnecting.</p>
</div>
<div id="disconnected_deleted">
<p>This pad was deleted.</p>
</div>
<div id="reconnect_advise">
<p>If this continues to happen, please let us know</p>
</div>
<div id="reconnect_form">
<button id="forcereconnect">Reconnect Now</button>
</div>
<div id="connectionbox" class="modaldialog">
<div id="connectionboxinner" class="modaldialog-inner">
<div class="connecting">Connecting...</div>
<div class="reconnecting">Reestablishing connection...</div>
<div class="disconnected">
<h2 class="h2_disconnect">Disconnected.</h2>
<h2 class="h2_userdup">Opened in another window.</h2>
<h2 class="h2_unauth">No Authorization.</h2>
<div id="disconnected_looping">
<p><b>We're having trouble talking to the EtherPad lite synchronization server.</b> You may be connecting through an incompatible firewall or proxy server.</p>
</div>
<div id="disconnected_initsocketfail">
<p><b>We were unable to connect to the EtherPad lite synchronization server.</b> This may be due to an incompatibility with your web browser or internet connection.</p>
</div>
<div id="disconnected_userdup">
<p><b>You seem to have opened this pad in another browser window.</b> If you'd like to use this window instead, you can reconnect.</p>
</div>
<div id="disconnected_unknown">
<p><b>Lost connection with the EtherPad lite synchronization server.</b> This may be due to a loss of network connectivity.</p>
</div>
<div id="disconnected_slowcommit">
<p><b>Server not responding.</b> This may be due to network connectivity issues or high load on the server.</p>
</div>
<div id="disconnected_unauth">
<p>Your browser's credentials or permissions have changed while viewing this pad. Try reconnecting.</p>
</div>
<div id="disconnected_deleted">
<p>This pad was deleted.</p>
</div>
<div id="reconnect_advise">
<p>If this continues to happen, please let us know</p>
</div>
<div id="reconnect_form">
<button id="forcereconnect">Reconnect Now</button>
</div>
</div>
<form id="reconnectform" method="post" action="/ep/pad/reconnect" accept-charset="UTF-8" style="display: none;">
<input type="hidden" class="padId" name="padId">
<input type="hidden" class="diagnosticInfo" name="diagnosticInfo">
<input type="hidden" class="missedChanges" name="missedChanges">
</form>
</div>
<form id="reconnectform" method="post" action="/ep/pad/reconnect" accept-charset="UTF-8" style="display: none;">
<input type="hidden" class="padId" name="padId">
<input type="hidden" class="diagnosticInfo" name="diagnosticInfo">
<input type="hidden" class="missedChanges" name="missedChanges">
</form>
</div>
<% e.end_block(); %>
</div>
<% e.end_block(); %>
<% e.begin_block("scripts"); %>
<script type="text/javascript" src="../static/js/require-kernel.js"></script>
<script type="text/javascript" src="../static/js/jquery.js"></script>
<script type="text/javascript" src="../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../javascripts/lib/ep_etherpad-lite/static/js/pad.js?callback=require.define"></script>
<script type="text/javascript">
document.domain = document.domain;
var clientVars = {};
(function () {
require.setRootURI("../javascripts/src");
require.setLibraryURI("../javascripts/lib");
require.setGlobalKeyPath("require");
<script type="text/javascript" src="../static/js/require-kernel.js"></script>
<script type="text/javascript" src="../static/js/jquery.js"></script>
<script type="text/javascript" src="../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../javascripts/lib/ep_etherpad-lite/static/js/pad.js?callback=require.define"></script>
<script type="text/javascript">
document.domain = document.domain;
var clientVars = {};
(function () {
var pathComponents = location.pathname.split('/');
// Strip 'p' and the padname from the pathname and set as baseURL
var baseURL = pathComponents.slice(0,pathComponents.length-2).join('/') + '/';
require.setRootURI(baseURL + "javascripts/src");
require.setLibraryURI(baseURL + "javascripts/lib");
require.setGlobalKeyPath("require");
var plugins = require('ep_etherpad-lite/static/js/pluginfw/plugins');
plugins.update(function () {
require('ep_etherpad-lite/static/js/pad').init();
});
var plugins = require('ep_etherpad-lite/static/js/pluginfw/plugins');
plugins.baseURL = baseURL;
plugins.update(function () {
var pad = require('ep_etherpad-lite/static/js/pad');
pad.baseURL = baseURL;
pad.init();
});
/* TODO: These globals shouldn't exist. */
pad = require('ep_etherpad-lite/static/js/pad').pad;
chat = require('ep_etherpad-lite/static/js/chat').chat;
padeditbar = require('ep_etherpad-lite/static/js/pad_editbar').padeditbar;
padimpexp = require('ep_etherpad-lite/static/js/pad_impexp').padimpexp;
}());
</script>
/* TODO: These globals shouldn't exist. */
pad = require('ep_etherpad-lite/static/js/pad').pad;
chat = require('ep_etherpad-lite/static/js/chat').chat;
padeditbar = require('ep_etherpad-lite/static/js/pad_editbar').padeditbar;
padimpexp = require('ep_etherpad-lite/static/js/pad_impexp').padimpexp;
}());
</script>
<% e.end_block(); %>
</html>

View File

@ -123,22 +123,35 @@
</div>
</div>
<script type="text/javascript" src="../../../static/js/require-kernel.js"></script>
<script type="text/javascript" src="../../../static/js/jquery.js"></script>
<script type="text/javascript" src="../../../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../../../javascripts/lib/ep_etherpad-lite/static/js/timeslider.js?callback=require.define"></script>
<script type="text/javascript" src="../../../static/custom/timeslider.js"></script>
<script type="text/javascript" src="../../static/js/require-kernel.js"></script>
<script type="text/javascript" src="../../static/js/jquery.js"></script>
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../../javascripts/lib/ep_etherpad-lite/static/js/timeslider.js?callback=require.define"></script>
<script type="text/javascript" src="../../static/custom/timeslider.js"></script>
<script type="text/javascript" >
document.domain = document.domain;
var clientVars = {};
(function () {
require.setRootURI("../../../javascripts/src");
require.setLibraryURI("../../../javascripts/lib");
var pathComponents = location.pathname.split('/');
// Strip 'p', the padname and 'timeslider' from the pathname and set as baseURL
var baseURL = pathComponents.slice(0,pathComponents.length-3).join('/') + '/';
require.setRootURI(baseURL + "javascripts/src");
require.setLibraryURI(baseURL + "javascripts/lib");
require.setGlobalKeyPath("require");
var plugins = require('ep_etherpad-lite/static/js/pluginfw/plugins');
plugins.baseURL = baseURL;
plugins.update(function () {
require('ep_etherpad-lite/static/js/timeslider').init();
var timeslider = require('ep_etherpad-lite/static/js/timeslider')
timeslider.baseURL = baseURL;
timeslider.init();
/* TODO: These globals shouldn't exist. */
padeditbar = require('ep_etherpad-lite/static/js/pad_editbar').padeditbar;