Merge pull request #614 from fourplusone/ace2_refactoring

Adding more hooks + some code refactoring
This commit is contained in:
John McLear 2012-04-08 17:28:47 -07:00
commit 18038ddd50
9 changed files with 112 additions and 34 deletions

View file

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

View file

@ -17,6 +17,12 @@ var lineAttributes = [lineMarkerAttribute,'list'];
@param rep the document representation to be used @param rep the document representation to be used
@param applyChangesetCallback this callback will be called @param applyChangesetCallback this callback will be called
once a changeset has been built. 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) var AttributeManager = function(rep, applyChangesetCallback)
@ -29,6 +35,8 @@ var AttributeManager = function(rep, applyChangesetCallback)
// it will be considered as a line marker // it will be considered as a line marker
}; };
AttributeManager.lineAttributes = lineAttributes;
AttributeManager.prototype = _(AttributeManager.prototype).extend({ AttributeManager.prototype = _(AttributeManager.prototype).extend({
applyChangeset: function(changeset){ applyChangeset: function(changeset){

View file

@ -256,6 +256,10 @@ require.setGlobalKeyPath("require");\n\
$$INCLUDE_CSS("../static/css/iframe_editor.css"); $$INCLUDE_CSS("../static/css/iframe_editor.css");
$$INCLUDE_CSS("../static/css/pad.css"); $$INCLUDE_CSS("../static/css/pad.css");
$$INCLUDE_CSS("../static/custom/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); pushStyleTagsFor(iframeHTML, includedCSS);
var includedJS = []; var includedJS = [];
@ -294,6 +298,11 @@ require.setGlobalKeyPath("require");\n\
$$INCLUDE_CSS("../static/css/iframe_editor.css"); $$INCLUDE_CSS("../static/css/iframe_editor.css");
$$INCLUDE_CSS("../static/css/pad.css"); $$INCLUDE_CSS("../static/css/pad.css");
$$INCLUDE_CSS("../static/custom/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); pushStyleTagsFor(outerHTML, includedCSS);
// bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly // bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly

View file

@ -2875,7 +2875,7 @@ function Ace2Inner(){
_.each(hooks.callAll('aceRegisterBlockElements'), function(element){ _.each(hooks.callAll('aceRegisterBlockElements'), function(element){
_blockElems[element] = 1; _blockElems[element] = 1;
}) });
function isBlockElement(n) function isBlockElement(n)
{ {
@ -3365,6 +3365,9 @@ function Ace2Inner(){
var thisLineListType = getLineListType(theLine); var thisLineListType = getLineListType(theLine);
var prevLineEntry = (theLine > 0 && rep.lines.atIndex(theLine - 1)); var prevLineEntry = (theLine > 0 && rep.lines.atIndex(theLine - 1));
var prevLineBlank = (prevLineEntry && prevLineEntry.text.length == prevLineEntry.lineMarker); var prevLineBlank = (prevLineEntry && prevLineEntry.text.length == prevLineEntry.lineMarker);
var thisLineHasMarker = documentAttributeManager.lineHasMarker(theLine);
if (thisLineListType) if (thisLineListType)
{ {
// this line is a list // this line is a list
@ -3378,6 +3381,9 @@ function Ace2Inner(){
// delistify // delistify
performDocumentReplaceRange([theLine, 0], [theLine, lineEntry.lineMarker], ''); 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) else if (theLine > 0)
{ {

View file

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

View file

@ -29,7 +29,7 @@
var Security = require('./security'); var Security = require('./security');
var hooks = require('./pluginfw/hooks'); var hooks = require('./pluginfw/hooks');
var _ = require('./underscore'); var _ = require('./underscore');
var lineAttributeMarker = require('./linestylefilter').lineAttributeMarker;
var Ace2Common = require('./ace2_common'); var Ace2Common = require('./ace2_common');
var noop = Ace2Common.noop; var noop = Ace2Common.noop;
@ -82,7 +82,8 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
} }
var html = []; var html = [];
var preHtml, postHtml; var preHtml = '',
postHtml = '';
var curHTML = null; var curHTML = null;
function processSpaces(s) function processSpaces(s)
@ -95,7 +96,9 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
var lineClass = 'ace-line'; var lineClass = 'ace-line';
result.appendSpan = function(txt, cls) 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 listType = /(?:^| )list:(\S+)/.exec(cls);
var start = /(?:^| )start:(\S+)/.exec(cls); var start = /(?:^| )start:(\S+)/.exec(cls);
@ -116,9 +119,25 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
postHtml = '</li></ol>'; 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; result.lineMarker += txt.length;
return; // don't append any text return; // don't append any text
} }
} }
var href = null; var href = null;
var simpleTags = null; var simpleTags = null;
@ -203,7 +222,7 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
{ {
newHTML = (preHtml || '') + newHTML + (postHtml || ''); newHTML = (preHtml || '') + newHTML + (postHtml || '');
} }
html = preHtml = postHtml = null; // free memory html = preHtml = postHtml = ''; // free memory
if (newHTML !== curHTML) if (newHTML !== curHTML)
{ {
curHTML = newHTML; curHTML = newHTML;

View file

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

View file

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

View file

@ -155,6 +155,9 @@ function SkipList()
var widthLoc = point.widthSkips[0] + point.nodes[0].downSkipWidths[0]; var widthLoc = point.widthSkips[0] + point.nodes[0].downSkipWidths[0];
var newWidth = _entryWidth(entry); var newWidth = _entryWidth(entry);
p.mark("loop1"); 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) while (newNode.levels == 0 || Math.random() < 0.01)
{ {
var lvl = newNode.levels; var lvl = newNode.levels;