diff --git a/CHANGELOG.md b/CHANGELOG.md
index 35ae5be1..7fb2d5be 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+### HEAD
+* Add module for a cleaner navigation walker ([#73](https://github.com/roots/soil/issues/72))
+* Use short array syntax ([#72](https://github.com/roots/soil/issues/72))
+
### 3.2.0: April 12th, 2015
* Add module for loading jQuery from Google's CDN with a local fallback ([#64](https://github.com/roots/soil/issues/64))
* Add note about activating the plugin ([#62](https://github.com/roots/soil/issues/62))
diff --git a/README.md b/README.md
index c415ab00..28afc881 100644
--- a/README.md
+++ b/README.md
@@ -42,6 +42,9 @@ wp plugin activate soil
* **Cleaner WordPress markup**
`add_theme_support('soil-clean-up');`
+* **Cleaner walker for navigation menus**
+ `add_theme_support('soil-nav-walker');`
+
* **Root relative URLs**
`add_theme_support('soil-relative-urls');`
diff --git a/lib/utils.php b/lib/utils.php
new file mode 100644
index 00000000..c5f4fec7
--- /dev/null
+++ b/lib/utils.php
@@ -0,0 +1,38 @@
+Home
+ *
+ *
+ *
+ * You can enable/disable this feature in functions.php (or lib/config.php if you're using Sage):
+ * add_theme_support('soil-nav-walker');
+ */
+class NavWalker extends \Walker_Nav_Menu {
+ private $cpt; // Boolean, is current post a custom post type
+ private $archive; // Stores the archive page for current URL
+
+ public function __construct() {
+ add_filter('nav_menu_css_class', array($this, 'cssClasses'), 10, 2);
+ add_filter('nav_menu_item_id', '__return_null');
+ $cpt = get_post_type();
+ $this->cpt = in_array($cpt, get_post_types(array('_builtin' => false)));
+ $this->archive = get_post_type_archive_link($cpt);
+ }
+
+ public function checkCurrent($classes) {
+ return preg_match('/(current[-_])|active/', $classes);
+ }
+
+ // @codingStandardsIgnoreStart
+ function display_element($element, &$children_elements, $max_depth, $depth = 0, $args, &$output) {
+ $element->is_subitem = ((!empty($children_elements[$element->ID]) && (($depth + 1) < $max_depth || ($max_depth === 0))));
+
+ if ($element->is_subitem) {
+ foreach ($children_elements[$element->ID] as $child) {
+ if ($child->current_item_parent || Utils\url_compare($this->archive, $child->url)) {
+ $element->classes[] = 'active';
+ }
+ }
+ }
+
+ $element->is_active = strpos($this->archive, $element->url);
+
+ if ($element->is_active) {
+ $element->classes[] = 'active';
+ }
+
+ parent::display_element($element, $children_elements, $max_depth, $depth, $args, $output);
+ }
+ // @codingStandardsIgnoreEnd
+
+ public function cssClasses($classes, $item) {
+ $slug = sanitize_title($item->title);
+
+ if ($this->cpt) {
+ $classes = str_replace('current_page_parent', '', $classes);
+
+ if (Utils\url_compare($this->archive, $item->url)) {
+ $classes[] = 'active';
+ }
+ }
+
+ $classes = preg_replace('/(current(-menu-|[-_]page[-_])(item|parent|ancestor))/', 'active', $classes);
+ $classes = preg_replace('/^((menu|page)[-_\w+]+)+/', '', $classes);
+
+ $classes[] = 'menu-' . $slug;
+
+ $classes = array_unique($classes);
+
+ return array_filter($classes, 'Roots\\Soil\\Utils\\is_element_empty');
+ }
+}
+
+/**
+ * Clean up wp_nav_menu_args
+ *
+ * Remove the container
+ * Remove the id="" on nav menu items
+ */
+function nav_menu_args($args = '') {
+ $nav_menu_args = [];
+ $nav_menu_args['container'] = false;
+
+ if (!$args['items_wrap']) {
+ $nav_menu_args['items_wrap'] = '';
+ }
+
+ if (!$args['walker']) {
+ $nav_menu_args['walker'] = new \Roots\Soil\Nav\NavWalker();
+ }
+
+ return array_merge($args, $nav_menu_args);
+}
+add_filter('wp_nav_menu_args', __NAMESPACE__ . '\\nav_menu_args');
+add_filter('nav_menu_item_id', '__return_null');
diff --git a/modules/relative-urls.php b/modules/relative-urls.php
index de623ca0..5d54e60e 100644
--- a/modules/relative-urls.php
+++ b/modules/relative-urls.php
@@ -11,18 +11,6 @@
* You can enable/disable this feature in functions.php (or lib/config.php if you're using Sage):
* add_theme_support('soil-relative-urls');
*/
-function root_relative_url($input) {
- preg_match('|(?:https?:)?//([^/]+)(/.*)|i', $input, $matches);
-
- if (!isset($matches[1]) || !isset($matches[2])) {
- return $input;
- } elseif (($matches[1] === $_SERVER['SERVER_NAME']) || $matches[1] === $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT']) {
- return wp_make_link_relative($input);
- } else {
- return $input;
- }
-}
-
function enable_root_relative_urls() {
return !(is_admin() || preg_match('/sitemap(_index)?\.xml/', $_SERVER['REQUEST_URI']) || in_array($GLOBALS['pagenow'], ['wp-login.php', 'wp-register.php']));
}
@@ -47,7 +35,7 @@ function enable_root_relative_urls() {
'style_loader_src'
];
- add_filters($root_rel_filters, __NAMESPACE__ . '\\root_relative_url');
+ add_filters($root_rel_filters, 'Roots\\Soil\\Utils\\root_relative_url');
}
function add_filters($tags, $function) {
diff --git a/soil.php b/soil.php
index a4b95555..40a0f6a8 100644
--- a/soil.php
+++ b/soil.php
@@ -50,6 +50,8 @@ public function set($options) {
}
}
+require_once(plugin_dir_path(__FILE__) . 'lib/utils.php');
+
function load_modules() {
global $_wp_theme_features;
foreach (glob(__DIR__ . '/modules/*.php') as $file) {