From 0340c87996655b3953d94a9b9a38bb92428aa341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Sat, 7 Apr 2012 01:05:25 +0200 Subject: [PATCH 01/11] Pad message handler detects lineattributemakers by asking Attribute manager --- src/node/handler/PadMessageHandler.js | 10 ++++++++-- src/static/js/AttributeManager.js | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 866edeb02..ecf79492e 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -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/AttributePool"); 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 0 && text.charAt(offset-1) != '\n') { badMarkers.push(offset); diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index c6351e868..8359b1c15 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -29,6 +29,8 @@ var AttributeManager = function(rep, applyChangesetCallback) // it will be considered as a line marker }; +AttributeManager.lineAttributes = lineAttributes; + AttributeManager.prototype = _(AttributeManager.prototype).extend({ applyChangeset: function(changeset){ From 38276e636c5fd694e5715a60fb06fabd92a1b801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Sat, 7 Apr 2012 01:06:29 +0200 Subject: [PATCH 02/11] postAceInit passes ace instance --- src/static/js/pad.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 426eb089e..3a1c1633e 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -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() From 34f07efcfbda9fd7dc302fdf4bb9a150c059a6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Sat, 7 Apr 2012 01:07:25 +0200 Subject: [PATCH 03/11] Delete key removes line attributes if prev line is not emtpy --- src/static/js/ace2_inner.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 99bbbb4f1..8980b2039 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3365,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 @@ -3378,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) { From c79593569a876e063719835231cd9de4ec41c150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Sat, 7 Apr 2012 01:40:13 +0200 Subject: [PATCH 04/11] added aceEditorCSS hook --- src/static/js/ace.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/static/js/ace.js b/src/static/js/ace.js index 4dfcc64ef..6ea2938b9 100644 --- a/src/static/js/ace.js +++ b/src/static/js/ace.js @@ -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 From 9efe838a325ca05dc5413fc131d98267a51a535a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Sat, 7 Apr 2012 01:57:10 +0200 Subject: [PATCH 05/11] removed unused variable --- src/static/js/contentcollector.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index e04fbb432..7f642cb20 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -161,12 +161,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 From 6507614e459dac868d3c76355ca013d13132bc79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Sat, 7 Apr 2012 02:12:42 +0200 Subject: [PATCH 06/11] made domline and content collector lineAttributes compatible --- src/static/js/contentcollector.js | 53 +++++++++++++++++++++---------- src/static/js/domline.js | 31 ++++++++++++++---- 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 7f642cb20..219976d33 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -27,6 +27,7 @@ var _MAX_LIST_LEVEL = 8; var Changeset = require('./Changeset'); var hooks = require('./pluginfw/hooks'); +var _ = require('./underscore'); function sanitizeUnicode(s) { @@ -182,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 } @@ -234,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); } @@ -295,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(); @@ -339,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; @@ -401,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; diff --git a/src/static/js/domline.js b/src/static/js/domline.js index d4f52a3ce..5d8bb7195 100644 --- a/src/static/js/domline.js +++ b/src/static/js/domline.js @@ -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 = '
  1. '; postHtml = '
'; } - } + } + 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; From a0235126e420c536dc2f54b730ea58766f1857c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Sat, 7 Apr 2012 02:13:26 +0200 Subject: [PATCH 07/11] syntax candy --- src/static/js/ace2_inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 8980b2039..d2d145035 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -2875,7 +2875,7 @@ function Ace2Inner(){ _.each(hooks.callAll('aceRegisterBlockElements'), function(element){ _blockElems[element] = 1; - }) + }); function isBlockElement(n) { From 2db74150eef92bae40f4a2a3f2c3de798d24127e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Sat, 7 Apr 2012 02:14:19 +0200 Subject: [PATCH 08/11] linestylefilter now uses attributemanager to find lineattributemarkers --- src/static/js/linestylefilter.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/static/js/linestylefilter.js b/src/static/js/linestylefilter.js index f328bd56a..1cbfac29c 100644 --- a/src/static/js/linestylefilter.js +++ b/src/static/js/linestylefilter.js @@ -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); } From 2406800cd6c01206ea6ec30bdab2f7367d44ad00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Sun, 8 Apr 2012 21:21:05 +0200 Subject: [PATCH 09/11] documentation on AttributeManager --- src/static/js/AttributeManager.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index be7c0d1b7..2d523f6aa 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -17,6 +17,12 @@ var lineAttributes = [lineMarkerAttribute,'list']; @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) From b7a0b36b5ea92e5947cfe235c36d15467aa6d390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Sun, 8 Apr 2012 21:21:30 +0200 Subject: [PATCH 10/11] ...well stupid error --- src/node/handler/PadMessageHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index ecf79492e..4a570e219 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -24,7 +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/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'); From c3ce00a82dc987fb48a85d676aa3d6e9ce2a7513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Sun, 8 Apr 2012 21:21:52 +0200 Subject: [PATCH 11/11] added a comment on skiplist.js --- src/static/js/skiplist.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/static/js/skiplist.js b/src/static/js/skiplist.js index 58477acca..a02a2ad52 100644 --- a/src/static/js/skiplist.js +++ b/src/static/js/skiplist.js @@ -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;