initP5MainMenuDropdown.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. if (!global.p5UI__MenuStore) throw 'Missing global.p5UI__MenuStore'
  2. var DBG = DBG || 0
  3. var DBG1 = 1
  4. // TODO: mv to React: { state: { menu items, query } }
  5. function renderLabel(item) {
  6. return (item.short_label !== item.desc)
  7. ? item.short_label + ' <em>' + item.desc + '</em>'
  8. : item.short_label
  9. ;
  10. }
  11. function renderTitle(item) {
  12. return (item.label !== item.desc)
  13. ? item.label + ' (' + item.desc + ')'
  14. : item.label
  15. ;
  16. }
  17. function renderStar(id, idsBookmarks) {
  18. return (-1 !== idsBookmarks.indexOf(id))
  19. ? '<i class=\"bookmark-item-rem glyphicon glyphicon-star\" title=\"Usuń z ulubionych\" onClick=\"return p5BookmarksRemove(event, ' + id + ')\"></i>'
  20. : '<i class=\"bookmark-item-add glyphicon glyphicon-star-empty\" title=\"Dodaj do ulubionych\" onClick=\"return p5BookmarksAdd(event, ' + id + ')\"></i>'
  21. ;
  22. }
  23. function makeRenderDropdownListItem(grouped, data) {
  24. return function renderDropdownListItem(baseNs) {
  25. var item = (baseNs in grouped.mapNsToIdx)
  26. ? data.objects[ grouped.mapNsToIdx[baseNs] ]
  27. : data.objects[ grouped.mapNsToIdx[ grouped.menuTreeNs[baseNs][0] ] ]
  28. ;
  29. DBG && console.log('DBG:renderP5MainMenuDropdown >> render loop', { baseNs, isInMap: (baseNs in grouped.mapNsToIdx), item, idx: [ grouped.mapNsToIdx[baseNs], grouped.menuTreeNs[baseNs][0] ] });
  30. return (grouped.menuTreeNs[baseNs].length > 1)
  31. ? '<li class="dropdown-submenu">' +
  32. '<a href=\"index.php?_route=ViewTableAjax&namespace=' + item.namespace + '\" title="' + renderTitle(item) + '">' +
  33. renderStar(item.id, data.idsBookmarks) +
  34. ' ' + renderLabel(item) +
  35. '</a>' + "\n" +
  36. '<ul class="dropdown-menu">' +
  37. grouped.menuTreeNs[baseNs].map(function (subItemNs) {
  38. var subItem = data.objects[ grouped.mapNsToIdx[subItemNs] ]
  39. return '<li data-id="' + subItem.id + '">' +
  40. '<a href=\"index.php?_route=ViewTableAjax&namespace=' + subItem.namespace + '\" title="' + renderTitle(subItem) + '">' +
  41. renderStar(subItem.id, data.idsBookmarks) +
  42. ' ' + renderLabel(subItem) +
  43. '</a>' +
  44. '</li>'
  45. ;
  46. }).join("\n") +
  47. '</ul>' +
  48. '</li>' + "\n"
  49. : '<li data-id="' + item.id + '">' +
  50. '<a href=\"index.php?_route=ViewTableAjax&namespace=' + item.namespace + '\" title="' + renderTitle(item) + '">' +
  51. renderStar(item.id, data.idsBookmarks) +
  52. ' ' + renderLabel(item) +
  53. '</a>' +
  54. '</li>' + "\n"
  55. ;
  56. }
  57. }
  58. function renderP5MainMenuDropdown(data) {
  59. DBG && console.log('DBG:renderP5MainMenuDropdown data.objects', data.objects)
  60. var grouped = data.objects.reduce(function (ret, item, idx) {
  61. ret.mapNsToIdx[ item.namespace ] = idx
  62. var nsParts = item.namespace.split('/')
  63. var baseNs = nsParts.slice(0, 2).join('/')
  64. DBG && console.log('DBG:renderP5MainMenuDropdown >> reduce loop ', { nsParts, baseNs });
  65. if (!ret.menuTreeNs[baseNs]) ret.menuTreeNs[baseNs] = []
  66. ret.menuTreeNs[baseNs].push(item.namespace)
  67. return ret;
  68. }, { mapNsToIdx: {}, menuTreeNs: {} })
  69. DBG && console.log('DBG:renderP5MainMenuDropdown grouped', grouped)
  70. var renderDropdownListItem = makeRenderDropdownListItem(grouped, data)
  71. var labels = Object.keys(grouped.menuTreeNs).map(function (baseNs) {
  72. var item = (baseNs in grouped.mapNsToIdx)
  73. ? data.objects[ grouped.mapNsToIdx[baseNs] ]
  74. : data.objects[ grouped.mapNsToIdx[ grouped.menuTreeNs[baseNs][0] ] ]
  75. ;
  76. return {
  77. ns: baseNs,
  78. label: (item.short_label !== item.desc) ? item.short_label : item.desc,
  79. }
  80. });
  81. DBG && console.log('DBG:renderP5MainMenuDropdown labels', labels)
  82. labels.sort(function (item1, item2) {
  83. var l1 = item1.label.toLowerCase()
  84. var l2 = item2.label.toLowerCase()
  85. if (l1 < l2) { return -1; }
  86. if (l1 > l2) { return 1; }
  87. return 0
  88. })
  89. var groupeByLabel = labels.reduce(function (ret, item) {
  90. if (!ret.length) return [ { prefix: item.label, labels: [ item ] } ];
  91. var last = ret.pop()
  92. var labelLast = last.prefix.replace('_', ' ').split(" ")
  93. var labelItem = item.label.replace('_', ' ').split(" ")
  94. DBG && console.log('DBG:renderP5MainMenuDropdown TODO: group?', { last, item, labelLast, labelItem })
  95. if (labelLast[0] === labelItem[0]) {
  96. last.labels.push(item)
  97. last.prefix = labelLast[0] + " <em>(" + last.labels.length + ")</em>"
  98. ret.push(last)
  99. } else {
  100. ret.push(last)
  101. ret.push({ prefix: item.label, labels: [item] })
  102. return ret;
  103. }
  104. return ret;
  105. }, [])
  106. var nodesGroupedByLabel = groupeByLabel.map(function (item) {
  107. // var baseNs =
  108. if (item.labels.length === 1) {
  109. return renderDropdownListItem(item.labels[0].ns)
  110. } else {
  111. // return '<details><summary>' + item.prefix + '</summary><div>' +
  112. // '' + item.labels.map(function (label) {
  113. // return renderDropdownListItem(label.ns)
  114. // }).join("\n") +
  115. // '</div></details>';
  116. return '<li class="dropdown-submenu">' +
  117. '<a href=\"#\" onClick="return false">' +
  118. // '<b style="padding-left:18px">' + item.prefix + '</b>' +
  119. '<i class="glyphicon glyphicon-folder-open" style="opacity:0.5; font-size:90%; padding-right:3px"></i>' + " " +
  120. '<b>' + item.prefix + '</b>' +
  121. '</a>' + "\n" +
  122. '<ul class="dropdown-menu">' +
  123. item.labels.map(function (label) {
  124. return renderDropdownListItem(label.ns);
  125. }).join("\n") +
  126. '</ul>' +
  127. '</li>' + "\n"
  128. ;
  129. }
  130. })
  131. DBG && console.log('DBG:renderP5MainMenuDropdown grouped', { grouped, labels, groupeByLabel })
  132. return nodesGroupedByLabel;
  133. // var jqDropdownMenu = jQuery('#' + idSubMenu)
  134. // jqDropdownMenu.empty()
  135. // jqDropdownMenu.append(searchBox);
  136. // jqDropdownMenu.append(nodesGroupedByLabel);
  137. // var liNodes = Object.keys(grouped.menuTreeNs).map(function (baseNs) {
  138. // var item = (baseNs in grouped.mapNsToIdx)
  139. // ? data.objects[ grouped.mapNsToIdx[baseNs] ]
  140. // : data.objects[ grouped.mapNsToIdx[ grouped.menuTreeNs[baseNs][0] ] ]
  141. // ;
  142. // DBG && console.log('DBG:renderP5MainMenuDropdown >> render loop', { baseNs, isInMap: (baseNs in grouped.mapNsToIdx), item, idx: [ grouped.mapNsToIdx[baseNs], grouped.menuTreeNs[baseNs][0] ] });
  143. // return (grouped.menuTreeNs[baseNs].length > 1)
  144. // ? '<li class="dropdown-submenu">' +
  145. // '<a href=\"index.php?_route=ViewTableAjax&namespace=' + item.namespace + '\" title="' + renderTitle(item) + '">' +
  146. // renderStar(item.id, data.idsBookmarks) +
  147. // ' ' + renderLabel(item) +
  148. // '</a>' + "\n" +
  149. // '<ul class="dropdown-menu">' +
  150. // grouped.menuTreeNs[baseNs].map(function (subItemNs) {
  151. // var subItem = data.objects[ grouped.mapNsToIdx[subItemNs] ]
  152. // return '<li>' +
  153. // '<a href=\"index.php?_route=ViewTableAjax&namespace=' + subItem.namespace + '\" title="' + renderTitle(subItem) + '">' +
  154. // renderStar(subItem.id, data.idsBookmarks) +
  155. // ' ' + renderLabel(subItem) +
  156. // '</a>' +
  157. // '</li>'
  158. // ;
  159. // }).join("\n") +
  160. // '</ul>' +
  161. // '</li>' + "\n"
  162. // : '<li>' +
  163. // '<a href=\"index.php?_route=ViewTableAjax&namespace=' + item.namespace + '\" title="' + renderTitle(item) + '">' +
  164. // renderStar(item.id, data.idsBookmarks) +
  165. // ' ' + renderLabel(item) +
  166. // '</a>' +
  167. // '</li>' + "\n"
  168. // ;
  169. // })
  170. // jqDropdownMenu.append(liNodes);
  171. }
  172. function initP5MainMenuDropdown( btnNode, idSubMenu ) { // @usage: onClick="return initP5MainMenuDropdown(this, 'SE-menu-tables')"
  173. DBG && console.log('DBG:initP5MainMenuDropdown({idSubMenu: '+idSubMenu+'})');
  174. if (!btnNode._initialized) {
  175. var jqDropdownTrigger = jQuery(btnNode)
  176. var jqDropdownMenu = jQuery('#' + idSubMenu)
  177. var jqDropdownParent = jqDropdownMenu.parent()
  178. var idSearchBoxNode = 'idSubMenu_searchBox';
  179. var rerenderDropdown = (function (global, idSubMenu, jqDropdownMenu, jqDropdownTrigger) {
  180. return function (data) {
  181. DBG && console.log('DBG renderP5MainMenuDropdown', {data, idSubMenu});
  182. var mainMenuFilterCallback = makeMainMenuFilterCallback(jqDropdownMenu, idSearchBoxNode);
  183. var searchBox = document.createElement('div')
  184. searchBox.setAttribute('style', "padding:12px 20px; min-width:200px");
  185. {
  186. var searchBox_group = document.createElement('div')
  187. searchBox_group.setAttribute('class', "input-group");
  188. {
  189. searchBox_group_icon = document.createElement('span')
  190. searchBox_group_icon.setAttribute('class', "input-group-addon");
  191. searchBox_group_icon.innerHTML = '<i class="glyphicon glyphicon-search" title="Szukaj"></i>';
  192. searchBox_group.appendChild(searchBox_group_icon)
  193. }
  194. {
  195. searchBox_group_input = document.createElement('input')
  196. searchBox_group_input.setAttribute('type', "text");
  197. searchBox_group_input.setAttribute('class', "form-control");
  198. searchBox_group_input.setAttribute('value', "");
  199. searchBox_group_input.setAttribute('id', idSearchBoxNode);
  200. searchBox_group_input.setAttribute('autofocus', "autofocus");
  201. // onChange: function (fieldName, value) {
  202. // priv.options.filterStore.dispatch(priv.options.filterActions.searchBox_delayFilter(fieldName, value));
  203. // }
  204. jQuery(searchBox_group_input).on('keyup', function (event) {
  205. event.preventDefault()
  206. event.stopPropagation()
  207. searchBox_delayFilter(mainMenuFilterCallback, event.target.value)
  208. })
  209. searchBox_group.appendChild(searchBox_group_input)
  210. }
  211. {
  212. searchBox_group_clear = document.createElement('span')
  213. searchBox_group_clear.setAttribute('class', "input-group-addon");
  214. searchBox_group_clear.style.display = "none";
  215. searchBox_group_clear.style.cursor = "pointer";
  216. searchBox_group_clear.innerHTML = '<i class="glyphicon glyphicon-remove" title="Kasuj filtr"></i>';
  217. jQuery(searchBox_group_clear).on('click', function (event) {
  218. event.preventDefault()
  219. event.stopPropagation()
  220. DBG && console.log("DBG:searchBox:clear", { value: jQuery('#' + idSearchBoxNode).value, node: jQuery('#' + idSearchBoxNode) })
  221. jQuery('#' + idSearchBoxNode).val('');
  222. mainMenuFilterCallback('')
  223. })
  224. searchBox_group.appendChild(searchBox_group_clear)
  225. }
  226. searchBox.appendChild(searchBox_group)
  227. jQuery(searchBox).on('click', function (event) {
  228. DBG && console.log("DBG:searchBox.on(click) ...")
  229. event.preventDefault()
  230. event.stopPropagation()
  231. })
  232. }
  233. jqDropdownMenu.empty()
  234. jqDropdownMenu.append(searchBox);
  235. jqDropdownMenu.append(renderP5MainMenuDropdown(data));
  236. }
  237. })(global, idSubMenu, jqDropdownMenu, jqDropdownTrigger);
  238. global.p5UI__MenuStore.subscribe(rerenderDropdown)
  239. jqDropdownTrigger.attr('data-toggle', 'dropdown') // is required by bootstrap dorpdown.js even if is called via js
  240. jqDropdownParent.on('shown.bs.dropdown', function () {
  241. // DBG && console.log("DBG:Main menu:shown.bs.dropdown")
  242. jQuery('#' + idSearchBoxNode).focus()
  243. })
  244. jqDropdownParent.on('hidden.bs.dropdown', function () {
  245. // DBG && console.log("DBG:Main menu:hidden.bs.dropdown")
  246. })
  247. global.p5UI__MenuStore.forceUpdate()
  248. jQuery(btnNode).dropdown()
  249. }
  250. btnNode._initialized = true // TODO: DBG TEST
  251. return true;
  252. }
  253. function menuStore__getObjectById(id) {
  254. var allData = global.p5UI__MenuStore.getData()
  255. if (!allData || !allData.objects) return null;
  256. for (var i = 0, l = allData.objects.length; i < l; i++) {
  257. var itemObj = allData.objects[i]
  258. if (itemObj.id === id) return itemObj;
  259. }
  260. return null;
  261. }
  262. function searchBox__isStringMatch(str, query) {
  263. // TODO: split query for words
  264. return (-1 !== str.toLowerCase().search(query.toLowerCase()));
  265. }
  266. function searchBox__isMenuObjectMatch(itemObj, query) {
  267. if (!itemObj) return false;
  268. if (itemObj.label && searchBox__isStringMatch(itemObj.label, query)) return true;
  269. if (itemObj.name && searchBox__isStringMatch(itemObj.name, query)) return true;
  270. if (itemObj.opis && searchBox__isStringMatch(itemObj.opis, query)) return true;
  271. if (itemObj.namespace && searchBox__isStringMatch(itemObj.namespace, query)) return true;
  272. // if (itemObj.desc && searchBox__isStringMatch(itemObj.desc, query)) return true;
  273. // if (itemObj.short_label && searchBox__isStringMatch(itemObj.short_label, query)) return true;
  274. // if (itemObj.id && searchBox__isStringMatch(itemObj.id, query)) return true;
  275. return false;
  276. }
  277. function searchBox__isMenuItemMatch(node, query) {
  278. DBG && console.log("DBG:Main menu:searchBox__isMenuItemMatch", { query: query, label: jQuery(node).children('a').text(), node: node })
  279. if (node.hasAttribute('data-id')) {
  280. var id = parseInt(node.getAttribute('data-id'));
  281. var itemObj = menuStore__getObjectById(id);
  282. return searchBox__isMenuObjectMatch(itemObj, query);
  283. }
  284. if (node.hasChildNodes()) {
  285. {
  286. var groupItemLabel = jQuery(node).children('a').text()
  287. if (searchBox__isStringMatch(groupItemLabel, query)) return true;
  288. }
  289. // DBG && console.log("DBG:Main menu:searchBox__isMenuItemMatch: loop start...", { query: query, label: jQuery(node).children('a').text(), node: node })
  290. for (var i = 0, l = node.childNodes.length; i < l; i++) {
  291. if ("UL" === node.childNodes[i].tagName) {
  292. // DBG && console.log("DBG:Main menu:searchBox__isMenuItemMatch: loop UL ", { query: query, label: jQuery(node).children('a').text(), node: node })
  293. for (var j = 0, l = node.childNodes[i].childNodes.length; j < l; j++) {
  294. if ("LI" === node.childNodes[i].childNodes[j].tagName) {
  295. // DBG && console.log("DBG:Main menu:searchBox__isMenuItemMatch: loop UL > LI ", { query: query, label: jQuery(node).children('a').text(), node: node })
  296. if (searchBox__isMenuItemMatch(node.childNodes[i].childNodes[j], query)) {
  297. return true;
  298. }
  299. }
  300. }
  301. break;
  302. }
  303. }
  304. }
  305. return false;
  306. }
  307. function makeMainMenuFilterCallback(jqDropdownMenu, idSearchBoxNode) {
  308. return function (query) {
  309. DBG && console.log("DBG:Main menu:makeMainMenuFilterCallback", { query: query, jqDropdownMenu, idSearchBoxNode })
  310. jQuery('#' + idSearchBoxNode).next('span').get(0).style.display = query ? 'table-cell' : 'none';
  311. jqDropdownMenu.children('li').each(function (idx, node) {
  312. node.style.display = "list-item";
  313. })
  314. if (!query || query.length < 3) return;
  315. jqDropdownMenu.children('li').each(function (idx, node) {
  316. // if (node.style.display === "none") node.style.display = "";
  317. var id = (node.hasAttribute('data-id')) ? parseInt(node.getAttribute('data-id')) : null;
  318. var itemObj = (id) ? menuStore__getObjectById(id) : null;
  319. var isMatch = searchBox__isMenuItemMatch(node, query)
  320. DBG && console.log("DBG:Main menu:makeMainMenuFilterCallback:loop", { isMatch: isMatch, id: id, itemObj: itemObj, display: node.style.display, node: node })
  321. node.style.display = isMatch ? "list-item" : "none";
  322. })
  323. }
  324. }
  325. var _config_delay_value = 450;
  326. var _config_delay_timeout = null;
  327. function searchBox_delayFilter(callback, value) {
  328. // DBG && console.log('DBG:searchBox:delayFilter', { value });
  329. if (_config_delay_timeout) clearTimeout(_config_delay_timeout)
  330. _config_delay_timeout = setTimeout(function () {
  331. DBG && console.log("DBG:Main menu:delayFilter", { value: value })
  332. callback(value)
  333. }, _config_delay_value)
  334. }
  335. global.initP5MainMenuDropdown = initP5MainMenuDropdown