Custom Menu Wizard Widget - Version 1.2.0

Version Description

  • added custom_menu_wizard shortcode, to run the widget from within content

  • moved the 'no ancestor' fallback into new Fallback collapsible section, and added a fallback for Current Item with no children

  • fixed a bug with optgroups/options made available for the 'Children of' selector after the widget has been saved (also affected disabled fields and styling)

  • don't include menus with no items

  • updated demo.html

Download this release

Release Info

Developer wizzud
Plugin Icon 128x128 Custom Menu Wizard Widget
Version 1.2.0
Comparing to
See all releases

Code changes from version 1.1.0 to 1.2.0

Files changed (4) hide show
  1. custom-menu-wizard.js +54 -28
  2. custom-menu-wizard.php +402 -72
  3. demo.html +119 -45
  4. readme.txt +211 -31
custom-menu-wizard.js CHANGED
@@ -1,12 +1,11 @@
1
  /* Plugin Name: Custom Menu Wizard
2
- * Version: 1.1.0
3
  * Author: Roger Barrett
4
  *
5
  * Script for controlling this widget's options (in Admin -> Widgets)
6
  */
7
  jQuery(function($){
8
- var dotPrefix = '.widget-custom-menu-wizard',
9
- filterItems = $('select' + dotPrefix + '-listen');
10
  $(document)
11
  //fieldsets...
12
  .on('click', dotPrefix + '-collapsible-fieldset', function(){
@@ -20,33 +19,60 @@ jQuery(function($){
20
  this.blur();
21
  return false;
22
  })
23
- //change of menu...
24
- .on('change', dotPrefix + '-selectmenu', function(){
25
- var select = $('select' + dotPrefix + '-listen', this.form),
26
- from = $('#' + select.attr('id') + '_ignore').find('optgroup'),
 
 
 
 
27
  groupClone;
28
- if(from.length > this.selectedIndex){
29
- if(select.val() > 0){
30
- select.val(0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  }
32
- groupClone = from.eq( this.selectedIndex ).clone();
33
- groupClone.find('option[selected]').removeAttr('selected').prop('selected', false);
34
- select.find('optgroup').remove();
35
- select.append(groupClone).trigger('change');
36
  }
37
- })
38
- //enableif and disableif...
39
- .on('change', dotPrefix + '-listen', function(){
40
- var listeners = $(dotPrefix + '-listen', this.form),
41
- showAll = listeners.eq(0).prop('checked'),
42
- rootParent = !showAll && listeners.filter('select').val() < 0;
43
- $(dotPrefix + '-disableif', this.form).css({color:showAll ? '#999999' : 'inherit'}).find('input,select').prop('disabled', showAll);
44
- $(dotPrefix + '-enableif', this.form).css({color:!rootParent ? '#999999' : 'inherit'}).find('input,select').prop('disabled', !rootParent);
45
  });
46
- //remove non-active optgroups...
47
- filterItems.find('optgroup').filter(function(){
48
- return !$(this).data('cmwActiveMenu');
49
- }).remove();
50
- //trigger change...
51
- filterItems.trigger('change');
 
 
 
 
 
 
 
 
 
 
 
 
52
  });
1
  /* Plugin Name: Custom Menu Wizard
2
+ * Version: 1.2.0
3
  * Author: Roger Barrett
4
  *
5
  * Script for controlling this widget's options (in Admin -> Widgets)
6
  */
7
  jQuery(function($){
8
+ var dotPrefix = '.widget-custom-menu-wizard';
 
9
  $(document)
10
  //fieldsets...
11
  .on('click', dotPrefix + '-collapsible-fieldset', function(){
19
  this.blur();
20
  return false;
21
  })
22
+ //change of menu, and enableif / disableif...
23
+ .on('change', dotPrefix + '-listen', function(){
24
+ var listeners = $(dotPrefix + '-listen', this.form),
25
+ selectMenu = listeners.filter(dotPrefix + '-selectmenu'),
26
+ others = listeners.not(selectMenu),
27
+ showAll = others.eq(0).prop('checked'),
28
+ filterItem = others.filter('select').eq(0),
29
+ fiVal = parseInt(filterItem.val(), 10),
30
  groupClone;
31
+ if(selectMenu.is(this)){
32
+ selectMenu = this.selectedIndex;
33
+ if(!filterItem.find('optgroup').filter(function(){
34
+ var keep = $(this).data('cmwOptgroupIndex') === selectMenu;
35
+ if(!keep){
36
+ $(this).remove();
37
+ }
38
+ return keep;
39
+ }).length){
40
+ groupClone = $('#' + filterItem.attr('id') + '_ignore').find('optgroup').eq(selectMenu).clone();
41
+ if(groupClone.length){
42
+ if(fiVal > 0){
43
+ fiVal = 0;
44
+ filterItem.val(fiVal);
45
+ }
46
+ groupClone.find('option[selected]').removeAttr('selected').prop('selected', false);
47
+ filterItem.append(groupClone);
48
+ }
49
  }
 
 
 
 
50
  }
51
+ $.each(
52
+ { '' : showAll,
53
+ 'not-rp' : showAll || fiVal >= 0,
54
+ 'not-ci' : showAll || !!fiVal
55
+ },
56
+ function(k, v){
57
+ $(dotPrefix + '-disableif' + k, this.form).css({color:v ? '#999999' : 'inherit'}).find('input,select').prop('disabled', v);
58
+ });
59
  });
60
+
61
+ //when a widget is opened or saved, trigger change on the filter_item select...
62
+ //to do this I've elected to modify WP's window.wpWidgets object and intercept its fixLabels()
63
+ //method, which, handily, gets called whenever a widget is opened or saved!
64
+ if(window.wpWidgets && window.wpWidgets.fixLabels && !window.wpWidgets._cmw_fixLabels){
65
+ //save the original...
66
+ window.wpWidgets._cmw_fixLabels = window.wpWidgets.fixLabels;
67
+ //replace the original...
68
+ window.wpWidgets.fixLabels = function(widget){
69
+ //trigger change on selectmenu...
70
+ widget.find('.widget-custom-menu-wizard-selectmenu').trigger('change');
71
+ //run the original...
72
+ window.wpWidgets._cmw_fixLabels(widget);
73
+ };
74
+ }else{
75
+ //one-off fallback...
76
+ $(dotPrefix + '-selectmenu').trigger('change');
77
+ }
78
  });
custom-menu-wizard.php CHANGED
@@ -1,15 +1,22 @@
1
  <?php
2
  /*
3
  * Plugin Name: Custom Menu Wizard
4
- * Plugin URI: http://www.wizzud.com/
5
  * Description: Full control over the wp_nav_menu parameters for a custom menu, plus the ability to filter for specific level(s), or for children of a selected menu item or the current item
6
- * Version: 1.1.0
7
  * Author: Roger Barrett
8
  * Author URI: http://www.wizzud.com/
9
  * License: GPL2+
10
  */
11
 
12
  /*
 
 
 
 
 
 
 
13
  * v1.1.0 change log:
14
  * - added 'Current Root Item' and 'Current Parent Item' to the 'Children of' filter
15
  * - added an Output option to include both the parent item and the parent's siblings (for a successful 'Children of' filter)
@@ -21,13 +28,14 @@
21
  * - moved the setting of 'disabled' attributes on INPUTs/SELECTs from PHP into javascript
22
  */
23
 
24
- $Custom_Menu_Wizard_Widget_Version = '1.1.0';
25
 
26
  /**
27
- * registers the widget
28
  */
29
  function custom_menu_wizard_register_widget() {
30
  register_widget('Custom_Menu_Wizard_Widget');
 
31
  }
32
  add_action('widgets_init', 'custom_menu_wizard_register_widget');
33
 
@@ -73,6 +81,8 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
73
  /**
74
  * pre-filters elements then calls parent::walk()
75
  *
 
 
76
  * @param array $elements Menu items
77
  * @param integer $max_depth
78
  * @return string
@@ -92,27 +102,33 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
92
  // include_parent : true = include the filter_item menu item
93
  // include_parent_siblings : true = include the siblings (& parent) of the filter_item menu item
94
  // include_ancestors : true = include the filter_item menu item plus all it's ancestors
95
- // title_from_parent : true = widget wants parent's title as title
 
96
  // start_level : integer, 1+
97
  // depth : integer, replacement for max_depth and also applied to 'flat' output
98
  // fallback_no_ancestor : true = if looking for an ancestor (root or parent) of a top-level current item, fallback to current item (v1.1.0)
99
  // fallback_include_parent : true = if fallback_no_ancestor comes into play then force include_parent to true (v1.1.0)
100
  // fallback_include_parent_siblings : true = if fallback_no_ancestor comes into play then force include_parent_siblings to true (v1.1.0)
 
 
 
101
  //$elements is an array of objects, indexed by position within the menu (menu_order),
102
  //starting at 1 and incrementing sequentially regardless of parentage (ie. first item is [1],
103
  //second item is [2] whether it's at root or subordinate to first item)
 
104
 
105
  $find_kids_of = $cmw['filter'];
106
  $find_current_item = $find_kids_of && empty( $cmw['filter_item'] );
107
  $find_current_parent = $find_kids_of && $cmw['filter_item'] == -1; //v1.1.0
108
  $find_current_root = $find_kids_of && $cmw['filter_item'] == -2; //v1.1.0
109
- $fallback_current_item = $cmw['fallback_no_ancestor'] && ( $find_current_parent || $find_current_root ); //v1.1.0
110
  //these could change depending on whether a fallback comes into play (v1.1.0)
111
  $include_parent = $cmw['include_parent'];
112
  $include_parent_siblings = $cmw['include_parent_siblings'];
113
 
114
  //are we looking for something in particular?...
115
  if( $find_kids_of || $cmw['start_level'] > 1 ){
 
116
  $id_field = $this->db_fields['id']; //eg. = 'db_id'
117
  $parent_field = $this->db_fields['parent']; //eg. = 'menu_item_parent'
118
 
@@ -151,6 +167,10 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
151
  //also note that orphans (in the original menu) are ignored by this widget!
152
 
153
  if( isset( $temp[ $item->$id_field ] ) ){
 
 
 
 
154
  //are we at or below the start level?...
155
  if( $temp[ $item->$id_field ]['level'] >= $start_level ){
156
  //are we still looking for a starting point?...
@@ -164,7 +184,7 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
164
  //...we're looking for a current root ancestor, and this is one...
165
  ( $find_current_root && $item->current_item_ancestor ) ||
166
  //...we've got a fallback for a top-level current item with no ancestor...
167
- ( $fallback_current_item && $item->current && $temp[ $item->$id_field ]['level'] == 1 ) ||
168
  //...we're looking for a particular menu item, and this is it...
169
  ( $cmw['filter_item'] == $item->$id_field )
170
  ){
@@ -173,8 +193,8 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
173
  if( !$find_kids_of ){
174
  $keep_items[] = $item;
175
  }
176
- //v1.1.0 if this was the fallback option, we may need to update $include_parent[_siblings]...
177
- if( $fallback_current_item && $item->current && $temp[ $item->$id_field ]['level'] == 1 ){
178
  $include_parent = $include_parent || $cmw['fallback_include_parent'];
179
  $include_parent_siblings = $include_parent_siblings || $cmw['fallback_include_parent_siblings'];
180
  }
@@ -193,7 +213,7 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
193
  //having found at least one, any more have to be:
194
  // - within max_depth of the first one found, and
195
  // - either it's an unspecific search, or we have the parent already
196
- }elseif( $temp[ $item->$id_field ]['level'] <= $max_level && (!$find_kids_of || in_array( $item->$parent_field, $keep_ids ) ) ){
197
  $keep_ids[] = $item->$id_field;
198
  $keep_items[] = $item;
199
  }
@@ -201,6 +221,23 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
201
  }
202
  } //end foreach
203
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  unset( $keep_ids );
205
  if( !empty( $keep_items) ){
206
 
@@ -211,14 +248,23 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
211
  //last element is now the parent (if there is one)
212
  $i = $j = count( $breadcrumb );
213
 
214
- //do we want the parent's title as the widget title?...
215
  if( $find_kids_of && $cmw['title_from_parent'] && $i > 0 ){
216
- $cmw['parent_title'] = apply_filters(
217
  'the_title',
218
  $elements[ $breadcrumb[ $i - 1 ] ]->title,
219
  $elements[ $breadcrumb[ $i - 1 ] ]->ID
220
  );
221
  }
 
 
 
 
 
 
 
 
 
222
 
223
  //if we have a parent and we also want all the parent siblings, then we need
224
  //to pop the parent off the bottom of temp and either append or prepend the
@@ -324,16 +370,21 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
324
  'fallback_no_ancestor', //v1.1.0 added
325
  'fallback_include_parent', //v1.1.0 added
326
  'fallback_include_parent_siblings', //v1.1.0 added
 
 
 
327
  'flat_output',
328
  'include_parent',
329
  'include_parent_siblings', //v1.1.0 added
330
  'include_ancestors',
331
  'hide_empty', //v1.1.0: this now only has relevance prior to WP v3.6
332
  'title_from_parent',
 
333
  'ol_root',
334
  'ol_sub',
335
  //field section toggles...
336
  'fs_filter',
 
337
  'fs_output',
338
  'fs_container',
339
  'fs_classes',
@@ -370,24 +421,23 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
370
  array(
371
  'classname' => 'widget_custom_menu_wizard',
372
  'description' => __('Add a custom menu, or part of one, as a widget')
373
- // ),
374
- // array(
375
- // 'width'=>560
376
  )
377
  );
378
  }
379
 
380
  /**
381
- * removes itself from the filters and, if available and requested, stores parent_title in the instance for use as the widget title
382
  *
383
  * @param array $items Filtered menu items
384
  * @param object $args
385
  * @return array Menu items
386
  */
387
- function cmw_filter_retain_parent_title($items, $args){
388
  remove_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_retain_parent_title' ), 10, 2);
389
- if( !empty( $args->_custom_menu_wizard['title_from_parent'] ) && !empty( $args->_custom_menu_wizard['parent_title'] ) ){
390
- $this->cmw_title_from_parent = $args->_custom_menu_wizard['parent_title'];
 
 
391
  }
392
  return $items;
393
  }
@@ -434,6 +484,8 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
434
  /**
435
  * produces the widget HTML at the front end
436
  *
 
 
437
  * @param object $args Widget arguments
438
  * @param array $instance Configuration for this widget instance
439
  */
@@ -460,7 +512,7 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
460
  //v1.1.0 As of WP v3.6, wp_nav_menu() automatically prevents any HTML output if there are no items...
461
  $instance['hide_empty'] = $instance['hide_empty'] && $this->_pre_3point6();
462
 
463
- $this->cmw_title_from_parent = '';
464
 
465
  //fetch menu...
466
  if( !empty($instance['menu'] ) ){
@@ -483,8 +535,8 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
483
  $instance['container_class'] = "menu-{$menu->slug}-container {$instance['container_class']}";
484
  }
485
 
486
- if( $instance['title_from_parent'] ){
487
- add_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_retain_parent_title' ), 10, 2);
488
  }
489
 
490
  if( $instance['hide_empty'] ){
@@ -512,10 +564,14 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
512
  'fallback_no_ancestor' => $instance['fallback_no_ancestor'], //v1.1.0
513
  'fallback_include_parent' => $instance['fallback_include_parent'], //v1.1.0
514
  'fallback_include_parent_siblings' => $instance['fallback_include_parent_siblings'], //v1.1.0
 
 
 
515
  'include_parent' => $instance['include_parent'],
516
  'include_parent_siblings' => $instance['include_parent_siblings'], //v1.1.0
517
  'include_ancestors' => $instance['include_ancestors'],
518
  'title_from_parent' => $instance['title_from_parent'],
 
519
  'ol_root' => $instance['ol_root'],
520
  'ol_sub' => $instance['ol_sub'],
521
  'flat_output' => $instance['flat_output'],
@@ -530,7 +586,7 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
530
  $params['container_class'] = $instance['container_class'];
531
  }
532
  //NB: wp_nav_menu() is in wp-includes/nav-menu-template.php
533
- $out = wp_nav_menu( $params );
534
 
535
  if( $instance['hide_empty'] ){
536
  remove_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
@@ -539,8 +595,15 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
539
  //only put something out if there is something to put out...
540
  if( !empty( $out ) ){
541
 
542
- if( $instance['title_from_parent'] && isset( $this->cmw_title_from_parent ) ){
543
- $title = $this->cmw_title_from_parent;
 
 
 
 
 
 
 
544
  }
545
  if( empty( $title ) ){
546
  $title = $instance['hide_title'] ? '' : $instance['title'];
@@ -605,11 +668,20 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
605
  }
606
 
607
  //get menus...
608
- $menus = get_terms( 'nav_menu', array( 'hide_empty' => false ) );
 
 
 
 
 
 
 
 
 
609
 
610
- //if no menus exist, suggest the user go create one...
611
- if( empty( $menus ) ){
612
- echo '<p>'. sprintf( __('No menus have been created yet. <a href="%s">Create one</a>.'), admin_url('nav-menus.php') ) .'</p>';
613
  return;
614
  }
615
 
@@ -619,7 +691,7 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
619
  ?>
620
  <p>
621
  <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:') ?></label>
622
- <label for="<?php echo $this->get_field_id('hide_title'); ?>" class="alignright">
623
  <input id="<?php echo $this->get_field_id('hide_title'); ?>" name="<?php echo $this->get_field_name('hide_title'); ?>"
624
  type="checkbox" value="1" <?php checked( $instance['hide_title'] ); ?> />
625
  <?php _e('Hide'); ?></label>
@@ -630,14 +702,16 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
630
 
631
  <p>
632
  <label for="<?php echo $this->get_field_id('menu'); ?>"><?php _e('Select Menu:'); ?></label>
633
- <select id="<?php echo $this->get_field_id('menu'); ?>" class="widget-<?php echo $this->id_base; ?>-selectmenu"
 
634
  name="<?php echo $this->get_field_name('menu'); ?>">
635
  <?php
636
  foreach( $menus as $i=>$menu ){
637
- $menus[ $i ]->_items = wp_get_nav_menu_items( $menu->term_id );
638
  ?>
639
  <option <?php selected($instance['menu'], $menu->term_id); ?> value="<?php echo $menu->term_id; ?>"><?php echo $menu->name; ?></option>
640
  <?php
 
641
  }
642
  ?>
643
  </select>
@@ -651,11 +725,11 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
651
  ?>
652
  <small class="alignright" style="line-height:1;"><a href="<?php echo plugins_url('/demo.html', __FILE__); ?>" target="_blank"><?php _e(' demo' ); ?></a></small>
653
  <p>
654
- <label for="<?php echo $this->get_field_id('filter'); ?>_0">
655
  <input id="<?php echo $this->get_field_id('filter'); ?>_0" class="widget-<?php echo $this->id_base; ?>-listen"
656
  name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="0" <?php checked(!$instance['filter']); ?> />
657
  <?php _e('Show all'); ?></label>
658
- <br /><label for="<?php echo $this->get_field_id('filter'); ?>_1">
659
  <input id="<?php echo $this->get_field_id('filter'); ?>_1" class="widget-<?php echo $this->id_base; ?>-listen"
660
  name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="1" <?php checked($instance['filter']); ?> />
661
  <?php _e('Children of:'); ?></label>
@@ -672,12 +746,11 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
672
 
673
  $maxlevel = 1;
674
  foreach( $menus as $i=>$menu ){
675
- //v1.1.0 changed the indents from padding to hyphen-space (for IE!!!! grrrr...)
676
- $itemindents = array('0' => 0);
677
- $activeOpts = ( $i == 0 && empty($instance['menu']) ) || $instance['menu'] == $menu->term_id;
678
- $style = $activeOpts ? '' : ' style="display:none;"';
679
- $menuOptions[] = '<optgroup label="' . $menu->name . '" data-cmw-active-menu="' . ($activeOpts ? 'true' : 'false') .'">';
680
  if( !empty( $menu->_items ) ){
 
 
 
681
  foreach( $menu->_items as $item ){
682
  //exclude orpans!
683
  if( isset($itemindents[ $item->menu_item_parent ])){
@@ -688,8 +761,8 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
688
  str_repeat('- ', $itemindents[ $item->menu_item_parent ]) . $item->title . '</option>';
689
  }
690
  }
 
691
  }
692
- $menuOptions[] = '</optgroup>';
693
  }
694
  $menuOptions = implode("\n", $menuOptions);
695
  echo $menuOptions;
@@ -702,25 +775,6 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
702
  </select>
703
  </p>
704
 
705
- <p class="widget-<?php echo $this->id_base; ?>-enableif">
706
- <label for="<?php echo $this->get_field_id('fallback_no_ancestor'); ?>">
707
- <input id="<?php echo $this->get_field_id('fallback_no_ancestor'); ?>"
708
- name="<?php echo $this->get_field_name('fallback_no_ancestor'); ?>" type="checkbox" value="1"
709
- <?php checked($instance['fallback_no_ancestor']); ?> />
710
- <?php _e('Fallback to Current Item, and'); ?></label>
711
- <br /><label for="<?php echo $this->get_field_id('fallback_include_parent'); ?>" style="padding-left:1em;">
712
- <input id="<?php echo $this->get_field_id('fallback_include_parent'); ?>"
713
- name="<?php echo $this->get_field_name('fallback_include_parent'); ?>" type="checkbox" value="1"
714
- <?php checked($instance['fallback_include_parent']); ?> />
715
- <?php _e('Include Parent...'); ?> </label>
716
- <label for="<?php echo $this->get_field_id('fallback_include_parent_siblings'); ?>">
717
- <input id="<?php echo $this->get_field_id('fallback_include_parent_siblings'); ?>"
718
- name="<?php echo $this->get_field_name('fallback_include_parent_siblings'); ?>" type="checkbox" value="1"
719
- <?php checked($instance['fallback_include_parent_siblings']); ?> />
720
- <?php _e('&amp; its Siblings'); ?></label>
721
- <br /><small><em><?php _e('If Current Root/Parent and no ancestor exists'); ?></em></small>
722
- </p>
723
-
724
  <p>
725
  <label for="<?php echo $this->get_field_id('start_level'); ?>"><?php _e('Starting Level:'); ?></label>
726
  <select id="<?php echo $this->get_field_id('start_level'); ?>" name="<?php echo $this->get_field_name('start_level'); ?>">
@@ -753,6 +807,54 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
753
  </p>
754
  <?php $this->_close_a_field_section(); ?>
755
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
756
  <?php
757
  /**
758
  * start collapsible section : 'Output'
@@ -761,47 +863,56 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
761
  ?>
762
  <small class="alignright" style="line-height:1;"><a href="<?php echo plugins_url('/demo.html', __FILE__); ?>" target="_blank"><?php _e(' demo' ); ?></a></small>
763
  <p>
764
- <label for="<?php echo $this->get_field_id('flat_output'); ?>_0">
765
  <input id="<?php echo $this->get_field_id('flat_output'); ?>_0" name="<?php echo $this->get_field_name('flat_output'); ?>"
766
  type="radio" value="0" <?php checked(!$instance['flat_output']); ?> />
767
  <?php _e('Hierarchical'); ?></label>
768
- &nbsp;<label for="<?php echo $this->get_field_id('flat_output'); ?>_1">
769
  <input id="<?php echo $this->get_field_id('flat_output'); ?>_1" name="<?php echo $this->get_field_name('flat_output'); ?>"
770
  type="radio" value="1" <?php checked($instance['flat_output']); ?> />
771
  <?php _e('Flat'); ?></label>
772
  </p>
773
 
774
  <p class="widget-<?php echo $this->id_base; ?>-disableif">
775
- <label for="<?php echo $this->get_field_id('include_parent'); ?>">
776
  <input id="<?php echo $this->get_field_id('include_parent'); ?>"
777
  name="<?php echo $this->get_field_name('include_parent'); ?>" type="checkbox"
778
  value="1" <?php checked($instance['include_parent']); ?> />
779
  <?php _e('Include Parent...'); ?> </label>
780
- <label for="<?php echo $this->get_field_id('include_parent_siblings'); ?>">
781
  <input id="<?php echo $this->get_field_id('include_parent_siblings'); ?>"
782
  name="<?php echo $this->get_field_name('include_parent_siblings'); ?>" type="checkbox"
783
  value="1" <?php checked($instance['include_parent_siblings']); ?> />
784
  <?php _e('&amp; its Siblings'); ?></label>
785
- <br /><label for="<?php echo $this->get_field_id('include_ancestors'); ?>">
786
  <input id="<?php echo $this->get_field_id('include_ancestors'); ?>"
787
  name="<?php echo $this->get_field_name('include_ancestors'); ?>" type="checkbox"
788
  value="1" <?php checked($instance['include_ancestors']); ?> />
789
  <?php _e('Include Ancestors'); ?></label>
790
- <br /><label for="<?php echo $this->get_field_id('title_from_parent'); ?>">
791
  <input id="<?php echo $this->get_field_id('title_from_parent'); ?>"
792
  name="<?php echo $this->get_field_name('title_from_parent'); ?>" type="checkbox"
793
  value="1" <?php checked($instance['title_from_parent']); ?> />
794
- <?php _e('Title from Parent Item'); ?></label>
795
- <br /><small><em><?php _e('Only if the &quot;Children of:&quot; Filter returns items'); ?></em></small>
 
 
 
 
 
 
 
 
 
796
  </p>
797
 
798
  <p>
799
  <?php _e('Change UL to OL:'); ?>
800
- <br /><label for="<?php echo $this->get_field_id('ol_root'); ?>">
801
  <input id="<?php echo $this->get_field_id('ol_root'); ?>" name="<?php echo $this->get_field_name('ol_root'); ?>"
802
  type="checkbox" value="1" <?php checked($instance['ol_root']); ?> />
803
  <?php _e('Top Level'); ?></label>
804
- &nbsp;<label for="<?php echo $this->get_field_id('ol_sub'); ?>">
805
  <input id="<?php echo $this->get_field_id('ol_sub'); ?>" name="<?php echo $this->get_field_name('ol_sub'); ?>"
806
  type="checkbox" value="1" <?php checked($instance['ol_sub']); ?> />
807
  <?php _e('Sub-Levels'); ?></label>
@@ -814,7 +925,7 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
814
  if( $this->_pre_3point6() ){
815
  ?>
816
  <p>
817
- <label for="<?php echo $this->get_field_id('hide_empty'); ?>">
818
  <input id="<?php echo $this->get_field_id('hide_empty'); ?>" name="<?php echo $this->get_field_name('hide_empty'); ?>"
819
  type="checkbox" value="1" <?php checked($instance['hide_empty']); ?> />
820
  <?php _e('Hide Widget if Empty'); ?></label>
@@ -923,7 +1034,7 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
923
  <input id="<?php echo $this->get_field_id($fname); ?>" class="hidden-field" name="<?php echo $this->get_field_name($fname); ?>"
924
  type="checkbox" value="1" <?php checked($collapsed); ?> />
925
  <div style="background:transparent url(images/arrows.png) no-repeat 0 <?php echo $collapsed ? '0' : '-36px'; ?>;height:16px; width:16px;float:right;outline:0 none;"></div>
926
- <h3 style="font-size:1em;margin:0;padding:2px 0.5em;"><?php echo $text; ?></h3>
927
  </div>
928
  <div class="hide-if-js"<?php echo !$collapsed ? ' style="display:block;"' : ''; ?>>
929
  <?php
@@ -947,4 +1058,223 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
947
  return version_compare( strtolower( $wp_version ), '3.6a', '<' );
948
  } //end _pre_3point6()
949
 
950
- } //end of class
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
  /*
3
  * Plugin Name: Custom Menu Wizard
4
+ * Plugin URI: http://wordpress.org/plugins/custom-menu-wizard/
5
  * Description: Full control over the wp_nav_menu parameters for a custom menu, plus the ability to filter for specific level(s), or for children of a selected menu item or the current item
6
+ * Version: 1.2.0
7
  * Author: Roger Barrett
8
  * Author URI: http://www.wizzud.com/
9
  * License: GPL2+
10
  */
11
 
12
  /*
13
+ * v1.2.0 change log:
14
+ * - added custom_menu_wizard shortcode, to run the widget from within content
15
+ * - moved the 'no ancestor' fallback into new Fallback collapsible section, and added a fallback for Current Item with no children
16
+ * - fixed bug with optgroups/options made available for the 'Children of' selector after the widget has been saved (also affecting disabled fields & styling)
17
+ * - don't include menus with no items
18
+ * - updated demo.html
19
+ *
20
  * v1.1.0 change log:
21
  * - added 'Current Root Item' and 'Current Parent Item' to the 'Children of' filter
22
  * - added an Output option to include both the parent item and the parent's siblings (for a successful 'Children of' filter)
28
  * - moved the setting of 'disabled' attributes on INPUTs/SELECTs from PHP into javascript
29
  */
30
 
31
+ $Custom_Menu_Wizard_Widget_Version = '1.2.0';
32
 
33
  /**
34
+ * registers the widget and adds the shortcode
35
  */
36
  function custom_menu_wizard_register_widget() {
37
  register_widget('Custom_Menu_Wizard_Widget');
38
+ add_shortcode('custom_menu_wizard', 'custom_menu_wizard_widget_shortcode');
39
  }
40
  add_action('widgets_init', 'custom_menu_wizard_register_widget');
41
 
81
  /**
82
  * pre-filters elements then calls parent::walk()
83
  *
84
+ * @filters : custom_menu_wizard_walker_items array of filtered menu elements; array of args
85
+ *
86
  * @param array $elements Menu items
87
  * @param integer $max_depth
88
  * @return string
102
  // include_parent : true = include the filter_item menu item
103
  // include_parent_siblings : true = include the siblings (& parent) of the filter_item menu item
104
  // include_ancestors : true = include the filter_item menu item plus all it's ancestors
105
+ // title_from_parent : true = widget wants parent's title
106
+ // title_from_current : true = widget wants current item's title (v1.2.0)
107
  // start_level : integer, 1+
108
  // depth : integer, replacement for max_depth and also applied to 'flat' output
109
  // fallback_no_ancestor : true = if looking for an ancestor (root or parent) of a top-level current item, fallback to current item (v1.1.0)
110
  // fallback_include_parent : true = if fallback_no_ancestor comes into play then force include_parent to true (v1.1.0)
111
  // fallback_include_parent_siblings : true = if fallback_no_ancestor comes into play then force include_parent_siblings to true (v1.1.0)
112
+ // fallback_no_children : true = if looking for a current item, and that item turns out to have no children, fallback to current parent (v1.2.0)
113
+ // fallback_nc_include_parent : true = if fallback_no_children comes into play then force include_parent to true (v1.2.0)
114
+ // fallback_nc_include_parent_siblings : true = if fallback_no_children comes into play then force include_parent_siblings to true (v1.2.0)
115
  //$elements is an array of objects, indexed by position within the menu (menu_order),
116
  //starting at 1 and incrementing sequentially regardless of parentage (ie. first item is [1],
117
  //second item is [2] whether it's at root or subordinate to first item)
118
+ $cmw['_titles'] = array();
119
 
120
  $find_kids_of = $cmw['filter'];
121
  $find_current_item = $find_kids_of && empty( $cmw['filter_item'] );
122
  $find_current_parent = $find_kids_of && $cmw['filter_item'] == -1; //v1.1.0
123
  $find_current_root = $find_kids_of && $cmw['filter_item'] == -2; //v1.1.0
124
+ $fallback_to_current_item = $cmw['fallback_no_ancestor'] && ( $find_current_parent || $find_current_root ); //v1.1.0
125
  //these could change depending on whether a fallback comes into play (v1.1.0)
126
  $include_parent = $cmw['include_parent'];
127
  $include_parent_siblings = $cmw['include_parent_siblings'];
128
 
129
  //are we looking for something in particular?...
130
  if( $find_kids_of || $cmw['start_level'] > 1 ){
131
+
132
  $id_field = $this->db_fields['id']; //eg. = 'db_id'
133
  $parent_field = $this->db_fields['parent']; //eg. = 'menu_item_parent'
134
 
167
  //also note that orphans (in the original menu) are ignored by this widget!
168
 
169
  if( isset( $temp[ $item->$id_field ] ) ){
170
+ //let's keep track of current menu item (as index into $elements)...
171
+ if( $item->current ){
172
+ $ci_index = $i;
173
+ }
174
  //are we at or below the start level?...
175
  if( $temp[ $item->$id_field ]['level'] >= $start_level ){
176
  //are we still looking for a starting point?...
184
  //...we're looking for a current root ancestor, and this is one...
185
  ( $find_current_root && $item->current_item_ancestor ) ||
186
  //...we've got a fallback for a top-level current item with no ancestor...
187
+ ( $fallback_to_current_item && $item->current && $temp[ $item->$id_field ]['level'] == 1 ) ||
188
  //...we're looking for a particular menu item, and this is it...
189
  ( $cmw['filter_item'] == $item->$id_field )
190
  ){
193
  if( !$find_kids_of ){
194
  $keep_items[] = $item;
195
  }
196
+ //v1.1.0 if this was the fallback_no_ancestor option, we may need to update $include_parent[_siblings]...
197
+ if( $fallback_to_current_item && $item->current && $temp[ $item->$id_field ]['level'] == 1 ){
198
  $include_parent = $include_parent || $cmw['fallback_include_parent'];
199
  $include_parent_siblings = $include_parent_siblings || $cmw['fallback_include_parent_siblings'];
200
  }
213
  //having found at least one, any more have to be:
214
  // - within max_depth of the first one found, and
215
  // - either it's an unspecific search, or we have the parent already
216
+ }elseif( $temp[ $item->$id_field ]['level'] <= $max_level && ( !$find_kids_of || in_array( $item->$parent_field, $keep_ids ) ) ){
217
  $keep_ids[] = $item->$id_field;
218
  $keep_items[] = $item;
219
  }
221
  }
222
  } //end foreach
223
 
224
+ //v1.2.0 do we need to invoke the fallback for a childless 'children of current item'?...
225
+ //note that this is slightly different to the no_ancestor fallback, in that we are treating it like a switch from current item
226
+ //to current parent, *except* that here the fallback for not having an ancestor is to pretend there's a level-0 ancestor (above
227
+ //root); obviously it (the level-0 ancestor) will no item of its own, which means the kids now have no (real) parent
228
+ if( $find_current_item && $cmw['fallback_no_children'] && empty( $keep_items ) && !empty( $keep_ids ) ){
229
+ //what are the options?
230
+ // - the current item & its siblings
231
+ // - ... optionally plus its parent (if there is one)
232
+ // - ... optionally plus its parent (if there is one) and the parent's siblings
233
+ //keep_ids[0] is the id of the current item; put the parent's kids into keep_items...
234
+ foreach( $temp[ $temp[ $keep_ids[0] ]['parent'] ]['kids'] as $item ){
235
+ $keep_items[] = $item;
236
+ }
237
+ $include_parent = $include_parent || $cmw['fallback_nc_include_parent'];
238
+ $include_parent_siblings = $include_parent_siblings || $cmw['fallback_nc_include_parent_siblings'];
239
+ }
240
+
241
  unset( $keep_ids );
242
  if( !empty( $keep_items) ){
243
 
248
  //last element is now the parent (if there is one)
249
  $i = $j = count( $breadcrumb );
250
 
251
+ //might we want the parent's title as the widget title?...
252
  if( $find_kids_of && $cmw['title_from_parent'] && $i > 0 ){
253
+ $cmw['_titles']['parent'] = apply_filters(
254
  'the_title',
255
  $elements[ $breadcrumb[ $i - 1 ] ]->title,
256
  $elements[ $breadcrumb[ $i - 1 ] ]->ID
257
  );
258
  }
259
+ //v1.2.0 might we want the current item's title as the widget title?...
260
+ //NB: using $ci_index because it's quite possible that the current menu item is not in $keep_items
261
+ if( !empty( $ci_index ) && $cmw['title_from_current'] ){
262
+ $cmw['_titles']['current'] = apply_filters(
263
+ 'the_title',
264
+ $elements[ $ci_index ]->title,
265
+ $elements[ $ci_index ]->ID
266
+ );
267
+ }
268
 
269
  //if we have a parent and we also want all the parent siblings, then we need
270
  //to pop the parent off the bottom of temp and either append or prepend the
370
  'fallback_no_ancestor', //v1.1.0 added
371
  'fallback_include_parent', //v1.1.0 added
372
  'fallback_include_parent_siblings', //v1.1.0 added
373
+ 'fallback_no_children', //v1.2.0 added
374
+ 'fallback_nc_include_parent', //v1.2.0 added
375
+ 'fallback_nc_include_parent_siblings', //v1.2.0 added
376
  'flat_output',
377
  'include_parent',
378
  'include_parent_siblings', //v1.1.0 added
379
  'include_ancestors',
380
  'hide_empty', //v1.1.0: this now only has relevance prior to WP v3.6
381
  'title_from_parent',
382
+ 'title_from_current', //v1.2.0 added
383
  'ol_root',
384
  'ol_sub',
385
  //field section toggles...
386
  'fs_filter',
387
+ 'fs_fallbacks', //v1.2.0 added
388
  'fs_output',
389
  'fs_container',
390
  'fs_classes',
421
  array(
422
  'classname' => 'widget_custom_menu_wizard',
423
  'description' => __('Add a custom menu, or part of one, as a widget')
 
 
 
424
  )
425
  );
426
  }
427
 
428
  /**
429
+ * removes itself from the filters and, if available, stores current|parent title in the widget instance
430
  *
431
  * @param array $items Filtered menu items
432
  * @param object $args
433
  * @return array Menu items
434
  */
435
+ function cmw_filter_retain_possible_titles($items, $args){
436
  remove_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_retain_parent_title' ), 10, 2);
437
+ foreach(array('parent', 'current') as $n){
438
+ if( !empty( $args->_custom_menu_wizard['_titles'][ $n ] ) ){
439
+ $this->cmw_titles[ $n ] = $args->_custom_menu_wizard['_titles'][ $n ];
440
+ }
441
  }
442
  return $items;
443
  }
484
  /**
485
  * produces the widget HTML at the front end
486
  *
487
+ * @filters : custom_menu_wizard_nav_params array of params that will be sent to wp_nav_menu()
488
+ *
489
  * @param object $args Widget arguments
490
  * @param array $instance Configuration for this widget instance
491
  */
512
  //v1.1.0 As of WP v3.6, wp_nav_menu() automatically prevents any HTML output if there are no items...
513
  $instance['hide_empty'] = $instance['hide_empty'] && $this->_pre_3point6();
514
 
515
+ $this->cmw_titles = array();
516
 
517
  //fetch menu...
518
  if( !empty($instance['menu'] ) ){
535
  $instance['container_class'] = "menu-{$menu->slug}-container {$instance['container_class']}";
536
  }
537
 
538
+ if( $instance['title_from_parent'] || $instance['title_from_current'] ){
539
+ add_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_retain_possible_titles' ), 10, 2);
540
  }
541
 
542
  if( $instance['hide_empty'] ){
564
  'fallback_no_ancestor' => $instance['fallback_no_ancestor'], //v1.1.0
565
  'fallback_include_parent' => $instance['fallback_include_parent'], //v1.1.0
566
  'fallback_include_parent_siblings' => $instance['fallback_include_parent_siblings'], //v1.1.0
567
+ 'fallback_no_children' => $instance['fallback_no_children'], //v1.2.0
568
+ 'fallback_nc_include_parent' => $instance['fallback_nc_include_parent'], //v1.2.0
569
+ 'fallback_nc_include_parent_siblings' => $instance['fallback_nc_include_parent_siblings'], //v1.2.0
570
  'include_parent' => $instance['include_parent'],
571
  'include_parent_siblings' => $instance['include_parent_siblings'], //v1.1.0
572
  'include_ancestors' => $instance['include_ancestors'],
573
  'title_from_parent' => $instance['title_from_parent'],
574
+ 'title_from_current' => $instance['title_from_current'], //v1.2.0
575
  'ol_root' => $instance['ol_root'],
576
  'ol_sub' => $instance['ol_sub'],
577
  'flat_output' => $instance['flat_output'],
586
  $params['container_class'] = $instance['container_class'];
587
  }
588
  //NB: wp_nav_menu() is in wp-includes/nav-menu-template.php
589
+ $out = wp_nav_menu( apply_filters( 'custom_menu_wizard_nav_params', $params ) );
590
 
591
  if( $instance['hide_empty'] ){
592
  remove_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
595
  //only put something out if there is something to put out...
596
  if( !empty( $out ) ){
597
 
598
+ //title from : 'from parent' has priority over 'from current'...
599
+ //note that 'parent' is whatever you are getting the children of and therefore doesn't apply to a ShowAll, whereas
600
+ //'current' is the current menu item (as determined by WP); also note that neither parent nor current actually has
601
+ //to be present in the results
602
+ if( $instance['title_from_parent'] && !empty( $this->cmw_titles['parent'] ) ){
603
+ $title = $this->cmw_titles['parent'];
604
+ }
605
+ if( empty( $title ) && $instance['title_from_current'] && !empty( $this->cmw_titles['current'] ) ){
606
+ $title = $this->cmw_titles['current'];
607
  }
608
  if( empty( $title ) ){
609
  $title = $instance['hide_title'] ? '' : $instance['title'];
668
  }
669
 
670
  //get menus...
671
+ $menus = wp_get_nav_menus( array( 'orderby' => 'name' ) );
672
+ $noitems = true;
673
+ if( !empty( $menus ) ){
674
+ foreach( $menus as $i=>$menu ){
675
+ $menus[ $i ]->_items = wp_get_nav_menu_items( $menu->term_id );
676
+ if( !empty( $menus[ $i ]->_items ) ){
677
+ $noitems = false;
678
+ }
679
+ }
680
+ }
681
 
682
+ //if no populated menus exist, suggest the user go create one...
683
+ if( $noitems ){
684
+ echo '<p>'. sprintf( __('No populated menus have been created yet. <a href="%s">Create one</a>.'), admin_url('nav-menus.php') ) .'</p>';
685
  return;
686
  }
687
 
691
  ?>
692
  <p>
693
  <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:') ?></label>
694
+ <label class="alignright">
695
  <input id="<?php echo $this->get_field_id('hide_title'); ?>" name="<?php echo $this->get_field_name('hide_title'); ?>"
696
  type="checkbox" value="1" <?php checked( $instance['hide_title'] ); ?> />
697
  <?php _e('Hide'); ?></label>
702
 
703
  <p>
704
  <label for="<?php echo $this->get_field_id('menu'); ?>"><?php _e('Select Menu:'); ?></label>
705
+ <select id="<?php echo $this->get_field_id('menu'); ?>"
706
+ class="widget-<?php echo $this->id_base; ?>-selectmenu widget-<?php echo $this->id_base; ?>-listen"
707
  name="<?php echo $this->get_field_name('menu'); ?>">
708
  <?php
709
  foreach( $menus as $i=>$menu ){
710
+ if( !empty( $menu->_items ) ){
711
  ?>
712
  <option <?php selected($instance['menu'], $menu->term_id); ?> value="<?php echo $menu->term_id; ?>"><?php echo $menu->name; ?></option>
713
  <?php
714
+ }
715
  }
716
  ?>
717
  </select>
725
  ?>
726
  <small class="alignright" style="line-height:1;"><a href="<?php echo plugins_url('/demo.html', __FILE__); ?>" target="_blank"><?php _e(' demo' ); ?></a></small>
727
  <p>
728
+ <label>
729
  <input id="<?php echo $this->get_field_id('filter'); ?>_0" class="widget-<?php echo $this->id_base; ?>-listen"
730
  name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="0" <?php checked(!$instance['filter']); ?> />
731
  <?php _e('Show all'); ?></label>
732
+ <br /><label>
733
  <input id="<?php echo $this->get_field_id('filter'); ?>_1" class="widget-<?php echo $this->id_base; ?>-listen"
734
  name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="1" <?php checked($instance['filter']); ?> />
735
  <?php _e('Children of:'); ?></label>
746
 
747
  $maxlevel = 1;
748
  foreach( $menus as $i=>$menu ){
749
+ //as of v1.2.0 : no items, no optgroup!
 
 
 
 
750
  if( !empty( $menu->_items ) ){
751
+ //v1.1.0 changed the indents from padding to hyphen-space (for IE!!!! grrrr...)
752
+ $itemindents = array('0' => 0);
753
+ $menuOptions[] = '<optgroup label="' . $menu->name . '" data-cmw-optgroup-index="' . $i . '">';
754
  foreach( $menu->_items as $item ){
755
  //exclude orpans!
756
  if( isset($itemindents[ $item->menu_item_parent ])){
761
  str_repeat('- ', $itemindents[ $item->menu_item_parent ]) . $item->title . '</option>';
762
  }
763
  }
764
+ $menuOptions[] = '</optgroup>';
765
  }
 
766
  }
767
  $menuOptions = implode("\n", $menuOptions);
768
  echo $menuOptions;
775
  </select>
776
  </p>
777
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
778
  <p>
779
  <label for="<?php echo $this->get_field_id('start_level'); ?>"><?php _e('Starting Level:'); ?></label>
780
  <select id="<?php echo $this->get_field_id('start_level'); ?>" name="<?php echo $this->get_field_name('start_level'); ?>">
807
  </p>
808
  <?php $this->_close_a_field_section(); ?>
809
 
810
+ <?php
811
+ /**
812
+ * v1.2.0 start collapsible section : 'Fallbacks'
813
+ */
814
+ $this->_open_a_field_section($instance, 'Fallbacks', 'fs_fallbacks');
815
+ ?>
816
+ <small class="alignright" style="line-height:1;"><a href="<?php echo plugins_url('/demo.html', __FILE__); ?>" target="_blank"><?php _e(' demo' ); ?></a></small>
817
+ <p class="clear widget-<?php echo $this->id_base; ?>-disableifnot-rp" style="line-height:1.3333;">
818
+ <small><strong><?php _e( 'If &quot;Children of&quot; is <em>Current Root / Parent Item</em>, and no ancestor exists' ); ?> :</strong></small>
819
+ <br /><label>
820
+ <input id="<?php echo $this->get_field_id('fallback_no_ancestor'); ?>"
821
+ name="<?php echo $this->get_field_name('fallback_no_ancestor'); ?>" type="checkbox" value="1"
822
+ <?php checked($instance['fallback_no_ancestor']); ?> />
823
+ <?php _e('Switch to Current Item, and'); ?></label>
824
+ <br /><label style="padding-left:1em;">
825
+ <input id="<?php echo $this->get_field_id('fallback_include_parent'); ?>"
826
+ name="<?php echo $this->get_field_name('fallback_include_parent'); ?>" type="checkbox" value="1"
827
+ <?php checked($instance['fallback_include_parent']); ?> />
828
+ <?php _e('Include Parent...'); ?> </label>
829
+ <label>
830
+ <input id="<?php echo $this->get_field_id('fallback_include_parent_siblings'); ?>"
831
+ name="<?php echo $this->get_field_name('fallback_include_parent_siblings'); ?>" type="checkbox" value="1"
832
+ <?php checked($instance['fallback_include_parent_siblings']); ?> />
833
+ <?php _e('&amp; its Siblings'); ?></label>
834
+ </p>
835
+
836
+ <p class="widget-<?php echo $this->id_base; ?>-disableifnot-ci" style="line-height:1.3333;">
837
+ <small><strong><?php _e( 'If &quot;Children of&quot; is <em>Current Item</em>, and current item has no children' ); ?> :</strong></small>
838
+ <br /><label>
839
+ <input id="<?php echo $this->get_field_id('fallback_no_children'); ?>"
840
+ name="<?php echo $this->get_field_name('fallback_no_children'); ?>" type="checkbox" value="1"
841
+ <?php checked($instance['fallback_no_children']); ?> />
842
+ <?php _e('Switch to Current Parent Item, and'); ?></label>
843
+ <br /><label style="padding-left:1em;">
844
+ <input id="<?php echo $this->get_field_id('fallback_nc_include_parent'); ?>"
845
+ name="<?php echo $this->get_field_name('fallback_nc_include_parent'); ?>" type="checkbox" value="1"
846
+ <?php checked($instance['fallback_nc_include_parent']); ?> />
847
+ <?php _e('Include Parent...'); ?> </label>
848
+ <label>
849
+ <input id="<?php echo $this->get_field_id('fallback_nc_include_parent_siblings'); ?>"
850
+ name="<?php echo $this->get_field_name('fallback_nc_include_parent_siblings'); ?>" type="checkbox" value="1"
851
+ <?php checked($instance['fallback_nc_include_parent_siblings']); ?> />
852
+ <?php _e('&amp; its Siblings'); ?></label>
853
+ </p>
854
+
855
+
856
+ <?php $this->_close_a_field_section(); ?>
857
+
858
  <?php
859
  /**
860
  * start collapsible section : 'Output'
863
  ?>
864
  <small class="alignright" style="line-height:1;"><a href="<?php echo plugins_url('/demo.html', __FILE__); ?>" target="_blank"><?php _e(' demo' ); ?></a></small>
865
  <p>
866
+ <label>
867
  <input id="<?php echo $this->get_field_id('flat_output'); ?>_0" name="<?php echo $this->get_field_name('flat_output'); ?>"
868
  type="radio" value="0" <?php checked(!$instance['flat_output']); ?> />
869
  <?php _e('Hierarchical'); ?></label>
870
+ &nbsp;<label>
871
  <input id="<?php echo $this->get_field_id('flat_output'); ?>_1" name="<?php echo $this->get_field_name('flat_output'); ?>"
872
  type="radio" value="1" <?php checked($instance['flat_output']); ?> />
873
  <?php _e('Flat'); ?></label>
874
  </p>
875
 
876
  <p class="widget-<?php echo $this->id_base; ?>-disableif">
877
+ <label>
878
  <input id="<?php echo $this->get_field_id('include_parent'); ?>"
879
  name="<?php echo $this->get_field_name('include_parent'); ?>" type="checkbox"
880
  value="1" <?php checked($instance['include_parent']); ?> />
881
  <?php _e('Include Parent...'); ?> </label>
882
+ <label>
883
  <input id="<?php echo $this->get_field_id('include_parent_siblings'); ?>"
884
  name="<?php echo $this->get_field_name('include_parent_siblings'); ?>" type="checkbox"
885
  value="1" <?php checked($instance['include_parent_siblings']); ?> />
886
  <?php _e('&amp; its Siblings'); ?></label>
887
+ <br /><label>
888
  <input id="<?php echo $this->get_field_id('include_ancestors'); ?>"
889
  name="<?php echo $this->get_field_name('include_ancestors'); ?>" type="checkbox"
890
  value="1" <?php checked($instance['include_ancestors']); ?> />
891
  <?php _e('Include Ancestors'); ?></label>
892
+ <br /><label>
893
  <input id="<?php echo $this->get_field_id('title_from_parent'); ?>"
894
  name="<?php echo $this->get_field_name('title_from_parent'); ?>" type="checkbox"
895
  value="1" <?php checked($instance['title_from_parent']); ?> />
896
+ <?php _e('Title from Parent'); ?></label>
897
+ <br /><small><em><?php _e('Only if the &quot;Children of&quot; Filter returns items'); ?></em></small>
898
+ </p>
899
+
900
+ <p>
901
+ <label>
902
+ <input id="<?php echo $this->get_field_id('title_from_current'); ?>"
903
+ name="<?php echo $this->get_field_name('title_from_current'); ?>" type="checkbox"
904
+ value="1" <?php checked($instance['title_from_current']); ?> />
905
+ <?php _e('Title from &quot;Current&quot; Item'); ?></label>
906
+ <br /><small><em><?php _e('Lower priority than &quot;Title from Parent&quot;'); ?></em></small>
907
  </p>
908
 
909
  <p>
910
  <?php _e('Change UL to OL:'); ?>
911
+ <br /><label>
912
  <input id="<?php echo $this->get_field_id('ol_root'); ?>" name="<?php echo $this->get_field_name('ol_root'); ?>"
913
  type="checkbox" value="1" <?php checked($instance['ol_root']); ?> />
914
  <?php _e('Top Level'); ?></label>
915
+ &nbsp;<label>
916
  <input id="<?php echo $this->get_field_id('ol_sub'); ?>" name="<?php echo $this->get_field_name('ol_sub'); ?>"
917
  type="checkbox" value="1" <?php checked($instance['ol_sub']); ?> />
918
  <?php _e('Sub-Levels'); ?></label>
925
  if( $this->_pre_3point6() ){
926
  ?>
927
  <p>
928
+ <label>
929
  <input id="<?php echo $this->get_field_id('hide_empty'); ?>" name="<?php echo $this->get_field_name('hide_empty'); ?>"
930
  type="checkbox" value="1" <?php checked($instance['hide_empty']); ?> />
931
  <?php _e('Hide Widget if Empty'); ?></label>
1034
  <input id="<?php echo $this->get_field_id($fname); ?>" class="hidden-field" name="<?php echo $this->get_field_name($fname); ?>"
1035
  type="checkbox" value="1" <?php checked($collapsed); ?> />
1036
  <div style="background:transparent url(images/arrows.png) no-repeat 0 <?php echo $collapsed ? '0' : '-36px'; ?>;height:16px; width:16px;float:right;outline:0 none;"></div>
1037
+ <h3 style="font-size:1em;margin:0;padding:2px 0.5em;"><?php _e( $text ); ?></h3>
1038
  </div>
1039
  <div class="hide-if-js"<?php echo !$collapsed ? ' style="display:block;"' : ''; ?>>
1040
  <?php
1058
  return version_compare( strtolower( $wp_version ), '3.6a', '<' );
1059
  } //end _pre_3point6()
1060
 
1061
+ } //end of class
1062
+
1063
+ /**
1064
+ * as of v1.2.0
1065
+ * shortcode processing for [custom_menu_wizard option="" option="" ...]
1066
+ * see wp-includes/widgets.php for the_widget() code
1067
+ * Note that hide_empty is set to ON and can not be overridden!
1068
+ *
1069
+ * default (ie. no options) is:
1070
+ * - show all
1071
+ * - of first populated menu found (alphabetically)
1072
+ * - from root, for unlimited depth
1073
+ * - as hierarchical nested ULs inside a DIV.widget_custom_menu_wizard.shortcode_custom_menu_wizard
1074
+ *
1075
+ * @filters : custom_menu_wizard_shortcode_attributes array of attributes supplied to the shortcode
1076
+ * custom_menu_wizard_shortcode_settings array of widget settings derived from the attributes
1077
+ * custom_menu_wizard_shortcode_widget_args array of the sidebar args used to wrap widgets and their titles (before|after_widget, before|after_title)
1078
+ *
1079
+ * @param array $atts options supplied to the shortcode
1080
+ * @param string $content Within start-end shortcode tags
1081
+ * @param string $tag Shortcode tag
1082
+ * @return string HTML
1083
+ */
1084
+ function custom_menu_wizard_widget_shortcode($atts, $content, $tag){
1085
+ $html = '';
1086
+ $ok = false;
1087
+ $instance = shortcode_atts( array(
1088
+ 'title' => '',
1089
+ 'menu' => 0, // menu id, slug or name
1090
+ //determines filter & filter_item...
1091
+ 'children_of' => '', // empty = show all; menu item id or title (caseless), or current|current-item|parent|current-parent|root|current-ancestor
1092
+ 'start_level' => 1,
1093
+ 'depth' => 0, // 0 = unlimited
1094
+ //only if children_of is (parent|current-parent|root|current-ancestor); determines fallback_no_ancestor, fallback_include_parent & fallback_include_parent_siblings...
1095
+ 'fallback_parent' => 0, // 1 = use current-item; 'parent' = *and* include parent, 'siblings' = *and* include both parent and its siblings
1096
+ //only if children_of is (current|current-item); determines fallback_no_children, fallback_nc_include_parent & fallback_nc_include_parent_siblings...
1097
+ 'fallback_current' => 0, // 1 = use current-parent; 'parent' = *and* include parent (if available), 'siblings' = *and* include both parent (if available) and its siblings
1098
+ //switches...
1099
+ 'flat_output' => 0,
1100
+ //determines include_parent, include_parent_siblings & include_ancestors...
1101
+ 'include' =>'', //comma|space|hyphen separated list of 'parent', 'siblings', 'ancestors'
1102
+ 'ol_root' => 0,
1103
+ 'ol_sub' => 0,
1104
+ //determines title_from_parent & title_from_current...
1105
+ 'title_from' => '', //comma|space|hyphen separated list of 'parent', 'current'
1106
+ //strings...
1107
+ 'container' => 'div', // a tag : div|nav are WP restrictions, not the widget's; '' = no container
1108
+ 'container_id' => '',
1109
+ 'container_class' => '',
1110
+ 'menu_class' => 'menu-widget',
1111
+ 'widget_class' => '',
1112
+ //determines before & after...
1113
+ 'wrap_link' => '', // a tag name (eg. div, p, span, etc)
1114
+ //determines link_before & link_after...
1115
+ 'wrap_link_text' => '' // a tag name (eg. span, em, strong)
1116
+ ), $atts );
1117
+
1118
+ $instance = apply_filters( 'custom_menu_wizard_shortcode_attributes', $instance );
1119
+
1120
+ if( empty( $instance['menu'] ) ){
1121
+ //gonna find the first menu (alphabetically) that has items...
1122
+ $menus = wp_get_nav_menus( array( 'orderby' => 'name' ) );
1123
+ }else{
1124
+ //allow for menu being something other than an id (eg. slug or name), but we need the id for the widget...
1125
+ $menus = wp_get_nav_menu_object( $instance['menu'] );
1126
+ if( !empty( $menus) ){
1127
+ $menus = array( $menus );
1128
+ }
1129
+ }
1130
+ if( !empty( $menus ) ){
1131
+ foreach( $menus as $i=>$menu ){
1132
+ $items = wp_get_nav_menu_items( $menu->term_id );
1133
+ $ok = !empty( $items );
1134
+ if( $ok ){
1135
+ $instance['menu'] = $menu->term_id;
1136
+ break;
1137
+ }
1138
+ }
1139
+ }
1140
+ unset( $menus );
1141
+
1142
+ if( $ok ){
1143
+ //children_of => filter & filter_item...
1144
+ if( empty( $instance['children_of'] ) ){
1145
+ $instance['children_of'] = '';
1146
+ }
1147
+ $instance['filter'] = $instance['filter_item'] = 0;
1148
+ switch( $instance['children_of'] ){
1149
+ case '':
1150
+ break;
1151
+ case 'root': case 'current-ancestor':
1152
+ --$instance['filter_item']; //ends up as -2
1153
+ case 'parent': case 'current-parent':
1154
+ --$instance['filter_item']; //ends up as -1
1155
+ case 'current': case 'current-item':
1156
+ $instance['filter'] = 1;
1157
+ break;
1158
+ default:
1159
+ $instance['filter'] = 1;
1160
+ $instance['filter_item'] = strtolower( $instance['children_of'] );
1161
+ }
1162
+ unset( $instance['children_of'] );
1163
+ //if filter_item is non-numeric then it could be the title of a menu item, but we need it to be the menu item's id...
1164
+ if( !is_numeric( $instance['filter_item'] ) ){
1165
+ foreach( $items as $item ){
1166
+ $ok = strtolower( $item->title ) == $instance['filter_item'];
1167
+ if( $ok ){
1168
+ $instance['filter_item'] = $item->ID;
1169
+ break;
1170
+ }
1171
+ }
1172
+ }
1173
+ }
1174
+
1175
+ if( $ok ){
1176
+ //fallback_parent => fallback_no_ancestor switch (and extension switches)...
1177
+ $instance['fallback_no_ancestor'] = $instance['fallback_include_parent'] = $instance['fallback_include_parent_siblings'] = 0;
1178
+ if( $instance['filter_item'] < 0 && !empty( $instance['fallback_parent'] ) ){
1179
+ $instance['fallback_no_ancestor'] = 1;
1180
+ $i = preg_split( '/[\s,-]+/', strtolower( $instance['fallback_parent'] ), -1, PREG_SPLIT_NO_EMPTY );
1181
+ foreach( $i as $j ){
1182
+ if( $j == 'parent' ){
1183
+ $instance['fallback_include_parent'] = 1;
1184
+ }elseif( $j == 'siblings' ){
1185
+ $instance['fallback_include_parent_siblings'] = 1;
1186
+ }
1187
+ }
1188
+ }
1189
+ //fallback_current => fallback_no_children switch (and extension switches)...
1190
+ $instance['fallback_no_children'] = $instance['fallback_nc_include_parent'] = $instance['fallback_nc_include_parent_siblings'] = 0;
1191
+ if( $instance['filter'] == 1 && $instance['filter_item'] == 0 && !empty( $instance['fallback_current'] ) ){
1192
+ $instance['fallback_no_children'] = 1;
1193
+ $i = preg_split( '/[\s,-]+/', strtolower( $instance['fallback_current'] ), -1, PREG_SPLIT_NO_EMPTY );
1194
+ foreach( $i as $j ){
1195
+ if( $j == 'parent' ){
1196
+ $instance['fallback_nc_include_parent'] = 1;
1197
+ }elseif( $j == 'siblings' ){
1198
+ $instance['fallback_nc_include_parent_siblings'] = 1;
1199
+ }
1200
+ }
1201
+ }
1202
+ unset( $instance['fallback_parent'], $instance['fallback_current'] );
1203
+ //include => include_* ...
1204
+ $instance['include_parent'] = $instance['include_parent_siblings'] = $instance['include_ancestors'] = 0;
1205
+ if( $instance['filter'] == 1 && !empty( $instance['include'] ) ){
1206
+ $i = preg_split( '/[\s,-]+/', strtolower( $instance['include'] ), -1, PREG_SPLIT_NO_EMPTY );
1207
+ foreach( $i as $j ){
1208
+ if( $j == 'parent' ){
1209
+ $instance['include_parent'] = 1;
1210
+ }elseif( $j == 'siblings' ){
1211
+ $instance['include_parent_siblings'] = 1;
1212
+ }elseif( $j == 'ancestors' ){
1213
+ $instance['include_ancestors'] = 1;
1214
+ }
1215
+ }
1216
+ }
1217
+ unset( $instance['include'] );
1218
+ //title_from => title_from_parent, title_from_current ...
1219
+ $instance['title_from_parent'] = $instance['title_from_current'] = 0;
1220
+ if( !empty( $instance['title_from'] ) ){
1221
+ $i = preg_split( '/[\s,-]+/', strtolower( $instance['title_from'] ), -1, PREG_SPLIT_NO_EMPTY );
1222
+ foreach( $i as $j ){
1223
+ if( $j == 'parent' ){
1224
+ $instance['title_from_parent'] = 1;
1225
+ }elseif( $j == 'current' ){
1226
+ $instance['title_from_current'] = 1;
1227
+ }
1228
+ }
1229
+ }
1230
+ unset( $instance['title_from'] );
1231
+ //wrap_link => before & after...
1232
+ $instance['before'] = $instance['after'] = '';
1233
+ $instance['wrap_link'] = esc_attr( trim( $instance['wrap_link'] ) );
1234
+ if( !empty( $instance['wrap_link'] ) ){
1235
+ $instance['before'] = '<' . $instance['wrap_link'] . '>';
1236
+ $instance['after'] = '</' . $instance['wrap_link'] . '>';
1237
+ }
1238
+ //wrap_link_text => link_before & link_after...
1239
+ $instance['link_before'] = $instance['link_after'] = '';
1240
+ $instance['wrap_link_text'] = esc_attr( trim( $instance['wrap_link_text'] ) );
1241
+ if( !empty( $instance['wrap_link_text'] ) ){
1242
+ $instance['link_before'] = '<' . $instance['wrap_link_text'] . '>';
1243
+ $instance['link_after'] = '</' . $instance['wrap_link_text'] . '>';
1244
+ }
1245
+ //handle widget_class here because we have full control over $before_widget...
1246
+ $before_widget_class = array(
1247
+ 'widget_custom_menu_wizard',
1248
+ 'shortcode_custom_menu_wizard'
1249
+ );
1250
+ $instance['widget_class'] = empty( $instance['widget_class'] ) ? '' : esc_attr( trim ( $instance['widget_class'] ) );
1251
+ if( !empty( $instance['widget_class'] ) ){
1252
+ foreach( explode(' ', $instance['widget_class'] ) as $i ){
1253
+ if( !empty( $i ) && !in_array( $i, $before_widget_class ) ){
1254
+ $before_widget_class[] = $i;
1255
+ }
1256
+ }
1257
+ }
1258
+ $instance['widget_class'] = '';
1259
+ //turn on hide_empty...
1260
+ $instance['hide_empty'] = 1;
1261
+ }
1262
+
1263
+ if( $ok ){
1264
+ //apart from before_title, these are lifted from the_widget()...
1265
+ $sidebar_args = array(
1266
+ 'before_widget' => '<div class="' . implode( ' ', $before_widget_class ) . '">',
1267
+ 'after_widget' => '</div>',
1268
+ 'before_title' => '<h2 class="widgettitle">',
1269
+ 'after_title' => '</h2>'
1270
+ );
1271
+ ob_start();
1272
+ the_widget(
1273
+ 'Custom_Menu_Wizard_Widget',
1274
+ apply_filters('custom_menu_wizard_shortcode_settings', $instance ),
1275
+ apply_filters('custom_menu_wizard_shortcode_widget_args', $sidebar_args )
1276
+ );
1277
+ $html = ob_get_clean();
1278
+ }
1279
+ return empty($html) ? '' : $html;
1280
+ }
demo.html CHANGED
@@ -21,20 +21,21 @@
21
  #formheader {background-color:#666666; border-radius:0.4em 0.4em 0 0; color:#FFFFFF; margin:0 -1em -0.5em; padding:0.25em 1em; text-align:center;}
22
  #theform {margin:1em; padding:0 1em; border:1px solid #999999; border-radius:0.5em;}
23
  #theoutput {margin:1em;}
24
- #theoutput div {margin:0.5em 0; padding:0 1em; border:1px solid #cccccc; border-radius:0.5em; min-height:2em; min-width:8em;}
25
- #theoutput ul, #theoutput ol {margin:0.5em 0 0.5em 1.5em; padding:0;}
26
- #theoutput li ul {margin:0 0 0 1em;}
27
- #theoutput li ol {margin:0 0 0 1.5em;}
28
- #theoutput ul {list-style-type:disc;}
29
- #theoutput ul ul {list-style-type:circle;}
30
- #theoutput ul ul ul {list-style-type:square;}
31
- #theoutput ul ul ul ul {list-style-type:disc;}
32
- #theoutput ul ul ul ul ul {list-style-type:circle;}
33
- #theoutput ol {list-style-type:decimal;}
34
- #theoutput ol ol {list-style-type:upper-alpha;}
35
- #theoutput ol ol ol {list-style-type:lower-alpha;}
36
- #theoutput ol ol ol ol {list-style-type:lower-roman;}
37
- #theoutput ol ol ol ol ol {list-style-type:decimal;}
 
38
  #themenu {margin:1em;}
39
  #themenu ul {font-size:12px; list-style-type:none; margin:1em 0; padding:0;}
40
  #themenu ul ul {margin:0 0 0 5em;}
@@ -45,9 +46,14 @@
45
  #thefooter div {font-size:12px;}
46
  .alignleft {float:left;}
47
  .alignright {float:right;}
48
- .current-ancestor {border-color:#cccc00 !important; background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255, 255, 153, 1)), to(rgba(255, 255, 153, 0.1))); background-image:-webkit-linear-gradient(top, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); background-image:-moz--linear-gradient(top, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); background-image:-ms-linear-gradient(top, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); background-image:-o-linear-gradient(top, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); background-image:linear-gradient(to bottom, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffff99, endColorstr=#1affff99); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffff99, endColorstr=#1affff99)";}
49
- .current-item {border-color:#cc0000 !important; background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255, 204, 204, 1)), to(rgba(255, 204, 204, 0.1))); background-image:-webkit-linear-gradient(top, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); background-image:-moz--linear-gradient(top, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); background-image:-ms-linear-gradient(top, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); background-image:-o-linear-gradient(top, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); background-image:linear-gradient(to bottom, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffcccc, endColorstr=#1affcccc); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffcccc, endColorstr=#1affcccc)";}
50
- .current-parent {border-color:#ff9900 !important; background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255, 204, 153, 1)), to(rgba(255, 204, 153, 0.1))); background-image:-webkit-linear-gradient(top, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); background-image:-moz--linear-gradient(top, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); background-image:-ms-linear-gradient(top, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); background-image:-o-linear-gradient(top, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); background-image:linear-gradient(to bottom, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffcc99, endColorstr=#1affcc99); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffcc99, endColorstr=#1affcc99)";}
 
 
 
 
 
51
  .grey {color:#999999;}
52
  .picked > a {background-color:#000066; color:#ffffff; font-weight:bold;}
53
  .tryit {margin-left:1em; text-shadow:1px 1px 1px #ff0000;}
@@ -60,7 +66,7 @@
60
  <form>
61
  <div id="formheader">
62
  <strong>Custom Menu Wizard</strong> Interactive Demo
63
- <br /><small>v1.1.0 <em>(subset of admin options)</em></small><strong class="tryit"><em>Try it out&hellip;</em></strong>
64
  </div>
65
 
66
  <p>
@@ -72,6 +78,8 @@
72
  <small><em>Title can be set, but need not be displayed</em></small>
73
  </p>
74
 
 
 
75
  <p>
76
  <label>
77
  <input type="radio" checked="checked" value="0" name="filter" class="widget-custom-menu-wizard-listen" id="widget-custom-menu-wizard-2-filter_0">
@@ -83,23 +91,10 @@
83
  <option selected="selected" value="0">Current Item</option>
84
  <option value="-2">Current Root Item</option>
85
  <option value="-1">Current Parent Item</option>
86
- <optgroup label="the-menu" data-cmw-active-menu='true'></optgroup>
87
  </select>
88
  </p>
89
 
90
- <p class="widget-custom-menu-wizard-enableif">
91
- <label>
92
- <input type="checkbox" value="1" name="fallback_no_ancestor" id="widget-custom-menu-wizard-2-fallback_no_ancestor">
93
- Fallback to Current Item, and</label>
94
- <br /><label style="padding-left:1em;">
95
- <input type="checkbox" value="1" name="fallback_include_parent" id="widget-custom-menu-wizard-2-fallback_include_parent">
96
- Include Parent... </label>
97
- <label>
98
- <input type="checkbox" value="1" name="fallback_include_parent_siblings" id="widget-custom-menu-wizard-2-fallback_include_parent_siblings">
99
- &amp; its Siblings</label>
100
- <br /><small><em>If Current Root/Parent and no ancestor exists</em></small>
101
- </p>
102
-
103
  <p>
104
  <label for="widget-custom-menu-wizard-2-start_level">Starting Level:</label>
105
  <select name="start_level" id="widget-custom-menu-wizard-2-start_level">
@@ -115,6 +110,37 @@
115
  <br /><small><em>Relative to the first Filtered item found</em></small>
116
  </p>
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  <p>
119
  <label>
120
  <input type="radio" checked="checked" value="0" name="flat_output" id="widget-custom-menu-wizard-2-flat_output_0">
@@ -136,8 +162,15 @@
136
  Include Ancestors</label>
137
  <br /><label>
138
  <input type="checkbox" value="1" name="title_from_parent" id="widget-custom-menu-wizard-2-title_from_parent">
139
- Title from Parent Item</label>
140
- <br /><small><em>Only if the "Children of:" Filter returns items</em></small>
 
 
 
 
 
 
 
141
  </p>
142
 
143
  <p>
@@ -150,7 +183,7 @@
150
  Sub-Levels</label>
151
  </p>
152
 
153
- <p class="grey">
154
  <small><em>Note that since WP v3.6 does it automatically,
155
  <br />&quot;Hide Widget if Empty&quot; is ON for this demo.</em></small>
156
  </p>
@@ -162,6 +195,7 @@
162
  <div id='theoutput' class='alignright'>
163
  <strong>Basic Output</strong> &hellip;
164
  <div id='widget-output'></div>
 
165
  </div>
166
 
167
  <div id='themenu' class='alignleft'>
@@ -228,6 +262,9 @@
228
  </li>
229
  </ul>
230
  </li>
 
 
 
231
  </ul>
232
  </div>
233
 
@@ -242,7 +279,7 @@
242
  <script type='text/javascript'>
243
  jQuery(function($){
244
  var maxlevel = 0,
245
- switches = {hide_title:1, filter:1, fallback_no_ancestor:1, fallback_include_parent:1, fallback_include_parent_siblings:1, flat_output:1, include_parent:1, include_parent_siblings:1, include_ancestors:1, title_from_parent:1, ol_root:1, ol_sub:1},
246
  integers = {filter_item:1, start_level:1, depth:1},
247
  getSettings = function(){
248
  var settings = {};
@@ -262,16 +299,23 @@ jQuery(function($){
262
  var output = $('#widget-output').empty(),
263
  items = $('.picked'),
264
  html = '',
 
265
  currLevel = 0,
266
- startLevel, title;
267
  settings = settings || getSettings();
268
  if(items.length){
269
- title = settings.hide_title ? '' : settings.title;
270
  if(settings.filter && settings.title_from_parent){
271
  title = $('.picked-parent').children('a').text() || '';
272
  }
 
 
 
 
 
 
273
  items.each(function(){
274
- var level = settings.flat_output ? 1 : parseInt(this.className.match(/level-(\d+)/)[1], 10);
 
275
  if(currLevel){
276
  if(level === currLevel){
277
  html += '</li>';
@@ -285,7 +329,7 @@ jQuery(function($){
285
  }else{
286
  startLevel = level;
287
  }
288
- html += '<li><a href="#">' + $(this).children('a').text() + '</a>';
289
  currLevel = level;
290
  });
291
  while(currLevel-- > startLevel){
@@ -296,6 +340,9 @@ jQuery(function($){
296
  if(title){
297
  output.prepend('<h3>' + title + '</h3>');
298
  }
 
 
 
299
  }
300
  },
301
  walk = function(e){
@@ -306,6 +353,7 @@ jQuery(function($){
306
  picked = items.filter('.picked'),
307
  cls = [],
308
  firstParent = true,
 
309
  parent, i;
310
  if(!settings.filter){
311
  //show all...
@@ -334,6 +382,7 @@ jQuery(function($){
334
  parent = $('.current-item').closest('li');
335
  includeParent = includeParent || settings.fallback_include_parent;
336
  includeParentSiblings = includeParentSiblings || settings.fallback_include_parent_siblings;
 
337
  }
338
  }
339
  //could be multiple - only want first one that matches start_level...
@@ -351,6 +400,14 @@ jQuery(function($){
351
  });
352
  //kids...
353
  items = parent.find('li').filter( cls.join(',') );
 
 
 
 
 
 
 
 
354
  if(items.length){
355
  if(includeParentSiblings){
356
  items = items.add( parent.siblings('li') ).add( parent );
@@ -366,12 +423,12 @@ jQuery(function($){
366
  parent.addClass('picked-parent');
367
  }
368
  }
 
369
  picked.not(items).removeClass('picked');
370
  items.not('.picked').addClass('picked');
371
  show.call(this, e, settings);
372
  },
373
  flds = $('input,select');
374
- $('#widget-output').on('click', 'a', false);
375
  $('#themenu a').on('click', function(){
376
  //click on a #themenu item to [un]make it the current item...
377
  var cls = ['current-item', 'current-parent', 'current-ancestor'],
@@ -389,12 +446,18 @@ jQuery(function($){
389
  return false;
390
  }).map(function(i){
391
  //create filter_item's optgroup options...
392
- var el = $(this),
393
  level = el.parentsUntil('#themenu', 'li').length;
394
  maxlevel = Math.max(maxlevel, level);
395
  el.parent().addClass('level-' + level);
396
  return $('<option/>', {value:i + 1}).text( hyphenIndent(el.text(), level) ).get(0);
397
  }).appendTo('optgroup');
 
 
 
 
 
 
398
  //populate starting-level and depth options...
399
  for(i = 1; i < maxlevel + 1; i++){
400
  var levels = i < 2 ? ' level' : ' levels',
@@ -405,13 +468,24 @@ jQuery(function($){
405
  }
406
  $('<option/>', att).text(i).appendTo('#widget-custom-menu-wizard-2-start_level');
407
  }
 
 
 
 
408
  $('button').on('click', function(){
409
  $(this).parent().get(0).reset();
410
  $('#widget-custom-menu-wizard-2-filter_item').trigger('change');
411
  return false;
412
- });
413
- flds.not( flds.filter('[type="text"]').on('blur', show) )
414
- .on('change', walk).eq(0).trigger('change');
 
 
 
 
 
 
 
415
  });
416
  </script>
417
  </body>
21
  #formheader {background-color:#666666; border-radius:0.4em 0.4em 0 0; color:#FFFFFF; margin:0 -1em -0.5em; padding:0.25em 1em; text-align:center;}
22
  #theform {margin:1em; padding:0 1em; border:1px solid #999999; border-radius:0.5em;}
23
  #theoutput {margin:1em;}
24
+ #widget-output {margin:0.5em 0; padding:0 1em; border:1px solid #cccccc; border-radius:0.5em; min-height:2em; min-width:9em;}
25
+ #widget-output ul, #widget-output ol {margin:0.5em 0 0.5em 1.5em; padding:0;}
26
+ #widget-output li ul {margin:0 0 0 1em;}
27
+ #widget-output li ol {margin:0 0 0 1.5em;}
28
+ #widget-output ul {list-style-type:disc;}
29
+ #widget-output ul ul {list-style-type:circle;}
30
+ #widget-output ul ul ul {list-style-type:square;}
31
+ #widget-output ul ul ul ul {list-style-type:disc;}
32
+ #widget-output ul ul ul ul ul {list-style-type:circle;}
33
+ #widget-output ol {list-style-type:decimal;}
34
+ #widget-output ol ol {list-style-type:upper-alpha;}
35
+ #widget-output ol ol ol {list-style-type:lower-alpha;}
36
+ #widget-output ol ol ol ol {list-style-type:lower-roman;}
37
+ #widget-output ol ol ol ol ol {list-style-type:decimal;}
38
+ #fallback {display:none; text-align:right; color:#006600;}
39
  #themenu {margin:1em;}
40
  #themenu ul {font-size:12px; list-style-type:none; margin:1em 0; padding:0;}
41
  #themenu ul ul {margin:0 0 0 5em;}
46
  #thefooter div {font-size:12px;}
47
  .alignleft {float:left;}
48
  .alignright {float:right;}
49
+ .collapsible {background-color:#ffffff; background-image:-webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#e0e0e0)); background-image:-webkit-linear-gradient(top, #FFFFFF, #E0E0E0); background-image:-moz-linear-gradient(top, #FFFFFF, #E0E0E0); background-image:-ms-linear-gradient(top, #FFFFFF, #E0E0E0); background-image:-o-linear-gradient(top, #FFFFFF, #E0E0E0); background-image:linear-gradient(to bottom, #FFFFFF, #E0E0E0); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffff, endColorstr=#e0e0e0); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffff, endColorstr=#e0e0e0)";border:1px solid #CCCCCC; border-radius:0.3333em; cursor:pointer; font-size:12px; line-height:1.75; padding:0 0.5em;}
50
+ .collapsible span {display:none; position:relative; float:right; color:#999999;}
51
+ .collapsible:hover span {color:#333333;}
52
+ .collapsible span:first-child, .collapsible.closed span:last-child {display:inline;}
53
+ .collapsible.closed span:first-child {display:none;}
54
+ .current-ancestor {border-color:#cccc00 !important; background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255, 255, 153, 1)), to(rgba(255, 255, 153, 0.1))); background-image:-webkit-linear-gradient(top, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); background-image:-moz-linear-gradient(top, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); background-image:-ms-linear-gradient(top, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); background-image:-o-linear-gradient(top, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); background-image:linear-gradient(to bottom, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffff99, endColorstr=#1affff99); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffff99, endColorstr=#1affff99)";}
55
+ .current-item {border-color:#cc0000 !important; background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255, 204, 204, 1)), to(rgba(255, 204, 204, 0.1))); background-image:-webkit-linear-gradient(top, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); background-image:-moz-linear-gradient(top, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); background-image:-ms-linear-gradient(top, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); background-image:-o-linear-gradient(top, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); background-image:linear-gradient(to bottom, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffcccc, endColorstr=#1affcccc); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffcccc, endColorstr=#1affcccc)";}
56
+ .current-parent {border-color:#ff9900 !important; background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255, 204, 153, 1)), to(rgba(255, 204, 153, 0.1))); background-image:-webkit-linear-gradient(top, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); background-image:-moz-linear-gradient(top, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); background-image:-ms-linear-gradient(top, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); background-image:-o-linear-gradient(top, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); background-image:linear-gradient(to bottom, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffcc99, endColorstr=#1affcc99); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffcc99, endColorstr=#1affcc99)";}
57
  .grey {color:#999999;}
58
  .picked > a {background-color:#000066; color:#ffffff; font-weight:bold;}
59
  .tryit {margin-left:1em; text-shadow:1px 1px 1px #ff0000;}
66
  <form>
67
  <div id="formheader">
68
  <strong>Custom Menu Wizard</strong> Interactive Demo
69
+ <br /><small>v1.2.0 <em>(subset of admin options)</em></small><strong class="tryit"><em>Try it out&hellip;</em></strong>
70
  </div>
71
 
72
  <p>
78
  <small><em>Title can be set, but need not be displayed</em></small>
79
  </p>
80
 
81
+ <p class='collapsible'>Filter<span>&#9650;</span><span>&#9660;</span></p>
82
+
83
  <p>
84
  <label>
85
  <input type="radio" checked="checked" value="0" name="filter" class="widget-custom-menu-wizard-listen" id="widget-custom-menu-wizard-2-filter_0">
91
  <option selected="selected" value="0">Current Item</option>
92
  <option value="-2">Current Root Item</option>
93
  <option value="-1">Current Parent Item</option>
94
+ <optgroup label="the-menu"></optgroup>
95
  </select>
96
  </p>
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  <p>
99
  <label for="widget-custom-menu-wizard-2-start_level">Starting Level:</label>
100
  <select name="start_level" id="widget-custom-menu-wizard-2-start_level">
110
  <br /><small><em>Relative to the first Filtered item found</em></small>
111
  </p>
112
 
113
+ <p class='collapsible end-collapsible'>Fallbacks<span>&#9650;</span><span>&#9660;</span></p>
114
+
115
+ <p class="widget-custom-menu-wizard-disableifnot-rp">
116
+ <small><strong>If &quot;Children of&quot; is <em>Current Root / Parent Item</em>, and no ancestor exists :</strong></small>
117
+ <br /><label>
118
+ <input type="checkbox" value="1" name="fallback_no_ancestor" id="widget-custom-menu-wizard-2-fallback_no_ancestor">
119
+ Switch to Current Item, and</label>
120
+ <br /><label style="padding-left:1em;">
121
+ <input type="checkbox" value="1" name="fallback_include_parent" id="widget-custom-menu-wizard-2-fallback_include_parent">
122
+ Include Parent... </label>
123
+ <label>
124
+ <input type="checkbox" value="1" name="fallback_include_parent_siblings" id="widget-custom-menu-wizard-2-fallback_include_parent_siblings">
125
+ &amp; its Siblings</label>
126
+ <br /><small><em>If Current Root/Parent and no ancestor exists</em></small>
127
+ </p>
128
+
129
+ <p class="widget-custom-menu-wizard-disableifnot-ci">
130
+ <small><strong>If &quot;Children of&quot; is <em>Current Item</em>, and current item has no children :</strong></small>
131
+ <br /><label>
132
+ <input type="checkbox" value="1" name="fallback_no_children" id="widget-custom-menu-wizard-2-fallback_no_children">
133
+ Switch to Current Parent Item, and</label>
134
+ <br /><label style="padding-left:1em;">
135
+ <input type="checkbox" value="1" name="fallback_nc_include_parent" id="widget-custom-menu-wizard-2-fallback_nc_include_parent">
136
+ Include Parent... </label>
137
+ <label>
138
+ <input type="checkbox" value="1" name="fallback_nc_include_parent_siblings" id="widget-custom-menu-wizard-2-fallback_nc_include_parent_siblings">
139
+ &amp; its Siblings</label>
140
+ </p>
141
+
142
+ <p class='collapsible end-collapsible'>Output<span>&#9650;</span><span>&#9660;</span></p>
143
+
144
  <p>
145
  <label>
146
  <input type="radio" checked="checked" value="0" name="flat_output" id="widget-custom-menu-wizard-2-flat_output_0">
162
  Include Ancestors</label>
163
  <br /><label>
164
  <input type="checkbox" value="1" name="title_from_parent" id="widget-custom-menu-wizard-2-title_from_parent">
165
+ Title from Parent</label>
166
+ <br /><small><em>Only if the "Children of" Filter returns items</em></small>
167
+ </p>
168
+
169
+ <p>
170
+ <label>
171
+ <input type="checkbox" value="1" name="title_from_current" id="widget-custom-menu-wizard-2-title_from_current">
172
+ Title from &quot;Current&quot; Item</label>
173
+ <br /><small><em>Lower priority than "Title from Parent"</em></small>
174
  </p>
175
 
176
  <p>
183
  Sub-Levels</label>
184
  </p>
185
 
186
+ <p class="grey end-collapsible">
187
  <small><em>Note that since WP v3.6 does it automatically,
188
  <br />&quot;Hide Widget if Empty&quot; is ON for this demo.</em></small>
189
  </p>
195
  <div id='theoutput' class='alignright'>
196
  <strong>Basic Output</strong> &hellip;
197
  <div id='widget-output'></div>
198
+ <div id='fallback'><small>Fallback invoked</small></div>
199
  </div>
200
 
201
  <div id='themenu' class='alignleft'>
262
  </li>
263
  </ul>
264
  </li>
265
+ <li>
266
+ <a href='#'><span>oscar</span></a>
267
+ </li>
268
  </ul>
269
  </div>
270
 
279
  <script type='text/javascript'>
280
  jQuery(function($){
281
  var maxlevel = 0,
282
+ switches = {hide_title:1, filter:1, fallback_no_ancestor:1, fallback_include_parent:1, fallback_include_parent_siblings:1, fallback_no_children:1, fallback_nc_include_parent:1, fallback_nc_include_parent_siblings:1, flat_output:1, include_parent:1, include_parent_siblings:1, include_ancestors:1, title_from_parent:1, ol_root:1, ol_sub:1},
283
  integers = {filter_item:1, start_level:1, depth:1},
284
  getSettings = function(){
285
  var settings = {};
299
  var output = $('#widget-output').empty(),
300
  items = $('.picked'),
301
  html = '',
302
+ title = '',
303
  currLevel = 0,
304
+ startLevel;
305
  settings = settings || getSettings();
306
  if(items.length){
 
307
  if(settings.filter && settings.title_from_parent){
308
  title = $('.picked-parent').children('a').text() || '';
309
  }
310
+ if(!title && settings.title_from_current){
311
+ title = $('.current-item').text() || '';
312
+ }
313
+ if(!title && !settings.hide_title){
314
+ title = settings.title || '';
315
+ }
316
  items.each(function(){
317
+ var level = settings.flat_output ? 1 : parseInt(this.className.match(/level-(\d+)/)[1], 10),
318
+ anchor = $(this).children('a');
319
  if(currLevel){
320
  if(level === currLevel){
321
  html += '</li>';
329
  }else{
330
  startLevel = level;
331
  }
332
+ html += '<li class="cmw-level-' + level + '"><a href="#' + anchor.data('indx') + '">' + anchor.text() + '</a>';
333
  currLevel = level;
334
  });
335
  while(currLevel-- > startLevel){
340
  if(title){
341
  output.prepend('<h3>' + title + '</h3>');
342
  }
343
+ output.find('li').filter(function(){
344
+ return !!$(this).children('ul').length;
345
+ }).addClass('cmw-has-submenu');
346
  }
347
  },
348
  walk = function(e){
353
  picked = items.filter('.picked'),
354
  cls = [],
355
  firstParent = true,
356
+ fallback = false,
357
  parent, i;
358
  if(!settings.filter){
359
  //show all...
382
  parent = $('.current-item').closest('li');
383
  includeParent = includeParent || settings.fallback_include_parent;
384
  includeParentSiblings = includeParentSiblings || settings.fallback_include_parent_siblings;
385
+ fallback = !!parent.length;
386
  }
387
  }
388
  //could be multiple - only want first one that matches start_level...
400
  });
401
  //kids...
402
  items = parent.find('li').filter( cls.join(',') );
403
+ //...if current item is looked for and found, but has no kids, and fallback is set...
404
+ if(parent.length && !settings.filter_item && !items.length && settings.fallback_no_children){
405
+ items = parent.siblings('li').add( parent );
406
+ includeParent = includeParent || settings.fallback_nc_include_parent;
407
+ includeParentSiblings = includeParentSiblings || settings.fallback_nc_include_parent_siblings;
408
+ parent = parent.parents('li').eq(0);
409
+ fallback = !!parent.length;
410
+ }
411
  if(items.length){
412
  if(includeParentSiblings){
413
  items = items.add( parent.siblings('li') ).add( parent );
423
  parent.addClass('picked-parent');
424
  }
425
  }
426
+ $('#fallback')[fallback && items.length ? 'show' : 'hide']();
427
  picked.not(items).removeClass('picked');
428
  items.not('.picked').addClass('picked');
429
  show.call(this, e, settings);
430
  },
431
  flds = $('input,select');
 
432
  $('#themenu a').on('click', function(){
433
  //click on a #themenu item to [un]make it the current item...
434
  var cls = ['current-item', 'current-parent', 'current-ancestor'],
446
  return false;
447
  }).map(function(i){
448
  //create filter_item's optgroup options...
449
+ var el = $(this).data('indx', i),
450
  level = el.parentsUntil('#themenu', 'li').length;
451
  maxlevel = Math.max(maxlevel, level);
452
  el.parent().addClass('level-' + level);
453
  return $('<option/>', {value:i + 1}).text( hyphenIndent(el.text(), level) ).get(0);
454
  }).appendTo('optgroup');
455
+ //allow setting current from widget output (only setting, not un-setting!)...
456
+ $('#widget-output').on('click', 'a', function(){
457
+ var indx = this.href.split('#')[1];
458
+ $('#themenu a').eq(indx).not(':has(.current-item)').trigger('click');
459
+ return false;
460
+ });
461
  //populate starting-level and depth options...
462
  for(i = 1; i < maxlevel + 1; i++){
463
  var levels = i < 2 ? ' level' : ' levels',
468
  }
469
  $('<option/>', att).text(i).appendTo('#widget-custom-menu-wizard-2-start_level');
470
  }
471
+ //interactivity (need to ensure that any delegated handlers from the included script get run first)...
472
+ flds.not( flds.filter('[type="text"]').on('blur', show) ).addClass('demo-change');
473
+ $(document).on('change', '.demo-change', walk);
474
+ //reset...
475
  $('button').on('click', function(){
476
  $(this).parent().get(0).reset();
477
  $('#widget-custom-menu-wizard-2-filter_item').trigger('change');
478
  return false;
479
+ }).trigger('click');
480
+ //collapsibles...
481
+ $('.collapsible').on('click', function(){
482
+ var Self = $(this)
483
+ data = Self.data();
484
+ Self.toggleClass('closed', !data.closed).nextUntil('.end-collapsible')[data.closed ? 'slideDown' : 'slideUp']();
485
+ data.closed = !data.closed;
486
+ this.blur();
487
+ return false;
488
+ }).slice(1).trigger('click');
489
  });
490
  </script>
491
  </body>
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: wizzud
3
  Tags: menu,widget,widgets,navigation,nav,custom menus,custom menu,partial menu,menu level,menu branch
4
  Requires at least: 3.0.1
5
  Tested up to: 3.6
6
- Stable tag: 1.1.0
7
  License: GPLv2 or Later
8
 
9
  Custom Menu Wizard Widget : Show branches or levels of your menu in a widget, with full customisation.
@@ -11,7 +11,7 @@ Custom Menu Wizard Widget : Show branches or levels of your menu in a widget, wi
11
  == Description ==
12
 
13
  This plugin is a boosted version of the WordPress "Custom Menu" widget.
14
- It provides full control over most of the parameters available when calling WP's [wp_nav_menu()](http://codex.wordpress.org/Function_Reference/wp_nav_menu) function, as well as providing pre-filtering of the menu items in order to be able to select a specific portion of the custom menu. It also automatically adds a couple of custom classes.
15
 
16
  Features include:
17
 
@@ -26,7 +26,8 @@ Features include:
26
  * Add/specify custom class(es) for the widget block, the menu container, and the menu itself
27
  * Modify the link's output with additional HTML around the link's text and/or the link element itself
28
  * Use Ordered Lists (OL) for the top and/or sub levels instead of Unordered Lists (UL)
29
- * As of v1.1.0 : Select a branch based on the ultimate ancestor (root level) of the "current" item
 
30
 
31
  **Widget Options**
32
 
@@ -61,36 +62,17 @@ logical sections and made each section collapsible (with remembered state, open
61
  The dropdown list will present a "Current Item" option (default), a "Current Root Item" (v1.1.0)
62
  and "Current Parent Item" (v1.1.0) option, followed by all the available items from the menu chosen in `Select Menu`.
63
  The widget will output the *children* of the selected item, always assuming that they lie within the bounds of any other parameters set.
64
-
65
- "Current Item" is the menu item that WordPress recognises as being currently on display;
66
- "Current Parent Item" (v1.1.0) is the *immediate* ancestor (within `Select Menu`) of that same menu item;
67
- and "Current Root Item" (v1.1.0) is the *ultimate* ancestor (within `Select Menu`) of that same menu item.
68
- Obviously, if the menu item currently on display (as determined by WordPress) does not
69
- appear in the `Select Menu` then there is going to be no output for any of these options.
70
-
71
- If you change `Select Menu`, the options presented in this dropdown will change accordingly and the selected option will revert to the default.
72
 
73
- * **Fallback to Current Item** *(checkbox)* as of v1.1.0
74
 
75
- This option is only applicable when `Children of` is set to either "Current Root Item" or "Current Parent Item". If enabled,
76
- it provides a fallback of effectively switching the filter to "Current Item" **if, and only if**, the current menu item
77
- (as determined by WordPress) is at Level 1
78
- (top level) of the selected menu. For example, say you were to set `Children of` to "Current Parent Item", with `Starting Level`
79
- at "1" and `For Depth` at "unlimited" : if the current menu item was found at level 1 (root level of the menu) then ordinarily
80
- there would be no output because the current item has no parent! If you were to enable `Fallback to Current Item` then you
81
- *would* have some output - the entire branch below the current item.
82
-
83
- * **Include Parent...** *(checkbox)* as of v1.1.0
84
-
85
- This option extends the `Fallback to Current Item` option (above). If the enabled fallback is actually used, this option can
86
- temporarily override the equivalent **Output** option to ON. Note that if the **Output** options are already set to include
87
- the parent item (with or without siblings), this option has absolutely no effect.
88
 
89
- * **& its Siblings** *(checkbox)* as of v1.1.0
90
 
91
- This option extends the `Fallback to Current Item` option (above). If the enabled fallback is actually used, this option can
92
- temporarily override the equivalent **Output** option to ON. Note that if the equivalent **Output** option is already enabled,
93
- this option has absolutely no effect.
 
94
 
95
  * **Starting Level** *(select, default: "1")*
96
 
@@ -108,6 +90,57 @@ logical sections and made each section collapsible (with remembered state, open
108
  `Children of` to "Current Item", `Starting Level` to "2", and `For Depth` to "2 levels" : if the current item was found at level 3,
109
  then you would get the current item's immediate children (from level 4), plus *their* immediate children (from level 5).
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  * **Output**
112
 
113
  * **Hierarchical** *(radio, default)*
@@ -132,11 +165,19 @@ logical sections and made each section collapsible (with remembered state, open
132
  Same as `Include Parent` except that all ancestors, right back to root level, are included. Only applies to a successful
133
  `Children of` filter.
134
 
135
- * **Title from Parent Item** *(checkbox)*
136
 
137
  Again, this only applies to a successful `Children of` filter. If checked, use the title of the parent item as the widget's
138
  title when displaying the output. This will override (ie. ignore) the `Hide` checkbox setting!
139
 
 
 
 
 
 
 
 
 
140
  * **Change UL to OL**
141
 
142
  The standard for menus is to use UL (unordered list) elements to display the output. These settings give you the option to swap
@@ -177,7 +218,7 @@ logical sections and made each section collapsible (with remembered state, open
177
 
178
  * **Classes**
179
 
180
- * **Menu Class** *(default: "menu-widget")
181
 
182
  This is the class that will be applied to the list element that holds the entire menu.
183
 
@@ -203,6 +244,127 @@ logical sections and made each section collapsible (with remembered state, open
203
 
204
  Text or HTML that will be placed immediately after each menu item's link text.
205
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  == Installation ==
207
 
208
  1. EITHER Upload the zip package via 'Plugins > Add New > Upload' in your WP Admin
@@ -230,6 +392,18 @@ If you have a question or problem, please use the integrated Support forum.
230
 
231
  == Changelog ==
232
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  = 1.1.0 =
234
 
235
  * added 'Current Root Item' and 'Current Parent Item' to the `Children of` filter
@@ -256,6 +430,12 @@ Initial release
256
 
257
  == Upgrade Notice ==
258
 
 
 
 
 
 
 
259
  = 1.1.0 =
260
 
261
  `Children of` has 2 extra options, for filtering using the Current Item's parent (immediate ancestor) or root (ultimate ancestor) item,
3
  Tags: menu,widget,widgets,navigation,nav,custom menus,custom menu,partial menu,menu level,menu branch
4
  Requires at least: 3.0.1
5
  Tested up to: 3.6
6
+ Stable tag: 1.2.0
7
  License: GPLv2 or Later
8
 
9
  Custom Menu Wizard Widget : Show branches or levels of your menu in a widget, with full customisation.
11
  == Description ==
12
 
13
  This plugin is a boosted version of the WordPress "Custom Menu" widget.
14
+ It provides full control over most of the parameters available when calling WP's [wp_nav_menu()](http://codex.wordpress.org/Function_Reference/wp_nav_menu) function, as well as providing pre-filtering of the menu items in order to be able to select a specific portion of the custom menu. It also automatically adds a couple of custom classes. And there is now (v1.2.0) a shortcode that enables you to include the widget's output in your content.
15
 
16
  Features include:
17
 
26
  * Add/specify custom class(es) for the widget block, the menu container, and the menu itself
27
  * Modify the link's output with additional HTML around the link's text and/or the link element itself
28
  * Use Ordered Lists (OL) for the top and/or sub levels instead of Unordered Lists (UL)
29
+ * *As of v1.1.0* : Select a branch based on the ultimate ancestor (root level) of the "current" item
30
+ * *As of v1.2.0* : Shortcode, [custom_menu_wizard], available to run the widget from within content
31
 
32
  **Widget Options**
33
 
62
  The dropdown list will present a "Current Item" option (default), a "Current Root Item" (v1.1.0)
63
  and "Current Parent Item" (v1.1.0) option, followed by all the available items from the menu chosen in `Select Menu`.
64
  The widget will output the *children* of the selected item, always assuming that they lie within the bounds of any other parameters set.
 
 
 
 
 
 
 
 
65
 
66
+ * "Current Item" is the menu item that WordPress recognises as being currently on display (current menu item)
67
 
68
+ * "Current Parent Item" (v1.1.0) is the *immediate* ancestor (within `Select Menu`) of the current menu item
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
+ * "Current Root Item" (v1.1.0) is the *ultimate* ancestor (within `Select Menu`) of the current menu item.
71
 
72
+ Obviously, if the current menu item (as determined by WordPress) does not
73
+ appear in the `Select Menu` then there is going to be no output for any of these options.
74
+
75
+ If you change `Select Menu`, the options presented in this dropdown will change accordingly and the selected option will revert to the default.
76
 
77
  * **Starting Level** *(select, default: "1")*
78
 
90
  `Children of` to "Current Item", `Starting Level` to "2", and `For Depth` to "2 levels" : if the current item was found at level 3,
91
  then you would get the current item's immediate children (from level 4), plus *their* immediate children (from level 5).
92
 
93
+ * **Fallbacks**
94
+
95
+ Fallback for `Children of` being set to either *Current Root Item* or *Current Parent Item*, and current item not having an ancestor:
96
+
97
+ * **Switch to Current Item** *(checkbox)* as of v1.1.0
98
+
99
+ If enabled,
100
+ it provides a fallback of effectively switching the filter to "Current Item" if the current menu item
101
+ (as determined by WordPress) is at Level 1
102
+ (top level) of the selected menu. For example, say you were to set `Children of` to "Current Parent Item", with `Starting Level`
103
+ at "1" and `For Depth` at "unlimited" : if the current menu item was found at level 1 (root level of the menu) then ordinarily
104
+ there would be no output because the current item has no parent! If you were to enable the `Switch to Current Item` fallback then you
105
+ *would* have some output - the entire branch below the current item.
106
+
107
+ * **Include Parent...** *(checkbox)* as of v1.1.0
108
+
109
+ This option extends the `Switch to Current Item` option (above). If the enabled fallback is actually used, this option can
110
+ temporarily override the equivalent **Output** option to ON. Note that if the **Output** options are already set to include
111
+ the parent item (with or without siblings), this option has absolutely no effect.
112
+
113
+ * **& its Siblings** *(checkbox)* as of v1.1.0
114
+
115
+ This option extends the `Switch to Current Item` option (above). If the enabled fallback is actually used, this option can
116
+ temporarily override the equivalent **Output** option to ON. Note that if the equivalent **Output** option is already enabled,
117
+ this option has absolutely no effect.
118
+
119
+ Fallback for `Children of` being set to *Current Item*, and current item not having any children:
120
+
121
+ * **Switch to Current Parent Item** *(checkbox)* as of v1.2.0
122
+
123
+ If enabled, it provides a fallback of effectively switching the filter to "Current Parent Item" if looking for children
124
+ of Current Item and there aren't any. For example, say you were to set `Children of` to "Current Item", with `Starting Level`
125
+ at "1" and `For Depth` at "unlimited" : if the current menu item has no children then ordinarily
126
+ there would be no output! If you were to enable the `Switch to Current Item` fallback then you
127
+ *would* have some output - the current item and its siblings.
128
+
129
+ Please note that there is one difference between this fallback and the normal "Current Parent Item" filter : if the current item
130
+ has no ancestor (as well as no children) then you will always get the current item and its siblings, regardless of any other settings!
131
+
132
+ * **Include Parent...** *(checkbox)* as of v1.2.0
133
+
134
+ This option extends the `Switch to Current Parent Item` option (above). If the enabled fallback is actually used, this option can
135
+ temporarily override the equivalent **Output** option to ON. Note that if the **Output** options are already set to include
136
+ the parent item (with or without siblings), this option has absolutely no effect.
137
+
138
+ * **& its Siblings** *(checkbox)* as of v1.2.0
139
+
140
+ This option extends the `Switch to Current Parent Item` option (above). If the enabled fallback is actually used, this option can
141
+ temporarily override the equivalent **Output** option to ON. Note that if the equivalent **Output** option is already enabled,
142
+ this option has absolutely no effect.
143
+
144
  * **Output**
145
 
146
  * **Hierarchical** *(radio, default)*
165
  Same as `Include Parent` except that all ancestors, right back to root level, are included. Only applies to a successful
166
  `Children of` filter.
167
 
168
+ * **Title from Parent** *(checkbox)*
169
 
170
  Again, this only applies to a successful `Children of` filter. If checked, use the title of the parent item as the widget's
171
  title when displaying the output. This will override (ie. ignore) the `Hide` checkbox setting!
172
 
173
+ * **Title from "Current" Item** *(checkbox)*
174
+
175
+ If checked, use the title of the current menu item (as determined by WordPress) as the widget's
176
+ title when displaying the output. This will override (ie. ignore) the `Hide` checkbox setting!
177
+
178
+ Note that the current menu item is not required to be within the resultant output, merely within the `Select Menu`.
179
+ Also, `Title from Parent` (if applicable, and if available) takes priority over this option.
180
+
181
  * **Change UL to OL**
182
 
183
  The standard for menus is to use UL (unordered list) elements to display the output. These settings give you the option to swap
218
 
219
  * **Classes**
220
 
221
+ * **Menu Class** *(default: "menu-widget")*
222
 
223
  This is the class that will be applied to the list element that holds the entire menu.
224
 
244
 
245
  Text or HTML that will be placed immediately after each menu item's link text.
246
 
247
+ **Shortcode**
248
+
249
+ The shortcode is **`[custom_menu_wizard]`**. Most of the attributes reflect the options available to the widget, but some have been simplified for
250
+ easier use in the shortcode format.
251
+ Please note that the `Hide Widget if Empty` option is not available to the shortcode : it is set to enabled, and if there are no menu items
252
+ found then there will be no output from the shortcode.
253
+
254
+ * **title** *(string)*
255
+
256
+ The output's `Title`, which may be overridden by `title_from`. Note that there is no shortcode equivalent of the widget's `Hide` option for the title.
257
+
258
+ * **menu** *(string | integer)*
259
+
260
+ Accepts a menu name (most likely usage) or id. If not provided, the shortcode will attempt to find the first menu (ordered by name)
261
+ that has menu items attached to it, and use that.
262
+
263
+ * **children_of** *(string | integer)*
264
+
265
+ If empty, or not provided, this is the same as enabling `Show All` (see above) for the widget. Anything else is a `Children of` filter :
266
+
267
+ * If numeric, it is taken as being the id of a menu item. The widget will look for the `Children of` that menu item (within `menu`).
268
+ (Hint : In Menus Admin, hover over the item's **Remove** link and note the number after *menu-item=* in the URL)
269
+
270
+ * Certain specific strings have the following meanings:
271
+
272
+ * *'current'* or *'current-item'* : a `Children of` "Current Item" filter
273
+
274
+ * *'parent'* or *'current-parent'* : a `Children of` "Current Parent Item" filter
275
+
276
+ * *'root'* or *'current-ancestor'* : a `Children of` "Current Root Item" filter
277
+
278
+ * If any other string, it is taken to be the title of a menu item. The widget will look for the `Children of` that menu item
279
+ (within `menu`). Please note that the code looks for a *caseless* title match, so specifying `children_of="my menu item"` will
280
+ match against a menu item with the title "My Menu Item".
281
+
282
+ * **fallback_parent** *(string | integer)*
283
+
284
+ This is the fallback option for when `Children of` is set to either *Current Root Item* or *Current Parent Item*, and
285
+ the current item has no ancestors (see `Switch to Current Item` under **Fallbacks** above).
286
+
287
+ * Any "truthy" value (eg. 1, *'true'*, *'on'*, *'parent'*, *'siblings'*) : Enables widget's `Switch to Current Item` **Fallbacks** option
288
+
289
+ * *'parent'* : Enables widget's `Include Parent...` **Fallbacks** extension option (in addition to the above)
290
+
291
+ * *'siblings'* : Enables widget's `& its Siblings` **Fallbacks** extension option (in addition to the above)
292
+
293
+ * **fallback_current** *(string | integer)*
294
+
295
+ This is the fallback option for when `Children of` is set to *Current Item*, and
296
+ the current item has no children (see `Switch to Current Parent Item` under **Fallbacks** above).
297
+
298
+ * Any "truthy" value (eg. 1, *'true'*, *'on'*, *'parent'*, *'siblings'*) : Enables widget's `Switch to Current Parent Item` **Fallbacks** option
299
+
300
+ * *'parent'* : Enables widget's `Include Parent...` **Fallbacks** extension option (in addition to the above)
301
+
302
+ * *'siblings'* : Enables widget's `& its Siblings` **Fallbacks** extension option (in addition to the above)
303
+
304
+ * **start_level** *(integer, default 1)* See widget's `Starting Level` option, under **Filter** above.
305
+
306
+ * **depth** *(integer, default 0)* See widget's `For Depth` option, under **Filter** above.
307
+
308
+ * **flat_output** *(switch, off by default, 1 to enable)* See widget's `Flat` option, under **Output** above.
309
+
310
+ * **include** *(string)*
311
+
312
+ * *'parent'* : Enables widget's `Include Parent...` **Output** option
313
+
314
+ * *'siblings'* : Enables widget's `& its Siblings` **Output** option
315
+
316
+ * *'ancestors'* : Enables widget's `Include Ancestors` **Output** option
317
+
318
+ Supply more than one by separating them with a comma, space or hyphen, eg. `include="siblings ancestors"`.
319
+
320
+ * **title_from** *(string)*
321
+
322
+ * *'parent'* : Enables widget's `Title from Parent` **Output** option
323
+
324
+ * *'current'* : Enables widget's `Title from "Current" Item` **Output** option
325
+
326
+ Supply more than one by separating them with a comma, space or hyphen, eg. `title_from="parent,current"`.
327
+
328
+ * **ol_root** *(switch, off by default, 1 to enable)* See widget's `Top Level` option, under **Output** above.
329
+
330
+ * **ol_sub** *(switch, off by default, 1 to enable)* See widget's `Sub-Levels` option, under **Output** above.
331
+
332
+ * **container** *(string)* See widget's `Element` option, under **Container** above.
333
+
334
+ * **container_id** *(string)* See widget's `Unique ID` option, under **Container** above.
335
+
336
+ * **container_class** *(string)* See widget's `Class` option, under **Container** above.
337
+
338
+ * **menu_class** *(string)* See widget's `Menu Class` option, under **Classes** above.
339
+
340
+ * **widget_class** *(string)* See widget's `Widget Class` option, under **Classes** above.
341
+
342
+ * **wrap_link** *(string)*
343
+
344
+ This is an optional tag name (eg. *'div'*, *'p'*, *'span*') that, if provided, will be made into HTML start/end tags
345
+ and sent through to the widget as its `Before the Link` and `After the Link` options. Please note that the shortcode usage - a simple
346
+ tag name - is much more restrictive than the widget's options, which allow HTML.
347
+
348
+ * **wrap_link_text** *(string)*
349
+
350
+ This is an optional tag name (eg. *'span*', *'em'*, '*strong*') that, if provided, will be made into HTML start/end tags
351
+ and sent through to the widget as its `Before the Link Text` and `After the Link Text` options. Please note that the shortcode usage - a
352
+ simple tag name - is much more restrictive than the widget's options, which allow HTML.
353
+
354
+ **Shortcode Examples**
355
+
356
+ * Show the entire "main" menu :
357
+
358
+ `[custom_menu_wizard menu=main]`
359
+
360
+ * Show the children of the Current Item within the "main" menu, for unlimited depth, and include the Current Item's parent :
361
+
362
+ `[custom_menu_wizard menu=main children_of=current include_parent=1]`
363
+
364
+ * From the "animals" menu, show all the items *immediately* below (depth=1) "Small Dogs", plus "Small Dogs" and its sibling items, as ordered lists :
365
+
366
+ `[custom_menu_wizard menu="animals" children_of="small dogs" depth=1 include_parent_siblings=1 ol_root=1 ol_sub=1]`
367
+
368
  == Installation ==
369
 
370
  1. EITHER Upload the zip package via 'Plugins > Add New > Upload' in your WP Admin
392
 
393
  == Changelog ==
394
 
395
+ = 1.2.0 =
396
+
397
+ * added custom_menu_wizard shortcode, to run the widget from within content
398
+
399
+ * moved the 'no ancestor' fallback into new Fallback collapsible section, and added a fallback for Current Item with no children
400
+
401
+ * fixed a bug with optgroups/options made available for the 'Children of' selector after the widget has been saved (also affected disabled fields and styling)
402
+
403
+ * don't include menus with no items
404
+
405
+ * updated demo.html
406
+
407
  = 1.1.0 =
408
 
409
  * added 'Current Root Item' and 'Current Parent Item' to the `Children of` filter
430
 
431
  == Upgrade Notice ==
432
 
433
+ = 1.2.0 =
434
+
435
+ Added custom_menu_wizard shortcode, to run the widget from within content. Also added a new fallback for Current Item having no children, and
436
+ moved all fallbacks into a collapsible Fallbacks section. Fixed a bug with optgroups/options made available for the 'Children of' selector
437
+ after the widget has been saved (also affected disabled fields and styling).
438
+
439
  = 1.1.0 =
440
 
441
  `Children of` has 2 extra options, for filtering using the Current Item's parent (immediate ancestor) or root (ultimate ancestor) item,