Skip to content

Commit f867871

Browse files
committed
Teach <cxx-ref> to watch for new elements being added, which might have a needed ID.
1 parent 0264fbd commit f867871

File tree

3 files changed

+124
-26
lines changed

3 files changed

+124
-26
lines changed

ref.html

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
limitations under the License.
1414
-->
1515

16+
<link rel="import" href="util/get-element-by-id.html"/>
17+
1618
<!--
1719
The 'to' attribute is the id of a <cxx-section> or <cxx-table> element (or subclass).
1820
@@ -27,14 +29,16 @@
2729

2830
<polymer-element name="cxx-ref" attributes="to insynopsis in">
2931
<template
32+
><cxx-get-element-by-id elemId="{{in}}" elem="{{inElem}}"></cxx-get-element-by-id
33+
><template if="{{!in}}"><cxx-get-element-by-id elemId="{{to}}" elem="{{toElem}}"></cxx-get-element-by-id></template
3034
><template id="target_num"
31-
><template if="{{in_elem.index}}">{{in_elem.name}} <span title="{{to}}">&#xa7;{{in_elem.index[to]}}</span></template
35+
><template if="{{inElem.index}}">{{inElem.name}} <span title="{{to}}">&#xa7;{{inElem.index[to]}}</span></template
3236
><template if="{{!in}}"><a title="{{to}}" href="#{{to}}"
33-
><template if="{{to_elem.sec_num}}">{{to_elem.sec_num}}</template
34-
><template if="{{to_elem.table_num}}">Table {{to_elem.table_num}}</template
35-
><template if="{{to_elem.figure_num}}">Figure {{to_elem.figure_num}}</template></a></template
37+
><template if="{{toElem.sec_num}}">{{toElem.sec_num}}</template
38+
><template if="{{toElem.table_num}}">Table {{toElem.table_num}}</template
39+
><template if="{{toElem.figure_num}}">Figure {{toElem.figure_num}}</template></a></template
3640
></template
37-
><template if="{{insynopsis}}">// <i><template bind="" ref="target_num"></template>, {{to_elem.title_element.textContent}}</i></template
41+
><template if="{{insynopsis}}">// <i><template bind="" ref="target_num"></template>, {{toElem.title_element.textContent}}</i></template
3842
><template if="{{!insynopsis}}"><template bind="" ref="target_num"></template></template
3943
></template>
4044
<script src="ref.js"></script>

ref.js

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,43 +19,53 @@ limitations under the License.
1919
insynopsis: false,
2020

2121
observe: {
22-
'in_elem.index': 'indexChanged'
22+
'inElem.index': 'indexChanged'
2323
},
2424
applyAuthorStyles: true,
2525

26-
inChanged: function() {
27-
this.in_elem = document.getElementById(this.in);
28-
if (this.in &&
29-
!(this.in_elem &&
30-
this.in_elem.tagName.toUpperCase() == 'CXX-FOREIGN-INDEX')) {
26+
checkInvariants: function() {
27+
if (this.in) {
28+
if (!this.inElem) {
29+
console.error(this, '.in (', this.in,
30+
') must refer to a <cxx-foreign-index> element.');
31+
}
32+
} else {
33+
if (!this.to) {
34+
console.error('<cxx-ref>', this,
35+
'must have an `in` or `to` attribute.');
36+
} else if (!this.toElem) {
37+
console.error(this, '.to (', this.to,
38+
') must refer to the ID of another element.');
39+
}
40+
}
41+
},
42+
43+
inElemChanged: function() {
44+
if (this.inElem &&
45+
this.inElem.tagName.toUpperCase() != 'CXX-FOREIGN-INDEX') {
3146
console.error('<cxx-ref>.in (', this.in,
3247
') must be a <cxx-foreign-index>; was',
33-
this.in_elem);
48+
this.inElem);
3449
}
3550
},
36-
toChanged: function() {
37-
if (!this.in) {
38-
this.to_elem = document.getElementById(this.to);
39-
if (!this.to_elem) {
40-
console.error("Broken link", this.to, "from", this);
41-
return;
42-
}
51+
toElemChanged: function() {
52+
if (this.toElem) {
4353
this.async(function() {
44-
// Async makes sure the to_elem is upgraded.
45-
if (!(this.to_elem instanceof CxxSectionElement ||
46-
this.to_elem instanceof CxxTableElement ||
47-
this.to_elem instanceof CxxFigureElement)) {
54+
// Async makes sure the toElem is upgraded.
55+
if (!(this.toElem instanceof CxxSectionElement ||
56+
this.toElem instanceof CxxTableElement ||
57+
this.toElem instanceof CxxFigureElement)) {
4858
console.error("Reference from", this,
4959
"refers to non-section, non-table, non-figure element",
50-
this.to_elem);
60+
this.toElem);
5161
}
5262
});
5363
}
5464
},
5565

5666
indexChanged: function() {
57-
if (!(this.to in this.in_elem.index)) {
58-
console.error(this.to, 'not found in', this.in_elem);
67+
if (!(this.to in this.inElem.index)) {
68+
console.error(this.to, 'not found in', this.inElem);
5969
}
6070
}
6171
});

util/get-element-by-id.html

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<!-- Copyright 2014 Google Inc. All rights reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
-->
15+
16+
<!--
17+
Put this element in another element's <template> as:
18+
<cxx-get-element-by-id elemId="{{idToLookUp}}" elem="{{variableToFillIn}}">
19+
</cxx-get-element-by-id>
20+
21+
Then the containing element can define a method `variableToFillInChanged` that
22+
will be called when an element with the right ID is added to the document.
23+
-->
24+
<polymer-element name="cxx-get-element-by-id" attributes="elemId elem">
25+
<script>
26+
(function() {
27+
'use strict';
28+
29+
// Maps IDs to an array of elements waiting for that ID to appear.
30+
var pendingIds = {};
31+
32+
// Watches for all node changes that might make an ID appear, and checks
33+
// the result against the pendingIDs.
34+
var shared_observer = new MutationObserver(function(mutations) {
35+
// We only seem to get a mutation record for the top node of a subtree
36+
// that gets added to the DOM, which means that looking at just the
37+
// addedNodes' IDs misses a bunch of IDs. Instead, we'll just search
38+
// for all the IDs we care about, any time anything changes.
39+
Object.keys(pendingIds).forEach(function(id) {
40+
var elem = document.getElementById(id);
41+
if (elem) {
42+
pendingIds[id].forEach(function(waiting) {
43+
if (waiting.elemId == id)
44+
waiting.elem = elem;
45+
});
46+
delete pendingIds[id];
47+
}
48+
});
49+
50+
if (Object.keys(pendingIds).length == 0)
51+
shared_observer.disconnect();
52+
});
53+
54+
// Adds `to_notify` to the pendingIds list, waiting for `id` to appear,
55+
// and makes sure the MutationObserver is watching.
56+
function watchFor(id, to_notify) {
57+
if (!pendingIds.hasOwnProperty(id))
58+
pendingIds[id] = [];
59+
pendingIds[id].push(to_notify);
60+
61+
shared_observer.observe(document, {
62+
childList: true,
63+
attributes: true,
64+
subtree: true,
65+
attributeFilter: ['id'],
66+
});
67+
}
68+
69+
Polymer('cxx-get-element-by-id', {
70+
elemId: '',
71+
elemIdChanged: function() {
72+
if (!this.elemId) {
73+
this.elem = null;
74+
return;
75+
}
76+
this.elem = document.getElementById(this.elemId);
77+
if (!this.elem) {
78+
watchFor(this.elemId, this);
79+
}
80+
},
81+
});
82+
})();
83+
</script>
84+
</polymer-element>

0 commit comments

Comments
 (0)