Source: app.js

  1. /**
  2. * App Component<BR>
  3. * <BR><BR><img src=/tk/lib/components/w/img/app.png width=30% style="border:1px lime dashed;padding:20px">
  4. * <BR><BR><a href="/tk/lib/components/w/html/app.html">DEMO</a>
  5. */
  6. class App extends HTMLElement {
  7. constructor() {
  8. wc.group("App.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("App.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("App.connectedCallback")
  28. // ADD A RANDOM ID IF NONE EXIST
  29. if (!this.id) {
  30. this.id = this.constructor.name.toLowerCase() + "-" + wc.uid();
  31. }
  32. // GET PROPERTIES AND INTERESTING ELEMENTS
  33. this._initialize();
  34. // IF PAGE IS RELOADED
  35. if (wc.refreshed()) {
  36. this.properties.show = wc.parseQuery().page;
  37. console.log("THIS PAGE IS RELOADED:", this.properties.show);
  38. }
  39. this.configure();
  40. // ADD STATS AND OTHER FINAL STUFF
  41. this._finalize();
  42. wc.groupEnd();
  43. };
  44. /**
  45. * Called with .setAttribute(...) function call
  46. * @attributeChangedCallback
  47. */
  48. attributeChangedCallback(attr, oldval, newval) {
  49. wc.group("App.attributeChangedCallback:", attr, oldval, newval);
  50. this.properties = this.properties || [];
  51. let obs = App.observedAttributes;
  52. for (let i = 0; i < obs.length; i++) {
  53. if (newval) {
  54. this.properties[obs[i]] = newval;
  55. }
  56. }
  57. // YOUR CODE FOR CHANGES GO HERE (MAYBE NULL FIRST TIME THROUGH)
  58. try {
  59. switch(attr)
  60. {
  61. case "header":
  62. break;
  63. default:
  64. break;
  65. }
  66. } catch(e) {
  67. wc.warn(e.name + ' > ' + e.message);
  68. }
  69. wc.groupEnd();
  70. };
  71. /**
  72. * Stores DOM elements of interest for future use
  73. * @private
  74. * @_fetchElements
  75. */
  76. _fetchElements() {
  77. wc.group("App._fetchElements");
  78. this.dom = this.dom || [];
  79. this.dom.content = this.innerHTML;
  80. wc.groupEnd();
  81. };
  82. /**
  83. * Component attributes are _fetched and defaults are set if undefined
  84. * @private
  85. * @_fetchAttributes
  86. */
  87. _fetchAttributes() {
  88. wc.group("App._fetchAttributes");
  89. this.properties = {
  90. uparam : "",
  91. cname : "App",
  92. author : "Mel M. Heravi",
  93. version : "1.0"
  94. };
  95. // SAVE WIDGET SPECIFIC PROPERTIES
  96. this.propertiesW = [];
  97. // SAVE ALL OTHER PROPERTIES
  98. let attrs = wc.getAttributes(this)
  99. for (var key in attrs) {
  100. let attr = this.getAttribute(key) || this.properties.key;
  101. this.properties[key] = this.getAttribute(key);
  102. this.propertiesW[key] = this.getAttribute(key);
  103. wc.log(key + ": " + attrs[key]);
  104. }
  105. // SET ALL INITIAL ATTRIBUTES
  106. for (var key in this.properties) {
  107. switch(key)
  108. {
  109. case "header":
  110. break;
  111. default:
  112. break;
  113. }
  114. }
  115. wc.log("ATTRIBUTES: ", this.properties);
  116. wc.groupEnd();
  117. };
  118. /**
  119. * configure the instance object and artifacts
  120. * @configure
  121. * @param {string} data use data if exist else use 'this.properties.cfg' parameter
  122. */
  123. configure() {
  124. wc.group("App.configure:");
  125. let self = this;
  126. $.getJSON(this.properties.path + "/config.json", function(data) {
  127. self._process(data);
  128. }).fail(function(jqXHR, textStatus, errorThrown) {
  129. alert("ERROR: INCOMING TEXT " + jqXHR.responseText);
  130. });
  131. wc.groupEnd();
  132. };
  133. /**
  134. * _process the instance object and artifacts
  135. * @private
  136. * @_process
  137. */
  138. _process(data) {
  139. wc.group("App._process:", data);
  140. this.data = data;
  141. this.cname = this.properties.cname.toLowerCase();
  142. let tstr = `<div><wc-include href="/tk/lib/components/misc/webpack/src/w/html/parts/app/banner.html"></wc-include></div>`;
  143. tstr += `
  144. <wc-msg id="my-msg">WAITING...</wc-msg>
  145. <div class="${this.cname}__header sticky-top">
  146. <wc-include href="${data.header}"></wc-include>
  147. </div>`;
  148. for (var i = 0; i < data.pages.length; i++) {
  149. tstr += `<div class="${this.cname}__page ${data.pages[i].page}"></div>`;
  150. }
  151. tstr += `
  152. <div class="${this.cname}__footer">
  153. <wc-include href="${data.footer}"></wc-include>
  154. </div>`;
  155. // ADD COMPONENT MARKTOP
  156. this.innerHTML = tstr;
  157. wc.wait4(".navbar", ()=> {
  158. this.show(this.properties.show);
  159. // LISTEN TO HASH CHANGES
  160. wc.popstate(() => {
  161. let x = document.location.search.replace("?page=","")
  162. if (x != "") {
  163. // CLICK ON PAGE LINK
  164. this.show(x, false);
  165. // PUBLISH SHOW EVENT
  166. wc.publish(`${this.cname}`, {
  167. time: new Date().getTime(),
  168. action: "popstate",
  169. id: x
  170. });
  171. }
  172. });
  173. });
  174. wc.wait4("wc-msg", ()=> {
  175. this.msg = this.querySelector("wc-msg");
  176. this.msg.show({id:"M2011: ", type:"info", html:"Loading. Please wait...", timer:3000, block: true})
  177. this.tester();
  178. });
  179. wc.groupEnd();
  180. };
  181. /**
  182. * Initialize component
  183. * @private
  184. * @_initialize
  185. */
  186. _initialize() {
  187. wc.group("App._initialize:", this.id);
  188. // FETCH ALL INTERESTING ELEMENTS
  189. this._fetchElements();
  190. // FETCH ALL ATTRIBUTES
  191. this._fetchAttributes();
  192. wc.groupEnd();
  193. };
  194. /**
  195. * Save data for analytics and final wrap up
  196. * @private
  197. * @_finalize
  198. */
  199. _finalize() {
  200. wc.group("App._finalize:", this.id);
  201. this.classList.add("wc");
  202. // ADD ANALYTICS HERE
  203. wc.setStats(this, this.properties.cname, this.properties.version);
  204. // SHOW IT NOW (NO FLICKERS)
  205. this.style.visibility = "visible";
  206. wc.groupEnd();
  207. };
  208. /**
  209. * Invoked When component is removed. Usually with a .remove() function call
  210. * @disconnectedCallback
  211. */
  212. disconnectedCallback() {
  213. wc.group("App.disconnectedCallback")
  214. // FREE MEMORY AND CLEANUP
  215. wc.groupEnd();
  216. };
  217. /**
  218. * Destroy the instance object and artifacts
  219. * @destroy
  220. */
  221. destroy() {
  222. wc.group("App.destroy");
  223. // FREE ALL MEMORY
  224. // you should delete all created objects here
  225. // FREE POINTER
  226. delete this;
  227. // REMOVE ITEM FROM DOM
  228. this.parentNode.removeChild(this);
  229. wc.groupEnd();
  230. };
  231. /**
  232. * refresh a page
  233. * @refresh
  234. */
  235. refresh(page) {
  236. wc.group("App.refresh:", page);
  237. // EMPTY CONTENT
  238. $("." + page).empty();
  239. // RE-LOAD THE PAGE
  240. this.show(page);
  241. wc.groupEnd();
  242. return page;
  243. };
  244. /**
  245. * show a page
  246. * @show
  247. */
  248. show(page, push=true) {
  249. wc.group("App.show:", page);
  250. $("[page-id]").removeClass("active")
  251. $(`[page-id=${page}]`).addClass("active")
  252. // HIDE ALL PAGES
  253. $(`.${this.cname}__page`).hide();
  254. let obj = $.grep(this.data.pages, function(obj) {
  255. return obj.page === page;
  256. });
  257. let apage = $("." + page);
  258. let empty = apage.is(":empty");
  259. if (obj[0].cache == "false" || empty) {
  260. apage.load(obj[0].url)
  261. }
  262. // SHOW THIS PAGE
  263. apage.show();
  264. // PUBLISH SHOW EVENT
  265. wc.publish(`${this.cname}`, {
  266. time: new Date().getTime(),
  267. action: "show",
  268. id: page
  269. });
  270. // SAVE BROWSER HISTORY
  271. if (push) {
  272. wc.pushstate(page);
  273. }
  274. wc.groupEnd();
  275. return page;
  276. };
  277. /**
  278. * for testing only
  279. * @tester
  280. */
  281. tester(page) {
  282. wc.group("App.tester:", page);
  283. let self = this;
  284. let str = `<div class="btn-group btn-group-sm btn-group-tester" role="group" aria-label="btn-tester" style="position:fixed;bottom:60px;right:10px;z-index:99999999">`;
  285. for (var i = 0; i < this.data.pages.length; i++) {
  286. str += `<button type="button" class="btn btn-danger wc-app-btn-tester">${this.data.pages[i].page}</button>`;
  287. }
  288. str += "</div>"
  289. $("body").append(str);
  290. $(".wc-app-btn-tester").on("click", function() {
  291. self.show($(this).text());
  292. });
  293. wc.groupEnd();
  294. };
  295. /**
  296. * PROCESS JSON MESSAGES
  297. * @message
  298. * app.message('{"action": "show","page": "page-2"}')
  299. */
  300. message(text) {
  301. wc.group("App.message:", text);
  302. let msg = app.querySelector("wc-msg");
  303. let json = JSON.parse(text);
  304. if ("action" in json === false) {
  305. msg.show({id:"M2020: ", type:"error", html:"NO ACTION SPECIFIED", close: true, block: true})
  306. }
  307. switch(json.action)
  308. {
  309. case "show":
  310. app.show(json.page);
  311. break;
  312. default:
  313. msg.show({id:"M2020: ", type:"error", html:"NO SUCH ACTION: " + json.action, close: true, block: true})
  314. break;
  315. }
  316. wc.groupEnd();
  317. return true;
  318. };
  319. }
  320. window.customElements.define('wc-app', App);