Merge branch 'develop' of https://github.com/Pita/etherpad-lite into develop

pull/840/merge
johnyma22 2012-04-09 13:49:06 +01:00
commit baf215fae6
22 changed files with 581 additions and 387 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ bin/convertSettings.json
src/static/js/jquery.js
npm-debug.log
*.DS_Store
.ep_initialized

View File

@ -24,6 +24,7 @@ var async = require("async");
var padManager = require("../db/PadManager");
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
var AttributePool = require("ep_etherpad-lite/static/js/AttributePool");
var AttributeManager = require("ep_etherpad-lite/static/js/AttributeManager");
var authorManager = require("../db/AuthorManager");
var readOnlyManager = require("../db/ReadOnlyManager");
var settings = require('../utils/Settings');
@ -31,6 +32,7 @@ var securityManager = require("../db/SecurityManager");
var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins.js");
var log4js = require('log4js');
var messageLogger = log4js.getLogger("message");
var _ = require('underscore');
/**
* A associative array that translates a session to a pad
@ -591,8 +593,12 @@ function _correctMarkersInPad(atext, apool) {
var offset = 0;
while (iter.hasNext()) {
var op = iter.next();
var listValue = Changeset.opAttributeValue(op, 'list', apool);
if (listValue) {
var hasMarker = _.find(AttributeManager.lineAttributes, function(attribute){
return Changeset.opAttributeValue(op, attribute, apool);
}) !== undefined;
if (hasMarker) {
for(var i=0;i<op.chars;i++) {
if (offset > 0 && text.charAt(offset-1) != '\n') {
badMarkers.push(offset);

View File

@ -4,13 +4,14 @@ var formidable = require('formidable');
var apiHandler = require('../../handler/APIHandler');
//This is for making an api call, collecting all post information and passing it to the apiHandler
exports.apiCaller = function(req, res, fields) {
var apiCaller = function(req, res, fields) {
res.header("Content-Type", "application/json; charset=utf-8");
apiLogger.info("REQUEST, " + req.params.func + ", " + JSON.stringify(fields));
//wrap the send function so we can log the response
res._send = res.send;
//note: res._send seems to be already in use, so better use a "unique" name
res._____send = res.send;
res.send = function (response) {
response = JSON.stringify(response);
apiLogger.info("RESPONSE, " + req.params.func + ", " + response);
@ -19,13 +20,14 @@ exports.apiCaller = function(req, res, fields) {
if(req.query.jsonp)
response = req.query.jsonp + "(" + response + ")";
res._send(response);
res._____send(response);
}
//call the api handler
apiHandler.handle(req.params.func, fields, req, res);
}
exports.apiCaller = apiCaller;
exports.expressCreateServer = function (hook_name, args, cb) {
//This is a api GET call, collect all post informations and pass it to the apiHandler
@ -55,4 +57,4 @@ exports.expressCreateServer = function (hook_name, args, cb) {
res.end("OK");
});
});
}
}

View File

@ -56,6 +56,7 @@
, "rjquery.js"
, "AttributePool.js"
, "Changeset.js"
, "ChangesetUtils.js"
, "security.js"
, "skiplist.js"
, "virtual_lines.js"
@ -66,6 +67,7 @@
, "changesettracker.js"
, "linestylefilter.js"
, "domline.js"
, "AttributeManager.js"
, "ace2_inner.js"
]
}

View File

@ -42,9 +42,8 @@ a img
border-bottom: 1px solid #ccc;
overflow: hidden;
padding-top: 3px;
position: absolute;
left: 0;
right: 0;
width: 100%;
white-space: nowrap;
height: 32px;
}
@ -177,7 +176,6 @@ a#backtoprosite { padding-left: 20px; left: 6px;
background: url(static/img/protop.gif) no-repeat -5px -6px; }
#accountnav { right: 30px; color: #fff; }
.propad a#topbaretherpad { background: url(static/img/protop.gif) no-repeat -397px -3px; }
#specialkeyarea { top: 5px; left: 250px; color: yellow; font-weight: bold;
font-size: 1.5em; position: absolute; }
@ -606,8 +604,6 @@ table#otheruserstable { display: none; }
text-align: left;
}
.nonprouser #sharebox-stripe { display: none; }
.sharebox-url {
width: 440px; height: 18px;
text-align: left;

View File

@ -1,4 +1,7 @@
#editorcontainerbox {overflow:auto; top:40px;}
#editorcontainerbox {
overflow:auto; top:40px;
position: static;
}
#padcontent {font-size:12px; padding:10px;}
@ -67,8 +70,9 @@
width:122px;
}
.topbarcenter, #docbar {display:none;}
#padmain {top:30px;}
#padmain {top:0px !important;}
#editbarright {float:right;}
#returnbutton {color:#222; font-size:16px; line-height:29px; margin-top:0; padding-right:6px;}
#importexport .popup {width:185px;}
@ -77,6 +81,53 @@
width:185px;
}
.timeslider-bar
{
background: #f7f7f7;
background: linear-gradient(#f7f7f7, #f1f1f1 80%);
border-bottom: 1px solid #ccc;
overflow: hidden;
padding-top: 3px;
width: 100%;
}
.timeslider-bar #editbar
{
border-bottom: none;
float: right;
width: 170px;
width: initial;
}
.timeslider-bar h1
{
margin: 5px;
}
.timeslider-bar p
{
margin: 5px;
}
#timeslider-top {
width: 100%;
position: fixed;
z-index: 1;
}
#authorsList .author {
padding-left: 0.4em;
padding-right: 0.4em;
}
#authorsList .author-anonymous {
padding-left: 0.6em;
padding-right: 0.6em;
}
#padeditor {
position: static;
}
/* lists */
.list-bullet2, .list-indent2, .list-number2 {margin-left:3em;}
.list-bullet3, .list-indent3, .list-number3 {margin-left:4.5em;}

View File

@ -0,0 +1,164 @@
var Changeset = require('./Changeset');
var ChangesetUtils = require('./ChangesetUtils');
var _ = require('./underscore');
var lineMarkerAttribute = 'lmkr';
// If one of these attributes are set to the first character of a
// line it is considered as a line attribute marker i.e. attributes
// set on this marker are applied to the whole line.
// The list attribute is only maintained for compatibility reasons
var lineAttributes = [lineMarkerAttribute,'list'];
/*
The Attribute manager builds changesets based on a document
representation for setting and removing range or line-based attributes.
@param rep the document representation to be used
@param applyChangesetCallback this callback will be called
once a changeset has been built.
A document representation contains
- an array `alines` containing 1 attributes string for each line
- an Attribute pool `apool`
- a SkipList `lines` containing the text lines of the document.
*/
var AttributeManager = function(rep, applyChangesetCallback)
{
this.rep = rep;
this.applyChangesetCallback = applyChangesetCallback;
this.author = '';
// If the first char in a line has one of the following attributes
// it will be considered as a line marker
};
AttributeManager.lineAttributes = lineAttributes;
AttributeManager.prototype = _(AttributeManager.prototype).extend({
applyChangeset: function(changeset){
if(!this.applyChangesetCallback) return changeset;
var cs = changeset.toString();
if (!Changeset.isIdentity(cs))
{
this.applyChangesetCallback(cs);
}
return changeset;
},
/*
Sets attributes on a range
@param start [row, col] tuple pointing to the start of the range
@param end [row, col] tuple pointing to the end of the range
@param attribute: an array of attributes
*/
setAttributesOnRange: function(start, end, attribs)
{
var builder = Changeset.builder(this.rep.lines.totalWidth());
ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, start);
ChangesetUtils.buildKeepRange(this.rep, builder, start, end, attribs, this.rep.apool);
return this.applyChangeset(builder);
},
/*
Returns if the line already has a line marker
@param lineNum: the number of the line
*/
lineHasMarker: function(lineNum){
var that = this;
return _.find(lineAttributes, function(attribute){
return that.getAttributeOnLine(lineNum, attribute) != '';
}) !== undefined;
},
/*
Gets a specified attribute on a line
@param lineNum: the number of the line to set the attribute for
@param attributeKey: the name of the attribute to get, e.g. list
*/
getAttributeOnLine: function(lineNum, attributeName){
// get `attributeName` attribute of first char of line
var aline = this.rep.alines[lineNum];
if (aline)
{
var opIter = Changeset.opIterator(aline);
if (opIter.hasNext())
{
return Changeset.opAttributeValue(opIter.next(), attributeName, this.rep.apool) || '';
}
}
return '';
},
/*
Sets a specified attribute on a line
@param lineNum: the number of the line to set the attribute for
@param attributeKey: the name of the attribute to set, e.g. list
@param attributeValue: an optional parameter to pass to the attribute (e.g. indention level)
*/
setAttributeOnLine: function(lineNum, attributeName, attributeValue){
var loc = [0,0];
var builder = Changeset.builder(this.rep.lines.totalWidth());
var hasMarker = this.lineHasMarker(lineNum);
ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0]));
if(hasMarker){
ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 1]), [
[attributeName, attributeValue]
], this.rep.apool);
}else{
// add a line marker
builder.insert('*', [
['author', this.author],
['insertorder', 'first'],
[lineMarkerAttribute, '1'],
[attributeName, attributeValue]
], this.rep.apool);
}
return this.applyChangeset(builder);
},
/*
Removes a specified attribute on a line
@param lineNum: the number of the affected line
@param attributeKey: the name of the attribute to remove, e.g. list
*/
removeAttributeOnLine: function(lineNum, attributeName, attributeValue){
var loc = [0,0];
var builder = Changeset.builder(this.rep.lines.totalWidth());
var hasMarker = this.lineHasMarker(lineNum);
if(hasMarker){
ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0]));
ChangesetUtils.buildRemoveRange(this.rep, builder, loc, (loc = [lineNum, 1]));
}
return this.applyChangeset(builder);
},
/*
Sets a specified attribute on a line
@param lineNum: the number of the line to set the attribute for
@param attributeKey: the name of the attribute to set, e.g. list
@param attributeValue: an optional parameter to pass to the attribute (e.g. indention level)
*/
toggleAttributeOnLine: function(lineNum, attributeName, attributeValue) {
return this.getAttributeOnLine(attributeName) ?
this.removeAttributeOnLine(lineNum, attributeName) :
this.setAttributeOnLine(lineNum, attributeName, attributeValue);
}
});
module.exports = AttributeManager;

View File

@ -22,6 +22,12 @@
* limitations under the License.
*/
/*
An AttributePool maintains a mapping from [key,value] Pairs called
Attributes to Numbers (unsigened integers) and vice versa. These numbers are
used to reference Attributes in Changesets.
*/
var AttributePool = function () {
this.numToAttrib = {}; // e.g. {0: ['foo','bar']}
this.attribToNum = {}; // e.g. {'foo,bar': 0}

View File

@ -0,0 +1,60 @@
/**
* This module contains several helper Functions to build Changesets
* based on a SkipList
*/
/**
* 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.
*/
exports.buildRemoveRange = function(rep, builder, start, end)
{
var startLineOffset = rep.lines.offsetOfIndex(start[0]);
var endLineOffset = rep.lines.offsetOfIndex(end[0]);
if (end[0] > start[0])
{
builder.remove(endLineOffset - startLineOffset - start[1], end[0] - start[0]);
builder.remove(end[1]);
}
else
{
builder.remove(end[1] - start[1]);
}
}
exports.buildKeepRange = function(rep, builder, start, end, attribs, pool)
{
var startLineOffset = rep.lines.offsetOfIndex(start[0]);
var endLineOffset = rep.lines.offsetOfIndex(end[0]);
if (end[0] > start[0])
{
builder.keep(endLineOffset - startLineOffset - start[1], end[0] - start[0], attribs, pool);
builder.keep(end[1], 0, attribs, pool);
}
else
{
builder.keep(end[1] - start[1], 0, attribs, pool);
}
}
exports.buildKeepToStartOfRange = function(rep, builder, start)
{
var startLineOffset = rep.lines.offsetOfIndex(start[0]);
builder.keep(startLineOffset, start[0]);
builder.keep(start[1]);
}

View File

@ -256,6 +256,10 @@ require.setGlobalKeyPath("require");\n\
$$INCLUDE_CSS("../static/css/iframe_editor.css");
$$INCLUDE_CSS("../static/css/pad.css");
$$INCLUDE_CSS("../static/custom/pad.css");
var additionalCSS = _(hooks.callAll("aceEditorCSS")).map(function(path){ return '../static/plugins/' + path });
includedCSS = includedCSS.concat(additionalCSS);
pushStyleTagsFor(iframeHTML, includedCSS);
var includedJS = [];
@ -294,6 +298,11 @@ require.setGlobalKeyPath("require");\n\
$$INCLUDE_CSS("../static/css/iframe_editor.css");
$$INCLUDE_CSS("../static/css/pad.css");
$$INCLUDE_CSS("../static/custom/pad.css");
var additionalCSS = _(hooks.callAll("aceEditorCSS")).map(function(path){ return '../static/plugins/' + path });
includedCSS = includedCSS.concat(additionalCSS);
pushStyleTagsFor(outerHTML, includedCSS);
// bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly

View File

@ -35,6 +35,8 @@ var isNodeText = Ace2Common.isNodeText,
binarySearchInfinite = Ace2Common.binarySearchInfinite,
htmlPrettyEscape = Ace2Common.htmlPrettyEscape,
noop = Ace2Common.noop;
var hooks = require('./pluginfw/hooks');
function Ace2Inner(){
@ -45,10 +47,12 @@ function Ace2Inner(){
var domline = require('./domline').domline;
var AttribPool = require('./AttributePool');
var Changeset = require('./Changeset');
var ChangesetUtils = require('./ChangesetUtils');
var linestylefilter = require('./linestylefilter').linestylefilter;
var SkipList = require('./skiplist');
var undoModule = require('./undomodule').undoModule;
var makeVirtualLineView = require('./virtual_lines').makeVirtualLineView;
var AttributeManager = require('./AttributeManager');
var DEBUG = false; //$$ build script replaces the string "var DEBUG=true;//$$" with "var DEBUG=false;"
// changed to false
@ -78,14 +82,11 @@ function Ace2Inner(){
var overlaysdiv = lineMetricsDiv.nextSibling;
initLineNumbers();
var outsideKeyDown = function(evt)
{};
var outsideKeyPress = function(evt)
{
return true;
};
var outsideNotifyDirty = function()
{};
var outsideKeyDown = noop;
var outsideKeyPress = function(){return true;};
var outsideNotifyDirty = noop;
// selFocusAtStart -- determines whether the selection extends "backwards", so that the focus
// point (controlled with the arrow keys) is at the beginning; not supported in IE, though
@ -100,6 +101,7 @@ function Ace2Inner(){
alines: [],
apool: new AttribPool()
};
// lines, alltext, alines, and DOM are set up in setup()
if (undoModule.enabled)
{
@ -119,6 +121,7 @@ function Ace2Inner(){
iframePadRight = 0;
var console = (DEBUG && window.console);
var documentAttributeManager;
if (!window.console)
{
@ -155,6 +158,7 @@ function Ace2Inner(){
var textFace = 'monospace';
var textSize = 12;
function textLineHeight()
{
@ -929,7 +933,10 @@ function Ace2Inner(){
},
grayedout: setClassPresenceNamed(outerWin.document.body, "grayedout"),
dmesg: function(){ dmesg = window.dmesg = value; },
userauthor: function(value){ thisAuthor = String(value); },
userauthor: function(value){
thisAuthor = String(value);
documentAttributeManager.author = thisAuthor;
},
styled: setStyled,
textface: setTextFace,
textsize: setTextSize,
@ -1864,55 +1871,6 @@ function Ace2Inner(){
}
}
function setupMozillaCaretHack(lineNum)
{
// This is really ugly, but by god, it works!
// Fixes annoying Firefox caret artifact (observed in 2.0.0.12
// and unfixed in Firefox 2 as of now) where mutating the DOM
// and then moving the caret to the beginning of a line causes
// an image of the caret to be XORed at the top of the iframe.
// The previous solution involved remembering to set the selection
// later, in response to the next event in the queue, which was hugely
// annoying.
// This solution: add a space character (0x20) to the beginning of the line.
// After setting the selection, remove the space.
var lineNode = rep.lines.atIndex(lineNum).lineNode;
var fc = lineNode.firstChild;
while (isBlockElement(fc) && fc.firstChild)
{
fc = fc.firstChild;
}
var textNode;
if (isNodeText(fc))
{
fc.nodeValue = " " + fc.nodeValue;
textNode = fc;
}
else
{
textNode = doc.createTextNode(" ");
fc.parentNode.insertBefore(textNode, fc);
}
markNodeClean(lineNode);
return {
unhack: function()
{
if (textNode.nodeValue == " ")
{
textNode.parentNode.removeChild(textNode);
}
else
{
textNode.nodeValue = textNode.nodeValue.substring(1);
}
markNodeClean(lineNode);
}
};
}
function getPointForLineAndChar(lineAndChar)
{
var line = lineAndChar[0];
@ -2247,6 +2205,9 @@ function Ace2Inner(){
}
/*
Converts the position of a char (index in String) into a [row, col] tuple
*/
function lineAndColumnFromChar(x)
{
var lineEntry = rep.lines.atOffset(x);
@ -2301,8 +2262,8 @@ function Ace2Inner(){
// CCCC\n
// end[0]: <CCC end[1] CCC>-------\n
var builder = Changeset.builder(rep.lines.totalWidth());
buildKeepToStartOfRange(builder, start);
buildRemoveRange(builder, start, end);
ChangesetUtils.buildKeepToStartOfRange(rep, builder, start);
ChangesetUtils.buildRemoveRange(rep, builder, start, end);
builder.insert(newText, [
['author', thisAuthor]
], rep.apool);
@ -2313,69 +2274,17 @@ function Ace2Inner(){
function performDocumentApplyAttributesToCharRange(start, end, attribs)
{
if (end >= rep.alltext.length)
{
end = rep.alltext.length - 1;
}
performDocumentApplyAttributesToRange(lineAndColumnFromChar(start), lineAndColumnFromChar(end), attribs);
end = Math.min(end, rep.alltext.length - 1);
documentAttributeManager.setAttributesOnRange(lineAndColumnFromChar(start), lineAndColumnFromChar(end), attribs);
}
editorInfo.ace_performDocumentApplyAttributesToCharRange = performDocumentApplyAttributesToCharRange;
function performDocumentApplyAttributesToRange(start, end, attribs)
{
var builder = Changeset.builder(rep.lines.totalWidth());
buildKeepToStartOfRange(builder, start);
buildKeepRange(builder, start, end, attribs, rep.apool);
var cs = builder.toString();
performDocumentApplyChangeset(cs);
}
editorInfo.ace_performDocumentApplyAttributesToRange = performDocumentApplyAttributesToRange;
function buildKeepToStartOfRange(builder, start)
{
var startLineOffset = rep.lines.offsetOfIndex(start[0]);
builder.keep(startLineOffset, start[0]);
builder.keep(start[1]);
}
function buildRemoveRange(builder, start, end)
{
var startLineOffset = rep.lines.offsetOfIndex(start[0]);
var endLineOffset = rep.lines.offsetOfIndex(end[0]);
if (end[0] > start[0])
{
builder.remove(endLineOffset - startLineOffset - start[1], end[0] - start[0]);
builder.remove(end[1]);
}
else
{
builder.remove(end[1] - start[1]);
}
}
function buildKeepRange(builder, start, end, attribs, pool)
{
var startLineOffset = rep.lines.offsetOfIndex(start[0]);
var endLineOffset = rep.lines.offsetOfIndex(end[0]);
if (end[0] > start[0])
{
builder.keep(endLineOffset - startLineOffset - start[1], end[0] - start[0], attribs, pool);
builder.keep(end[1], 0, attribs, pool);
}
else
{
builder.keep(end[1] - start[1], 0, attribs, pool);
}
}
function setAttributeOnSelection(attributeName, attributeValue)
{
if (!(rep.selStart && rep.selEnd)) return;
performDocumentApplyAttributesToRange(rep.selStart, rep.selEnd, [
documentAttributeManager.setAttributesOnRange(rep.selStart, rep.selEnd, [
[attributeName, attributeValue]
]);
}
@ -2436,13 +2345,13 @@ function Ace2Inner(){
if (selectionAllHasIt)
{
performDocumentApplyAttributesToRange(rep.selStart, rep.selEnd, [
documentAttributeManager.setAttributesOnRange(rep.selStart, rep.selEnd, [
[attributeName, '']
]);
}
else
{
performDocumentApplyAttributesToRange(rep.selStart, rep.selEnd, [
documentAttributeManager.setAttributesOnRange(rep.selStart, rep.selEnd, [
[attributeName, 'true']
]);
}
@ -2964,6 +2873,10 @@ function Ace2Inner(){
"ul": 1
};
_.each(hooks.callAll('aceRegisterBlockElements'), function(element){
_blockElems[element] = 1;
});
function isBlockElement(n)
{
return !!_blockElems[(n.tagName || "").toLowerCase()];
@ -3328,6 +3241,7 @@ function Ace2Inner(){
{
return;
}
var lineNum = rep.selStart[0];
var listType = getLineListType(lineNum);
@ -3399,11 +3313,9 @@ function Ace2Inner(){
}
}
if (mods.length > 0)
{
setLineListTypes(mods);
}
_.each(mods, function(mod){
setLineListType(mod[0], mod[1]);
});
return true;
}
editorInfo.ace_doIndentOutdent = doIndentOutdent;
@ -3453,6 +3365,9 @@ function Ace2Inner(){
var thisLineListType = getLineListType(theLine);
var prevLineEntry = (theLine > 0 && rep.lines.atIndex(theLine - 1));
var prevLineBlank = (prevLineEntry && prevLineEntry.text.length == prevLineEntry.lineMarker);
var thisLineHasMarker = documentAttributeManager.lineHasMarker(theLine);
if (thisLineListType)
{
// this line is a list
@ -3466,6 +3381,9 @@ function Ace2Inner(){
// delistify
performDocumentReplaceRange([theLine, 0], [theLine, lineEntry.lineMarker], '');
}
}else if (thisLineHasMarker && prevLineEntry){
// If the line has any attributes assigned, remove them by removing the marker '*'
performDocumentReplaceRange([theLine -1 , prevLineEntry.text.length], [theLine, lineEntry.lineMarker], '');
}
else if (theLine > 0)
{
@ -3818,26 +3736,17 @@ function Ace2Inner(){
return;
}
var mozillaCaretHack = (false && browser.mozilla && selStart && selEnd && selStart[0] == selEnd[0] && selStart[1] == rep.lines.atIndex(selStart[0]).lineMarker && selEnd[1] == rep.lines.atIndex(selEnd[0]).lineMarker && setupMozillaCaretHack(selStart[0]));
var selection = {};
var ss = [selStart[0], selStart[1]];
if (mozillaCaretHack) ss[1] += 1;
selection.startPoint = getPointForLineAndChar(ss);
var se = [selEnd[0], selEnd[1]];
if (mozillaCaretHack) se[1] += 1;
selection.endPoint = getPointForLineAndChar(se);
selection.focusAtStart = !! rep.selFocusAtStart;
setSelection(selection);
if (mozillaCaretHack)
{
mozillaCaretHack.unhack();
}
}
function getRepHTML()
@ -4926,27 +4835,30 @@ function Ace2Inner(){
}
}
}
var listAttributeName = 'list';
function getLineListType(lineNum)
{
// get "list" attribute of first char of line
var aline = rep.alines[lineNum];
if (aline)
{
var opIter = Changeset.opIterator(aline);
if (opIter.hasNext())
{
return Changeset.opAttributeValue(opIter.next(), 'list', rep.apool) || '';
}
}
return '';
return documentAttributeManager.getAttributeOnLine(lineNum, listAttributeName)
}
function setLineListType(lineNum, listType)
{
setLineListTypes([
[lineNum, listType]
]);
if(listType == ''){
documentAttributeManager.removeAttributeOnLine(lineNum, listAttributeName);
}else{
documentAttributeManager.setAttributeOnLine(lineNum, listAttributeName, listType);
}
//if the list has been removed, it is necessary to renumber
//starting from the *next* line because the list may have been
//separated. If it returns null, it means that the list was not cut, try
//from the current one.
if(renumberList(lineNum+1)==null)
{
renumberList(lineNum);
}
}
function renumberList(lineNum){
@ -4993,8 +4905,8 @@ function Ace2Inner(){
}
else if(curLevel == level)
{
buildKeepRange(builder, loc, (loc = [line, 0]));
buildKeepRange(builder, loc, (loc = [line, 1]), [
ChangesetUtils.buildKeepRange(rep, builder, loc, (loc = [line, 0]));
ChangesetUtils.buildKeepRange(rep, builder, loc, (loc = [line, 1]), [
['start', position]
], rep.apool);
@ -5025,62 +4937,6 @@ function Ace2Inner(){
}
function setLineListTypes(lineNumTypePairsInOrder)
{
var loc = [0, 0];
var builder = Changeset.builder(rep.lines.totalWidth());
for (var i = 0; i < lineNumTypePairsInOrder.length; i++)
{
var pair = lineNumTypePairsInOrder[i];
var lineNum = pair[0];
var listType = pair[1];
buildKeepRange(builder, loc, (loc = [lineNum, 0]));
if (getLineListType(lineNum))
{
// already a line marker
if (listType)
{
// make different list type
buildKeepRange(builder, loc, (loc = [lineNum, 1]), [
['list', listType]
], rep.apool);
}
else
{
// remove list marker
buildRemoveRange(builder, loc, (loc = [lineNum, 1]));
}
}
else
{
// currently no line marker
if (listType)
{
// add a line marker
builder.insert('*', [
['author', thisAuthor],
['insertorder', 'first'],
['list', listType]
], rep.apool);
}
}
}
var cs = builder.toString();
if (!Changeset.isIdentity(cs))
{
performDocumentApplyChangeset(cs);
}
//if the list has been removed, it is necessary to renumber
//starting from the *next* line because the list may have been
//separated. If it returns null, it means that the list was not cut, try
//from the current one.
if(renumberList(lineNum+1)==null)
{
renumberList(lineNum);
}
}
function doInsertList(type)
{
@ -5118,7 +4974,10 @@ function Ace2Inner(){
var t = getLineListType(n);
mods.push([n, allLinesAreList ? 'indent' + level : (t ? type + level : type + '1')]);
}
setLineListTypes(mods);
_.each(mods, function(mod){
setLineListType(mod[0], mod[1]);
});
}
function doInsertUnorderedList(){
@ -5538,6 +5397,11 @@ function Ace2Inner(){
}
}
}
// Init documentAttributeManager
documentAttributeManager = new AttributeManager(rep, performDocumentApplyChangeset);
editorInfo.ace_performDocumentApplyAttributesToRange = documentAttributeManager.setAttributesOnRange;
$(document).ready(function(){
doc = document; // defined as a var in scope outside
@ -5577,7 +5441,13 @@ function Ace2Inner(){
bindTheEventHandlers();
});
hooks.callAll('aceInitialized', {
editorInfo: editorInfo,
rep: rep,
documentAttributeManager: documentAttributeManager
});
scheduler.setTimeout(function()
{
parent.readyFunc(); // defined in code that sets up the inner iframe

View File

@ -170,41 +170,67 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
$('#error').show();
}
var fixPadHeight = _.throttle(function(){
var height = $('#timeslider-top').height();
$('#editorcontainerbox').css({marginTop: height});
}, 600);
function setAuthors(authors)
{
$("#authorstable").empty();
var authorsList = $("#authorsList");
authorsList.empty();
var numAnonymous = 0;
var numNamed = 0;
var colorsAnonymous = [];
_.each(authors, function(author)
{
var authorColor = clientVars.colorPalette[author.colorId] || author.colorId;
if (author.name)
{
if (numNamed !== 0) authorsList.append(', ');
$('<span />')
.text(author.name || "unnamed")
.css('background-color', authorColor)
.addClass('author')
.appendTo(authorsList);
numNamed++;
var tr = $('<tr></tr>');
var swatchtd = $('<td></td>');
var swatch = $('<div class="swatch"></div>');
swatch.css('background-color', clientVars.colorPalette[author.colorId]);
swatchtd.append(swatch);
tr.append(swatchtd);
var nametd = $('<td></td>');
nametd.text(author.name || "unnamed");
tr.append(nametd);
$("#authorstable").append(tr);
}
else
{
numAnonymous++;
if(authorColor) colorsAnonymous.push(authorColor);
}
});
if (numAnonymous > 0)
{
var html = "<tr><td colspan=\"2\" style=\"color:#999; padding-left: 10px\">" + (numNamed > 0 ? "...and " : "") + numAnonymous + " unnamed author" + (numAnonymous > 1 ? "s" : "") + "</td></tr>";
$("#authorstable").append($(html));
var anonymousAuthorString = numAnonymous + " unnamed author" + (numAnonymous > 1 ? "s" : "")
if (numNamed !== 0){
authorsList.append(' + ' + anonymousAuthorString);
} else {
authorsList.append(anonymousAuthorString);
}
if(colorsAnonymous.length > 0){
authorsList.append(' (');
_.each(colorsAnonymous, function(color, i){
if( i > 0 ) authorsList.append(' ');
$('<span /> ')
.css('background-color', color)
.addClass('author author-anonymous')
.appendTo(authorsList);
});
authorsList.append(')');
}
}
if (authors.length == 0)
{
$("#authorstable").append($("<tr><td colspan=\"2\" style=\"color:#999; padding-left: 10px\">No Authors</td></tr>"))
authorsList.append("No Authors");
}
fixPadHeight();
}
BroadcastSlider = {
@ -465,7 +491,6 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
{
if (clientVars.supportsSlider)
{
$("#padmain, #rightbars").css('top', "130px");
$("#timeslider").show();
setSliderLength(clientVars.totalRevs);
setSliderPosition(clientVars.revNum);

View File

@ -27,6 +27,7 @@ var _MAX_LIST_LEVEL = 8;
var Changeset = require('./Changeset');
var hooks = require('./pluginfw/hooks');
var _ = require('./underscore');
function sanitizeUnicode(s)
{
@ -161,12 +162,6 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
var selection, startPoint, endPoint;
var selStart = [-1, -1],
selEnd = [-1, -1];
var blockElems = {
"div": 1,
"p": 1,
"pre": 1
};
function _isEmpty(node, state)
{
// consider clean blank lines pasted in IE to be empty
@ -188,7 +183,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
{
var ln = lines.length() - 1;
var chr = lines.textOfLine(ln).length;
if (chr == 0 && state.listType && state.listType != 'none')
if (chr == 0 && !_.isEmpty(state.lineAttributes))
{
chr += 1; // listMarker
}
@ -240,25 +235,30 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
function _enterList(state, listType)
{
var oldListType = state.listType;
state.listLevel = (state.listLevel || 0) + 1;
var oldListType = state.lineAttributes['list'];
if (listType != 'none')
{
state.listNesting = (state.listNesting || 0) + 1;
}
state.listType = listType;
if(listType === 'none' || !listType ){
delete state.lineAttributes['list'];
}
else{
state.lineAttributes['list'] = listType;
}
_recalcAttribString(state);
return oldListType;
}
function _exitList(state, oldListType)
{
state.listLevel--;
if (state.listType != 'none')
if (state.lineAttributes['list'])
{
state.listNesting--;
}
state.listType = oldListType;
if(oldListType) state.lineAttributes['list'] = oldListType;
_recalcAttribString(state);
}
@ -301,21 +301,29 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
state.attribString = Changeset.makeAttribsString('+', lst, apool);
}
function _produceListMarker(state)
function _produceLineAttributesMarker(state)
{
lines.appendText('*', Changeset.makeAttribsString('+', [
['list', state.listType],
// TODO: This has to go to AttributeManager.
var attributes = [
['lmkr', '1'],
['insertorder', 'first']
], apool));
].concat(
_.map(state.lineAttributes,function(value,key){
console.log([key, value])
return [key, value];
})
);
lines.appendText('*', Changeset.makeAttribsString('+', attributes , apool));
}
cc.startNewLine = function(state)
{
if (state)
{
var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
if (atBeginningOfLine && state.listType && state.listType != 'none')
if (atBeginningOfLine && !_.isEmpty(state.lineAttributes))
{
_produceListMarker(state);
_produceLineAttributesMarker(state);
}
}
lines.startNew();
@ -345,7 +353,14 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
localAttribs: null,
attribs: { /*name -> nesting counter*/
},
attribString: ''
attribString: '',
// lineAttributes maintain a map from attributes to attribute values set on a line
lineAttributes: {
/*
example:
'list': 'bullet1',
*/
}
};
}
var localAttribs = state.localAttribs;
@ -407,9 +422,9 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
// newlines in the source mustn't become spaces at beginning of line box
txt2 = txt2.replace(/^\n*/, '');
}
if (atBeginningOfLine && state.listType && state.listType != 'none')
if (atBeginningOfLine && !_.isEmpty(state.lineAttributes))
{
_produceListMarker(state);
_produceLineAttributesMarker(state);
}
lines.appendText(textify(txt2), state.attribString);
x += consumed;

View File

@ -29,7 +29,7 @@
var Security = require('./security');
var hooks = require('./pluginfw/hooks');
var _ = require('./underscore');
var lineAttributeMarker = require('./linestylefilter').lineAttributeMarker;
var Ace2Common = require('./ace2_common');
var noop = Ace2Common.noop;
@ -82,7 +82,8 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
}
var html = [];
var preHtml, postHtml;
var preHtml = '',
postHtml = '';
var curHTML = null;
function processSpaces(s)
@ -95,7 +96,9 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
var lineClass = 'ace-line';
result.appendSpan = function(txt, cls)
{
if (cls.indexOf('list') >= 0)
var processedMarker = false;
// Handle lineAttributeMarker, if present
if (cls.indexOf(lineAttributeMarker) >= 0)
{
var listType = /(?:^| )list:(\S+)/.exec(cls);
var start = /(?:^| )start:(\S+)/.exec(cls);
@ -115,10 +118,26 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
preHtml = '<ol '+start+' class="list-' + Security.escapeHTMLAttribute(listType) + '"><li>';
postHtml = '</li></ol>';
}
}
}
processedMarker = true;
}
_.map(hooks.callAll("aceDomLineProcessLineAttributes", {
domline: domline,
cls: cls
}), function(modifier)
{
preHtml += modifier.preHtml;
postHtml += modifier.postHtml;
processedMarker |= modifier.processedMarker;
});
if( processedMarker ){
result.lineMarker += txt.length;
return; // don't append any text
}
}
}
var href = null;
var simpleTags = null;
@ -203,7 +222,7 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
{
newHTML = (preHtml || '') + newHTML + (postHtml || '');
}
html = preHtml = postHtml = null; // free memory
html = preHtml = postHtml = ''; // free memory
if (newHTML !== curHTML)
{
curHTML = newHTML;

View File

@ -32,6 +32,8 @@ var Changeset = require('./Changeset');
var hooks = require('./pluginfw/hooks');
var linestylefilter = {};
var _ = require('./underscore');
var AttributeManager = require('./AttributeManager');
linestylefilter.ATTRIB_CLASSES = {
'bold': 'tag:b',
@ -40,6 +42,9 @@ linestylefilter.ATTRIB_CLASSES = {
'strikethrough': 'tag:s'
};
var lineAttributeMarker = 'lineAttribMarker';
exports.lineAttributeMarker = lineAttributeMarker;
linestylefilter.getAuthorClassName = function(author)
{
return "author-" + author.replace(/[^a-y0-9]/g, function(c)
@ -68,14 +73,19 @@ linestylefilter.getLineStyleFilter = function(lineLength, aline, textAndClassFun
function attribsToClasses(attribs)
{
var classes = '';
var isLineAttribMarker = false;
Changeset.eachAttribNumber(attribs, function(n)
{
var key = apool.getAttribKey(n);
var key = apool.getAttribKey(n);
if (key)
{
var value = apool.getAttribValue(n);
if (value)
{
if (!isLineAttribMarker && _.indexOf(AttributeManager.lineAttributes, key) >= 0){
isLineAttribMarker = true;
}
if (key == 'author')
{
classes += ' ' + linestylefilter.getAuthorClassName(value);
@ -99,10 +109,12 @@ linestylefilter.getLineStyleFilter = function(lineLength, aline, textAndClassFun
key: key,
value: value
}, " ", " ", "");
}
}
}
}
});
if(isLineAttribMarker) classes += ' ' + lineAttributeMarker;
return classes.substring(1);
}

View File

@ -534,7 +534,7 @@ var pad = {
if(padcookie.getPref("showAuthorshipColors") == false){
pad.changeViewOption('showAuthorColors', false);
}
hooks.aCallAll("postAceInit");
hooks.aCallAll("postAceInit", {ace: padeditor.ace});
}
},
dispose: function()

View File

@ -200,10 +200,11 @@ var padeditbar = (function()
else
{
var nth_child = indexOf(modules, moduleName) + 1;
if (nth_child > 0 && nth_child <= 3) {
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
for(var i=0;i<modules.length;i++)
{

View File

@ -98,6 +98,28 @@ if (exports.isClient) {
};
} else {
exports.callInit = function (cb) {
var hooks = require("./hooks");
async.map(
Object.keys(exports.plugins),
function (plugin_name, cb) {
var plugin = exports.plugins[plugin_name];
fs.stat(path.normalize(path.join(plugin.package.path, ".ep_initialized")), function (err, stats) {
if (err) {
async.waterfall([
function (cb) { fs.writeFile(path.normalize(path.join(plugin.package.path, ".ep_initialized")), 'done', cb); },
function (cb) { hooks.aCallAll("init_" + plugin_name, {}, cb); },
cb,
]);
} else {
cb();
}
});
},
function () { cb(); }
);
}
exports.update = function (cb) {
exports.getPackages(function (er, packages) {
var parts = [];
@ -109,11 +131,12 @@ exports.update = function (cb) {
exports.loadPlugin(packages, plugin_name, plugins, parts, cb);
},
function (err) {
exports.plugins = plugins;
if (err) cb(err);
exports.plugins = plugins;
exports.parts = exports.sortParts(parts);
exports.hooks = exports.extractHooks(exports.parts, "hooks");
exports.loaded = true;
cb(err);
exports.loaded = true;
exports.callInit(cb);
}
);
});

View File

@ -155,6 +155,9 @@ function SkipList()
var widthLoc = point.widthSkips[0] + point.nodes[0].downSkipWidths[0];
var newWidth = _entryWidth(entry);
p.mark("loop1");
// The new node will have at least level 1
// With a proability of 0.01^(n-1) the nodes level will be >= n
while (newNode.levels == 0 || Math.random() < 0.01)
{
var lvl = newNode.levels;

View File

@ -130,6 +130,7 @@
<script src="static/custom/index.js"></script>
<script>
document.domain = document.domain;
function go2Name()
{
var padname = document.getElementById("padname").value;

View File

@ -280,6 +280,7 @@
<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 () {
<% if (settings.minify) { %>

View File

@ -11,40 +11,14 @@
<style type="text/css" title="dynamicsyntax"></style>
</head>
<body id="padbody" class="timeslider limwidth nonpropad nonprouser">
<body id="padbody" class="timeslider limwidth">
<div id="padpage">
<div id="padtop">
<div class="topbar">
<div class="topbarleft">
<!-- -->
</div>
<div class="topbarright">
<!-- -->
</div>
<div class="topbarcenter">
<a href="/" class="topbarBrand">Etherpad v1.1</a> <a href="http://etherpad.org"
class="EtherpadLink">Etherpad is</a> <a href="../../static/LICENSE" class=
"Licensing">free software</a>
<div class="fullscreen" onclick="$('body').toggleClass('maximized');">
Full screen
</div><a href="javascript:void(0);" onclick=
"$('body').toggleClass('maximized');" class="topbarmaximize" title=
"Toggle maximization"></a>
</div>
<div class="specialkeyarea">
<!-- -->
</div>
</div>
<div id="alertbar">
<div id="servermsg">
<h3>Server Notice<span id="servermsgdate"><!-- --></span>:</h3><a id=
"hidetopmsg" href="javascript:%20void%20pad.hideServerMessage()" name=
"hidetopmsg">hide</a>
<p id="servermsgtext"><!-- --></p>
</div>
</div>
@ -67,104 +41,56 @@
</table>
</div><!-- /docbar -->
</div>
<div id="timeslider-wrapper">
<div id="error" style="display: none">
It looks like you're having connection troubles. <a href=
"/ep/pad/view/test/latest">Reconnect now</a>.
<div id="timeslider-top">
<div id="timeslider-wrapper">
<div id="timeslider" unselectable="on" style="display: none">
<div id="timeslider-left"></div>
<div id="timeslider-right"></div>
<div id="timer"></div>
<div id="timeslider-slider">
<div id="ui-slider-handle"></div>
<div id="ui-slider-bar"></div>
</div>
<div id="playpause_button">
<div id="playpause_button_icon" class=""></div>
</div>
<div id="steppers">
<div class="stepper" id="leftstep"></div>
<div class="stepper" id="rightstep"></div>
</div>
</div>
</div>
<div id="timeslider" unselectable="on" style="display: none">
<div id="timeslider-left"></div>
<div class="timeslider-bar">
<div class="editbarright toolbar" id="editbar">
<ul>
<li onClick="window.padeditbar.toolbarClick('import_export');return false;">
<a id="exportlink" title="Export to different document formats">
<div class="buttonicon buttonicon-import_export"></div>
</a>
</li>
</ul>
<a id="returnbutton">Return to pad</a>
</div>
<div id="timeslider-right"></div>
<div id="timer"></div>
<div id="timeslider-slider">
<div id="ui-slider-handle"></div>
<div id="ui-slider-bar"></div>
</div>
<div id="playpause_button">
<div id="playpause_button_icon" class=""></div>
</div>
<div id="steppers">
<div class="stepper" id="leftstep"></div>
<div class="stepper" id="rightstep"></div>
</div>
<div>
<h1>
<span id="revision_label"></span>
<span id="revision_date"></span>
</h1>
<p>Authors:
<span id="authorsList">
<span>No Authors</span>
</span> </p>
</div>
</div>
</div>
<!--<div id="rightbars" style="top: 95px;">
<div id="rightbar"><a href="/ep/pad/view/c6fg9GM51V/latest" id="viewlatest">Viewing latest content</a><br>
<a thref="/ep/pad/view/c6fg9GM51V/rev.%revision%" href="/ep/pad/view/c6fg9GM51V/rev.0" class="tlink">Link to this version</a>
<br><a thref="/ep/pad/view/ro.fw470Orpi4T/rev.%revision%" href="/ep/pad/view/ro.fw470Orpi4T/rev.0" class="tlink">Link to read-only page</a><br><a href="/c6fg9GM51V">Edit this pad</a>
<h2>Download as</h2>
<img src="../../static/img/may09/html.gif"><a thref="/ep/pad/export/c6fg9GM51V/rev.%revision%?format=html" href="/ep/pad/export/c6fg9GM51V/rev.0?format=html" class="tlink">HTML</a><br>
<img src="../../static/img/may09/txt.gif"><a thref="/ep/pad/export/c6fg9GM51V/rev.%revision%?format=txt" href="/ep/pad/export/c6fg9GM51V/rev.0?format=txt" class="tlink">Plain text</a><br>
<img src="../../static/img/may09/doc.gif"><a thref="/ep/pad/export/c6fg9GM51V/rev.%revision%?format=doc" href="/ep/pad/export/c6fg9GM51V/rev.0?format=doc" class="tlink">Microsoft Word</a><br>
<img src="../../static/img/may09/pdf.gif"><a thref="/ep/pad/export/c6fg9GM51V/rev.%revision%?format=pdf" href="/ep/pad/export/c6fg9GM51V/rev.0?format=pdf" class="tlink">PDF</a>
</div>
<div id="legend">
<h2>Authors</h2>
<table cellspacing="0" cellpadding="0" border="0" id="authorstable"><tbody><tr><td style="color:#999; padding-left: 10px" colspan="2">No Authors</td></tr></tbody></table>
</div>
</div>-->
<div id="padmain">
<div id="padeditor">
<div id="editbar" class="toolbar disabledtoolbar">
<div id="editbarinner" class="toolbarinner">
<div id="editbarleft" class="editbarleft">
<!-- -->
</div>
<div id="editbarright" class="editbarright">
<!-- termporary place holder-->
<ul>
<li onClick="window.padeditbar.toolbarClick('import_export');return false;">
<a id="exportlink" title="Export to different document formats">
<div class="buttonicon buttonicon-import_export"></div>
</a>
</li>
</ul>
<a id = "returnbutton">Return to pad</a>
</div>
<div id="editbarinner" class="editbarinner">
<table cellpadding="0" cellspacing="0" border="0" id="editbartable" class=
"editbartable">
<tr>
<td>
<h1>
<span id="revision_label"></span>
<span id="revision_date"></span>
</h1>
</td>
<td width="100%">&nbsp;</td>
</tr>
</table>
<table cellpadding="0" cellspacing="0" border="0" id="editbarsavetable"
class="editbarsavetable">
<tr>
<td></td>
</tr>
</table>
</div>
</div>
</div>
<div id="editorcontainerbox">
<div id="padcontent">
</div>
</div>
</div><!-- /padeditor -->
@ -203,6 +129,7 @@
<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");