ModeConnections.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. var m = require('mithril');
  2. // https://github.com/markdown-it/markdown-it
  3. var markdown = require('markdown-it')()
  4. .use(require('markdown-it-footnote'));
  5. var _dbs = require('./dbs');
  6. var _Header = require('./header');
  7. var _Footer = require('./footer');
  8. var _Ui = require('./ui.js');
  9. // ____ __
  10. // / __ \____ / /_
  11. // / / / / __ \/ __/
  12. // / /_/ / /_/ / /_
  13. // /_____/\____/\__/
  14. var _Dot = {
  15. id:null,
  16. dottype:null,
  17. type:'',
  18. title:'',
  19. breadcrumb:'',
  20. text:'',
  21. summary:'',
  22. active:true,
  23. opened:0,
  24. nested:false,
  25. links:null,
  26. parents:[],
  27. lang:null,
  28. setupTitle(vn){
  29. this.title = vn.attrs.title;
  30. if(!this.title) this.title = this.type;
  31. if(this.title)
  32. this.title = markdown.renderInline(this.title);
  33. },
  34. setuptext(vn){
  35. // console.log('setuptext', vn);
  36. // construct text
  37. this.text = vn.attrs.text || '';
  38. this.rendered_text = markdown.render(this.text);
  39. if(this.dottype == "preface"){
  40. this.summary = this.rendered_text;
  41. }else{
  42. // construct summary
  43. // TODO: summary needs more work (strip tags, markdown render)
  44. // remove img
  45. this.summary = this.text.replace(/!\[[^\]]+\]\([^\)]+\)/g, "");
  46. // get portion of text
  47. // this.summary = this.summary.match('([^ ]*[ ]{0,1}){1,6}')[0];
  48. // https://stackoverflow.com/questions/5454235/shorten-string-without-cutting-words-in-javascript
  49. // this.summary = this.summary.replace(/^(.{18}[^\s]*).*/, "$1");
  50. this.summary = this.summary.substr(0, this.summary.lastIndexOf(' ', 30));
  51. // end underscores (italic) splited by summarizing
  52. this.summary = this.summary.trim().replace(/_([^_]+)$/g, "_$1_");
  53. // remove brackets (links) splited by summarizing
  54. this.summary = this.summary.replace(/\[([^\]]+)$/g, "$1");
  55. // render the markdown
  56. this.summary = markdown.renderInline(this.summary) + " …";
  57. }
  58. },
  59. oninit(vn){
  60. this.id = vn.attrs.id;
  61. this.type = vn.attrs.type;
  62. this.level = vn.attrs.level;
  63. this.dottype = vn.attrs.dottype;
  64. this.breadcrumb = vn.attrs.breadcrumb;
  65. // console.log(`${this.id} -> ${this.dottype}`);
  66. if(typeof vn.attrs.active !== 'undefined')
  67. this.active = vn.attrs.active;
  68. // links
  69. this.links = _dbs.data_strct[this.id];
  70. // parents memorize where do we come from to avoid duplicates and looping nav
  71. if(vn.attrs.parents){
  72. this.parents = this.parents.concat(vn.attrs.parents);
  73. // console.log('_Dot init '+this.id+' parents :',this.parents);
  74. }
  75. this.nested = this.parents.length ? true : false;
  76. // if(this.nested) console.log(`oninit ${this.id}`);
  77. this.lang = vn.attrs.lang;
  78. this.setupTitle(vn);
  79. this.setuptext(vn);
  80. },
  81. oncreate(vn){
  82. if(this.active){
  83. vn.dom.classList.remove('disabled');
  84. }else{
  85. vn.dom.classList.add('disabled');
  86. }
  87. },
  88. onbeforeupdate(vn){
  89. if(this.lang != vn.attrs.lang){
  90. this.lang = vn.attrs.lang;
  91. this.breadcrumb = vn.attrs.breadcrumb;
  92. this.type = vn.attrs.type;
  93. this.setuptext(vn);
  94. this.setupTitle(vn);
  95. }
  96. },
  97. onupdate(vn){
  98. // if(this.nested) console.log(`onupdate ${this.id}`);
  99. if(this.active){
  100. if (this.opened){
  101. vn.dom.classList.add('opened');
  102. if(this.links.to.length)
  103. vn.dom.classList.add('to-links');
  104. if(this.links.from.length)
  105. vn.dom.classList.add('from-links');
  106. }else{
  107. vn.dom.classList.remove('opened');
  108. }
  109. }
  110. },
  111. setupLinks(vn, c, links){
  112. return m('nav', {'class':`links ${c}`}, links.map(id => {
  113. // console.log(id);
  114. if(typeof _dbs.data_byid[this.lang][id] !== 'undefined'){
  115. var obj = _dbs.data_byid[this.lang][id];
  116. // console.log('link to : obj', obj);
  117. return m(_Dot, {
  118. "id":id,
  119. 'title':obj.title,
  120. // TODO: translate breadcrumb
  121. 'breadcrumb':obj.breadcrumb,
  122. 'text':obj.text,
  123. 'dottype':obj.dottype,
  124. 'type':obj.type,
  125. // passe the memory of crossed dots plus the current one
  126. 'parents':vn.state.parents.concat([vn.state.id]),
  127. // activate link only if not in parents (already went through it)
  128. 'active':vn.state.parents.indexOf(id) == -1 ? true:false,
  129. // 'nested':true,
  130. 'lang':this.lang,
  131. });
  132. }
  133. })
  134. );
  135. },
  136. viewOpenedContent(vn){
  137. return m('div', {
  138. 'uid':this.id,
  139. 'class':`dot ${this.dottype}${this.nested ? ' nested':''}`},
  140. [
  141. // links to
  142. this.links.to.length
  143. ? this.setupLinks(vn, 'to', this.links.to)
  144. : null, // if no links to, add nothing
  145. // close btn
  146. m('div', {
  147. 'class':'close-link-btn',
  148. onclick(e){
  149. vn.state.opened = 0;
  150. // matomo
  151. if(typeof _paq !== 'undefined'){
  152. console.log('Matomo Connection closed', vn.state.breadcrumb);
  153. _paq.push(['trackEvent', 'Connections', 'closed', vn.state.breadcrumb]);
  154. }
  155. }
  156. }//, m('span') // , m.trust("🗙")
  157. ),
  158. // Title
  159. m('span', {'class':'title'}, m.trust(this.nested || _dbs.active_type_filter ? this.breadcrumb : this.title)),
  160. // full text
  161. m('section', {
  162. 'class':'text',
  163. onmouseover(e){
  164. e.preventDefault();
  165. if(e.target.nodeName == "A" ){
  166. let id = e.target.getAttribute("href");
  167. // add highlight class
  168. vn.dom.querySelector(`nav.links>div[uid="${id}"]`).classList.add('highlight');
  169. }else{
  170. // remove all hilight class
  171. for (let link of vn.dom.querySelectorAll('nav.links>div.dot')) {
  172. link.classList.remove('highlight');
  173. }
  174. }
  175. },
  176. onclick(e){
  177. e.preventDefault();
  178. if(e.target.nodeName == "A" ){
  179. let id = e.target.getAttribute("href");
  180. vn.dom.querySelector(`nav.links>div[uid="${id}"]>.title`).click();
  181. }
  182. }
  183. }, m.trust(this.rendered_text)
  184. ),
  185. // links from
  186. this.links.from.length
  187. ? this.setupLinks(vn, 'from', this.links.from)
  188. : null, // if no links from, add nothing
  189. ]
  190. );
  191. },
  192. viewPreviewContent(vn){
  193. return m('div', {
  194. 'uid':this.id,
  195. 'class':`dot ${this.dottype}${this.nested ? ' nested':''}`
  196. },
  197. [
  198. // bullet
  199. m('div', {'class':'bullet'}, m('span', m.trust('⚫'))),
  200. m('span', {
  201. 'class':'title',
  202. onclick(e){
  203. if(!vn.state.opened) vn.state.opened = 1;
  204. // matomo
  205. if(typeof _paq !== 'undefined'){
  206. console.log('Matomo Connection opened', vn.state.breadcrumb);
  207. _paq.push(['trackEvent', 'Connections', 'open', vn.state.breadcrumb]);
  208. }
  209. }
  210. }, m.trust(this.nested || _dbs.active_type_filter ? this.breadcrumb : this.title)),
  211. m('p', {
  212. 'class':'summary',
  213. onclick(e){
  214. if(!vn.state.opened) vn.state.opened = 1;
  215. // matomo
  216. if(typeof _paq !== 'undefined'){
  217. console.log('Matomo Connection opened', vn.state.breadcrumb);
  218. _paq.push(['trackEvent', 'Connections', 'open', vn.state.breadcrumb]);
  219. }
  220. }
  221. }, m.trust(this.summary))
  222. ]
  223. );
  224. },
  225. view(vn){
  226. return this.active && vn.state.opened
  227. ? this.viewOpenedContent(vn) // full view of dot with linked dots
  228. : this.viewPreviewContent(vn); // preview dot
  229. }
  230. }
  231. /*
  232. full list of black dotlikes from unicode
  233. ● - ● - Black Circle
  234. ⏺ - ⏺ - Black Circle for Record
  235. ⚫ - ⚫ - Medium Black Circle
  236. ⬤ - ⬤ - Black Large Circle
  237. ⧭ - ⧭ - Black Circle with Down Arrow
  238. 🞄 - 🞄 - Black Slightly Small Circle
  239. • - • - Bullet
  240. ∙ - ∙ - Bullet Operator
  241. ⋅ - ⋅ - Dot Operator
  242. 🌑 - 🌑 - New Moon Symbol
  243. */
  244. // _______ _ __ __
  245. // / ___/ / (_) /__/ /
  246. // / /__/ _ \/ / / _ /
  247. // \___/_//_/_/_/\_,_/
  248. var _Child = {
  249. // oninit(vn){},
  250. // onbeforeupdate(vn, old){},
  251. view(vn){
  252. return [
  253. !_dbs.active_type_filter || _dbs.active_type_filter == vn.attrs.dottype
  254. ? m(_Dot, vn.attrs)
  255. : null,
  256. // add children
  257. typeof vn.attrs.childs !== 'undefined'
  258. ? vn.attrs.childs.map(c => {
  259. c.lang = vn.attrs.lang;
  260. c.level = vn.attrs.level + 1;
  261. return m(_Child, c);
  262. })
  263. : null
  264. ];
  265. }
  266. };
  267. // ______
  268. // / ____/___ ____ ____ ________
  269. // / __/ / __ \/ __ \/ __ \/ ___/ _ \
  270. // / /___/ / / / /_/ / / / / /__/ __/
  271. // /_____/_/ /_/\____/_/ /_/\___/\___/
  272. var _Enonce = {
  273. // oninit(vn){},
  274. // onbeforeupdate(vn, old) {},
  275. view(vn){
  276. vn.attrs.level = 0;
  277. return [
  278. !_dbs.active_type_filter || _dbs.active_type_filter == vn.attrs.dottype
  279. // create dot
  280. ? m(_Dot, vn.attrs)
  281. : null,
  282. // add children
  283. vn.attrs.childs.map(c => {
  284. c.lang = vn.attrs.lang;
  285. c.level = 1;
  286. return m(_Child, c);
  287. })
  288. ]
  289. }
  290. }
  291. // ____ __
  292. // / __ \____ ______/ /_
  293. // / /_/ / __ `/ ___/ __/
  294. // / ____/ /_/ / / / /_
  295. // /_/ \__,_/_/ \__/
  296. var _Part = {
  297. oninit(vn){
  298. this.id = vn.attrs.id;
  299. this.title = vn.attrs.title || "";
  300. },
  301. onbeforeupdate(vn, old){
  302. this.title = vn.attrs.title || "";
  303. },
  304. view(vn){
  305. return m("section", {
  306. 'id' :this.id,
  307. 'class' :'part'
  308. },
  309. [
  310. // create title node
  311. m("h1", {'class':'part-title', 'part':this.id}, m.trust(markdown.renderInline(this.title))),
  312. // create text node
  313. vn.attrs.enonces.map(e => {
  314. // console.log(e.text);
  315. e.lang = vn.attrs.lang;
  316. switch (e.type) {
  317. case "title":
  318. // if(_dbs.active_type_filter){
  319. // console.log('filter:', _dbs.active_type_filter, 'title:',e.title);
  320. // }
  321. // handle titles
  322. return !_dbs.active_type_filter
  323. ? m("h2", {'class':'title'}, m.trust(markdown.renderInline(e.title)))
  324. : null;
  325. break;
  326. case "filet":
  327. // handle filets
  328. return m("h4", {'class':'filet'}, m.trust(markdown.renderInline(e.title)));
  329. break;
  330. default:
  331. // or build structure
  332. return m(_Enonce, Object.assign({"partid":this.id},e));
  333. }
  334. })
  335. ]
  336. )
  337. }
  338. }
  339. // ____ __
  340. // / _/___ / /__________
  341. // / // __ \/ __/ ___/ __ \
  342. // _/ // / / / /_/ / / /_/ /
  343. // /___/_/ /_/\__/_/ \____/
  344. var _Intro = {
  345. oninit(vn){
  346. console.log('_Intro : oninit : vn', vn);
  347. this.id = vn.attrs.id;
  348. this.text = vn.attrs.text || '';
  349. },
  350. onbeforeupdate(vn, old){
  351. this.id = vn.attrs.id;
  352. this.text = vn.attrs.text || '';
  353. },
  354. view(vn){
  355. return m("section", {'class':'intro'}, m("p",m.trust(markdown.renderInline(this.text))));
  356. }
  357. }
  358. // ______ __ _
  359. // / ____/___ ____ ____ ___ _____/ /_(_)___ ____ _____
  360. // / / / __ \/ __ \/ __ \/ _ \/ ___/ __/ / __ \/ __ \/ ___/
  361. // / /___/ /_/ / / / / / / / __/ /__/ /_/ / /_/ / / / (__ )
  362. // \____/\____/_/ /_/_/ /_/\___/\___/\__/_/\____/_/ /_/____/
  363. module.exports = {
  364. oncreate(vn){
  365. document.body.classList.add('mode-connections');
  366. _Ui.init();
  367. },
  368. onbeforeupdate(vn, old){
  369. console.log('Connection, onbeforeupdate old',old, 'vn',vn);
  370. },
  371. view(vn){
  372. // console.log('_ModeConnections view', vn.attrs.lang);
  373. console.log('_dbs.active_type_filter : ', _dbs.active_type_filter);
  374. return m('main', {id:"content", 'class':'mode-connections'}, _dbs.data[vn.attrs.lang].map(p => {
  375. p.lang = vn.attrs.lang;
  376. if(p.id == 'intro'){
  377. return m(_Intro,p);
  378. }else{
  379. return m(_Part,p);
  380. }
  381. })
  382. );
  383. }
  384. }