From bbb8e987c93207472f61e44c5fca3bec8ed935f7 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Wed, 8 Mar 2017 11:43:58 -0500 Subject: [PATCH] Add support for Pages > Tree navigation: drill down through the page tree within the top navigation dropdowns. This works in AdminThemeDefault and AdminThemeUikit but not in AdminThemeReno (which just shows the first level, per its nav setup). --- .../AdminTheme/AdminThemeDefault/default.php | 2 +- .../ProcessPageList/ProcessPageList.module | 110 +++++++++++++++++- .../ProcessPageList/ProcessPageListRender.php | 25 ++-- .../System/SystemUpdater/SystemUpdate14.php | 2 +- .../System/SystemUpdater/SystemUpdate16.php | 30 +++++ .../System/SystemUpdater/SystemUpdater.module | 2 +- wire/templates-admin/scripts/main.js | 47 +++++++- wire/templates-admin/scripts/main.min.js | 2 +- 8 files changed, 198 insertions(+), 22 deletions(-) create mode 100644 wire/modules/System/SystemUpdater/SystemUpdate16.php diff --git a/wire/modules/AdminTheme/AdminThemeDefault/default.php b/wire/modules/AdminTheme/AdminThemeDefault/default.php index 7483bcca..00debd8d 100644 --- a/wire/modules/AdminTheme/AdminThemeDefault/default.php +++ b/wire/modules/AdminTheme/AdminThemeDefault/default.php @@ -29,7 +29,7 @@ if($user->hasPermission('page-edit')) { $searchForm = ''; } -$version = $adminTheme->version . 'j'; +$version = $adminTheme->version . 'k'; $config->styles->prepend($config->urls->root . "wire/templates-admin/styles/AdminTheme.css?v=$version"); $config->styles->prepend($config->urls->adminTemplates . "styles/" . ($adminTheme->colors ? "main-$adminTheme->colors" : "main-classic") . ".css?v=$version"); diff --git a/wire/modules/Process/ProcessPageList/ProcessPageList.module b/wire/modules/Process/ProcessPageList/ProcessPageList.module index 44ac5e47..68b1c914 100644 --- a/wire/modules/Process/ProcessPageList/ProcessPageList.module +++ b/wire/modules/Process/ProcessPageList/ProcessPageList.module @@ -242,7 +242,16 @@ class ProcessPageList extends Process implements ConfigurableModule { "data-token-value='$tokenValue'>" . "$script"; } - + + /** + * Get the appropriate PageListRender classs + * + * @param Page $page + * @param null|int $limit + * @param null|int $start + * @return ProcessPageListRender + * + */ protected function getPageListRender(Page $page, $limit = null, $start = null) { require_once(dirname(__FILE__) . '/ProcessPageListRender.php'); @@ -353,13 +362,104 @@ class ProcessPageList extends Process implements ConfigurableModule { /** * Output JSON list of navigation items for this module's bookmarks + * + * @param array $options + * @return string|array * */ public function ___executeNavJSON(array $options = array()) { - $bookmarks = $this->getPageBookmarks(); - $options['edit'] = $this->wire('config')->urls->admin . 'page/?id={id}'; - $options = $bookmarks->initNavJSON($options); - return parent::___executeNavJSON($options); + + $config = $this->wire('config'); + $urls = $this->wire('urls'); + + if($this->useBookmarks) { + $bookmarks = $this->getPageBookmarks(); + $options['edit'] = $urls->admin . 'page/?id={id}'; + $options = $bookmarks->initNavJSON($options); + return parent::___executeNavJSON($options); + } + + $parentID = (int) $this->wire('input')->get('parent_id'); + if(!$parentID) $parentID = 1; + $parent = $this->wire('pages')->get($parentID); + $parentViewable = $parent->viewable(false); + $renderer = $this->getPageListRender($parent); + $items = $parentViewable ? $renderer->getChildren() : new PageArray(); + if($parentID === 1 && $parentViewable) $items->prepend($parent); + $skipPageIDs = array($config->trashPageID, $config->adminRootPageID); + $maxLabelLength = 40; + + $data = array( + 'url' => $urls->admin . 'page/list/navJSON/', + 'label' => '', + 'icon' => 'sitemap', + 'list' => array(), + ); + + $data = array_merge($options, $data); + + foreach($items as $page) { + + $id = $page->id; + if(in_array($id, $skipPageIDs)) continue; + $url = ''; + + if($page->editable()) { + $url = $page->editUrl(); + } else if($page->viewable()) { + $url = $page->url(); + } else if(!$page->listable()) { + continue; + } + + $numChildren = $id > 1 ? $page->numChildren : 0; + $label = $renderer->getPageLabel($page, array('noTags' => true)); + if($page->isUnpublished()) $label = "$label"; + if(strlen($label) > $maxLabelLength) { + $label = substr($label, 0, $maxLabelLength); + $pos = strrpos($label, ' '); + if($pos !== false) $label = substr($label, 0, $pos); + $label .= ' …'; + } + if($numChildren) $label .= " $numChildren"; + $label .= '   '; + + $a = array( + 'url' => $url, + 'id' => $id, + 'label' => $label, + 'icon' => $page->getIcon(), + ); + + if($page->id > 1 && $page->numChildren) { + $a['navJSON'] = $data['url'] . "?parent_id=$page->id"; + } + + $data['list'][] = $a; + } + + if($items->getTotal() > $items->count()) { + $data['list'][] = array( + 'url' => $urls->admin . "page/?open=$parentID", + 'label' => $this->_('Show All') . ' ' . + '' . sprintf($this->_('(%d pages)'), $items->getTotal()) . '', + 'icon' => 'arrow-circle-right', + 'className' => 'separator', + ); + } + + if($parent->addable()) { + $data['list'][] = array( + 'url' => $urls->admin . "page/add/?parent_id=$parentID", + 'label' => __('Add New', '/wire/templates-admin/default.php'), + 'icon' => 'plus-circle', + 'className' => 'separator', + ); + } + + if($config->ajax) header("Content-Type: application/json"); + + return json_encode($data); } public function ___executeOpen() { diff --git a/wire/modules/Process/ProcessPageList/ProcessPageListRender.php b/wire/modules/Process/ProcessPageList/ProcessPageListRender.php index 6bb4a8b1..4950f9a4 100644 --- a/wire/modules/Process/ProcessPageList/ProcessPageListRender.php +++ b/wire/modules/Process/ProcessPageList/ProcessPageListRender.php @@ -4,7 +4,7 @@ * Base class for Page List rendering * * @method array getPageActions(Page $page) - * @method string getPageLabel(Page $page) + * @method string getPageLabel(Page $page, array $options = array()) * */ abstract class ProcessPageListRender extends Wire { @@ -90,13 +90,14 @@ abstract class ProcessPageListRender extends Wire { * Return the Page's label text, whether that originates from the Page's name, headline, title, etc. * * @param Page $page + * @param array $options * @return string * */ - public function ___getPageLabel(Page $page) { + public function ___getPageLabel(Page $page, array $options = array()) { $value = ''; - $icon = $page->getIcon(); + $icon = empty($options['noTags']) ? $page->getIcon() : ''; if(strpos($this->pageLabelField, '!') === 0) { // exclamation forces this one to be used, rather than template-specific one @@ -162,20 +163,22 @@ abstract class ProcessPageListRender extends Wire { if(!strlen("$v")) continue; - $value .= - "" . - htmlspecialchars(strip_tags("$v"), ENT_QUOTES, "UTF-8", false) . - ""; + if(empty($options['noTags'])) $value .= ""; + $value .= htmlspecialchars(strip_tags("$v"), ENT_QUOTES, "UTF-8", false); + if(empty($options['noTags'])) $value .= ""; } } - $icon = $page->getIcon(); if($icon) { $icon = $this->wire('sanitizer')->name($icon); $icon = ""; } - if(!strlen($value)) $value = $page->get("title|name"); + if(!strlen($value)) $value = $this->wire('sanitizer')->entities($page->getUnformatted("title|name")); + + if(!empty($options['noTags']) && strpos($value, '<') !== false) { + $value = strip_tags($value); + } return $icon . trim($value); } @@ -194,6 +197,10 @@ abstract class ProcessPageListRender extends Wire { } return ''; } + + public function getChildren() { + return $this->children; + } } diff --git a/wire/modules/System/SystemUpdater/SystemUpdate14.php b/wire/modules/System/SystemUpdater/SystemUpdate14.php index 4e5567dc..155c9b77 100644 --- a/wire/modules/System/SystemUpdater/SystemUpdate14.php +++ b/wire/modules/System/SystemUpdater/SystemUpdate14.php @@ -16,7 +16,7 @@ class SystemUpdate14 extends SystemUpdate { $admin = $this->wire('pages')->get($this->wire('config')->adminRootPageID); $info = array( 'ProcessPageEdit' => $admin->path . 'page/edit/', - 'ProcessPageList' => $admin->path . 'page/list/', + // 'ProcessPageList' => $admin->path . 'page/list/', ); $numCompleted = 0; diff --git a/wire/modules/System/SystemUpdater/SystemUpdate16.php b/wire/modules/System/SystemUpdater/SystemUpdate16.php new file mode 100644 index 00000000..e97d754f --- /dev/null +++ b/wire/modules/System/SystemUpdater/SystemUpdate16.php @@ -0,0 +1,30 @@ + Tree navigation item + * + */ +class SystemUpdate16 extends SystemUpdate { + + public function execute() { + $this->wire()->addHookAfter('ProcessWire::ready', $this, 'executeAtReady'); + return 0; // indicates we will update system version ourselves when ready + } + + public function executeAtReady() { + + $admin = $this->wire('pages')->get($this->wire('config')->adminRootPageID); + $page = $this->wire('pages')->get($admin->path . 'page/list/'); + + if(!$page->id) return; + $page->of(false); + $page->removeStatus(Page::statusHidden); + try { + $page->save(); + $this->updater->saveSystemVersion(16); + } catch(\Exception $e) { + // will try next time + } + } +} + diff --git a/wire/modules/System/SystemUpdater/SystemUpdater.module b/wire/modules/System/SystemUpdater/SystemUpdater.module index 3990f076..89626cc8 100644 --- a/wire/modules/System/SystemUpdater/SystemUpdater.module +++ b/wire/modules/System/SystemUpdater/SystemUpdater.module @@ -26,7 +26,7 @@ class SystemUpdater extends WireData implements Module, ConfigurableModule { * This version number is important, as this updater keeps the systemVersion up with this version * */ - 'version' => 15, + 'version' => 16, ); } diff --git a/wire/templates-admin/scripts/main.js b/wire/templates-admin/scripts/main.js index da792bc3..84b885c1 100644 --- a/wire/templates-admin/scripts/main.js +++ b/wire/templates-admin/scripts/main.js @@ -278,6 +278,11 @@ var ProcessWireAdmin = { $.getJSON(url, function(data) { $itemsIcon.removeClass('fa-spinner fa-spin').addClass('fa-angle-right'); + + if(!data.list) { + console.log(data); + return; + } // now add new event to monitor menu positions if(!dropdownPositionsMonitored && data.list.length > 10) { @@ -298,21 +303,55 @@ var ProcessWireAdmin = { ); $ul.append($li); } + + var numSubnavJSON = 0; + // populate the retrieved items $.each(data.list, function(n) { + var icon = ''; - if(this.icon) icon = ""; - var url = this.url.indexOf('/') === 0 ? this.url : data.url + this.url; - var $li = $("
  • " + icon + this.label + "
  • "); + var url = ''; + + if(this.icon) { + icon = ""; + } + + if(this.url == 'navJSON') { + // click triggers another navJSON load + } else { + var url = this.url.indexOf('/') === 0 ? this.url : data.url + this.url; + } + + var $li = $("
  • "); + var $a = $("" + icon + this.label + ""); + var $ulSub = null; + + if(this.navJSON) { + $a.attr('data-json', this.navJSON).addClass('pw-has-items pw-has-ajax-items'); + $ulSub = $("").addClass('subnavJSON'); + var $icon = $(""); + $a.prepend($icon); + $li.prepend($a).append($ulSub); + numSubnavJSON++; + } else { + $li.prepend($a); + } + if(typeof this.className != "undefined" && this.className && this.className.length) { $li.addClass(this.className); } + $ul.append($li); }); - $ul.addClass('navJSON').addClass('length' + parseInt(data.list.length)).hide(); if($ul.children().length) $ul.css('opacity', 1.0).fadeIn('fast'); + + if(numSubnavJSON) { + var numParents = $ul.parents('ul').length; + $ul.find('ul.subnavJSON').css('z-index', 200 + numParents); + $ul.menu({}); + } // trigger the first call hoverDropdownAjaxItem($a); diff --git a/wire/templates-admin/scripts/main.min.js b/wire/templates-admin/scripts/main.min.js index 6c261511..6028a935 100644 --- a/wire/templates-admin/scripts/main.min.js +++ b/wire/templates-admin/scripts/main.min.js @@ -1 +1 @@ -var ProcessWireAdmin={init:function(){this.setupButtonStates();this.setupTooltips();this.setupDropdowns()},setupTooltips:function(){$("a.tooltip, .pw-tooltip").tooltip({position:{my:"center bottom",at:"center top"}}).hover(function(){var a=$(this);if(a.is("a")){a.addClass("ui-state-hover")}else{a.data("pw-tooltip-cursor",a.css("cursor"));a.css("cursor","pointer")}a.addClass("pw-tooltip-hover");a.css("cursor","pointer")},function(){var a=$(this);a.removeClass("pw-tooltip-hover ui-state-hover");if(!a.is("a")){a.css("cursor",a.data("pw-tooltip-cursor"))}})},setupButtonStates:function(){$(document).on("mouseover",".ui-button",function(){var a=$(this);a.removeClass("ui-state-default").addClass("ui-state-hover");if(a.hasClass("ui-priority-secondary")){a.toggleClass("xui-priority-secondary ui-priority-secondary")}if(a.hasClass("pw-button-dropdown-main")){a.siblings("#pw-dropdown-toggle-"+a.attr("id")).trigger("mouseover")}}).on("mouseout",".ui-button",function(){var a=$(this);a.removeClass("ui-state-hover").addClass("ui-state-default");if(a.hasClass("xui-priority-secondary")){a.toggleClass("xui-priority-secondary ui-priority-secondary")}if(a.hasClass("pw-button-dropdown-main")){a.siblings("#pw-dropdown-toggle-"+a.attr("id")).trigger("mouseout")}}).on("click",".ui-button",function(){$(this).removeClass("ui-state-default").addClass("ui-state-active")}).on("click","a > button",function(){var a=$(this).parent();var b=a.attr("target");if(typeof b!="undefined"&&b=="_blank"){}else{window.location=a.attr("href")}})},setupDropdowns:function(){var c=false;var h;function g(){var l=$(this);var k;if(l.attr("data-pw-dropdown")){k=l.siblings(l.attr("data-pw-dropdown"));if(!k.length){k=$(l.attr("data-pw-dropdown"))}}else{k=l.siblings(".pw-dropdown-menu")}k.hide();l.data("pw-dropdown-ul",k);if(l.is("button")){if(l.find(".ui-button-text").length==0){l.button()}if(l.attr("type")=="submit"){l.click(function(){l.addClass("pw-dropdown-disabled");setTimeout(function(){l.removeClass("pw-dropdown-disabled")},2000)})}}else{}k.find("a").click(function(){k.hide();return true});k.find(".pw-has-items").each(function(){var o=$("");$(this).prepend(o)});if(l.hasClass("pw-dropdown-toggle-click")){var n=null;function m(){if(n){clearTimeout(n)}n=setTimeout(function(){if(k.filter(":hover").length||l.filter(":hover").length){return}k.fadeOut("fast");l.removeClass("hover pw-dropdown-toggle-open")},1000)}k.mouseleave(m);l.mouseleave(m)}else{k.mouseleave(function(){k.hide();l.removeClass("hover")})}}function e(p){var o=$(this);var l=o.data("pw-dropdown-ul");var k=o.hasClass("pw-dropdown-toggle-delay")?700:0;var m=l.data("pw-dropdown-last-offset");var n=o.data("pw-dropdown-timeout");if(o.hasClass("pw-dropdown-toggle-click")){if(p.type!="mousedown"){return false}o.removeClass("ui-state-focus");if(o.hasClass("pw-dropdown-toggle-open")){o.removeClass("pw-dropdown-toggle-open hover");l.hide();return}else{$(".pw-dropdown-toggle-open").each(function(){var r=$(this);var q=r.data("pw-dropdown-ul");q.mouseleave()});o.addClass("pw-dropdown-toggle-open")}}if(o.hasClass("pw-dropdown-disabled")){return}n=setTimeout(function(){if(o.hasClass("pw-dropdown-disabled")){return}var t=o.offset();if(m!=null){if(t.top!=m.top||t.left!=m.left){l.menu("destroy").removeClass("pw-dropdown-ready")}}if(!l.hasClass("pw-dropdown-ready")){l.css("position","absolute");l.prependTo($("body")).addClass("pw-dropdown-ready").menu();var r={my:"right top",at:"right bottom",of:o};var s=l.attr("data-my");var q=l.attr("data-at");if(s){r.my=s}if(q){r.at=q}l.position(r).css("z-index",200)}o.addClass("hover");l.show();l.data("pw-dropdown-last-offset",t)},k);o.data("pw-dropdown-timeout",n)}function d(){var m=$(this);var k=m.data("pw-dropdown-ul");var l=m.data("pw-dropdown-timeout");if(l){clearTimeout(l)}setTimeout(function(){if(k.filter(":hover").length){return}k.find("ul").hide();k.hide();m.removeClass("hover")},50);if($("body").hasClass("touch-device")){$(this).attr("data-touchCnt",0)}}function i(m){var k=m.attr("data-from");if(!k){return}var l=$("#"+m.attr("data-from"));if(l.length>0){setTimeout(function(){var p=l.offset().left;var n=m.closest("li").parent("ul");var o=n.offset().left;if(o!=p){n.css("left",p)}},500)}}function f(){var k=$(this);h=k;setTimeout(function(){if(!h){return}if(h!=k){return}k.addClass("pw-ajax-items-loaded");var n=k.attr("data-json");var m=k.siblings("ul");var o=false;var l=k.children(".pw-has-items-icon");l.removeClass("fa-angle-right").addClass("fa-spinner fa-spin");m.css("opacity",0);$.getJSON(n,function(p){l.removeClass("fa-spinner fa-spin").addClass("fa-angle-right");if(!c&&p.list.length>10){c=true;o=true;$(document).on("hover","ul.pw-dropdown-menu a",function(){i($(this))})}if(p.add){var q=$("
  • "+p.add.label+"
  • ");m.append(q)}$.each(p.list,function(u){var s="";if(this.icon){s=""}var r=this.url.indexOf("/")===0?this.url:p.url+this.url;var t=$("
  • "+s+this.label+"
  • ");if(typeof this.className!="undefined"&&this.className&&this.className.length){t.addClass(this.className)}m.append(t)});m.addClass("navJSON").addClass("length"+parseInt(p.list.length)).hide();if(m.children().length){m.css("opacity",1).fadeIn("fast")}i(k)})},250)}var b=null;function a(n){var l=$(this);var m=l.attr("data-touchCnt");if(b&&l.attr("id")!=b.attr("id")){b.attr("data-touchCnt",0)}b=l;if(!m){m=0}m++;l.attr("data-touchCnt",m);if(m==2||(l.hasClass("pw-has-ajax-items")&&!l.closest("ul").hasClass("topnav"))){var k=l.attr("href");l.attr("data-touchCnt",0);if(typeof k!="undefined"&&k.length>1){return true}else{l.mouseleave()}}else{var p=l.attr("data-from");if(typeof p=="undefined"){var p=""}if(p.indexOf("topnav")>-1){var o=p.replace("topnav-","")+"-";$("a.pw-dropdown-toggle.hover:not('."+o+"')").attr("data-touchCnt",0).mouseleave()}l.mouseenter()}return false}function j(){if($("body").hasClass("touch-device")){$(document).on("touchstart","a.pw-dropdown-toggle, a.pw-has-items",a)}$(".pw-dropdown-menu").on("click","a:not(.pw-modal)",function(k){k.stopPropagation()});$(".pw-dropdown-toggle").each(g);$(document).on("mousedown",".pw-dropdown-toggle-click",e).on("mouseenter",".pw-dropdown-toggle:not(.pw-dropdown-toggle-click)",e).on("mouseleave",".pw-dropdown-toggle:not(.pw-dropdown-toggle-click)",d).on("mouseenter",".pw-dropdown-menu a.pw-has-ajax-items:not(.pw-ajax-items-loaded)",f).on("mouseleave",".pw-dropdown-menu a.pw-has-ajax-items",function(){h=null})}j()}};if(typeof ProcessWire!="undefined"){ProcessWire.confirm=function(b,a){if(typeof vex!="undefined"&&typeof a!="undefined"){vex.dialog.confirm({message:b,callback:function(c){if(c){a()}}})}else{if(typeof a!="undefined"){if(confirm(b)){a()}}else{return confirm(b)}}};ProcessWire.alert=function(b,a){if(typeof a=="undefined"){var a=false}if(typeof vex!="undefined"){if(a){vex.dialog.alert({unsafeMessage:b})}else{vex.dialog.alert(b)}}else{alert(b)}};ProcessWire.prompt=function(b,c,a){if(typeof vex=="undefined"){alert("prompt function requires vex");return}return vex.dialog.prompt({message:b,placeholder:c,callback:a})}}; \ No newline at end of file +var ProcessWireAdmin={init:function(){this.setupButtonStates();this.setupTooltips();this.setupDropdowns()},setupTooltips:function(){$("a.tooltip, .pw-tooltip").tooltip({position:{my:"center bottom",at:"center top"}}).hover(function(){var a=$(this);if(a.is("a")){a.addClass("ui-state-hover")}else{a.data("pw-tooltip-cursor",a.css("cursor"));a.css("cursor","pointer")}a.addClass("pw-tooltip-hover");a.css("cursor","pointer")},function(){var a=$(this);a.removeClass("pw-tooltip-hover ui-state-hover");if(!a.is("a")){a.css("cursor",a.data("pw-tooltip-cursor"))}})},setupButtonStates:function(){$(document).on("mouseover",".ui-button",function(){var a=$(this);a.removeClass("ui-state-default").addClass("ui-state-hover");if(a.hasClass("ui-priority-secondary")){a.toggleClass("xui-priority-secondary ui-priority-secondary")}if(a.hasClass("pw-button-dropdown-main")){a.siblings("#pw-dropdown-toggle-"+a.attr("id")).trigger("mouseover")}}).on("mouseout",".ui-button",function(){var a=$(this);a.removeClass("ui-state-hover").addClass("ui-state-default");if(a.hasClass("xui-priority-secondary")){a.toggleClass("xui-priority-secondary ui-priority-secondary")}if(a.hasClass("pw-button-dropdown-main")){a.siblings("#pw-dropdown-toggle-"+a.attr("id")).trigger("mouseout")}}).on("click",".ui-button",function(){$(this).removeClass("ui-state-default").addClass("ui-state-active")}).on("click","a > button",function(){var a=$(this).parent();var b=a.attr("target");if(typeof b!="undefined"&&b=="_blank"){}else{window.location=a.attr("href")}})},setupDropdowns:function(){var c=false;var h;function g(){var l=$(this);var k;if(l.attr("data-pw-dropdown")){k=l.siblings(l.attr("data-pw-dropdown"));if(!k.length){k=$(l.attr("data-pw-dropdown"))}}else{k=l.siblings(".pw-dropdown-menu")}k.hide();l.data("pw-dropdown-ul",k);if(l.is("button")){if(l.find(".ui-button-text").length==0){l.button()}if(l.attr("type")=="submit"){l.click(function(){l.addClass("pw-dropdown-disabled");setTimeout(function(){l.removeClass("pw-dropdown-disabled")},2000)})}}else{}k.find("a").click(function(){k.hide();return true});k.find(".pw-has-items").each(function(){var o=$("");$(this).prepend(o)});if(l.hasClass("pw-dropdown-toggle-click")){var n=null;function m(){if(n){clearTimeout(n)}n=setTimeout(function(){if(k.filter(":hover").length||l.filter(":hover").length){return}k.fadeOut("fast");l.removeClass("hover pw-dropdown-toggle-open")},1000)}k.mouseleave(m);l.mouseleave(m)}else{k.mouseleave(function(){k.hide();l.removeClass("hover")})}}function e(p){var o=$(this);var l=o.data("pw-dropdown-ul");var k=o.hasClass("pw-dropdown-toggle-delay")?700:0;var m=l.data("pw-dropdown-last-offset");var n=o.data("pw-dropdown-timeout");if(o.hasClass("pw-dropdown-toggle-click")){if(p.type!="mousedown"){return false}o.removeClass("ui-state-focus");if(o.hasClass("pw-dropdown-toggle-open")){o.removeClass("pw-dropdown-toggle-open hover");l.hide();return}else{$(".pw-dropdown-toggle-open").each(function(){var r=$(this);var q=r.data("pw-dropdown-ul");q.mouseleave()});o.addClass("pw-dropdown-toggle-open")}}if(o.hasClass("pw-dropdown-disabled")){return}n=setTimeout(function(){if(o.hasClass("pw-dropdown-disabled")){return}var t=o.offset();if(m!=null){if(t.top!=m.top||t.left!=m.left){l.menu("destroy").removeClass("pw-dropdown-ready")}}if(!l.hasClass("pw-dropdown-ready")){l.css("position","absolute");l.prependTo($("body")).addClass("pw-dropdown-ready").menu();var r={my:"right top",at:"right bottom",of:o};var s=l.attr("data-my");var q=l.attr("data-at");if(s){r.my=s}if(q){r.at=q}l.position(r).css("z-index",200)}o.addClass("hover");l.show();l.data("pw-dropdown-last-offset",t)},k);o.data("pw-dropdown-timeout",n)}function d(){var m=$(this);var k=m.data("pw-dropdown-ul");var l=m.data("pw-dropdown-timeout");if(l){clearTimeout(l)}setTimeout(function(){if(k.filter(":hover").length){return}k.find("ul").hide();k.hide();m.removeClass("hover")},50);if($("body").hasClass("touch-device")){$(this).attr("data-touchCnt",0)}}function i(m){var k=m.attr("data-from");if(!k){return}var l=$("#"+m.attr("data-from"));if(l.length>0){setTimeout(function(){var p=l.offset().left;var n=m.closest("li").parent("ul");var o=n.offset().left;if(o!=p){n.css("left",p)}},500)}}function f(){var k=$(this);h=k;setTimeout(function(){if(!h){return}if(h!=k){return}k.addClass("pw-ajax-items-loaded");var n=k.attr("data-json");var m=k.siblings("ul");var o=false;var l=k.children(".pw-has-items-icon");l.removeClass("fa-angle-right").addClass("fa-spinner fa-spin");m.css("opacity",0);$.getJSON(n,function(r){l.removeClass("fa-spinner fa-spin").addClass("fa-angle-right");if(!r.list){console.log(r);return}if(!c&&r.list.length>10){c=true;o=true;$(document).on("hover","ul.pw-dropdown-menu a",function(){i($(this))})}if(r.add){var s=$("
  • "+r.add.label+"
  • ");m.append(s)}var q=0;$.each(r.list,function(z){var w="";var v="";if(this.icon){w=""}if(this.url=="navJSON"){}else{var v=this.url.indexOf("/")===0?this.url:r.url+this.url}var y=$("
  • ");var x=$(""+w+this.label+"");var u=null;if(this.navJSON){x.attr("data-json",this.navJSON).addClass("pw-has-items pw-has-ajax-items");u=$("").addClass("subnavJSON");var t=$("");x.prepend(t);y.prepend(x).append(u);q++}else{y.prepend(x)}if(typeof this.className!="undefined"&&this.className&&this.className.length){y.addClass(this.className)}m.append(y)});m.addClass("navJSON").addClass("length"+parseInt(r.list.length)).hide();if(m.children().length){m.css("opacity",1).fadeIn("fast")}if(q){var p=m.parents("ul").length;m.find("ul.subnavJSON").css("z-index",200+p);m.menu({})}i(k)})},250)}var b=null;function a(n){var l=$(this);var m=l.attr("data-touchCnt");if(b&&l.attr("id")!=b.attr("id")){b.attr("data-touchCnt",0)}b=l;if(!m){m=0}m++;l.attr("data-touchCnt",m);if(m==2||(l.hasClass("pw-has-ajax-items")&&!l.closest("ul").hasClass("topnav"))){var k=l.attr("href");l.attr("data-touchCnt",0);if(typeof k!="undefined"&&k.length>1){return true}else{l.mouseleave()}}else{var p=l.attr("data-from");if(typeof p=="undefined"){var p=""}if(p.indexOf("topnav")>-1){var o=p.replace("topnav-","")+"-";$("a.pw-dropdown-toggle.hover:not('."+o+"')").attr("data-touchCnt",0).mouseleave()}l.mouseenter()}return false}function j(){if($("body").hasClass("touch-device")){$(document).on("touchstart","a.pw-dropdown-toggle, a.pw-has-items",a)}$(".pw-dropdown-menu").on("click","a:not(.pw-modal)",function(k){k.stopPropagation()});$(".pw-dropdown-toggle").each(g);$(document).on("mousedown",".pw-dropdown-toggle-click",e).on("mouseenter",".pw-dropdown-toggle:not(.pw-dropdown-toggle-click)",e).on("mouseleave",".pw-dropdown-toggle:not(.pw-dropdown-toggle-click)",d).on("mouseenter",".pw-dropdown-menu a.pw-has-ajax-items:not(.pw-ajax-items-loaded)",f).on("mouseleave",".pw-dropdown-menu a.pw-has-ajax-items",function(){h=null})}j()}};if(typeof ProcessWire!="undefined"){ProcessWire.confirm=function(b,a){if(typeof vex!="undefined"&&typeof a!="undefined"){vex.dialog.confirm({message:b,callback:function(c){if(c){a()}}})}else{if(typeof a!="undefined"){if(confirm(b)){a()}}else{return confirm(b)}}};ProcessWire.alert=function(b,a){if(typeof a=="undefined"){var a=false}if(typeof vex!="undefined"){if(a){vex.dialog.alert({unsafeMessage:b})}else{vex.dialog.alert(b)}}else{alert(b)}};ProcessWire.prompt=function(b,c,a){if(typeof vex=="undefined"){alert("prompt function requires vex");return}return vex.dialog.prompt({message:b,placeholder:c,callback:a})}}; \ No newline at end of file