Skip to content

Commit fef503b

Browse files
committed
Only warn about interactive HTML nodes; document HTML interactivity.
see numbas/Numbas#1120
1 parent bf2a0f3 commit fef503b

4 files changed

Lines changed: 29 additions & 5 deletions

File tree

docs/jme-reference.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,12 @@ Some extensions add new data types.
492492

493493
See functions related to :ref:`jme-fns-html`.
494494

495+
.. warning::
496+
497+
Interactive HTML nodes can not be safely copied, so each HTML value should only be used once in a question.
498+
You can mark an HTML node as non-interactive by adding the attribute ``data-interactive="false"`` to it.
499+
Elements created using the built-in HTML functions are automatically marked as non-interactive.
500+
495501
.. data:: expression
496502

497503
A JME sub-expression.

editor/static/js/numbas/numbas-runtime.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3564,6 +3564,9 @@ var TBool = types.TBool = function(b) {
35643564
jme.registerType(TBool,'boolean');
35653565

35663566
/** HTML DOM element.
3567+
*
3568+
* If the element has the attribute `data-interactive="false"` then it can be safely copied and embedded multiple times.
3569+
* If the attribute is not present or has any other value, then it's assumed that it can't be safely copied.
35673570
*
35683571
* @memberof Numbas.jme.types
35693572
* @augments Numbas.jme.token
@@ -3589,6 +3592,11 @@ var THTML = types.THTML = function(html) {
35893592
this.value = Array.from(elem.childNodes);
35903593
this.html = elem.innerHTML;
35913594
}
3595+
THTML.prototype = {
3596+
isInteractive: function() {
3597+
return this.value.some(e => e.getAttribute('data-interactive') !== 'false');
3598+
}
3599+
}
35923600
jme.registerType(THTML,'html');
35933601

35943602
/** List of elements of any data type.
@@ -6314,7 +6322,9 @@ newBuiltin('html',[TString],THTML,null, {
63146322
container.innerHTML = args[0].value;
63156323
var subber = new jme.variables.DOMcontentsubber(scope);
63166324
subber.subvars(container);
6317-
return new THTML(Array.from(container.childNodes));
6325+
var nodes = Array.from(container.childNodes);
6326+
nodes.forEach(node => node.setAttribute('data-interactive', 'false'));
6327+
return new THTML(nodes);
63186328
}
63196329
});
63206330
newBuiltin('isnonemptyhtml',[TString],TBool,function(html) {
@@ -6335,6 +6345,7 @@ newBuiltin('image',[TString, '[number]', '[number]'],THTML,null, {
63356345
}
63366346
var subber = new jme.variables.DOMcontentsubber(scope);
63376347
var element = subber.subvars(img);
6348+
element.setAttribute('data-interactive', 'false');
63386349
return new THTML(element);
63396350
}
63406351
});
@@ -6810,6 +6821,7 @@ newBuiltin('scientificnumberhtml', [TDecimal], THTML, function(n) {
68106821
var bits = math.parseScientific(n.re.toExponential());
68116822
var s = document.createElement('span');
68126823
s.innerHTML = math.niceRealNumber(bits.significand)+' × 10<sup>'+bits.exponent+'</sup>';
6824+
s.setAttribute('data-interactive', 'false');
68136825
return s;
68146826
});
68156827
newBuiltin('scientificnumberhtml', [TNum], THTML, function(n) {
@@ -6819,6 +6831,7 @@ newBuiltin('scientificnumberhtml', [TNum], THTML, function(n) {
68196831
var bits = math.parseScientific(math.niceRealNumber(n,{style:'scientific', scientificStyle:'plain'}));
68206832
var s = document.createElement('span');
68216833
s.innerHTML = math.niceRealNumber(bits.significand)+' × 10<sup>'+bits.exponent+'</sup>';
6834+
s.setAttribute('data-interactive', 'false');
68226835
return s;
68236836
});
68246837

@@ -8378,6 +8391,7 @@ newBuiltin('table',[TList,TList],THTML, null, {
83788391
row.appendChild(td);
83798392
}
83808393
}
8394+
table.setAttribute('data-interactive','false');
83818395
return new THTML(table);
83828396
}
83838397
});
@@ -8396,6 +8410,7 @@ newBuiltin('table',[TList],THTML, null, {
83968410
row.appendChild(td);
83978411
}
83988412
}
8413+
table.setAttribute('data-interactive','false');
83998414
return new THTML(table);
84008415
}
84018416
});
@@ -14110,6 +14125,9 @@ jme.variables = /** @lends Numbas.jme.variables */ {
1411014125
function doToken(token) {
1411114126
if(jme.isType(token,'html')) {
1411214127
token = jme.castToType(token,'html');
14128+
if(!token.isInteractive()) {
14129+
return token.value.map(e => e.cloneNode(true));
14130+
}
1411314131
if(token.value.numbas_embedded) {
1411414132
throw(new Numbas.Error('jme.subvars.html inserted twice'))
1411514133
}

editor/static/js/question/edit.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2431,12 +2431,12 @@ $(document).ready(function() {
24312431
return val.type;
24322432
},this);
24332433

2434-
this.isHTML = ko.pureComputed(function() {
2434+
this.isInteractiveHTML = ko.pureComputed(function() {
24352435
var val = this.value();
24362436
if(!val || this.error()) {
24372437
return false;
24382438
}
2439-
return Numbas.jme.isType(val,'html');
2439+
return Numbas.jme.isType(val,'html') && val.isInteractive();
24402440
},this);
24412441

24422442
this.thisLocked = ko.observable(false);

editor/templates/question/tabs/variables.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,8 @@ <h5>Suggestions</h5>
251251
</li>
252252
</ul>
253253
</div>
254-
<div class="html-type-warning value-error alert alert-danger" data-bind="visible: isHTML">
255-
<p>This variable is an HTML node. HTML nodes can not be relied upon to work correctly when resuming a session - for example, attached event callbacks will be lost, and mathematical notation will likely also break.</p>
254+
<div class="html-type-warning value-error alert alert-danger" data-bind="visible: isInteractiveHTML">
255+
<p>This variable is an interactive HTML node. Interactive HTML nodes can not be relied upon to work correctly when resuming a session - for example, attached event callbacks will be lost, and mathematical notation will likely also break.</p>
256256
<p>If this causes problems, try to create HTML nodes where you use them in content areas, instead of storing them in variables.</p>
257257
</div>
258258
</div>

0 commit comments

Comments
 (0)