added AttributeManager, ChangesetUtils
This commit is contained in:
parent
6c647baa02
commit
23cda77b65
|
@ -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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
@ -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]);
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue