This commit is contained in:
Chad Weider 2013-02-09 08:31:47 -08:00
commit 519d0b9c1b
3 changed files with 195 additions and 166 deletions

View file

@ -2839,69 +2839,7 @@ function Ace2Inner(){
function doCreateDomLine(nonEmpty) function doCreateDomLine(nonEmpty)
{ {
if (browser.msie && (!nonEmpty)) return domline.createDomLine(nonEmpty, doesWrap, browser, doc);
{
var result = {
node: null,
appendSpan: noop,
prepareForAdd: noop,
notifyAdded: noop,
clearSpans: noop,
finishUpdate: noop,
lineMarker: 0
};
var lineElem = doc.createElement("div");
result.node = lineElem;
result.notifyAdded = function()
{
// magic -- settng an empty div's innerHTML to the empty string
// keeps it from collapsing. Apparently innerHTML must be set *after*
// adding the node to the DOM.
// Such a div is what IE 6 creates naturally when you make a blank line
// in a document of divs. However, when copy-and-pasted the div will
// contain a space, so we note its emptiness with a property.
lineElem.innerHTML = " "; // Frist we set a value that isnt blank
// a primitive-valued property survives copy-and-paste
setAssoc(lineElem, "shouldBeEmpty", true);
// an object property doesn't
setAssoc(lineElem, "unpasted", {});
lineElem.innerHTML = ""; // Then we make it blank.. New line and no space = Awesome :)
};
var lineClass = 'ace-line';
result.appendSpan = function(txt, cls)
{
if ((!txt) && cls)
{
// gain a whole-line style (currently to show insertion point in CSS)
lineClass = domline.addToLineClass(lineClass, cls);
}
// otherwise, ignore appendSpan, this is an empty line
};
result.clearSpans = function()
{
lineClass = ''; // non-null to cause update
};
var writeClass = function()
{
if (lineClass !== null) lineElem.className = lineClass;
};
result.prepareForAdd = writeClass;
result.finishUpdate = writeClass;
result.getInnerHTML = function()
{
return "";
};
return result;
}
else
{
return domline.createDomLine(nonEmpty, doesWrap, browser, doc);
}
} }
function textify(str) function textify(str)

View file

@ -30,6 +30,41 @@ var Changeset = require('./Changeset');
var hooks = require('./pluginfw/hooks'); var hooks = require('./pluginfw/hooks');
var _ = require('./underscore'); var _ = require('./underscore');
var DOMInterface = {
isNodeText: function(n)
{
return (n.nodeType == 3);
},
nodeTagName: function(n)
{
return n.tagName;
},
nodeValue: function(n)
{
return n.nodeValue;
},
nodeNumChildren: function(n)
{
return n.childNodes.length;
},
nodeChild: function(n, i)
{
return n.childNodes.item(i);
},
nodeProp: function(n, p)
{
return n[p];
},
nodeAttr: function(n, a)
{
return n.getAttribute(a);
},
optNodeInnerHTML: function(n)
{
return n.innerHTML;
}
};
function sanitizeUnicode(s) function sanitizeUnicode(s)
{ {
return UNorm.nfc(s).replace(/[\uffff\ufffe\ufeff\ufdd0-\ufdef\ud800-\udfff]/g, '?'); return UNorm.nfc(s).replace(/[\uffff\ufffe\ufeff\ufdd0-\ufdef\ud800-\udfff]/g, '?');
@ -39,40 +74,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
{ {
browser = browser || {}; browser = browser || {};
var dom = domInterface || { var dom = domInterface || DOMInterface;
isNodeText: function(n)
{
return (n.nodeType == 3);
},
nodeTagName: function(n)
{
return n.tagName;
},
nodeValue: function(n)
{
return n.nodeValue;
},
nodeNumChildren: function(n)
{
return n.childNodes.length;
},
nodeChild: function(n, i)
{
return n.childNodes.item(i);
},
nodeProp: function(n, p)
{
return n[p];
},
nodeAttr: function(n, a)
{
return n.getAttribute(a);
},
optNodeInnerHTML: function(n)
{
return n.innerHTML;
}
};
var _blockElems = { var _blockElems = {
"div": 1, "div": 1,
@ -729,5 +731,6 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
return cc; return cc;
} }
exports.DOMInterface = DOMInterface;
exports.sanitizeUnicode = sanitizeUnicode; exports.sanitizeUnicode = sanitizeUnicode;
exports.makeContentCollector = makeContentCollector; exports.makeContentCollector = makeContentCollector;

View file

@ -32,10 +32,7 @@ var _ = require('./underscore');
var lineAttributeMarker = require('./linestylefilter').lineAttributeMarker; var lineAttributeMarker = require('./linestylefilter').lineAttributeMarker;
var noop = function(){}; var noop = function(){};
function addToLineClass(lineClass, cls)
var domline = {};
domline.addToLineClass = function(lineClass, cls)
{ {
// an "empty span" at any point can be used to add classes to // an "empty span" at any point can be used to add classes to
// the line, using line:className. otherwise, we ignore // the line, using line:className. otherwise, we ignore
@ -51,49 +48,66 @@ domline.addToLineClass = function(lineClass, cls)
return lineClass; return lineClass;
} }
// if "document" is falsy we don't create a DOM node, just function DOMLine(nonEmpty, doesWrap, browser, document) {
// an object with innerHTML and className this.node = null;
domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) this.lineMarker = 0;
{ this.nonEmpty = nonEmpty;
var result = { this.doesWrap = doesWrap;
node: null, this.browser = browser;
appendSpan: noop, this.document = document;
prepareForAdd: noop,
notifyAdded: noop,
clearSpans: noop,
finishUpdate: noop,
lineMarker: 0
};
var browser = (optBrowser || {});
var document = optDocument;
// if "document" is falsy we don't create a DOM node, just
// an object with innerHTML and className
if (document) if (document)
{ {
result.node = document.createElement("div"); this.node = document.createElement("div");
} }
else else
{ {
result.node = { this.node = {
innerHTML: '', innerHTML: '',
className: '' className: ''
}; };
} }
var html = []; this.html = [];
var preHtml = '', this.preHtml = '';
postHtml = ''; this.postHtml = '';
var curHTML = null; this.curHTML = null;
function processSpaces(s) this.lineClass = 'ace-line';
{
return domline.processSpaces(s, doesWrap); // Apparently overridden at the instance level sometimes...
this.notifyAdded = function () {this._notifyAdded()};
this.finishUpdate = function () {this._finishUpdate()};
}
DOMLine.prototype = {};
(function () {
this.perTextNodeProcess = function (s) {
if (this.doesWrap) {
return _.identity(s);
} else {
return this.processSpaces(s);
}
} }
var perTextNodeProcess = (doesWrap ? _.identity : processSpaces); this.perHtmlLineProcess = function (s) {
var perHtmlLineProcess = (doesWrap ? processSpaces : _.identity); if (this.doesWrap) {
var lineClass = 'ace-line'; return this.processSpaces(s);
result.appendSpan = function(txt, cls) } else {
return _.identity(s);
}
}
this.processSpaces = function(s)
{
return processSpaces(s, this.doesWrap);
}
this._notifyAdded = function () {};
this.appendSpan = function(txt, cls)
{ {
var processedMarker = false; var processedMarker = false;
// Handle lineAttributeMarker, if present // Handle lineAttributeMarker, if present
@ -109,30 +123,30 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
{ {
if(listType.indexOf("number") < 0) if(listType.indexOf("number") < 0)
{ {
preHtml = '<ul class="list-' + Security.escapeHTMLAttribute(listType) + '"><li>'; this.preHtml = '<ul class="list-' + Security.escapeHTMLAttribute(listType) + '"><li>';
postHtml = '</li></ul>'; this.postHtml = '</li></ul>';
} }
else else
{ {
preHtml = '<ol '+start+' class="list-' + Security.escapeHTMLAttribute(listType) + '"><li>'; this.preHtml = '<ol '+start+' class="list-' + Security.escapeHTMLAttribute(listType) + '"><li>';
postHtml = '</li></ol>'; this.postHtml = '</li></ol>';
} }
} }
processedMarker = true; processedMarker = true;
} }
_.map(hooks.callAll("aceDomLineProcessLineAttributes", { _.map(hooks.callAll("aceDomLineProcessLineAttributes", {
domline: domline, domline: exports,
cls: cls cls: cls
}), function(modifier) }), function(modifier)
{ {
preHtml += modifier.preHtml; this.preHtml += modifier.preHtml;
postHtml += modifier.postHtml; this.postHtml += modifier.postHtml;
processedMarker |= modifier.processedMarker; processedMarker |= modifier.processedMarker;
}); });
if( processedMarker ){ if( processedMarker ){
result.lineMarker += txt.length; this.lineMarker += txt.length;
return; // don't append any text return; // don't append any text
} }
@ -162,7 +176,7 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
var extraCloseTags = ""; var extraCloseTags = "";
_.map(hooks.callAll("aceCreateDomLine", { _.map(hooks.callAll("aceCreateDomLine", {
domline: domline, domline: exports,
cls: cls cls: cls
}), function(modifier) }), function(modifier)
{ {
@ -173,7 +187,7 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
if ((!txt) && cls) if ((!txt) && cls)
{ {
lineClass = domline.addToLineClass(lineClass, cls); this.lineClass = addToLineClass(this.lineClass, cls);
} }
else if (txt) else if (txt)
{ {
@ -193,57 +207,127 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
simpleTags.reverse(); simpleTags.reverse();
extraCloseTags = '</' + simpleTags.join('></') + '>' + extraCloseTags; extraCloseTags = '</' + simpleTags.join('></') + '>' + extraCloseTags;
} }
html.push('<span class="', Security.escapeHTMLAttribute(cls || ''), '">', extraOpenTags, perTextNodeProcess(Security.escapeHTML(txt)), extraCloseTags, '</span>'); this.html.push('<span class="', Security.escapeHTMLAttribute(cls || ''), '">', extraOpenTags, this.perTextNodeProcess(Security.escapeHTML(txt)), extraCloseTags, '</span>');
} }
}; };
result.clearSpans = function() this.clearSpans = function()
{ {
html = []; this.html = [];
lineClass = ''; // non-null to cause update this.lineClass = ''; // non-null to cause update
result.lineMarker = 0; this.lineMarker = 0;
}; };
function writeHTML() this._writeHTML = function ()
{ {
var newHTML = perHtmlLineProcess(html.join('')); var newHTML = this.perHtmlLineProcess(this.html.join(''));
if (!newHTML) if (!newHTML)
{ {
if ((!document) || (!optBrowser)) if ((!this.document) || (!this.browser))
{ {
newHTML += '&nbsp;'; newHTML += '&nbsp;';
} }
else if (!browser.msie) else if (!(this.browser || {}).msie)
{ {
newHTML += '<br/>'; newHTML += '<br/>';
} }
} }
if (nonEmpty) if (this.nonEmpty)
{ {
newHTML = (preHtml || '') + newHTML + (postHtml || ''); newHTML = (this.preHtml || '') + newHTML + (this.postHtml || '');
} }
html = preHtml = postHtml = ''; // free memory this.html = this.preHtml = this.postHtml = ''; // free memory
if (newHTML !== curHTML) if (newHTML !== this.curHTML)
{ {
curHTML = newHTML; this.curHTML = newHTML;
result.node.innerHTML = curHTML; this.node.innerHTML = this.curHTML;
} }
if (lineClass !== null) result.node.className = lineClass; if (this.lineClass !== null) this.node.className = this.lineClass;
hooks.callAll("acePostWriteDomLineHTML", { hooks.callAll("acePostWriteDomLineHTML", {
node: result.node node: this.node
}); });
} }
result.prepareForAdd = writeHTML; this.prepareForAdd = function () {
result.finishUpdate = writeHTML; return this._writeHTML();
result.getInnerHTML = function() };
this._finishUpdate = function () {
return this._writeHTML();
};
this.getInnerHTML = function()
{ {
return curHTML || ''; return this.curHTML || '';
}; };
return result; }).call(DOMLine.prototype);
function SpecialIEDOMLine(nonEmpty, doesWrap, browser, document) {
this.node = null;
this.lineMarker = 0;
this.node = document.createElement("div")
this.lineClass = 'ace-line';
// Apparently overridden at the instance level sometimes...
this.notifyAdded = function () {this._notifyAdded()};
this.finishUpdate = function () {this._finishUpdate()};;
}
SpecialIEDOMLine.prototype = {};
(function () {
this._notifyAdded = function () {
// magic -- settng an empty div's innerHTML to the empty string
// keeps it from collapsing. Apparently innerHTML must be set *after*
// adding the node to the DOM.
// Such a div is what IE 6 creates naturally when you make a blank line
// in a document of divs. However, when copy-and-pasted the div will
// contain a space, so we note its emptiness with a property.
this.node.innerHTML = " "; // Frist we set a value that isnt blank
// a primitive-valued property survives copy-and-paste
Ace2Common.setAssoc(lineElem, "shouldBeEmpty", true);
// an object property doesn't
Ace2Common.setAssoc(lineElem, "unpasted", {});
this.node.innerHTML = ""; // Then we make it blank.. New line and no space = Awesome :)
}
this.appendSpan = function (txt, cls) {
if ((!txt) && cls) {
// gain a whole-line style (currently to show insertion point in CSS)
this.lineClass = addToLineClass(lineClass, cls);
}
// otherwise, ignore appendSpan, this is an empty line
}
this.clearSpans = function () {
this.lineClass = ''; // non-null to cause update
}
this._writeClass = function () {
if (this.lineClass !== null) {
this.node.className = this.lineClass;
}
}
this.prepareForAdd = function () {
return this._writeClass;
}
this._finishUpdate = function () {
return this._writeClass;
}
this.getInnerHTML = function () {
return "";
}
}).call(SpecialIEDOMLine.prototype);
function createDomLine(nonEmpty, doesWrap, browser, document)
{
if (browser.msie && (!nonEmpty)) {
// TODO: Why is this necessary?
return new SpecialIEDOMLine(nonEmpty, doesWrap, browser, document);
} else {
return new DOMLine(nonEmpty, doesWrap, browser, document);
}
}; };
domline.processSpaces = function(s, doesWrap) function processSpaces(s, doesWrap)
{ {
if (s.indexOf("<") < 0 && !doesWrap) if (s.indexOf("<") < 0 && !doesWrap)
{ {
@ -305,4 +389,8 @@ domline.processSpaces = function(s, doesWrap)
return parts.join(''); return parts.join('');
}; };
exports.domline = domline; exports.addToLineClass = addToLineClass;
exports.createDomLine = createDomLine;
exports.processSpaces = processSpaces;
exports.domline = exports; // For compatibility