From a82cc078818c15e24f07541e2fc7bd19f2fde80e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Tue, 27 Mar 2012 12:28:47 +0200 Subject: [PATCH 01/14] replaced function(){} with noop --- src/static/js/ace2_inner.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index a2c4afeb6..2f380fc2a 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -78,14 +78,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 From 120340600a8f5ba1124b0f50f4b180a2387da34a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Tue, 27 Mar 2012 12:29:22 +0200 Subject: [PATCH 02/14] setupMozillaCaretHack is never called, so removing it should be safe --- src/static/js/ace2_inner.js | 58 ------------------------------------- 1 file changed, 58 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 2f380fc2a..3d7b2764a 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -1861,55 +1861,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]; @@ -3812,26 +3763,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() From 23cda77b650120cc9e892c73918dafbafc94d0d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Thu, 5 Apr 2012 00:50:04 +0200 Subject: [PATCH 03/14] added AttributeManager, ChangesetUtils --- src/node/utils/tar.json | 2 + src/static/js/AttributeManager.js | 109 +++++++++++++++++++ src/static/js/ChangesetUtils.js | 60 +++++++++++ src/static/js/ace2_inner.js | 172 +++++++++--------------------- 4 files changed, 220 insertions(+), 123 deletions(-) create mode 100644 src/static/js/AttributeManager.js create mode 100644 src/static/js/ChangesetUtils.js diff --git a/src/node/utils/tar.json b/src/node/utils/tar.json index 14c93f5c7..5c1abac5d 100644 --- a/src/node/utils/tar.json +++ b/src/node/utils/tar.json @@ -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" ] } diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js new file mode 100644 index 000000000..5373a41d8 --- /dev/null +++ b/src/static/js/AttributeManager.js @@ -0,0 +1,109 @@ +var Changeset = require('./Changeset'); +var ChangesetUtils = require('./ChangesetUtils'); +var _ = require('./underscore'); + +var AttributeManager = function(rep, applyChangesetCallback) +{ + this.rep = rep; + this.applyChangesetCallback = applyChangesetCallback; + this.author = ''; +}; + +AttributeManager.prototype = _(AttributeManager.prototype).extend({ + + applyChangeset: function(changeset){ + var cs = changeset.toString(); + if (!Changeset.isIdentity(cs)) + { + this.applyChangesetCallback(cs); + } + }, + + lineHasMarker: function(lineNum){ + // get "list" attribute of first char of line + return this.getAttributeOnLine(lineNum, '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'], + [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); + + // TODO + + if(hasMarker){ + ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0]), [ + [attributeName, attributeValue] + ], this.rep.apool); + 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; \ No newline at end of file diff --git a/src/static/js/ChangesetUtils.js b/src/static/js/ChangesetUtils.js new file mode 100644 index 000000000..e0b67881f --- /dev/null +++ b/src/static/js/ChangesetUtils.js @@ -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]); +} + diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 927b1f33d..769922599 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -45,10 +45,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 @@ -97,6 +99,7 @@ function Ace2Inner(){ alines: [], apool: new AttribPool() }; + // lines, alltext, alines, and DOM are set up in setup() if (undoModule.enabled) { @@ -116,6 +119,7 @@ function Ace2Inner(){ iframePadRight = 0; var console = (DEBUG && window.console); + var documentAttributeManager; if (!window.console) { @@ -152,6 +156,7 @@ function Ace2Inner(){ var textFace = 'monospace'; var textSize = 12; + function textLineHeight() { @@ -926,7 +931,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, @@ -2249,8 +2257,8 @@ function Ace2Inner(){ // CCCC\n // end[0]: -------\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); @@ -2272,53 +2280,16 @@ function Ace2Inner(){ function performDocumentApplyAttributesToRange(start, end, attribs) { var builder = Changeset.builder(rep.lines.totalWidth()); - buildKeepToStartOfRange(builder, start); - buildKeepRange(builder, start, end, attribs, rep.apool); + ChangesetUtils.buildKeepToStartOfRange(rep, builder, start); + ChangesetUtils.buildKeepRange(rep, 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); - } - } + + // TODO move to AttributeManager function setAttributeOnSelection(attributeName, attributeValue) { if (!(rep.selStart && rep.selEnd)) return; @@ -3276,6 +3247,7 @@ function Ace2Inner(){ { return; } + var lineNum = rep.selStart[0]; var listType = getLineListType(lineNum); @@ -3347,11 +3319,9 @@ function Ace2Inner(){ } } - if (mods.length > 0) - { - setLineListTypes(mods); - } - + _.each(mods, function(mod){ + setLineListType.apply(this, mod); + }); return true; } editorInfo.ace_doIndentOutdent = doIndentOutdent; @@ -4865,27 +4835,31 @@ 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] - ]); + debugger; + 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){ @@ -4932,8 +4906,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); @@ -4964,62 +4938,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) { @@ -5057,7 +4975,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.apply(this, mod); + }); } function doInsertUnorderedList(){ @@ -5477,6 +5398,11 @@ function Ace2Inner(){ } } } + + + // Init documentAttributeManager + + documentAttributeManager = new AttributeManager(rep, performDocumentApplyChangeset); $(document).ready(function(){ doc = document; // defined as a var in scope outside From 60942b09a44ece04fad24824de98270a80614ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Thu, 5 Apr 2012 01:07:47 +0200 Subject: [PATCH 04/14] fixing lineHasMarker --- src/static/js/AttributeManager.js | 14 +++++++++++--- src/static/js/ace2_inner.js | 4 ++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 5373a41d8..6e3a2490a 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -7,6 +7,10 @@ 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 + this.lineAttributes = ['list']; }; AttributeManager.prototype = _(AttributeManager.prototype).extend({ @@ -21,7 +25,13 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ lineHasMarker: function(lineNum){ // get "list" attribute of first char of line - return this.getAttributeOnLine(lineNum, 'list'); + var that = this; + + return _.find(this.lineAttributes, function(attribute){ + return that.getAttributeOnLine(lineNum, attribute) != ''; + }) !== undefined; + + }, getAttributeOnLine: function(lineNum, attributeName){ @@ -80,8 +90,6 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ var builder = Changeset.builder(this.rep.lines.totalWidth()); var hasMarker = this.lineHasMarker(lineNum); - // TODO - if(hasMarker){ ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0]), [ [attributeName, attributeValue] diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 769922599..d4ae599a0 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3320,7 +3320,7 @@ function Ace2Inner(){ } _.each(mods, function(mod){ - setLineListType.apply(this, mod); + setLineListType(mod[0], mod[1]); }); return true; } @@ -4977,7 +4977,7 @@ function Ace2Inner(){ } _.each(mods, function(mod){ - setLineListType.apply(this, mod); + setLineListType(mod[0], mod[1]); }); } From 9f02972af40d4236a9f66a0182ebc5fa75e84a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Thu, 5 Apr 2012 01:08:01 +0200 Subject: [PATCH 05/14] remove debugger statement --- src/static/js/ace2_inner.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index d4ae599a0..c89ed7006 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -4845,7 +4845,6 @@ function Ace2Inner(){ function setLineListType(lineNum, listType) { - debugger; if(listType == ''){ documentAttributeManager.removeAttributeOnLine(lineNum, listAttributeName); }else{ From 093b7ab4bfd20ff1d276bd194bbad7ba51d022d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Thu, 5 Apr 2012 15:20:48 +0200 Subject: [PATCH 06/14] added lineMarkerAttribute + more comments --- src/static/js/AttributeManager.js | 37 ++++++++++++++++++++++++++----- src/static/js/AttributePool.js | 6 +++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 6e3a2490a..3ea2b6dab 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -2,6 +2,23 @@ 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 SkipList + for setting and removing range or line-based Attributes. + + @param rep the SkipList to be used + @param applyChangesetCallback this callback will be called + once a changeset has been built. +*/ + var AttributeManager = function(rep, applyChangesetCallback) { this.rep = rep; @@ -10,30 +27,39 @@ var AttributeManager = function(rep, applyChangesetCallback) // If the first char in a line has one of the following attributes // it will be considered as a line marker - this.lineAttributes = ['list']; }; 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; }, + /* + Returns if the line already has a line marker + @param lineNum: the number of the line + */ lineHasMarker: function(lineNum){ - // get "list" attribute of first char of line var that = this; - return _.find(this.lineAttributes, function(attribute){ + 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]; @@ -71,6 +97,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ builder.insert('*', [ ['author', this.author], ['insertorder', 'first'], + [lineMarkerAttribute, '1'], [attributeName, attributeValue] ], this.rep.apool); } diff --git a/src/static/js/AttributePool.js b/src/static/js/AttributePool.js index a9245daf8..f5990c07d 100644 --- a/src/static/js/AttributePool.js +++ b/src/static/js/AttributePool.js @@ -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} From 5833cb1037c6e52fb2537765acc4014c6feb6a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Thu, 5 Apr 2012 15:22:22 +0200 Subject: [PATCH 07/14] renamed performDocumentApplyAttributesToRange to setAttributesOnRange and moved it to AttributeManager --- src/static/js/AttributeManager.js | 14 ++++++++++++++ src/static/js/ace2_inner.js | 22 +++++----------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 3ea2b6dab..2f40c4f43 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -43,6 +43,20 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ 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(rep.lines.totalWidth()); + ChangesetUtils.buildKeepToStartOfRange(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 diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index c89ed7006..e57ee15f5 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -2276,25 +2276,13 @@ function Ace2Inner(){ performDocumentApplyAttributesToRange(lineAndColumnFromChar(start), lineAndColumnFromChar(end), attribs); } editorInfo.ace_performDocumentApplyAttributesToCharRange = performDocumentApplyAttributesToCharRange; - - function performDocumentApplyAttributesToRange(start, end, attribs) - { - var builder = Changeset.builder(rep.lines.totalWidth()); - ChangesetUtils.buildKeepToStartOfRange(rep, builder, start); - ChangesetUtils.buildKeepRange(rep, builder, start, end, attribs, rep.apool); - var cs = builder.toString(); - performDocumentApplyChangeset(cs); - } - editorInfo.ace_performDocumentApplyAttributesToRange = performDocumentApplyAttributesToRange; - - - // TODO move to AttributeManager + function setAttributeOnSelection(attributeName, attributeValue) { if (!(rep.selStart && rep.selEnd)) return; - performDocumentApplyAttributesToRange(rep.selStart, rep.selEnd, [ + documentAttributeManager.setAttributesOnRange(rep.selStart, rep.selEnd, [ [attributeName, attributeValue] ]); } @@ -2355,13 +2343,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'] ]); } @@ -5400,8 +5388,8 @@ 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 From 2ccc70daf6962e6b388bf20114b9d6d9630ce112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Thu, 5 Apr 2012 15:25:17 +0200 Subject: [PATCH 08/14] documented lineAndColumnFromChar --- src/static/js/ace2_inner.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index e57ee15f5..28e2f2a75 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -2203,6 +2203,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); From ff9e4070921867031fec50f794ce44404df5dc1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Thu, 5 Apr 2012 15:26:51 +0200 Subject: [PATCH 09/14] Minor coding style improvement. Does the same as before --- src/static/js/ace2_inner.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 28e2f2a75..4dcd5edcc 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -2272,11 +2272,8 @@ 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; From 9b1536ac107791925a4c76799e19fc88c8ef690b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Thu, 5 Apr 2012 22:26:39 +0200 Subject: [PATCH 10/14] fixing 'rep is not defined' error --- src/static/js/AttributeManager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 2f40c4f43..0b10274d2 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -51,8 +51,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ */ setAttributesOnRange: function(start, end, attribs) { - var builder = Changeset.builder(rep.lines.totalWidth()); - ChangesetUtils.buildKeepToStartOfRange(rep, builder, start); + 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); }, From 39d0b149a9e1d077ee5b904068ccc14396fa3f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Fri, 6 Apr 2012 17:44:34 +0200 Subject: [PATCH 11/14] corrected documentation on Attribute Manager --- src/static/js/AttributeManager.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 0b10274d2..c6351e868 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -11,10 +11,10 @@ var lineMarkerAttribute = 'lmkr'; var lineAttributes = [lineMarkerAttribute,'list']; /* - The Attribute manager builds changesets based on a SkipList - for setting and removing range or line-based Attributes. + The Attribute manager builds changesets based on a document + representation for setting and removing range or line-based attributes. - @param rep the SkipList to be used + @param rep the document representation to be used @param applyChangesetCallback this callback will be called once a changeset has been built. */ From ae21eb30ddda46f535d5024bd192145383d4be5d Mon Sep 17 00:00:00 2001 From: 0ip Date: Fri, 6 Apr 2012 22:32:19 +0300 Subject: [PATCH 12/14] Deselect highlighted editbar module button when userlist is opened --- src/static/js/pad_editbar.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js index 7e8cf96e8..95d195058 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.js @@ -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 Date: Sat, 7 Apr 2012 01:02:50 +0200 Subject: [PATCH 13/14] Added 2 new Hooks + aceInitialized is called when ace has been fully initialized + aceRegisterBlockElements allows to register new Block Elements with ace. --- src/static/js/ace2_inner.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 4dcd5edcc..99bbbb4f1 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -35,6 +35,8 @@ var isNodeText = Ace2Common.isNodeText, binarySearchInfinite = Ace2Common.binarySearchInfinite, htmlPrettyEscape = Ace2Common.htmlPrettyEscape, noop = Ace2Common.noop; + var hooks = require('./pluginfw/hooks'); + function Ace2Inner(){ @@ -2871,6 +2873,10 @@ function Ace2Inner(){ "ul": 1 }; + _.each(hooks.callAll('aceRegisterBlockElements'), function(element){ + _blockElems[element] = 1; + }) + function isBlockElement(n) { return !!_blockElems[(n.tagName || "").toLowerCase()]; @@ -5429,7 +5435,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 From 18dc111010075d4e47db371e38b5b10fc208d30b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bartelme=C3=9F?= Date: Sat, 7 Apr 2012 03:42:05 +0200 Subject: [PATCH 14/14] Fixing bug where lists get deleted when pressing enter --- src/static/js/AttributeManager.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 5373a41d8..ed6bf1d68 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -83,9 +83,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ // TODO if(hasMarker){ - ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0]), [ - [attributeName, attributeValue] - ], this.rep.apool); + ChangesetUtils.buildKeepRange(this.rep, builder, loc, (loc = [lineNum, 0])); ChangesetUtils.buildRemoveRange(this.rep, builder, loc, (loc = [lineNum, 1])); }