Source: tabs.js

  1. /**
  2. * Tabs Component<BR>
  3. * <BR><BR><img src=/tk/lib/components/w/img/tabs.png width=30% style="border:1px lime dashed;padding:20px">
  4. * <BR><BR><a href="/tk/lib/components/w/html/tabs.html">DEMO</a>
  5. */
  6. class Tabs extends HTMLElement {
  7. constructor() {
  8. wc.group("Tabs.constructor")
  9. super();
  10. wc.groupEnd();
  11. };
  12. /**
  13. * Set observable values here. When Changed, attributeChangedCallback is invoked
  14. * @observedAttributes
  15. */
  16. static get observedAttributes() {
  17. wc.group("Tabs.observedAttributes");
  18. this.observables = [];
  19. wc.groupEnd();
  20. return this.observables;
  21. };
  22. /**
  23. * Called when this is attached to DOM
  24. * @connectedCallback.
  25. */
  26. connectedCallback() {
  27. wc.group("Tabs.connectedCallback")
  28. let self = this;
  29. // ADD A RANDOM ID IF NONE EXIST
  30. if (!this.id) {
  31. this.id = this.constructor.name.toLowerCase() + "-" + wc.uid();
  32. }
  33. // GET PROPERTIES AND INTERESTING ELEMENTS
  34. this._initialize();
  35. // ADD COMPONENT MARKTOP
  36. this.innerHTML = this._template()
  37. if (this.properties.cfg) {
  38. this.configure();
  39. }
  40. // SUBSCRIBE TO REQUESTS FROM CLIENTS
  41. wc.subscribe(this.id, function(e) {
  42. self._request(e)
  43. });
  44. // ADD STATS AND OTHER FINAL STUFF
  45. this._finalize();
  46. ga('send', {'hitType': 'event','eventCategory': 'wc-connected','eventAction': 'connected','eventLabel': this.properties.cname, 'eventValue':JSON.stringify({'env':wcENV,'app':wcAPP,'url':wcURL})});
  47. wc.groupEnd();
  48. };
  49. /**
  50. * Initial Markup
  51. * @private
  52. * @_template
  53. */
  54. _template() {
  55. wc.group("Tabs.template");
  56. var temp = `
  57. <div class="container-fluid">
  58. <div class="row">
  59. <div class="col-sm-12 wc-tabs-top">
  60. TOP
  61. </div>
  62. <div class="wc-tabs-lhs">
  63. LHS
  64. </div>
  65. <div class="wc-tabs-mid w-100">
  66. MID
  67. </div>
  68. <div class="wc-tabs-rhs">
  69. RHS
  70. </div>
  71. <div class="col-sm-12 wc-tabs-bot">
  72. BOT
  73. </div>
  74. </div>`
  75. wc.wait4(".wc-tabs-lhs", () => {
  76. this.top = this.querySelector(".wc-tabs-top")
  77. this.bot = this.querySelector(".wc-tabs-bot")
  78. this.lhs = this.querySelector(".wc-tabs-lhs")
  79. this.rhs = this.querySelector(".wc-tabs-rhs")
  80. this.mid = this.querySelector(".wc-tabs-mid")
  81. $(".wc-tabs-top, .wc-tabs-bot, .wc-tabs-lhs, .wc-tabs-rhs").hide();
  82. });
  83. wc.groupEnd();
  84. return temp;
  85. };
  86. /**
  87. * Called with .setAttribute(...) function call
  88. * @attributeChangedCallback
  89. */
  90. attributeChangedCallback(attr, oldval, newval) {
  91. wc.group("Tabs.attributeChangedCallback:", attr, oldval, newval);
  92. this.properties = this.properties || [];
  93. let obs = Tabs.observedAttributes;
  94. for (let i = 0; i < obs.length; i++) {
  95. if (newval) {
  96. this.properties[obs[i]] = newval;
  97. }
  98. }
  99. // YOUR CODE FOR CHANGES GO HERE (MAYBE NULL FIRST TIME THROUGH)
  100. try {
  101. switch(attr)
  102. {
  103. case "header":
  104. //this.querySelector("h1").innerHTML = newval;
  105. break;
  106. default:
  107. break;
  108. }
  109. } catch(e) {
  110. wc.warn(e.name + ' > ' + e.message);
  111. }
  112. wc.groupEnd();
  113. };
  114. /**
  115. * Stores DOM elements of interest for future use
  116. * @private
  117. * @_fetchElements
  118. */
  119. _fetchElements() {
  120. wc.group("Tabs._fetchElements");
  121. this.dom = this.dom || [];
  122. this.dom.content = this.innerHTML;
  123. wc.groupEnd();
  124. };
  125. /**
  126. * Component attributes are _fetched and defaults are set if undefined
  127. * @private
  128. * @_fetchAttributes
  129. */
  130. _fetchAttributes() {
  131. wc.group("Tabs._fetchAttributes");
  132. this.properties = {
  133. uparam : "",
  134. cname : "Tabs",
  135. author : "Mel M. Heravi, Ph.D.",
  136. version : "1.0",
  137. show : 0,
  138. cols : "2,10"
  139. };
  140. // SAVE WIDGET SPECIFIC PROPERTIES
  141. this.propertiesW = [];
  142. // SAVE ALL OTHER PROPERTIES
  143. let attrs = wc.getAttributes(this)
  144. for (var key in attrs) {
  145. let attr = this.getAttribute(key) || this.properties.key;
  146. this.properties[key] = this.getAttribute(key);
  147. this.propertiesW[key] = this.getAttribute(key);
  148. wc.log(key + ": " + attrs[key]);
  149. }
  150. // SET ALL INITIAL ATTRIBUTES
  151. for (var key in this.properties) {
  152. switch(key)
  153. {
  154. case "header":
  155. break;
  156. default:
  157. break;
  158. }
  159. }
  160. wc.log("ATTRIBUTES: ", this.properties);
  161. wc.groupEnd();
  162. };
  163. /**
  164. * configure the instance object and artifacts
  165. * @configure
  166. * @param {string} data use data if exist else use 'this.properties.cfg' parameter
  167. */
  168. configure(data) {
  169. wc.group("Tabs.configure:", data);
  170. // IF JSON VARIABLE (data) IS PROVIDED
  171. if (data) {
  172. this._process(data);
  173. } else {
  174. let self = this;
  175. $.getJSON(this.properties.cfg, function(data) {
  176. self._process(data);
  177. }).fail(function(jqXHR, textStatus, errorThrown) {
  178. alert("ERROR: INCOMING TEXT " + jqXHR.responseText);
  179. });
  180. }
  181. wc.groupEnd();
  182. };
  183. /**
  184. * _process the instance object and artifacts
  185. * @private
  186. * @_process
  187. */
  188. _process(data) {
  189. wc.group("Tabs._process:", data);
  190. switch(this.properties.side)
  191. {
  192. case "top":
  193. this._top(data);
  194. break;
  195. case "bot":
  196. this._bot(data);
  197. break;
  198. case "lhs":
  199. this.cols = this.properties.cols.split(",");
  200. this._lhs(data);
  201. break;
  202. case "rhs":
  203. this.cols = this.properties.cols.split(",");
  204. this._rhs(data);
  205. break;
  206. }
  207. wc.groupEnd();
  208. };
  209. /**
  210. * @private
  211. * @_top
  212. */
  213. _top(data) {
  214. wc.group("Tabs._top");
  215. let self = this;
  216. $(this.top).show();
  217. this.mid = this.querySelector(".wc-tabs-mid");
  218. if (this.properties.cfg) {
  219. $(this.mid).html(`<wc-pager id="${this.id}-pager" class="wc-maximize" env="dev" cfg="${this.properties.cfg}"></wc-pager>`);
  220. } else {
  221. $(this.mid).html(`<wc-pager id="${this.id}-pager" class="wc-maximize" env="dev"></wc-pager>`);
  222. wc.wait4(`wc-pager`, function() {
  223. let tmp = self.querySelector(`wc-pager`);
  224. tmp.configure(data);
  225. });
  226. }
  227. // PAGER HANDLE
  228. let str = `<ul class="nav nav-tabs">`;
  229. let num =0;
  230. for (var key in data) {
  231. var p = data[key]
  232. str += `<a tab-num="${num++}" page="${data[key].page}" class="nav-link" href="#!">${data[key].label}</a>`
  233. }
  234. str += "</ul>"
  235. $(this.top).html(str);
  236. $(`#${this.id} [tab-num=${this.properties.show}]`).addClass("active");
  237. wc.wait4("wc-pager", () => {
  238. this.pager = this.querySelector("wc-pager");
  239. this.pager.show($(`#${this.id} [tab-num="${this.properties.show}"]`).attr("page"));
  240. let as = this.querySelectorAll("ul a");
  241. $(as).on("click", function() {
  242. $(as).removeClass("active");
  243. $(this).addClass("active");
  244. let tn = $(this).attr("tab-num");
  245. let pg = $(this).attr("page");
  246. self.pager.show(pg);
  247. });
  248. });
  249. wc.groupEnd();
  250. };
  251. /**
  252. * @private
  253. * @_bot
  254. */
  255. _bot(data) {
  256. wc.group("Tabs._bot");
  257. let self = this;
  258. $(this.bot).show();
  259. this.mid = this.querySelector(".wc-tabs-mid");
  260. if (this.properties.cfg) {
  261. $(this.mid).html(`<wc-pager id="${this.id}-pager" class="wc-maximize" env="dev" cfg="${this.properties.cfg}"></wc-pager>`);
  262. } else {
  263. $(this.mid).html(`<wc-pager id="${this.id}-pager" class="wc-maximize" env="dev"></wc-pager>`);
  264. wc.wait4(`wc-pager`, function() {
  265. let tmp = self.querySelector(`wc-pager`);
  266. tmp.configure(data);
  267. });
  268. }
  269. wc.wait4("wc-pager", () => {
  270. this.pager = this.querySelector("wc-pager");
  271. this.pager.configure(data);
  272. });
  273. // PAGER HANDLE
  274. let str = `<ul class="nav nav-tabs">`;
  275. let num =0;
  276. for (var key in data) {
  277. var p = data[key]
  278. str += `<a tab-num="${num++}" page="${data[key].page}" class="nav-link" href="#!">${data[key].label}</a>`
  279. }
  280. str += "</ul>"
  281. this.top = this.querySelector(".wc-tabs-bot");
  282. $(this.top).html(str);
  283. $(`#${this.id} [tab-num=${this.properties.show}]`).addClass("active");
  284. wc.wait4("wc-pager", () => {
  285. this.pager = this.querySelector("wc-pager");
  286. let as = this.querySelectorAll("ul a");
  287. $(as).on("click", function() {
  288. $(as).removeClass("active");
  289. $(this).addClass("active");
  290. let tn = $(this).attr("tab-num");
  291. let pg = $(this).attr("page");
  292. self.pager.show(pg);
  293. });
  294. this.pager.show($(`#${this.id} [tab-num=${this.properties.show}]`).attr("page"));
  295. });
  296. wc.groupEnd();
  297. };
  298. /**
  299. * @private
  300. * @_lhs
  301. */
  302. _lhs(data) {
  303. wc.group("Tabs._lhs");
  304. let self = this;
  305. $(this.lhs).show();
  306. // ADD PAGER
  307. if (this.properties.cfg) {
  308. $(this.mid).html(`<wc-pager id="${this.id}-pager" class="wc-maximize" env="dev" cfg="${this.properties.cfg}"></wc-pager>`);
  309. } else {
  310. $(this.mid).html(`<wc-pager id="${this.id}-pager" class="wc-maximize" env="dev"></wc-pager>`);
  311. wc.wait4(`wc-pager`, function() {
  312. let tmp = self.querySelector(`wc-pager`);
  313. tmp.configure(data);
  314. });
  315. }
  316. $(this.lhs).addClass("col-sm-" + this.cols[0]);
  317. $(this.mid).addClass("col-sm-" + this.cols[1]);
  318. let str = `<ul class="list-group list-group-flush">`;
  319. let n = 0;
  320. for (var key in data) {
  321. str += `<li tab-num="${n++}" page="${data[key].page}" id="wc-tabs-${data[key].id}" class="list-group-item list-group-item-action" url="${data[key].url}">${data[key].label}</li>`
  322. }
  323. str += "</ul>";
  324. $(this.lhs).html(str);
  325. wc.wait4("wc-pager", () => {
  326. this.pager = this.querySelector("wc-pager");
  327. $(`#${this.id} [tab-num=${this.properties.show}]`).addClass("active");
  328. let as = this.querySelectorAll("ul li");
  329. $(as).on("click", function() {
  330. $(as).removeClass("active");
  331. $(this).addClass("active");
  332. let tn = $(this).attr("tab-num");
  333. let pg = $(this).attr("page");
  334. self.pager.show(pg);
  335. });
  336. });
  337. wc.groupEnd();
  338. };
  339. /**
  340. * @private
  341. * @_rhs
  342. */
  343. _rhs(data) {
  344. wc.group("Tabs._rhs");
  345. let self = this;
  346. $(this.rhs).show();
  347. // ADD PAGER
  348. if (this.properties.cfg) {
  349. $(this.mid).html(`<wc-pager id="${this.id}-pager" class="wc-maximize" env="dev" cfg="${this.properties.cfg}"></wc-pager>`);
  350. } else {
  351. $(this.mid).html(`<wc-pager id="${this.id}-pager" class="wc-maximize" env="dev"></wc-pager>`);
  352. wc.wait4(`wc-pager`, function() {
  353. let tmp = self.querySelector(`wc-pager`);
  354. tmp.configure(data);
  355. });
  356. }
  357. $(this.rhs).addClass("col-sm-" + this.cols[0]);
  358. $(this.mid).addClass("col-sm-" + this.cols[1]);
  359. let str = `<ul class="list-group list-group-flush">`;
  360. let n = 0;
  361. for (var key in data) {
  362. str += `<li tab-num="${n++}" page="${data[key].page}" id="wc-tabs-${data[key].id}" class="list-group-item list-group-item-action" url="${data[key].url}">${data[key].label}</li>`
  363. }
  364. str += "</ul>";
  365. $(this.rhs).html(str);
  366. wc.wait4("wc-pager", () => {
  367. this.pager = this.querySelector("wc-pager");
  368. $(`#${this.id} [tab-num=${this.properties.show}]`).addClass("active");
  369. let as = this.querySelectorAll("ul li");
  370. $(as).on("click", function() {
  371. $(as).removeClass("active");
  372. $(this).addClass("active");
  373. let tn = $(this).attr("tab-num");
  374. let pg = $(this).attr("page");
  375. self.pager.show(pg);
  376. });
  377. });
  378. wc.groupEnd();
  379. };
  380. /**
  381. * Initialize component
  382. * @private
  383. * @_initialize
  384. */
  385. _initialize() {
  386. wc.group("Tabs._initialize:", this.id);
  387. // FETCH ALL INTERESTING ELEMENTS
  388. this._fetchElements();
  389. // FETCH ALL ATTRIBUTES
  390. this._fetchAttributes();
  391. wc.groupEnd();
  392. };
  393. /**
  394. * Save data for analytics and final wrap up
  395. * @private
  396. * @_finalize
  397. */
  398. _finalize() {
  399. wc.group("Tabs._finalize:", this.id);
  400. this.classList.add("wc");
  401. // ADD ANALYTICS HERE
  402. wc.setStats(this, this.properties.cname, this.properties.version);
  403. // SHOW IT NOW (NO FLICKERS)
  404. this.style.visibility = "visible";
  405. wc.groupEnd();
  406. };
  407. /**
  408. * Invoked When component is removed. Usually with a .remove() function call
  409. * @disconnectedCallback
  410. */
  411. disconnectedCallback() {
  412. wc.group("Tabs.disconnectedCallback")
  413. ga('send', {'hitType': 'event','eventCategory': 'wc-disconnected','eventAction': 'disconnected','eventLabel': this.properties.cname, 'eventValue':JSON.stringify({'env':wcENV,'app':wcAPP,'url':wcURL})});
  414. wc.groupEnd();
  415. };
  416. /**
  417. * Destroy the instance object and artifacts
  418. * @destroy
  419. */
  420. destroy() {
  421. wc.group("Tabs.destroy");
  422. // FREE ALL MEMORY
  423. // you should delete all created objects here
  424. // FREE POINTER
  425. delete this;
  426. // REMOVE ITEM FROM DOM
  427. this.parentNode.removeChild(this);
  428. ga('send', {'hitType': 'event','eventCategory': 'wc-destroyed','eventAction': 'distroy','eventLabel': this.properties.cname, 'eventValue':JSON.stringify({'env':wcENV,'app':wcAPP,'url':wcURL})});
  429. wc.groupEnd();
  430. };
  431. /**
  432. * @_publish
  433. */
  434. _publish(type) {
  435. wc.group("Tabs._publish:", type);
  436. wc.publish("wc-tabs", {
  437. time: new Date().getTime(),
  438. action: type,
  439. id: this.id
  440. });
  441. wc.groupEnd();
  442. return true;
  443. };
  444. /**
  445. * @_open
  446. */
  447. _open(tabid) {
  448. wc.group("Tabs._open:", tabid);
  449. let tab = this.querySelector(`[tab-num="${tabid}"]`);
  450. // CLICK TO OPEN THE PAGE
  451. $(tab).click();
  452. wc.publish("wc-tabs", {
  453. time: new Date().getTime(),
  454. action: "open",
  455. tab: tabid,
  456. id: this.id
  457. });
  458. wc.groupEnd();
  459. return true;
  460. };
  461. /**
  462. * @_load
  463. */
  464. _load(tabid, type, content) {
  465. wc.group("Tabs._load:", tabid);
  466. let tab = this.querySelector(`[tab-num="${tabid}"]`);
  467. let page = $(tab).attr("page");
  468. let pager = this.querySelector(`.wc-pager__page.${page}`);
  469. // OPEN THE TAB
  470. this._open(tabid);
  471. // LOAD CONTENT FROM CONTENT
  472. if (type == "url") {
  473. $(pager).load(content);
  474. }
  475. if (type == "html") {
  476. pager.innerHTML = content;
  477. }
  478. wc.publish("wc-tabs", {
  479. time: new Date().getTime(),
  480. action: "load",
  481. url: content,
  482. id: this.id
  483. });
  484. wc.groupEnd();
  485. return true;
  486. };
  487. /**
  488. * @_request
  489. */
  490. _request(e) {
  491. wc.group("Tabs._request:", this.id, JSON.stringify(e.detail));
  492. switch(e.detail.request)
  493. {
  494. case "open":
  495. this._open(e.detail.tab);
  496. break;
  497. case "load":
  498. // LOAD FROM URL
  499. if (typeof e.detail.url != "undefined") {
  500. this._load(e.detail.tab, "url", e.detail.url);
  501. }
  502. // ADD CONTENT AS HTML
  503. if (typeof e.detail.html != "undefined") {
  504. this._load(e.detail.tab, "html", e.detail.html);
  505. }
  506. break;
  507. default:
  508. alert("DO NOT HAVE SUCH REQUEST: " + e.detail.request);
  509. break;
  510. }
  511. wc.groupEnd();
  512. return true;
  513. };
  514. /**
  515. * @test
  516. */
  517. static test(what) {
  518. wc.group("Tabs.test:", what);
  519. switch(what)
  520. {
  521. case "open":
  522. wc.publish("my-tabs-top", {time:new Date().getTime(), requestor:"my-tabs-top", request:"open", tab:2})
  523. break;
  524. case "load":
  525. wc.publish("my-tabs-top", {time:new Date().getTime(), requestor:"my-tabs-top", request:"load", tab:1, url:"/mtk/render?ajax=1&callback=tk::dummy::3"})
  526. break;
  527. case "html":
  528. wc.publish("my-tabs-top", {time:new Date().getTime(), requestor:"my-tabs-top", request:"load", tab:1, html:"<h1>Mel was here</h1>"})
  529. break;
  530. }
  531. wc.groupEnd();
  532. return true;
  533. }
  534. }
  535. window.customElements.define('wc-tabs', Tabs);
  536. // SO I CAN CALL THE STATIC METHOD GLOBALLY
  537. window.Tabs = Tabs;