added AttributeManager, ChangesetUtils

This commit is contained in:
Matthias Bartelmeß 2012-04-05 00:50:04 +02:00
parent 6c647baa02
commit 23cda77b65
4 changed files with 220 additions and 123 deletions

View File

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

View File

@ -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;

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

@ -45,10 +45,12 @@ function Ace2Inner(){
var domline = require('./domline').domline; var domline = require('./domline').domline;
var AttribPool = require('./AttributePool'); var AttribPool = require('./AttributePool');
var Changeset = require('./Changeset'); var Changeset = require('./Changeset');
var ChangesetUtils = require('./ChangesetUtils');
var linestylefilter = require('./linestylefilter').linestylefilter; var linestylefilter = require('./linestylefilter').linestylefilter;
var SkipList = require('./skiplist'); var SkipList = require('./skiplist');
var undoModule = require('./undomodule').undoModule; var undoModule = require('./undomodule').undoModule;
var makeVirtualLineView = require('./virtual_lines').makeVirtualLineView; 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;" var DEBUG = false; //$$ build script replaces the string "var DEBUG=true;//$$" with "var DEBUG=false;"
// changed to false // changed to false
@ -97,6 +99,7 @@ function Ace2Inner(){
alines: [], alines: [],
apool: new AttribPool() apool: new AttribPool()
}; };
// lines, alltext, alines, and DOM are set up in setup() // lines, alltext, alines, and DOM are set up in setup()
if (undoModule.enabled) if (undoModule.enabled)
{ {
@ -116,6 +119,7 @@ function Ace2Inner(){
iframePadRight = 0; iframePadRight = 0;
var console = (DEBUG && window.console); var console = (DEBUG && window.console);
var documentAttributeManager;
if (!window.console) if (!window.console)
{ {
@ -153,6 +157,7 @@ function Ace2Inner(){
var textFace = 'monospace'; var textFace = 'monospace';
var textSize = 12; var textSize = 12;
function textLineHeight() function textLineHeight()
{ {
return Math.round(textSize * 4 / 3); return Math.round(textSize * 4 / 3);
@ -926,7 +931,10 @@ function Ace2Inner(){
}, },
grayedout: setClassPresenceNamed(outerWin.document.body, "grayedout"), grayedout: setClassPresenceNamed(outerWin.document.body, "grayedout"),
dmesg: function(){ dmesg = window.dmesg = value; }, dmesg: function(){ dmesg = window.dmesg = value; },
userauthor: function(value){ thisAuthor = String(value); }, userauthor: function(value){
thisAuthor = String(value);
documentAttributeManager.author = thisAuthor;
},
styled: setStyled, styled: setStyled,
textface: setTextFace, textface: setTextFace,
textsize: setTextSize, textsize: setTextSize,
@ -2249,8 +2257,8 @@ function Ace2Inner(){
// CCCC\n // CCCC\n
// end[0]: <CCC end[1] CCC>-------\n // end[0]: <CCC end[1] CCC>-------\n
var builder = Changeset.builder(rep.lines.totalWidth()); var builder = Changeset.builder(rep.lines.totalWidth());
buildKeepToStartOfRange(builder, start); ChangesetUtils.buildKeepToStartOfRange(rep, builder, start);
buildRemoveRange(builder, start, end); ChangesetUtils.buildRemoveRange(rep, builder, start, end);
builder.insert(newText, [ builder.insert(newText, [
['author', thisAuthor] ['author', thisAuthor]
], rep.apool); ], rep.apool);
@ -2272,53 +2280,16 @@ function Ace2Inner(){
function performDocumentApplyAttributesToRange(start, end, attribs) function performDocumentApplyAttributesToRange(start, end, attribs)
{ {
var builder = Changeset.builder(rep.lines.totalWidth()); var builder = Changeset.builder(rep.lines.totalWidth());
buildKeepToStartOfRange(builder, start); ChangesetUtils.buildKeepToStartOfRange(rep, builder, start);
buildKeepRange(builder, start, end, attribs, rep.apool); ChangesetUtils.buildKeepRange(rep, builder, start, end, attribs, rep.apool);
var cs = builder.toString(); var cs = builder.toString();
performDocumentApplyChangeset(cs); performDocumentApplyChangeset(cs);
} }
editorInfo.ace_performDocumentApplyAttributesToRange = performDocumentApplyAttributesToRange; 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) function setAttributeOnSelection(attributeName, attributeValue)
{ {
if (!(rep.selStart && rep.selEnd)) return; if (!(rep.selStart && rep.selEnd)) return;
@ -3276,6 +3247,7 @@ function Ace2Inner(){
{ {
return; return;
} }
var lineNum = rep.selStart[0]; var lineNum = rep.selStart[0];
var listType = getLineListType(lineNum); var listType = getLineListType(lineNum);
@ -3347,11 +3319,9 @@ function Ace2Inner(){
} }
} }
if (mods.length > 0) _.each(mods, function(mod){
{ setLineListType.apply(this, mod);
setLineListTypes(mods); });
}
return true; return true;
} }
editorInfo.ace_doIndentOutdent = doIndentOutdent; editorInfo.ace_doIndentOutdent = doIndentOutdent;
@ -4866,26 +4836,30 @@ function Ace2Inner(){
} }
} }
var listAttributeName = 'list';
function getLineListType(lineNum) function getLineListType(lineNum)
{ {
// get "list" attribute of first char of line return documentAttributeManager.getAttributeOnLine(lineNum, listAttributeName)
var aline = rep.alines[lineNum];
if (aline)
{
var opIter = Changeset.opIterator(aline);
if (opIter.hasNext())
{
return Changeset.opAttributeValue(opIter.next(), 'list', rep.apool) || '';
}
}
return '';
} }
function setLineListType(lineNum, listType) function setLineListType(lineNum, listType)
{ {
setLineListTypes([ debugger;
[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){ function renumberList(lineNum){
@ -4932,8 +4906,8 @@ function Ace2Inner(){
} }
else if(curLevel == level) else if(curLevel == level)
{ {
buildKeepRange(builder, loc, (loc = [line, 0])); ChangesetUtils.buildKeepRange(rep, builder, loc, (loc = [line, 0]));
buildKeepRange(builder, loc, (loc = [line, 1]), [ ChangesetUtils.buildKeepRange(rep, builder, loc, (loc = [line, 1]), [
['start', position] ['start', position]
], rep.apool); ], 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) function doInsertList(type)
{ {
@ -5057,7 +4975,10 @@ function Ace2Inner(){
var t = getLineListType(n); var t = getLineListType(n);
mods.push([n, allLinesAreList ? 'indent' + level : (t ? type + level : type + '1')]); mods.push([n, allLinesAreList ? 'indent' + level : (t ? type + level : type + '1')]);
} }
setLineListTypes(mods);
_.each(mods, function(mod){
setLineListType.apply(this, mod);
});
} }
function doInsertUnorderedList(){ function doInsertUnorderedList(){
@ -5478,6 +5399,11 @@ function Ace2Inner(){
} }
} }
// Init documentAttributeManager
documentAttributeManager = new AttributeManager(rep, performDocumentApplyChangeset);
$(document).ready(function(){ $(document).ready(function(){
doc = document; // defined as a var in scope outside doc = document; // defined as a var in scope outside
inCallStack("setup", function() inCallStack("setup", function()