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