collapsible.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741
  1. /*************************************************************
  2. *
  3. * [Contrib]/a11y/collapsible.js
  4. *
  5. * A filter to add maction elements to the enriched MathML for parts that
  6. * can be collapsed. We determine this based on a "complexity" value and
  7. * collapse those terms that exceed a given complexity.
  8. *
  9. * The parameters controlling the complexity measure still need work.
  10. *
  11. * ---------------------------------------------------------------------
  12. *
  13. * Copyright (c) 2016-2017 The MathJax Consortium
  14. *
  15. * Licensed under the Apache License, Version 2.0 (the "License");
  16. * you may not use this file except in compliance with the License.
  17. * You may obtain a copy of the License at
  18. *
  19. * http://www.apache.org/licenses/LICENSE-2.0
  20. *
  21. * Unless required by applicable law or agreed to in writing, software
  22. * distributed under the License is distributed on an "AS IS" BASIS,
  23. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  24. * See the License for the specific language governing permissions and
  25. * limitations under the License.
  26. */
  27. (function (HUB) {
  28. var MML;
  29. var SETTINGS = HUB.config.menuSettings;
  30. var COOKIE = {}; // replaced when menu is available
  31. var NOCOLLAPSE = 10000000; // really big complexity
  32. var COMPLEXATTR = "data-semantic-complexity";
  33. //
  34. // Set up the a11y path,if it isn't already in place
  35. //
  36. var PATH = MathJax.Ajax.config.path;
  37. if (!PATH.a11y) PATH.a11y = HUB.config.root + "/extensions/a11y";
  38. var Collapsible = MathJax.Extension.collapsible = {
  39. version: "1.2.3",
  40. config: HUB.CombineConfig("collapsible",{
  41. disabled: false
  42. }),
  43. dependents: [], // the extensions that depend on this one
  44. COMPLEXATTR: COMPLEXATTR, // attribute name for the complexity value
  45. /*****************************************************************/
  46. //
  47. // Complexity values to use for different structures
  48. //
  49. COMPLEXITY: {
  50. TEXT: .5, // each character of a token element adds this to complexity
  51. TOKEN: .5, // each toekn element gets this additional complexity
  52. CHILD: 1, // child nodes add this to their parent node's complexity
  53. SCRIPT: .8, // script elements reduce their complexity by this factor
  54. SQRT: 2, // sqrt adds this extra complexity
  55. SUBSUP: 2, // sub-sup adds this extra complexity
  56. UNDEROVER: 2, // under-over adds this extra complexity
  57. FRACTION: 2, // fractions add this extra complexity
  58. ACTION: 2, // maction adds this extra complexity
  59. PHANTOM: 0, // mphantom makes complexity 0?
  60. XML: 2, // Can't really measure complexity of annotation-xml, so punt
  61. GLYPH: 2 // Can't really measure complexity of mglyph, to punt
  62. },
  63. //
  64. // These are the cut-off complexity values for when
  65. // the structure should collapse
  66. //
  67. COLLAPSE: {
  68. identifier: 3,
  69. number: 3,
  70. text: 10,
  71. infixop: 15,
  72. relseq: 15,
  73. multirel: 15,
  74. fenced: 18,
  75. bigop: 20,
  76. integral: 20,
  77. fraction: 12,
  78. sqrt: 9,
  79. root: 12,
  80. vector: 15,
  81. matrix: 15,
  82. cases: 15,
  83. superscript: 9,
  84. subscript: 9,
  85. subsup: 9,
  86. punctuated: {
  87. endpunct: NOCOLLAPSE,
  88. startpunct: NOCOLLAPSE,
  89. value: 12
  90. }
  91. },
  92. //
  93. // These are the characters to use for the various collapsed elements
  94. // (if an object, then semantic-role is used to get the character
  95. // from the object)
  96. //
  97. MARKER: {
  98. identifier: "x",
  99. number: "#",
  100. text: "...",
  101. appl: {
  102. "limit function": "lim",
  103. value: "f()"
  104. },
  105. fraction: "/",
  106. sqrt: "\u221A",
  107. root: "\u221A",
  108. superscript: "\u25FD\u02D9",
  109. subscript: "\u25FD.",
  110. subsup:"\u25FD:",
  111. vector: {
  112. binomial: "(:)",
  113. determinant: "|:|",
  114. value: "\u27E8:\u27E9"
  115. },
  116. matrix: {
  117. squarematrix: "[::]",
  118. rowvector: "\u27E8\u22EF\u27E9",
  119. columnvector: "\u27E8\u22EE\u27E9",
  120. determinant: "|::|",
  121. value: "(::)"
  122. },
  123. cases: "{:",
  124. infixop: {
  125. addition: "+",
  126. subtraction: "\u2212",
  127. multiplication: "\u22C5",
  128. implicit: "\u22C5",
  129. value: "+"
  130. },
  131. punctuated: {
  132. text: "...",
  133. value: ","
  134. }
  135. },
  136. /*****************************************************************/
  137. Enable: function (update,menu) {
  138. SETTINGS.collapsible = true;
  139. if (menu) COOKIE.collapsible = true;
  140. this.config.disabled = false;
  141. MathJax.Extension["semantic-enrich"].Enable(false,menu);
  142. if (update) HUB.Queue(["Reprocess",HUB]);
  143. },
  144. Disable: function (update,menu) {
  145. SETTINGS.collapsible = false;
  146. if (menu) COOKIE.collapsible = false;
  147. this.config.disabled = true;
  148. for (var i = this.dependents.length-1; i >= 0; i--) {
  149. var dependent = this.dependents[i];
  150. if (dependent.Disable) dependent.Disable(false,menu);
  151. }
  152. if (update) HUB.Queue(["Reprocess",HUB]);
  153. },
  154. //
  155. // Register a dependent
  156. //
  157. Dependent: function (extension) {
  158. this.dependents.push(extension);
  159. },
  160. Startup: function () {
  161. MML = MathJax.ElementJax.mml;
  162. //
  163. // Inform semantic-enrich extension that we are a dependent
  164. //
  165. var Enrich = MathJax.Extension["semantic-enrich"];
  166. if (Enrich) Enrich.Dependent(this);
  167. //
  168. // Add the filter into the post-input hooks (priority 100, so other
  169. // hooks run first, in particular, the enrichment hook).
  170. //
  171. HUB.postInputHooks.Add(["Filter",Collapsible],100);
  172. },
  173. //
  174. // The main filter: add mactions for collapsing the math.
  175. //
  176. Filter: function (jax,id,script) {
  177. if (!jax.enriched || this.config.disabled) return;
  178. jax.root = jax.root.Collapse();
  179. jax.root.inputID = script.id;
  180. },
  181. /*****************************************************************/
  182. //
  183. // Create a marker from a given string of characters
  184. // (several possibilities are commented out)
  185. //
  186. // Marker: function (c) {return MML.mtext("\u25C3"+c+"\u25B9").With({mathcolor:"blue",attr:{},attrNames:[]})},
  187. // Marker: function (c) {return MML.mtext("\u25B9"+c+"\u25C3").With({mathcolor:"blue",attr:{},attrNames:[]})},
  188. Marker: function (c) {return MML.mtext("\u25C2"+c+"\u25B8").With({mathcolor:"blue",attr:{},attrNames:[]})},
  189. // Marker: function (c) {return MML.mtext("\u25B8"+c+"\u25C2").With({mathcolor:"blue",attr:{},attrNames:[]})},
  190. // Marker: function (c) {return MML.mtext("\u27EA"+c+"\u27EB").With({mathcolor:"blue",attr:{},attrNames:[]})},
  191. /*****************************************************************/
  192. //
  193. // Make a collapsible element using maction that contains
  194. // an appropriate marker, and the expanded MathML.
  195. // If the MathML is a <math> node, make an mrow to use instead,
  196. // and move the semantic data to it (I guess it would have been
  197. // easier to have had that done initially, oh well).
  198. //
  199. MakeAction: function (collapse,mml) {
  200. var maction = MML.maction(collapse).With({
  201. id:this.getActionID(), actiontype:"toggle",
  202. complexity:collapse.getComplexity(), collapsible:true,
  203. attrNames:["id","actiontype","selection",COMPLEXATTR], attr:{}, selection:2
  204. });
  205. maction.attr[COMPLEXATTR] = maction.complexity;
  206. if (mml.type === "math") {
  207. var mrow = MML.mrow().With({
  208. complexity: mml.complexity,
  209. attrNames: [], attr: {}
  210. });
  211. mrow.Append.apply(mrow,mml.data);
  212. for (var i = mml.attrNames.length-1, name; name = mml.attrNames[i]; i--) {
  213. if (name.substr(0,14) === "data-semantic-") {
  214. mrow.attr[name] = mml.attr[name];
  215. mrow.attrNames.push(name);
  216. delete mml.attr[name];
  217. mml.attrNames.splice(i,1);
  218. }
  219. }
  220. mrow.complexity = mml.complexity;
  221. maction.Append(mrow); mml.data = []; mml.Append(maction);
  222. mml.complexity = maction.complexity; maction = mml;
  223. } else {
  224. maction.Append(mml);
  225. }
  226. return maction;
  227. },
  228. actionID: 1,
  229. getActionID: function () {return "MJX-Collapse-"+this.actionID++},
  230. /*****************************************************************/
  231. //
  232. // If there is a specific routine for the type, do that, otherwise
  233. // check if there is a complexity cut-off and marker for this type.
  234. // If so, check if the complexity exceeds the cut off, and
  235. // collapse using the appropriate marker for the type
  236. // Return the (possibly modified) MathML
  237. //
  238. Collapse: function (mml) {
  239. mml.getComplexity();
  240. var type = (mml.attr||{})["data-semantic-type"];
  241. if (type) {
  242. if (this["Collapse_"+type]) mml = (this["Collapse_"+type])(mml);
  243. else if (this.COLLAPSE[type] && this.MARKER[type]) {
  244. var role = mml.attr["data-semantic-role"];
  245. var complexity = this.COLLAPSE[type];
  246. if (typeof(complexity) !== "number") complexity = complexity[role] || complexity.value;
  247. if (mml.complexity > complexity) {
  248. var marker = this.MARKER[type];
  249. if (typeof(marker) !== "string") marker = marker[role] || marker.value;
  250. mml = this.MakeAction(this.Marker(marker),mml);
  251. }
  252. }
  253. }
  254. return mml;
  255. },
  256. //
  257. // If a parent is going to be collapsible, if can call this
  258. // to put back a collapsed child (rather than have too many
  259. // nested collapsings)
  260. //
  261. UncollapseChild: function (mml,n,m) {
  262. if (m == null) m = 1;
  263. if (this.SplitAttribute(mml,"children").length === m) {
  264. var child = (mml.data.length === 1 && mml.data[0].inferred ? mml.data[0] : mml);
  265. if (child && child.data[n] && child.data[n].collapsible) {
  266. child.SetData(n,child.data[n].data[1]);
  267. mml.complexity = child.complexity = null; mml.getComplexity();
  268. return 1;
  269. }
  270. }
  271. return 0;
  272. },
  273. //
  274. // Locate child node and return its text
  275. //
  276. FindChildText: function (mml,id) {
  277. var child = this.FindChild(mml,id);
  278. return (child ? (child.CoreMO()||child).data.join("") : "?");
  279. },
  280. FindChild: function (mml,id) {
  281. if (mml) {
  282. if (mml.attr && mml.attr["data-semantic-id"] === id) return mml;
  283. if (!mml.isToken) {
  284. for (var i = 0, m = mml.data.length; i < m; i++) {
  285. var child = this.FindChild(mml.data[i],id);
  286. if (child) return child;
  287. }
  288. }
  289. }
  290. return null;
  291. },
  292. //
  293. // Split a data attribute at commas
  294. //
  295. SplitAttribute: function (mml,id) {
  296. return (mml.attr["data-semantic-"+id]||"").split(/,/);
  297. },
  298. /*****************************************************************/
  299. /*
  300. * These routines implement the collapsing of the various semantic types
  301. */
  302. //
  303. // For fenced elements, if the contents are collapsed,
  304. // collapse the fence instead.
  305. //
  306. Collapse_fenced: function (mml) {
  307. this.UncollapseChild(mml,1);
  308. if (mml.complexity > this.COLLAPSE.fenced) {
  309. if (mml.attr["data-semantic-role"] === "leftright") {
  310. var marker = mml.data[0].data.join("") + mml.data[mml.data.length-1].data.join("");
  311. mml = this.MakeAction(this.Marker(marker),mml);
  312. }
  313. }
  314. return mml;
  315. },
  316. //
  317. // Collapse function applications if the argument is collapsed
  318. // (Handle role="limit function" a bit better?)
  319. //
  320. Collapse_appl: function (mml) {
  321. if (this.UncollapseChild(mml,2,2)) {
  322. var marker = this.MARKER.appl;
  323. marker = marker[mml.attr["data-semantic-role"]] || marker.value;
  324. mml = this.MakeAction(this.Marker(marker),mml);
  325. }
  326. return mml;
  327. },
  328. //
  329. // For sqrt elements, if the contents are collapsed,
  330. // collapse the sqrt instead.
  331. //
  332. Collapse_sqrt: function (mml) {
  333. this.UncollapseChild(mml,0);
  334. if (mml.complexity > this.COLLAPSE.sqrt)
  335. mml = this.MakeAction(this.Marker(this.MARKER.sqrt),mml);
  336. return mml;
  337. },
  338. Collapse_root: function (mml) {
  339. this.UncollapseChild(mml,0);
  340. if (mml.complexity > this.COLLAPSE.sqrt)
  341. mml = this.MakeAction(this.Marker(this.MARKER.sqrt),mml);
  342. return mml;
  343. },
  344. //
  345. // For enclose, include enclosure in collapsed child, if any
  346. //
  347. Collapse_enclose: function (mml) {
  348. if (this.SplitAttribute(mml,"children").length === 1) {
  349. var child = (mml.data.length === 1 && mml.data[0].inferred ? mml.data[0] : mml);
  350. if (child.data[0] && child.data[0].collapsible) {
  351. //
  352. // Move menclose into the maction element
  353. //
  354. var maction = child.data[0];
  355. child.SetData(0,maction.data[1]);
  356. maction.SetData(1,mml);
  357. mml = maction;
  358. }
  359. }
  360. return mml;
  361. },
  362. //
  363. // For bigops, get the character to use from the largeop at its core.
  364. //
  365. Collapse_bigop: function (mml) {
  366. if (mml.complexity > this.COLLAPSE.bigop || mml.data[0].type !== "mo") {
  367. var id = this.SplitAttribute(mml,"content").pop();
  368. var op = Collapsible.FindChildText(mml,id);
  369. mml = this.MakeAction(this.Marker(op),mml);
  370. }
  371. return mml;
  372. },
  373. Collapse_integral: function (mml) {
  374. if (mml.complexity > this.COLLAPSE.integral || mml.data[0].type !== "mo") {
  375. var id = this.SplitAttribute(mml,"content")[0];
  376. var op = Collapsible.FindChildText(mml,id);
  377. mml = this.MakeAction(this.Marker(op),mml);
  378. }
  379. return mml;
  380. },
  381. //
  382. // For multirel and relseq, use proper symbol
  383. //
  384. Collapse_relseq: function (mml) {
  385. if (mml.complexity > this.COLLAPSE.relseq) {
  386. var content = this.SplitAttribute(mml,"content");
  387. var marker = Collapsible.FindChildText(mml,content[0]);
  388. if (content.length > 1) marker += "\u22EF";
  389. mml = this.MakeAction(this.Marker(marker),mml);
  390. }
  391. return mml;
  392. },
  393. Collapse_multirel: function (mml) {
  394. if (mml.complexity > this.COLLAPSE.multirel) {
  395. var content = this.SplitAttribute(mml,"content");
  396. var marker = Collapsible.FindChildText(mml,content[0]) + "\u22EF";
  397. mml = this.MakeAction(this.Marker(marker),mml);
  398. }
  399. return mml;
  400. },
  401. //
  402. // Include super- and subscripts into a collapsed base
  403. //
  404. Collapse_superscript: function (mml) {
  405. this.UncollapseChild(mml,0,2);
  406. if (mml.complexity > this.COLLAPSE.superscript)
  407. mml = this.MakeAction(this.Marker(this.MARKER.superscript),mml);
  408. return mml;
  409. },
  410. Collapse_subscript: function (mml) {
  411. this.UncollapseChild(mml,0,2);
  412. if (mml.complexity > this.COLLAPSE.subscript)
  413. mml = this.MakeAction(this.Marker(this.MARKER.subscript),mml);
  414. return mml;
  415. },
  416. Collapse_subsup: function (mml) {
  417. this.UncollapseChild(mml,0,3);
  418. if (mml.complexity > this.COLLAPSE.subsup)
  419. mml = this.MakeAction(this.Marker(this.MARKER.subsup),mml);
  420. return mml;
  421. }
  422. };
  423. HUB.Register.StartupHook("End Extensions", function () {
  424. if (SETTINGS.collapsible == null) {
  425. SETTINGS.collapsible = !Collapsible.config.disabled;
  426. } else {
  427. Collapsible.config.disabled = !SETTINGS.collapsible;
  428. }
  429. HUB.Register.StartupHook("MathMenu Ready", function () {
  430. COOKIE = MathJax.Menu.cookie;
  431. var Switch = function(menu) {
  432. Collapsible[SETTINGS.collapsible ? "Enable" : "Disable"](true,true);
  433. MathJax.Menu.saveCookie();
  434. };
  435. var ITEM = MathJax.Menu.ITEM,
  436. MENU = MathJax.Menu.menu;
  437. var menu = ITEM.CHECKBOX(
  438. ['CollapsibleMath','Collapsible Math'], 'collapsible', {action: Switch}
  439. );
  440. var submenu = (MENU.FindId('Accessibility')||{}).submenu, index;
  441. if (submenu) {
  442. index = submenu.IndexOfId('CollapsibleMath');
  443. if (index !== null) {
  444. submenu.items[index] = menu;
  445. } else {
  446. submenu.items.push(ITEM.RULE(),menu);
  447. }
  448. } else {
  449. index = MENU.IndexOfId('About');
  450. MENU.items.splice(index,0,menu,ITEM.RULE());
  451. }
  452. },15); // before explorer extension
  453. },15);
  454. })(MathJax.Hub);
  455. /*****************************************************************/
  456. /*
  457. * Add Collapse() and getComplexity() methods to the internal
  458. * MathML elements, and override these in the elements that need
  459. * special handling.
  460. */
  461. MathJax.Ajax.Require("[a11y]/semantic-enrich.js");
  462. MathJax.Hub.Register.StartupHook("Semantic Enrich Ready", function () {
  463. var MML = MathJax.ElementJax.mml,
  464. Collapsible = MathJax.Extension.collapsible,
  465. COMPLEXITY = Collapsible.COMPLEXITY,
  466. COMPLEXATTR = Collapsible.COMPLEXATTR;
  467. Collapsible.Startup(); // Initialize the collapsing process
  468. MML.mbase.Augment({
  469. //
  470. // Just call the Collapse() method from the extension by default
  471. // (but can be overridden)
  472. //
  473. Collapse: function () {return Collapsible.Collapse(this)},
  474. //
  475. // If we don't have a cached complexity value,
  476. // For token elements, just use the data length,
  477. // Otherwise
  478. // Add up the complexities of the (collapsed) children
  479. // and add the child complexity based on the number of children
  480. // Cache the complexity result
  481. // return the complexity
  482. //
  483. getComplexity: function () {
  484. if (this.complexity == null) {
  485. var complexity = 0;
  486. if (this.isToken) {
  487. complexity = COMPLEXITY.TEXT * this.data.join("").length + COMPLEXITY.TOKEN;
  488. } else {
  489. for (var i = 0, m = this.data.length; i < m; i++) {
  490. if (this.data[i]) {
  491. this.SetData(i,this.data[i].Collapse());
  492. complexity += this.data[i].complexity;
  493. }
  494. }
  495. if (m > 1) complexity += m * COMPLEXITY.CHILD;
  496. }
  497. if (this.attrNames && !("complexity" in this)) this.attrNames.push(COMPLEXATTR);
  498. if (this.attr) this.attr[COMPLEXATTR] = complexity;
  499. this.complexity = complexity;
  500. }
  501. return this.complexity;
  502. },
  503. reportComplexity: function () {
  504. if (this.attr && this.attrNames && !(COMPLEXATTR in this.attr)) {
  505. this.attrNames.push(COMPLEXATTR);
  506. this.attr[COMPLEXATTR] = this.complexity;
  507. }
  508. }
  509. });
  510. //
  511. // For fractions, scale the complexity of the parts, and add
  512. // a complexity for fractions.
  513. //
  514. MML.mfrac.Augment({
  515. getComplexity: function () {
  516. if (this.complexity == null) {
  517. this.SUPER(arguments).getComplexity.call(this);
  518. this.complexity *= COMPLEXITY.SCRIPT;
  519. this.complexity += COMPLEXITY.FRACTION;
  520. this.attr[COMPLEXATTR] = this.complexity;
  521. }
  522. return this.complexity;
  523. }
  524. });
  525. //
  526. // Square roots add extra complexity
  527. //
  528. MML.msqrt.Augment({
  529. getComplexity: function () {
  530. if (this.complexity == null) {
  531. this.SUPER(arguments).getComplexity.call(this);
  532. this.complexity += COMPLEXITY.SQRT;
  533. this.attr[COMPLEXATTR] = this.complexity;
  534. }
  535. return this.complexity;
  536. }
  537. });
  538. MML.mroot.Augment({
  539. getComplexity: function () {
  540. if (this.complexity == null) {
  541. this.SUPER(arguments).getComplexity.call(this);
  542. this.complexity -= (1-COMPLEXITY.SCRIPT) * this.data[1].getComplexity();
  543. this.complexity += COMPLEXITY.SQRT;
  544. this.attr[COMPLEXATTR] = this.complexity;
  545. }
  546. return this.complexity;
  547. }
  548. });
  549. //
  550. // For msubsup, use the script complexity factor,
  551. // take the maximum of the scripts,
  552. // and add the sub-sup complexity
  553. //
  554. MML.msubsup.Augment({
  555. getComplexity: function () {
  556. if (this.complexity == null) {
  557. var C = 0;
  558. if (this.data[this.sub]) C = this.data[this.sub].getComplexity() + COMPLEXITY.CHILD;
  559. if (this.data[this.sup]) C = Math.max(this.data[this.sup].getComplexity(),C);
  560. C *= COMPLEXITY.SCRIPT;
  561. if (this.data[this.sub]) C += COMPLEXITY.CHILD;
  562. if (this.data[this.sup]) C += COMPLEXITY.CHILD;
  563. if (this.data[this.base]) C += this.data[this.base].getComplexity() + COMPLEXITY.CHILD;
  564. this.complexity = C + COMPLEXITY.SUBSUP;
  565. this.reportComplexity();
  566. }
  567. return this.complexity;
  568. }
  569. });
  570. //
  571. // For munderover, use the script complexity factor,
  572. // take the maximum of the scripts and the base,
  573. // and add the under-over complexity
  574. //
  575. MML.munderover.Augment({
  576. getComplexity: function () {
  577. if (this.complexity == null) {
  578. var C = 0;
  579. if (this.data[this.sub]) C = this.data[this.sub].getComplexity() + COMPLEXITY.CHILD;
  580. if (this.data[this.sup]) C = Math.max(this.data[this.sup].getComplexity(),C);
  581. C *= COMPLEXITY.SCRIPT;
  582. if (this.data[this.base]) C = Math.max(this.data[this.base].getComplexity(),C);
  583. if (this.data[this.sub]) C += COMPLEXITY.CHILD;
  584. if (this.data[this.sup]) C += COMPLEXITY.CHILD;
  585. if (this.data[this.base]) C += COMPLEXITY.CHILD;
  586. this.complexity = C + COMPLEXITY.UNDEROVER;
  587. this.reportComplexity();
  588. }
  589. return this.complexity;
  590. }
  591. });
  592. //
  593. // For mphantom, complexity is 0?
  594. //
  595. MML.mphantom.Augment({
  596. getComplexity: function () {
  597. this.complexity = COMPLEXITY.PHANTOM;
  598. this.reportComplexity();
  599. return this.complexity;
  600. }
  601. });
  602. //
  603. // For ms, add width of quotes. Don't cache the result, since
  604. // mstyle above it could affect the result.
  605. //
  606. MML.ms.Augment({
  607. getComplexity: function () {
  608. this.SUPER(arguments).getComplexity.call(this);
  609. this.complexity += this.Get("lquote").length * COMPLEXITY.TEXT;
  610. this.complexity += this.Get("rquote").length * COMPLEXITY.TEXT;
  611. this.attr[COMPLEXATTR] = this.complexity;
  612. return this.complexity;
  613. }
  614. });
  615. // ### FIXME: getComplexity special cases:
  616. // mtable, mfenced, mmultiscript
  617. //
  618. // For menclose, complexity goes up by a fixed amount
  619. //
  620. MML.menclose.Augment({
  621. getComplexity: function () {
  622. if (this.complexity == null) {
  623. this.SUPER(arguments).getComplexity.call(this);
  624. this.complexity += COMPLEXITY.ACTION;
  625. this.attr[COMPLEXATTR] = this.complexity;
  626. }
  627. return this.complexity;
  628. }
  629. });
  630. //
  631. // For maction, complexity is complexity of selected element
  632. //
  633. MML.maction.Augment({
  634. getComplexity: function () {
  635. //
  636. // Don't cache it, since selection can change.
  637. //
  638. this.complexity = (this.collapsible ? this.data[0] : this.selected()).getComplexity();
  639. this.reportComplexity();
  640. return this.complexity;
  641. }
  642. });
  643. //
  644. // For semantics, complexity is complexity of first child
  645. //
  646. MML.semantics.Augment({
  647. getComplexity: function () {
  648. if (this.complexity == null) {
  649. this.complexity = (this.data[0] ? this.data[0].getComplexity() : 0);
  650. this.reportComplexity();
  651. }
  652. return this.complexity;
  653. }
  654. });
  655. //
  656. // Use fixed complexity, since we can't really measure it
  657. //
  658. MML["annotation-xml"].Augment({
  659. getComplexity: function () {
  660. this.complexity = COMPLEXITY.XML;
  661. this.reportComplexity();
  662. return this.complexity;
  663. }
  664. });
  665. MML.annotation.Augment({
  666. getComplexity: function () {
  667. this.complexity = COMPLEXITY.XML;
  668. this.reportComplexity();
  669. return this.complexity;
  670. }
  671. });
  672. //
  673. // Use fixed complexity, since we can't really measure it
  674. //
  675. MML.mglyph.Augment({
  676. getComplexity: function () {
  677. this.complexity = COMPLEXITY.GLYPH;
  678. this.reportComplexity();
  679. return this.complexity;
  680. }
  681. });
  682. //
  683. // Signal that we are ready
  684. //
  685. MathJax.Hub.Startup.signal.Post("Collapsible Ready");
  686. MathJax.Ajax.loadComplete("[a11y]/collapsible.js");
  687. });